[img] Calling BASIC from machine code
 MDFS::Info.Comp.Spectrum.ProgTips.linerun/htm Search  

Spectrum BASIC allows you the usual methods to call machine code from BASIC - USR address calls the code and returns the value in the Z80's BC register. However, it can often be useful to be able to call a piece of BASIC code from machine code.

LINE_RUN at &1B8A executes a line of BASIC in the editing area pointed to by E_LINE, such as a line entered directly. If you point E_LINE to your own BASIC you can call LINE_RUN to execute it. Slightly different code has to be used for 48K Basic commands and Interface 1 commands as Interface 1 commands always return via ERR_SP rather than via a RET.

; Execute a Interface 1 command at E_LINE
; ---------------------------------------
; NB! Interface 1 commands *ALWAYS* return via (ERR_SP), even when OK
; NB! Must ALWAYS be CALLed, cannot be JPed to as stack fiddled with
SET 7,(IY+1)                 ; Set 'Not syntax checking'
SET 2,(IY+124)               ; Ensure ERR_SP used for errors
LD (IY+10),1                 ; Set ZXBasic statement 1
LD HL,(ERR_SP):PUSH HL       ; Save ERR_SP
LD HL,&FFFE:ADD HL,SP        ; Point to where return address will be
LD (ERR_SP),HL               ; Set up new ERR_SP
CALL &1B8A                   ; Execute command at E_LINE, will always return to here
BIT 7,(IY+0):JR NZ,MDNoError ; No error occured
RES 1,(IY+124)               ; Ensure autoload trap avoided
LD HL,&0046:LD (&5CED),HL    ; Point to part of error handler
RST &08:DEFB &32             ; Reclaim channels and turn off drives
XOR A                        ; Set Z to indicate error occured
POP HL:LD (ERR_SP),HL        ; Restore ERR_SP
; On exit,
;  NZ=No error, Z=Error occured
;  ERR_NR=&FF - ok
;  ERR_NR=&14 - BREAK
;  ERR_NR=&0B - Interface 1 error
;  Could be Invalid name/Missing name/Drive write protected/Microdrive full/Microdrive not present
;          File not found/Wrong file type/Writing to a 'read' file
With this code you can then execute an Interface 1 command from machine code. The following loads a piece of code by executing a LOAD *"m" command.
; Load code from microdrive
; -------------------------
RST &08:DEFB &31             ; Wake up Interface 1, ensure E_LINE in correct place
LD BC,31:LDIR                ; Copy LOAD*"m";VAL"1";"filename"CODE0<0e><0><0><80><0><0><cr><80>
EX DE,HL:CALL &16B9          ; Point WORKSP to end of line
CALL MDCommand               ; Execute LOAD command

DEFB &EF:DEFM "*":DEFB &22:DEFM "m":DEFB &22:DEFM ";" ; LOAD*"m";
DEFB &B0:DEFB &22:DEFM "1":DEFB &22:DEFM ";":DEFB &22 ; VAL"1";"
DEFM "filename"                                       ; filename
DEFB &22:DEFB &AF:DEFM "0":DEFB &0E:DEFB &00:DEFB &00 ; "CODE0<0e><00><00>
DEFB &00:DEFB &80:DEFB &00:DEFB &0D:DEFB &80          ; <00><80><00><cr><80>
Note that the file is loaded to &8000 by specifying the address in the embedded number. The literal number is ignored. This procedure can be used to load an arbitary file by copying the filename to E_LINE seperately, and to load to an arbitary address by setting the embedded number after copying, and from an arbitary drive by overwriting the drive number.
; Load code from microdrive
; -------------------------
; A=drive number
; HL=>filename
; BC=length of filename
; DE=load address
PUSH DE:PUSH HL:PUSH BC:PUSH AF ; Wake-up trashes all registers
RST &08:DEFB &31                ; Wake up Interface 1, ensure E_LINE in correct place
PUSH DE:POP IX                  ; IX=>start of command
LD BC,12:LDIR                   ; Copy LOAD*"m";VAL"1";"
POP AF:OR 48:LD (IX+8),A        ; Overwrite drive number
POP BC:POP HL:LDIR              ; Copy filename
PUSH DE:POP IX                  ; IX=>end of filename
LD HL,MDLoad2:LD BC,11:LDIR     ; Copy "CODE0<0e><00><00><00><00><00><0d><80>
POP DE:LD (IX+6),E:LD (IX+7),D  ; Insert load address
EX DE,HL:CALL &16B9             ; Point WORKSP to end of line
CALL MDCommand                  ; Execute LOAD command - MUST be called
RET                             ; Exit with Z=error, NZ=no error
DEFB &EF:DEFM "*":DEFB &22:DEFM "m":DEFB &22:DEFM ";" ; LOAD*"m";
DEFB &B0:DEFB &22:DEFM "1":DEFB &22:DEFM ";":DEFB &22 ; VAL"1";"
DEFB &22:DEFB &AF:DEFM "0":DEFB &0E:DEFB &00:DEFB &00 ; "CODE0<0e><00><00>
DEFB &00:DEFB &80:DEFB &00:DEFB &0D:DEFB &80          ; <00><80><00><cr><80>

Best viewed with Any Browser Valid HTML 4.0! Authored by J.G.Harston
Last update: 10-Jul-2005