7. Name List

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.