; ZXZVM: Z-Code interpreter for the Z80 processor ; Copyright (C) 1998-9,2006,2016 John Elliott ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; load_zvm: ld de,zvmfcb call find_fcb ld de,cfind$ jp nc,errpr ; ;The file has been found. ; ld de,zvmfcb call zfcb ;Zero all reserved fields in the FCB ld c,0Fh call FDOS ;Try to open it ld de,cfind$ ;Can't open it? inc a jp z,errpr ;Fail. ; ;File open. ; ld hl,7000h ld de,zvmfcb rnext: call readrec ;Read 1 record at HL, with auto-increment jp nc,diewith or a jr z,rnext ; ld de,zvmfcb ld c,10h call FDOS scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Read one record from DE->FCB, at HL. Add 80h to HL. ; ;If OK: returns carry set. A=0 for OK, 1 for EOF. ;If error: returns carry reset, HL-> ASCII7 reason. ; readrec: push de ex de,hl ;DE = record load address call setdma ld hl,80h ;Add 80h to address add hl,de ;since record is 80h bytes pop de push hl push de ld c,14h ;Read next record call FDOS pop de cp 2 jr nc,xlterr ;Some read error pop hl ret ;with Carry set. ; xlterr: cp 1 ld hl,dflerr jr z,xltend cp 2 jr z,xltend cp 10 ld hl,mederr jr z,xltend cp 9 ld hl,fcberr jr z,xltend ld a,h cp 11 jr c,xlter1 ld h,0 xlter1: ld l,h ld h,0 add hl,hl ld bc,hwerrs add hl,bc ld a,(hl) inc hl ld h,(hl) ld l,a xltend: xor a pop bc ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; hwerrs: defw hw0,hw1,hw2,hw3,hw4,hw5,hw6,hw7,hw8,hw9,hw10 hw1: defb 'Failed to access disc driv',0E5h ;'e'+80h hw2: defb 'The disc is read-onl',0F9h ;'y'+80h hw3: defb 'The file is read-onl',0F9h ;'y'+80h hw4: defb 'Not a valid drive lette',0F2h ;'r'+80h hw5: defb 'File is already ope',0EFh ;'n'+80h hw0: hw6: defb 'Hardware erro',0F2h ;'r'+80h hw7: defb 'File is password protecte',0E4h ;'d'+80h hw8: defb 'File exist',0F3h ;'s'+80h hw9: defb '? in filenam',0E5h ;'e'+80h hw10: defb 'File is wheel protecte',0E4h ;'d'+80h mederr: defb 'Disc change',0E4h ;'d'+80h fcberr: defb 'Invalid FC',0C2h ;'B'+80h dflerr: defb 'Disc ful',0ECh ;'l'+80h nfer$: defb 'File not foun',0E4h ;'d'+80h ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Search for a file. DE->FCB; returns carry set if found; else ;carry reset. ; find_fcb: push de ld a,(de) ;Drive as passed ld hl,tmpfcb ld bc,36 ex de,hl ldir ;Copy the FCB in question to tmpfcb or a ;Resets Carry call nz,trydrv ;Was a drive passed? jr c,gotdrv xor a ;Try the default drive. call trydrv jr c,gotdrv ld a,(iy+4Ch) ;1st "search" drive cp 0FFh jr z,tryept call trydrv jr c,gotdrv ld a,(iy+4Dh) ;2nd "search" drive cp 0FFh jr z,tryept call trydrv jr c,gotdrv ld a,(iy+4Eh) ;3rd "search" drive cp 0FFh jr z,tryept call trydrv jr c,gotdrv ld a,(iy+4Fh) ;4th "search" drive cp 0FFh jr z,tryept call trydrv jr c,gotdrv tryept: xor a pop de ret ; gotdrv: pop de ld a,(tmpfcb) ld (de),a scf ret ; trydrv: cp 0FFh ;Attempt to find the file in tmpfcb jr nz,trydrv1 ;on a given drive (drive in A). xor a ret ; ;Rather than use 'find first', I'm using 'open file' to check if the file ;is there. 'open file' can see system files in user 0 from other user areas, ;which 'find first' can't. ; trydrv1: ld de,tmpfcb ld (de),a call zfcb ;Zero all reserved fields in the FCB ld c,0Fh ;Open existing file. call FDOS inc a scf ccf ret z ;Not found ld c,10h ld de,tmpfcb ;Close it again, this was only a dummy call FDOS ;attempt. scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Set the data transfer address. ; setdma: call pusha ld c,1Ah call FDOS jp popa ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Zero all the reserved fields in the FCB. DE->FCB ; zfcb: push hl push de push bc ld hl,12 add hl,de ;HL -> 1st field ld d,h ld e,l inc de ld bc,23 ld (hl),b ldir pop bc pop de pop hl ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Open the story file in DE. Load either the first 64k, or all of it. ; st_open: xor a ld (lbank),a push de call willitfit pop de ret nc push af ld a,1 ld (allhere),a call zfcb ;Zero all reserved fields in the FCB ld c,0Fh call FDOS ;Try to open the story file. ld hl,nostor$ ;Can't open it? inc a jp z,ret_nc pop af jr z,ldall ld b,4 lbloop: call ldbank ;Load 4 banks ret nc ret z djnz lbloop xor a ld hl,21h ;Reset the record counter ld de,(story) add hl,de ld (hl),a inc hl ld (hl),a inc hl ld (hl),a ld (allhere),a scf ret ; ret_nc: xor a ret ; ldall: call ldbank ;Load every bank ret nc ret z jr ldall ; st_close: ld de,(story) ld c,10h call FDOS scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; st_reload: ld hl,10h call ZXPK64 ld b,a inc hl call ZXPK64 ld c,a ;BC = Flags 10 (preserve through restart) push bc xor a ld (lbank),a ld de,(story) ld c,10h call FDOS ld de,(story) call zfcb ;Zero all reserved fields in the FCB ld c,0Fh call FDOS ;Try to open the story file. cp 0FFh pop bc jp z,xfcber push bc ld b,4 rbloop: call ldbank ;Load 4 banks pop hl ret nc jr z,rbend push hl djnz rbloop pop hl rbend: ex de,hl ;DE = flags 2 ld hl,10h ld a,d call ZXPOKE inc hl ld a,e call ZXPOKE scf ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Some variables ; tmpfcb: defs 36 findbuf: defs 80h recbuf: defs 80h zvmfcb: defb 0,'ZXZVM BIN' defs 24 ; allhere: defb 0 ;Load all the file, or only the first 64k? ; m_dph: defw 0 ;Disc parameter header for M: ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Push / pop all the registers that ZXZVM uses. ; pusha: ex (sp),hl ;HL = return address ld (pusha1+1),hl pop hl push af push bc push de push hl push ix push iy ;Note: ZXZVM _never_ uses IY, so I'm going pusha1: jp 0 ;to use it to point at the SCB. popa: pop iy pop ix pop hl pop de pop bc pop af ret ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Disable drive M: (so that the user can't (eg) use it to save files to) ; disable_M: call drvtbl ;HL -> DPH table, 2 bytes / drive ld de,24 ;Offset to M: add hl,de ld c,(hl) ld (hl),0 inc hl ld b,(hl) ld (hl),0 ld (m_dph),bc ld c,30h ;Force empty of all disc buffers (which might be ld e,0FFh ;looking at M:) call FDOS ret ; drvtbl: ld hl,(1) ld de,3Fh ;DRVTBL add hl,de jp (hl) ; enable_M: ld hl,rdbuf ld de,rdbuf+1 ld bc,7fh ld (hl),0E5h ldir call drvtbl ld de,24 ld bc,(m_dph) add hl,de ld (hl),c inc hl ld (hl),b ; ; CP/M seems to be very stubborn about reloading cached directory buffers ; looking at M:. Force it to. ; ld c,32h ;Force CP/M to examine drive M: ld de,biosp1 call FDOS or a jr nz,disend ld c,32h ld de,biosp2 call FDOS ld c,32h ld de,biosp3 call FDOS ld c,32h ld de,biosp4 call FDOS ld c,32h ld de,biosp5 call FDOS disend: ld c,30h ;Force empty of all disc buffers (which might be ld e,0FFh ;looking at M:) call FDOS ret ; biosp1: defw 9, 0Ch, 0, 0 ;SELDSK to M: biosp2: defw 10, 3, 0, 0 ;SETTRK to track 3 (directory) biosp3: defw 11, 1, 0, 0 ;SETSEC to sector 1 biosp4: defw 12,rdbuf,0, 0 ;SETDMA to RDBUF biosp5: defw 14, 1, 0, 0 ;WRITE data ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Output_stream directive. ; ;<< v1.00 record state of printer output ; strm1: cp 2 jr z,ptron cp 0FEh jr z,ptroff scf ret ; ptron: defb 03Eh ptroff: xor a ld (printing),a scf ret ; printing: defb 0 ; ;>> v1.00 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; fopn: ld (tfcb),hl ex de,hl call zfcb ld a,b or a ;0=open 1=create jr nz,creat ld a,80h ld (rdptr),a ld c,0Fh call FDOS cp 0FFh scf ret nz ld a,h or a jr z,nferr xfcber: push bc jp xlterr ; nferr: ld hl,nfer$ xor a ret ; creat: xor a ld (wrptr),a ld c,13h call FDOS ld de,(tfcb) ld c,16h call FDOS cp 0FFh scf ret nz push bc jp xlterr ; tfcb: defw 0 ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; fclse: ld a,(wrptr) or a scf call nz,putrec ret nc ld de,(tfcb) ld c,10h call FDOS cp 0FFh scf ret nz push bc jp xlterr ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Memory read & write functions ; rdbuf: defs 80h wrbuf: defs 80h rdptr: defb 80h wrptr: defb 0 ; fread: ld a,b or c scf ret z call getbyt ret nc ld (hl),a inc hl dec bc jr fread ; fwrite: ld a,b or c scf ret z ld a,(hl) call putbyt ret nc inc hl dec bc jr fwrite ; fwmem: ld a,b or c scf ret z call ZXPK64 call putbyt ret nc inc hl dec bc jr fwmem ; frmem: ld a,b or c scf ret z call getbyt ret nc call ZXPOKE inc hl dec bc jr frmem ; getbyt: push hl push bc ld a,(rdptr) cp 80h call nc,getrec jr nc,endit ld l,a ld h,0 ld de,rdbuf inc a ld (rdptr),a add hl,de ld a,(hl) scf endit: pop bc pop hl scf ret ; putbyt: push hl push bc ld c,a ld a,(wrptr) cp 80h call nc,putrec jr nc,endit ld l,a ld h,0 ld de,wrbuf inc a ld (wrptr),a add hl,de ld (hl),c scf jr endit ; getrec: ld de,rdbuf call setdma ld c,14h ld de,(tfcb) call FDOS or a scf ret z push bc jp xlterr ; putrec: push bc ld de,wrbuf call setdma ld c,15h ld de,(tfcb) call FDOS pop bc or a scf ret z push bc jp xlterr ;