;
;==================================================================================================
;   HDIAG
;==================================================================================================
;
; THIS FILE CONTAINS A FRAMEWORK FOR A DIAGNOTIC ENVIRONMENT.  IT IS ESSENTIALLY A STRIPPED DOWN
; VERSION OF HBIOS.  IT DOES NO BANK SWITCHING, BUT THE INTERRUPT MANAGEMENT FRAMEWORK IS
; RETAINED.  UPON STARTUP, IT RELOCATES TO UPPER (COMMON) RAM AS QUICKLY AS POSSIBLE.
;
; THERE IS NO HBIOS API.  MINIMAL SERIAL PORT ACCESS IS PROVIDED BY INCLUDING THE PLATFORM
; APPROPRIATE MIN_XXX.ASM SERIAL DRIVER.  SERIAL PORT SPEEDS ARE FIXED AND THERE IS NO
; FLOW CONTROL.  NONE OF THE TYPICAL DRIVER MODULES (CHARACTER, DISK, VIDEO, ETC.) ARE INCLUDED
; AND THERE IS NO DISPATCHING MECHANISM.
;
; IN ORDER TO BUILD PLATFORM APPROPRIATE VERSIONS, THE FULL HBIOS INCLUDE FILE STRUCTURE
; IS USED SO THAT ALL OF THE HBIOS CONFIG SETTINGS CAN BE USED.
;
; THIS FILE CAN BE COMPILED TO BOOT IN ONE OF 2 MODES (ROM OR APPLICATION) AS DESCRIBED
; BELOW.  WHEN COMPILED, YOU MUST DEFINE EXACTLY ONE OF THE FOLLOWING MACROS:
;
; - ROMBOOT: BOOT FROM A ROM BANK
;
;   WHEN ROMBOOT IS DEFINED, THE FILE IS ASSEMBLED TO BE IMBEDDED AT THE START OF A ROM
;   ASSUMING THAT THE CPU WILL START EXECUTION AT ADDRESS 0.
;
; - APPBOOT: BOOT FROM A CP/M STYLE APPLICATION FILE
;
;   WHEN APPBOOT IS DEFINED, THE FILE IS ASSEMBLED AS A CP/M APPLICATION ASSUMING
;   THAT IT WILL BE LOADED AT 100H BY THE CP/M (OR COMPATIBLE) OS.
;
; INCLUDE FILE NESTING:
;
; hdiag.asm
;  - std.asm
;    - ver.inc
;    - hbios.inc
;    - build.inc
;      - config/<plt>_<cfg>.asm
;        - cfg_<plt>.asm
;  - util.asm
;  - min_[uart|asci|sio|acia].asm
;  - dsky.asm ???
;  - diagnostic modules...
;
; INCLUDE GENERIC STUFF
;
#INCLUDE "std.asm"
;
#DEFINE	HDIAG
;
; MAKE SURE EXACTLY ONE OF ROMBOOT, APPBOOT IS DEFINED.
;
MODCNT	.EQU	0
#IFDEF ROMBOOT
MODCNT	.SET	MODCNT + 1
#ENDIF
#IFDEF APPBOOT
MODCNT	.SET	MODCNT + 1
#ENDIF
#IF (MODCNT != 1)
	.ECHO	"*** ERROR: PLEASE DEFINE ONE AND ONLY ONE OF ROMBOOT, APPBOOT!!!\n"
	!!!	; FORCE AN ASSEMBLY ERROR
#ENDIF
;
;
;
#IF (DIAGENABLE)
#DEFINE	DIAG(N)	PUSH AF
  #DEFCONT \	LD A,N
  #DEFCONT \	OUT (DIAGPORT),A
  #DEFCONT \	POP AF
#ELSE
#DEFINE	DIAG(N)	\;
#ENDIF
;
#IF (LEDENABLE)
  #IF (LEDMODE == LEDMODE_STD)
#DEFINE	LED(N)	PUSH	AF
  #DEFCONT \	LD	A,~N
  #DEFCONT \	OUT	(LEDPORT),A
  #DEFCONT \	POP	AF
  #ENDIF
  #IF (LEDMODE == LEDMODE_RTC)
#DEFINE LED(N)	PUSH	AF
  #DEFCONT \	LD	A,(HB_RTCVAL)
  #DEFCONT \	AND	%11111100
  #DEFCONT \	OR	(N & %00000011)
  #DEFCONT \	LD	(HB_RTCVAL),A
  #DEFCONT \	OUT	(LEDPORT),A
  #DEFCONT \	POP	AF
  #ENDIF
#ELSE
#DEFINE	LED(N)	\;
#ENDIF
;
;
;
#IF (INTMODE == 0)
; NO INTERRUPT HANDLING
#DEFINE	HB_DI		;
#DEFINE	HB_EI		;
#ELSE
  #IF (CPUFAM == CPU_Z280)
    #IF (INTMODE == 3)
; Z280 MODE 3 INTERRUPT HANDLING (INTA, C/T 0, & UART RCVR ENABLED)
#DEFINE	HB_DI		DI
#DEFINE	HB_EI		.DB	$ED,$7F,$0B
    #ELSE
; Z280 MODE 1/2 INTERRUPT HANDLING
#DEFINE	HB_DI		DI
#DEFINE	HB_EI		EI
    #ENDIF
  #ELSE
#DEFINE	HB_DI		DI
#DEFINE	HB_EI		EI
  #ENDIF
#ENDIF
;
#IF (INTMODE > 3)
	.ECHO	"*** ERROR: INVALID INTMODE SETTING!!!\n"
	!!!	; FORCE AN ASSEMBLY ERROR
#ENDIF
;
#IF (INTMODE == 3)
  #IF (CPUFAM != CPU_Z280)
	.ECHO	"*** ERROR: INTMODE 3 REQUIRES Z280 FAMILY CPU!!!\n"
	!!!	; FORCE AN ASSEMBLY ERROR
  #ENDIF
  #IF (MEMMGR != MM_Z280)
	.ECHO	"*** ERROR: INTMODE 3 REQUIRES Z280 MEMORY MANAGER!!!\n"
	!!!	; FORCE AN ASSEMBLY ERROR
  #ENDIF
#ENDIF
;
#IF (MEMMGR == MM_Z280)
  #IF (INTMODE != 3)
	.ECHO	"*** ERROR: Z280 MEMORY MANAGER REQUIRES INTMODE 3!!!\n"
	!!!	; FORCE AN ASSEMBLY ERROR
  #ENDIF
  #IF (CPUFAM != CPU_Z280)
	.ECHO	"*** ERROR: Z280 MEMORY MANAGER REQUIRES Z280 FAMILY CPU!!!\n"
	!!!	; FORCE AN ASSEMBLY ERROR
  #ENDIF
#ENDIF
;
;
;
#IF (CTCENABLE)
CTCA		.EQU	CTCBASE + 0	; CTC: CHANNEL A REGISTER ADR
CTCB		.EQU	CTCBASE + 1	; CTC: CHANNEL B REGISTER ADR
CTCC		.EQU	CTCBASE + 2	; CTC: CHANNEL C REGISTER ADR
CTCD		.EQU	CTCBASE + 3	; CTC: CHANNEL D REGISTER ADR
#ENDIF
;
; THIS EQUATE IS UPDATED BY DRIVER INCLUDES THAT SHARE THE RTC LATCH.
; AS DRIVER IS INCLUDED, IT WILL USE .SET TO SET ANY BITS THEY OWN
; AND WANT TO SET AS DEFAULT.
;
RTCDEF		.EQU	0		; ALLOWS DRIVERS TO SET BITS
;
#IF (PLATFORM == PLT_SCZ180)
RTCDEF		.SET	RTCDEF | %00000001	; SC128 I2C SCL BIT
#ENDIF
;
;
;
#IFNDEF	APPBOOT
;
	.ORG	0
;
;==================================================================================================
; NORMAL PAGE ZERO SETUP, RET/RETI/RETN AS APPROPRIATE, LEAVE INTERRUPTS DISABLED
;==================================================================================================
;
	.FILL	(000H - $),0FFH		; RST 0
	JP	HB_START
	.DW	ROM_SIG
	.FILL	(008H - $),0FFH		; RST 8
	RET
	.FILL	(010H - $),0FFH		; RST 10
	RET
	.FILL	(018H - $),0FFH		; RST 18
	RET
	.FILL	(020H - $),0FFH		; RST 20
	RET
	.FILL	(028H - $),0FFH		; RST 28
	RET
	.FILL	(030H - $),0FFH		; RST 30
	RET
	.FILL	(038H - $),0FFH		; RST 38 / IM1 INT
  #IF (INTMODE == 1)
	JP	INT_IM1			; JP TO INTERRUPT HANDLER IN HI MEM
  #ELSE
	RET				; RETURN W/ INTS DISABLED
  #ENDIF
	.FILL	(066H - $),0FFH		; NMI
	RETN
;
	.FILL	(070H - $),0FFH		; SIG STARTS AT $80
;
ROM_SIG:
	.DB	$76, $B5		; 2 SIGNATURE BYTES
	.DB	1			; STRUCTURE VERSION NUMBER
	.DB	7			; ROM SIZE (IN MULTIPLES OF 4KB, MINUS ONE)
	.DW	NAME			; POINTER TO HUMAN-READABLE ROM NAME
	.DW	AUTH			; POINTER TO AUTHOR INITIALS
	.DW	DESC			; POINTER TO LONGER DESCRIPTION OF ROM
	.DB	0, 0, 0, 0, 0, 0	; RESERVED FOR FUTURE USE; MUST BE ZERO
;
NAME	.DB	"ROMWBW v", BIOSVER, ", ", TIMESTAMP, 0
AUTH	.DB	"WBW",0
DESC	.DB	"ROMWBW v", BIOSVER, ", Copyright (C) 2020, Wayne Warthen, GNU GPL v3", 0
;
	.FILL	($100 - $),$FF		; PAD REMAINDER OF PAGE ZERO
;
#ENDIF
;
;
;
	.ORG	$100
	JP	HB_START
;
;==================================================================================================
;   HBIOS UPPER MEMORY PROXY (RELOCATED TO RUN IN TOP 2 PAGES OF CPU RAM)
;==================================================================================================
;
; THE FOLLOWING CODE IS RELOCATED TO THE TOP OF MEMORY TO HANDLE INVOCATION DISPATCHING
;
	;.FILL	(HBX_IMG - $)		; FILL TO START OF PROXY IMAGE START
HBX_IMG	.SET	$
	.ORG	HBX_LOC			; ADJUST FOR RELOCATION
;
; MEMORY LAYOUT:
;
;   HBIOS PROXY CODE		$FE00 (256 BYTES)
;   INTERRUPT VECTORS		$FF00 (32 BYTES, 16 ENTRIES)
;   INTERRUPT HANDLER STUBS	$FF20 (128 BYTES)
;   HBIOS PROXY COPY BUFFER	$FF80 (64 BYTES)
;   HBIOS PROXY MGMT BLOCK	$FFE0 (32 BYTES)
;
; DEFINITIONS
;
HBX_BUFSIZ	.EQU	$40		; INTERBANK COPY BOUNCE BUFFER SIZE
;
; HBIOS IDENTIFICATION DATA BLOCK
;
HBX_IDENT:
	.DB	'W',~'W'		; MARKER
	.DB	RMJ << 4 | RMN		; FIRST BYTE OF VERSION INFO
	.DB	RUP << 4 | RTP		; SECOND BYTE OF VERSION INFO
;;;;
;;;;==================================================================================================
;;;;   HBIOS ENTRY FOR RST 08 PROCESSING
;;;;==================================================================================================
;;;;
;;;; NOTE: THE SIZE OF HBX_TMPSTK (TYPICALLY 20 BYTES) IS INSUFFICIENT FOR
;;;; FREERTOS IF AN INTERRUPT STRIKES WHILE THE TEMPORARY STACK IS ACTIVE.
;;;; BELOW, HBX_BUF HAS BEEN USURPED TO PROVIDE A LARGER TEMP STACK TO
;;;; ACCOMMODATE FREERTOS.  HBX_BUF IS ONLY USED AS A BOUNCE BUFFER, SO IT'S
;;;; USE WILL NEVER OVERLAP WITH BELOW.
;;;;
;;;; WARNING: HBX_INVOKE IS *NOT* REENTRANT!
;;;;
;;;HBX_INVOKE:
;;;;
;;;#IF (HBIOS_MUTEX == TRUE)
;;;	PUSH	HL			; SAVE HL
;;;	LD	HL,HB_LOCK		; POINT TO LOCK
;;;	SRA	(HL)			; TEST/ACQUIRE MUTEX LOCK
;;;	JR	C,$-2			; KEEP TRYING ON FAILURE
;;;	POP	HL			; RESTORE HL
;;;#ENDIF
;;;;
;;;#IF (MEMMGR == MM_Z280)
;;;;
;;;	LD	A,(HB_CURBNK)		; GET CURRENT BANK
;;;	LD	(HB_INVBNK),A		; SAVE INVOCATION BANK
;;;;
;;;	LD	A,BID_BIOS		; HBIOS BANK
;;;	LD	(HB_CURBNK),A		; SET AS CURRENT BANK
;;;;
;;;	.DB	$ED,$71			; SC
;;;	.DW	HB_DISPATCH		; SC PARAMETER
;;;;
;;;	PUSH	AF
;;;	LD	A,(HB_INVBNK)
;;;	LD	(HB_CURBNK),A
;;;	POP	AF
;;;;
;;;#ELSE
;;;;
;;;	LD	(HBX_INVSP),SP		; SAVE ORIGINAL STACK FRAME
;;;	LD	SP,HBX_BUF_END		; BORROW HBX_BUF FOR TEMP STACK
;;;;
;;;	LD	A,(HB_CURBNK)		; GET CURRENT BANK
;;;	LD	(HB_INVBNK),A		; SAVE INVOCATION BANK
;;;;
;;;	LD	A,BID_BIOS		; HBIOS BANK
;;;	CALL	HBX_BNKSEL		; SELECT IT
;;;	LD	SP,HB_STACK		; NOW USE FULL HBIOS STACK IN HBIOS BANK
;;;;
;;;	;CALL	HB_DISPATCH		; CALL HBIOS FUNCTION DISPATCHER
;;;;
;;;	LD	SP,HBX_BUF_END		; BORROW HBX_BUF FOR TEMP STACK
;;;	PUSH	AF			; SAVE AF (FUNCTION RETURN)
;;;;
;;;	LD	A,(HB_INVBNK)		; LOAD ORIGINAL BANK
;;;	CALL	HBX_BNKSEL		; SELECT IT
;;;	POP	AF			; RESTORE AF
;;;	LD	SP,0			; RESTORE ORIGINAL STACK FRAME
;;;HBX_INVSP	.EQU	$ - 2
;;;;
;;;#ENDIF
;;;;
;;;#IF (HBIOS_MUTEX == TRUE)
;;;	PUSH	HL			; SAVE HL
;;;	LD	HL,HB_LOCK		; POINT TO LOCK
;;;	LD	(HL),$FE		; RELEASE MUTEX LOCK
;;;	POP	HL			; RESTORE HL
;;;#ENDIF
;;;;
;;;	RET				; RETURN TO CALLER
;
;;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;; BNKSEL - Switch Memory Bank to Bank in A.
;;   Preserve all Registers including Flags.
;;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
HBX_BNKSEL:
	; IF AN INTERRUPT OCCURS DURING THE BANK SWITCH CODE,
	; THE BANK WILL BE SET TO (CURBNK) AS THE INTERRUPT
	; RETURNS.  SO, IT IS IMPORTANT THAT (HB_CURBNK) BE
	; SET AS THE FIRST STEP TO AVOID ISSUES IF AN INTERRUPT
	; OCCURS DURING PROCESSING.
	LD	(HB_CURBNK),A		; RECORD NEW CURRENT BANK
