;
; Mk14 Firmware Version 1 (SCMPKB)
;
; Formatted for the SB-Assembler (https://www.sbprojects.net/sbasm/)
;
; Chris Oddy Dec 2020
;
		.CR		scmp
		.LF		SCIOS_Version_1.list		; listing file
;		.TF		SCIOS_Version_1.bin		; object file
		.TF		SCIOS_version_1.hex,INT		; Intel hex file
;
RAM		.EQ		$0F00
DISPLY	.EQ		$0D00
;
;		Segment Assignments
SA		.EQ		1
SB		.EQ		2
SC		.EQ		4
SD		.EQ		8
SE		.EQ		16
SF		.EQ		32
SG		.EQ		64
;		7-Segment Conversion
N0		.EQ		SA+SB+SC+SD+SE+SF
N1		.EQ		SB+SC
N2		.EQ		SA+SB+SD+SE+SG
N3		.EQ		SA+SB+SC+SD+SG
N4		.EQ		SB+SC+SF+SG
N5		.EQ		SA+SC+SD+SF+SG
N6		.EQ		SA+SC+SD+SE+SF+SG
N7		.EQ		SA+SB+SC
N8		.EQ		SA+SB+SC+SD+SE+SF+SG
N9		.EQ		SA+SB+SC+SF+SG
NA		.EQ		SA+SB+SC+SE+SF+SG
NB		.EQ		SC+SD+SE+SF+SG
NC		.EQ		SA+SD+SE+SF
ND		.EQ		SB+SC+SD+SE+SG
NE		.EQ		SA+SD+SE+SF+SG
NF		.EQ		SA+SE+SF+SG
DASH		.EQ		SG
KE		.EQ		NE
KR		.EQ		SE+SG
KO		.EQ		SC+SD+SE+SG
;
NEXT		.EQ		17			; Flag for now data
;
;		'Hardware for Keyboard'
;
;	FUNCTION	DATA	KYBFUNCTION
;	0			080			0
;	1			081			1
;	2			082			2
;	3			083			3
;	4			084			4
;	5			085			5
;	6			086			6
;	7			087			7
;	8			040			8
;	9			041			9
;	A			010			+
;	B			011			-
;	C			012			MUL
;	D			013			DIV
;	E			016			SQUARE
;	F			017			SQRT
;	GO			022			%
;	MEM			023			=
;	ABORT		024			CE/C
;	TERM		027
;
;	RAM Pointers used by KITBUG, P3 is saved elsewhere
P1H		.EQ		$FFF9
P1L		.EQ		$FFFA
P2H		.EQ		$FFFB
P2L		.EQ		$FFFC
A		.EQ		$FFFD
EE		.EQ		$FFFE		('E' is a reserved word)
S		.EQ		$FFFF
;		Commands
;		ABORT	This aborts the present operation displays-
;		MEM		Allows users to read/modify, address is entered until TERM then data is entered.
;				To write data in memory TERM is pushed.  Data is read to check if it got written in RAM.
;		GO		Address is entered until TERM.  The registers are loaded from RAM and program is
;				transferred using XPPC P3.  To get back do a XPPC P3.
;
		.OR		$0000
;
;		'Initialise'
		NOP
INIT:	JMP	START
					; Debug Exit
					; Restore Environment
GOOUT:	LD	ADH(2)	; get GO address
		XPAH	3
		LD	ADL(2)
		XPAL	3
		LD	@-1(3)	; fix GO address
		LD	EE		; restore registers
		XAE
		LD	P1L
		XPAL	1
		LD	P1H
		XPAH	1
		LD	P2L
		XPAL	2
		LD	P2H
		XPAH	2
		LD	S
		CAS
		LD	A
		XPPC	3		; to get back
					; Entry Point for Debug
START:	ST	A		; save status
		LDE
		ST	EE
		CSA
		ST	S
		XPAH	1
		ST	P1H
		XPAL	1
		ST	P1L
		LDI	/RAM		; set P2 to point to RAM
		XPAH	2
		ST	P2H
		LDI	#RAM
		XPAL	2
		ST	P2L
		LD	@1(3)		; bump P3 for return
		XPAL	3		; save P3
		ST	ADL(2)
		XPAH	3
		ST	ADH(2)
					; Abort Sequence
ABORT:	LDI	0
		ST	D3(2)
		ST	D4(2)
		NOP			; mod to prevent blanking of 9th digit (was ST D9(2))
		NOP
		LDI	DASH		; set segments to _
		ST	DL(2)
		ST	DH(2)
		ST	ADLL(2)
		ST	ADLH(2)
		ST	ADHL(2)
		ST	ADHH(2)
WAIT:		JS	3,KYBD	; display and read keyboard
		JMP	WCK		; command return
		JMP	ABORT		; return for number
WCK:		XRI	$07		; check if MEM
		JZ	MEM
		XRI	01		; check if GO
		JNZ	ABORT
					; 'Go To'
;
					; Go was Pushed
					; Go to user program
GO:		LDI	-1		; set first flag
		ST	DDTA(2)
		LDI	DASH		; set data to dash
		ST	DL(2)
		ST	DH(2)
GOL:		LDI	DISPA-1	; fix address segment
		XPAL	3
		XPPC	3		; do display and keyboard
		JMP	GOCK		; command return
		LDI	ADR-1		; set address
		XPAL	3
		XPPC	3
		JMP	GOL		; not done
GOCK:		XRI	03		; check for TERM
		JZ	GOOUT		; error if not TERM
					; Incorrect Sequence
					; Display Error Wait for New Input
ERROR:	LDI	KE		; fill with error
		ST	ADHH(2)
		LDI	KR
		ST	ADHL(2)
		ST	ADLH(2)
		ST	D4(2)
		LDI	KO
		ST	ADLL(2)
		LDI	0
		ST	D3(2)
		ST	DH(2)
		ST	DL(2)
		JMP	WAIT
					; 'Memory Transactions'
DTACK:	LD	NEXT2(2)	; check if data field
		JNZ	DATA		; address done
MEMDN:	LD	ADH(2)	; put word in memory
		XPAH	1
		LD	ADL(2)
		XPAL	1
		LD	WORD(2)
		ST	(1)
		JMP	MEM
MEMCK:	XRI	$06		; check for GO
		JZ	ERROR		; cannot GO now
		XRI	$05		; check for TERM
		JZ	DTACK		; check if done
		ILD	ADL(2)	; update address low
		JNZ	MEM		; check if update high
		ILD	ADH(2)
					; MEM Key Pushed
MEM:		LDI	-1		; set first flag
		ST	NEXT(2)	; set flag for address now
		ST	DDTA(2)
MEML:		LD	ADH(2)
		XPAH	1		; set P1 for MEM address
		LD	ADL(2)
		XPAL	1
		LD	(1)
		ST	WORD(2)	; save MEM data
		LDI	DISPD-1	; fix data segment
		XPAL	3
		XPPC	3		; go to DISPD set segment for data
		JMP	MEMCK		; command return
		LDI	ADR-1		; make address
		XPAL	3
		XPPC	3
		JMP	MEML		; get next character
DATA:		LDI	-1		; set first flag
		ST	DDTA(2)
		LD	ADH(2)	; set P1 to memory address
		XPAH	1		; 275
		LD	ADL(2)
		XPAL	1
		LD	(1)		; read data word
		ST	WORD(2)	; save for display
DATAL:	LDI	DISPD-1	; fix data segment
		XPAL	3
		XPPC	3		; fix data segment GO to DISPD
		JMP	MEMCK		; character return
		LDI	4		; set counter for number of shifts
		ST	CNT(2)
		ILD	DDTA(2)	; check if first
		JNZ	DNFST
		LDI	0		; zero word if first
		ST	WORD(2)
		ST	NEXT(2)	; set flag for address done
DNFST:	CCL
		LD	WORD(2)	; shift left
		ADD	WORD(2)
		ST	WORD(2)
		DLD	CNT(2)	; check for 4 shifts
		JNZ	DNFST
		LD	WORD(2)	; add new data
		ORE
		ST	WORD(2)	; add new data
		JMP	DATAL
					; 'Hexnumber to Segment Table'
					; 'Hex Number to Seven Segment Table'
CROM:		.DB	N0
		.DB	N1
		.DB	N2
		.DB	N3
		.DB	N4
		.DB	N5
		.DB	N6
		.DB	N7
		.DB	N8
		.DB	N9
		.DB	NA
		.DB	NB
		.DB	NC
		.DB	ND
		.DB	NE
		.DB	NF
					; 'Make 4 Digit Address'
					; Shift address left one digit then add new low hex digit in E register P2 points to RAM
ADR:		LDI	4		; set number of shifts]
		ST	CNT(2)
		ILD	DDTA(2)	; check if first
		JNZ	NOTFST	; jump if number
		LDI	0		; zero address
		ST	ADH(2)
		ST	ADL(2)
