;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Z80 code running on Softcard
; This is invoked by the companion SOFTCARD65 6502 code
; Bobbi 2019
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

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		START			; Skip to the actual program

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; BDOS entry point must be at address 0005H for CP/M compatibility
; Function to invoke is passed in C
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
			ORG		0005H
BDOS		JP		BDOSI			; 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 the alphabet using C_WRITE
START		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
			
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Implementation of BDOS
; Function to invoke is passed in C
; C=01H   C_READ    Console read
; C=02H   C_WRITE   Console write
; C=0BH   C_STAT    Console status
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;			ORG		0D000H			; $B000 on 6502 - OK FOR NOW
			ORG		05000H			; Move it down to avoid Z80asm bug
BDOSI		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	UNIMP			; 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	UNIMP			; C=09H
			DEFW	UNIMP			; C=0AH
			DEFW	C_STAT			; C=0BH
			DEFW	UNIMP			; C=0CH
			; TODO: Complete this!!

; 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

; 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

; Returns 0 in A and L if no chars waiting, non zero otherwise
; TODO: Implement this
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