;
HBX_BNKSEL_INT:
;
#IF (MEMMGR == MM_SBC)
  #IF (INTMODE == 1)
	; THIS BIT OF ABSURDITY HANDLES A RARE (BUT FATAL) SITUATION
	; WHERE AN IM1 INTERRUPT OCCURS BETWEEN SETTING THE RAM AND
	; ROM SELECTORS.  BRACKETING THE INSTRUCTIONS WITH DI/EI
	; IS CONTRAINDICATED BECAUSE THIS ROUTINE IS CALLED BY
	; OTHER ROUTINES THAT MUST CONTROL INT ENABLE AT A HIGHER
	; LEVEL.  THE FOLLOWING TECHNIQUE ENSURES THAT YOU ALWAYS
	; SWITCH DIRECTLY FROM THE PREVIOUS BANK TO THE TARGET BANK
	; WITHOUT AN "ERRANT" BANK BEING ACTIVE BETWEEN THE TWO
	; BANK SELECTION I/O INSTRUCTIONS.  THE TECHNIQUE IS ONLY
	; NEEDED WHEN USING INT MODE 1 BECAUSE THAT MODE REQUIRES
	; PAGE ONE TO HAVE A VALID INT HANDLER WHENEVER INTS ARE
	; ENABLED.
	;BIT	7,A			; [8] TEST RAM BIT
	;JR	Z,HBX_ROM		; [12/7] IF NOT SET, JUST DO ROM
	OR	A			; [4] SET FLAGS
	JP	P,HBX_ROM		; [10] BIT 7 INDICATES RAM
  #ENDIF
	OUT	(MPCL_RAM),A		; SET RAM PAGE SELECTOR
HBX_ROM:
	OUT	(MPCL_ROM),A		; SET ROM PAGE SELECTOR
	RET				; DONE
#ENDIF
;
#IF (MEMMGR == MM_Z2)
	BIT	7,A			; BIT 7 SET REQUESTS RAM PAGE
	JR	Z,HBX_ROM		; NOT SET, SELECT ROM PAGE
	RES	7,A			; RAM PAGE REQUESTED: CLEAR ROM BIT
	ADD	A,16			; ADD 16 x 32K - RAM STARTS FROM 512K
;
HBX_ROM:
	RLCA				; TIMES 2 - GET 16K PAGE INSTEAD OF 32K
	OUT	(MPGSEL_0),A		; BANK_0: 0K - 16K
	INC	A			;
	OUT	(MPGSEL_1),A		; BANK_1: 16K - 32K
  #IF (CPUFAM == CPU_Z280)
	PCACHE
  #ENDIF
	RET				; DONE
#ENDIF
;
#IF (MEMMGR == MM_N8)
	BIT	7,A			; TEST BIT 7 FOR RAM VS. ROM
	JR	Z,HBX_ROM		; IF NOT SET, SELECT ROM PAGE
;
HBX_RAM:
	RES	7,A			; CLEAR BIT 7 FROM ABOVE
	RLCA				; SCALE SELECTOR TO
	RLCA				; ... GO FROM Z180 4K PAGE SIZE
	RLCA				; ... TO DESIRED 32K PAGE SIZE
	OUT0	(Z180_BBR),A		; WRITE TO BANK BASE
	LD	A,N8_DEFACR | 80H	; SELECT RAM BY SETTING BIT 7
	OUT0	(N8_ACR),A		; ... IN N8 ACR REGISTER
	RET				; DONE
;
HBX_ROM:
	OUT0	(N8_RMAP),A		; BANK INDEX TO N8 RMAP REGISTER
	XOR	A			; ZERO ACCUM
	OUT0	(Z180_BBR),A		; ZERO BANK BASE
	LD	A,N8_DEFACR		; SELECT ROM  BY CLEARING BIT 7
	OUT0	(N8_ACR),A		; ... IN N8 ACR REGISTER
	RET				; DONE
#ENDIF
;
#IF (MEMMGR == MM_Z180)
	RLCA				; RAM FLAG TO CARRY FLAG AND BIT 0
	JR	NC,HBX_BNKSEL1		; IF NC, WANT ROM PAGE, SKIP AHEAD
	XOR	%00100001		; SET BIT FOR HI 512K, CLR BIT 0
HBX_BNKSEL1:
	RLCA				; CONTINUE SHIFTING TO SCALE SELECTOR
	RLCA				; FOR Z180 4K PAGE -> DESIRED 32K PAGE
	OUT0	(Z180_BBR),A		; WRITE TO BANK BASE
	RET				; DONE
#ENDIF
;
#IF (MEMMGR == MM_Z280)
	PUSH	BC			; SAVE BC
	PUSH	HL			; SAVE HL
	LD	B,$00			; FIRST USER PDR
	.DB	$ED,$71			; SC
	.DW	Z280_BNKSEL		; SC PARAMETER
	POP	HL			; RESTORE HL
	POP	BC			; RESTORE BC
	RET				; DONE
#ENDIF
;
#IF (MEMMGR == MM_ZRC)
	BIT	7,A			; BIT 7 SET REQUESTS RAM PAGE
	JR	Z,HBX_ROM		; NOT SET, SELECT ROM PAGE
	RES	7,A			; RAM PAGE REQUESTED: CLEAR ROM BIT
	ADD	A,$10			; ADD 16 x 32K - RAM STARTS FROM 512K
;
HBX_ROM:
	OUT	($1F),A			; HCS WRITE TO THE BANK CONTROL REGISTER
	RET				; DONE
#ENDIF
;
#IF (MEMMGR == MM_MBC)
;
  #IF (INTMODE == 1)
	LD	(HBX_MMA),A		; SAVE ACCUM
	LD	A,I			; GET INT CTL REG
	HB_DI				; DISABLE INTS
	PUSH	AF			; SAVE INT CTL REG
	LD	A,(HBX_MMA)		; RESTORE ACCUM
  #ENDIF
;
        OR      A                       ; SET FLAGS
        JP      P,HBX_ROM               ; BIT 7 INDICATES RAM
        OUT     (MPCL_ROM),A            ; ENSURE ROM PAGE OUT OF MEMORY BEFORE SWITCH

#IF (RAMSIZE == 256)
	XOR	%00000100		; TOP 32K IS ALWAYS IN FIRST CHIP
#ENDIF

#IF (RAMSIZE == 1024)
	XOR	%00010000		; TOP 32K IS ALWAYS IN FIRST CHIP
#ENDIF

        OUT     (MPCL_RAM),A            ; SET RAM PAGE SELECTOR
	JR	HBX_RAMX
HBX_ROM:
        OUT     (MPCL_RAM),A            ; ENSURE RAM PAGE OUT OF MEMORY BEFORE SWITCH
        OUT     (MPCL_ROM),A            ; SET ROM PAGE SELECTOR

HBX_RAMX:
;
  #IF (INTMODE == 1)
	POP	AF			; RESTORE INT CTL REG
	JP	PO,$+4			; WERE INTS DISABLED AT ENTRY?
	EI				; *** DO NOT USE HB_EI HERE ***
	LD	A,(HBX_MMA)		; RESTORE INCOMING ACCUM
  #ENDIF
;
	RET
;
HBX_MMA	.DB	0			; TEMPORARY STORAGE FOR REG A
#ENDIF
;
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; Copy Data - Possibly between banks.  This resembles CP/M 3, but
;  usage of the HL and DE registers is reversed.
; Caller MUST preset HBX_SRCBNK and HBX_DSTBNK.
; IM1/IM2 interrupts are disabled during HBX_BNKCPY.
; Enter:
;	 HL = Source Address
;	 DE = Destination Address
;	 BC = Number of bytes to copy
; Exit : None
; Uses : AF,BC,DE,HL
;
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;
HBX_BNKCPY:
#IF (MEMMGR == MM_Z280)
	.DB	$ED,$71			; SC
	.DW	Z280_BNKCPY		; SC PARAMETER
	RET
#ELSE
  #IF (CPUFAM == CPU_Z280)
	PUSH	HL
	PUSH	BC
	LD	C,Z280_MSR
	LDCTL	HL,(C)
	POP	BC
	EX	(SP),HL
	HB_DI
  #ELSE
	LD	A,I
	HB_DI
	PUSH	AF
  #ENDIF
	LD	(HBX_BC_SP),SP		; PUT STACK
	LD	SP,HBX_TMPSTK		; ... IN HI MEM

	LD	A,(HB_CURBNK)		; GET CURRENT BANK
	PUSH	AF			; AND SAVE TO RESTORE LATER
	PUSH	BC			; CUR LEN -> (SP)
;
HBX_BC_LOOP:
	EX	(SP),HL			; HL := CUR LEN, (SP) := CUR SRC
	LD	BC,HBX_BUFSIZ		; SET BC TO BOUNCE BUFFER SIZE
	OR	A			; CLEAR CARRY FLAG
	SBC	HL,BC			; CUR LEN := CUR LEN - BBUF SIZE
	JR	C,HBX_BC_LAST		; END GAME, LESS THAN BBUF BYTES LEFT
	EX	(SP),HL			; HL := CUR SRC, (SP) := REM LEN
	CALL	HBX_BC_ITER		; DO A FULL BBUF SIZE CHUNK
	JR	HBX_BC_LOOP		; AND REPEAT TILL DONE
;
HBX_BC_LAST:
	; HL IS BETWEEN -(BBUF SIZE) AND -1, BC = BBUF SIZE
	OR	A			; CLEAR CARRY
	ADC	HL,BC			; HL := REM LEN (0 - 127)
	EX	(SP),HL			; HL := CUR SRC, (SP) := REM LEN
	POP	BC			; BC := REM LEN
	CALL	NZ,HBX_BC_ITER		; DO FINAL CHUNK, IFF > 0 BYTES
	POP	AF			; RECOVER ORIGINAL BANK
	CALL	HBX_BNKSEL		; SWITCH

	LD	SP,$FFFF		; RESTORE STACK
HBX_BC_SP	.EQU	$ - 2		; ... TO ORIGINAL VALUE
  #IF (CPUFAM == CPU_Z280)
	EX	(SP),HL			; SAVE HL, RECOVER MSR
	PUSH	BC			; SAVE BC
	LD	C,Z280_MSR
	LDCTL	(C),HL
	POP	BC			; RECOVER BC
	POP	HL			; RECOVER HL
  #ELSE
	POP	AF
	JP	PO,$+4
	EI				; *** DO NOT USE HB_EI HERE ***
  #ENDIF
	RET
;
HBX_BC_ITER:
	; HL = SRC ADR, DE = DEST ADR, BC = LEN
	PUSH	BC			; SAVE COPY LEN
	PUSH	DE			; FINAL DEST ON STACK
	LD	DE,HBX_BUF		; SET DEST TO BUF
	LD	A,(HB_SRCBNK)		; GET SOURCE BANK
	CALL	HBX_BNKSEL		; SWITCH TO SOURCE BANK
	LDIR				; HL -> BUF (DE), BC BYTES, HL UPDATED SRC ADR
	POP	DE			; DE := FINAL DEST
	POP	BC			; GET LEN BACK IN BC
	PUSH	HL			; SAVE UPDATED SRC ADR
	LD	HL,HBX_BUF		; SET SRC ADR TO BUF
	LD	A,(HB_DSTBNK)		; GET DEST BANK
	CALL	HBX_BNKSEL		; SWITCH TO DEST BANK
	LDIR				; BUF (HL) -> DE, BC BYTES, DE UPDATED DEST ADR
	POP	HL			; RECOVER UPDATED SRC ADR
	; HL = UPDATED SRC, DE = UPDATED DEST, BC = 0
	RET
#ENDIF
;
; CALL A ROUTINE IN ANOTHER BANK.
; CALLER MUST ENSURE STACK IS ALREADY IN HIGH MEMORY AND HAS ADEQUATE SPACE.
; IF IM1 INTERRUPTS ARE POSSIBLE, CALLER MUST EITHER DISABLE THEM PRIOR TO
; BNKCALL OR MAKE SURE THAT PAGE ZERO IN TARGET BANK IS PREPARED FOR THEM.
; ON INPUT A=TARGET BANK, IX=TARGET ADDRESS
;
HBX_BNKCALL:
;
#IF (MEMMGR == MM_Z280)
	CP	BID_BIOS		; CALLING HBIOS?
	JR	NZ,HBX_BNKCALL3		; NOPE, DO NORMAL PROCESSING
	.DB	$ED,$71			; SC
	.DW	HBX_BNKCALL2		; CALL HERE IN SYSTEM MODE
	RET				; THEN RETURN
