;
;==================================================================================================
; DSKY V1 ICM7218 (DISPLAY AND KEYBOARD) ROUTINES 
;==================================================================================================
;
; THE ICM MAY COINCIDE ON THE SAME PPI BUS AS A PPISD.  IT MAY NOT
; SHARE A PPI BUS WITH A PPIDE.  SEE PPI_BUS.TXT FOR MORE INFORMATION.
;
; LED SEGMENTS (BIT VALUES)
;
;	+--40--+
;	02    20
;	+--04--+
;	08    10
;	+--01--+  80
;
; ICM SCAN CODES ARE ONE BYTE: CCRRRRRR
; BITS 7-6 IDENTFY THE COLUMN OF THE KEY PRESSED
; BITS 5-0 ARE A BITMAP, WITH A BIT ON TO INDICATE ROW OF KEY PRESSED
;
;      ____PC0________PC1________PC2________PC3____
; PB5 |	 $20 [D]    $60 [E]    $A0 [F]	  $E0 [BO]
; PB4 |	 $10 [A]    $50 [B]    $90 [C]	  $D0 [GO]
; PB3 |	 $08 [7]    $48 [8]    $88 [9]	  $C8 [EX]
; PB2 |	 $04 [4]    $44 [5]    $84 [6]	  $C4 [DE]
; PB1 |	 $02 [1]    $42 [2]    $82 [3]	  $C2 [EN]
; PB0 |	 $01 [FW]   $41 [0]    $81 [BK]	  $C1 [CL]
;
;
ICM_PPIA	.EQU 	ICMPPIBASE + 0	; PORT A
ICM_PPIB	.EQU 	ICMPPIBASE + 1	; PORT B
ICM_PPIC	.EQU 	ICMPPIBASE + 2	; PORT C
ICM_PPIX	.EQU 	ICMPPIBASE + 3	; PPI CONTROL PORT
;
ICM_ROWS	.EQU	1		; DISPLAY ROWS	
ICM_COLS	.EQU	8		; DISPLAY COLUMNS
;
	DEVECHO	"ICM: IO="
	DEVECHO	ICMPPIBASE
	DEVECHO	", SIZE="
	DEVECHO	ICM_COLS
	DEVECHO	"X"
	DEVECHO	ICM_ROWS
	DEVECHO	"\n"
;
;__ICM_INIT________________________________________________________________________________________
;
;  CONFIGURE PARALLEL PORT AND CLEAR KEYPAD BUFFER
;__________________________________________________________________________________________________
;
ICM_PREINIT:
;
	; RESET PRESENCE FLAG
	XOR	A			; ASSUME NOT PRESENT
	LD	(ICM_PRESENT),A		; SAVE IT
;
	OR	$FF			; SIGNAL TO WAIT FOR KEY RELEASE
	LD	(ICM_KEYBUF),A		; SET IT
;
	; PPI PORT B IS NORMALLY SET TO INPUT, BUT HERE WE
	; TEMPORARILY SET IT TO OUTPUT.  WHILE IN OUTPUT MODE, WE
	; WRITE A VALUE OF $FF WHICH WILL BE PERSISTED BY THE PPI
	; CHIP BUS HOLD CIRCUIT IF THERE IS NO ICM PRESENT.  SO,
	; WE CAN SUBSEQUENTLY TEST FOR PPIB=$FF TO SEE IF THERE IS
	; NO ICM AND PREVENT PROBLEMS WITH PHANTOM ICM KEY PRESSES.
	; IF A ICM IS PRESENT, IT WILL SIMPLY OVERPOWER THE PPI
	; BUS HOLD CIRCUIT.
	LD	A,$80			; PA OUT, PB OUT, PC OUT
	OUT	(ICM_PPIX),A
	LD	A,$FF			; SET PPIB=$FF, BUS HOLD
	OUT	(ICM_PPIB),A
;
	LD	A,$82			; PA OUT, PB IN, PC OUT
	OUT 	(ICM_PPIX),A
;
	;IN	A,(ICM_PPIB)		; *DEBUG*
	;CALL	PRTHEXBYTE		; *DEBUG*
;
	IN	A,(ICM_PPIB)		; READ PPIB
	XOR	$FF			; INVERT RESULT
