[comp.sys.atari.st] Midi problem revisted

UUCJEFF@ECNCDC.BITNET (06/16/88)

A few weeks ago a reader had a problem with outputing several midi bytes.
I think he was trying to use the Xbtimer.  I have later found out courtesy
of Stefan Daystrom of Hybrid Arts that you cannot have any TOS, BIOS, or
XBIOS calls inside an interrupt.  You have to do whatever it is you want without
the calls, this is due to the re-entrancy characteristics of the ST.
Therefore, you cannot use Bconstat or Midiws from inside and Xbtimer interrupt.

I was having the same problem, I was thinking it might have been the stack
size, but replaced it with the same code that the system call would have done,
(modifying it to fit the routine) and it works perfectly.  I even save
time by avoiding all the stack bookkeeping by the subroutine call.
I got the code of those system calls from Abacus, you can also get them
from the developer's kit.

While this fact problem was explained somewhere in the docs, it would
have been nice if they would have mentioned this in the docs of the
Xbtimer call.

Jeff, UUCJEFF@ECNCDC.BITNET

leo@philmds.UUCP (Leo de Wit) (06/20/88)

In article <8806151931.AA05945@ucbvax.Berkeley.EDU> UUCJEFF@ECNCDC.BITNET writes:
>A few weeks ago a reader had a problem with outputing several midi bytes.
>I think he was trying to use the Xbtimer.  I have later found out courtesy
>of Stefan Daystrom of Hybrid Arts that you cannot have any TOS, BIOS, or
>XBIOS calls inside an interrupt.  You have to do whatever it is you want without
>the calls, this is due to the re-entrancy characteristics of the ST.
>Therefore, you cannot use Bconstat or Midiws from inside and Xbtimer interrupt.
>
    [14 lines deleted, says vi 8-]

    I've tried to figure out the characteristics of the TOS, BIOS and
XBIOS calls as far as re-entrancy is conceirned and I came to the
following conclusions (correct me if I'm wrong, I've just disassembled
some code):

    1) TOS calls are not re-entrant whatsoever. This is due to the fact
that the current registers are saved on the basepage of the current
process, and restored on exit from the call. Any TOS call from within a
TOS call would overwrite the saved registers. Also the stack pointer is
set to point to a definite area (except in the case of the Super() call).

    2) BIOS and XBIOS calls (they have the same characteristics) ARE in
fact re-entrant, be it not multitasking re-entrant. The registers D3-D7
and A3-A7 are saved on a separate stack pointed to by SAVPTR ($4A2).
This pointer is updated when the registers have been saved, and also
when they have been restored (so you may see SAVPTR as a kind of stack
pointer).  The problem with this scheme is that the BIOS (XBIOS) call
can get interrupted when the registers are being stored and SAVPTR is
not yet updated.  When the interrupt routine now uses BIOS it gets the
old SAVPTR and will overwrite the registers on that stack. A better
approach for the save area would have been to FIRST update SAVPTR
(thereby claiming the area) and THEN save the registers.

    3) It is - More or Less - possible to use (X)BIOS even from within
interrupt code. After some trying it worked (it was in fact your
article that stimulated me). So first the More (I omitted a test to
prevent re-entrance of this routine, in this case necessary but it
could be done more general, permitting re-entrance):

#include <portab.h>

#define SAVPTR (*(WORD **)0x4A2)

static WORD savebuf[24];    /* large enough for a PC, a SR and 10 regs */

intrupt()  /* e.g. a vertical blank interrupt routine */
{
    WORD *savep;

    savep = SAVPTR;                      /* save the pointer */
    SAVPTR = savebuf + sizeof(savebuf);  /* point to tail of buffer */

    /* here your code using (X)BIOS calls */

    SAVPTR = savep;                      /* restore the pointer */
}

    And now the Less:  Because the code of the BIOS/XBIOS depends rather
heavy on static/external data you can get into trouble. For instance if
your interrupt routine uses Bconout(2,..) and your application uses
Bconout(2,..) you will get a mess (unless you are able to restore all
the global/static data altered).
In the case of Midiws() it is unlikely that your program uses that
XBIOS call synchronous and asynchronous; and it is unlikely (but not
inconceivable) that Midiws() shares updatable data with other calls
(didn't check this one).  The Midi user should give it a try, I think,
before reinventing the wheel.
But beware of stack sizes: don't overdo it with local variables, or use
a separate stack area. There could be less space on the (supervisor)
stack than you think; the reason why it didn't work the first time in
my case.

     Leo.