Mastering Sideways ROM & RAM - Module 07 - Osword ------------------------------------------------- The new Osbyte demonstrated in Module 6 used the X and Y registers to pass two parameters to a new routine. Osword is a similar concept to Osbyte except that parameters are passed in a parameter block the address of which is sent to the Osword routine in the X and Y registers. The low byte of the address is in X and the high byte in Y. Osword uses the same three consecutive zero page bytes as Osbyte to store the values of the A, X and Y registers. The accumulator value for the most recent Osword (or Osbyte) is stored in &EF. The X register value is stored in &F0 and the Y register value is stored in &F1. Oswords &00 to &0F are used by the MOS. Oswords &7D to &7F are used by the DFS, Oswords &70 to &77 and Oswords &10 to &17 are used by other software, and Oswords &E0 to &FF are passed to the user vector (USERV) at &200. You can define any other Osword in your SWR programs but it is wise to consult any lists of Oswords to make sure the number you intend to use is available. The example program in this module will define Osword 100 (&64). Using TRACE to find the service request for an unrecognised Osword involves a bit more typing than using it to find the service request for an unrecognised Osbyte. Load the object code generated by TRACE into SWR and press the Break key. Call Osword 100 using a dummy parameter block address with the trace active. You should get the response shown in figure 7.1 which shows that service call 8 is used and that the address of the parameter block is only available from &F0 and &F1 and not directly from the index registers. >A%=100:X%=100:Y%=100:CALL &FFF1 A=08 X=0F Y=00 Unrecognised Osword > Figure 7.1 Service trace for unrecognised Osword. ------------------------------------------------- Defining a new Osword is no more difficult than defining a new Osbyte but it more useful because you can pass more than two parameters to and from the new routine in the parameter block. Up to 256 new routines can share the same Osword number by using the a byte of the parameter block as an index on the available routines. For example, if the number stored in the first byte of the parameter block is n, your Osword interpreter could pass control to the (n+1)th routine. The outline Osword interpreter in figure 7.2 does not use this type of extended Osword. Oswords with numbers less than 128 have a 16-byte control block. Oswords 128 and higher have a control block up to 128 bytes, specified in the first two bytes of the control block. The first byte contains the number of bytes sent to the Osword, the second byte contains the number of bytes expected in return. .service PHA \ push accumulator CMP #8 \ is it unrecognised Osword? BEQ newosword \ branch if it is .exit PLA \ pull accumulator RTS \ return and give other ROMs the call .newosword LDA &EF \ load Osword number CMP #100 \ is it 100? BNE exit \ branch if not Osword 100 . . \ new routine goes in here . LDX &F0 \ make sure X and &F0 are identical LDY &F1 \ make sure Y and &F1 are indenical PLA \ pull A pushed by interpreter LDA #0 \ other ROMs ignore this call RTS \ return to MOS Figure 7.2 An Osword interpreter. --------------------------------- The service entry point of the outline interpreter in figure 7.2 will have a jump to the label ".service". On entering the interpreter the accumulator is pushed on the stack and then compared with 8 to see if the call is an unrecognised Osword. If it is not the accumulator is pulled off the stack and control is returned to the MOS. If the call is for an unrecognised Osword the number stored in &EF is loaded into the accumulator and compared with the number of the new Osword, in this example 100. If the unrecognised Osword number stored in &EF is not 100 the accumulator is pulled off the stack and control is returned to the MOS. If it is 100 then the new routine can be executed. The address of the parameter block can be read from &F0 (low byte) and &F1 (high byte). The parameter block must be in the I/O processor. After completing the new routine you should ensure that the index registers and their zero page copies are identical. In figure 7.2 it is assumed that the contents of &EF, &F0 and &F1 have not been altered. The accumulator was pushed on the stack by the interpreter and it is pulled back off to balance the stack. The accumulator is reset to zero to inform other ROMs that the service call has been recognised and control is returned to the MOS. On return the MOS copies the Osword number into the accumulator. Unlike Osbyte, Osword does not use &F0 and &F1 to write any results. If your new routines write any results these must be written in the parameter block. &EF, &F0 and &F1 should be restored before returning from Osword. The program OSWORD uses an interpreter similar to the one outlined in figure 7.2 to implement a PRINT TAB(X,Y) routine as Osword 100 (&64). This routine gives access to PRINT TAB in both BASIC and machine code. To use the new Osword you need to load the object code generated by the program OSWORD into SWR and press the Break key. Any program which uses the new routine must define a parameter block to contain the two byte address of a text string and the two bytes for the screen X and Y co-ordinates of the first character of the string. Because four bytes of information are needed for this routine an Osbyte could not be used. The progrm OSWDEMO shows how to set up the parameter block in Assembly language. 10 REM OSWDEMO 20 MODE7 30 DIM mcode &100 40 osword=&FFF1 50 FOR pass=0 TO 2 STEP 2 60 P%=mcode 70 [ OPT pass 80 LDA #100 90 LDX #pblock MOD 256 100 LDY #pblock DIV 256 110 JSR osword 120 LDA #100 130 LDX #pblock2 MOD 256 140 LDY #pblock2 DIV 256 150 JSR osword 160 RTS 170 .pblock 180 OPT FNequw(text) 190 OPT FNequb(5) 200 OPT FNequb(11) 210 .pblock2 220 OPT FNequw(text) 230 OPT FNequb(5) 240 OPT FNequb(12) 250 .text 260 OPT FNequb(141) 270 OPT FNequb(131) 280 OPT FNequs("Demonstration of new Osword") 290 OPT FNequb(13) 300 ] 310 NEXT 320 CALL mcode 330 END 340 DEFFNequb(byte) 350 ?P%=byte 360 P%=P%+1 370 =pass 380 DEFFNequw(word) 390 ?P%=word 400 P%?1=word DIV 256 410 P%=P%+2 420 =pass 430 DEFFNequd(double) 440 !P%=double 450 P%=P%+4 460 =pass 470 DEFFNequs(string$) 480 $P%=string$ 490 P%=P%+LEN(string$) 500 =pass The new Osword 100 can be used in any text mode but OSWDEMO uses it to print a string in double height, yellow, Mode 7 characters. OSWDEMO must run in the I/O processor. The first two bytes of the parameter blocks store the address of the text block (lines 180 and 220). The third byte of the parameter blocks store the screen X co-ordinate of the first byte of the string (lines 190 and 230) and the fourth byte stores the screen Y co-ordinate of the first byte of the string (lines 200 and 240). Before calling the new Osword (lines 110 and 150) the accumulator is loaded with the new Osword number (lines 80 and 120), the X register with the low byte of the parameter block address (lines 90 and 130), and the Y register with the high byte of the parameter block address (lines 100 and 140). Osword 100 prints the string on the screen and returns with A, X and Y registers preserved. The program OSWORD uses an Osword interpreter (lines 270-370) similar to the one outlined in figure 7.2. After recognising the new Osword (lines 350-370) the new routine preserves the two zero page bytes it uses to print the string by pushing them on the stack (lines 380-410). The address of the parameter block is stored in &F0 (low byte) and &F1 (high byte). These two zero page memory locations are used with post-indexed indirect addressing to read the contents of the parameter block. The Y register is reset to zero (line 420) and the address of the text block is read from the parameter block and stored in zero page ready to print the text string (lines 430-470). The screen X and Y co-ordinates are read from the parameter block and the text cursor is positioned using the machine code equivalent of VDU 31,x,y (lines 480-550). The text string is then printed on the screen (lines 560-640). The zero page locations used by the print routine are pulled off the stack and restored (lines 650-680). The index registers are restored (lines 690-700), the accumulator is reset to zero to inform the other ROMs that the service call has been recognised (lines 710-720) and contol is returned to the MOS (line 730). 10 REM: OSWORD 20 MODE7 30 HIMEM=&3C00 40 DIM save 50 50 diff=&8000-HIMEM 60 address=&A8 70 accumulator=&EF 80 xregister=&F0 90 yregister=&F1 100 osasci=&FFE3 110 oscli=&FFF7 120 FOR pass = 0 TO 2 STEP 2 130 P%=HIMEM 140 [ OPT pass 150 BRK 160 BRK 170 BRK 180 JMP service+diff 190 OPT FNequb(&82) 200 OPT FNequb((copyright+diff) MOD 256) 210 BRK 220 OPT FNequs("OSWORD") 230 .copyright 240 BRK 250 OPT FNequs("(C)1987 Gordon Horsington") 260 BRK 270 .service 280 PHA 290 CMP #8 300 BEQ newosword 310 .exit 320 PLA 330 RTS 340 .newosword 350 LDA accumulator 360 CMP #100 370 BNE exit 380 LDA address 390 PHA 400 LDA address+1 410 PHA 420 LDY #0 430 LDA (xregister),Y 440 STA address 450 INY 460 LDA (xregister),Y 470 STA address+1 480 LDA #31 490 JSR osasci 500 INY 510 LDA (xregister),Y 520 JSR osasci 530 INY 540 LDA (xregister),Y 550 JSR osasci 560 LDY #&FF 570 .printloop 580 INY 590 LDA (address),Y 600 BEQ finish 610 JSR osasci 620 CMP #13 630 BNE printloop 640 .finish 650 PLA 660 STA address+1 670 PLA 680 STA address 690 LDX xregister 700 LDY yregister 710 PLA 720 LDA #0 730 RTS 740 .lastbyte 750 ] 760 NEXT 770 INPUT'"Save filename = "filename$ 780 IF filename$="" END 790 $save="SAVE "+filename$+" "+STR$~(HIMEM)+" "+STR$~(las tbyte)+" FFFF8000 FFFF8000" 800 X%=save 810 Y%=X% DIV 256 820 *OPT1,2 830 CALL oscli 840 *OPT1,0 850 END 860 DEFFNequb(byte) 870 ?P%=byte 880 P%=P%+1 890 =pass 900 DEFFNequw(word) 910 ?P%=word 920 P%?1=word DIV 256 930 P%=P%+2 940 =pass 950 DEFFNequd(double) 960 !P%=double 970 P%=P%+4 980 =pass 990 DEFFNequs(string$) 1000 $P%=string$ 1010 P%=P%+LEN(string$) 1020 =pass