The name list is a simple structure in SuperBasic. It holds the names of all procedures, variables, functions etc that have ever been used in this session at the QL. It is odd in that each name is preceeded by a BYTE defining its length as opposed to a word in the normal QDOS manner. This implies that names can be up to 255 characters long. There are no padding bytes to force even addresses in the name list either. Beware when accessing this area thet you only do byte sized operations !
The name list starts at the address BV_NLBAS(A6) to BV_NLP(A6) with BV_NLBAS(A6) being the lowest address and BV_NLP(A6) pointing to the first byte AFTER the last entry in the name list. As usual, the offsets you get from these basic variables are thenselves relative to A6 !
To explain further, Fetch the offsets from BV_NLBAS(A6) into A0. The address 0(A6,A0.L) is the start of the name list. Or, in code :
start move.l BV_NLBAS(a6),a0 lea.l 0(a6,a0.l),a0 move.b 0(a0),d0 ; D0 is now the size of the first entry ... ; More code here
Now A0 has the start of the name list, but beware of doing this in case SuperBasic gets moved. It is best to stay relative as in the following :
start move.l BV_NLBAS(a6),a0 move.b 0(a6,a0.l),d0 ; D0 is now the size of the first entry ... ; More code here
This is much safer.
The internal structure therefore looks like this :
+----------------+
BV_NLP(A6)--------->| Random Garbage |
|----------------|
| 9 | FLP1_NAME |
|----------------|
| | | <--- Lots ommitted here !
|----------------|
BV_NLBAS(A6)------->| 5 | PRINT |
+----------------+
How is the name list useful to us in writing procedures and functions? consider these commands :
OPEN_IN #3,'ram1_test_file' OPEN_IN #3,ram1_test_file
What is the difference? In the first case, the parameter for the filename is a quoted string and internally, the OPEN_IN routine can fetch it using CA_GTSTR as described above. In the second, it will fail if it uses CA_GTSTR because without quotes, the parameter is a NAME and not a STRING.
The procedure/function writer must check for a string parameter or a name parameter and treat each accordingly. How is this done? - use the name table type byte as described above.
In the procedure or function, process a name as follows :
Assuming that A3 points to the name table entry for this parameter, then if bits 0 to 4 of 1(a6,a3.l) is zero then we have a name and not a variable. We must copy the name to the stack (or to the appropriate buffer) making sure that the size byte in the name list is converted to a size word on the stack or in the buffer. The following fragment of code gives the general idea :
name_test move.b 1(a6,a3.l),d0 andi.b #$0f,d0 bne.s not_name ; ; Must be a name so process accordingly here ; not_name ; Process a string here
So whan a name is detected we have to make space for it, copy the size BYTE from from the name list into the size WORD in our string buffer (which has to be word aligned on an even address) and then copy the individual bytes from the name list to the string buffer. At this point we are in the same situation we would be in had we fetched a string using CA_GTSTR and copied it from the maths stack into our buffer. Simple? (In my famous DJToolkit extensions I never actually bothered doing this and I simply fetched all filenames etc as strings - if the user supplied a name instead, the procedure or function complained. So far no- one has requested that it be updated to allow names !)
How about a bit of fun - lets write a procedure that prints the entire name list to a channel. It shall be called nlist and it shall take one parameter which is the channel number - this will default to #1 if no parameter supplied.
bv_nlbas equ $20 ; Base of name list bv_nlp equ $24 ; End of name list bv_chbas equ $30 ; Base of channel table bv_chp equ $34 ; End of channel table err_no equ -6 ; Channel not open error err_bp equ -15 ; Bad parameter error start lea define,a1 ; Pointer to the definition table move.w BP_INIT,a2 ; The vector we need to use (= $110) jsr (a2) ; Call the vectored routine rts ; And return any errors back to SuperBasic *----------------------------------------------------------------------------- * Definition table for one new procedure *----------------------------------------------------------------------------- define dc.w 1 ; 1 new procedure dc.w nlist-* ; Offset to procedure dc.b 5,'NLIST' ; Size and name dc.w 0 ; End of procedures dc.w 0 ; Number of functions dc.w 0 ; End of functions *----------------------------------------------------------------------------- * Procedure NLIST starts here ... * * Check for one or zero parameters - if not then error exit *----------------------------------------------------------------------------- nlist cmpa.l a3,a5 ; No parameters? beq.s nl_none ; Yes, skip move.l a5,d0 ; Last parameter pointer sub.l a3,d0 ; minus first cmpi.w #8,d0 ; One parameter? beq.s got_one ; Yes bad_par moveq #-15,d0 error_exit rts *----------------------------------------------------------------------------- * If one parameter, must have a hash else error exit *----------------------------------------------------------------------------- got_one btst #7,1(a6,a3.l) ; check for a hash beq.s bad_par ; Not got one *----------------------------------------------------------------------------- * It has a hash - fetch the channel id. If this fails, error exit. *----------------------------------------------------------------------------- get_one move.w ca_gtint,a2 ; Vector for word integers jsr (a2) ; Fetch ! tst.l d0 ; Ok? bne.s error_exit ; No, bale out cmpi.w #1,d3 ; One only? bne.s error_exit ; No, bale out move.w 0(a6,a1.l),d0 ; Fetch channel number addq.l #2,a1 ; Tidy stack tst.w d0 ; Set flags blt.s bad_par ; Negative is a bad channel id bra.s chan_ok ; skip default channel handling *----------------------------------------------------------------------------- * No parameters supplied - default channel number to #1 *----------------------------------------------------------------------------- nl_none moveq #1,d0 ; Default to channel #1 chan_ok bsr.s channel_id ; convert to channel id in A0 bne.s error_exit ; Oops ! *----------------------------------------------------------------------------- * Fetch the start of the name list from BV_NLBAS(A6). The result of this is * an offset from A6 to where the namelist actually starts. *----------------------------------------------------------------------------- move.l bv_nlbas(a6),a3 ; Start of name list (relative a6 !) *----------------------------------------------------------------------------- * Our main loop starts here. We test to see if we are finished and if not * copy the (next) name to the buffer formatting it as a QDOS string. * D3 is preserved inside the loop, so set it once just before the loop starts. *----------------------------------------------------------------------------- moveq #-1,d3 ; Timeout for the channel nl_loop cmpa.l bv_nlp(a6),a3 ; Are we done yet? (Compare offsets) bge.s nl_done ; Yes moveq #io_sstrg,d0 ; Print some bytes please move.b 0(a6,a3.l),d2 ; Counter byte from name list ext.w d2 ; Needs to be word sized for IO_SSTRG lea 1(a6,a3.l),a1 ; Start of bytes to print adda.w d2,a3 ; Adjust upwards to end of bytes addq.l #1,a3 ; And point at the next size byte trap #3 ; Print the name (preserves A0, A3 and D3) tst.l d0 ; Ok? bne.s error_exit ; Oops - failed nl_nl moveq #io_sbyte,d0 ; Code for 'send one byte' moveq #10,d1 ; Newline character trap #3 ; Print newline (preserves A0, A3 and D3) tst.l d0 ; Ok? bne.s error_exit ; Oops - failed bra.s nl_loop ; Lets go round again ! *----------------------------------------------------------------------------- * If there is no more to do, return to SuperBasic. *----------------------------------------------------------------------------- nl_done moveq #0,d0 ; No errors rts ; Exit to SuperBasic *----------------------------------------------------------------------------- * Copy the above code for the CHANNEL_ID subroutine to here as it is required. * I am not printing it here because it is a simple duplication and we need to * save paper in the magazine ! *----------------------------------------------------------------------------- channel_id ...
Save the file, assemble, fix typing errors and test - super stuff this eh?
When this procedure runs, you can see all the internal names like PRINT, CLOSE etc and also all your own stuff like NLIST, GREEN, RED etc and also any filenames that you have used without quotes around them. These are names like anything else.
if you try the following :
open_new #3,ram1_test nlist #3 close #3
then load ram1_test into your editor (or copy to scr_), the last entry in the name list will be ram1_test - because you didn't use quotes. If you now try :
open_new #3,"ram1_test_again" nlist #3 close #3
This time, ram1_test_again will NOT be in the list because it is not a name, simply a string. This routine can be used to get a list of all procedures, functions, names etc that are loaded into your QL.