REM > Z80.SerZ80/src REM Source for Single Port Tube Client MOS for Z80 REM ============================================== REM J.G.Harston, 09-Apr-1995 REM This code may be freely reused, with acknowledgements : REM This implements a Tube client communicating with a host REM via a single port, such as a serial port, a parallel port, REM or even a single Tube register. Without the CLICOM and REM code header checks it will all squeeze into less than 1K. : REM The link is a text channel that escapes out to send REM commands and receive data transfers. : : REM Boilerplate REM =========== ON ERROR REPORT:PRINT" at line ";ERL:END IF PAGE>&8000:SYS "OS_GetEnv"TOA$:IFLEFT$(A$,5)="BASIC":OSCLI"TextToBas "+MID$(A$,INSTR(A$," ",1+INSTR(A$," ")))+" *Z80":END DEFFNif(A%):IFA%:z%=-1:=opt% ELSE z%=P%:=opt% DEFFNendif:IFz%=-1:=opt% ELSE z%=P%-z%:P%=P%-z%:O%=O%-z%:=opt% DEFFNelse:IFz%=-1:z%=P%:=opt% ELSE z%=P%-z%:P%=P%-z%:O%=O%-z%:z%=-1:=opt%: : : REM Specify target REM ============== SMALL%=TRUE :REM No CLICOM, no code header checking, fits into 1K SMALL%=FALSE :REM CLICOM and code header checking, fits into 2K ROMSTART%=&F800 :REM Start of ROM code to copy to RAM : REM I/O values suitable for an 8255 on a Torch REM ========================================== TxStatus=6:TxRDY=&80:TxData=4:TxSetup=7:TxInit=&AE:RxStop=&00 RxStatus=6:RxRDY=&02:RxData=5:RxSetup=7:RxInit=&AE:RxCont=&00 TARGET$="Torch":name$="TorchZ80" : REM Torch I/O Map: REM b2=0 - page ROM in, b2=1 - page ROM out REM &00-&03 - Page ROM in, access 8255 register REM &04-&07 - Page ROM out, access 8255 register REM OUT &04 - Port A Data I/O, Tube Data out REM IN &05 - Port B Data I/O, Tube Data in REM IN &06 - Port C Data I/O, Tube Status b7----b1- - OUTRDY---INRDY- REM OUT &07 - 8255 control REM &10-&17 - Z80 SIO REM &18-&1F - Baud rate : REM I/O values, these are suitable for a 6850 REM ========================================= TxStatus=0:TxRDY=&02:TxData=1:TxSetup=0:TxInit=&13:RxStop=&00:REM &13=Reset ACIA, &55=RTS High, 8N1, clock/16 RxStatus=0:RxRDY=&01:RxData=1:RxSetup=0:RxInit=&15:RxCont=&00:REM &15=RTS Low, 8N1, clock/16 TARGET$="Serial":name$="SerialZ80" : REM Declare code start and space REM ============================ IF NOT SMALL%:load%=&F800:DIM mcode% &900 IF SMALL%:load%=&FC00:DIM mcode% &500:name$="SmallZ80":ROMSTART%=load% : REM Serial Tube system values REM ========================= esc=&9B : : ver$="0.10":date$=" (09 Apr 1995)" FOR opt%=4 TO 7 STEP 3 P%=load%:O%=mcode% [OPT opt% ; System Reset ; ============ ; On RESET, ROM is paged into low memory. ROM is paged out by an ; instruction fetch from an address in the top 32K of memory or ; by an I/O operation. .RESET DI LD DE,RESET:LD HL,&0000 ; Copy code from low memory to LD BC,&10000-RESET:LDIR ; high memory JP STARTUP ; Enter code in high memory ; Tube Client Startup ; =================== ; Code has been copied into RAM ; Will be executing in RAM if high jump pages ROM out. ; Will be executing in RAM after I/O setup if I/O access pages ROM out. ; .STARTUP DI:LD SP,ROMSTART% ; Set up initial stack below ROM LD A,TxInit:OUT (TxSetup),A ; Initialise I/O hardware LD A,RxInit:OUT (RxSetup),A ; Initialise I/O hardware CALL INITERR OPT FNif(TARGET$<>"Torch") ; Send software reset OPT FNendif CALL PRTEXT ; Print startup banner .StartupMessage .StartupCR DEFB 13:DEFM TARGET$+" TUBE Z80 64K "+ver$ DEFB 13:DEFB 13:DEFB 0 XOR A:LD (ESCFLG),A ; Clear Escape flag OPT FNif(TARGET$="Torch") ; Torch shares hardware reset CALL OSWRCH ; Send terminating zero byte OPT FNendif CALL WaitEnterCode ; Wait for ack and enter code if needed ; Fall through to command prompt if nothing entered OPT FNif(SMALL%=0) ; Minimal Command prompt ; ====================== .CmdOSLoop LD SP,(MEMTOP) ; Clear stack LD HL,ErrorHandler ; Claim error handler LD (BRKV),HL LD A,ASC"*":CALL OSWRCH ; Print '*' prompt LD HL,CmdOSCtrl XOR A:CALL OSWORD ; Read line to INPBUF JR C,CmdOSEscape LD HL,INPBUF:CALL OS_CLI ; Execute command JR CmdOSLoop ; and loop back for another command .CmdOSEscape LD A,&7E:CALL OSBYTE ; Acknowledge Escape state RST &38:DEFB 17:DEFM "Escape":NOP ; Control block for command prompt input ; -------------------------------------- .CmdOSCtrl DEFW INPBUF ; Input text to INPBUF DEFB INPBUFEND-INPBUF ; Size of INPBUF DEFB &20:DEFB &FF ; Min=&20, Max=&FF OPT FNendif ; Error handlers ; ============== .InitErr LD A,&C3:LD (&38),A ; Put 'JP' opcode at &38 LD HL,(ERRJMP+1) ; Get destination of ERRJMP LD (&39),HL:RET ; RST &38 generates an error .RestartHandler POP HL:LD (FAULT),HL ; Get address after RST &38 LD HL,(BRKV):JP (HL) ; Vector via BRKV .ErrorHandler CALL Report:JP CLICOM ; Report error and enter command prompt ; Printout routines ; ================= .Report CALL OSNEWL:LD HL,(FAULT) ; Get error pointer INC HL:CALL PrString ; Print text after error number JP OSNEWL ; Print final NL ; Print string at HL ; ------------------ .PrString LD A,(HL):INC HL ; Get character AND A:RET Z ; Return if zero byte CALL OSASCI:JR PrString ; Print current character and loop back ; Print embedded string ; --------------------- .PrText EX (SP),HL:CALL PrString ; Print from address on stack EX (SP),HL ; Return updated address to stack .EventHandler .InterruptHandler .osFSC RET ; MOS INTERFACE ; ============= ; ; ; OSRDCH - Wait for character from input stream ; ============================================= ; On exit, A =char, Cy=Escape flag ; .osRDCH PUSH BC:CALL WaitByte ; Wait for character LD B,A ; Save character LD A,(ESCFLG):ADD A,A ; Get Escape flag to Carry LD A,B:POP BC:RET ; Get character to A ; OSRDCH_IO - Request character via Tube ; ====================================== ; On exit, A =char, Cy=carry ; ; Tube data &00 -- Carry Char ; .osRDCH_IO XOR A:CALL SendCommand ; Send command &00 - OSRDCH .WaitCarryChar CALL WaitByte:ADD A,A ; Wait for Carry JP WaitByte ; Wait for Char ; OSCLI - Send command line to host ; ================================= ; On entry, HL=>command string ; On exit, A, HL corrupted ; ; Tube data &02 string &0D -- &7F or &80 ; ; Should use a temporary stack in case command causes a ; data transfer that overwrites current stack. Needs to ; be able to cope with OSCLI calling code that then calls ; another OSCLI and be able to return recusively. ; .osCLI LD A,&02:CALL SendCommand ; Send command &02 - OSCLI CALL SendString ; Send command string .CLIAck PUSH BC:PUSH DE:PUSH HL ; Save registers LD HL,(PROG):PUSH HL ; Save current program CALL WaitEnterCode ; Wait for ack and enter code if needed POP HL:LD (PROG),HL ; Restore current program POP HL:POP DE:POP BC:RET ; Restore registers and return ; OSBYTE - Byte MOS functions ; =========================== ; On entry, A, HL=OSBYTE parameters ; On exit, A preserved ; If A<&80, L=returned value ; If A>&7F, HL, Carry=returned values ; .osBYTE CP &80:JR NC,ByteHigh ; Jump for long OSBYTEs ; ; BYTELO ; Tube data &04 X A -- X ; PUSH AF LD A,&04:CALL SendCommand ; Send command &04 - OSBYTELO LD A,L:CALL SendByte ; Send single parameter POP AF:PUSH AF:CALL SendByte ; Send function CALL WaitByte:LD L,A ; Get return value POP AF:RET ; .ByteHigh CP &82:JR Z,Byte82 ; Read memory high word CP &83:JR Z,Byte83 ; Read bottom of memory CP &84:JR Z,Byte84 ; Read top of memory ; ; BYTEHI ; Tube data &06 X Y A -- Cy Y X ; PUSH AF LD A,&06:CALL SendCommand ; Send command &06 - OSBYTEHI LD A,L:CALL SendByte ; Send parameter 1 LD A,H:CALL SendByte ; Send parameter 2 POP AF:CALL SendByte ; Send function CP &9D:RET Z ; Fast return with 'Fast BPUT' CP &8E:JR Z,CLIAck ; Check acknowledge with 'Enter language' PUSH BC:LD B,A ; Save function CALL WaitByte:ADD A,A ; Wait for Carry CALL WaitByte:LD H,A ; Wait for return high byte CALL WaitByte:LD L,A ; Wait for return low byte LD A,B:POP BC:RET ; Restore function, BC .Byte82:LD HL,&0000:RET ; Read memory high word .Byte83:LD HL,(MEMBOT):RET ; Read bottom of memory .Byte84:LD HL,(MEMTOP):RET ; Read top of memory ; OSWORD - Various functions ; ========================== ; On entry, A =function ; HL=>control block ; .osWORD AND A:JR Z,RDLINE ; OSWORD 0, jump to read line of text ; ; Tube data &08 A in_length block out_length -- block ; PUSH AF ; Save function LD A,&08:CALL SendCommand ; Send command &08 - OSWORD POP AF:PUSH AF:CALL SendByte ; Send function PUSH BC:PUSH HL ; Save BC, save control block address ADD A,A:JR C,WordIndex ; If &80+, use control block entries LD BC,&1010 CP 42:JR NC,WordSend ; If 21..127, use 16 both ways LD HL,WordLengths LD B,0:LD C,A:ADD HL,BC ; If 1..20, index into length table .WordIndex LD B,(HL):INC HL:LD C,(HL) ; B=send length, C=receive length .WordSend POP HL:PUSH HL:PUSH BC ; Get control block address back, save lengths LD A,B:CALL SendByte ; Send send length LD C,B:CALL SendBlockC ; Send control block POP BC:POP HL:PUSH HL ; Get lengths and control block address back LD A,C:CALL SendByte ; Send receive length CALL WaitBlockC ; Wait for returned control block POP HL:POP BC:POP AF:RET ; Restore registers and return ; OSWORD control block lengths ; ---------------------------- .WordLengths DEFB &00:DEFB &05 ; &01 =TIME DEFB &05:DEFB &00 ; &02 TIME= DEFB &00:DEFB &05 ; &03 =Timer DEFB &05:DEFB &00 ; &04 Timer= DEFB &04:DEFB &05 ; &05 =IO DEFB &05:DEFB &00 ; &06 IO= DEFB &08:DEFB &00 ; &07 SOUND DEFB &0E:DEFB &00 ; &08 ENVELOPE DEFB &04:DEFB &05 ; &09 =POINT DEFB &01:DEFB &09 ; &0A =CHR$ DEFB &01:DEFB &05 ; &0B =Palette DEFB &05:DEFB &00 ; &0C Palette= DEFB &00:DEFB &08 ; &0D =Coord DEFB &10:DEFB &19 ; &0E =TIME$ DEFB &19:DEFB &10 ; &0F TIME$= DEFB &10:DEFB &01 ; &10 Net_Tx DEFB &0D:DEFB &0D ; &11 Net_Args DEFB &00:DEFB &80 ; &12 Net_Rx DEFB &08:DEFB &08 ; &13 NetFS_Info DEFB &80:DEFB &80 ; &14 NetFS_Op ; RDLINE - Read a line of text ; ============================ ; On entry, A =0 ; HL=>control block ; On exit, A =undefined ; H =length of returned string ; Cy=0 ok, Cy=1 Escape ; ; Tube data &0A block -- &FF or &7F string &0D ; .RDLINE LD A,&0A:CALL SendCommand ; Send command &0A - RDLINE INC HL:INC HL LD C,3:CALL SendBlock ; Send control block LD A,&07:CALL SendByte XOR A:CALL SendByte ; Send &0700 CALL WaitByte:ADD A,A ; Wait for response JR C,RdLineEscape DEC HL:LD A,(HL) DEC HL:LD L,(HL):LD H,A ; Get address of text buffer PUSH BC:LD BC,&FF00 ; Save BC, initialise counter .RdLineLp CALL WaitByte:LD (HL),A ; Wait for a byte INC HL:INC B ; Inc. pointer and count CP 13:JR NZ,RdLineLp ; Loop until PUSH BC:POP HL:POP BC ; Copy counter to H, restore BC XOR A:RET ; Return A=0, Cy=0 .RdLineEscape LD HL,&00FF ; Return count=0 XOR A:SCF:RET ; Return A=0, Cy=1 ; OSARGS - Read info on open file ; =============================== ; On entry, A =function ; HL=>control block ; E =handle ; On exit, A =returned value ; HL preserved ; E preserved ; ; Tube data &0C handle block function -- result block ; .osARGS PUSH BC:PUSH AF ; Save BC, function LD A,&0C:CALL SendCommand ; Send command &0C - OSARGS LD A,E:CALL SendByte ; Send handle LD C,4:CALL SendBlock ; Send data POP AF:CALL SendByte ; Send function CALL WaitByte:PUSH AF ; Wait for result LD C,4:CALL WaitBlock ; Wait for data POP AF:POP BC:RET ; Get result, restore BC ; OSBGET - Get a byte from open file ; ================================== ; On entry, H =handle ; On exit, A =byte Read ; H =preserved ; Cy set if EOF ; ; Tube data &0E handle -- Carry byte ; .osBGET LD A,&0E:CALL SendCommand ; Send command &0E - OSBGET LD A,H:CALL SendByte ; Send handle JP WaitCarryChar ; Wait for Carry and byte ; OSBPUT - Put a byte to an open file ; =================================== ; On entry, A =byte to write ; H =handle ; On exit, A =preserved ; H =preserved ; ; Tube data &10 handle byte -- &7F ; .osBPUT PUSH AF ; Save byte LD A,&10:CALL SendCommand ; Send command &10 - OSBPUT LD A,H:CALL SendByte ; Send handle POP AF:CALL SendByte ; Send byte PUSH AF:CALL WaitByte ; Wait for acknowledge POP AF:RET ; Restore A, return ; OSFIND - Open or Close a file ; ============================= ; On entry, A =function ; H =handle or HL=>filename ; On exit, A =zero or handle ; ; Tube data &12 function string &0D -- handle ; &12 &00 handle -- &7F ; .osFIND PUSH AF ; Save function LD A,&12:CALL SendCommand ; Send command &12 - OSFIND POP AF:CALL SendByte ; Send function AND A:JR NZ,OPEN ; If A<>0, jump to do OPEN ; CLOSE LD A,H:CALL SendByte ; Send handle CALL WaitByte ; Wait for acknowledgement XOR A:RET ; Restore function .OPEN CALL SendString ; Send pathname JP WaitByte ; Wait for and return handle ; OSFILE - Operate on whole files ; =============================== ; On entry, A =function ; HL=>control block ; On exit, A =result ; control block updated ; ; Tube data &14 block string function -- result block ; .osFILE PUSH BC:PUSH AF ; Save BC and function LD A,&14:CALL SendCommand ; Send command &14 - OSFILE INC HL:INC HL:PUSH HL ; Point to control block contents LD C,16:CALL SendBlock ; Send 16-byte control block DEC HL:LD A,(HL) DEC HL:LD L,(HL):LD H,A ; Get pointer to pathname CALL SendString ; Send pathname POP HL ; Get control block address back POP AF:CALL SendByte ; Send function CALL WaitByte:PUSH AF ; Wait for result LD C,16:CALL WaitBlock ; Wait for 16-byte control block DEC HL:DEC HL ; Restore HL POP AF:POP BC ; Get result back, restore BC RET ; OSGBPB - Multiple byte Read and write ; ===================================== ; On entry, A =function ; HL=>control block ; On exit, A =returned value ; control block updated ; ; Tube data &16 block function -- block Carry result ; .osGBPB PUSH BC:PUSH AF ; Save BC and function LD A,&16:CALL SendCommand ; Send command &16 - OSGBPB LD C,13:CALL SendBlock ; Send 13-byte control block POP AF:CALL SendByte ; Send function LD C,13:CALL WaitBlock ; Wait for 13-byte control block POP BC:JP WaitCarryChar ; Jump to get Carry and A ; Tube I/O routines ; ================= ; Send a string ; ------------- .SendString LD A,(HL):CALL SendByte ; Send a character INC HL ; Move to next character CP 13:JR NZ,SendString ; Loop until sent RET ; Send a block of data ; -------------------- .SendBlockC DEC C:RET M:INC C ; Don't send if &81..&FF or &00 .SendBlock LD B,0:ADD HL,BC ; Point to end of data block .SendBlockLp DEC HL ; Move downwards through data LD A,(HL):CALL SendByte ; Send a byte DEC C:JR NZ,SendBlockLp ; Loop for whole block RET ; Wait for a block of data ; ------------------------ .WaitBlockC DEC C:RET M:INC C ; Don't wait if &81..&FF or &00 .WaitBlock LD B,0:ADD HL,BC ; Point to end of data block .WaitBlockLp DEC HL ; Move downwards through data CALL WaitByte:LD (HL),A ; Wait for a byte DEC C:JR NZ,WaitBlockLp ; Loop for whole block RET ; OSWRCH - Send character to output stream ; ======================================== ; On entry, A =character ; On exit, A =preserved ; ; Tube data character -- ; .osWRCH ; WRCH is simply SendByte ; Tube Core I/O Routines ; ====================== ; Characters and commands are sent over the same single port ; Outward commands are escaped, and inward responses are escaped ; ; Outward ; x VDU x ; esc,esc VDU esc ; esc,n MOS function, control block follows ; ; Inward ; x char/byte x ; esc,esc char/byte esc ; esc,&00 BRK, error number+text+null follows ; esc,<&80 read returned control block set length ; esc,&8n Escape change, b0=new state ; esc,&9x,Y,X,A Event ; esc,&Ax reserved for networking ; esc,&Bx end transfer ; esc,&Cx,addr set address ; esc,&Dx,addr execute address ; esc,&Ex,addr start load from address ; esc,&Fx,addr start save from address ; All commands are data inward, except esc,&Fx which is data outward ; Send a byte, escaping it if needed ; ---------------------------------- ; On entry, A=byte to send ; On exit, A preserved, F corrupted ; .SendByte CP esc:CALL Z,SendData ; If esc, prefix it with another esc .SendData PUSH AF .SendWait IN A,(TxStatus) AND TxRDY:JR Z,SendWait ; Wait until data can be sent POP AF:OUT (TxData),A ; Send data RET ; Send an escaped command ; ----------------------- ; On entry, A=command ; On exit, All registers preserved .SendCommand PUSH AF ; Save command byte .SendCmdLp CALL ReadByte:JR NZ,SendCmdLp ; Flush input LD A,esc:CALL SendData ; Send esc prefix POP AF:JR SendData ; Send command byte ; Check if a byte is waiting, and read it if there ; ------------------------------------------------ ; On exit, F=Z - nothing waiting ; F=NZ - byte waiting, returned in A ; .ReadByte IN A,(RxStatus) AND RxRDY:RET Z ; Nothing waiting ; Continue into WaitByte ; Wait for a byte, decoding escaped data ; -------------------------------------- ; On exit, A =byte ; F =preserved ; .WaitByte PUSH BC:PUSH AF ; Save BC and flags .WaitByteLp CALL WaitData:JR NZ,WaitByteOk ; Not esc, return it CALL WaitData:JR Z,WaitByteOk ; esc,esc, return as esc PUSH HL:PUSH DE:CALL WaitCommand ; Decode escaped command POP DE:POP HL:JR WaitByteLp ; Loop back to wait for a byte .WaitByteOk LD B,A:POP AF ; Save byte, get flags back LD A,B:POP BC ; Put byte in A, restore BC RET ; Wait for raw byte of data ; ------------------------- ; On exit, A =byte ; F =Z byte=esc, NZ byte<>esc ; .WaitData IN A,(RxStatus) AND RxRDY:JR Z,WaitData ; Wait until data present IN A,(RxData) CP esc:RET ; Fetch data ; Decode escaped command ; ---------------------- ; On entry, A=command ; AF, BC, DE, HL can be trashed ; .WaitCommand AND A:JR Z,WaitError ; esc,&00 - error JP M,WaitTransfer ; esc,>&7F - data transfer ; esc,1..127 - read a control block ; --------------------------------- LD C,A:LD B,0 ; Move count to BC LD HL,(CTRL):ADD HL,BC ; Point to end of control block .WaitLength CALL WaitByte:DEC HL ; Wait for a byte LD (HL),A ; Store it DEC C:JR NZ,WaitLength ; Loop back RET ; Return to WaitByte ; esc,&00 - error ; --------------- .WaitError LD HL,ERRBUF ; Point to error buffer LD (HL),&FF:INC HL:PUSH HL ; Store error RST CALL WaitByte:LD (HL),A:INC HL ; Store error number .WaitErrorLp CALL WaitByte:LD (HL),A:INC HL ; Store error character AND A:JR NZ,WaitErrorLp ; Loop until final &00 POP HL:PUSH HL:LD A,(HL) ; Get error number INC HL:OR (HL):JP Z,STARTUP ; If error 0,"" do a RESET JP ERRJMP ; Enter error handler, no return ; esc,&8n - Escape change ; ----------------------- .WaitTransfer CP &C0:JR NC,WaitStart CP &A0:JR NC,WaitEnd CP &90:JR NC,WaitEvent RRCA:LD (ESCFLG),A:RET ; Set error flag from b0 ; esc,&9x - Event ; --------------- .WaitEvent CALL WaitByte:LD H,A ; Fetch event Y parameter CALL WaitByte:LD L,A ; Fetch event X parameter CALL WaitByte ; Fetch event A parameter PUSH HL:LD HL,(EVENTV) ; Get event vector EX (SP),HL:RET ; Dispatch and return ; esc,&Ax - Reserved ; ------------------ .WaitEnd CP &B0:RET C ; Return to WaitByte ; esc,&Bx - End transfer ; ---------------------- POP HL:POP HL:POP HL ; Drop data from stack to fall POP HL:POP HL ; out of WaitSave/WaitLoad loop RET ; Return to WaitByte ; esc,&C0+ - Start transfer ; ------------------------- .WaitStart PUSH AF:LD HL,ADDR ; Save command, point to ADDR LD C,4:CALL WaitBlock ; Wait for 4-byte data address LD HL,(ADDR):POP AF ; Get address to HL CP &D0:RET C ; esc,&Cx - set address for later execution CP &E0:JP C,CallCode ; esc,&Dx - enter code immediately CP &F0:JR NC,WaitSave ; esc,&Fx - save data .WaitLoad CALL WaitByte:LD (HL),A ; esc,&Ex - load data INC HL:JR WaitLoad ; Loop until terminated by esc,&Bx .WaitSave LD A,(HL):CALL SendByte ; esc,&Fx - save data INC HL:CALL ReadByte ; Poll input for termination JR WaitSave ; Loop until terminated by esc,&Bx ; Wait for acknowledge and enter code if >&7F ; ------------------------------------------- .WaitEnterCode CALL WaitByte:ADD A,A:RET NC ; Wait for ack, exit if <&80 LD HL,(ADDR) ; Fetch transfer address ; EnterCode - Enter code at HL ; ---------------------------- ; All registers trashable ; .EnterCode OPT FNif(SMALL%=0) PUSH HL:LD DE,6:ADD HL,DE ; HL+1=>Offset to copyright message LD A,(HL):INC HL:LD E,(HL) ; A=ROM type, DE=Offset to copyright message POP HL:PUSH HL:ADD HL,DE ; HL=>Copyright message LD (FAULT),HL:LD E,A ; Set previous error, save ROM type in E LD A,(HL):AND A:JR NZ,EnterRunCode ; Look for &00,"(C)" INC HL:LD A,(HL):CP ASC"(":JR NZ,EnterRunCode INC HL:LD A,(HL):CP ASC"C":JR NZ,EnterRunCode INC HL:LD A,(HL):CP ASC")":JR NZ,EnterRunCode ; &00,"(C)" exists, check ROM type byte ; ------------------------------------- LD A,E:LD A,(HL):AND &4F ; Keep Language and CPU bits CP &40:JR C,errNotLanguage ; <&40, no language bit set CP &48:JR Z,EnterRunCode ; CPU=8, Z80 code .errNotZ80Code RST &38:DEFB 249:DEFM "This is not Z80 code":DEFB 0 .errNotLanguage RST &38:DEFB 249:DEFM "This is not a language":DEFB 0 .EnterRunCode POP HL:LD (PROG),HL ; Set as current program OPT FNendif LD A,1 .CallCode JP (HL) DEFM STRING$((&FC00-P%)AND(P%<&FC00),CHR$&FF) DEFM STRING$((&FC80-P%)AND(P%<&FC80),CHR$&FF) DEFM STRING$((&FD00-P%)AND(P%<&FD00),CHR$&FF) DEFM STRING$((&FD80-P%)AND(P%<&FD80),CHR$&FF) DEFM STRING$((&FE00-P%)AND(P%<&FE00),CHR$&FF) DEFM STRING$((&FE80-P%)AND(P%<&FE80),CHR$&FF) DEFM STRING$((&FF20-P%)AND(P%<&FF00),CHR$&FF) .ERRBUF ; About 40 bytes for error strings/system stack .INPBUF ; Also use for input buffer DEFM STRING$(&FF80-P%,CHR$&00) .INPBUFEND .SystemStack ; Stack grows down from here .ESCFLG :DEFB &00 ; &FF80 Escape flag .FF81 :DEFB &00 ; &FF81 TempA .FAULT :DEFW StartupMessage ; &FF82 Fault pointer .ERRDEF :DEFW ErrorHandler ; &FF84 Default error handler .LPTR :DEFW StartupCR ; &FF86 Command line tail pointer .MEMBOT :DEFW &0100 ; &FF88 Bottom of available memory .MEMTOP :DEFW ROMSTART% ; &FF8A Top of available memory .ADDR :DEFW &0000:DEFW &0000 ; &FF8C Transfer address .PROG :DEFW STARTUP ; &FF90 Current program .CTRL :DEFW ERRBUF ; &FF92 Control block .LFF94 :NOP ; &FF94 .LFF95 :RET:RET:RET ; &FF95 .LFF98 :RET:RET:RET ; &FF98 .PRSTNG :JP PrString ; &FF9B Print zero-terminated text at HL, returns A=0, HL corrupted .RDDEC :RET:RET:RET ; &FF9E .RDHEX :RET:RET:RET ; &FFA1 Scan hex string at HL, returned in HL .DSKACC :RET:RET:RET ; &FFA4 .DSKCCP :RET:RET:RET ; &FFA7 .PRHEX :RET:RET:RET ; &FFAA Print A in hex, A corrupted .PR2HEX :RET:RET:RET ; &FFAD Print HL in hex, A corrupted .USERINT:RET:RET:RET ; &FFB0 .PRTEXT :JP PrText ; &FFB3 Print zero-terminated inline text, returns A=0 .SPARE1 :RET:RET:RET ; &FFB6 .OSQUIT OPT FNif(SMALL%) .CLICOM :JP CLICOM ; &FFB9 Quit current program OPT FNelse .CLICOM :JP CmdOSLoop ; &FFB9 Quit current program OPT FNendif .ERRJMP :JP RestartHandler ; &FFBC .INITERR:JP InitErr ; &FFBF Initialise MOS error handler, A corrupted .DSKRST :RET:RET:RET .LFFC5 :JP PrString ; &FFC5 .SPARE4 :RET:RET:RET ; FILE I/O ENTRIES ; ================ .OSFSC :JP osFSC ; &FFCB .OSFIND :JP osFIND ; &FFCE .OSGBPB :JP osGBPB ; &FFD1 .OSBPUT :JP osBPUT ; &FFD4 .OSBGET :JP osBGET ; &FFD7 .OSARGS :JP osARGS ; &FFDA .OSFILE :JP osFILE ; &FFDD ; CHARACTER I/O ENTRIES ; ===================== .OSRDCH :JP osRDCH ; &FFE0 .OSASCI :CP 13:JR NZ,OSWRCH ; &FFE3 .OSNEWL :LD A,10:CALL OSWRCH; &FFE7 .OSWRCR :LD A,13 ; &FFEC .OSWRCH :JP osWRCH ; &FFEE ; MOS ENTRIES ; =========== .OSWORD :JP osWORD ; &FFF1 .OSBYTE :JP osBYTE ; &FFF4 .OS_CLI :JP osCLI ; &FFF7 ; SYSTEM VECTORS ; ============== .BRKV :DEFW ErrorHandler ; &FFFA .EVENTV :DEFW EventHandler ; &FFFC .IRQV :DEFW InterruptHandler ; &FFFE ]NEXT OSCLI "SAVE "+name$+" "+STR$~mcode%+" "+STR$~O%+" "+STR$~load%+" "+STR$~load% *Quit