changeset 4:4206b7c12099

Add framework for handling disk files. Added a framework for handling disk files including a framework for trapping errors triggered by the Basic ROM.
author William Astle <lost@l-w.ca>
date Sun, 28 Dec 2014 12:53:41 -0700
parents 154ca5ac37d3
children 9c6ffcaccfa9
files dod.s
diffstat 1 files changed, 123 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/dod.s	Sun Dec 28 11:51:03 2014 -0700
+++ b/dod.s	Sun Dec 28 12:53:41 2014 -0700
@@ -203,6 +203,12 @@
 BLKLEN		equ $7d
 CBUFAD		equ $7e
 
+RSTFLG		equ $71
+DFLTYP		equ $957
+DASCFL		equ $958
+DEFDRV		equ $959
+DNAMBF		equ $94c
+
 RESVEC		equ $A027
 
 ; SWI routines
@@ -242,7 +248,7 @@
 
 PROGLOAD	equ $e00
 ; These items must be in this order by address.
-VARSTART	equ $4200			; start of variable space
+VARSTART	equ $4100			; start of variable space
 SCREEN0		equ $5000			; start of first graphic screen
 SCREEN1		equ $6800			; start of second grpahic screen
 TOPRAM		equ SCREEN1+$1800		; top of memory
@@ -253,6 +259,13 @@
 
 ; the direct page
 		org VARSTART
+hook_openi	rmb 2				; the address of the "open for input" routine in Disk ROM
+hook_openo	rmb 2				; the address of the "open for output" routine in the Disk ROM
+error_handler	rmb 2				; the installed error trap
+error_stack	rmb 2				; the stack address to use when transferring control to the error trap
+; These variables are additional ones used by extensions to the original ROM
+		rmb 256-(*&255)			; align the next bit on a page boundary
+; These are the original variables used by the system, including the direct page.
 zero		rmb 2				; initialized to $0000
 V202		rmb 1				; apparently unused
 allones		rmb 2				; initialized to $ffff
