diff --git a/SOFTCARD65#060900 b/SOFTCARD65#060900 new file mode 100644 index 0000000..715d881 Binary files /dev/null and b/SOFTCARD65#060900 differ diff --git a/SOFTCARD65#069000 b/SOFTCARD65#069000 deleted file mode 100644 index 6875100..0000000 Binary files a/SOFTCARD65#069000 and /dev/null differ diff --git a/SOFTCARD65.S#040000 b/SOFTCARD65.S#040000 index 4bd56be..879e9c0 100644 --- a/SOFTCARD65.S#040000 +++ b/SOFTCARD65.S#040000 @@ -1 +1 @@ - ڸ 䍪 ڸ Ǡ Ġՠô Ġՠ Ǡՠ ڸǠՠ ڸǠՠ ڸҠՠ  ڸȠՠà ҍӠՠĠ ڸ̠ՠĠ 썍ԍĠ ڸ ҠҠ ڸ 占РԠ ōˠ 򍍪 ڸ ҠčР Ľ 占Šı 덠 РŠ 卺ıР Ľ ڸŠIJӠ Р Ǡ ؠǠ ٠Ǡ ҠР ҍǠ ؠǠ ٠Ǡ Р 덠Ӡ Ӡ ڸIJР Ľ ڸŠij 捠Ӡ ڸijР Ľ ڸŠĴԠð ɠóӠ 占 ǍРóŠ óӠ ǍóŠǠ ڸӠ ڸĴҠ̠ ˠ РРҩ \ No newline at end of file + ڸ 䍪 ڸ ǠĠՠô Ġՠ Ǡՠ ڸǠՠ ڸǠՠ ڸҠՠ  ڸȠՠà ҍӠՠĠ ڸ̠ՠĠ 썍ԍĠ ڸ ҠҠ ڸ 占РԠ ōˠ 򍍪 ڸ 卪 Ľ 卪 Ľ ڸ Ľ ڸ Ľ 󍪪ҠčР Ľ 占Šı 덠 РŠ 卺ıР Ľ ڸŠIJӠ Р Ǡ ؠǠ ٠Ǡ ҠР ҩǠ ؠǠ ٠Ǡ Р 덠Ӡ Ӡ ڸIJР Ľ ڸŠijРҩ ɧ 򩍠Ǡ ڸӠ ڸijР Ľ ڸŠĴԠð ɠóӠ 占 ǍРóŠ óӠ ǍóŠǠ ڸӠ ڸĴҠ̠ ˠ РРҩ \ No newline at end of file diff --git a/SOFTCARD80.ASM#040000 b/SOFTCARD80.ASM#040000 index ca572c0..322652a 100644 --- a/SOFTCARD80.ASM#040000 +++ b/SOFTCARD80.ASM#040000 @@ -1 +1 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Z80 code running on Softcard ; This is invoked by the companion SOFTCARD65 6502 code ; Bobbi 2019 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BDOSADDR EQU 05000H ; Keep below 32K for now (Z80asm bug) STCKTOP EQU 06000H ; Top of Z80 stack SOFTCARD EQU 0E400H ; Softcard in slot 4 ($C400) ; 6502 zero page, in Z80 address space CMD EQU 0F006H ; 6502 $06 AREG EQU 0F007H ; 6502 $07 XREG EQU 0F008H ; 6502 $08 YREG EQU 0F009H ; 6502 $09 ADDR EQU 0F0EBH ; 6502 $EB (LSB) ADDRH EQU 0F0ECH ; 6502 $EC (MSB) ; Addresses of 6502 routines, in 6502 address space COUT EQU 0FDEDH ; Print char in A RDKEY EQU 0FD0CH ; Read key, return in A BELL EQU 0FBE4H ; Sound the bell ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Entry point when Z80 cold starts is 0000H ORG 0000H JP BDOSINIT ; Initialize BDOS ; See obsolescence.wix.com/obsolescence/cpm-internals for info ; on low storage usage ... IOBYTE DEFB 0 ; Maps virtual to real devices CURDRV DEFB 0 ; Currently selected drive 0=A: etc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; BDOS entry point must be at address 0005H for CP/M compatibility ; Function to invoke is passed in C ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BDOS JP BDOSIMP ; BDOS code is at top of memory RVEC1 DEFW 0000H ; Restart vector 1 RVEC2 DEFW 0000H ; Restart vector 2 RVEC3 DEFW 0000H ; Restart vector 3 RVEC4 DEFW 0000H ; Restart vector 4 RVEC5 DEFW 0000H ; Restart vector 5 RVEC6 DEFW 0000H ; Restart vector 6 RVEC7 DEFW 0000H ; Restart vector 7 ORG 0060H ; Standard addr of 32 byte FCB FCBDRV DEFB 00H ; FCB Drive (0 current, 1 A:, 2 B: etc) FCBNAM DEFM 'FILENAMEEXT' ; FCB filename and extension FCBEX DEFB 00H ; FCB extent field FCBS1 DEFB 00H ; FCB S1 field FCBS2 DEFB 00H ; FCB S2 field FCBRC DEFB 00H ; FCB RC field FCBMAP DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; File map (16 bytes) ORG 0080H ; Standard addr of 128 byte File Buffer FILEBUF DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The application program proper starts at 0100H ; in order to be compatible with CP/M .COM programs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG 0100H STCHAR EQU 65 ; First character code 'A' ENDCHAR EQU 91 ; Last character code 'Z' ; Print signon message using C_WRITESTR PROGSTRT LD DE,WELCOME ; Address of string LD C,09H ; C_WRITESTR call CALL BDOS ; CP/M BDOS call ; Print the alphabet using C_WRITE LD B,STCHAR L1 LD E,B ; Character to print LD C,2 ; C_WRITE call PUSH BC ; Preserve B (and C) CALL BDOS ; CP/M BDOS call POP BC ; Restore B (and C) INC B LD A,ENDCHAR CP B JP Z,S1 JP L1 ; Loop until there is a keystroke waiting using C_STAT S1 LD C,0BH ; C_STAT call CALL BDOS ; CP/M BDOS call CP 0 ; Anything? JR Z,S1 ; If not, loop ; Print a couple of spaces LD E,32 ; LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call LD E,32 ; LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call ; Read keyboard and echo to screen C_READ, C_WRITE L2 LD C,1 ; C_READ call CALL BDOS ; CP/M BDOS call LD E,A ; Prepare to echo keystroke LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call JP L2 ; Forever and ever WELCOME DEFM 'ZAPPLE-II TEST STUB' DEFB 13, '$' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Implementation of BDOS ; Function to invoke is passed in C ; C=00H C_TERMCPM System reset ; C=01H C_READ Console read ; C=02H C_WRITE Console write ; C=09H C_WRITESTR Console write string ; C=0BH C_STAT Console status ; C=0CH S_BDOSVER Return version number ; C=0DH DRV_ALLRESET Reset disks ; C=0EH DRV_SET Select disk ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG BDOSADDR BDOSINIT LD SP,STCKTOP ; Initialize SP JP PROGSTRT ; Run user program BDOSIMP LD A,C ; Prepare to check C is in range CP 41 ; Max syscall# for CP/M 2.2 is 40 JP NC,UNIMP ; If >41 then call UNIMP LD HL,BDOSVEC ; Start of vector table SLA C ; Multiply C by 2 LD B,0 ; MSB of BC is zero ADD HL,BC ; Address of vector in HL LD C,(HL) ; Read LSB of address to jump to INC HL ; Read MSB of address to jump to LD H,(HL) ; ... LD L,C ; Address needs to be in HL JP (HL) ; Jump to it! ; Vector table BDOSVEC DEFW C_TERMCPM ; C=00H DEFW C_READ ; C=01H DEFW C_WRITE ; C=02H DEFW UNIMP ; C=03H DEFW UNIMP ; C=04H DEFW UNIMP ; C=05H DEFW UNIMP ; C=06H DEFW UNIMP ; C=07H DEFW UNIMP ; C=08H DEFW C_WRITESTR ; C=09H DEFW UNIMP ; C=0AH DEFW C_STAT ; C=0BH DEFW S_BDOSVER ; C=0CH DEFW DRV_ALLRST ; C=0DH DEFW DRV_SET ; C=0EH DEFW UNIMP ; C=0FH DEFW UNIMP ; C=10H DEFW UNIMP ; C=11H DEFW UNIMP ; C=12H DEFW UNIMP ; C=13H DEFW UNIMP ; C=14H DEFW UNIMP ; C=15H DEFW UNIMP ; C=16H DEFW UNIMP ; C=17H DEFW UNIMP ; C=18H DEFW UNIMP ; C=19H DEFW UNIMP ; C=1AH DEFW UNIMP ; C=1BH DEFW UNIMP ; C=1CH DEFW UNIMP ; C=1DH DEFW UNIMP ; C=1EH DEFW UNIMP ; C=1FH DEFW UNIMP ; C=20H DEFW UNIMP ; C=21H DEFW UNIMP ; C=22H DEFW UNIMP ; C=23H DEFW UNIMP ; C=24H DEFW UNIMP ; C=25H DEFW UNIMP ; C=26H DEFW UNIMP ; C=27H ; Unimplemented BDOS call, just ring the bell UNIMP LD HL,BELL ; We are going to call BELL LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! RET ; Return to calling program ; System reset. Jump to $0000 - doesn't return C_TERMCPM RST 0 ; Quick jump to zero ; Wait for a character from the console, return it in A and L ; Also echoes the char to the console C_READ LD HL,RDKEY ; We are going to call RDKEY LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! LD A,(AREG) ; Grab the return value PUSH AF ; Preserve A (and F) LD HL,COUT ; Echo the character using COUT LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! POP AF ; Restore A (and F) AND 7FH ; Mask high bit LD L,A ; Copy A to L RET ; Return to calling program ; Write character in E to the console ; TODO: Handle tabs, ^S and ^Q C_WRITE LD A,80H ; Set high bit OR E ; ... CP 8AH ; Check for linefeed RET Z ; If LF, don't print it LD (AREG),A ; Pass char to COUT in 6502 A LD HL,COUT ; We are going to call COUT LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! RET ; Return to calling program ; Write ASCII string to console. '$' is the terminator ; DE contains the address of the string C_WRITESTR LD A,(DE) ; Fetch character from string CP 36 ; Is it '$'? RET Z ; If so, we are done PUSH DE ; We are gonna need E LD E,A ; For C_WRITE CALL C_WRITE ; Sent char to console POP DE ; Recover the pointer INC DE ; Advance pointer JP C_WRITESTR ; Handle the next char ; Returns 0 in A and L if no chars waiting, non zero otherwise C_STAT LD A,3 ; CMD=3 means peek at keyboard LD (CMD),A ; ... LD (SOFTCARD),A ; Do it LD A,(AREG) ; Grab the return value LD L,A ; Copy A to L RET ; Return to calling program ; Returns system type in B and H, BDOS version in A and L S_BDOSVER LD B,0 ; System is 8080 CP/M LD H,B ; Also in H LD A,22H ; Pretend to v2.2 LD L,A ; Also in A RET ; Reset disks ; Makes A: drive the default ; TODO: Empty all disk buffers, reset DMA address to BOOT+80H DRV_ALLRST LD A,0 ; 0 means drive A: LD (CURDRV),A ; Store in CURDRV RET ; Select disk ; Disk to select is passed in E (A: is 0, B: is 1 etc.) ; Return 00 for success, 0FFH for error in A and L DRV_SET LD A,E ; Prepare to compare disk number CP 2 ; Support two 'drives' A:,B: JP NC,DSERR ; If >1 LD (CURDRV),A ; Store the requested drive number LD A,0 ; Return code meaning success JP DSRET ; DSERR LD A,0FFH ; Return code for error DSRET LD L,A ; Return code in L too RET \ No newline at end of file +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Z80 code running on Softcard ; Implements CP/M style BDOS interface ; Requires the companion SOFTCARD65 6502 code ; Bobbi 2019 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BDOSADDR EQU 05000H ; Keep below 32K for now (Z80asm bug) STCKTOP EQU 06000H ; Top of Z80 stack SOFTCARD EQU 0E400H ; Softcard in slot 4 ($C400) ; 6502 zero page, in Z80 address space CMD EQU 0F006H ; 6502 $06 AREG EQU 0F007H ; 6502 $07 XREG EQU 0F008H ; 6502 $08 YREG EQU 0F009H ; 6502 $09 ADDR EQU 0F0EBH ; 6502 $EB (LSB) ADDRH EQU 0F0ECH ; 6502 $EC (MSB) ; Addresses of 6502 routines, in 6502 address space COUT EQU 0FDEDH ; Print char in A RDKEY EQU 0FD0CH ; Read key, return in A BELL EQU 0FBE4H ; Sound the bell ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Entry point when Z80 cold starts is 0000H ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG 0000H JP BDOSINIT ; Initialize BDOS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; See obsolescence.wix.com/obsolescence/cpm-internals for info ; on low storage usage ... ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; IOBYTE DEFB 0 ; Maps virtual to real devices CURDRV DEFB 0 ; Currently selected drive 0=A: etc ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; BDOS entry point must be at address 0005H for CP/M compatibility ; Function to invoke is passed in C ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BDOS JP BDOSIMP ; BDOS code is at top of memory RVEC1 DEFW 0000H ; Restart vector 1 RVEC2 DEFW 0000H ; Restart vector 2 RVEC3 DEFW 0000H ; Restart vector 3 RVEC4 DEFW 0000H ; Restart vector 4 RVEC5 DEFW 0000H ; Restart vector 5 RVEC6 DEFW 0000H ; Restart vector 6 RVEC7 DEFW 0000H ; Restart vector 7 ; Space for private BDOS data (implementation dependent) up to 0060H DMAADDR DEFW 0080H ; DMA address defaults to FILEBUF (0080H) LOGVEC DEFW 0000H ; Vector of logged in drives ROVEC DEFW 0000H ; Vector of read-only drives USERNUM DEFB 00H ; User number ; End of private, implementation dependent space ORG 0060H ; Standard addr of 32 byte FCB FCBDRV DEFB 00H ; FCB Drive (0 current, 1 A:, 2 B: etc) FCBNAM DEFM 'FILENAMEEXT' ; FCB filename and extension FCBEX DEFB 00H ; FCB extent field FCBS1 DEFB 00H ; FCB S1 field FCBS2 DEFB 00H ; FCB S2 field FCBRC DEFB 00H ; FCB RC field FCBMAP DEFS 16 ; Map of blocks in file ORG 0080H ; Standard addr of 128 byte File Buffer FILEBUF DEFS 128 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The application program proper starts at 0100H ; in order to be compatible with CP/M .COM programs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG 0100H ; Print signon message using C_WRITESTR PROGSTRT LD DE,WELCOME ; Address of string LD C,09H ; C_WRITESTR call CALL BDOS ; CP/M BDOS call ; Print the alphabet using C_WRITE LD B,'A' L1 LD E,B ; Character to print LD C,02H ; C_WRITE call PUSH BC ; Preserve B (and C) CALL BDOS ; CP/M BDOS call POP BC ; Restore B (and C) INC B LD A,'Z' CP B JP Z,S1 JP L1 ; Loop until there is a keystroke waiting using C_STAT S1 LD C,0BH ; C_STAT call CALL BDOS ; CP/M BDOS call CP 0 ; Anything? JR Z,S1 ; If not, loop ; Print a couple of asterisks LD E,'*' ; LD C,02H ; C_WRITE call CALL BDOS ; CP/M BDOS call LD E,'*' ; LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call ; Create a file using ProDOS MLI ; Creates 'A/TESTFILE.TMP' ; Directory 'A' needs to exist already LD DE,MSG1 ; Address of string LD C,09H ; C_WRITESTR call CALL BDOS ; CP/M BDOS call LD A,1 ; A: drive LD (FCBDRV),A ; LD A,'T' ; Filename LD (FCBNAM),A ; LD A,'E' ; Filename LD (FCBNAM+1),A ; LD A,'S' ; Filename LD (FCBNAM+2),A ; LD A,'T' ; Filename LD (FCBNAM+3),A ; LD A,'F' ; Filename LD (FCBNAM+4),A ; LD A,'I' ; Filename LD (FCBNAM+5),A ; LD A,'L' ; Filename LD (FCBNAM+6),A ; LD A,'E' ; Filename LD (FCBNAM+7),A ; LD A,'T' ; Extension LD (FCBNAM+8),A ; LD A,'M' ; Extension LD (FCBNAM+9),A ; LD A,'P' ; Extension LD (FCBNAM+10),A ; LD DE,0060H ; Default FCB address LD C,16H ; F_MAKE call CALL BDOS ; CP/M BDOS call LD A,(AREG) ; Look at the return code CP 0 ; Success? JP Z,SUCC ; LD DE,FAILMSG ; Fail message JP PRMSG ; SUCC LD DE,SUCCMSG ; Success message PRMSG LD C,09H ; C_WRITESTR call CALL BDOS ; CP/M BDOS call ; Read keyboard and echo to screen C_READ, C_WRITE L2 LD C,1 ; C_READ call CALL BDOS ; CP/M BDOS call LD E,A ; Prepare to echo keystroke LD C,2 ; C_WRITE call CALL BDOS ; CP/M BDOS call JP L2 ; Forever and ever WELCOME DEFB 13 DEFM 'Zapple-II Test Stub...' DEFB 13, '$' MSG1 DEFB 13 DEFM 'Creating A/TESTFILE.TMP' DEFB 13, '$' SUCCMSG DEFM 'Success!' DEFB 13, '$' FAILMSG DEFM 'FAIL!' DEFB 13, '$' ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Implementation of CP/M STYLE BDOS ; Function to invoke is passed in C ; C=00H C_TERMCPM System reset ; C=01H C_READ Console read ; C=02H C_WRITE Console write ; C=07H GET_IOB Get IOBYTE ; C=08H SET_IOB Set IOBYTE ; C=09H C_WRITESTR Console write string ; C=0BH C_STAT Console status ; C=0CH S_BDOSVER Return version number ; C=0DH DRV_ALLRESET Reset disks ; C=0EH DRV_SET Select disk ; C=0FH F_OPEN Open file (IN PROGRESS) ; C=10H F_CLOSE Close file (IN PROGRESS) ; C=16H F_MAKE Create file (IN PROGRESS) ; C=17H DRV_LOGVEC Return bitmap of logged-in drives ; C=19H DRV_GET Return current drive ; C=1AH F_DMAOFF Set DMA address ; C=1CH DRV_SETRO Software write-protect current drive ; C=1DH DRV_ROVEC Return bitmap of read-only drives ; C=20H F_USERNUM Get/set user number ; C=25H DRV_RESET Selectively reset disk drives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ORG BDOSADDR BDOSINIT LD SP,STCKTOP ; Initialize SP LD A,0 ; LD (IOBYTE),A ; Initialize IOBYTE LD (CURDRV),A ; Drive A: LD (FRN1),A ; Initialize FRNs to zero LD (FRN2),A ; ... LD (FRN3),A ; ... LD (FRN4),A ; ... LD HL,0080H ; Initialize DMAADDR to 0080H LD (DMAADDR),HL ; ... LD HL,0000H ; Initialize LOGVEC & ROVEC to 0000H LD (LOGVEC),HL ; ... LD (ROVEC),HL ; ... JP PROGSTRT ; Run user program BDOSIMP LD A,C ; Prepare to check C is in range CP 41 ; Max syscall# for CP/M 2.2 is 40 JP NC,UNIMP ; If >41 then call UNIMP LD HL,BDOSVEC ; Start of vector table SLA C ; Multiply C by 2 LD B,0 ; MSB of BC is zero ADD HL,BC ; Address of vector in HL LD C,(HL) ; Read LSB of address to jump to INC HL ; Read MSB of address to jump to LD H,(HL) ; ... LD L,C ; Address needs to be in HL JP (HL) ; Jump to it! ; Vector table BDOSVEC DEFW C_TERMCPM ; C=00H DEFW C_READ ; C=01H DEFW C_WRITE ; C=02H DEFW UNIMP ; C=03H (A_READ) AUX DEFW UNIMP ; C=04H (A_WRITE) AUX DEFW UNIMP ; C=05H (L_WRITE) PRN DEFW UNIMP ; C=06H (C_RAWIO) DEFW GET_IOB ; C=07H DEFW SET_IOB ; C=08H DEFW C_WRITESTR ; C=09H DEFW UNIMP ; C=0AH (C_READSTR) DEFW C_STAT ; C=0BH DEFW S_BDOSVER ; C=0CH DEFW DRV_ALLRST ; C=0DH DEFW DRV_SET ; C=0EH DEFW F_OPEN ; C=0FH DEFW F_CLOSE ; C=10H DEFW UNIMP ; C=11H (F_SFIRST) DEFW UNIMP ; C=12H (F_SNEXT) DEFW UNIMP ; C=13H (F_DELETE) DEFW UNIMP ; C=14H (F_READ) DEFW UNIMP ; C=15H (F_WRITE) DEFW F_MAKE ; C=16H DEFW UNIMP ; C=17H (F_RENAME) DEFW DRV_LOGVEC ; C=18H DEFW DRV_GET ; C=19H DEFW F_DMAOFF ; C=1AH DEFW UNIMP ; C=1BH (DRV_ALLOCVEC) DEFW DRV_SETRO ; C=1CH DEFW DRV_ROVEC ; C=1DH DEFW UNIMP ; C=1EH (F_ATTRIB) DEFW UNIMP ; C=1FH (DRV_DPB) DEFW F_USERNUM ; C=20H DEFW UNIMP ; C=21H (F_READRAND) DEFW UNIMP ; C=22H (F_WRITERAND) DEFW UNIMP ; C=23H (F_SIZE) DEFW UNIMP ; C=24H (F_RANDREC) DEFW DRV_RESET ; C=25H DEFW UNIMP ; C=26H (*nothing* in CP/M 2.2) DEFW UNIMP ; C=27H (*nothing* in CP/M 2.2) DEFW UNIMP ; C=28H (F_WRITEZF) ; Unimplemented BDOS call, just ring the bell UNIMP LD HL,BELL ; We are going to call BELL LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! RET ; Return to calling program ; System reset. Jump to $0000 - doesn't return C_TERMCPM RST 0 ; Quick jump to zero ; Wait for a character from the console, return it in A and L ; Also echoes the char to the console C_READ LD HL,RDKEY ; We are going to call RDKEY LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! LD A,(AREG) ; Grab the return value PUSH AF ; Preserve A (and F) LD HL,COUT ; Echo the character using COUT LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! POP AF ; Restore A (and F) AND 7FH ; Mask high bit LD L,A ; Copy A to L RET ; Return to calling program ; Write character in E to the console ; TODO: Handle tabs, ^S and ^Q C_WRITE LD A,80H ; Set high bit OR E ; ... CP 8AH ; Check for linefeed RET Z ; If LF, don't print it LD (AREG),A ; Pass char to COUT in 6502 A LD HL,COUT ; We are going to call COUT LD (ADDR),HL ; ... LD A,1 ; CMD=1 means call 6502 sub LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! RET ; Return to calling program ; Get the IOBYTE in A and L GET_IOB LD A,(IOBYTE) ; LD L,A ; Copy to L RET ; Set the IOBYTE ; E contains the IOBYTE value to set SET_IOB LD A,E ; LD (IOBYTE),A ; RET ; Write ASCII string to console. '$' is the terminator ; DE contains the address of the string C_WRITESTR LD A,(DE) ; Fetch character from string CP '$' ; Is it '$'? RET Z ; If so, we are done PUSH DE ; We are gonna need E LD E,A ; For C_WRITE CALL C_WRITE ; Sent char to console POP DE ; Recover the pointer INC DE ; Advance pointer JP C_WRITESTR ; Handle the next char ; Returns 0 in A and L if no chars waiting, non zero otherwise C_STAT LD A,3 ; CMD=3 means peek at keyboard LD (CMD),A ; ... LD (SOFTCARD),A ; Do it LD A,(AREG) ; Grab the return value LD L,A ; Copy A to L RET ; Return to calling program ; Returns system type in B and H, BDOS version in A and L S_BDOSVER LD B,0 ; System is 8080 CP/M LD H,B ; Also in H LD A,22H ; Pretend to v2.2 LD L,A ; Also in A RET ; Reset disks ; Makes A: drive the default ; TODO: Empty all disk buffers DRV_ALLRST LD A,0 ; 0 means drive A: LD (CURDRV),A ; Store in CURDRV LD BC,FILEBUF ; FILEBUF is at 0080H LD (DMAADDR),BC ; Reset DMA address RET ; Select disk ; Disk to select is passed in E (A: is 0, B: is 1 etc.) ; Return 00 for success, 0FFH for error in A and L DRV_SET LD A,E ; Prepare to compare disk number CP 16 ; Support 16 'drives' A: - P: JP NC,DSERR ; If A>15 ... error LD (CURDRV),A ; Store the requested drive number LD A,0 ; Return code meaning success JP DSRET ; DSERR LD A,0FFH ; Return code for error DSRET LD L,A ; Return code in L too RET ; Open file ; DE is the address of the FCB describing the file to open ; Returns error codes in A and L: ; 0 through 3 for success, 0FFH is file not found F_OPEN CALL MAKEPATH ; Populate PATHLEN and PATH ; Work out which IOBUF to allocate for this file ; And set the pointer in the ProDOS parameter list LD A,(FRN1) ; See if IOBUF1 is free CP 0 ; ... JP Z,FOS1 ; IOBUF1 is free LD A,(FRN2) ; See if IOBUF2 is free CP 0 ; ... JP Z,FOS2 ; IOBUF2 is free LD A,(FRN3) ; See if IOBUF3 is free CP 0 ; ... JP Z,FOS3 ; IOBUF3 is free LD A,(FRN4) ; See if IOBUF4 is free CP 0 ; ... JP Z,FOS4 ; IOBUF4 is free LD A,0FFH ; Error too many files open LD L,A ; Copy to L RET ; Error return FOS1 LD HL,IOBUF1 ; Address of IOBUF1 LD A,1 ; Buffer 1 JP FOS5 ; FOS2 LD HL,IOBUF2 ; Address of IOBUF2 LD A,2 ; Buffer 2 JP FOS5 ; FOS3 LD HL,IOBUF3 ; Address of IOBUF3 LD A,3 ; Buffer 3 JP FOS5 ; FOS4 LD HL,IOBUF4 ; Address of IOBUF4 LD A,4 ; Buffer 4 FOS5 LD (FOMLII),HL ; Store in parameter list LD (FOIOB),A ; Record the buffer index for later ; ; TODO NOW THE CODE TO ACTUALLY INVOKE THE MLI CALL ON 6502 ; ; ; TODO HANDLE ERROR RETURN CODES FROM MLI ; LD A,(FOMLIN) ; Get ProDOS file reference number LD H,D ; Pointer to FCB ... LD L,E ; ... into HL LD BC,14 ; Offset to S2 field (reserved field) ADD HL,BC ; Compute address LD (HL),A ; Store file refence number is S2 field LD A,(FOIOB) ; Obtain IOBUF idx (1,2,3,4) CP 1 ; Is it 1? ... JP Z,FOS6 ; ... yes CP 2 ; Is it 2? ... JP Z,FOS7 ; ... yes CP 3 ; Is it 3? ... JP Z,FOS8 ; ... yes JP FOS9 ; Must be 4 FOS6 LD A,(FOMLIN) ; LD (FRN1),A ; JP FOS10 ; FOS7 LD A,(FOMLIN) ; LD (FRN2),A ; JP FOS10 ; FOS8 LD A,(FOMLIN) ; LD (FRN3),A ; JP FOS10 ; FOS9 LD A,(FOMLIN) ; LD (FRN4),A ; JP FOS10 ; FOS10 ; ; TODO HANDLE RETURN CODE ON SUCCESS (0,1,2,3) ; RET FOMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code DEFB 3 ; ProDOS PL: Three parameters FOMLIP DEFW PATHLEN ; ProDOS PL: pointer to path FOMLII DEFW 0000H ; ProDOS PL: pointer to IO buffer FOMLIN DEFB 0 ; ProDOS PL: File reference number FOIOB DEFB 0 ; Local variable to record IOBUF idx DEFB 60H ; RTS in 6502 code ; Close file ; DE is the address of the FCB describing the file to close ; Returns error codes in BA and HL: ; TODO WRITE THIS!!!! F_CLOSE ; Steps: ; 1) Get the file reference number from field S2 of FCB & fill in FCMLIN ; 2) Hand off pointer to the JSR instruction to 6502 to run ; 3) On return, check return code and set BA and HL accordingly RET FCMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code DEFB 1 ; ProDOS PB: One parameter FCMLIN DEFB 0 ; ProDOS PB: File reference number DEFB 60H ; RTS in 6502 code ; Create (and open) file ; DE is the address of the FCB describing the file to create ; Returns error codes in A and L: ; 0 through 3 for success, 0FFH is file could not be created F_MAKE CALL MAKEPATH ; Populate PATHLEN and PATH LD HL,FMMLI ; Pass address of 6502 JSR instruction LD (ADDR),HL ; ... LD A,2 ; CMD=2 means ProDOS MLI call LD (CMD),A ; ... LD (SOFTCARD),A ; Do it! LD A,(AREG) ; Get return code from the MLI call CP 0 ; See if there was an error JP NZ,FMS1 ; Handle error ;;;; CALL F_OPEN ; Open the file using same FCB (DE ptr) LD A,0 ;;; TEMP DEBUG - SHOULD CALL F_OPEN ABOVE ;;; RET FMS1 LD A,0FFH ; 0FFH for error LD L,A ; Return code in L also RET RET FMMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code DEFB 7 ; ProDOS PL: Seven parameters FMMLIP DEFW PATHLEN ; ProDOS PL: Pointer to path FMMLIA DEFB 0C3H ; ProDOS PL: Access (0C3H full access) FMMLIT DEFB 0 ; ProDOS PL: File type (0 means typeless) FMMLIAT DEFW 0000H ; ProDOS PL: Aux file type (always 0000H) FMMLIS DEFB 1 ; ProDOS PL: Storage type (1 for file) FMMLICD DEFW 0000H ; ProDOS PL: Create date (always 0000H) FMMLICT DEFW 0000H ; ProDOS PL: Create time (always 0000H) DEFB 60H ; RTS in 6502 code ; Return bitmap of logged-in drives in HL DRV_LOGVEC LD HL,(LOGVEC) ; RET ; Return current drive in A DRV_GET LD A,(CURDRV) ; RET ; Set DMA address ; DMA address is passed in DE F_DMAOFF LD (DMAADDR),DE ; RET ; Software write-protect current disk DRV_SETRO LD B,80H ; Set MS-bit in B, will rotate this below LD A,(CURDRV) ; Current drive (0 A:, 1 B: ...) INC A ; It is easier if A: is 1, B is 2 etc. CP 9 ; See if it in LS-byte or MS-byte of ROVEC JP NC,DSRMSB ; If A>8 then drive bit in in MS-byte DSRL1 RL B ; DEC A ; JP NZ,DSRL1 ; LD A,(ROVEC) ; Fetch the LS-byte of ROVEC OR B ; Set the bit using OR LD (ROVEC),A ; Store LS-byte back to ROVEC RET ; We're done DSRMSB LD C,8 ; Subtract 8 from the drive number SUB C ; A = A-8 DSRL2 RL B ; DEC A ; JP NZ,DSRL2 ; LD A,(ROVEC+1) ; Fetch the MS-byte of ROVEC OR B ; Set the bit using OR LD (ROVEC+1),A ; Store MS-byte back to ROVEC RET ; Return bitmap of read-only drives in HL DRV_ROVEC LD HL,(ROVEC) ; Bit 0 of L is A:, bit 7 of H is P: RET ; Get/set user number ; E contains the user number to set, or 0FFH to get current user number ; If E is 0FFH then user number is returned in A F_USERNUM LD A,E ; See if it is get or set CP 0FFH ; 0FFH means 'get' JP Z,FUNS1 ; It is 'get' LD (USERNUM),A ; Set user number RET FUNS1 LD A,(USERNUM) ; Get user number RET ; Selectively reset disk drives ; DE contains bitmap of drives to reset (bit 0 of E if A:, bit 7 of D is P:) ; Returns A=00H if okay, A=FFH if error ; Resetting means removing the read-only status DRV_RESET LD A,(ROVEC) ; Do the LSB ... XOR E ; ... with E LD (ROVEC),A ; LD A,(ROVEC+1) ; Then the MSB ... XOR D ; ... with D LD (ROVEC+1),A ; RET ; Populate the PATH buffer (and PATHLEN) by copying from FCB ; DE contains a pointer to the FCB ; Be sure not to trash DE ; TODO FINISH THIS!!! MAKEPATH LD A,(DE) ; Get drive number from FCB CP 0 ; See if it is zero (default drive) JP NZ,MPS1 ; If drive explicit LD A,(CURDRV) ; If default drive use CURDRV INC A ; CURDRV is zero based MPS1 ADD A,'A'-1 ; Convert to drive letter LD (PATH),A ; Store as first char of path LD A,'/' ; Second char of path is '/' LD (PATH+1),A ; ... LD C,2 ; Use C to count chars in filename LD H,D ; Copy address of FCB from DE ... LD L,E ; ... to HL INC HL ; HL points to filename in FCB LD IX,PATH+2 ; IX points to next char of path to write MPL1 LD A,(HL) ; Obtain filename character CP ' ' ; See if it is a space (? or NULL maybe?) JP Z,MPS3 ; If so we are done with filename EX AF,AF' ; We need to re-use A here LD A,C ; Get character count CP 10 ; Drive letter, slash and 8 char filename JP Z,MPS2 ; If so we are done with filename EX AF,AF' ; Swap back to original A reg LD (IX+0),A ; Copy to PATH buffer INC C ; Count the chars INC HL ; Next byte of filename in FCB INC IX ; Next byte of PATH buffer JP MPL1 ; Loop till done MPS2 EX AF,AF' ; Swap back to original A reg MPS3 ; EXTENSION ; ... LD A,C ; Store length of string LD (PATHLEN),A ; ... RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Additional private scratch space for BDOS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 64 bytes for storing a file path as a Pascal style string PATHLEN DEFB 0 PATH DEFS 64 ; Four 1024 byte ProDOS I/O buffers ; TODO THESE MUST START ON A PAGE BOUNDARY!!!! IOBUF1 DEFS 1024 IOBUF2 DEFS 1024 IOBUF3 DEFS 1024 IOBUF4 DEFS 1024 ; Record file reference numbers for each I/O buffer ; Or 0 if no file is using the buffer FRN1 DEFB 0 FRN2 DEFB 0 FRN3 DEFB 0 FRN4 DEFB 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; \ No newline at end of file diff --git a/SOFTCARD80.BIN#041000 b/SOFTCARD80.BIN#041000 index 0501379..63b5071 100644 Binary files a/SOFTCARD80.BIN#041000 and b/SOFTCARD80.BIN#041000 differ diff --git a/zapple2.po b/zapple2.po index 3104c6d..d78f910 100644 Binary files a/zapple2.po and b/zapple2.po differ