;
HBX_BNKCALL2:
	HB_EI				; INTS ARE OK
	LD	(HBX_BNKCALL_GO+1),IX	; SETUP DEST ADR
	PCACHE				; CRITICAL!!!
HBX_BNKCALL_GO:
	JP	$FFFF			; DO THE REAL WORK AND RETURN
#ENDIF
;
HBX_BNKCALL3:
	LD	(HBX_BNKCALL_BNK+1),A	; STUFF TARGET BANK TO CALL INTO CODE BELOW
	LD	(HBX_BNKCALL_ADR+1),IX	; STUFF ADDRESS TO CALL INTO CODE BELOW
	LD	A,(HB_CURBNK)		; GET CURRENT BANK
	PUSH	AF			; SAVE FOR RETURN
HBX_BNKCALL_BNK:
	LD	A,$FF			; LOAD BANK TO CALL ($FF OVERLAID AT ENTRY)
	CALL	HBX_BNKSEL		; ACTIVATE THE NEW BANK
HBX_BNKCALL_ADR:
	CALL	$FFFF			; CALL ROUTINE ($FFFF IS OVERLAID ABOVE)
	EX	(SP),HL			; SAVE HL AND GET BANK TO RESTORE IN HL
	PUSH	AF			; SAVE AF
	LD	A,H			; BANK TO RESTORE TO A
	CALL	HBX_BNKSEL		; RESTORE IT
	POP	AF			; RECOVER AF
	POP	HL			; RECOVER HL
	RET
;
; PEEK & POKE ROUTINES
;   ADDRESS IN HL, BANK IN D, VALUE IN/OUT IN E, A IS TRASHED
;
; THESE ROUTINES ARE NOT INTENDED TO BE CALLED DIRECTLY -- THEY ARE
; HELPERS FOR THE HBIOS API AND ARE CALLED BY HBIOS BANK CODE.  THE
; HBIOS BANK CODE BRACKETS THE USE OF THESE ROUTINES WITH DI/EI IF
; NECESSARY FOR THE CURRENT INTERRUPT MODE.
;
; NOTE: THE SIZE OF HBX_TMPSTK (TYPICALLY 20 BYTES) IS INSUFFICIENT FOR
; FREERTOS IF AN INTERRUPT STRIKES WHILE THE TEMPORARY STACK IS ACTIVE.
; BELOW, HBX_BUF HAS BEEN USURPED TO PROVIDE A LARGER TEMP STACK TO
; ACCOMMODATE FREERTOS.  HBX_BUF IS ONLY USED AS A BOUNCE BUFFER, SO IT'S
; USE WILL NEVER OVERLAP WITH BELOW.
;
HBX_PEEK:
	LD	(HBX_PPSP),SP		; SAVE ORIGINAL STACK FRAME
	LD	SP,HBX_BUF_END		; BORROW HBX_BUF FOR TEMP STACK
	LD	A,(HB_CURBNK)
	PUSH	AF
	LD	A,D
	CALL	HBX_BNKSEL
	LD	E,(HL)
	JR	HBX_PPRET
;
HBX_POKE:
	LD	(HBX_PPSP),SP		; SAVE ORIGINAL STACK FRAME
	LD	SP,HBX_BUF_END		; BORROW HBX_BUF FOR TEMP STACK
	LD	A,(HB_CURBNK)
	PUSH	AF
	LD	A,D
	CALL	HBX_BNKSEL
	LD	(HL),E
;
HBX_PPRET:
	POP	AF
#IF (MEMMGR == MM_Z280)
	LD	A,(HB_INVBNK)		; SPECIAL CASE FOR Z280 MEM MGR
#ENDIF
	CALL	HBX_BNKSEL
	LD	SP,0			; RESTORE ORIGINAL STACK FRAME
HBX_PPSP	.EQU	$ - 2
	RET
;
; SPECIAL ROUTINE IN HIGH MEMORY TO PERFORM A COLD START ON Z280
; THIS REQUIRES US TO REMAP LOW MEMORY, THEN JUMP TO ZERO
;
#IF (MEMMGR == MM_Z280)
;
Z280_RESTART:
	DI				; KILL INTERRUPTS
	LD	SP,HBX_LOC		; STACK IN HIGH MEMORY
;
	; COPY Z280 BANK SELECT ROUTINE TO HIGH MEMORY
	LD	HL,Z280_BNKSEL
	LD	DE,$8000
	LD	BC,Z280_BNKSEL_LEN
	LDIR
;
	; MAKE ROM BOOT BANK ACTIVE IN LOW SYS MEM
	LD	A,BID_BOOT
	LD	B,$10			; FIRST SYS PDR
	CALL	$8000			; DO IT
;
	; NOW JUST JUMP TO START OF ROM BOOT CODE
	JP	0
#ENDIF
;
; PRIVATE STACK AT END OF HBIOS CODE
; OCCUPIES SPACE BEFORE IVT
;
HBX_INTSTKSIZ	.EQU	$FF00 - $
		.ECHO	"HBIOS INT STACK space: "
		.ECHO	HBX_INTSTKSIZ
		.ECHO	" bytes.\n"
		.FILL	HBX_INTSTKSIZ,$FF
HBX_INTSTK	.EQU	$
;
;#IF (HBX_INTSTKSIZ < 24)
#IF (HBX_INTSTKSIZ < 22)
	.ECHO	"*** ERROR: INTERRUPT STACK IS TOO SMALL!!!\n"
	!!!	; FORCE AN ASSEMBLY ERROR
#ENDIF
;
#IF ((INTMODE == 2) | (INTMODE == 3))
;
; HBIOS INTERRUPT SLOT ASSIGNMENTS
;
; #	Z80		Z180
; ---	--------------	--------------
; 0	CTC0A		INT1 -+
; 1	CTC0B		INT2  |
; 2	CTC0C		TIM0  |
; 3	CTC0D		TIM1  |
; 4	UART0		DMA0  +- Z180 INTERNAL
; 5	UART1		DMA1  |
; 6			CSIO  |
; 7	SIO0		SER0  |
; 8	SIO1		SER1 -+
; 9	PIO0A		PIO0A
; 10	PIO0B		PIO0B
; 11	PIO1A		PIO1A
; 12	PIO1B		PIO1B
; 13			SIO0
; 14			SIO1
; 15
;
HBX_IVT:
	.DW	HBX_IV00
	.DW	HBX_IV01
	.DW	HBX_IV02
	.DW	HBX_IV03
	.DW	HBX_IV04
	.DW	HBX_IV05
	.DW	HBX_IV06
	.DW	HBX_IV07
	.DW	HBX_IV08
	.DW	HBX_IV09
	.DW	HBX_IV0A
	.DW	HBX_IV0B
	.DW	HBX_IV0C
	.DW	HBX_IV0D
	.DW	HBX_IV0E
	.DW	HBX_IV0F
;
HBX_IVTCNT	.EQU	($ - HBX_IVT) / 2
;
HBX_IV00:	CALL HBX_INT \ .DB $00 << 2
HBX_IV01:	CALL HBX_INT \ .DB $01 << 2
HBX_IV02:	CALL HBX_INT \ .DB $02 << 2
HBX_IV03:	CALL HBX_INT \ .DB $03 << 2
HBX_IV04:	CALL HBX_INT \ .DB $04 << 2
HBX_IV05:	CALL HBX_INT \ .DB $05 << 2
HBX_IV06:	CALL HBX_INT \ .DB $06 << 2
HBX_IV07:	CALL HBX_INT \ .DB $07 << 2
HBX_IV08:	CALL HBX_INT \ .DB $08 << 2
HBX_IV09:	CALL HBX_INT \ .DB $09 << 2
HBX_IV0A:	CALL HBX_INT \ .DB $0A << 2
HBX_IV0B:	CALL HBX_INT \ .DB $0B << 2
HBX_IV0C:	CALL HBX_INT \ .DB $0C << 2
HBX_IV0D:	CALL HBX_INT \ .DB $0D << 2
HBX_IV0E:	CALL HBX_INT \ .DB $0E << 2
HBX_IV0F:	CALL HBX_INT \ .DB $0F << 2
;
#ENDIF
;
INT_IM1:
#IF (INTMODE == 1)
	CALL	HBX_INT
	.DB	$00
#ELSE
	RETI				; UNEXPECTED INT, RET W/ INTS LEFT DISABLED
#ENDIF
;
#IF (INTMODE > 0)
;
HBX_INT:	; COMMON INTERRUPT ROUTING CODE
;
  #IF (MEMMGR == MM_Z280)
;
; THIS CODE ASSUMES Z280 IM 3.  IM 1 AND IM 2 ON Z280
; DO NOT SAVE MSR AT INTERRUPT MAKING IT VIRTUALLY IMPOSSIBLE
; TO RETURN FROM THE INTERRUPT TO THE CORRECT MODE (SYSTEM
; OR USER).  THIS IS BECAUSE THERE IS NO WAY TO KNOW WHETHER
; SYSTEM OR USER MODE WAS ACTIVE AT THE TIME OF THE INTERRUPT.
;
	EX	(SP),HL			; SAVE HL AND GET INT JP TABLE OFFSET

	; SAVE STATE (HL SAVED PREVIOUSLY ON ORIGINAL STACK FRAME)
	PUSH	AF			; SAVE AF
	PUSH	BC			; SAVE BC
	PUSH	DE			; SAVE DE
	PUSH	IY			; SAVE IY
;
	; HANDLE INT VIA JP TABLE IN HBIOS
	LD	L,(HL)			; OFFSET INTO JP TABLE FOR THIS INT
	LD	H,HB_IVT >> 8		; MSB OF HBIOS INT JP TABLE
	CALL	JPHL			; CALL HANDLER VIA INT JP TABLE
;
	; RESTORE STATE
	POP	IY			; RESTORE IY
	POP	DE			; RESTORE DE
	POP	BC			; RESTORE BC
	POP	AF			; RESTORE AF
	POP	HL			; RESTORE HL
;
	; BURN THE REASON CODE
	EX	(SP),HL			; HL TO STK, RC TO HL
	POP	HL			; RESTORE HL
;
	CALL	HBX_RETI		; RETI FOR Z80 PERIPHERALS
	RETIL
;
HBX_RETI:
	RETI
;
  #ELSE
;
; COMMON INTERRUPT DISPATCHING CODE
; SETUP AND CALL HANDLER IN BIOS BANK
;
	EX	(SP),HL			; SAVE HL AND GET INT JP TABLE OFFSET
;
	LD	(HBX_INT_SP),SP		; SAVE ORIGINAL STACK FRAME
	LD	SP,HBX_INTSTK		; USE DEDICATED INT STACK FRAME IN HI MEM
;
	; SAVE STATE (HL SAVED PREVIOUSLY ON ORIGINAL STACK FRAME)
	PUSH	AF			; SAVE AF
	PUSH	BC			; SAVE BC
	PUSH	DE			; SAVE DE
	PUSH	IY			; SAVE IY
;
	;LD	A,BID_BIOS		; HBIOS BANK
	;CALL	HBX_BNKSEL_INT		; SELECT IT
;
	LD	L,(HL)			; OFFSET INTO JP TABLE FOR THIS INT
	LD	H,HB_IVT >> 8		; MSB OF HBIOS INT JP TABLE
;
	CALL	JPHL			; CALL HANDLER VIA INT JP TABLE
;
	;LD	A,(HB_CURBNK)		; GET PRE-INT BANK
	;CALL	HBX_BNKSEL		; SELECT IT
;
	; RESTORE STATE
	POP	IY			; RESTORE IY
	POP	DE			; RESTORE DE
	POP	BC			; RESTORE BC
	POP	AF			; RESTORE AF
;
	LD	SP,$FFFF		; RESTORE ORIGINAL STACK FRAME
HBX_INT_SP	.EQU	$ - 2
;
	POP	HL			; RESTORE HL
;
	HB_EI				; ENABLE INTERRUPTS
	RETI				; AND RETURN
;
  #ENDIF

#ENDIF
;
; SMALL TEMPORARY STACK FOR USE BY HBX_BNKCPY
;
HBX_TMPSTKSIZ	.EQU	(HBX_XFC - HBX_BUFSIZ - $)
		.ECHO	"HBIOS TEMP STACK space: "
		.ECHO	HBX_TMPSTKSIZ
		.ECHO	" bytes.\n"
		.FILL	HBX_TMPSTKSIZ,$CC
HBX_TMPSTK	.EQU	$
;
; INTERBANK COPY BOUNCE BUFFER (64 BYTES)
;
; N.B., THIS BUFFER IS ALSO USED AS A TEMPORARY STACK BY INVOKE, PEEK, AND POKE.
; THEREFORE, THIS BUFFER *CANNOT* BE USED TO PASS DATA OUTSIDE OF
; HBIOS FUNCTION CALLS.
;
HBX_BUF		.FILL	HBX_BUFSIZ,0
HBX_BUF_END	.EQU	$
;
; HBIOS PROXY MGMT BLOCK (TOP 32 BYTES)
;
#IFDEF ROMBOOT
	.DB	BID_BOOT		; HB_CURBNK: CURRENTLY ACTIVE LOW MEMORY BANK ID
#ELSE
	.DB	BID_USR			; HB_CURBNK: CURRENTLY ACTIVE LOW MEMORY BANK ID
