| Introduction | Meetings | Members | Magazine | Articles | Programs |
SQLUG
SCOTTISH QL USERS GROUP
ASSEMBLY LANGUAGE PROGRAMMING Pt.2
GEORGE GWILT with JOHN SADLER
We are now at last. going to explain how to add procedurec and functions to SuperBASIC. For the first time this will involve using the operating system, QDOS.
Our demonstration program consists of only one routine which is designed to alter the name of a disk. The first part of this, which is given below, describes its action in more detail
A program to extend SuperBASIC
must always comprise two sections. the first section causes the second to
be linked into the BASIC name table and name list.
LINKING
The first section consists of only three lines of code and
four defining a table. There are two ways of using QDOS, by the TRAP
instruction and by using a Vector, which is how we link in our procedure.
We will come to TRAPs later in the program so we concentrate now on
Vectors. Each Vector is a word long address pointing to a routine in
QDOS. The Vectors are located at absolute addresses
$C0 to $12A. For all the Vectors in
which we will be interested the word must be put into an address register
after which you would normally issue a JSR instruction
to it, for the vectors are all subroutines. We will activate our program
by CALLing it (or using LRESPR which
performs an LBYTES followed by a CALL).
In either case, as you will remember, we should end the CALLed
code with DO set to zero and then RTS.
In the case of BP_INIT there are no errors so all we
need is RTS. Since
JSR
RTS
can be shortened to
JMP
that is what our program does.
As with all TRAPs and calls to Vectors we have to set some registers. In our case this consists of setting A1 pointing to a list of relevant information about the procedures and functions we want to link in.
| Item | Length | |
| Number of procedures | Word |
Then, for each procedure:
| Relative pointer to routine | Word | |
| Length of procedure name (n) | Byte | |
| ASCII name n | Bytes |
To mark the end of the procedure list:
| 0 | Word |
The whole of this is then repeated for functions. If, as in our case, there are no functions, we simply have two zero words.
LOADING
The procedures and functions are loaded by LBYTEing the assembled program into a RESPRd area of RAM and CALLing it. If you have activated TK2 then you can simply LRESPR the binary file.
; DRENAM_ASM
; 6 June 1996
;
; DRENAM is a BASIC procedure set a disk name.
; The name on FLPn is set by "DRENAM n,name$"
; where "n" is the drive number and "name$" is
; the name to be set.
;
; "n" must be one of 1 to 8 if present. The default value is 1.
; "name$" must be a string of up to 10 characters.
; PWID 128 BV_RIP EQU $58
;
; **************************
; * Code to set DRENAM as *
; * a BASIC procedure *
; **************************
;
LEA DEFINE,A1
MOVE.W BP_INIT,A2
JMP (A2)
;
DEFINE DC.W 1 one procedure
DC.W DRN-* offset to code
DC.B 6,"DRENAM"
DC.W 0,0,0 mark end of procedures and no
; functions
Last time we examined how to link in new procedures and functions to SuperBASlC. This month we deal with the parameter associated with DRENAM, leaving to later the question of how we actually alter the Disks name.
First of all we should understand that the program starting at DRN is used as a subroutine by SuperBASIC. Juct before DRN is called the parameters have been processed and their type has been put onto SuperBASICs Name Table. As you can see from the section of program given below A3 and A5 point to the position in the Table.
Having satisfied ourcelves that the parameters are acceptabld we use two of the vectored routines in QDOS to fetch them an put them on the Maths Stack for our use. The parameters may be "n,name$" or just "name$. We can tell which by examining the type of the first parameter. If "n" has to be brought in we take it off the Maths Stack and put it in D7. being careful to adjust the value of the pointer to the Maths Stack.
Nows the time to discuss:
1. Name Table, 2. Maths Stack, 3. QDOS vectored routines.
Name Table
Each entry in the Name Table is eight bytes long. The first word indicates type'. In fact we are only interested here in the low byte of this first word which has the meanings shown;
Bit 7 is set if the parameter is preceded by # (hash).
| Separator after the parameter | Type of parameter | |
| Bits 4 to 6 | Bits 0 to 3 | |
| Value | Meaning | |
| 0 | None | NULL |
| 1 | , | String |
| 2 | ; | Floating point |
| 3 | \ | Integer |
| 4 | ! | - |
| 5 | TO | - |
Maths Stack
The Math. Stack is one of the areas used by SuperBASIC. The pointer to this stack, which is relative to A6. is held in BV_RIP(A6). The stack itself is upside down, just like the user stack - the fuller the stack, the lower the value of the pointer.
QDOS Vectored Utilities
Various routines in QDOS are made available to the uger by means of vectors, wordg from $C0 onwards whose value points to the routine itself thus enabling upward compatibility with different versions of QDOS.
We are here interested in CA_GTINT ($112) and CA_GTSTR (S116). These will place the values of all the parameters in the Name Table between A3 and A5 (relative to A6) on the Maths Stack. On exit D3.W will contain the number of parameters fetched and Al will contain the updated value of the pointer to the Maths Stack. D0.L will contain any error code and the condition code will be set.
; *********************
; * Code to implement *
; * DRENAM *
; *********************
;
; At this point A3 -> 1st parameter in the Name Table (rel to A6)
; A5 -> end of last parameter "
;
DRN MOVE.B 1(A3,A6.L),D0 1st parameter type
MOVEQ #"1",D7 set default drive number
MOVE.B D0,D1
CMPI.B #1,D0
BEQ.S DRN_1 just "name$"
ANDI.B #$FE,D1
CMPI.B #$12,D1
BNE BAD_PAR ----> not "n,"
MOVEA.L A5,A4 keep pointer to end
LEA 8(A3),A5 arrange to bring in 1 parameter
CMPA.L A3,A4
BLE BAD_PAR ----> too few parameters
MOVEA.W CA_GTINT,A0
JSR (A0) 1st parameter to maths stack
BNE EXIT ---->
ADDQ.L #2,BV_RIP(A6) reset stack
MOVE.W (A1,A6.L),D7 "n" to D7.W
BEQ BAD_PAR ---->
CMPI.B #8,D7
BGT BAD_PAR ---->
ADDI.W #"0",D7 set to ASCII
MOVEA.L A5,A3 point to next parameter
MOVEA.L A4,A5 restore pointer to end
CMPI.B #1,1(A3,A6.L)
BNE BAD_PAR ----> not a string
DRN_1 MOVE.L A5,D0
SUB.L A3,D0
CMPI.L #8,D0 there must be just ..
BNE BAD_PAR ----> .. one parameter
MOVEA.W CA_GTSTR,A0
JSR (A0) get "name$" to stack
BNE EXIT ---->
MOVEA.L A1,A4 keep maths pointer
CMPI.W #10,(A1,A6.L)
BGT BIG_NME ----> "name$" is too long
LEA FLE,A0 point to file name
MOVE.W #5,(A0) set length for FLPn_
MOVE.B D7,5(A0) set drive number "n"
MOVEQ #-1,D1 this job
MOVEQ #4,D3 open directory
MOVEQ #IO_OPEN,D0 (does not use A1)
TRAP #2
TST.L D0
BNE EXIT ---->
Now we can finish the explanation of DRENAM. the program to alter a floppy disks name. If you remember last time we left off with the drive number (1 to 8) in register D7 and the new name on the Maths stack.
The disks IO-byte name starts at byte 4 of the 1st sector of the disk. We aim to read that sector directly. push in the new name and rewrite the sector. The way to open a channel for accessing a disk directly is of the form
OPEN#3, "FLP1_*Dxy"
in SuperBASIC. It is much the same in assembly language. The values of x and y depend on the length of sectors and density of disk (DD, HD or ED). To find the values of x and y needed we examine the extended information. This is obtained by use of Trap #3 with D0 = $4F, and A0 = channel ID. The channel opened can be to any file on the disk. But what if there are no files? There must always be a directory even if empty, so we first open a channel to the directory of our disk to get our ID.
You may have noticed that the description just given is in the reverse order of the finished program. Thats what programming is like! In fact there is one more little thing to be done before we start opening files and that is to check the length of the name. It would definitely not be a good idea to try and insert a name longer than the allowed 10 bytes.
This final section of program contains six different calls to QDOS, two via TRAP #2 and four via TRAP #3. All but one of these is described in the books by Pennel and Dickens. The odd one out is a new TRAP #3 call with D0 set to IOF_XINF (equivalent to $4F). This TRAP which comes with Toolkit II gets eXtended INFormation.
| Call parameters | Return parameters | |||
| D1 | 0 | D1 preserved | ||
| D2 | D2 preserved | |||
| D3.W | timeout | D3 preserved | ||
| A0 | channel ID | A0 preserved | ||
| A1 | pointer to buffer | A1 preserved |
The type and position of the relevant information set in the buffer is described in the IOI _xxxx EQUates at the head of the program.
The method of deciding whether a disk is DD, HD or ED is to examine the total number of bytes. If this number is less than 1440 512-byte sections it must be DD otherwise if less than 2880 section it is HD else it is ED.
The remainder should be obvious from the program and its comments
MOVEQ #-1,D3 timeout
LEA SECT,A1 (space needed for medium name)
MOVEQ #FS_MDINF,D0 (D0-1, A1-3 used)
TRAP #3
TST.L D0
BNE ER1 ---->
MOVEQ #IO_CLOSE,D0
TRAP #2 close directory
MOVE.W #"D2",D7 prepare to open disk
CMPI.W #1440,D1
BLE.S DRN_3 DD disk
CMPI.W #2880,D1
BLE.S DRN_4 HD disk
MOVE.W #"E4",D7
BRA.S DRN_3
DRN_4 MOVE.W #"H2",D7
DRN_3 LEA FLE,A0
MOVE.W #9,(A0) set length of file name
MOVE.B D7,9(A0)
LSR.W #8,D7 get next byte
MOVE.B D7,10(A0)
MOVEQ #-1,D1 this job
MOVEQ #0,D3 type of open
MOVEQ #IO_OPEN,D0
TRAP #2
TST.L D0
BNE.S EXIT ---->
BSR.S SET_POS set file position to 1st sector
MOVE.W #512,D2 default length of sector
MOVE.B FLE+9,D0 get real length code
CMPI.B #"2",D0
BEQ.S DRN_5 it is 512
LSL.W #2,D2 set 2048
DRN_5 MOVEQ #-1,D3 timeout
LEA SECT,A1 space for sector info
MOVEQ #IO_FSTRG,D0
TRAP #3
MOVEA.L A4,A1 reset pointer to stack
BSR ER1 reset BV_RIP
MOVE.W (A1,A6.L),D0 length of "name$"
ADDQ.L #2,A1
MOVEQ #10,D7
SUB.W D0,D7 number of spaces needed
LEA 4+SECT,A2 point to name in sector
BRA.S DRN_6
DRN_7 MOVE.B (A1,A6.L),(A2)+ put "name$" to sector
ADDQ.L #1,A1 step maths stack
DRN_6 DBF D0,DRN_7
BRA.S DRN_8 now pad with spaces
DRN_9 MOVE.B #32,(A2)+
DRN_8 DBF D7,DRN_9
BSR.S SET_POS
LEA SECT,A1
MOVEQ #IO_SSTRG,D0
TRAP #3 write back sector
TST.L D0
BNE.S EXIT ---->
MOVEQ #IO_CLOSE,D0
TRAP #2 close file
EXIT RTS D0.L is set to 0 or the
; error code
BAD_PAR MOVEQ #-15,D0
BRA.S EXIT
;
; SET_POS sets file to first sector
;
SET_POS MOVEQ #1,D1 position @ 1st sector
MOVEQ #-1,D3 timeout
MOVEQ #FS_POSAB,D0
TRAP #3
TST.L D0
BEQ.S EXIT1 OK
ADDQ.L #4,A7 return with error code
EXIT1 RTS
;
BIG_NME MOVEQ #-15,D0
ER1 MOVE.W (A1,A6.L),D1 length of name
ADDQ.L #3,D1 2 for length & 1 for rounding
BCLR #0,D1 make even
EXT.L D1
ADD.L D1,BV_RIP(A6) reset maths pointer
RTS
;
FLE DC.W 5
DC.B "FLP1_*D2D"
SECT DS.W 1024 enough space for ED disk
| Introduction | Meetings | Members | Magazine | Articles | Programs |