@@ -4585,11 +4598,120 @@
 		fcb $fe
 		fcc 'KSK'
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; The code in this area is the general file I/O handling system. It contains various bits to call into the Disk Basic ROM as
+; required to handle opening and closing files, etc.
+;
+; These routines hook the Basic error handler to prevent crashing. The way this works is a routine that wishes to trap errors
+; loads the address of its error handler in X and calls error_settrap. The error_settrap routine will record the requested handler
+; and install an extra routine that gets called when the caller returns. NOTE: only one error handler can be in play at a time.
+; The error handler can assume the stack pointer is at the exact place where it was when the parent routine called error_settrap
+; and should do whatever cleanup it needs to do then return normally (RTS or equivalent). In both the normal return case and
+; the error handler returning, the extra shim routine will be called to remove the installed error handler.
+;
+; The error handler can expect the error code from Basic doubled in B.
+;
+; If the error system is triggered without a handler in place, it will trigger a cold start by jumping to the RESET vector
+; address. This should work on both a coco1/2 and a coco3.
+;
+; This routine is called for disk routine traps if there is no recognized version of Disk Basic.
+disk_error	ldb #255			; internal error code equivalent of "ENOSYS"
+; fall through to error handler is intentional.
+; This is the error handler.
+error_hook	ldx error_handler		; is there a handler registered?
+		bne error_hook000		; brif so
+		clr RSTFLG			; force Basic to do a cold start
+		jmp [$FFFE]			; force a reset if we got an error we weren't expecting
+error_hook000	lds error_stack			; reset the stack to a known state
+		jmp ,x				; transfer control to the handler
+; This is the routine that installs a handler and the cleanup routine.
+error_settrap	stx error_handler		; set the handler
+		puls y				; get the return address back
+		ldx #error_return		; point to the trap cleanup routine
+		pshs x				; set so we return there
+		sts error_stack			; save the stack pointer for recovery
+		jmp ,y				; return to caller
+; This is the shim routine that clean out the error handler.
+error_return	pshs cc				; save flags
+		clr error_handler		; clear the hander
+		clr error_handler+1
+		puls cc,pc			; return to original caller
+; This routine opens a file for INPUT. If the file does not exist or cannot be opened for some reason, it will return
+; nonzero. Otherwise it returns zero. Enter with U pointing to the filename/extension (11 characters). On return,
+; DEVNUM will be set to the correct file number.
+file_openi	pshs d,x,y,u			; save registers
+		ldb #11				; copy 11 bytes
+		ldx #DNAMBF			; point to requested file name
+file_openi000	lda ,u+				; copy a byte
+		sta ,x+
+		decb				; done?
+		bne file_openi000		; brif not
+		ldx #file_openerr		; point to error handler for opening files
+		bsr error_settrap		; set Basic error handler
+		bsr set_basdp			; set direct page for Basic call
+		jsr [hook_openi]		; go call the file open handler in ROM
+		bsr restore_dp			; restore direct page
+		clra				; set Z for success
+		puls d,x,y,u,pc			; restore registers and return
+; This routine opens a file for OUTPUT. If the file does not exist or cannot be opened for some reason, it will return
+; nonzero. Otherwise it returns zero. Enter with U pointing to the filename/extension (11 characters). On return,
+; DEVNUM will be set to the correct file number. Enter with the type of file in A and the ascii flag in B.
+file_openo	pshs d,x,y,u			; save registers
+		std DFLTYP			; set file type and ASCII flag
+		ldb #11				; copy 11 bytes
+		ldx #DNAMBF			; point to requested file name
+file_openo000	lda ,u+				; copy a byte
+		sta ,x+
+		decb				; done?
+		bne file_openo000		; brif not
+		ldx #file_openerr		; point to error handler for opening files
+		bsr error_settrap		; set Basic error handler
+		bsr set_basdp			; set direct page for Basic call
+		jsr [hook_openo]		; go call the file open handler in ROM
+		bsr restore_dp			; restore direct page
+		clra				; set Z for success
+		puls d,x,y,u,pc			; restore registers and return
+; This is the error handler for opening files, both input and output.
+file_openerr	bsr restore_dp			; restore direct page
+		lda #1				; clear Z
+		puls d,x,y,u,pc			; restore registers and return
+; This routine sets the direct page properly for a Basic call
+set_basdp	pshs a				; save register
+		clra				; basic's DP is on page 0
+		tfr a,dp			; set DP
+		puls a,pc			; restore registers and return
+; This routine restores the direct page properly for Daggorath
+restore_dp	pshs cc,a			; save flags and temp
+		lda #zero/256			; get proper DP value
+		tfr a,dp			; set DP
+		puls cc,a,pc			; restore registers and return
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; This is an autostart loader. Theoretically, when LOADM completes, control should come here. It works by intercepting the
 ; RAM vector used by the Basic line input routine. Once Daggorath starts, that routine is never used for anything. Also, under
 ; both versions of Disk Basic, even though it is hooked by them it does nothing (just RTS). By setting the execution address
 ; also to LOADER, it means that even if, for some reason, control doesn't transfer here, EXEC will still do so.
 LOADER		clr $FF40			; turn off drive motors
+		lda #$7f			; opcode for JMP extended
+		ldx #error_hook			; point to the error handler
+		sta $191			; set up handler for the error routine at AC46
+		stx $192
+		ldx $C004			; get DSKCON address pointer
+		cmpx #$D75F			; is it disk basic 1.1?
+		beq load_disk11			; brif so
+		cmpx #$d66c			; is it disk basic 1.0?
+		beq load_disk10			; brif so
+		ldx #disk_error			; point to the routine for no disk system
+		stx hook_openi			; set the disk basic vectors to go there
+		stx hook_openo
+		bra load_001			; go finish initializing
+load_disk10	ldx #$C956			; address of "open system file for output" routine
+		ldu #$C959			; address of "open system file for input" routine
+		bra load_000			; go set vectors
+load_disk11	ldx #$CA04			; address of "open system file for output" routine
+		ldu #$CA07			; address of "Open system file for input" routine
+load_000	stx hook_openo			; save address of "open for output"
+		stu hook_openi			; save address of "open for input"
+load_001	clr error_handler		; mark the error handler as disabled
+		clr error_handler+1
 		jmp START			; transfer control to the main stream code
 ; This is the bit that intercepts RVEC12. It requires at least Extended Basic to work because it relies on multi-origin binaries.
 ; That will always be the case for Disk Basic. Theoretically, with just this, a loader will work from tape in Extended Basic