Zapple-II/SOFTCARD80.ASM#040000
2019-10-13 19:46:02 -04:00

1 line
7.6 KiB
Plaintext

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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 05200H ; Top of Z80 stack
SOFTCARD EQU 0E400H ; Softcard in slot 4 ($C400)
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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; BDOS entry point must be at address 0005H for CP/M compatibility
; Function to invoke is passed in C
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ORG 0005H
BDOS JP BDOSIMP ; BDOS code is at top of memory
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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
; BDOS data
CURDRV DEFB 0 ; Currently logged in drive