;
	CALL	ICM_RESET
;
	RET	Z			; BAIL OUT NOW IF NOT PRESENT
;
	; RECORD HARDWARE PRESENT
	LD	A,$FF
	LD	(ICM_PRESENT),A
;
	; REGISTER DRIVER WITH HBIOS
	LD	BC,ICM_DISPATCH
	CALL	DSKY_SETDISP
;
	CALL	ICM_SHOWVER
;
	RET
;
ICM_SHOWVER:
	LD	HL,ICM_VERSTR
	LD	DE,ICM_BUF
	LD	BC,ICM_COLS
	LDIR
	LD	HL,ICM_BUF + 5
	LD	A,RMJ
	LD	A,(ICM_SEGMAP + RMJ)
	OR	$80
	LD	(HL),A
	INC	HL
	LD	A,(ICM_SEGMAP + RMN)
	OR	$80
	LD	(HL),A
	INC	HL
	LD	A,(ICM_SEGMAP + RUP)
	LD	(HL),A
;
	; DISPLAY VERSION ON ICM
	LD	C,0
	LD	B,8
	LD	HL,ICM_BUF
	CALL	ICM_PUTBUF
	RET
;
ICM_INIT:
	CALL	NEWLINE			; FORMATTING
	PRTS("ICM:$")			; FORMATTING
;
	PRTS(" IO=0x$")			; FORMATTING
	LD	A,ICMPPIBASE		; GET BASE PORT
	CALL	PRTHEXBYTE		; PRINT BASE PORT
;
	LD	A,(ICM_PRESENT)		; PRESENT?
	OR	A			; SET FLAGS
	JR	NZ,ICM_INIT1		; YES, CONTINUE
	PRTS(" NOT PRESENT$")		; NOT PRESENT
	RET
;
ICM_INIT1:
	CALL	PC_SPACE
	LD	A,ICM_COLS
	CALL	PRTDEC8
	LD	A,'X'
	CALL	COUT
	LD	A,ICM_ROWS
	CALL	PRTDEC8
	RET
;
; ICM DEVICE FUNCTION DISPATCH ENTRY
;   A: RESULT (OUT), 0=OK, Z=OK, NZ=ERR
;   B: FUNCTION (IN)
;
ICM_DISPATCH:
	LD	A,B			; GET REQUESTED FUNCTION
	AND	$0F			; ISOLATE SUB-FUNCTION
	JP	Z,ICM_RESET		; RESET DSKY HARDWARE
	DEC	A	
	JP	Z,ICM_STAT		; GET KEYPAD STATUS
	DEC	A	
	JP	Z,ICM_GETKEY		; READ A KEY FROM THE KEYPAD
	DEC	A	
	JP	Z,ICM_SHOWHEX		; DISPLAY A 32-BIT BINARY VALUE IN HEX
	DEC	A	
	JP	Z,ICM_SHOWSEG		; DISPLAY SEGMENTS
	DEC	A	
	JP	Z,ICM_KEYLEDS		; SET KEYPAD LEDS
	DEC	A	
	JP	Z,ICM_STATLED		; SET STATUS LED
	DEC	A	
	JP	Z,ICM_BEEP		; BEEP DSKY SPEAKER
	DEC	A
	JP	Z,ICM_DEVICE		; DEVICE INFO
	DEC	A
	JP	Z,ICM_MESSAGE		; HANDLE MESSAGE
	DEC	A
	JP	Z,ICM_EVENT		; HANDLE EVENT
	SYSCHKERR(ERR_NOFUNC)
	RET
;
;
;
ICM_RESET:
	PUSH	AF
	LD	A,$70			; PPISD AND 7218 INACTIVE
	OUT	(ICM_PPIC),A
	POP	AF
	RET
;
; CHECK FOR KEY PRESS, SAVE RAW VALUE, RETURN STATUS
;
ICM_STAT:
	LD	A,(ICM_KEYBUF)		; GET CURRENT BUF VAL
	CP	$FF			; $FF MEANS WE ARE WAITING FOR PREV KEY TO BE RELEASED
	JR	Z,ICM_STAT1		; CHECK FOR PREV KEY RELEASE
	OR	A			; DO WE HAVE A SCAN CODE BUFFERED ALREADY?
	RET	NZ			; IF SO, WE ARE DONE
	JR	ICM_STAT2		; OTHERWISE, DO KEY CHECK
	