#ENDIF
	.DB	$FF			; HB_INVBNK: BANK ACTIVE AT TIME OF HBIOS CALL INVOCATION
	.DW	0			; HB_SRCADR: BNKCPY SOURCE ADDRESS
	.DB	BID_USR			; HB_SRCBNK: BNKCPY SOURCE BANK ID
	.DW	0			; HB_DSTADR: BNKCPY DESTINATION ADDRESS
	.DB	BID_USR			; HB_DSTBNK: BNKCPY DESTINATION BANK ID
	.DW	0			; HB_CPYLEN: BNKCPY LENGTH
	.FILL	4,0			; FILLER, RESERVED FOR FUTURE HBIOS USE
	.DB	RTCDEF			; SHADOW VALUE FOR RTC LATCH PORT
	.DB	$FE			; HB_LOCK: HBIOS MUTEX LOCK
	JP	0			; HB_INVOKE: FIXED ADR ENTRY FOR HBX_INVOKE (ALT FOR RST 08)
	JP	HBX_BNKSEL		; HB_BNKSEL: FIXED ADR ENTRY FOR HBX_BNKSEL
	JP	HBX_BNKCPY		; HB_BNKCPY: FIXED ADR ENTRY FOR HBX_BNKCPY
	JP	HBX_BNKCALL		; HB_BNKCALL: FIXED ADR ENTRY FOR HBX_BNKCALL
	.DW	HBX_IDENT		; ADDRESS OF HBIOS PROXY START (DEPRECATED)
	.DW	HBX_IDENT		; HB_IDENT: ADDRESS OF HBIOS IDENT INFO DATA BLOCK
;
	.FILL	MEMTOP - $		; FILL TO END OF MEMORY (AS NEEDED)
	.ORG	HBX_IMG + HBX_SIZ	; RESET ORG
;
;==================================================================================================
;   HDIAG CORE
;==================================================================================================
;
;==================================================================================================
;   SYSTEM INITIALIZATION
;==================================================================================================
;
HB_START:
#IFDEF APPBOOT
  #IF (MEMMGR == MM_Z280)
	LD	A,%00000001
	OUT	(DIAGPORT),A
	LD	DE,Z280_BOOTERR
	LD	C,9
	LD	A,%00000010
	OUT	(DIAGPORT),A
	CALL	$0005
	LD	A,%00001000
	OUT	(DIAGPORT),A
	RET
;
Z280_BOOTERR	.TEXT	"\r\n\r\n*** Application mode boot not supported under Z280 native memory management!!!\r\n\r\n$"
  #ENDIF
#ENDIF

	DI				; NO INTERRUPTS
	IM	1			; INTERRUPT MODE 1
;
#IF (DIAGENABLE)
	LD	A,%00000001
	OUT	(DIAGPORT),A
#ENDIF
#IF (LEDENABLE)
  #IF (LEDMODE == LEDMODE_STD)
	XOR	A			; LED IS INVERTED, TURN IT ON
  #ENDIF
  #IF (LEDMODE == LEDMODE_RTC)
	LD	A,%00000001		; LED 0
	LD	(HB_RTCVAL),A		; SAVE TO SHADOW REGISTER
  #ENDIF
	OUT	(LEDPORT),A
#ENDIF
;
	LD	SP,HBX_LOC		; SETUP INITIAL STACK JUST BELOW HBIOS PROXY
;
#IF (CPUFAM == CPU_Z280)
	; SET MAXIMUM I/O WAIT STATES FOR NOW
	LD	C,Z280_BTCR		; BUS TIMING AND CONTROL REGISTER
	LD	HL,$0033		; 3 I/O WAIT STATES ADDED
	LDCTL	(C),HL
;
	; START BY SELECTING I/O PAGE $FF
	LD	L,$FF			; MMU AND DMA PAGE I/O REG IS $FF
	LD	C,Z280_IOPR		; REG C POINTS TO I/O PAGE REGISTER
	LDCTL	(C),HL
;
  #IF (MEMMGR == MM_Z280)
;
	; INITIALIZE ALL OF THE SYSTEM PAGE DESCRIPTORS WITH BLOCK MOVE
	XOR	A			; FIRST USER PDR
	OUT	(Z280_MMUPDRPTR),A	; SET THE PDR POINTER
	LD	HL,Z280_BOOTPDRTBL	; START OF PDR VALUES TABLE
	LD	C,Z280_MMUBLKMOV	; PDR BLOCK MOVE PORT
	LD	B,16			; PROGRAM 16 PDRS
	.DB	$ED,$93			; OTIRW
;
	; INITIALIZE ALL OF THE USER PAGE DESCRIPTORS WITH BLOCK MOVE
	LD	A,$10			; FIRST SYSTEM PDR
	OUT	(Z280_MMUPDRPTR),A	; SET THE PDR POINTER
	LD	HL,Z280_BOOTPDRTBL	; START OF PDR VALUES TABLE
	LD	C,Z280_MMUBLKMOV	; PDR BLOCK MOVE PORT
	LD	B,16			; PROGRAM 16 PDRS
	.DB	$ED,$93			; OTIRW
;
	; ENABLE MMU (SYSTEM AND USER TRANSLATION)
	LD	C,Z280_MMUMCR		; MMU MASTER CONTROL REGISTER
	LD	HL,$BBFF		; ENABLE USER & SYSTEM TRANSLATE
	OUTW	(C),HL
;
	; DISABLE MEMORY REFRESH CYCLES
	LD	A,$08			; DISABLED
	OUT	(Z280_RRR),A		; SET REFRESH RATE REGISTER
;
	; CONFIGURE Z280 INT/TRAP VECTOR TABLE POINTER REGISTER
	; WILL POINT TO ROM COPY FOR NOW, UPDATED TO RAM LATER ON
	LD	C,Z280_VPR
	LD	HL,Z280_IVT >> 8	; TOP 16 BITS OF PHYSICAL ADR OF IVT
	LDCTL	(C),HL
;
	JR	Z280_INITZ		; JUMP TO CODE CONTINUATION
;
    #IF (($ % 2) == 1)
	; WORD ALIGN THE TABLE
	.DB	0
    #ENDIF
;
Z280_BOOTPDRTBL:
	; LOWER 32 K (BANKED)
	.DW	($000 << 4) | $A
	.DW	($001 << 4) | $A
	.DW	($002 << 4) | $A
	.DW	($003 << 4) | $A
	.DW	($004 << 4) | $A
	.DW	($005 << 4) | $A
	.DW	($006 << 4) | $A
	.DW	($007 << 4) | $A
	; UPPER 32 K (COMMON)
	.DW	(((((BID_COM & $7F) * 8) + 0) + (1 << (RAMLOC - 12))) << 4) | $A
	.DW	(((((BID_COM & $7F) * 8) + 1) + (1 << (RAMLOC - 12))) << 4) | $A
	.DW	(((((BID_COM & $7F) * 8) + 2) + (1 << (RAMLOC - 12))) << 4) | $A
	.DW	(((((BID_COM & $7F) * 8) + 3) + (1 << (RAMLOC - 12))) << 4) | $A
	.DW	(((((BID_COM & $7F) * 8) + 4) + (1 << (RAMLOC - 12))) << 4) | $A
	.DW	(((((BID_COM & $7F) * 8) + 5) + (1 << (RAMLOC - 12))) << 4) | $A
	.DW	(((((BID_COM & $7F) * 8) + 6) + (1 << (RAMLOC - 12))) << 4) | $A
	.DW	(((((BID_COM & $7F) * 8) + 7) + (1 << (RAMLOC - 12))) << 4) | $A
;
Z280_INITZ:
;
  #ENDIF
;
	; RESTORE I/O PAGE TO $00
	LD	L,$00			; NORMAL I/O REG IS $00
	LD	C,Z280_IOPR		; REG C POINTS TO I/O PAGE REGISTER
	LDCTL	(C),HL
;
#ENDIF
;
#IF (CPUFAM == CPU_Z180)
	; SET BASE FOR CPU IO REGISTERS
	; DO NOT USE Z180_ICR FROM Z180.INC BECAUSE THE ICR
	; IS NOT YET AT THE RUNNING LOCATION.  AT RESET, THE Z180
	; REGISTER BASE I/O ADDRESS IS ZERO, SO INITIALLY, ICR IS
	; AT $3F.
	LD	A,Z180_BASE
	OUT0	($3F),A		; AT RESET, ICR IS AT $3F

	DIAG(%00000010)

	; DISABLE REFRESH
	XOR	A
	OUT0	(Z180_RCR),A

	; MASK OFF TIMER INTERRUPTS
	XOR	A
	OUT0	(Z180_TCR),A
	OUT0	(Z180_ITC),A

	; SET DEFAULT CPU CLOCK MULTIPLIERS (XTAL / 2)
	;
	; IT HAS BEEN REPORTED THAT CMR NEEDS TO BE SET PRIOR TO CCR
	; WHEN USING AN INPUT FREQUENCY THAT IS XTAL / 2.
	; I NEVER EXPERIENCED A PROBLEM RELATED TO ORDER, BUT JUST
	; FOR GOOD MEASURE, CMR IS SET PRIOR TO CCR BELOW.
	; https://www.retrobrewcomputers.org/forum/index.php?t=msg&th=316&#msg_5045
	XOR	A
	OUT0	(Z180_CMR),A
	OUT0	(Z180_CCR),A

	; SET DEFAULT WAIT STATES
	LD	A,$F0
	OUT0	(Z180_DCNTL),A

  #IF ((MEMMGR == MM_Z180) | (MEMMGR == MM_N8))
	; Z180 MMU SETUP
	LD	A,$80
	OUT0	(Z180_CBAR),A		; SETUP FOR 32K/32K BANK CONFIG
;#IFDEF ROMBOOT
;	XOR	A
;	OUT0	(Z180_BBR),A		; BANK BASE = 0
;#ENDIF
	LD	A,(RAMSIZE + RAMBIAS - 64) >> 2
	OUT0	(Z180_CBR),A		; COMMON BASE = LAST (TOP) BANK
;
	; SET DEFAULT CSIO SPEED (INTERNAL CLOCK, SLOW AS POSSIBLE)
	LD	A,Z180_CNTR_DEF		; DIV 1280, 14KHZ @ 18MHZ CLK
	OUT0	(Z180_CNTR),A
  #ENDIF
;
#ENDIF
;
#IF (EIPCENABLE)
	LD	A,(EIPC_WDT_CONST | EIPC_HALT_RUN | EIPC_WDT_P2_22)
	OUT	(EIPC_WDTMR),A		; CLEAR WDTE BIT (DISABLE WATCHDOG)
	LD	A,EIPC_DIS_WDT		; DISABLE WDT - SECOND KEY
	OUT 	(EIPC_WDTCR),A
	LD	A,EIPC_WCR		; SET SYSTEM CONTROL REGISTER POINTER
					; (SCRP) TO POINT TO WAIT STATE
	OUT	(EIPC_SCRP),A		; CONTROL REGISTER (WCR)
	LD	A,(EIPC_IO_0WS | EIPC_MEM_OWS | EIPC_OCF_0WS | EIPC_INT_0WS | EIPC_CHAIN_0WS)
	OUT	(EIPC_SCDP),A		; NO WAIT STATES
	LD	A,EIPC_MCR		; SET SCRP TO POINT TO MISCELLANEOUS
	OUT	(EIPC_SCRP),A		; CONTROL REGISTER (MCR)
	LD	A,EIPC_CLKDIV1		; DIVIDE CLOCK BY 1, /CS0 DISABLE
	OUT	(EIPC_SCDP),A		; SET SYSTEM CONTROL DATA PORT (SCDP)
#ENDIF
;
#IF ((MEMMGR == MM_SBC) | (MEMMGR == MM_MBC))
	; SET PAGING REGISTERS
  #IFDEF ROMBOOT
	XOR	A
	OUT	(MPCL_RAM),A		; REMOVE RAM FIRST!
	OUT	(MPCL_ROM),A		; SELECT ROM PAGE 0
  #ENDIF
#ENDIF
;
#IF (MEMMGR == MM_Z2)
	; SET PAGING REGISTERS
  #IFDEF ROMBOOT
	XOR	A
	OUT	(MPGSEL_0),A
	INC	A
	OUT	(MPGSEL_1),A
  #ENDIF
	LD	A,62
	OUT	(MPGSEL_2),A
	INC	A
	OUT	(MPGSEL_3),A
	; ENABLE PAGING
	LD	A,1
	OUT	(MPGENA),A
#ENDIF
;
	DIAG(%00000011)
;
; CHECK BATTERY BACKUP STATUS BEFORE WE COPY PROXY TO UPPER MEMORY
;
; IF A DS1210 POWER CONTROLLER IS INSTALLED AND BATTERY BACKUP IS NOT INSTALLED
; OR IS LESS THAN 2V THEN THE DS1210 WILL BLOCK THE SECOND RAM ACCESS.
; FAILURE TO COMPLETE TWO RAM ACCESSES BEFORE INSTALLING PROXY WILL RESULT
; IN THE ROM ID BYTES NOT BEING COPIED CORRECTLY AND CP/M APPLICATIONS
; WILL NOT START CORRECTLY WHEN THEY CHECK THE ROM ID VERSION BYTES.
; THE BATTERY CONDITION VALUE IS TEMPORARILY STORED AT HBX_LOC - 1.
; IF THERE IS NO DS1210 IN THE SYSTEM, THE CODE BELOW DOES NO HARM.
;
	DEC	SP			; RESERVE A STACK BYTE
	XOR	A			; ZERO MEANS LOW BAT
	LD	(HBX_LOC - 1),A		; WRITE IT (SHOULD ALWAYS WORK)
	INC	A			; 1 MEANS BAT OK
	LD	(HBX_LOC - 1),A		; OVERWRITE IF NVC ALLOWS IT
;
; IF APPBOOT, SAVE CURRENT BANKID
;
; THIS IS NOT GOING TO WORK IF THE APP BOOT IMAGE IS LOADED
; USING THE UNA FAT32 LOADER.  SHOULD PROBABLY CHECK THAT THERE
; IS A VALID ROMWBW PROXY IN MEMORY BEFORE DOING THIS.  HOWEVER,
; THIS USE CASE IS PROBABLY NON-EXISTENT.  THE IMG BOOT IMAGE
; SHOULD WORK FINE WITH THE UNA FAT32 LOADER.
;
#IFDEF APPBOOT
	LD	A,(HB_CURBNK)
	DEC	SP			; RESERVE A STACK BYTE
	LD	(HBX_LOC - 2),A		; SAVE BANK
	PUSH	AF			; ALSO ON STACK
