mirror of
https://github.com/bobbimanners/Zapple-II.git
synced 2024-06-17 17:30:50 +00:00
1 line
7.6 KiB
Plaintext
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
|
|
|
|
|