ICM_STAT1:
	; WAITING FOR PREVIOUS KEY RELEASE
	CALL	ICM_KEY			; SCAN
	JR	Z,ICM_STAT2		; IF ZERO, PREV KEY RELEASED, CONTINUE
	XOR	A			; SIGNAL NO KEY PRESSED
	RET				; AND DONE

ICM_STAT2:
	CALL	ICM_KEY			; SCAN
	LD	(ICM_KEYBUF),A		; SAVE RESULT
	RET				; RETURN WITH ZF SET APPROPRIATELY
;
; WAIT FOR A ICM KEYPRESS AND RETURN
;
ICM_GETKEY:
	CALL	ICM_STAT		; CHECK STATUS
	JR	Z,ICM_GETKEY		; LOOP IF NOTHING READY
	LD	A,(ICM_KEYBUF)
	LD	B,24			; SIZE OF DECODE TABLE
	LD	C,0			; INDEX
	LD	HL,ICM_KEYMAP		; POINT TO BEGINNING OF TABLE
ICM_GETKEY1:
	CP	(HL)			; MATCH?
	JR	Z,ICM_GETKEY2		; FOUND, DONE
	INC	HL
	INC	C			; BUMP INDEX
	DJNZ	ICM_GETKEY1		; LOOP UNTIL EOT
ICM_GETKEY1A:
	LD	A,$FF			; NOT FOUND ERR, RETURN $FF
	RET
ICM_GETKEY2:
	LD	A,$FF			; SET KEY BUF TO $FF
	LD	(ICM_KEYBUF),A		; DO IT
	; RETURN THE INDEX POSITION WHERE THE SCAN CODE WAS FOUND
	LD	E,C			; RETURN INDEX VALUE
	XOR	A			; SIGNAL SUCCESS
	RET
;
; DISPLAY HEX VALUE FROM DE:HL
;
ICM_SHOWHEX:
	LD	BC,ICM_TMP32		; POINT TO HEX BUFFER
	CALL	ST32			; STORE 32-BIT BINARY THERE
	LD	HL,ICM_TMP32		; FROM: BINARY VALUE (HL)
	LD	DE,ICM_BUF		; TO: SEGMENT BUFFER (DE)
	CALL	ICM_BIN2SEG		; CONVERT
	LD	HL,ICM_BUF		; POINT TO SEGMENT BUFFER
	CALL	ICM_PUTBUF		; DO IT
	XOR	A			; SUCCESS
	RET				; AND RETURN
;
; ICM SHOW BUFFER
;   HL: ADDRESS OF BUFFER
;
ICM_SHOWSEG:
	; CONVERT FROM DBG ALPHABET TO SEG CODES
	LD	B,ICM_COLS		; DO FOR ALL CHARS
	LD	DE,ICM_BUF		; DESTINATION BUFFER
ICM_SHOWSEG1:
	LD	A,(HL)			; GET SOURCE VALUE
	INC	HL			; BUMP FOR NEXT TIME
	PUSH	AF			; SAVE IT
	AND	$80			; ISOLATE HI BIT (DP)
	LD	C,A			; SAVE IN C
	POP	AF			; RECOVER ORIGINAL
	AND	$7F			; REMOVE HI BIT (DP)
	PUSH	HL			; SAVE IT
	LD	HL,ICM_SEGMAP		; POINT TO XLAT MAP
	CALL	ADDHLA			; OFFSET BY VALUE
	LD	A,(HL)			; GET NEW VALUE
	OR	C			; RECOMBINE WITH DP BIT
	LD	(DE),A			; SAVE IT
	INC	DE			; BUMP PTR
	POP	HL			; RESTORE SOURCE PTR
	DJNZ	ICM_SHOWSEG1		; LOOP TILL DONE
;	
	; DISPLAY CONVERTED BUFFER
	LD	HL,ICM_BUF		; BUFFER
	CALL	ICM_PUTBUF		; DO IT
	XOR	A			; SIGNAL SUCCESS
	RET
;
;
;
ICM_KEYLEDS:
ICM_STATLED:
ICM_BEEP:
	XOR	A			; PRETEND SUCCESS
	RET