#ENDIF
;
; INSTALL PROXY IN UPPER MEMORY
;
	LD	DE,HBX_LOC		; AS PER ABOVE
	LD	HL,HBX_IMG
	LD	BC,HBX_SIZ
	LDIR
;
; IF APPBOOT, RESTORE CURRENT BANK ID
;
#IFDEF APPBOOT
	POP	AF
	LD	(HB_CURBNK),A
#ENDIF
;
; TRANSITION TO UPPER (COMMON) RAM
;
	LD	HL,0			; FROM START OF CURRENT BANK
	LD	DE,$8000		; TO START OF COMMON RAM
	LD	BC,$8000-$200-1		; EVERYTHING BUT THE PROXY AND BATCOND
	LDIR
	JP	HB_START1
;
; EXECUTION RESUMES HERE AFTER SWITCH TO RAM BANK
;
	.ORG	$8000 + $
;
HB_START1:				; BNKCALL ARRIVES HERE, BUT NOW RUNNING IN RAM BANK
;
	DIAG(%00000111)
	LED(%00000010)
;
	LD	A,(HBX_LOC - 1)		; RECALL BATTERY STATE AND SAVE
	LD	(HB_BATCOND),A		; FOR FUTURE REFERENCE
;
	LD	SP,HBX_LOC		; RESET STACK SINCE WE DO NOT RETURN
;
#IF (MEMMGR == MM_Z280)
	; NOW POINT TO RAM COPY OF Z280 INT/TRAP TABLE
	; HL IS TOP 16 BITS OF PHYSICAL ADDRESS OF IVT
	; IVT *MUST* BE ON A 4K BOUNDARY
	LD	C,Z280_VPR
	LD	HL,0 + ((((BID_BIOS & $7F) * 8) + (1 << (RAMLOC - 12))) << 4) + (Z280_IVT >> 8)
	LDCTL	(C),HL
#ENDIF
;
; IF APPBOOT, WE NEED TO FIX UP A FEW THINGS IN PAGE ZERO
;
#IFDEF APPBOOT
;
	; GET AND SAVE APP BOOT BANK ID
	LD	A,(HBX_LOC - 2)
	LD	(HB_APPBNK),A
;;;;
;;;	; MAKE SURE RST 08 VECTOR IS RIGHT
;;;	LD	A,$C3
;;;	LD	($0008),A
;;;	LD	HL,HB_INVOKE
;;;	LD	($0009),HL
;
	; MAKE SURE IM1 INT VECTOR IS RIGHT
  #IF (INTMODE == 1)
	; JP INT_IM1 IF INTERRUPT MODE ACTIVE
	LD	A,$C3
	LD	($0038),A
	LD	HL,INT_IM1
	LD	($0039),HL
  #ELSE
	; RETI ($ED, $4D) IF NON-INTERRUPT MODE
	LD	HL,$0038
	LD	(HL),$ED
	INC	HL
	LD	(HL),$4D
  #ENDIF
;
#ENDIF
;
	DIAG(%00001111)
;
#IF FALSE
;
; TEST DEBUG ***************************************************************************************
;
	PRTS("DEBUG-IVT$")
	LD	DE,HB_IVT
	CALL	DUMP_BUFFER
	CALL	NEWLINE
;
; TEST DEBUG ***************************************************************************************
;
#ENDIF
;
; DISCOVER CPU TYPE
;
; SOME OF THIS CODE IS DERIVED FROM UNA BY JOHN COFFMAN
;
;	0: Z80
;	1: Z80180 - ORIGINAL Z180 (EQUIVALENT TO HD64180)
;	2: Z8S180 - ORIGINAL S-CLASS, REV. K, AKA SL1960, NO ASCI BRG
;	3: Z8S180 - REVISED S-CLASS, REV. N, W/ ASCI BRG
;	4: Z8280
;
	LD	HL,0		; L = 0 MEANS Z80
;
#IF (CPUFAM == CPU_Z180)
;
	; TEST FOR ORIGINAL Z180 USING MLT
	LD	DE,$0506	; 5 X 6
	MLT	DE		; DE = 30 IF Z180
	LD	A,E		; CHECK IF MULTIPLY HAPPENED
	CP	30
	JR	NZ,HB_CPU1	; IT IS A Z80 IF != 30
	INC	L		; FLAG Z80180 OR BETTER
;
	; TEST FOR OLDER S-CLASS (REV K)
	IN0	A,(Z180_CCR)	; SUPPOSEDLY ONLY ON S-CLASS
	INC	A		; FF -> 0
	JR	Z,HB_CPU1
	INC	L		; FLAG Z8S180 REV K (SL1960) OR BETTER
;
	; TEST FOR NEWER S-CLASS (REV N)
	; ON OLDER S-CLASS, ASCI TIME CONSTANT REG DOES NOT EXIST
	; AND WILL ALWYAS READ BACK AS $FF
	OUT0	(Z180_ASTC1L),D	; D = 0 AT THIS POINT
	IN0	A,(Z180_ASTC1L)	; ASCI TIME CONSTANT REG
	INC	A		; FF -> 0
	JR	Z,HB_CPU1
	INC	L		; FLAG Z8S180 REV N W/ ASCI BRG
;
#ENDIF
;
#IF (CPUFAM == CPU_Z280)
;
	; TEST FOR Z280 PER ZILOG DOC
	LD	A,$40		; INITIALIZE THE OPERAND
	.DB	$CB,$37		; THIS INSTRUCTION WILL SET THE S FLAG
				; ON THE Z80 CPU AND CLEAR THE S FLAG
				; ON THE Z280 MPU.
	JP	M,HB_CPU1	; IF Z80, SKIP AHEAD
	LD	L,4		; WE ARE Z280
;
#ENDIF
;
HB_CPU1:
	LD	A,L
	LD	(HB_CPUTYPE),A
;
#IF (SKZENABLE)
;
	; SET THE SK Z80-512K UART CLK2 DIVIDER AS
	; CONFIGURED.  NOTE THAT THIS IMPLICITLY
	; CLEARS THE WATCHDOG BIT.  THE WATCHDOG
	; WILL BE ENABLED LATER IF CONFIGURED.
	LD	A,SKZDIV	; GET DIVIDER CODE
	OUT	($6D),A		; IMPLEMENT IT
;
#ENDIF
;
#IF (CPUFAM == CPU_Z180)
;
	; AT BOOT, Z180 PHI IS OSC / 2
	LD	C,(CPUOSC / 2) / 1000000
	LD	DE,(CPUOSC / 2) / 1000
;
  #IF (Z180_CLKDIV >= 1)
	LD	A,(HB_CPUTYPE)	; GET CPU TYPE
	CP	2		; Z8S180 REV K OR BETTER?
	JR	C,HB_CPU2	; IF NOT, NOT POSSIBLE!
	; SET CLOCK DIVIDE TO 1 RESULTING IN FULL XTAL SPEED
	LD	A,$80
	OUT0	(Z180_CCR),A
	; REFLECT SPEED CHANGE
	LD	C,CPUOSC / 1000000
	LD	DE,CPUOSC / 1000
  #ENDIF

  #IF (Z180_CLKDIV >= 2)
	LD	A,(HB_CPUTYPE)	; GET CPU TYPE
	CP	3		; Z8S180 REV N OR BETTER?
	JR	C,HB_CPU2	; IF NOT, NOT POSSIBLE!
	; SET CPU MULTIPLIER TO 1 RESULTING IN XTAL * 2 SPEED
	; ALSO SET CCR AGAIN BECAUSE OF REPORTS THAT CCR
	; *MUST* BE SET AFTER CMR.
	LD	A,$80
	OUT0	(Z180_CMR),A	; CPU MULTIPLIER
	OUT0	(Z180_CCR),A	; CLOCK DIVIDE
	; REFLECT SPEED CHANGE
	LD	C,(CPUOSC * 2) / 1000000
	LD	DE,(CPUOSC * 2) / 1000
  #ENDIF
;
HB_CPU2:
	; SAVE CPU SPEED
	LD	A,C
	LD	(CB_CPUMHZ),A
	LD	(CB_CPUKHZ),DE
;
#ENDIF
;
	DIAG(%00011111)
;
	LD	A,(CPUMHZ)		; CPU SPEED TO ACCUM AND INIT
	CALL	DELAY_INIT		; .. SPEED COMPENSATED DELAY
;
#IF (CPUFAM == CPU_Z180)
;
	; SET FINAL DESIRED WAIT STATES
	LD	A,0 + (Z180_MEMWAIT << 6) | (Z180_IOWAIT << 4)
	OUT0	(Z180_DCNTL),A
;
#ENDIF
;
#IF (CPUFAM == CPU_Z280)
;
	LD	C,Z280_BTCR		; BUS TIMING AND CONTROL REG
	LDCTL	HL,(C)
	LD	A,L			; PUT IN A
	AND	%00111100		; CLEAR DC,HM, AND IO FIELDS
	OR	Z280_INTWAIT << 6	; SET INT ACK WAIT STATE BITS (DC)
	OR	Z280_MEMHIWAIT << 2	; SET HIGH 8MB WAIT STATE BITS (HM)
	OR	Z280_IOWAIT		; SET I/O WAIT STATE BITS
	LD	L,A			; BACK TO L
	LDCTL	(C),HL
;
	LD	C,Z280_BTIR		; BUS TIMING AND INIT REG
	LDCTL	HL,(C)
	LD	A,L			; PUT IN A
	AND	%11110011		; CLEAR LM FIELD
	OR	Z280_MEMLOWAIT << 2	; SET LOW 8MB WAIT STATE BITS
	LD	L,A			; BACK TO L
	LDCTL	(C),HL
;
#ENDIF
;
#IF (INTMODE == 2)
	; SETUP Z80 IVT AND INT MODE 2
	LD	A,HBX_IVT >> 8		; SETUP HI BYTE OF IVT ADDRESS
	LD	I,A			; ... AND PLACE IT IN I REGISTER

  #IF (CPUFAM == CPU_Z180)
	; SETUP Z180 IVT
	XOR	A			; SETUP LO BYTE OF IVT ADDRESS
	OUT0	(Z180_IL),A		; ... AND PLACE IN Z180 IL REGISTER
  #ENDIF

	IM	2			; SWITCH TO INT MODE 2
#ENDIF
;
#IF (INTMODE == 3)
;
	; SETUP Z280 INT A FOR VECTORED INTERRUPTS
	LD	HL,%0010000000000000
	LD	C,Z280_ISR
	LDCTL	(C),HL
;
	IM	3
;
#ENDIF
;
#IF (CPUFAM == CPU_Z180)
;
  #IF (INTMODE == 2)
;
	; MASK ALL EXTERNAL INTERRUPTS FOR NOW
	LD	A,$01			; INT0 ENABLED, INT1-2 DISABLED
	OUT0	(Z180_ITC),A		; WRITE TO INT/TRAP CONTROL REGISTER
;
  #ENDIF
;
#ENDIF
;
	DIAG(%00111111)
#IF FALSE
;
; TEST DEBUG ***************************************************************************************
;
	CALL	NEWLINE
	CALL	REGDMP
;
; TEST DEBUG ***************************************************************************************
;
#ENDIF
;
#IF (DSKYENABLE)
	CALL	DSKY_PREINIT
;
	LD	HL,MSG_HBVER +  5
	LD	A,(DSKY_HEXMAP + RMJ)
	OR	$80
	LD	(HL),A
	INC	HL
	LD	A,(DSKY_HEXMAP + RMN)
	OR	$80
	LD	(HL),A
	INC	HL
	LD	A,(DSKY_HEXMAP + RUP)
	LD	(HL),A
	LD	HL,MSG_HBVER
	CALL	DSKY_SHOW
#ENDIF
;
	; INITIALIZE SERIAL PORT
	CALL	CINIT
;
#IF FALSE
;
; TEST DEBUG ***************************************************************************************
;
	CALL	NEWLINE
	CALL	REGDMP
;
; TEST DEBUG ***************************************************************************************
;
#ENDIF
;
	DIAG(%01111111)
	LED(%00000011)
;
#IF FALSE
;
; TEST DEBUG ***************************************************************************************
;
	CALL	NEWLINE2
	PRTS("DEBUG+IVT$")
	LD	DE,HB_IVT
	CALL	DUMP_BUFFER
;
; TEST DEBUG ***************************************************************************************
;
#ENDIF
;
; ANNOUNCE HBIOS
;
	CALL	NEWLINE2
	PRTX(STR_BANNER)
;
	DIAG(%11111111)
;
; IO PORT SCAN
;
#IF FALSE
PSCN:
	LD	C,0			; IO PORT NUMBER
	LD	B,0			; LOOP COUNTER
	CALL	NEWLINE
PSCN1:
	CALL	NEWLINE
	LD	A,C
	CALL	PRTHEXBYTE
	CALL	PC_COLON
	CALL	PC_SPACE
	CALL	DELAY
	LD	A,C
	LD	(PSCNX),A
PSCNX	.EQU	$ + 1
	IN	A,(0)
	CALL	PRTHEXBYTE
	CALL	PC_COMMA
	PUSH	BC
	LD	B,0
	IN	A,(C)
	POP	BC
	CALL	PRTHEXBYTE
	INC	C
	DJNZ	PSCN1
#ENDIF
;
	HB_EI				; INTERRUPTS SHOULD BE OK NOW
;
; DISPLAY PLATFORM INFORMATION
;
	CALL	NEWLINE2
	PRTX(STR_PLATFORM)
;
	LD	A,(HB_CPUTYPE)		; GET CPU TYPE
	LD	DE,HB_CPU_STR		; DISPLAY IT
	CALL	PRTIDXDEA
;
	PRTS(" @ $")
	LD	HL,(CB_CPUKHZ)
	CALL	PRTD3M			; PRINT AS DECIMAL WITH 3 DIGIT MANTISSA
	PRTS("MHz$")
