[comp.sys.atari.st.tech] Using Timer A to replay a sample

mlake@irscscm.UUCP (Marshall Lake) (09/24/90)

I have a programming problem and I'm hoping someone here can help me.

I am attempting to play back a sample via timer A while allowing other
things to go on as it plays.  When the program attempts to start the
actual playback it blows, sometimes displaying 4 bombs and sometimes
6.  I have also tried to place the interrupt code into the main flow
of the program to see what would happen.  The bombs don't occur but
nothing is emitted by the sound chip either.  What am I doing wrong?

The code follows:

#include <osbind.h>
#include <xbios.h>

long *buff_cnt, *buff_end, *sound_chip = 0xff8800, *gi_write = 0xff8802;
char *trumpet_sound;
int snd_value,
    snd_out[1025] = { <table of values same as in other programs
                       that play back samples>  };

do_snd () {
    if (buff_cnt > buff_end)
        return;

    snd_value = (*buff_cnt + 128) * 8;*/
    *sound_chip = snd_out[snd_value];
    *sound_chip = snd_out[snd_value + 4];

    buff_cnt++;
}

play (addr)
char *addr;
{
    buff_cnt = addr;
    buff_end = addr + 58965L;
}

set_snd_chip () {
    *sound_chip = 0;
    *gi_write = 0;
    *sound_chip = 1;
    *gi_write = 0;
    *sound_chip = 2;
    *gi_write = 0;
    *sound_chip = 3;
    *gi_write = 0;
    *sound_chip = 4;
    *gi_write = 0;
    *sound_chip = 5;
    *gi_write = 0;
    *sound_chip = 7;
    *gi_write = 255;
    *sound_chip = 8;
    *gi_write = 0;
    *sound_chip = 9;
    *gi_write = 0;
    *sound_chip = 10;
    *gi_write = 0;
}

main () {
    char *save_ssp;
    int counter, read_handle;

    if ((trumpet_sound = (char *) Malloc (58 * 1024L)) == 0) {
        printf ("\n\n NOT ENOUGH MEMORY \n\n");
        exit ();
    }
    if ((read_handle = Fopen ("trumpet.spl", 0)) < 0) {
        printf ("\n Cannot find trumpet.spl \n");
        exit ();
    }
    Fread (read_handle, 58965L, trumpet_sound);
    Fclose (read_handle);

    buff_cnt = 1L;
    buff_end = 0L;

    Supexec (&set_snd_chip);
    save_ssp = Super (0L);
    Xbtimer (0, 7, 0, &do_snd);

    play (trumpet_sound);

    counter = 0;
    while (1) {
        if (counter == 30000)
            break;
        printf ("counter = %d\n", counter++);
    }

    Xbtimer (0, 0, 0, &do_snd);
    Super (save_ssp);
}
-- 
Marshall Lake
mlake@irscscm.UUCP
...!uunet!media!ka3ovk!irscscm!mlake

leo@ehviea.ine.philips.nl (Leo de Wit) (09/26/90)

In article <1990Sep24.122920.15704@irscscm.UUCP> mlake@irscscm.UUCP (Marshall Lake) writes:
|
|I have a programming problem and I'm hoping someone here can help me.
|
|I am attempting to play back a sample via timer A while allowing other
|things to go on as it plays.  When the program attempts to start the
|actual playback it blows, sometimes displaying 4 bombs and sometimes
|6.  I have also tried to place the interrupt code into the main flow
|of the program to see what would happen.  The bombs don't occur but
|nothing is emitted by the sound chip either.  What am I doing wrong?
[...]
|do_snd () {
|    if (buff_cnt > buff_end)
|        return;
|
|    snd_value = (*buff_cnt + 128) * 8;*/
|    *sound_chip = snd_out[snd_value];
|    *sound_chip = snd_out[snd_value + 4];
|
|    buff_cnt++;
|}

[...]
|main () {
[...]
|    Xbtimer (0, 7, 0, &do_snd);
[...]
|}

Your do_snd routine must be coded as an interrupt routine; that is, it
must end in a rte instruction; not only that, it must also mark
(clear?) a bit in the I/O range (sorry, have to look this up),
otherwise the system will think the interrupt is still pending
(actually, I think the peripheral chip is thinking this 8-). For an
example, take a peek into the ROMs to see how the system timer (timer
C) is coded (the I/O address I mentioned is different, but you'll get
the picture).

Alternatively, you can do it the easy way, forget about programming it
yourself and use the Xbios function Dosound() (but I assume there is a
special reason you cannot use this?).

And if you experience spurious interrupts using Xbtimer(), I got a
solution for this too (8-).

Cheers,
        Leo.

P.S. The 4 bombs (illegal instruction) confirm that a wild jump was
taken; this is because your code pops a return address off the stack
where there is a status word and a return address.

leo@ehviea.ine.philips.nl (Leo de Wit) (09/27/90)