;
; DEVICE INFORMATION
;
ICM_DEVICE:
	LD	D,DSKYDEV_ICM		; D := DEVICE TYPE
	LD	E,0			; E := PHYSICAL DEVICE NUMBER
	LD	H,0			; H := MODE
	LD	L,ICMPPIBASE		; L := BASE I/O ADDRESS
	XOR	A			; SIGNAL SUCCESS
	RET
;
; MESSAGE HANDLER
;
ICM_MESSAGE:
	LD	A,C			; GET MESSAGE ID
	ADD	A,A			; WORD OFFSET
	LD	HL,ICM_MSGTBL		; START OF MESSAGE TABLE
	CALL	ADDHLA			; ADD OFFSET
	LD	A,(HL)			; SAVE LSB
	INC	HL			; BUMP TO MSB
	LD	H,(HL)			; GET MSB
	LD	L,A			; GET LSB
;
	; HL HAS POINTER TO MESSAGE
	CALL	ICM_PUTBUF		; DISPLAY IT
	XOR	A			; SIGNAL SUCCESS
	RET
;
; EVENT HANDLER
;
ICM_EVENT:
	LD	A,C			; EVENT ID
	OR	A			; 0=CPUSPD
	JR	Z,ICM_EVT_CPUSPD	; HANDLE CPU SPD CHANGE
	DEC	A			; 1=DSKACT
	JR	Z,ICM_EVT_DSKACT	; HANDLE DISK ACTIVITY
	XOR	A
	RET
;
; CPU SPEED CHANGE
;
ICM_EVT_CPUSPD:
	XOR	A
	RET
;
; DISK ACTIVITY
;
ICM_EVT_DSKACT:
;
	; USE DISK UNIT NUMBER FOR MSB
	LD	A,(HB_DSKUNIT)		; GET DISK UNIT NUM
	LD	(HB_DSKADR+3),A		; REPLACE HIGH BYTE W/ DISK #
;
	; CONVERT TO SEGMENT DISPLAY
	LD	HL,HB_DSKADR		; INPUT POINTER
	LD	DE,ICM_BUF		; BUF FOR OUTPUT
	CALL	ICM_BIN2SEG		; CONVERT TO SEG DISPLAY
;
	; DECIMAL POINT FOR DISK UNIT SEPARATION
	LD	HL,ICM_BUF+1		; SECOND CHAR OF DISP
	SET	7,(HL)			; TURN ON DECIMAL PT
;
	; DECIMAL POINT TO INDICATE WRITE ACTION
	LD	A,(HB_DSKFUNC)		; GET CURRENT I/O FUNCTION
	CP	BF_DIOWRITE		; IS IT A WRITE?
	JR	NZ,ICM_EVT_DSKACT2	; IF NOT, SKIP
	LD	HL,ICM_BUF+7		; POINT TO CHAR 7
	SET	7,(HL)			; SET WRITE DOT
;
ICM_EVT_DSKACT2:
	; UPDATE DISPLAY
	LD	HL,ICM_BUF		; SEG DISPLAY BUF TO HL
	CALL	ICM_PUTBUF
;
	XOR	A
	RET
;
;__ICM_KEY_________________________________________________________________________________________
;
;  CHECK FOR KEY PRESS W/ DEBOUNCE
;__________________________________________________________________________________________________
;
ICM_KEY:
	CALL	ICM_SCAN		; INITIAL KEY PRESS SCAN
	LD	E,A			; SAVE INITIAL SCAN VALUE
ICM_KEY1:
	; MAX BOUNCE TIME FOR OMRON B3F IS 3MS
	PUSH	DE			; SAVE DE
	LD	DE,300			; ~3MS DELAY
	CALL	VDELAY			; DO IT
	CALL	ICM_SCAN		; REPEAT SCAN
	POP	DE			; RESTORE DE
	RET	Z			; IF NOTHING PRESSED, DONE
	CP	E			; SAME?
	JR	ICM_KEY2		; YES, READY TO RETURN
	LD	E,A			; OTHERWISE, SAVE NEW SCAN VAL
	JR	ICM_KEY1		; AND LOOP UNTIL STABLE VALUE
ICM_KEY2:
	OR	A			; SET FLAGS BASED ON VALUE
	RET				; AND DONE