;
#IF (CPUFAM == CPU_Z180)
	PRTS(" IO=0x$")
	LD	A,Z180_BASE
	CALL	PRTHEXBYTE
#ENDIF
;
#IF (CPUFAM == CPU_Z280)
	CALL	PRTSTRD
	.TEXT	", BUS @ $"
	LD	C,Z280_BTIR		; BUS TIMING AND CTL REG
	LDCTL	HL,(C)
	LD	A,L			; MOVE TO A
	AND	%00000011		; ISOLATE CS FIELD
	LD	HL,(CB_CPUKHZ)		; GET CPU SPEED
	CP	%00000001		; BUS @ 1/1
	JR	Z,HB_Z280BUS		; GOT IT, SHOW IT
	SRL	H			; DIVIDE
	RR	L			; ... BY 2
	CP	%00000000		; BUS @ 1/2
	JR	Z,HB_Z280BUS		; GOT IT, SHOW IT
	SRL	H                       ; DIVIDE
	RR	L                       ; ... BY 2
	CP	%00000010		; BUS @ 1/4
	JR	Z,HB_Z280BUS		; GOT IT, SHOW IT
	PRTS("???$")			; INVALID VALUE
	JR	HB_Z280BUS1		; CONTINUE
HB_Z280BUS:
	CALL	PRTD3M			; PRINT AS DECIMAL WITH 3 DIGIT MANTISSA
HB_Z280BUS1:
	PRTS("MHz$")			; SUFFIX
#ENDIF
;
; DISPLAY CPU CONFIG
;
	CALL	NEWLINE

#IF (CPUFAM == CPU_Z280)
	LD	A,Z280_MEMLOWAIT
	CALL	PRTDECB
	LD	A,'/'
	CALL	COUT
	LD	A,Z280_MEMHIWAIT
	CALL	PRTDECB
	CALL	PRTSTRD
	.TEXT	" MEM W/S, $"
#ELSE
	XOR	A
  #IF (CPUFAM == CPU_Z180)
	LD	A,Z180_MEMWAIT
  #ENDIF
	CALL	PRTDECB
	CALL	PRTSTRD
	.TEXT	" MEM W/S, $"
#ENDIF
	LD	A,1
#IF (CPUFAM == CPU_Z180)
	LD	A,Z180_IOWAIT + 1
#ENDIF
#IF (CPUFAM == CPU_Z280)
	LD	A,Z280_IOWAIT + 1
#ENDIF
	CALL	PRTDECB
	CALL	PRTSTRD
	.TEXT	" I/O W/S$"
#IF (CPUFAM == CPU_Z280)
	CALL	PRTSTRD
	.TEXT	", $"
	LD	A,Z280_INTWAIT
	CALL	PRTDECB
	CALL	PRTSTRD
	.TEXT	" INT W/S$"
#ENDIF
#IF (INTMODE > 0)
	CALL	PRTSTRD
	.TEXT	", INT MODE $"
	LD	A,INTMODE
	CALL	PRTDECB
#ENDIF
;
	CALL	PRTSTRD
	.TEXT	", $"
	CALL	PRTSTRD
#IF (MEMMGR == MM_NONE)
	.TEXT	"NONE$"
#ENDIF
#IF (MEMMGR == MM_SBC)
	.TEXT	"SBC$"
#ENDIF
#IF (MEMMGR == MM_Z2)
	.TEXT	"Z2$"
#ENDIF
#IF (MEMMGR == MM_N8)
	.TEXT	"N8$"
#ENDIF
#IF (MEMMGR == MM_Z180)
	.TEXT	"Z180$"
#ENDIF
#IF (MEMMGR == MM_Z280)
	.TEXT	"Z280$"
#ENDIF
#IF (MEMMGR == MM_ZRC)
	.TEXT	"ZRC$"
#ENDIF
#IF (MEMMGR == MM_MBC)
	.TEXT	"MBC$"
#ENDIF
	CALL	PRTSTRD
	.TEXT	" MMU$"
;
; DISPLAY MEMORY CONFIG
;
	CALL	NEWLINE
	LD	HL,ROMSIZE
	CALL	PRTDEC
	CALL	PRTSTRD
	.TEXT	"KB ROM, $"
	LD	HL,RAMSIZE
	CALL	PRTDEC
	CALL	PRTSTRD
	.TEXT	"KB RAM$"
;
#IF (CPUFAM == CPU_Z280)
	CALL	NEWLINE
	PRTS("Z280: $")
	PRTS("MSR=$")
	LD	C,Z280_MSR		; MASTER STATUS REGISTER
	LDCTL	HL,(C)
	CALL	PRTHEXWORDHL
	CALL	PC_SPACE
	PRTS("BTCR=$")
	LD	C,Z280_BTCR		; BUS TIMING AND CONTROL REGISTER
	LDCTL	HL,(C)
	CALL	PRTHEXWORDHL
	CALL	PC_SPACE
	PRTS("BTIR=$")
	LD	C,Z280_BTIR		; BUS TIMING AND CONTROL REGISTER
	LDCTL	HL,(C)
	CALL	PRTHEXWORDHL
	CALL	PC_SPACE
	PRTS("CCR=$")
	LD	C,Z280_CCR		; CACHE CONTROL REGISTER
	LDCTL	HL,(C)
	CALL	PRTHEXWORDHL
#ENDIF
;
#IFDEF ROMBOOT
;
; ROM CHECKSUM VERIFICATION
; THE FIRST ROM BANK HAS A CHECKSUM INJECTED SUCH THAT
; A COMPUTED CHECKSUM ACROSS THE ENTIRE BANK SHOLD ALWAYS BE ZERO
;
HB_ROMCK:
	CALL	NEWLINE
	PRTS("ROM VERIFY:$")
;
	; TEST FIRST 4 BANKS OF ROM
	LD	B,1			; 1 BANK
	LD	C,0			; STARTING AT BANK 0
HB_ROMCK1:
	PUSH	BC			; SAVE LOOP CONTROL
	CALL	HB_CKBNK		; TEST THE BANK
	CALL	PC_SPACE		; FORMATTING
	CALL	PRTHEXBYTE		; PRINT RESULT
	POP	BC			; RESTORE LOOP CONTROL
	OR	A			; SET FLAGS
	JR	NZ,HB_ROMCK2		; HANDLE FAILURE
	INC	C			; NEXT BANK
	DJNZ	HB_ROMCK1		; LOOP FOR BANKS
	PRTS(" PASS$")			; DISPLAY SUCCESS
	JR	HB_ROMCKZ		; CONTINUE BOOT
HB_ROMCK2:
	PRTS(" FAIL$")			; DISPLAY ERROR
	JR	HB_ROMCKZ		; CONTINUE BOOT
;
; VERIFY ROM CHECKSUM BANK SPECIFIED IN REG C
; THIS MUST BE COPIED TO UPPER RAM TO RUN
; INTERRUPTS ARE DISABLED SINCE PAGE ZERO VECTOR WILL BE
; SWAPPED OUT.  ASSUMES THAT INTERRUPTS ARE ENABLED AT ENTRY.
;
HB_CKBNK:
	HB_DI				; SUPPRESS INTERRUPTS
	LD	HL,$7FFF		; START AT BANK END
	LD	BC,1			; DECREMENT VALUE
	XOR	A			; ZERO ACCUM
HB_CKBNK1:
	ADD	A,(HL)			; ADD NEXT BYTE
	OR	A			; CLEAR CARRY
	SBC	HL,BC			; DECREMENT
	JR	NC,HB_CKBNK1		; LOOP TILL DONE
	HB_EI				; ALLOW INTERRUPTS AGAIN
	RET				; AND DONE
;
HB_CKBNKSIZ	.EQU	$-HB_CKBNK	; SIZE OF ROUTINE
;
HB_ROMCKZ:
;
#ENDIF
;
; LOW BATTERY DIAGNOSTIC MESSAGE
;
#IF (BATCOND)
	LD	A,(HB_BATCOND)
	OR	A
	LD	DE,STR_LOWBAT
	CALL	Z,WRITESTR
#ENDIF
;
	LD	DE,STR_SYSHALT
	CALL	WRITESTR
	JR	$
	DI
	HALT
;
;==================================================================================================
;   INTERRUPT VECTOR TABLE (MUST START AT PAGE BOUNDARY!!!)
;==================================================================================================
;
	.FILL	$100 - ($ & $FF)
;
;
; IM1 INTERRUPTS ARRIVE HERE AFTER BANK SWITCH TO HBIOS BANK
; LIST OF IM1 INT CALLS IS BUILT DYNAMICALLY BELOW
; SEE HB_ADDIM1 ROUTINE
; EACH ENTRY WILL LOOK LIKE:
;	CALL	XXXX			; CALL INT HANDLER
;	RET	NZ			; RETURN IF HANDLED
;
; NOTE THAT THE LIST IS INITIALLY FILLED WITH CALLS TO HB_BADINT.
; AS THE TABLE IS POPULATED, THE ADDRESS OF HB_BADINT IS OVERLAID
; WITH THE ADDRESS OF A REAL INTERRUPT HANDLER.
;
; THERE IS ROOM FOR 8 ENTRIES PLUS A FINAL CALL TO HB_BADINT.
;
#IF (INTMODE < 2)
;
HB_IVT:
	CALL HB_BADINT \ RET NZ
	CALL HB_BADINT \ RET NZ
	CALL HB_BADINT \ RET NZ
	CALL HB_BADINT \ RET NZ
	CALL HB_BADINT \ RET NZ
	CALL HB_BADINT \ RET NZ
	CALL HB_BADINT \ RET NZ
	CALL HB_BADINT \ RET NZ
	CALL HB_BADINT \ RET NZ
;
#ENDIF
;
; IM2 INTERRUPTS ARRIVE HERE AFTER BANK SWITCH TO HBIOS BANK
; THE LIST OF JP TABLE ENTRIES MATCHES THE IM2 VECTORS ONE FOR
; ONE.  ANY CALL TO THE PRIMARY IVT (HBX_IVT) WILL BE MAPPED TO
; THE CORRESPONDING JP TABLE ENTRY BELOW AFTER THE BANK SWITCH.
;
; NOTE THAT THE LIST IS INITIALLY FILLED WITH CALLS TO HB_BADINT.
; IT IS INTENDED THAT HARDWARE DRIVERS WILL DYNAMICALLY OVERLAY
; THE ADDRESS PORTION OF THE APPROPRIATE JP TO POINT TO THE
; DESIRED INTERRUPT HANDLER DURING THE DRIVERS INITIALIZATION.
;
; NOTE THAT EACH ENTRY HAS A FILLER BYTE OF VALUE ZERO.  THIS BYTE
; HAS NO FUNCTION.  IT IS JUST USED TO MAKE ENTRIES AN EVEN 4 BYTES.
;
#IF ((INTMODE == 2) | (INTMODE == 3))
;
HB_IVT:
HB_IVT00:	JP	HB_BADINT \ .DB 0
HB_IVT01:	JP	HB_BADINT \ .DB 0
HB_IVT02:	JP	HB_BADINT \ .DB 0
HB_IVT03:	JP	HB_BADINT \ .DB 0
HB_IVT04:	JP	HB_BADINT \ .DB 0
HB_IVT05:	JP	HB_BADINT \ .DB 0
HB_IVT06:	JP	HB_BADINT \ .DB 0
HB_IVT07:	JP	HB_BADINT \ .DB 0
HB_IVT08:	JP	HB_BADINT \ .DB 0
HB_IVT09:	JP	HB_BADINT \ .DB 0
HB_IVT0A:	JP	HB_BADINT \ .DB 0
HB_IVT0B:	JP	HB_BADINT \ .DB 0
HB_IVT0C:	JP	HB_BADINT \ .DB 0
HB_IVT0D:	JP	HB_BADINT \ .DB 0
HB_IVT0E:	JP	HB_BADINT \ .DB 0
HB_IVT0F:	JP	HB_BADINT \ .DB 0
;
#ENDIF
;
#IF (INTMODE == 1)
;
; ROUTINE BELOW IS USED TO ADD A NEW VECTOR TO THE IM1
; CALL LIST ABOVE.  ENTER WITH HL=VECTOR ADDRESS IN HBIOS
;
HB_ADDIM1:
	EX	DE,HL			; VECTOR ADDRESS TO DE
	LD	HL,(HB_IM1PTR)		; GET PTR FOR NEXT ENTRY
	INC	HL			; BUMP PTR TO ADDRESS FIELD OF CALL OPCODE
	LD	(HL),E			; ADD VECTOR ADDRESS
	INC	HL			; ...
	LD	(HL),D			; ...
	INC	HL			; BUMP PTR
	INC	HL			; BUMP PTR
	LD	(HB_IM1PTR),HL		; SAVE UPDATED POINTER
	LD	HL,HB_IM1CNT		; POINT TO ENTRY COUNT
	INC	(HL)			; INCREMENT
	RET				; DONE
;
HB_IM1CNT	.DB	0		; NUMBER OF ENTRIES IN CALL LIST
HB_IM1MAX	.DB	8		; MAX ENTRIES IN CALL LIST
HB_IM1PTR	.DW	HB_IVT		; POINTER FOR NEXT IM1 ENTRY
;
#ENDIF
;
; BAD INTERRUPT HANDLER
;
HB_BADINT:

#IF FALSE	; *DEBUG*
	LD	HL,HB_BADINTCNT
	INC	(HL)
	LD	A,(HL)
	OUT	(DIAGPORT),A
	OR	$FF
	RET
HB_BADINTCNT	.DB	0
#ENDIF	; *DEBUG*

	CALL	NEWLINE2
	PRTS("+++ BAD INT $")
	LD	A,L
	RRCA
	RRCA
	CALL PRTHEXBYTE
	PRTS("H: $")

	CALL	XREGDMP
	;CALL	CONTINUE
	OR	$FF			; SIGNAL INTERRUPT HANDLED
	RET