In article <891@ehviea.ine.philips.nl> leo@ehviea.UUCP (Leo de Wit) writes:
|Your do_snd routine must be coded as an interrupt routine; that is, it
|must end in a rte instruction; not only that, it must also mark
|(clear?) a bit in the I/O range (sorry, have to look this up),

The sequence for timer A is (I looked it up):

my_timint
    ... do your stuff here ...
    bclr #5,$fffa0f
    rte

The documentation I have says that in order to clear a bit in a MFP
register, you have to write 1's into the other bits; this is in
contradiction with the above (which is analogous to the ROM code). The
bclr instruction to memory does both read and write a byte (correct me
if I'm wrong), but this does not guarantee that 1's are written to the
other bits (they must have been there already).

The explanation for this instruction was something like: the 68K
notifies the MFP that interrupts may be delivered; I wonder what
happens if one is being delivered before the rte (this is theoretically
possible, and my spurious interrupt problem seems to indicate
practically too).

    Leo.

dac@ukc.ac.uk (David Clear) (09/28/90)

In article <892@ehviea.ine.philips.nl> leo@ehviea.UUCP (Leo de Wit) writes:
>
>The sequence for timer A is (I looked it up):
>
>my_timint
>    ... do your stuff here ...
>    bclr #5,$fffa0f
>    rte
>

This sequence is correct - I have had no problems using it. I've never
seen any documents to suggest that 1s have to be written into the other
bits of the "interrupt in service" register. As I understand it, when
the MFP gets an interrupt, it will not signal the 68000 unless it is
of a higher priority than those already in service. Similarly, if it
does signal the 68000, the 68000 will not service it unless the IPL has
been moved back below 6 (?).

So, for general purpose, multi-level interrupts, the order of events
should be:

int:	move.w	#$2300,sr	/* Also enables VBI, #$2400, disables VBI */
	.. your own thang
	bclr.b	#n,MFP_ISR[AB]
	rte

On the subject of sampled sound drivers, as I understand it, to plug a
value in a volume register you first do a register select and then you
plug the data in. This can be done with a movep.w, right? Wanting to speed
up his sample player, a friend of mine discovered that the sound chip only
decoded the lowest bit of the address - so the CONTROL/DATA register pair
were replicated throughout the address space of the chip. This means that
rather than doing three movep.w instructions, you can get away with
a movep.l and a movep.w. Of course, this can't be guaranteed to work.

Dave.
-- 
% cc life.c                      | David Clear dac@ukc.ac.uk +44 227 764000x7592
% a.out                          | Local Area Networks, Computing Laboratory,
Segmentation fault (core dumped) | University of Kent, Canterbury, England.
>>> Kernel R0M. His Mission: To rid the world of wobbly ZX-81 16K RAM packs. <<<

leo@ehviea.ine.philips.nl (Leo de Wit) (09/30/90)

In article <5558@harrier.ukc.ac.uk> dac@ukc.ac.uk (David Clear) writes:
|In article <892@ehviea.ine.philips.nl> leo@ehviea.UUCP (Leo de Wit) writes:
|>
|>The sequence for timer A is (I looked it up):
|>
|>my_timint
|>    ... do your stuff here ...
|>    bclr #5,$fffa0f
|>    rte
|>
|
|This sequence is correct - I have had no problems using it. I've never
|seen any documents to suggest that 1s have to be written into the other
|bits of the "interrupt in service" register.

I'll quote my source (the Atari Profi buch, Dutch translation):

- Nadat de lopende interrupt werd afgehandeld, dient de CPU het bijbehorende
bit in het ISRA of ISRB "0" te maken. Een bit wordt "0" door in alle bits van
het register, behalve het bedoelde bit, een "1" te schrijven. Doordat bits in
het Interrupt Pending Register softwarematig alleen "0" en niet "1" gemaakt
kunnen worden, wordt de inhoud van het register door het schrijven van de
"enen" niet gewijzigd.

(Original text to help understand a bit what it is like to read texts
that are not written in your native language).

|                                             As I understand it, when
|the MFP gets an interrupt, it will not signal the 68000 unless it is
|of a higher priority than those already in service.

The MFP can't even GET an interrupt that is of equal or lower priority
than that currently being serviced, let alone notify the 68000 (by
interrupting it); however such an interrupt remains pending and will
be serviced when the current one terminates (if this is within the
maximum amount of time it may be pending, the so-called interrupt
latency).

|                                                    Similarly, if it
|does signal the 68000, the 68000 will not service it unless the IPL has
|been moved back below 6 (?).

The same holds here; the interrupt will be serviced when the IPL has
been moved back below 6 (and if it is still pending then).

|
|So, for general purpose, multi-level interrupts, the order of events
|should be:
|
|int:	move.w	#$2300,sr	/* Also enables VBI, #$2400, disables VBI */

Sorry, no. The whole purpose of having multi-level interrupts is that
an higher level interrupt won't be interrupted by a lower level one;
this just defeats it. Also never plainly put a value in sr, OR it in or
AND it out.

Sorry to be such a nit-picker, Dave. Feel free to counter-attack 8-).

    Leo.