NOTFST:	CCL			; clear link
		LD	ADL(2)	; shift address left 4 times
		ADD	ADL(2)
		ST	ADL(2)	; save it
		LD	ADH(2)	; now shift high
		ADD	ADH(2)
		ST	ADH(2)
		DLD	CNT(2)	; check if shifted 4 times
		JNZ	NOTFST	; jmp if not done
		LD	ADL(2)	; now add new number
		ORE
		ST	ADL(2)	; number is now updated
		XPPC	3
					; 'Data to Segments'
					; Convert Hex Data to Segments, P2 points to RAM, drops through to hex address conversion
DISPD:	LDI	/CROM		; set address of table
		XPAH	1
		LDI	#CROM
		XPAL	1
		LD	WORD(2)	; get memory word
		ANI	$0F
		XAE
		LD	-128(1)	; get segment display
		ST	DL(2)		; save at data low
		LD	WORD(2)	; fix high
		SR			; shift high to low
		SR
		SR
		SR
		XAE
		LD	-128(1)	; get segments
		ST	DH(2)		; save in data HL
					; Address to Segments
					; Convert hex address to segments, P2 points to RAM, drops through to keyboard and display
DISPA:	SCL
		LDI	/CROM		; set address of table
		XPAH	1
		LDI	#CROM
		XPAL	1