;
; Z280 BAD INT HANDLER
;
#IF (MEMMGR == MM_Z280)
;
Z280_BADINT:
	; SAVE REASON CODE FOR POSSIBLE RETURN VIA RETIL
	EX	(SP),HL			; GET MSR, SAVE HL
	LD	(HB_RCSAV),HL		; SAVE IT
	POP	HL			; RECOVER HL, POP STACK
	; SAVE MSR FOR POSSIBLE RETURN VIA RETIL
	EX	(SP),HL			; GET MSR, SAVE HL
	LD	(HB_MSRSAV),HL		; SAVE IT
	POP	HL			; RECOVER HL, POP STACK
;
	PUSH	DE
	LD	DE,Z280_BADINTSTR
	CALL	NEWLINE2
	PRTS("+++ $")
	CALL	WRITESTR
	POP	DE
	CALL	XREGDMP
;
	; RECOVER MSR, THEN RETURN VIA RETIL
	PUSH	HL			; SAVE HL
	LD	HL,(HB_RCSAV)		; GET SAVED REASON CODE
	PRTS(" RC=$")
	CALL	PRTHEXWORDHL		; DUMP MSR
	LD	HL,(HB_MSRSAV)		; GET SAVED MSR
	PRTS(" MSR=$")
	CALL	PRTHEXWORDHL		; DUMP MSR
	EX	(SP),HL			; MSR TO STK, RECOVER HL
;
	.DB	$ED,$55			; RETIL
;
Z280_SSTEP:
	; SAVE HL AND MSR FOR POSSIBLE RETURN VIA RETIL
	EX	(SP),HL			; GET MSR, SAVE HL
	LD	(HB_MSRSAV),HL		; SAVE IT
	POP	HL			; RECOVER HL, POP STACK
;
	PUSH	DE
	LD	DE,Z280_SSTEPSTR
	JP	Z280_DIAG
;
Z280_BRKHLT:
	; SAVE HL AND MSR FOR POSSIBLE RETURN VIA RETIL
	EX	(SP),HL			; GET MSR, SAVE HL
	LD	(HB_MSRSAV),HL		; SAVE IT
	POP	HL			; RECOVER HL, POP STACK
;
	PUSH	DE
	LD	DE,Z280_BRKHLTSTR
	JP	Z280_DIAG
;
Z280_DIVEXC:
	; SAVE HL AND MSR FOR POSSIBLE RETURN VIA RETIL
	EX	(SP),HL			; GET MSR, SAVE HL
	LD	(HB_MSRSAV),HL		; SAVE IT
	POP	HL			; RECOVER HL, POP STACK
;
	PUSH	DE
	LD	DE,Z280_DIVEXCSTR
	JP	Z280_DIAG
;
Z280_STKOVR:
	; SAVE HL AND MSR FOR POSSIBLE RETURN VIA RETIL
	EX	(SP),HL			; GET MSR, SAVE HL
	LD	(HB_MSRSAV),HL		; SAVE IT
	POP	HL			; RECOVER HL, POP STACK
;
	PUSH	DE
	LD	DE,Z280_STKOVRSTR
	JP	Z280_DIAG
;
Z280_ACCVIO:
	; SAVE HL AND MSR FOR POSSIBLE RETURN VIA RETIL
	EX	(SP),HL			; GET MSR, SAVE HL
	LD	(HB_MSRSAV),HL		; SAVE IT
	POP	HL			; RECOVER HL, POP STACK
;
	PUSH	DE
	LD	DE,Z280_ACCVIOSTR
	JP	Z280_DIAG
;
Z280_DIAG:
	CALL	NEWLINE2
	PRTS("+++ $")
	CALL	WRITESTR
	POP	DE
	CALL	XREGDMP
;
	; RECOVER MSR, THEN RETURN VIA RETIL
	PUSH	HL			; SAVE HL
	LD	HL,(HB_MSRSAV)		; GET SAVED MSR
	PRTS(" MSR=$")
	CALL	PRTHEXWORDHL		; DUMP MSR
	EX	(SP),HL			; MSR TO STK, RECOVER HL
;
	;RETIL
	DI
	HALT
;
Z280_BADINTSTR	.TEXT	"BAD INT $"
Z280_SSTEPSTR	.TEXT	"SINGLE STEP $"
Z280_BRKHLTSTR	.TEXT	"BREAK HALT $"
Z280_DIVEXCSTR	.TEXT	"DIVISION EXCEPTION $"
Z280_STKOVRSTR	.TEXT	"STACK OVERFLOW $"
Z280_ACCVIOSTR	.TEXT	"ACCESS VIOLATION $"
;
#ENDIF
;
; Z280 PRIVILEGED INSTRUCTION HANDLER
;
#IF (MEMMGR == MM_Z280)
;
Z280_PRIVINST:
	; SAVE HL AND MSR FOR POSSIBLE RETURN VIA RETIL
	EX	(SP),HL			; GET MSR, SAVE HL
	LD	(HB_MSRSAV),HL		; SAVE IT
	POP	HL			; RECOVER HL, POP STACK
	EX	(SP),HL			; GET ADR, SAVE HL
;
	PUSH	AF
	PUSH	BC
	PUSH	DE
;
	.DB	$ED,$96			; LDUP	A,(HL)
;
	; HANDLE DI
	CP	$F3			; DI?
	JR	NZ,Z280_PRIVINST2
	HB_DI				; DO THE DI
	INC	HL			; BUMP PAST IT
	JR	Z280_PRIVINSTX
;
Z280_PRIVINST2:
	; HANDLE EI
	CP	$FB			; EI?
	JR	NZ,Z280_PRIVINST3
	HB_EI				; DO THE EI
	INC	HL			; BUMP PAST IT
	JR	Z280_PRIVINSTX
;
Z280_PRIVINST3:
	; SOMETHING ELSE, DIAGNOSE & HALT SYSTEM
	LD	DE,Z280_PRIVSTR
	CALL	WRITESTR
	CALL	PRTHEXWORDHL
;
	; DUMP 16 BYTES OF USER ADDRESS SPACE
	CALL	PC_SPACE
	CALL	PC_LBKT
	LD	B,$10
Z280_PRIVINST4:
	.DB	$ED,$96			; LDUP	A,(HL)
	CALL	PRTHEXBYTE
	INC	HL
	DJNZ	Z280_PRIVINST4
	CALL	PC_RBKT
;
	; GO NO FURTHER
	DI
	HALT
;
Z280_PRIVINSTX:
	; RESTORE REGISTERS
	POP	DE
	POP	BC
	POP	AF
;
	; RECOVER HL AND MSR, THEN RETURN VIA RETIL
	EX	(SP),HL			; RECOVER HL, ADR TO STK
	PUSH	HL			; SAVE HL
	LD	HL,(HB_MSRSAV)		; GET SAVED MSR
	EX	(SP),HL			; MSR TO STK, RECOVER HL
	.DB	$ED,$55			; RETIL
;
HB_MSRSAV	.DW	0		; SAVED MSR
HB_RCSAV	.DW	0		; SAVED REASON CODE
;
Z280_PRIVSTR	.TEXT	"\r\n\r\n*** Privileged Instruction @$"
;
#ENDIF
;
;==================================================================================================
;   Z280 INTERRUPT VECTOR TABLE
;==================================================================================================
;
#IF (MEMMGR == MM_Z280)
;
	; THE Z280 IVT MUST BE ON A 4K BOUNDARY.  IT HAS BEEN LOCATED
	; HERE IN AN EFFORT TO MINIMIZE WASTED SPACE.  THERE SHOULD BE
	; A LITTLE LESS THAN 4K OF CODE ABOVE.
;
	.FILL	$1000 - ($ & $FFF)	; MUST BE 4K ALIGNED!
;
Z280_IVT:
	.DW	0, 0			; RESERVED
	.DW	0			; NMI MSR
	.DW	0			; NMI VECTOR
	.DW	$0000			; INT A MSR
	.DW	Z280_BADINT		; INT A VECTOR
	.DW	$0000			; INT B MSR
	.DW	Z280_BADINT		; INT B VECTOR
	.DW	$0000			; INT C MSR
	.DW	Z280_BADINT		; INT C VECTOR
	.DW	$0000			; COUNTER/TIMER 0 MSR
	.DW	Z280_BADINT		; COUNTER/TIMER 0 VECTOR
	.DW	$0000			; COUNTER/TIMER 1 MSR
	.DW	Z280_BADINT		; COUNTER/TIMER 1 VECTOR
	.DW	0, 0			; RESERVED
	.DW	$0000			; COUNTER/TIMER 2 MSR
	.DW	Z280_BADINT		; COUNTER/TIMER 2 VECTOR
	.DW	$0000			; DMA CHANNEL 0 MSR
	.DW	Z280_BADINT		; DMA CHANNEL 0 VECTOR
	.DW	$0000			; DMA CHANNEL 1 MSR
	.DW	Z280_BADINT		; DMA CHANNEL 1 VECTOR
	.DW	$0000			; DMA CHANNEL 2 MSR
	.DW	Z280_BADINT		; DMA CHANNEL 2 VECTOR
	.DW	$0000			; DMA CHANNEL 3 MSR
	.DW	Z280_BADINT		; DMA CHANNEL 3 VECTOR
	.DW	$0000			; UART RECEIVER MSR
	.DW	Z280_BADINT		; UART RECEIVER VECTOR
	.DW	$0000			; UART TRANSMITTER MSR
	.DW	Z280_BADINT		; UART TRANSMITTER VECTOR
	.DW	$0000			; SINGLE STEP TRAP MSR
	.DW	Z280_SSTEP		; SINGLE STEP TRAP VECTOR
	.DW	$0000			; BREAK ON HALT TRAP MSR
	.DW	Z280_BRKHLT		; BREAK ON HALT TRAP VECTOR
	.DW	$0000			; DIVISION EXCEPTION TRAP MSR
	.DW	Z280_DIVEXC		; DIVISION EXCEPTION TRAP VECTOR
	.DW	$0000			; STACK OVERFLOW WARNING TRAP MSR
	.DW	Z280_STKOVR		; STACK OVERFLOW WARNING TRAP VECTOR
	.DW	$0000			; ACCESS VIOLATION TRAP MSR
	.DW	Z280_ACCVIO		; ACCESS VIOLATION TRAP VECTOR
	.DW	$0000			; SYSTEM CALL TRAP MSR
	.DW	Z280_SYSCALL		; SYSTEM CALL TRAP VECTOR
	.DW	$0000			; PRIVILEGED INSTRUCTION TRAP MSR
	.DW	Z280_PRIVINST		; PRIVILEGED INSTRUCTION TRAP VECTOR
	.DW	$0000			; EPU <- MEMORY EXTENDED INSTRUCTION TRAP MSR
	.DW	$0000			; EPU <- MEMORY EXTENDED INSTRUCTION TRAP VECTOR
	.DW	$0000			; MEMORY <- EPU EXTENDED INSTRUCTION TRAP MSR
	.DW	$0000			; MEMORY <- EPU EXTENDED INSTRUCTION TRAP VECTOR
	.DW	$0000			; A <- EPU EXTENDED INSTRUCTION TRAP MSR
	.DW	$0000			; A <- EPU EXTENDED INSTRUCTION TRAP VECTOR
	.DW	$0000			; EPU INTERNAL OPERATION EXTENDED INSTRUCTION TRAP MSR
	.DW	$0000			; EPU INTERNAL OPERATION EXTENDED INSTRUCTION TRAP VECTOR
	.DW	0, 0			; RESERVED
	.DW	0, 0			; RESERVED
	; PROGRAM COUNTER VALUES FOR NMI/INTA (16)
	.DW	HBX_IV00
	.DW	HBX_IV01
	.DW	HBX_IV02
	.DW	HBX_IV03
	.DW	HBX_IV04
	.DW	HBX_IV05
	.DW	HBX_IV06
	.DW	HBX_IV07
	.DW	HBX_IV08
	.DW	HBX_IV09
	.DW	HBX_IV0A
	.DW	HBX_IV0B
	.DW	HBX_IV0C
	.DW	HBX_IV0D
	.DW	HBX_IV0E
	.DW	HBX_IV0F
	; THE REMAINDER OF THE Z280 IVT IS TRUNCATED HERE BECAUSE IT
	; TAKES A BUNCH OF SPACE AND IS NOT USED.  WE SUPPORT ONLY
	; 16 VECTORED INTERRUPTS AND THEY MUST BE CONNECTED TO INTA.
;
#ENDIF
;
; Z280 BANK SELECTION (CALLED FROM PROXY)
;
#IF (MEMMGR == MM_Z280)
;
; REG A HAS BANK ID, REG B HAS INITIAL PDR TO PROGRAM
; REGISTERS AF, BC, HL DESTROYED
;
; THIS ROUTINE MAY BE RELOCATED TO RUN IN HIGH MEMORY IN CERTAIN CASES
; LIKE A SYSTEM RESTART.  IT MUST BE KEPT ENTIRELY RELOCATABLE.
;
Z280_BNKSEL:
	;; *DEBUG*
	;CALL	PC_LBKT
	;CALL	PRTHEXBYTE
	;CALL	PC_RBKT

	; SELECT I/O PAGE $FE (SAVING PREVIOUS VALUE)
	LD	C,Z280_IOPR		; REG C POINTS TO I/O PAGE REGISTER
	LDCTL	HL,(C)			; GET CURRENT I/O PAGE
	PUSH	HL			; SAVE IT
	LD	L,$FF			; NEW I/O PAGE
	LDCTL	(C),HL
;
	; CONVERT BANK ID TO TOP 12 BITS OF PHYSICAL ADDRESS
	; WITH $0A IN THE LOW ORDER NIBBLE:
	; BANK ID: R000 BBBB
	; PDR: R000 0BBB B000 1010 (RCBUS)
	; PDR: 0000 RBBB B000 1010 (ZZ80MB)
;
	MULTU	A,$80			; HL=0R00 0BBB B000 0000
	BIT	6,H			; RAM BIT SET?
	JR	Z,Z280_BNKSEL2		; IF NOT, ALL DONE
	RES	6,H			; OTHERWISE, MOVE RAM BIT
	SET	RAMLOC-16,H		; HL=0000 RBBB B000 0000
