In this short lesson on SPDOS, I am going to reveal one of the many ways that SPDOS can be used by assembly programmers. This is not to imply that if you are not an assembly programmer, you cannot use this article or the program herein contained. To the contrary, you may find it very useful.
The SPDOS system call is one that checks for a filename’s existance on a disk. If you try to load a file that is not on your disk, SPDOS will create an error that cannot be trapped by the usual ON ERR GOTO. Try it! It can leave you with a locked up computer, forever trying to GOTO and always ERRing.
The only satisfactory solution to this situation is assembly. There is a hook code call that can be made to SPDOS. Some of you may be familiar with some of the hook routines if you are assembly programmers and were once SURFers. But let’s get on with the program.
According to the SPDOS Newsletter #2, there is a section of memory set aside by SPDOS to hold headers for files much the same way BASIC uses the DE and IX register pairs to hold the address of a file header and its length. The address of this header area is fixed at E420 hex or 58400. The map of this area is as follows:
E420 Filetype (one byte) E421 Filename (ten bytes) E42B Filelength (two bytes) E42D Start (two bytes) E42F Zeros (15 bytes)
The start bytes are the starting address of a byte file, the first line to execute for a BASIC program, or zeros for any other type of file. The filetype can be 0 for BASIC or 3 for a byte file. (I will leave the other types for another article or for you to figure out.)
The hook doesn’t work unless you tell it what you want to do. For the function in which we’re interested, the B register communicates the function desired. The hook we will use is:
B = 13 This checks for the file
on the disk
With these prerequisites met, we make the call to E490 (58512). That is where the hook code is. Now we go for the program.
ADDR CODE MNEMONIC COMMENTS
F400 2A5D5C MAIN LD HL,(chad) get BASIC pointer
F403 7E FIND LD A,(HL) get the character there
F404 FE24 CP 24 is it "$"?
F406 2817 JR Z,VSTR yes, jump to $var evaluator
F408 FE22 CP 22 is it """?
F40A 23 INC HL point to next char
F40B 20F6 JR NZ,FIND not """, loop
F40D E5 PUSH HL save pointer
F40E 01FF0A LD BC,0AFF set counters
F411 7E GETC LD A,(HL) get char of string
F412 0C INC C increment count
F413 23 INC HL point to next char
F414 FE22 CP 22 is this one """?
F416 2802 JR Z,DOLN yes, jump to length evaluator
F418 10F7 DJNZ,GETC no, loop to max of ten char
F41A 0600 DOLN LD B,00 clear counter
F41C E1 POP HL get start of string
F41D 185F JR DOHD jump to do header
F41F 2B VSTR DEC HL point back one char
F420 7E LD A,(HL) get var name
F421 E6DF AND DF convert it to upper case
F423 F5 PUSH AF save it on stack
F424 2A4B5C LD HL,(vars) get start of vars area
F427 ED5B595C LOOP LD DE,(elin) get end of vars area
F42B 1B DEC DE point back one
F42C F1 POP AF restore converted char
F42D E5 PUSH HL save current pointer
F42E A7 AND A clear carry flag
F42F ED52 SBC HL,DE check for end of vars
F431 E1 POP HL restore current pointer
F432 2874 JR Z,ERR2 end of vars, quit to ERR2
F434 BE CP (HL) is this the string we need?
F435 F5 PUSH AF stack the converted char
F436 2831 JR Z,DOIT yes, we got it so go do it
F438 7E LD A,(HL) not right so lets check for type
F439 CB7F BIT 7,A is bit 7 set?
F43B 2012 JR NZ,BIT6 yes, test more
F43D CB6F BIT 5,A is bit 5 set?
F43F 2806 JR Z,ARAY no, ths is an array
F441 110600 #VAR LD DE,0006 this is simple # var
F444 19 ADD HL,DE so point past it and
F445 18E0 JR F427 loop back
F447 23 ARAY INC HL point to next byte
F448 4E LD C,(HL) get the
F449 23 INC HL byte count
F44A 46 LD B,(HL) into BC
F44B 23 INC HL point past byte count
F44C 09 ADD HL,BC point past var
F44D 18D8 JR LOOP loop back
F44F CB77 BIT6 BIT 6,A is bit 6 set?
F451 200C JR NZ,MORE no, test some more
F453 CB6F BIT 5,A is bit 5 set?
F455 28F0 JR Z,ARAY no, test more
F457 23 NAME INC HL point past this byte
F458 7E LD A,(HL) get byte
F459 CB7F BIT 7,A is bit seven set?
F45B 20E4 JR NZ,#VAR yes, go increment past value
F45D 18F8 JR NAME loop
F45F CB6F MORE BIT 5,A is bit 5 set?
F461 28E4 JR Z,ARAY no, then it is an array
F463 011300 LD BC,0013 increment
F466 09 ADD HL,BC by 19 bytes
F467 18BE JR LOOP and loop
F469 23 DOIT INC HL get byte
F46A 4E LD C,(HL) count for this
F46B 23 INC HL variable
F46C 46 LD B,(HL) into registers
F46D 23 INC HL point past this count
F46E F1 POP AF restore char
F46F C5 PUSH BC copy count to
F470 D1 POP DE second register pair
F471 E5 PUSH HL stack pointer
F472 210A00 LD HL,000A set to max length
F475 A7 AND A clear carry
F476 ED52 SBC HL,DE test length
F478 E1 POP HL restore pointer
F479 3003 JR NC,DOHD if not too long then do header
F47B 010A00 LD BC,000A else, set length to ten
F47E 1121E4 DOHD LD DE,E421 point to disk header area
F481 79 LD A,C save count
F482 EDB0 LDIR move string
F484 67 LD H,A put count in register
F485 3E0A LD A,0A set limit
F487 94 SUB H get number of spaces needed
F488 2807 JR Z,SKIP skip if none needed
F48A 47 LD B,A transfer count again
F48B 3E20 LD A,20 put space in accumulator
F48D 12 ADSP LD (DE),A add it to end of string
F48E 13 INC DE increment string pointer
F48F 10FC DJNZ,ADSP repeat till count expires
F491 3E00 SKIP LD A,00 clear accumulator
F493 62 LD H,D transfer pointer
F494 6B LD L,E to HL
F495 13 INC DE point to byte past pointer
F496 77 LD (HL),A clear the first byte (prime pump
F497 011100 LD BC,0011 set counter
F49A EDB0 LDIR zero fill
F49C 0613 LD B,13 set request to check for file
F49E CD90E4 CALL HOOK check for file
F4A1 010100 LD BC,0001 set return to true value
F4A4 D8 RET C and return if file found
F4A5 0E00 LD C,00 else, set to false
F4A7 C9 RET and return
F4A8 CF ERR2 RST 08H do error
F4A9 01 ERROR 2 #2 report
F4A9 HOOK EQU E490 HOOK is 58512
This utility must exist somewhere in memory. A REM statement of 170 free bytes at the beginning of a BASIC program will do nicely but it can be put anywhere you want as it is relocatable.
This utility can handle simple string variable references or constants only. I did not intend to include TS function dispatcher in this discussion and, instead, I made the little lookup routine. It does not handle string array references!
Here is an example of a use for this routine.
10 REM "CHECK" IS A VARIABLE CONTAINING THE ADDRESS OF THE FIRST BYTE OF THE PROGRAM ABOVE
1000 IF USR CHECK THEN PRINT #4: LOAD "YOUR PROG"
1010 IF NOT USR CHECK THEN PRINT "YOUR PROG"; " NOT ON THIS DISK!"''"PUT RIGHT DISK IN DRIVE AND PRESS ENTER": PAUSE 0: GO TO 1000
Or try this:
10 REM "CHECK" IS AS ABOVE
1000 INPUT "ENTER TITLE OF FILE YOU DESIRE: ";LINE T$
1010 IF USR CHECK THEN PRINT #4: LOAD T$
1020 IF NOT USR CHECK THEN PRINT T$;" NOT ON THIS DISK!"''"PUT RIGHT DISK IN DRIVE AND PRESS ENTER": PAUSE 0: GO TO 1000
Remember, the first string reference after the call to CHECK must be the filename that you are seeking as in lines:
1000 and 1010 in first program fragment
1010 and 1020 in second program fragment
Here is a little program in BASIC to load the code.
10 LET START = 62464: REM START can be any convenient address. 62464 is the start of a 1k buffer in SPDOS.
1000 FOR A = START TO START + 169: READ X: POKE A,X: NEXT A
9000 DATA 42, 93, 92, 126, 254, 36, 40, 23, 254, 34, 35, 32, 246, 229, 1, 255, 10, 126, 12, 35, 254, 34, 40, 2, 16, 247, 6, 0, 225, 24, 95, 43, 126, 230, 223, 245, 42, 75, 92, 237, 91, 89, 92, 27, 241, 229, 167, 237, 82, 225, 40, 116, 190, 245, 40, 49, 126, 203, 127, 32, 18, 203, 111, 40, 6, 17, 6, 0, 25, 24, 224, 35, 78, 35, 70, 35, 9, 24, 216, 203, 119, 32, 12, 203, 111, 40, 240, 35, 126, 203, 127, 32, 228, 24, 248, 203, 111, 40, 228, 1, 19, 0, 9, 24, 190, 35, 78, 35, 70, 35, 241, 197, 209, 229, 33, 10, 0, 167, 237, 82, 225, 48, 3, 1, 10, 0, 17, 33, 228, 121, 237, 176, 103, 62, 10, 148, 40, 7, 71, 62, 32, 18, 19, 16, 252, 62, 0, 98, 107, 19, 119, 1, 17, 0, 237, 176, 6, 19, 205, 144, 228, 1, 1, 0, 216, 14, 0, 201, 207, 1
Have fun with this and send your comments/questions to:
342 Trotter Court
Sanford, FL 32773
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.