LOOPD:	LD	ADL(2)	; get address
		ANI	$0F
		XAE
		LD	-128(1)	; get segments
		ST	ADLL(2)	; save segment of ADR LL
		LD	ADL(2)
		SR			; shift high digit to low
		SR
		SR
		SR
		XAE
		LD	-128(1)	; get segments
		ST	ADLH(2)
		CSA			; check if done
		ANI	$080
		JZ	DONE
		CCL			; clear flag
		LDI	0
		ST	D4(2)		; zero digit 4
		LD	@2(2)		; fix P2 for next loop
		JMP	LOOPD
DONE:		LD	@-2(2)	; fix P2
					; 'Display and Keyboard Input'
					; CALL XPPC 3
					; JMP Command in a GO=6, MEM=7, TERM=3
					; in E GO=22, MEM=23, TERM=27, number return hex number in E register
					; ABORT key goes to ABORT
					; All registers are used
					; P2 must point to RAM, address must be xxx0
					; To re-execute routine do XPPC P3
KYBD:		LDI	0		; zero character
		ST	CHAR(2)
		LDI	/DISPLY	; set display address
		XPAH	1
OFF:		LDI	-1		; set row/digit address
		ST	ROW(2)	; save row counter
		LDI	10		; set row count
		ST	CNT(2)
		LDI	0
		ST	PUSHED(2)	; zero keyboard input
		XPAL	1		; set display address low
LOOP:		ILD	ROW(2)	; update row address
		XAE
		LD	-128(2)	; get segment
		ST	-128(1)	; send it
		DLY	0		; delay for diaplay
		LD	-128(1)	; get keyboard input
		XRI	$FF		; check if pushed
		JNZ	KEY		; jump if pushed
BACK:		DLD	CNT(2)	; check if done
		JNZ	LOOP		; no if jump
		LD	PUSHED(2)	; check if key
		JZ	CKMORE
		LD	CHAR(2)	; was there a character
		JNZ	OFF		; yes - wait for release
		LD	PUSHED(2)	; no set character
		ST	CHAR(2)
		JMP	OFF
CKMORE:	LD	CHAR(2)	; check if there was a character
		JZ	OFF		; no keep looking
					; Command Key Processing
COMAND:	XAE			; save character
		LDE			; get character
		ANI	$020		; check for command
		JNZ	CMND		; jump if command
		LDI	$080		; find number
		ANE
		JNZ	LT7		; 0 to 7
		LDI	$040
		ANE
		JNZ	N89		; 8 or 9
		LDI	$0F
		ANE
		ADI	$7		; make offset to table
		XAE			; put offset away
		LD	-128(0)	; get number
KEYRTN:	XAE			; save in E
		LD	@2(3)		; fix return
		XPPC	3		; return
		JMP	KYBD		; allows XPPC P3 to return
		.DB	$0A,$0B,$0C,$0D,$0,$0,$0E,$0F
LT7:		XRE			; keep low digit
		JMP	KEYRTN
N89:		XRE			; get low
		ADI	$08		; make digit 8 or 9
		JMP	KEYRTN
CMND:		XRE
		XRI	$04		; check if abort
		JZ	ABRT		; abort
		XPPC	3		; in E 23=MEM,  22=GO, 27=TERM
					; in A 7=MEM, 6=GO, 3=TERM
		JMP	KYBD		; allows just a XPPC P3 to return
KEY:		ORE			; make character
		ST	PUSHED(2)	; save character
		JMP	BACK
ABRT:		LDI	\ABORT
		XPAH	3
		LDI	#ABORT-1
		XPAL	3
		XPPC	3		; go to abort
;		'RAM	$E0FF'
DL		.EQ	0		; Segment for digit 1
DH		.EQ	1		; Segment for digit 2
D3		.EQ	2		; Segment for digit 3
D4		.EQ	3		; Segment for digit 4
ADLL		.EQ	4		; Segment for digit 5
ADLH		.EQ	5		; Segment for digit 6
ADHL		.EQ	6		; Segment for digit 7
ADHH		.EQ	7		; Segment for digit 8
D9		.EQ	8		; Segment for digit 9
CNT		.EQ	9		; Counter
PUSHED	.EQ	10		; Key Pushed
CHAR		.EQ	11		; Char Read
ADL		.EQ	12		; Memory Address Low
WORD		.EQ	13		; Memory Word
ADH		.EQ	14		; Memory Hi
DDTA		.EQ	15		; First Flag
ROW		.EQ	16		; Row Counter
NEXT2 	.EQ	17		; Flag for now data
		.END
