However with the help of a list of C calls, some free time due to holidays and a trawl through the operating system, here is a reasonably full guide on how to control data in a friendly and "clean" kind of way. It should be accurate because I've spent a fair amount of time ripping the code apart, but I can't be held responsible for any errors.
This is not a course on programming the DSP;
it is basically a reference on how the XBIOS works. It
is becoming more and more important, as variations on the
standard Falcon emerge, to write system-friendly DSP code. In addition,
it is also very useful to find out just how Atari programmed
the wee beastie...
- The first is to use the Host Port, which is a set of registers specifically designed for programmers to send data at high speed from the Host Processor (in our case the 68030 chip) to the DSP; this can be done in the normal course of program execution, or can be controlled via the use of programmable 68030 interrupts.
- The other is the use of the DMA (Direct Memory Access) which automatically sends blocks of data to and from the DSP without any specific intervention from the Host Processor DMA uses aspects of the CODEC sound chip to control flow of data and is therefore closely linked with the sound libraries.
Sound programming is beyond the scope of this article, so I don't propose to go into it here unless specifically necessary. This could well be the subject of a further article though.
The layout of the Host Port registers as seen from the 68030 are as
|$FFA200||Interrupt Control Register. Controls methods of transferring data to/from the DSP, whether using the DMA or the Host Port, including the use of interrupts to do this|
|$FFA201||Command Vector Register. Used to send special commands to the DSP to force interrupts; often used by DSPdebuggers or special interrupts such as sound generation|
|$FFA202||Interrupt Status Register. Gives the current status of DSP transmission, including interrupts it uses.|
|$FFA203||Interrupt Vector Register. Controls which vector the 68030 will use for interrupt-driven host port data transfer.|
|$FFA204||Unused. (Always reads as zero, but does not cause bus error if read)|
|$FFA205||DSP-Word Hi. Used to send data to the DSP Host Port.|
|$FFA206||DSP-Word Mid. Used to send data to the DSP Host Port.|
|$FFA207||DSP-Word Low. Used to send data to the DSP Host Port.|
If we test Bit 0 and it is zero then we are not free to receive data; similarly if we test Bit 1 and it is zero then we are not free to transmit data.
For example, here is a piece of assembler code to send 512 bytes to the DSP (taken from the XBIOS):
; d0 = our counter
Lab08: BTST #1,$FFFFA202.W ; ready to transmit?
BEQ.S Lab08 ; bit 1 = 0? Not ready
MOVE.B (A0)+,$FFFFA205.W ; send hi byte
MOVE.B (A0)+,$FFFFA206.W ; send mid byte
MOVE.B (A0)+,$FFFFA207.W ; send lo byte
DBF D0,Lab08 ; loop
This is an over-simplified model of DSP transfer (in future issues of Maggie I hope to go into this further) but for now we can use the XBIOS to send programs to the DSP and execute them. All you need to know for now is that this is the main model for Host Port transfer.
Obviously some of these look a bit confusing so for the moment, but
for this article we'll look at some of the basic commands to send a program
to the DSP, and doing some general housekeeping:
C: short DspLock(void);
Operation: Attempts to lock the DSP so that it can not be used by other programs. The call returns the status of the locking BEFORE the call was made (ie. -1 if locked, 0 if unlocked) Hence 0 denotes a fully successful call, -1 semi-successful since the DSP is now locked anyway.
C: short DspUnlock(void);
Operation: Frees the flag denoting the DSP has been previously used.
Despite indications to the contrary, this call does *not* return a value, at least on my version of TOS! This is because the flag is always cleared, no matter whether the DSP has been reserved or not. This operation should be carried when execution of your DSP application has finished.
In practice for testing code, the above two operations can be ignored because the 'locked' flag is not checked when carrying out any other XBIOS commands (at least not in my version of TOS) NOTE that this 'lock' is purely a software lock - there is no feature built into the hardware to lock the DSP to one program. You are perfectly free to come along and access all the hardware directly, demo coders.
Now we have (hopefully) determined that the DSP is free for use, we can proceed to send it programs, routines or data. The XBIOS system calls seem to have been set up with two broad aims in mind:-
- To allow full access to the DSP without direct hardware manipulation, provided that the DSP code is correctly written; this includes interrupt driven host port access;
- To allow combinations of programs, routines and memory allocation routines, providing that they conform to certain standards.
void DspExecBoot(char *codeptr,long codesize,short ability);
This is the most basic way of sending code to the DSP. It resets the DSP completely and then transfers 512 DSP-Words of code to the bottom of DSP memory. It does this by:-
1. Setting bit 4 of the Port A to 0, and pausing for 1/100 of a second. Note that timer C must be operating normally for this code to work!
2. Setting the bit high momentarily, then low. This has the effect of resetting the DSP. The DSP's "bootstrap" load code comes into operation: it expects 512 dsp-words of code to be received via the normal host port registers. These are placed at address p:0 and execution restarts at p:0. Note that system vectors are positioned at the range p:1 to p:$3f, so the first instruction is invariably a 'jmp' command to the first line of your code.
Also note that in the following three calls, no status value is returned in D0 - the call is assumed to have worked correctly in all cases. In fact D0 will be set to -1 because it is used for a DBF loop, so it will appear to the unaware that the code has failed!
The code is expected to be stored in "unpadded" format: that is the first word (24 bits) of DSP code should occupy bytes 0, 1 and 2 of the data, the second word bytes 3,4 and 5 and so on.
If the code length ("codesize") is less than 512 words, the XBIOS pads out the data stream at the end by appending (512-codesize) dsp-words of zero value.
The value of "ability" is unused in my version of
TOS and has no effect.
Similar to ExecBoot, in that the DSP is reset and a piece of bootcode is sent but this time the bootcode is supplied by the XBIOS. It then jumps to a piece of code in high P memory (at around p:$7800) which expects a piece of unpadded DSP code in the following format:
DSP-WORD: destination memory
$000000 - P memory
$000001 - X memory
$000002 - Y memory
DSP-WORD: destination address in DSP memory bank
DSP-WORD: length of chunk of data in dsp words
DSP-WORDS: chunk of data of length given above
The length of the overall file of data is determined by the call to
the XBIOS itself. An incorrect value to this can result
in extra data being sent which overwrites DSP memory.
Once the data is sent, the XBIOS itself sends an extra chunk of code which installs a couple of DSP interrupt vectors. It then jumps to address P:0, so your code must include an instruction at P:0 which jumps to the start of your code. Also note that resetting the DSP rewrites all P memory below P:$200, so your code must sit above this address!
The 'ability' value can be left at zero for present; it seems to be used to determine the identity of DSP subprograms (see a future article for this?)
In addition to sending the lowest-level code yourself, you can also
utilise some DSP routines that are built in to the XBIOS. Here are
two which allow you to use LOD files instead of lower-level code.
This routine converts a LOD file produced by most DSP assemblers to
the format needed by the DspExecProg call above. "Filename"
is a pointer to the name of the file,
which is loaded from disk automatically during the
call (it cannot be stored in memory!) "Destaddr"
is the destination buffer which will hold the converted DSP code.
The length of the produced code, in
DSP words, is returned in D0.
short DspLoadProg(char *file, short ability, char *ptr);
(This call was incorrectly defined in the documentation I had) This routine is a combination of "LodToBinary" and "ExecProg." It loads in the named lodfile, converts it to the correct format (storing it in the buffer pointed to by 'ptr' and then sends the code to the DSP. If successful the code returns 0, else -1 if an error has occurred (eg. specified file not found)
These are the main calls to send programs to the DSP. In
the next article I'll take a closer look at how the operating system handles