;
;__ICM_SCAN________________________________________________________________________________________
;
;  SCAN KEYPAD AND RETURN RAW SCAN CODE (RETURNS ZERO IF NO KEY PRESSED)
;__________________________________________________________________________________________________
;
ICM_SCAN:
	LD	B,4			; 4 COLUMNS
	LD	C,$01			; FIRST COLUMN
	LD	E,0			; INITIAL COL ID
ICM_SCAN1:
	LD	A,C			; COL TO A
	OR	$70			; KEEP PPISD AND 7218 INACTIVE
	OUT	(ICM_PPIC),A		; ACTIVATE COL
	IN	A,(ICM_PPIB)		; READ ROW BITS
	AND	$3F			; MASK, WE ONLY HAVE 6 ROWS, OTHERS UNDEFINED
	JR	NZ,ICM_SCAN2		; IF NOT ZERO, GOT SOMETHING
	RLC	C			; NEXT COL
	INC	E			; BUMP COL ID
	DJNZ	ICM_SCAN1		; LOOP THROUGH ALL COLS
	XOR	A			; NOTHING FOUND, RETURN ZERO
	JP	ICM_RESET		; RETURN VIA RESET
ICM_SCAN2:
	RRC	E			; MOVE COL ID
	RRC	E			; ... TO HIGH BITS 6 & 7
	OR	E			; COMBINE WITH ROW
	JP	ICM_RESET		; RETURN VIA RESET
;
;__ICM_PUTBUF______________________________________________________________________________________
;
;  WRITE BUFFER AT HL TO SEGMENT DISPLAY
;__________________________________________________________________________________________________
;
ICM_PUTBUF:
	LD	A,82H			; SETUP PPI
	OUT	(ICM_PPIX),A
	CALL	ICM_COFF
	LD	A,$F0			; 7218 -> (DATA COMING, NO DECODE)
	OUT	(ICM_PPIA),A
	CALL	ICM_STROBEC		; STROBE COMMAND
	LD	B,ICM_COLS		; NUMBER OF DIGITS
	LD	C,ICM_PPIA
ICM_HEXOUT2:
	LD	A,(HL)
	XOR	$80			; FIX DOT POLARITY
	OUT	(C),A
	INC	HL
	DEC	B
	JP	Z,ICM_STROBE		; DO FINAL STROBE AND RETURN
	CALL	ICM_STROBE		; STROBE BYTE VALUE
	JR	ICM_HEXOUT2
ICM_STROBEC:	; COMMAND STROBE
	LD	A,80H | 30H
	JP	ICM_STROBE0
ICM_STROBE:	; DATA STROBE
	LD	A,00H | 30H		; SET WRITE STROBE
ICM_STROBE0:
	OUT	(ICM_PPIC),A		; OUT TO PORTC
	CALL	DLY2			; DELAY
ICM_COFF:
	LD	A,40H | 30H		; QUIESCE
	OUT	(ICM_PPIC),A		; OUT TO PORTC
	XOR	A			; SIGNAL SUCCESS
	RET
;
;_KEYMAP_TABLE_____________________________________________________________________________________
;
ICM_KEYMAP:
	; POS	$00  $01  $02  $03  $04  $05  $06  $07
	; KEY   [0]  [1]  [2]  [3]  [4]  [5]  [6]  [7]
	.DB	$41, $02, $42, $82, $04, $44, $84, $08
;                                                  
	; POS	$08  $09  $0A  $0B  $0C  $0D  $0E  $0F
	; KEY   [8]  [9]  [A]  [B]  [C]  [D]  [E]  [F]
	.DB	$48, $88, $10, $50, $90, $20, $60, $A0
;                                                  
	; POS	$10  $11  $12  $13  $14  $15  $16  $17
	; KEY   [FW] [BK] [CL] [EN] [DE] [EX] [GO] [BO]
	.DB	$01, $81, $C1, $C2, $C4, $C8, $D0, $E0