;
Z280_BNKSEL2:
;
	; SET LOW NIBBLE
	LD	A,$0A			; VALUE FOR LOW NIBBLE
	.DB	$ED,$6D			; ADD HL,A ; HL=0000 RBBB B000 1010
;
	; POINT TO FIRST PDR TO PROGRAM
	LD	A,B			; INITIAL PDR TO PROG
	OUT	(Z280_MMUPDRPTR),A	; SET THE PDR POINTER
;
	; PROGRAM 8 PDRS
	LD	C,Z280_MMUBLKMOV	; PDR BLOCK MOVE PORT
	;LD	B,8			; PROGRAM 8 PDRS
	LD	A,$10			; PDR VALUE INCREMENT
Z280_BNKSEL3:
	; PROGRAM 8 PDR VALUES
	; LOOP UNROLLED FOR SPEED
	OUTW	(C),HL			; WRITE VALUE
	ADD	HL,A			; BUMP VALUE
	OUTW	(C),HL			; WRITE VALUE
	ADD	HL,A			; BUMP VALUE
	OUTW	(C),HL			; WRITE VALUE
	ADD	HL,A			; BUMP VALUE
	OUTW	(C),HL			; WRITE VALUE
	ADD	HL,A			; BUMP VALUE
	OUTW	(C),HL			; WRITE VALUE
	ADD	HL,A			; BUMP VALUE
	OUTW	(C),HL			; WRITE VALUE
	ADD	HL,A			; BUMP VALUE
	OUTW	(C),HL			; WRITE VALUE
	ADD	HL,A			; BUMP VALUE
	OUTW	(C),HL			; WRITE VALUE
	ADD	HL,A			; BUMP VALUE
	;DJNZ	Z280_BNKSEL3		; DO ALL PDRS
;
	; RESTORE I/O PAGE
	LD	C,Z280_IOPR		; REG C POINTS TO I/O PAGE REGISTER
	POP	HL			; RECOVER ORIGINAL I/O PAGE
	LDCTL	(C),HL
;
	RET
;
Z280_BNKSEL_LEN	.EQU	$ - Z280_BNKSEL
;
#ENDIF
;
; Z280 BANK COPY (CALLED FROM PROXY)
;
#IF (MEMMGR == MM_Z280)
;
Z280_BNKCPY:
	; Z280 MEMORY TO MEMORY DMA
	; USE FLOW THROUGH MODE
	; SINGLE BYTE TRANSFER
	; TRANSACTION DESCRIPTION REGISTER (TDR)
	; %0000 0000 0000 0000
	; - AUTO INCREMENT MEMORY
	; - FLOWTHROUGH OPERATION
	; - SINGLE TRANSACTION (CAN WE USE CONTINUOUS???)
	; - 1 BYTE XFER SIZE
;
	; SAVE INCOMING REGISTERS
	PUSH	HL
	PUSH	DE
	PUSH	BC
;
	PUSH	BC			; SAVE COUNT
	PUSH	HL			; SAVE SOURCE ADDRESS
;
	; SELECT I/O PAGE $FF
	LD	C,Z280_IOPR		; I/O PAGE REGISTER
	LDCTL	HL,(C)			; GET CURRENT I/O PAGE
	LD	(IOPRVAL),HL		; SAVE IT
	LD	L,$FF			; I/O PAGE $FF
	LDCTL	(C),HL
;
	LD	C,Z280_DMA0_DSTL	; START WITH DEST REG LO
;
	LD	A,(HB_DSTBNK)		; DEST BANK TO ACCUM
	CALL	Z2DMAADR		; SETUP DEST ADR REGS
;
	POP	DE			; SRC ADR TO DE
	LD	A,(HB_SRCBNK)		; DEST BANK TO ACCUM
	CALL	Z2DMAADR		; SETUP SOURCE ADR REGS
;
	POP	HL			; COUNT TO HL
	OUTW	(C),HL
	INC	C			; BUMP TO TDR
;
	LD	HL,$8000		; ENABLE DMA0 TO RUN!
	OUTW	(C),HL
;
	; WAIT FOR XFER TO COMPLETE
Z2DMALOOP:
	.DB	$ED,$B7			; INW HL,(C)
	BIT	7,H			; CHECK EN BIT OF TDR
	JR	NZ,Z2DMALOOP		; LOOP WHILE ACTIVE
;
	; RESTORE I/O PAGE
	LD	C,Z280_IOPR		; I/O PAGE REGISTER
	LD	HL,(IOPRVAL)		; RESTORE I/O PAGE
	LDCTL	(C),HL
;
	; SETUP RETURN VALUES
	POP	BC			; RECOVER ORIGINAL BC
	POP	DE			; RECOVER ORIGINAL DE
	POP	HL			; RECOVER ORIGINAL HL
	ADD	HL,BC			; INCREMENT SRC ADR BY COUNT
	EX	DE,HL			; SWAP
	ADD	HL,BC			; INCREMENT DST ADR BY COUNT
	EX	DE,HL			; SWAP BACK
	LD	BC,0			; COUNT IS NOW ZERO
;
	RET
;
Z2DMAADR:
	; SET ADDRESS REGISTERS, BANK IN A, ADDRESS IN DE
	; C POINTS TO FIRST DMA ADR PORT TO SET
	; A=R000 BBBB, DE=0AAA AAAA AAAA AAAA
	; RC: DMA HI=0000 RBBB BAAA 1111 LO=1111 AAAA AAAA AAAA
	; ZZ: DMA HI=R000 0BBB BAAA 1111 LO=1111 AAAA AAAA AAAA
	BIT	7,D			; HIGH RAM?
	JR	Z,Z2DMAADR1		; NO, SKIP
	LD	A,$8F			; SUBSTITUTE COMMON RAM BANK ID
;
Z2DMAADR1:
	; ADR HI FROM A:DE
	LD	L,D			; L=?AAA AAAA
	LD	H,A			; H=R000 BBBB
	SLA	L			; L=AAAA AAA0 ?
	SRL	H			; H=0R00 0BBB B
	RR	L			; L=BAAA AAAA 0
	LD	A,$0F			; A=0000 1111
	OR	L			; A=BAAA 1111
	LD	L,A			; L=BAAA 1111
;
	; MOVE THE RAM/ROM BIT.
	; RCBUS DMA HI=0000 RBBB BAAA 1111 LO=1111 AAAA AAAA AAAA
	; ZZ80MB DMA HI=R000 0BBB BAAA 1111 LO=1111 AAAA AAAA AAAA
	BIT	6,H
	JR	Z,Z2DMAADR2
	RES	6,H
	SET	RAMLOC-16,H
;
Z2DMAADR2:
	PUSH	HL			; SAVE IT FOR NOW

	; ADR LO FROM DE:
	LD	L,E			; L=AAAA AAAA
	LD	A,$F0			; A=1111 0000
	OR	D			; A=1111 AAAA
	LD	H,A			; HL=1111 AAAA AAAA AAAA
;
	; SET ADR LO REG
	OUTW	(C),HL
	INC	C			; BUMP TO ADR HI REG
;
	; SET ADR HI REG
	POP	HL			; RECOVER THE HI VAL
	OUTW	(C),HL
	INC	C			; BUMP TO NEXT REG
;
	RET
#ENDIF
;
; Z280 SYSCALL VECTOR ENTRY POINT.  TAKES STACK PARAMETER AS A BRANCH
; ADDRESS AND CALLS IT.  ALLOWS ANY USER MODE CODE TO CALL INTO AN
; ARBITRARY LOCATION OF SYSTEM MODE CODE.
;
#IF (MEMMGR == MM_Z280)
Z280_SYSCALL:
	EX	(SP),HL
	LD	(Z280_SYSCALL_GO+1),HL
	POP	HL
Z280_SYSCALL_GO:
	CALL	$FFFF			; PARM SET ABOVE
	.DB	$ED,$55			; RETIL
#ENDIF
;
#DEFINE USEDELAY
#INCLUDE "util.asm"
;
#IF (DSKYENABLE)
#DEFINE	DSKY_KBD
  #IF (DSKYMODE == DSKYMODE_V1)
#INCLUDE "dsky.asm"
  #ENDIF
  #IF (DSKYMODE == DSKYMODE_NG)
#INCLUDE "dskyng.asm"
  #ENDIF
#ENDIF
;
; PANIC: DUMP MACHINE STATE AND HALT
;
PANIC:
	PUSH	DE
	LD	DE,STR_PANIC
	CALL	WRITESTR
	POP	DE
	CALL	XREGDMP			; DUMP REGISTERS
	JR	SYSHALT			; FULL STOP
;
;
;
CONTINUE:
	PUSH	AF
CONTINUE1:
	PUSH	DE
	LD	DE,STR_CONTINUE
	CALL	WRITESTR
	POP	DE
	CALL	CIN
	RES	5,A			; FORCE UPPERCASE (IMPERFECTLY)
	CALL	COUT			; ECHO
	CP	'Y'
	JR	Z,CONTINUE3
	CP	'N'
	JR	Z,SYSHALT
	JR	CONTINUE1
CONTINUE3:
	CALL	NEWLINE
	POP	AF
	RET
;
;
;
SYSHALT:
	LD	DE,STR_HALT
	CALL	WRITESTR
	DI
	HALT
;
; PRINT VALUE OF HL AS THOUSANDTHS, IE. 0.000
;
PRTD3M:
	PUSH	BC
	PUSH	DE
	PUSH	HL
	LD	E,'0'
	LD	BC,-10000
	CALL	PRTD3M1
	LD	E,0
	LD	BC,-1000
	CALL	PRTD3M1
	CALL	PC_PERIOD
	LD	BC,-100
	CALL	PRTD3M1
	LD	C,-10
	CALL	PRTD3M1
	LD	C,-1
	CALL	PRTD3M1
	POP	HL
	POP	DE
	POP	BC
	RET
PRTD3M1:
	LD	A,'0' - 1
PRTD3M2:
	INC	A
	ADD	HL,BC
	JR	C,PRTD3M2
	SBC	HL,BC
	CP	E
	JR	Z,PRTD3M3
	LD	E,0
	CALL	COUT
PRTD3M3:
	RET
;
HB_CPU_STR:	.TEXT	" Z80$"
		.TEXT	" Z80180$"
		.TEXT	" Z8S180-K$"
		.TEXT	" Z8S180-N$"
		.TEXT	" Z80280$"
;
;==================================================================================================
; CONSOLE CHARACTER I/O HELPER ROUTINES (REGISTERS PRESERVED)
;==================================================================================================
;
#IF (MINIO == MINIO_UART)
  #INCLUDE "min_uart.asm"
#ENDIF
;
#IF (MINIO == MINIO_ASCI)
  #INCLUDE "min_asci.asm"
#ENDIF
;
#IF (MINIO == MINIO_ACIA)
  #INCLUDE "min_acia.asm"
#ENDIF
;
#IF (MINIO == MINIO_SIO)
  #INCLUDE "min_sio.asm"
#ENDIF
;
#IF (MINIO == MINIO_Z2U)
  #INCLUDE "min_z2u.asm"
#ENDIF
;
;==================================================================================================
;   MISCELLANEOUS UTILITY FUNCTIONS
;==================================================================================================
;
; SET HL TO IY+A, A IS TRASHED
;
LDHLIYA:
	PUSH	IY			; COPY INSTANCE DATA PTR
	POP	HL			; ... TO HL
	;JP	ADDHLA			; APPLY OFFSET TO HL AND RETURN
	ADD	A,L			; ADD OFFSET TO LSB
	LD	L,A			; ... PUT BACK IN L
	RET	NC			; DONE IF CF NOT SET
	INC	H			; IF CF SET, BUMP MSB
	RET				; ... AND RETURN
;
;==================================================================================================
;   HBIOS GLOBAL DATA
;==================================================================================================
;
HB_CPUTYPE	.DB	0		; 0=Z80, 1=80180, 2=SL1960, 3=ASCI BRG
;
CB_CPUMHZ	.DB	CPUMHZ
CB_CPUKHZ	.DW	CPUKHZ
;
IOPRVAL		.DW	0		; TEMP STORAGE FOR IOPR
;
HB_BATCOND	.DB	0		; BATTERY CONDITION (0=LOW, 1=OK)
;
STR_BANNER	.DB	"RomWBW HDIAG v", BIOSVER, ", ", TIMESTAMP, "$"
STR_PLATFORM	.DB	PLATFORM_NAME, "$"
STR_SWITCH	.DB	"*** Activating CRT Console ***$"
STR_BADINT	.DB	"\r\n*** BAD INT ***\r\n$"
STR_LOWBAT	.DB	"\r\n\r\n+++ LOW BATTERY +++$"
;
STR_PANIC	.TEXT	"\r\n>>> PANIC: $"
STR_SYSCHK	.TEXT	"\r\n>>> SYSCHK: $"
STR_CONTINUE	.TEXT	"\r\nContinue (Y/N)? $"
STR_SYSHALT	.TEXT	"\r\n\r\n*** SYSTEM HALTED ***$"
;
#IF (DSKYENABLE)	;	'H','B','I','O',' ',' ',' ',' '
  #IF (DSKYMODE == DSKYMODE_V1)
MSG_HBVER		.DB	$3E,$7F,$0A,$7B,$00,$00,$00,$00	; "HBIO    "
  #ENDIF
  #IF (DSKYMODE == DSKYMODE_NG)
MSG_HBVER		.DB	$76,$7F,$30,$3F,$00,$00,$00,$00	; "HBIO    "
  #ENDIF
#ENDIF
;
HB_APPBNK	.DB	0		; START BANK WHEN RUN IN APP MODE
;
HB_BCDTMP	.FILL	5,0		; BCD NUMBER STORAGE (TEMP)
;
HB_END		.EQU	$
;
SLACK		.EQU	HBX_LOC - $
		.ECHO	"HDIAG space remaining: "
		.ECHO	SLACK
		.ECHO	" bytes.\n"
;
#IFDEF ROMBOOT
		.FILL	SLACK
#ENDIF
;
		.END