;
; KBD WORKING STORAGE
;
ICM_KEYBUF	.DB	0
;
;==================================================================================================
; UTILTITY FUNCTIONS
;==================================================================================================
;
; CONVERT 32 BIT BINARY TO 8 BYTE HEX SEGMENT DISPLAY
;
;	HL: ADR OF 32 BIT BINARY
;	DE: ADR OF DEST LED SEGMENT DISPLAY BUFFER (8 BYTES)
;
ICM_BIN2SEG:
	LD	B,4			; 4 BYTES OF INPUT
	LD	A,B			; PUT IN ACCUM
	CALL	ADDHLA			; PROCESS FROM END (LITTLE ENDIAN)
ICM_BIN2SEG1:
	DEC	HL			; DEC PTR (LITTLE ENDIAN)
	LD	A,(HL)			; HIGH NIBBLE
	RRCA \ RRCA \ RRCA \ RRCA	; ROTATE BITS
	CALL	ICM_BIN2SEG_NIB	; CONVERT NIBBLE INTO OUTPUT BUF
	LD	A,(HL)			; LOW NIBBLE
	CALL	ICM_BIN2SEG_NIB	; CONVERT NIBBLE INTO OUTPUT BUF
	DJNZ	ICM_BIN2SEG1		; LOOP FOR ALL INPUT BYTES
	RET				; DONE
;
ICM_BIN2SEG_NIB:
	PUSH	HL			; SAVE HL
	LD	HL,ICM_SEGMAP		; POINT TO SEG MAP TABLE
	AND	$0F			; ISOLATE LOW NIBBLE
	CALL	ADDHLA			; OFFSET INTO TABLE
	LD	A,(HL)			; LOAD VALUE FROM TABLE
	POP	HL			; RESTORE HL
	LD	(DE),A			; SAVE VALUE TO OUTPUT BUF
	INC	DE			; BUMP TO NEXT OUTPUT BYTE
	RET				; DONE
;
;==================================================================================================
; STORAGE
;==================================================================================================
;
; SEG DISPLAY WORKING STORAGE
;
ICM_PRESENT	.DB	0
ICM_BUF		.FILL	ICM_COLS
ICM_TMP32	.FILL	4,0		; TEMP DWORD
;
ICM_VERSTR	.DB	$3E,$7F,$0A,$7B,$57,$00,$00,$00	; "HBIOS   "
;
ICM_SEGMAP:
;
	; POS	$00  $01  $02  $03  $04  $05  $06  $07
	; GLYPH '0'  '1'  '2'  '3'  '4'  '5'  '6'  '7'

	.DB	$7B, $30, $6D, $75, $36, $57, $5F, $70
;
	; POS	$08  $09  $0A  $0B  $0C  $0D  $0E  $0F
	; GLYPH	'8'  '9'  'A'  'B'  'C'  'D'  'E'  'F'
	.DB	$7F, $76, $7E, $1F, $4B, $3D, $4F, $4E
;
	; POS	$10  $11  $12  $13  $14  $15  $16  $17  $18  $19  $1A
	; GLYPH	' '  '-'  '.'  'P'  'o'  'r'  't'  'A'  'd'  'r'  'G'
	.DB	$00, $04, $00, $6E, $1D, $0C, $0F, $7E, $3D, $0C, $5F
;
ICM_MSGTBL:
	.DW	ICM_MSG_LDR_SEL
	.DW	ICM_MSG_LDR_BOOT
	.DW	ICM_MSG_LDR_LOAD
	.DW	ICM_MSG_LDR_GO
	.DW	ICM_MSG_MON_RDY
	.DW	ICM_MSG_MON_BOOT
;
ICM_MSG_LDR_SEL		.DB	$7F,$1D,$1D,$0F,$6C,$00,$00,$00	; "Boot?   "
ICM_MSG_LDR_BOOT	.DB	$7F,$1D,$1D,$0F,$80,$80,$80,$00	; "Boot... "
ICM_MSG_LDR_LOAD	.DB	$0B,$1D,$7E,$3D,$80,$80,$80,$00	; "Load... "
ICM_MSG_LDR_GO		.DB	$5F,$1D,$80,$80,$80,$00,$00,$00	; "Go...   "
ICM_MSG_MON_RDY		.DB	$04,$4B,$6E,$3B,$00,$3B,$6E,$04	; "-CPU UP-"
ICM_MSG_MON_BOOT	.DB	$7F,$1D,$1D,$0F,$B0,$00,$00,$00	; "Boot!   "
