You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

7109 lines
173 KiB

;
;==================================================================================================
; HBIOS
;==================================================================================================
;
; THIS FILE CONTAINS THE HBIOS IMAGE THAT IS INTENDED TO RUN IN A DEDICATED RAM BANK. THE CODE IS
; CONSTRUCTED SUCH THAT IT CAN BE LAUNCHED IN A VARIETY OF MODES AND INSTALL ITSELF. A SMALL 512
; BYTE PROXY IS PLACED AT THE TOP OF CPU MEMORY (FE00H-FFFFH). THIS PROXY CODE ALLOWS CODE
; RUNNING FROM ANY BANK TO INVOKE HBIOS FUNCTIONS. NORMALLY, ANY BANK THAT RUNS CODE WOULD SETUP
; THE RST 8 VECTOR TO POINT TO THE PROXY INVOKE ENTRY POINT AT FFF0H. CALLS VIA THE PROXY INVOKE
; ENTRY POINT TRANSPARENTLY SWAP IN THE HBIOS BANK, PERFORM THE REQUESTED FUNCTION, AND RETURN
; WITH THE ORIGINAL BANK ACTIVE. THE CODE USING HBIOS FUNCTIONS DOES NOT NEED TO BE AWARE OF
; THE BANK SWITCHING THAT OCCURS.
;
; THIS FILE CAN BE COMPILED TO BOOT IN ONE OF 3 MODES (ROM, APPLICATION, OR IMAGE) 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. AFTER PERFORMING MINIMAL
; SYSTEM INITIALIZATION, THE IMAGE OF THE RUNNING ROM BANK IS COPIED TO A RAM BANK
; CREATING A SHADOW COPY IN RAM. EXECUTION IS THAN TRANSFERRED TO THE RAM SHADOW COPY.
; THIS IS ESSENTIAL BECAUSE THE HBIOS CODE DOES NOT SUPPORT RUNNING IN READ ONLY MEMORY
; (EXCEPT FOR THE INITIAL LAUNCHING CODE). IN THIS MODE, THE HBIOS INITIALIZATION WILL
; ALSO COPY THE OS IMAGES BANK IN ROM TO THE USER RAM BANK AND LAUNCH IT AFTER HBIOS
; IS INSTALLED.
;
; - 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. NOTE THAT IN
; THIS CASE IT IS ASSUMED THAT AN OS IMAGES FILE IS APPENDED TO THE END OF THE
; HBIOS APPLICATION BINARY. THE APPENDED OS IMAGES ARE COPIED TO THE USER RAM
; BANK AND LAUNCHED AFTER HBIOS HAS INSTALLED ITSELF.
;
; - IMGBOOT: BOOT FROM AN IMAGE FILE THAT HAS BEEN PLACED IN THE USER BANK
;
; WHEN IMGBOOT IS DEFINED, THE FILE IS ASSEMBLED SUCH THAT IT CAN BE PRELOADED
; INTO THE RAM USER BANK BY AN EXTERNAL PROCESS THAT SUBSEQUENTLY LAUNCHES
; THE CODE AT ADDRESS 0. THE MOST COMMON EXAMPLE OF THIS IS THE UNA FSFAT
; TOOL WHICH CAN LOAD AN IMAGE FROM A DOS FAT FILESYSTEM PROVIDING A SIMPLE
; WAY TO LOAD A TEST COPY OF HBIOS. AS IS THE CASE WITH APPBOOT, IT IS ASSUMED
; THAT AN OS IMAGES FILE IS APPENDED TO THE END OF THE IMAGE AND IS LAUNCHED
; AFTER HBIOS IS INSTALLED.
;
; INCLUDE FILE NESTING:
;
; hbios.asm
; - std.asm
; - ver.inc
; - hbios.inc
; - build.inc
; - config/<plt>_<cfg>.asm
; - cfg_<plt>.asm
; - <drivers>.asm
; - <fonts>.asm
; - util.asm
; - time.asm
; - bcd.asm
; - decode.asm
; - encode.asm
; - [xio|mio].asm
; - dsky.asm
; - unlzsa2s.asm
;
; INCLUDE GENERIC STUFF
;
#INCLUDE "std.asm"
;
#DEFINE HBIOS
;
; MAKE SURE EXACTLY ONE OF ROMBOOT, APPBOOT, IMGBOOT IS DEFINED.
;
MODCNT .EQU 0
#IFDEF ROMBOOT
MODCNT .SET MODCNT + 1
#ENDIF
#IFDEF APPBOOT
MODCNT .SET MODCNT + 1
#ENDIF
#IFDEF IMGBOOT
MODCNT .SET MODCNT + 1
#ENDIF
#IF (MODCNT != 1)
.ECHO "*** ERROR: PLEASE DEFINE ONE AND ONLY ONE OF ROMBOOT, APPBOOT, IMGBOOT!!!\n"
!!! ; FORCE AN ASSEMBLY ERROR
#ENDIF
;
; SOME HARDWARE REQUIRES A SPECIFIC ROMSIZE (NOTABLY ZZRCC) OR THE
; RESULTING BUILD IMAGES WILL BE CORRUPT. ROMSIZE_CHK IS SPECIFIED
; IN THE CONFIG FILE AND IS VERIFIED AGAINST THE ROMSIZE BEING USED
; BY THE BUILD. A ROMSIZE_CHK VALUE OF 0 INDICATES THE VERFICATION
; IS DISABLED (WHICH IT USUALLY IS).
;
#IF (ROMSIZE_CHK != 0) & (ROMSIZE != ROMSIZE_CHK)
.ECHO "*** ERROR: ROMSIZE VALUE VERIFICATION FAILURE.\n"
.ECHO "THIS CONFIGURATION REQUIRES A ROMSIZE OF " \ .ECHO ROMSIZE_CHK \ .ECHO ".\n"
.ECHO "BUILD IS USING A ROMSIZE OF " \ .ECHO ROMSIZE \ .ECHO ".\n"
.ECHO "SEE COMMENTS IN HBIOS.ASM.\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 EI $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
;
;
;
; THE RTCDEF EQUATE IS INITIALIZED HERE AND UPDATED BY DRIVER INCLUDES
; THAT SHARE THE RTC LATCH. AS EACH DRIVER FILE IS INCLUDED, IT CAN
; USE .SET TO SET ANY BITS THEY OWN WITHIN THE RTC LATCH BYTE.
; SINCE RTCDEF IS CHANGED AFTER IT NEEDS TO BE USED BY THE CODE, IT
; CANNOT BE USED DIRECTLY TO SET THE LATCH. INSTEAD, THE FINAL VALUE
; OF RTCDEF IS USED TO INITIALIZE A STORAGE BYTE CALLED RTCDEFVAL AT
; THE END OF HBIOS.ASM. SO (RTCDEFVAL) CAN BE USED ANYWHERE IN
; HBIOS.ASM TO ACCESS THE FINAL RTCDEF VALUE.
;
RTCDEF .EQU 0 ; ALLOWS DRIVERS TO SET BITS
;
#IF (PLATFORM == PLT_SCZ180)
RTCDEF .SET RTCDEF | %00000001 ; SC128 I2C SCL BIT
#ENDIF
;
#IF ((CPUSPDCAP==SPD_HILO) & (PLATFORM==PLT_MBC))
RTCDEF .SET RTCDEF & ~%00001000 ; INITIAL SPEED LOW
#ENDIF
;
#IF ((CPUSPDCAP==SPD_HILO) & (PLATFORM==PLT_SBC))
RTCDEF .SET RTCDEF | %00001000 ; INITIAL SPEED LOW
#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
JP HB_INVOKE ; INVOKE HBIOS FUNCTION
.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
;
;==================================================================================================
; HBIOS CONFIGURATION BLOCK (HCB)
;==================================================================================================
;
.ORG HCB_LOC
HCB:
JP HB_START
;
CB_MARKER .DB 'W',~'W' ; MARKER
CB_VERSION .DB RMJ << 4 | RMN ; FIRST BYTE OF VERSION INFO
.DB RUP << 4 | RTP ; SECOND BYTE OF VERSION INFO
;
CB_PLATFORM .DB PLATFORM
CB_CPUMHZ .DB CPUMHZ
CB_CPUKHZ .DW CPUKHZ
CB_RAMBANKS .DB RAMSIZE / 32
CB_ROMBANKS .DB ROMSIZE / 32
;
CB_BOOTVOL .DW 0 ; BOOT VOLUME IS UNIT/SLICE, SET BY LOADER
CB_BOOTBID .DB 0 ; BOOT BANK ID, SET BY LOADER
CB_SERDEV .DB 0 ; PRIMARY SERIAL UNIT IS UNIT #0 BY FIAT
CB_CRTDEV .DB $FF ; PRIMARY CRT UNIT, $FF UNTIL AFTER HBIOS INIT
CB_CONDEV .DB $FF ; CONSOLE UNIT, $FF UNTIL AFTER HBIOS INIT
;
CB_DIAGLVL .DB DIAGLVL ; ROMWBW HBIOS DIAGNOSTIC LEVEL
;
; MEMORY MANAGEMENT VARIABLES START AT $20
;
.FILL (HCB + $20 - $),0
;
CB_HEAP .DW 0
CB_HEAPTOP .DW 0
;
; STANDARD BANK ID'S START AT $D8. DEFAULT VALUES FOR 512KB SYSTEM WITH NO RESERVED BANKS
;
.FILL (HCB + $D8 - $),0
;
CB_BIDCOM .DB BID_COM
CB_BIDUSR .DB BID_USR
CB_BIDBIOS .DB BID_BIOS
CB_BIDAUX .DB BID_AUX
CB_BIDRAMD0 .DB BID_RAMD0
CB_BIDRAMDN .DB BID_RAMDN
CB_BIDROMD0 .DB BID_ROMD0
CB_BIDROMDN .DB BID_ROMDN
;
.FILL (HCB + HCB_SIZ - $),0 ; PAD REMAINDER OF HCB
;
;==================================================================================================
; 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
.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
;
SC HB_DISPATCH
;
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:
AND %00011111 ; AVOID WRAPPING BITS
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:
AND %00111111 ; AVOID WRAPPING BITS
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
SC Z280_BNKSEL ; SYSCALL
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,ROMSIZE / 32 ; STARTING RAM BANK NUMBER OFFSET
;
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
; SEE MBC RUNTIME MEMORY SIZE ADJUSTMENT
HBX_MBCMSK .EQU $+1 ; FORCE TOP 32K ; MASK POPULATED
XOR %00000000 ; TO BE IN FIRST CHIP ; DURING INITIALIZATION
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)
SC Z280_BNKCPYX ; SYSCALL TO BNKCPYX
RET
;
IOPRSAV .DW 0 ; TEMP STORAGE FOR IOPR
;
#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
SC HBX_BNKCALL2 ; SYSCALL TO BNKCALL2
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 0 ; SHADOW VALUE FOR RTC LATCH PORT
.DB $FE ; HB_LOCK: HBIOS MUTEX LOCK
JP HBX_INVOKE ; 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
;
;==================================================================================================
; HBIOS CORE
;==================================================================================================
;
;==================================================================================================
; ENTRY VECTORS (JUMP TABLE) AND INTERNAL PROCESSING STACK
;==================================================================================================
;
HB_ENTRYTBL .EQU $
;
JP HB_START ; HBIOS INITIALIZATION
JP HB_DISPATCH ; VECTOR TO DISPATCHER
;
HB_STKSIZ .EQU $100 - ($ & $FF)
;
.FILL HB_STKSIZ,$FF ; USE REMAINDER OF PAGE FOR HBIOS STACK
HB_STACK .EQU $ ; TOP OF HBIOS STACK
;
;==================================================================================================
; INTERRUPT VECTOR TABLE (MUST START AT PAGE BOUNDARY!!!)
;==================================================================================================
;
; 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
;
;==================================================================================================
; 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 ((PLATFORM == PLT_MBC) | (PLATFORM == PLT_SBC))
; INITIALIZE RTC LATCH BYTE
; FOR SOME PLATFORMS THIS CONTROLS HI/LO SPEED CIRCUIT
LD A,(RTCDEFVAL) ; GET DEFAULT VALUE
OUT (RTCIO),A ; SET IT
;#ENDIF
;
#IF (PLATFORM == PLT_N8)
LD A,N8_DEFACR ; ENSURE N8 ACR
OUT0 (N8_ACR),A ; ... REGISTER IS INITIALIZED
#ENDIF
;
#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,(RTCDEFVAL) ; DEFAULT LATCH VALUE
OR %00000001 ; LED 0 ON
#ENDIF
OUT (LEDPORT),A
#ENDIF
;
; WARNING: ALTHOUGH WE ARE INITIALIZING SP HERE, IT IS NOT YET
; SAFE TO PUSH VALUES TO THE STACK BECAUSE SOME PLATFORMS WILL
; NOT YET HAVE RAM MAPPED TO THE UPPER 32K YET!
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
OTIRW ; 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
OTIRW ; 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
;
; AT THIS POINT, RAM SHOULD BE AVAILABLE IN THE COMMON BANK
; (TOP 32K).
;
; NOTIFICATION THAT WE HAVE MADE THE JUMP TO RAM BANK!
; THE DIAG() MACRO IS NOT USED BECAUSE IT USES THE STACK AND WE DO
; NOT WANT TO EFFECT RAM UNTIL AFTER THE BACKUP BATTERY STATUS CHECK
; IS PERFORMED NEXT.
;
LD A,%00000011
OUT (DIAGPORT),A
;
; WE USE THE TWO BYTES IMMEDIATELY BELOW THE PROXY TO STORE A COUPLE
; VALUES TEMPORARILY BECAUSE WE MAY BE OPERATING IN ROM AT THIS POINT.
; (HBX_LOC - 1) = BATCOND, (HBX_LOC - 2) = APPBANK
; THERE IS NOTHING ON THE STACK AT THIS POINT SO, HERE, WE JUST RESET
; THE STACK TO HBX_LOC - 2.
;
LD SP,HBX_LOC - 2
;
; CHECK BATTERY BACKUP STATUS BEFORE WE TOUCH RAM (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
; BECAUSE WE ARE CURRENTLY RUNNING IN ROM. AFTER WE TRANSITION HBIOS
; TO RAM, THE VALUE IS MOVED TO IT'S REAL LCOATION AT HB_BATCOND.
; IF THERE IS NO DS1210 IN THE SYSTEM, THE CODE BELOW DOES NO HARM.
;
LD HL,HBX_LOC - 1 ; POINT TO BYTE
XOR A ; ZERO MEANS LOW BAT
LD (HL),A
INC A ; 1 MEANS BAT OK
LD (HL),A
;
; INSTALL PROXY IN UPPER MEMORY
; THE HB_CURBNK MUST BE PRESERVED IF THIS IS AN APPBOOT.
;
LD A,(HB_CURBNK) ; SAVE EXISTING HB_CURBNK
LD DE,HBX_LOC ; AS PER ABOVE
LD HL,HBX_IMG
LD BC,HBX_SIZ
LDIR
;
#IFDEF APPBOOT
LD (HB_CURBNK),A ; RESTORE HB_CURBNK
#ENDIF
;
; 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.
;
; THIS VALUE IS TEMPORARILY STORED AT HBX_LOC - 2
; BECAUSE WE ARE CURRENTLY RUNNING IN ROM. AFTER WE TRANSITION HBIOS
; TO RAM, THE VALUE IS MOVED TO IT'S REAL LCOATION AT HB_APPBNK.
;
LD A,(HB_CURBNK) ; GET HB_CURBNK
LD (HBX_LOC - 2),A ; ... AND SAVE TEMP FOR APPBNK
;
; THE RTCVAL FIELD OF THE PROXY DATA NEEDS TO BE INITIALIZED HERE
; BECAUSE IT CANNOT BE PRE-INITIALIZED (SEE COMMENTS ABOVE WHERE
; RTCVAL EQUATE IS DEFINED).
;
LD A,(RTCDEFVAL)
LD (HB_RTCVAL),A
;
#IFDEF TESTING
;
; THIS IS WHERE WE PROBE FOR THE ACTUAL NUMBER OF RAM
; BANKS AVAILABLE IN THE SYSTEM. THE PROBE CODE NEEDS
; TO BE COPIED TO AND RUN FROM THE COMMON RAM BANK.
;
LD DE,$F000
LD HL,RS_IMAGE
LD BC,RS_LEN
LDIR
CALL RS_START
JP RS_IMAGE + RS_LEN
;
; CODE THAT IS COPIED TO $F000 TO PERFORM RAM SIZE DETECTION
;
RS_IMAGE:
.ORG $F000
RS_START:
LD A,(HB_CURBNK) ; GET CURRENT BANK
PUSH AF ; SAVE IT
LD C,0 ; RUNNING BANK COUNT
LD HL,$7FFF ; BYTE TEST ADDRESS
LD IX,RS_ARY ; ORIG BYTE STORAGE ARRAY PTR
RS_LOOP1:
LD A,C
ADD A,$80 ; OFFSET BY START OF RAM BANKS
CALL HBX_BNKSEL ; SELECT THE BANK
LD A,(HL) ; GET ORIGINAL VALUE
LD (IX),A ; SAVE IT TO RESTORE LATER
INC IX ; BUMP IX
LD A,$AA ; TEST LOC WITH $AA
LD (HL),A ; AVOID PROBLEMS WITH
LD (HL),A ; ... DS1210
LD (HL),A
LD A,(HL)
CP $AA
JR NZ,RS_DONE
LD A,$55 ; TEST LOC WITH $55
LD (HL),A
LD A,(HL)
CP $55
JR NZ,RS_DONE
; STORE A UNIQUE VALUE
LD A,C
LD (HL),A
OR A ; ZERO?
JR Z,RS_NEXT ; SKIP STORED VALUE CHECK
; VERIFY ALL STORED VALUES
LD B,C ; INIT LOOP COUNTER
LD E,0 ; INIT BANK ID
RS_LOOP3:
LD A,E
ADD A,$80
CALL HBX_BNKSEL
LD A,(HL)
CP E ; VERIFY
JR NZ,RS_DONE ; ABORT IF MISCOMPARE
INC E ; NEXT BANK
DJNZ RS_LOOP3
;
RS_NEXT:
INC C ; ADD 1 TO RAM BANK COUNT
JR RS_LOOP1 ; AND LOOP TILL DONE
;
RS_DONE:
LD E,C ; FINAL BANK COUNT TO E
LD A,C
OR A
JR Z,RS_LOOPZ
; RESTORE SAVED VALUES
LD IX,RS_ARY
LD B,C ; LOOP COUNT
LD C,$80 ; BANK ID
RS_LOOP2:
LD A,C
CALL HBX_BNKSEL
INC C
LD A,(IX) ; GET VALUE
LD (HL),A ; RESTORE IT
INC IX
DJNZ RS_LOOP2 ; ALL BANKS
RS_LOOPZ:
;
; MBC RUNTIME MEMORY SIZE ADJUSTMENT
;
; THE MBC RAM BOARD CAN CONTAIN 1 OR 2 RAM CHIPS. THEY CAN BE
; EITHER 128K OR 512K EACH. SO THE MBC RAM BOARD CAN HAVE A
; TOTAL OF 128K, 256K, 512K, OR 1024K. THE COMMON (HIMEM) RAM
; IS ALWAYS MAPPED TO THE LAST 32K OF THE FIRST CHIP ON THE BOARD.
; IF THERE ARE TWO CHIPS ON THE BOARD, THIS MEANS THE COMMON
; BANK WILL APPEAR IN THE "MIDDLE" OF THE PHYSICAL RAM BANKS.
; ROMWBW NEEDS THE COMMON BANK TO BE AT THE LAST BANK OF PHYSICAL
; RAM IN ORDER TO HAVE SEQUENTIAL RAM BANKS AVAILABLE FOR THE
; RAM DISK. TO WORK AROUND THIS, WE FLIP THE HIGH BIT OF THE
; BANK ID FOR AN MBC SYSTEM IFF IT HAS 2 CHIPS (256K OR 1024K).
; THE CODE BELOW GENERATES THE CORRECT MASK TO ACCOMPLISH THIS
; AND THEN POKES THE MASK INTO AN XOR INSTRUCTION IN THE MBC
; MEMORY MANAGER.
;
#IF (MEMMGR == MM_MBC)
;
;LD HL,CB_RAMBANKS ; IN NUMBER OF RAMBANKS DETECTED FOR MBC
LD A,%11101011 ; IS 4 (128KB) OR 16 (512KB) THEN
;AND (HL) ; ZERO THE LAST BANK MASK OTHERWISE
AND E ; ZERO THE LAST BANK MASK OTHERWISE
JR Z,MBC_SINGLE ; CALCULATE THE LAST BANK MASK (BANKS/2)
RRA ; 256K = %00000100, 1024K = %00010000
MBC_SINGLE:
LD (HBX_MBCMSK),A
;
#ENDIF
;
; RETURN TO ORIGINAL BANK
POP AF
CALL HBX_BNKSEL
LD A,E ; RETURN BANK COUNT
LD ($FFEA),A ; STASH HERE FOR A BIT
RET
;
RS_ARY .EQU $
;
RS_LEN .EQU $ - RS_START
.ORG RS_IMAGE + RS_LEN
;
#ELSE
;
; MBC RUNTIME MEMORY SIZE ADJUSTMENT
;
; THE MBC RAM BOARD CAN CONTAIN 1 OR 2 RAM CHIPS. THEY CAN BE
; EITHER 128K OR 512K EACH. SO THE MBC RAM BOARD CAN HAVE A
; TOTAL OF 128K, 256K, 512K, OR 1024K. THE COMMON (HIMEM) RAM
; IS ALWAYS MAPPED TO THE LAST 32K OF THE FIRST CHIP ON THE BOARD.
; IF THERE ARE TWO CHIPS ON THE BOARD, THIS MEANS THE COMMON
; BANK WILL APPEAR IN THE "MIDDLE" OF THE PHYSICAL RAM BANKS.
; ROMWBW NEEDS THE COMMON BANK TO BE AT THE LAST BANK OF PHYSICAL
; RAM IN ORDER TO HAVE SEQUENTIAL RAM BANKS AVAILABLE FOR THE
; RAM DISK. TO WORK AROUND THIS, WE FLIP THE HIGH BIT OF THE
; BANK ID FOR AN MBC SYSTEM IFF IT HAS 2 CHIPS (256K OR 1024K).
; THE CODE BELOW GENERATES THE CORRECT MASK TO ACCOMPLISH THIS
; AND THEN POKES THE MASK INTO AN XOR INSTRUCTION IN THE MBC
; MEMORY MANAGER.
;
#IF (MEMMGR == MM_MBC)
LD HL,CB_RAMBANKS ; IF NUMBER OF RAMBANKS DETECTED FOR MBC
LD A,%11101011 ; IS 4 (128KB) OR 16 (512KB) THEN
AND (HL) ; ZERO THE LAST BANK MASK OTHERWISE
JR Z,MBC_SINGLE ; CALCULATE THE LAST BANK MASK (BANKS/2)
RRA ; 256K = %00000100, 1024K = %00010000
MBC_SINGLE:
LD (HBX_MBCMSK),A
#ENDIF
;
#ENDIF
;
; IF THIS IS A ROM-LESS SYSTEM, THEN WE NEED TO COPY THE PAYLOAD
; (LOADER, MONITOR, ZSDOS) THAT HAS BEEN LOADED TO PHYSICAL RAM
; BANKS 0 AND 1 TO THE USER TPA BANK TO RUN AFTER BOOT.
; IT IS DONE PRIOR TO COPYING HBIOS TO IT'S FINAL BANK BECAUSE
; THE PAYLOAD MAY EXTEND INTO THE HBIOS OPERATING BANK. THIS
; HAPPENS PRIMARILY IN THE CASE WHERE THE
; SYSTEM HAS THE MINIMUM 128KB OF RAM.
;
#IFDEF ROMBOOT
#IF (ROMSIZE == 0)
;
; THE PAYLOAD IS LIKELY TO CROSS OVER THE RAM BANK 0/1
; BOUNDARY. BNKCPY DOES NOT HANDLE THIS BECAUSE IT ASSUMES
; THE COMMON BANK IS USED AFTER PASSING OVER THE BANK
; BOUNDARY. WE WORK AROUND THAT HERE BY DOING TWO COPIES.
; THE FIRST ONE HANDLES THE PORTION OF THE PAYLOAD FROM THE
; END OF HBIOS TO THE BANK BOUNDARY ($8000). THE SECOND
; ONE HANDLES THE PORTION THAT EXTENDS INTO THE SECOND
; PHYSICAL RAM BANK.
;
; COPY PORTION OF PAYLOAD FOLLOWING HBIOS TO THE BANK
; BOUNDARY AT $8000 INTO START OF TPA.
LD A,BID_RAM0
LD (HB_SRCBNK),A
LD A,BID_USR
LD (HB_DSTBNK),A
LD HL,HB_END
LD DE,0
LD BC,$8000-HB_END
;
#IF (MEMMGR == MM_Z280)
CALL Z280_BNKCPY
#ELSE
CALL HBX_BNKCPY
#ENDIF
;
; COPY REMAINDER OF PAYLOAD EXTENDING INTO THE SECOND PHYSICAL
; RAM BANK. NOTE THAT THE DESTINATION ADDRESS (DE) IS
; ALREADY CORRECT FROM THE PRIOR COPY.
LD A,BID_RAM0+1
LD (HB_SRCBNK),A
LD HL,$0000
; DE IS ALREADY CORRECT
LD BC,$8000-($8000-HB_END)
;
#IF (MEMMGR == MM_Z280)
CALL Z280_BNKCPY
#ELSE
CALL HBX_BNKCPY
#ENDIF
;
#ENDIF
;
#ENDIF
;
; IF ALREADY EXECUTING IN RAM, BYPASS RAM BANK INSTALLATION
;
LD A,(HB_RAMFLAG)
OR A
JR NZ,HB_START1
;
; INSTALL HBIOS IN RAM BANK
;
LD A,(HB_CURBNK)
LD (HB_SRCBNK),A
LD A,BID_BIOS
LD (HB_DSTBNK),A
LD HL,0
LD DE,0
LD BC,$8000
#IF (MEMMGR == MM_Z280)
CALL Z280_BNKCPY
#ELSE
CALL HBX_BNKCPY
#ENDIF
;
; TRANSITION TO HBIOS IN RAM BANK
;
#IF (MEMMGR == MM_Z280)
LD A,BID_BIOS
LD B,$10 ; FIRST SYSTEM PDR
CALL Z280_BNKSEL
JR HB_START1
#ELSE
LD A,BID_BIOS ; BIOS BANK ID
LD IX,HB_START1 ; EXECUTION RESUMES HERE
CALL HBX_BNKCALL ; CONTINUE IN RAM BANK, DO NOT RETURN
HALT ; WE SHOULD NOT COME BACK HERE!
#ENDIF
;
HB_RAMFLAG .DB FALSE ; INITIALLY FALSE, SET TO TRUE BELOW AFTER RAM TRANSITION
;
; EXECUTION RESUMES HERE AFTER SWITCH TO RAM BANK
;
HB_START1: ; BNKCALL ARRIVES HERE, BUT NOW RUNNING IN RAM BANK
;
; WE RESET THE STACK HERE BECAUSE WE ARE NOT GOING TO RETURN
; FROM THE BNKCALL. REMEMBER THAT WE STORED A COUPLE BYTES
; RIGHT BELOW HBX_LOC, SO THE STACK IS SET TO START JUST BELOW
; THAT.
LD SP,HBX_LOC - 2 ; RESET STACK
;
; NOTIFY THAT WE MADE THE TRANSTION!
DIAG(%00000111)
LED(%00000010)
;
; SET THE IN-RAM FLAG
LD A,TRUE ; ACCUM := TRUE
LD (HB_RAMFLAG),A ; SET RAMFLAG
;
; RECOVER DATA PASSED PRIOR TO RAM TRANSITION
; (HBX_LOC - 1) = BATCOND, (HBX_LOC - 2) = APPBNK
POP HL ; POP 2 BYTES
LD A,H ; GET FIRST BYTE PUSHED
LD (HB_BATCOND),A ; ... AND SAVE AS BAT COND
;
#IFDEF APPBOOT
LD A,L ; GET SECOND BYTE PUSHED
LD (HB_APPBNK),A ; ... AND SAVE AS APPBNK
#ENDIF
;
#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
;
; 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
;
#IF FALSE
;
; POPULATE THE CRITICAL RAM BANK NUMBERS.
;
; ASSUME THAT CB_RAMBANKS IS THE NUMBER OF 32K RAM BANKS THAT HAS BEEN SET EITHER
; AT ASSEMBLY TIME OR BY PROBING THE ACTUAL AVAILABLE MEMORY (NOT IMPLEMENTED YET).
;
LD A,(CB_RAMBANKS) ; CALCULATE TOP RAMBANK
ADD A,BID_RAM0 ; AS FIRST RAMBANK +
DEC A ; #RAMBANKS - 1
;
LD HL,CB_BIDCOM
LD B,4
CB_IDS: LD (HL),A ; POPULATE CB_BIDCOM
INC HL ; POPULATE CB_BIDUSR
DEC A ; POPULATE CB_BIDBIOS
DJNZ CB_IDS ; POPULATE CB_BIDAUX
;
LD A,(CB_BIDUSR)
LD (HB_SRCBNK),A ; POPULATE HB_SRCBNK
LD (HB_DSTBNK),A ; POPULATE HB_DSTBNK
;
LD A,BID_RAM0 ; POPULATE CB_BIDRAMD0 ; START RAMBANK
LD (HL),A
INC HL
;
LD A,(CB_RAMBANKS) ; POPULATE CB_BIDRAMDN ; END RAMBANK
DEC A
SUB TOT_RAM_RB
LD (HL),A
;
#ENDIF
;
;==================================================================================================
; RECOVERY MODE
;==================================================================================================
;
; PLATFORM SPECIFIC CODE FOR DETECTING RECOVERY MODE SWITCH
;
#IF (BT_REC_TYPE != BT_REC_NONE)
#IF (BT_REC_TYPE == BT_REC_FORCE)
LD A,1 ; SET FOR RECOVERY MODE
LD (HB_BOOT_REC),A ; SAVE FOR LATER
#ENDIF
#IF ((PLATFORM == PLT_SBC) | (PLATFORM == PLT_MBC))
#IF (BT_REC_TYPE == BT_REC_SBC01)
LD A,%00100000 ; DISABLE RTC AND
OUT (RTCIO),A ; DRQ DRIVER READ
IN A,(RTCIO) ; BIT 0 (DRQ).
CPL ; PULLED HIGH
AND 1 ; IS RECOVERY MODE
LD (HB_BOOT_REC),A ; SAVE FOR LATER
#ENDIF
#IF (BT_REC_TYPE == BT_REC_SBC1B)
IN A,(RTCIO) ; RTC PORT, BIT 6 HAS THE
BIT 6,A ; STATE OF CONFIG JUMPER
LD A,1 ; JUMPER INSTALLED
JR Z,SAVE_REC_M ; IS RECOVERY MODE
LD A,0
SAVE_REC_M:
LD (HB_BOOT_REC),A ; SAVE FOR LATER
#ENDIF
#IF (BT_REC_TYPE == BT_REC_SBCRI)
IN A,($68 + 6) ; UART_MSR MODEM
BIT 6,A ; STATUS REGISTER
LD A,0 ; BIT 6
JR Z,SAVE_REC_M ; IS RECOVERY MODE
LD A,1
SAVE_REC_M:
LD (HB_BOOT_REC),A ; SAVE FOR LATER
#ENDIF
#ENDIF
#ENDIF
;
DIAG(%00001111)
;
#IF (WBWDEBUG == USEMIO) ; BUFFER OUTPUT UNTIL
CALL MIO_INIT ; WE GET TO BOOT MESSAGE
#ENDIF
;
#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 (DSRTCENABLE)
CALL DSRTC_PREINIT
#ENDIF
;
#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
;
DIAG(%00011111)
;
; INIT OSCILLATOR SPEED FROM CONFIG
;
LD HL,CPUOSC / 1000
LD (HB_CPUOSC),HL
;
; ATTEMPT DYNAMIC CPU SPEED DERIVATION
; NOTE THAT FOR PLATFORMS WITH SOFTWARE SELECTABLE CPU SPEED,
; THIS IS BEING DONE WITH THE CPU SPEED SET TO THE LOWEST
; POSSIBLE SETTING. THE FINAL CPU SPEED WILL BE ADJUSTED
; LATER.
;
CALL HB_CPUSPD ; CPU SPEED DETECTION
JR NZ,HB_CPUSPD2 ; SKIP IF FAILED
;
; RECORD THE UPDATED CPU OSCILLATOR SPEED
;
#IF ((CPUFAM == CPU_Z180) | (CPUSPDCAP == SPD_HILO))
; SPEED MEASURED WILL BE HALF OSCILLATOR SPEED
; SO RECORD DOUBLE THE MEASURED VALUE
SLA L
RL H
LD (HB_CPUOSC),HL
#ENDIF
;
HB_CPUSPD2:
;
; INIT CPUKHZ BASED ON OSCILLATOR SPEED
;
LD HL,(HB_CPUOSC)
;
; TRANSITION TO FINAL DESIRED CPU SPEED FOR THOSE PLATFORMS
; THAT SUPPORT SOFTWARE SELECTABLE CPU SPEED. UPDATE CB_CPUKHZ
; IN HCB AS WE DO THIS.
;
#IF ((CPUSPDCAP==SPD_HILO) & (PLATFORM==PLT_MBC))
#IF (CPUSPDDEF==SPD_HIGH)
; SET HIGH SPEED VIA RTC LATCH
LD A,(HB_RTCVAL)
OR %00001000 ; SET HI SPEED BIT
LD (HB_RTCVAL),A ; SAVE SHADOW
OUT (RTCIO),A ; IMPLEMENT
; HL IS ALREADY CORRECT FOR FULL SPEED OPERATION
#ELSE
; ADJUST HL TO REFLECT HALF SPEED OPERATION
SRL H ; ADJUST HL ASSUMING
RR L ; HALF SPEED OPERATION
#ENDIF
#ENDIF
;
#IF ((CPUSPDCAP==SPD_HILO) & (PLATFORM==PLT_SBC))
#IF (CPUSPDDEF==SPD_HIGH)
; SET HIGH SPEED VIA RTC LATCH
LD A,(HB_RTCVAL)
AND ~%00001000 ; CLEAR HI SPEED BIT
LD (HB_RTCVAL),A ; SAVE SHADOW
OUT (RTCIO),A ; IMPLEMENT
; HL IS ALREADY CORRECT FOR FULL SPEED OPERATION
#ELSE
; ADJUST HL TO REFLECT HALF SPEED OPERATION
SRL H ; ADJUST HL ASSUMING
RR L ; HALF SPEED OPERATION
#ENDIF
#ENDIF
;
#IF (CPUFAM == CPU_Z180)
;
LD HL,(HB_CPUOSC) ; INIT HL TO CPU OSC FREQ (KHZ)
;
#IF (Z180_CLKDIV == 0)
; ADJUST HL TO REFLECT HALF SPEED OPERATION
SRL H ; ADJUST HL ASSUMING
RR L ; HALF SPEED OPERATION
#ENDIF
;
#IF (Z180_CLKDIV == 1)
LD A,(HB_CPUTYPE) ; GET CPU TYPE
CP 2 ; Z8S180 REV K OR BETTER?
JR C,HB_CPU3 ; IF NOT, NOT POSSIBLE!
; SET CLOCK DIVIDE TO 1 RESULTING IN FULL XTAL SPEED
LD A,$80
OUT0 (Z180_CCR),A
; HL ALREADY REFLECTS FULL SPEED OPERATION
#ENDIF
;
#IF (Z180_CLKDIV >= 2)
LD A,(HB_CPUTYPE) ; GET CPU TYPE
CP 3 ; Z8S180 REV N OR BETTER?
JR C,HB_CPU3 ; 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
; ADJUST HL TO REFLECT DOUBLE SPEED OPERATION
SLA L
RL H
#ENDIF
;
HB_CPU3:
#ENDIF
;
; HL SHOULD NOW HAVE FINAL CPU RUNNING SPEED IN KHZ.
; UPDATE CB_CPUMHZ/CB_CPUKHZ WITH THIS VALUE.
;
LD (CB_CPUKHZ),HL ; UPDATE CPUKHZ
LD DE,1000 ; SET UP TO DIV BY 1000 FOR MHZ
CALL DIV16 ; BC=CPU MHZ, HL=REMAINDER
LD DE,500 ; SET UP TO ROUND UP
XOR A ; IF WITHIN 500 KHZ
SBC HL,DE ; REMAINDER - 500
CCF ; COMPLEMENT CF
ADC A,C ; C -> A; ADD CF FOR ROUNDING
LD (CB_CPUMHZ),A ; SAVE IT
;
#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
;
LD A,(CB_CPUMHZ) ; CPU SPEED TO ACCUM AND INIT
CALL DELAY_INIT ; .. SPEED COMPENSATED DELAY
;
#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 (PLATFORM == PLT_SBC)
;
#IF (HTIMENABLE) ; SIMH TIMER
;
#IF (INTMODE == 1)
LD HL,HB_TIMINT
CALL HB_ADDIM1 ; ADD TO IM1 CALL LIST
#ENDIF
#ENDIF
;
#ENDIF
;
#IF (KIOENABLE)
CALL KIO_PREINIT
#ENDIF
;
#IF (CTCENABLE)
CALL CTC_PREINIT
#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
;
#IF (Z180_TIMER)
;
; SETUP Z180 TIMER0 INTERRUPT VECTOR IN IVT
LD HL,HB_TIMINT
LD (IVT(INT_TIM0)),HL ; Z180 TIMER 0
; SETUP PERIODIC TIMER INTERRUPT ON TIMER 0
; *** THIS ASSUMES A TICKFREQ OF 50HZ!!! ***
;
#IF (TICKFREQ != 50)
.ECHO "TICKFREQ *MUST* BE 50 FOR Z180 TIMER\n"
!!!
#ENDIF
;
; Z180 PRESCALES THE COUNTER BY 20 SO,
; RLDR = CPU CLK / 20 / TICKFREQ
; IF WE ASSUME TICKFREQ = 50, WE CAN SIMPIFY TO
; RLDR = CPU CLK / 1000
; NOW IF DIVIDE BOTH SIDES BY 1000, WE CAN USE
; CPUKHZ VALUE AND SIMPLIFY TO
; RLDR = CPUKHZ
XOR A ; ALL BITS ZERO
OUT0 (Z180_TCR),A ; ... INHIBITS TIMER OPERATION
LD HL,(CB_CPUKHZ) ; 50HZ = 18432000 / 20 / 50 / X, SO X = CPU KHZ
OUT0 (Z180_TMDR0L),L ; INITIALIZE TIMER 0 DATA REGISTER
OUT0 (Z180_TMDR0H),H
DEC HL ; RELOAD OCCURS *AFTER* ZERO
OUT0 (Z180_RLDR0L),L ; INITIALIZE TIMER 0 RELOAD REGISTER
OUT0 (Z180_RLDR0H),H
LD A,%00010001 ; ENABLE TIMER0 INT AND DOWN COUNTING
OUT0 (Z180_TCR),A
;
#ENDIF
;
#ENDIF
;
#ENDIF
;
#IF (CPUFAM == CPU_Z280)
;
#IF (MEMMGR == MM_Z280)
;
#IF (Z280_TIMER)
;
Z280_TC .EQU CPUOSC / 4 / 50 / 2 ; TIME CONSTANT
;
LD HL,Z280_TIMINT
LD (Z280_IVT+$16),HL ; Z280 T/C VECTOR
;
; SELECT I/O PAGE $FE
LD C,Z280_IOPR ; I/O PAGE REGISTER
LDCTL HL,(C) ; GET CURRENT I/O PAGE
PUSH HL ; SAVE IT
LD L,$FE ; I/O PAGE $FE
LDCTL (C),HL
;
LD A,%10100000 ; CONFIG: C, RE, IE
OUT (Z280_CT0_CFG),A ; SET C/T 0
LD HL,CPUOSC / 50 / 16 ; TIME CONSTANT & COUNTER
LD C,Z280_CT0_TC ; SET C/T 0
OUTW (C),HL
LD C,Z280_CT0_CT ; SET C/T 0
OUTW (C),HL
LD A,%11100000 ; CMD: EN, GT
OUT (Z280_CT0_CMDST),A ; SET C/T 0
;
; RESTORE I/O PAGE
LD C,Z280_IOPR ; I/O PAGE REGISTER
POP HL ; RESTORE I/O PAGE
LDCTL (C),HL
;
#ENDIF
;
#ENDIF
;
#ENDIF
;
; INITIALIZE HEAP STORAGE
;
; INITIALIZE POINTERS
LD HL,HB_END ; HEAP FOLLOWS HBIOS CODE
LD (CB_HEAP),HL ; INIT HEAP BASE ADDRESS
LD (CB_HEAPTOP),HL ; INIT HEAP TOP ADDRESS
; CLEAR HEAP
LD BC,BNKTOP - HB_END ; MAX SIZE OF HEAP
LD A,$FF ; FILL WITH $FF
CALL FILL ; DO IT
;
DIAG(%00111111)
;
#IF FALSE
;
; TEST DEBUG ***************************************************************************************
;
CALL NEWLINE
CALL REGDMP
;
; TEST DEBUG ***************************************************************************************
;
#ENDIF
;
; PRE-CONSOLE INITIALIZATION
;
LD DE,HB_PCINITTBL ; POINT TO PRECONSOLE INIT TABLE
LD B,HB_PCINITTBLLEN ; NUMBER OF ENTRIES
;
#IF (BT_REC_TYPE != BT_REC_NONE)
LD A,(HB_BOOT_REC) ; IF WE ARE IN RECOVERY MODE
OR A ; POINT TO THE RECOVER MODE
JR Z,NOT_REC_M0 ; INITIALIZATION TABLE
LD B,HB_PCINITRLEN
LD DE,HB_PCINIT_REC
NOT_REC_M0:
;
#ENDIF
CALL CALLLIST ; PROCESS THE PRE-INIT CALL TABLE
;
#IF (DSKYENABLE)
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
;
#IF FALSE
;
; TEST DEBUG ***************************************************************************************
;
CALL NEWLINE
CALL REGDMP
;
; TEST DEBUG ***************************************************************************************
;
#ENDIF
;
DIAG(%01111111)
LED(%00000011)
;
; PRIOR TO THIS POINT, CONSOLE I/O WAS NOT AVAILABLE UNLESS DIRECTED TO DEBUG OUTPUT I.E. XIO
; NOW THAT HBIOS IS READY, SET THE CONSOLE UNIT TO ACTIVATE CONSOLE I/O
; VIA HBIOS.
;
XOR A ; FAILSAFE VALUE FOR BOOT CONSOLE DEVICE
LD (CB_CONDEV),A ; SAVE IT
;
LD A,(CIO_CNT) ; GET COUNT OF CHAR DEVICES
CP BOOTCON + 1 ; COUNT - (DEVICE + 1)
JR C,HB_CONRDY ; IF TOO HIGH, JUST USE FAILSAFE
LD A,BOOTCON ; GET REQUESTED CONSOLE DEV
LD (CB_CONDEV),A ; SAVE IT
HB_CONRDY:
#IF (WBWDEBUG == USEMIO) ; OUTPUT ANY CACHED DEBUG TEXT
LD HL,MIOOUTPTR
LD E,(HL)
INC HL
LD D,(HL)
INC HL
NXTMIO: LD A,(HL)
CALL COUT
INC HL
LD A,L
CP E
JR NZ,NXTMIO
LD A,H
CP D
JR NZ,NXTMIO
; CALL WRITESTR ; WRITESTR WILL WORK WILL ONLY PRINT UP TO FIRST $
#ENDIF
;
#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)
;
; DISPLAY HBIOS MUTEX ENABLED MESSAGE
;
#IF (HBIOS_MUTEX == TRUE)
CALL NEWLINE
CALL PRTSTRD
.TEXT "HBIOS MUTEX ENABLED$"
#ENDIF
;
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
;
#IF FALSE
HB_SPDTST:
CALL HB_CPUSPD ; CPU SPEED DETECTION
CALL NEWLINE
LD HL,(CB_CPUKHZ)
CALL PRTD3M ; PRINT AS DECIMAL WITH 3 DIGIT MANTISSA
JR HB_SPDTST
#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,(CB_RAMBANKS) ; GET NUMBER OF BANKS IN L
LD H,0 ; CALCULATE RAM SIZE
ADD HL,HL
ADD HL,HL ; X4
ADD HL,HL ; X8
ADD HL,HL ; X16
ADD HL,HL ; X32
;
CALL PRTDEC
CALL PRTSTRD
.TEXT "KB RAM$"
;
#IFDEF TESTING
;
CALL PRTSTRD
.TEXT ", RAMBANKS=0x$"
LD A,($FFEA)
CALL PRTHEXBYTE
;
CALL PRTSTRD
.TEXT ", RTCDEF=0x$"
LD A,(RTCDEFVAL)
CALL PRTHEXBYTE
;
#ENDIF
;
#IF 0
;
; DIAGNOSTIC DISPLAY OF BANK IDS IN HCB
;
CALL PRTSTRD
.TEXT ", BANK IDS:$"
LD DE,CB_BIDCOM
LD A,8
CALL PRTHEXBUF
;
#ENDIF
;
#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
#IF (ROMSIZE > 0)
;
; ROM CHECKSUM VERIFICATION
; EACH OF THE FIRST 4 ROM BANKS HAS A CHECKSUM INJECTED SUCH THAT
; A COMPUTED CHECKSUM ACROSS THE ENTIRE BANK SHOULD ALWAYS BE ZERO
;
HB_ROMCK:
CALL NEWLINE
PRTS("ROM VERIFY:$")
;
; COPY CHECKSUM ROUTINE TO UPPER RAM
LD HL,HB_CKBNK
LD DE,$F000
LD BC,HB_CKBNKSIZ
LDIR
;
; TEST FIRST 4 BANKS OF ROM
LD B,4 ; 4 BANKS
LD C,0 ; STARTING AT BANK 0
HB_ROMCK1:
PUSH BC ; SAVE LOOP CONTROL
CALL $F000 ; 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 A,(HB_CURBNK) ; GET CURRENT BANK
LD E,A ; SAVE IN E
LD A,C ; BANK TO TEST
CALL HBX_BNKSEL ; SELECT BANK IT
LD HL,$7FFF ; START AT BANK END
LD BC,1 ; DECREMENT VALUE
XOR A ; ZERO ACCUM
HB_CKBNK1:
#IF (MEMMGR == MM_Z280)
LD D,A ; WORKING VALUE TO D
LDUD A,(HL) ; GRAB NEXT BYTE FROM USER SPACE
ADD A,D ; ADD NEXT BYTE
#ELSE
ADD A,(HL) ; ADD NEXT BYTE
#ENDIF
OR A ; CLEAR CARRY
SBC HL,BC ; DECREMENT
JR NC,HB_CKBNK1 ; LOOP TILL DONE
PUSH AF ; SAVE RESULT
LD A,E ; BANK TO RESTORE
CALL HBX_BNKSEL ; RESTORE ORIG BANK
POP AF ; RECOVER RESULT
HB_EI ; ALLOW INTERRUPTS AGAIN
RET ; AND DONE
;
HB_CKBNKSIZ .EQU $-HB_CKBNK ; SIZE OF ROUTINE
;
HB_ROMCKZ:
;
#ENDIF
#ENDIF
;
; LOW BATTERY DIAGNOSTIC MESSAGE
;
#IF (BATCOND)
LD A,(HB_BATCOND)
OR A
LD DE,STR_LOWBAT
CALL Z,WRITESTR
#ENDIF
;
; PERFORM DEVICE INITIALIZATION
;
CALL NEWLINE
#IF (BT_REC_TYPE != BT_REC_NONE)
LD A,(HB_BOOT_REC) ; IF WE ARE IN RECOVERY MODE
OR A ; POINT TO THE RECOVER MODE
JR Z,NOT_REC_M1 ; INITIALIZATION TABLE
LD B,HB_INITRLEN
LD DE,HB_INIT_REC
JR IS_REC_M1
#ENDIF
NOT_REC_M1:
LD B,HB_INITTBLLEN
LD DE,HB_INITTBL
IS_REC_M1:
CALL CALLLIST
;
; IF WATCHDOG FUNCTIONALITY IS REQUESTED, CHECK TO MAKE SURE
; WE ARE GETTING INTERRUPTS. IF SO, ENABLE THE WATCHDOG.
;
#IF (WDOGMODE != WDOG_NONE)
CALL NEWLINE
PRTS("WDOG: $")
PRTS("MODE=$")
#IF (WDOGMODE == WDOG_EZZ80)
PRTS("EZZ80$")
#ENDIF
#IF (WDOGMODE == WDOG_SKZ)
PRTS("SKZ$")
#ENDIF
;
PRTS(" IO=0x$")
LD A,WDOGIO
CALL PRTHEXBYTE
;
#IF (WDOGMODE == WDOG_SKZ)
; SKZ WATCHDOG IS DISABLED EARLY IN BOOT PROCESS
; HERE, WE ONLY NEED TO ENABLE IT, IF APPROPRIATE
LD HL,(HB_TICKS) ; GET LOW WORD
LD A,H ; CHECK FOR
OR L ; ... ZERO
JR Z,HB_WDOFF ; SKIP IF NOT TICKING
IN A,($6D) ; GET PORT VALUE
SET 5,A ; SET THE WATCHDOG ENABLE BIT
OUT ($6D),A ; ACTIVATE WATCHDOG
#ENDIF
;
PRTS(" ENABLED$")
JR HB_WDZ
;
HB_WDOFF:
PRTS(" DISABLED$")
;
HB_WDZ:
;
#ENDIF
;
; RECORD HEAP CURB AT THE CURRENT VALUE OF HEAP TOP. HEAP CURB
; MARKS THE POINT IN THE HEAP AFTER WHICH MEMORY IS RELEASED
; WHEN AN HBIOS RESET IS PEFORMED.
;
LD HL,(CB_HEAPTOP)
LD (HEAPCURB),HL
;
; NOW SWITCH TO CRT CONSOLE IF CONFIGURED
;
#IF CRTACT
;
; BIOS IS CONFIGURED TO AUTO ACTIVATE CRT DEVICE. FIRST,
; CHECK TO SEE IF WE HAVE A VALID CRT DEVICE TO USE.
;
LD A,(CB_CRTDEV) ; GET THE CRT DEVICE
INC A ; INCREMENT TO TEST FOR $FF
JR Z,INITSYS3 ; IF NO CRT DEVICE, BYPASS CONSOLE SWITCH
;
; IF PLATFORM HAS A CONFIG JUMPER, CHECK TO SEE IF IT IS JUMPERED.
; IF SO, BYPASS SWITCH TO CRT CONSOLE (FAILSAFE MODE)
;
#IF ((PLATFORM == PLT_SBC) | (PLATFORM == PLT_ZETA) | (PLATFORM == PLT_ZETA2) | (PLATFORM == PLT_MBC))
IN A,(RTCIO) ; RTC PORT, BIT 6 HAS STATE OF CONFIG JUMPER
BIT 6,A ; BIT 6 HAS CONFIG JUMPER STATE
JR Z,INITSYS3 ; Z=SHORTED, BYPASS CONSOLE SWITCH
#ENDIF
;
; NOTIFY USER OF CONSOLE SWITCH ON BOOT CONSOLE
CALL NEWLINE2
PRTX(STR_SWITCH)
CALL NEWLINE
;
; SWITCH TO CRT CONSOLE
LD A,(CB_CRTDEV) ; GET CRT DISPLAY DEVICE
LD (CB_CONDEV),A ; SAVE IT AS ACTIVE CONSOLE DEVICE
;
; DISPLAY HBIOS BANNER ON NEW CONSOLE
PRTX(STR_BANNER)
#ENDIF
;
INITSYS3:
CALL PRTSUM ; PRINT UNIT/DEVICE SUMMARY TABLE
;
#IF 0
CALL NEWLINE
CALL NEWLINE
CALL NEWLINE
; SRC & DEST BELOW BND
CALL NEWLINE
LD HL,$4000
LD DE,$5000
LD BC,$3000
CALL NEWLINE
CALL REGDMP
CALL Z280_BNKCPYX
; SRC & DEST ABOVE BND
CALL NEWLINE
LD HL,$8000
LD DE,$9000
LD BC,$1000
CALL NEWLINE
CALL REGDMP
CALL Z280_BNKCPYX
; SRC CROSSOVER
CALL NEWLINE
LD HL,$7000
LD DE,$9000
LD BC,$2000
CALL NEWLINE
CALL REGDMP
CALL Z280_BNKCPYX
; DEST CROSSOVER
CALL NEWLINE
LD HL,$9000
LD DE,$7000
LD BC,$2000
CALL NEWLINE
CALL REGDMP
CALL Z280_BNKCPYX
; DOUBLE CROSSOVER
CALL NEWLINE
LD HL,$7800
LD DE,$7000
LD BC,$2000
CALL NEWLINE
CALL REGDMP
CALL Z280_BNKCPYX
; DOUBLE CROSSOVER SINGLE BYTES
CALL NEWLINE
LD HL,$7FFE
LD DE,$7FFF
LD BC,$0500
CALL NEWLINE
CALL REGDMP
CALL Z280_BNKCPYX
CALL NEWLINE
CALL NEWLINE
CALL NEWLINE
;
#ENDIF
;
INITSYS4:
;
#IF (MEMMGR == MM_Z280)
; LEAVE SYSTEM MODE STACK POINTING TO AN OK PLACE
LD SP,HB_STACK ; NOW USE REAL SYSTEM STACK LOC
;
HB_DI ; NOT SURE THIS IS NEEDED
;
; ACTIVATE THE CORRECT USER MODE BANK
LD A,(HB_CURBNK) ; GET CURRENT BANK
CALL HBX_BNKSEL
;
; PRESET THE USER MODE STACK
LD HL,HBX_LOC
LDCTL USP,HL
;
HB_EI ; NOT SURE THIS IS NEEDED
;
; SWITCH TO USER MODE NOW
LD C,Z280_MSR
LD HL,$407F
LDCTL (C),HL
#ENDIF
;
#IFNDEF ROMBOOT
;
; COPY OS IMAGE: BID_USR:<IMG START> --> BID_USR:0
LD B,BF_SYSSETCPY ; HBIOS FUNC: SETUP BANK COPY
LD D,BID_USR ; D = DEST BANK = USER BANK
;LD E,BID_USR ; E = SRC BANK = USER BANK
LD A,(HB_APPBNK) ; GET APP LOAD BANK
LD E,A ; USE AS SOURCE
LD HL,$8000 ; HL = COPY LEN = ENTIRE BANK
RST 08 ; DO IT
LD B,BF_SYSBNKCPY ; HBIOS FUNC: PERFORM BANK COPY
LD HL,HB_END ; COPY FROM END OF HBIOS
LD DE,0 ; TO USER ADDRESS 0
RST 08 ; DO IT
;
#ENDIF
;
; CHAIN TO LOADER
;
#IFDEF ROMBOOT
#IF (ROMSIZE > 0)
LD A,BID_IMG0 ; CHAIN TO OS IMAGES BANK
#ELSE
LD A,BID_USR ; CHAIN TO USER BANK
#ENDIF
#ELSE
LD A,BID_USR ; CHAIN TO USER BANK
#ENDIF
LD IX,0 ; ENTER AT ADDRESS 0
CALL HBX_BNKCALL ; GO THERE
HALT ; WE SHOULD NEVER COME BACK!
;
; CALL A LIST OF ROUTINES POINTED TO BY DE OF LENGTH B.
;
CALLLIST:
LD A,(DE)
LD L,A
INC DE
LD A,(DE)
LD H,A
INC DE
PUSH DE
PUSH BC
CALL JPHL
POP BC
POP DE
DJNZ CALLLIST
CALLDUMMY:
RET
;
;==================================================================================================
; TABLE OF RECOVERY MODE INITIALIZATION ENTRY POINTS
;==================================================================================================
;
; USE "CALLDUMMY" IF NO ENTRY REQUIRED
;
#IF (BT_REC_TYPE != BT_REC_NONE)
;
HB_PCINIT_REC:
#IF ((PLATFORM == PLT_SBC) | (PLATFORM == PLT_MBC))
.DW UART_PREINIT
; .DW CALLDUMMY
#ENDIF
HB_PCINITRLEN .EQU (($ - HB_PCINIT_REC) / 2)
;
HB_INIT_REC:
#IF ((PLATFORM == PLT_SBC) | (PLATFORM == PLT_MBC))
.DW UART_INIT
.DW MD_INIT
.DW PPIDE_INIT
#ENDIF
HB_INITRLEN .EQU (($ - HB_INIT_REC) / 2)
;
#ENDIF
;
;==================================================================================================
; TABLE OF PRE-CONSOLE INITIALIZATION ENTRY POINTS
;==================================================================================================
;
HB_PCINITTBL:
#IF (DSKYENABLE)
.DW DSKY_PREINIT
#ENDIF
#IF (ASCIENABLE)
.DW ASCI_PREINIT
#ENDIF
#IF (Z2UENABLE)
.DW Z2U_PREINIT
#ENDIF
#IF (UARTENABLE)
.DW UART_PREINIT
#ENDIF
#IF (DUARTENABLE)
.DW DUART_PREINIT
#ENDIF
#IF (SIOENABLE)
.DW SIO_PREINIT
#ENDIF
#IF (ACIAENABLE)
.DW ACIA_PREINIT
#ENDIF
#IF (PIOENABLE)
.DW PIO_PREINIT
#ENDIF
#IF CENENABLE)
.DW CEN_PREINIT
#ENDIF
#IF (PIO_4P | PIO_ZP)
.DW PIO_PREINIT
#ENDIF
#IF (UFENABLE)
.DW UF_PREINIT
#ENDIF
#IF (TMSENABLE)
.DW TMS_PREINIT
#ENDIF
HB_PCINITTBLLEN .EQU (($ - HB_PCINITTBL) / 2)
;==================================================================================================
; TABLE OF INITIALIZATION ENTRY POINTS
;==================================================================================================
;
HB_INITTBL:
#IF (KIOENABLE)
.DW KIO_INIT
#ENDIF
#IF (CTCENABLE)
.DW CTC_INIT
#ENDIF
#IF (DSKYENABLE)
.DW DSKY_INIT
#ENDIF
#IF (AY38910ENABLE)
.DW AY38910_INIT ; AUDIBLE INDICATOR OF BOOT START
#ENDIF
#IF (SN76489ENABLE)
.DW SN76489_INIT
#ENDIF
#IF (SPKENABLE)
.DW SP_INIT ; AUDIBLE INDICATOR OF BOOT START
#ENDIF
#IF (ASCIENABLE)
.DW ASCI_INIT
#ENDIF
#IF (Z2UENABLE)
.DW Z2U_INIT
#ENDIF
#IF (UARTENABLE)
.DW UART_INIT
#ENDIF
#IF (DUARTENABLE)
.DW DUART_INIT
#ENDIF
#IF (SIOENABLE)
.DW SIO_INIT
#ENDIF
#IF (ACIAENABLE)
.DW ACIA_INIT
#ENDIF
#IF (DSRTCENABLE)
.DW DSRTC_INIT
#ENDIF
#IF (BQRTCENABLE)
.DW BQRTC_INIT
#ENDIF
#IF (SIMRTCENABLE)
.DW SIMRTC_INIT
#ENDIF
#IF (INTRTCENABLE)
.DW INTRTC_INIT
#ENDIF
#IF (DS7RTCENABLE)
.DW PCF8584_INIT
.DW DS7RTC_INIT
#ENDIF
#IF (RP5RTCENABLE)
.DW RP5RTC_INIT
#ENDIF
#IF (VDUENABLE)
.DW VDU_INIT
#ENDIF
#IF (CVDUENABLE)
.DW CVDU_INIT
#ENDIF
#IF (VGAENABLE)
.DW VGA_INIT
#ENDIF
#IF (NECENABLE)
.DW NEC_INIT
#ENDIF
#IF (TMSENABLE)
.DW TMS_INIT
#ENDIF
;#IF (DSKYENABLE)
; .DW DSKY_INIT
;#ENDIF
#IF (DMAENABLE)
.DW DMA_INIT
#ENDIF
#IF (MDENABLE)
.DW MD_INIT
#ENDIF
#IF (FDENABLE)
.DW FD_INIT
#ENDIF
#IF (RFENABLE)
.DW RF_INIT
#ENDIF
#IF (IDEENABLE)
.DW IDE_INIT
#ENDIF
#IF (PPIDEENABLE)
.DW PPIDE_INIT
#ENDIF
#IF (SDENABLE)
.DW SD_INIT
#ENDIF
#IF (HDSKENABLE)
.DW HDSK_INIT
#ENDIF
#IF (PRPENABLE)
.DW PRP_INIT
#ENDIF
#IF (PPPENABLE)
.DW PPP_INIT
#ENDIF
#IF (PIOENABLE)
.DW PIO_INIT
#ENDIF
#IF (CENENABLE)
.DW CEN_INIT
#ENDIF
#IF (PIO_4P | PIO_ZP)
.DW PIO_INIT
#ENDIF
#IF (UFENABLE)
.DW UF_INIT
#ENDIF
;
HB_INITTBLLEN .EQU (($ - HB_INITTBL) / 2)
;
;==================================================================================================
; IDLE
;==================================================================================================
;
;__________________________________________________________________________________________________
;
IDLE:
PUSH AF
PUSH BC
PUSH DE
PUSH HL
PUSH IY
#IF (FDENABLE)
CALL FD_IDLE
#ENDIF
POP IY
POP HL
POP DE
POP BC
POP AF
RET
;
;==================================================================================================
; BIOS FUNCTION DISPATCHER
;==================================================================================================
;
; MAIN BIOS FUNCTION
; B: FUNCTION
;__________________________________________________________________________________________________
;
HB_DISPATCH:
;
#IF (MEMMGR == MM_Z280)
; FOR Z280 MEMMGR, WE DISPATCH VIA THE Z280 SYSCALL.
; THE SYSCALL MECHANISM WILL CLEAR INTERRUPTS. IN
; GENERAL, INTERRUPTS ARE OK DURING API PROCESSING,
; SO ENABLE THEM HERE.
HB_EI
#ENDIF
;
;
#IF FALSE ; *DEBUG* START
;
CALL HB_DISPATCH1 ; DO THE WORK
;
; CHECK STACK INTEGRITY
PUSH AF
LD A,(HB_STACK - HB_STKSIZ + $08)
CP $FF
CALL SYSCHK
LD A,$FF
LD (HB_STACK - HB_STKSIZ + $08),A
POP AF
RET
HB_DISPATCH1:
;
#ENDIF ; *DEBUG* END
;
LD A,B ; REQUESTED FUNCTION IS IN B
CP BF_CIO + $10 ; $00-$0F: CHARACTER I/O
JP C,CIO_DISPATCH
CP BF_DIO + $10 ; $10-$1F: DISK I/O
JP C,DIO_DISPATCH
CP BF_RTC + $10 ; $20-$2F: REAL TIME CLOCK (RTC)
JP C,RTC_DISPATCH
CP BF_EMU + $10 ; $30-$3F: EMULATION
JR C,HB_DISPERR
CP BF_VDA + $10 ; $40-$4F: VIDEO DISPLAY ADAPTER
JP C,VDA_DISPATCH
CP BF_SND + $08 ; $50-$58: SOUND DRIVERS
JP C,SND_DISPATCH
CP BF_SYS ; SKIP TO BF_SYS VALUE AT $F0
JR C,HB_DISPERR ; ERROR IF LESS THAN BF_SYS
JP SYS_DISPATCH ; OTHERWISE SYS CALL
; FALL THRU
;
HB_DISPERR:
CALL SYSCHK
LD A,ERR_NOFUNC
OR A
RET
;
;==================================================================================================
; CHARACTER I/O DEVICE FUNCTION DISPATCHER
;==================================================================================================
;
; ROUTE CALL TO SPECIFIED CHARACTER I/O DRIVER
; B: FUNCTION
; C: UNIT NUMBER
;
CIO_DISPATCH:
BIT 7,C ; CHECK FOR SPECIAL UNIT CODE
CALL NZ,CIO_SPECIAL ; IF SO, HANDLE IT
PUSH IY ; SAVE INCOMING IY
LD IY,CIO_TBL ; POINT IY TO START OF CIO TABLE
CALL HB_DISPCALL ; GO TO GENERIC API CALL CODE
POP IY ; RESTORE IY
RET ; AND DONE
;
; SPECIAL HANDLING FOR DEDICATED UNIT CODES
;
CIO_SPECIAL:
; FOR NOW, ONLY SPECIAL CODE IS A CONSOLE REQUEST
; SO JUST SWAP IN ACTIVE CONSOLE UNIT
LD A,(CB_CONDEV) ; GET ACTIVE CONSOLE
LD C,A ; OVERLAY UNIT CODE IN C
RET ; AND REJOIN MAIN DISPATCH FLOW
;
; ADD AN ENTRY TO THE CIO UNIT TABLE (SEE HB_ADDENT FOR DETAILS)
;
CIO_ADDENT:
LD HL,CIO_TBL ; POINT TO CIO TABLE
JP HB_ADDENT ; ... AND GO TO COMMON CODE
;
; HBIOS CHARACTER DEVICE UNIT TABLE
;
; TABLE IS BUILT DYNAMICALLY BY EACH DRIVER DURING INITIALIZATION.
; THE TABLE IS PREFIXED BY TWO BYTES. TABLE - 1 CONTAINS THE CURRENT
; NUMBER OF ENTRIES. TABLE - 2 CONTAINS THE MAXIMUM NUMBER OF ENTRIES.
; TABLE - 3 CONTAINS THE NUMBER OF CIO FUNCTION IDS
; EACH ENTRY IS DEFINED AS:
;
; WORD DRIVER FUNCTION TABLE ADDRESS
; WORD UNIT SPECIFIC DATA (TYPICALLY A DEVICE INSTANCE DATA ADDRESS)
;
CIO_FNCNT .EQU 7 ; NUMBER OF CIO FUNCS (FOR RANGE CHECK)
CIO_MAX .EQU 32 ; UP TO 32 UNITS
CIO_SIZ .EQU CIO_MAX * 4 ; EACH ENTRY IS 4 BYTES
;
.DB CIO_FNCNT ; CIO FUNCTION COUNT (FOR RANGE CHECK)
.DB CIO_MAX ; MAX ENTRY COUNT TABLE PREFIX
CIO_CNT .DB 0 ; ENTRY COUNT PREFIX
CIO_TBL .FILL CIO_SIZ,0 ; SPACE FOR ENTRIES
;
;==================================================================================================
; DISK I/O DEVICE FUNCTION DISPATCHER
;==================================================================================================
;
; ROUTE CALL TO SPECIFIED DISK I/O DRIVER
; B: FUNCTION
; C: UNIT NUMBER
;
DIO_DISPATCH:
;
#IF FALSE ; *DEBUG* START
;
; DUMP INCOMING CALL
CALL NEWLINE
PRTS("DIO>$")
CALL REGDMP ; DUMP REGS, NONE DESTROYED
;
; DO THE ACTUAL DISPATCH PROCESSING
CALL DIO_DISPCALL
;
; DUMP CALL RESULTS AND RETURN
CALL NEWLINE
PRTS("DIO<$")
CALL REGDMP ; DUMP REGS, NONE DESTROYED
RET
;
#ENDIF ; *DEBUG* END
;
DIO_DISPCALL:
PUSH IY ; SAVE INCOMING IY
LD IY,DIO_TBL ; POINT IY TO START OF DIO TABLE
CALL HB_DISPCALL ; GO TO GENERIC API CALL CODE
POP IY ; RESTORE IY
RET ; AND DONE
;
; ADD AN ENTRY TO THE DIO UNIT TABLE
;
DIO_ADDENT:
LD HL,DIO_TBL ; POINT TO DIO TABLE
JP HB_ADDENT ; ... AND GO TO COMMON CODE
;
; HBIOS DISK DEVICE UNIT TABLE
;
; TABLE IS BUILT DYNAMICALLY BY EACH DRIVER DURING INITIALIZATION.
; THE TABLE IS PREFIXED BY TWO BYTES. TABLE - 1 CONTAINS THE CURRENT
; NUMBER OF ENTRIES. TABLE - 2 CONTAINS THE MAXIMUM NUMBER OF ENTRIES.
; TABLE - 3 CONTAINS THE NUMBER OF DIO FUNCTION IDS
; EACH ENTRY IS DEFINED AS:
;
; WORD DRIVER FUNCTION TABLE ADDRESS
; WORD UNIT SPECIFIC DATA (TYPICALLY A DEVICE INSTANCE DATA ADDRESS)
;
DIO_FNCNT .EQU 12 ; NUMBER OF DIO FUNCS (FOR RANGE CHECK)
DIO_MAX .EQU 16 ; UP TO 16 UNITS
DIO_SIZ .EQU DIO_MAX * 4 ; EACH ENTRY IS 4 BYTES
;
.DB DIO_FNCNT ; DIO FUNCTION COUNT (FOR RANGE CHECK)
.DB DIO_MAX ; MAX ENTRY COUNT TABLE PREFIX
DIO_CNT .DB 0 ; ENTRY COUNT PREFIX
DIO_TBL .FILL DIO_SIZ,0 ; SPACE FOR ENTRIES
;
;==================================================================================================
; DISK READ HELPER
;==================================================================================================
;
; IMPLEMENTS MULTI SECTOR READS AND I/O TO/FROM
; BANKED RAM VIA BOUNCE BUFFER
;
; ON ENTRY:
; TOS=READ FN ADR
; HL=BUF ADR
; E=SEC COUNT
; D=BUF BANK ID
;
HB_DSKREAD:
;
; THE ACTUAL SECTOR READ FUNCTION ADDRESS IS ON TOS, SAVE IT
EX (SP),HL ; SAVE HL TO TOS, HL := READ FN ADR
LD (HB_DSKFNADR),HL ; IMBED IN CALL OP BELOW
POP HL ; RECOVER HL
;
LD (HB_DSKCMD),BC ; SAVE HBIOS FUNC & UNIT
;
#IF (DIAGENABLE)
; SAVE DISK UNIT NUMBER BIT MASK
LD A,C ; GET DISK UNIT NUMBER
LD B,A ; PUT IN B FOR LOOP COUNTER
INC B ; LOOP ONE EXTRA TIME TO HANDLE UNIT=0
XOR A ; START WITH ACCUM ZERO
SCF ; ... AND CF SET
HB_DSKREAD0:
RLA ; ROTATE BIT
DJNZ HB_DSKREAD0 ; ... UNTIL IN PROPER LOCATION
LD (HB_DSKBIT),A ; SAVE IT FOR DIAGNOSTICS
#ENDIF
;
#IF 1
; CHECK TO SEE IF INTER-BANK I/O NEEDED.
BIT 7,H ; TGT BUF IN UPPER 32K?
JP NZ,HB_DSKIO ; IF SO, NON-BANKED
LD A,D ; GET TGT BANK
CP BID_BIOS ; BIOS BANK?
JP Z,HB_DSKIO ; IF SO, NON-BANKED
#ENDIF
;
#IF 1
; RAM BANK RANGE CHECK
LD A,D ; GET TGT BANK
CP BID_RAMN ; BANK IN RANGE 0-N?
CALL NC,PANIC ; IF >N, PANIC
#ENDIF
;
; SAVE TGT BUF BNK/ADR
LD (HB_IOBUF),HL
LD A,D
LD (HB_IOBNK),A
;
; SETUP READ AND LOOP COUNT
LD B,E ; SEC LOOP COUNTER
LD C,0 ; SEC COMPLETE COUNTER
;
HB_DSKREAD1:
LD HL,HB_WRKBUF ; USE WORK BUF REAL I/O
;
; CALL READ FN
CALL HB_DSKFN ; READ ONE SECTOR
;
; IF FAIL, RETURN ERR
JR NZ,HB_DSKREADX ; BAIL OUT ON ERROR
;
; BNKCPY SEC DATA TO REAL BANK/BUF & INC BUF ADR
PUSH BC ; SAVE COUNTERS
LD A,(HB_IOBNK) ; DEST BANK
LD (HB_DSTBNK),A ; ... TO PROXY
LD A,BID_BIOS ; SRC BANK
LD (HB_SRCBNK),A ; ... TO PROXY
LD BC,512 ; COPY 512 BYTES (1 SEC)
LD DE,(HB_IOBUF) ; TGT BUFFER ADR
LD HL,HB_WRKBUF ; SOURCE BUFFER
CALL HBX_BNKCPY ; DO BANK COPY
LD (HB_IOBUF),DE ; SAVE UPDATED TGT BUF ADR
POP BC ; RESTORE COUNTERS
;
; INC READ COUNT
INC C ; BUMP SEC READ COUNT
DJNZ HB_DSKREAD1 ; LOOP AS NEEDED
XOR A ; SIGNAL SUCCESS
;
HB_DSKREADX:
LD HL,(HB_IOBUF) ; NEXT BUF ADR
JR HB_DSKIOX ; DONE
;
;==================================================================================================
; DISK WRITE HELPER
;==================================================================================================
;
; IMPLEMENTS MULTI SECTOR WRITES AND I/O TO/FROM
; BANKED RAM VIA BOUNCE BUFFER
;
; TOS=WRITE FN ADR
; HL=BUF ADR
; E=SEC COUNT
; D=BUF BANK ID
;
HB_DSKWRITE:
;
; THE ACTUAL SECTOR READ FUNCTION ADDRESS IS ON TOS, SAVE IT
EX (SP),HL ; SAVE HL TO TOS, HL := READ FN ADR
LD (HB_DSKFNADR),HL ; IMBED IN CALL OP BELOW
POP HL ; RECOVER HL
;
LD (HB_DSKCMD),BC ; SAVE HBIOS FUNC & UNIT
;
#IF (DIAGENABLE)
; SAVE DISK UNIT NUMBER BIT MASK
LD A,C ; GET DISK UNIT NUMBER
LD B,A ; PUT IN B FOR LOOP COUNTER
INC B ; LOOP ONE EXTRA TIME TO HANDLE UNIT=0
XOR A ; START WITH ACCUM ZERO
SCF ; ... AND CF SET
HB_DSKWRITE0:
RLA ; ROTATE BIT
DJNZ HB_DSKWRITE0 ; ... UNTIL IN PROPER LOCATION
LD (HB_DSKBIT),A ; SAVE IT FOR DIAGNOSTICS
#ENDIF
;
#IF 1
; CHECK TO SEE IF INTER-BANK I/O NEEDED.
BIT 7,H ; TGT BUF IN UPPER 32K?
JP NZ,HB_DSKIO ; IF SO, NON-BANKED
LD A,D ; GET TGT BANK
CP BID_BIOS ; BIOS BANK?
JP Z,HB_DSKIO ; IF SO, NON-BANKED
#ENDIF
;
#IF 1
; RAM BANK RANGE CHECK
LD A,D ; GET TGT BANK
CP BID_RAMN ; BANK IN RANGE 0-N?
CALL NC,PANIC ; IF >N, PANIC
#ENDIF
;
; SAVE TGT BUF BNK/ADR
LD (HB_IOBUF),HL
LD A,D
LD (HB_IOBNK),A
;
; SETUP WRITE AND LOOP COUNT
LD B,E ; SEC LOOP COUNTER
LD C,0 ; SEC COMPLETE COUNTER
;
HB_DSKWRITE1:
; BNKCPY SEC DATA TO WORK BANK/BUF & INC BUF ADR
PUSH BC ; SAVE COUNTERS
LD A,BID_BIOS ; DEST BANK
LD (HB_DSTBNK),A ; ... TO PROXY
LD A,(HB_IOBNK) ; SRC BANK
LD (HB_SRCBNK),A ; ... TO PROXY
LD BC,512 ; COPY 512 BYTES (1 SEC)
LD DE,HB_WRKBUF ; TGT BUFFER ADR
LD HL,(HB_IOBUF) ; SOURCE BUFFER
CALL HBX_BNKCPY ; DO BANK COPY
LD (HB_IOBUF),HL ; SAVE UPDATED SRC BUF ADR
POP BC ; RESTORE COUNTERS
;
; CALL WRITE FN
LD HL,HB_WRKBUF ; WRITE FROM WORK BUFFER
CALL HB_DSKFN ; WRITE ONE SECTOR
;
; IF FAIL, RETURN ERR
JR NZ,HB_DSKWRITEX ; BAIL OUT ON ERROR
;
; INC WRITE COUNT
INC C ; BUMP SEC WRITE COUNT
DJNZ HB_DSKWRITE1 ; LOOP AS NEEDED
XOR A ; SIGNAL SUCCESS
;
HB_DSKWRITEX:
LD HL,(HB_IOBUF) ; NEXT BUF ADR
JR HB_DSKIOX ; DONE
;
;==================================================================================================
; NON-BANKED DISK READ/WRITE
;==================================================================================================
;
HB_DSKIO:
;
; SETUP LOOP COUNT
LD B,E ; SEC LOOP COUNTER
LD C,0 ; SEC COMPLETE COUNTER
;
HB_DSKIO1:
; CALL READ/WRITE FN
CALL HB_DSKFN ; READ/WRITE ONE SECTOR
;
; IF FAIL, RETURN ERR
JR NZ,HB_DSKIOX ; BAIL OUT ON ERROR
;
; INC SECTOR COUNT
INC C ; BUMP SEC READ/WRITE COUNT
DJNZ HB_DSKIO1 ; LOOP AS NEEDED
XOR A ; SIGNAL SUCCESS
;
HB_DSKIOX:
LD E,C ; WRITE COUNT TO E
OR A ; SET RESULT FLAGS
RET ; DONE
;
HB_DSKFN:
PUSH BC ; SAVE COUNTERS
#IF (DIAGENABLE & DIAGDISKIO)
LD A,(HB_DSKBIT) ; LOAD UNIT DISK BIT MASK
OUT (DIAGPORT),A ; DISPLAY ON DIAG LEDS
#ENDIF
#IF (LEDENABLE & LEDDISKIO)
LED(%00000001)
#ENDIF
LD E,1 ; ONE SECTOR
HB_DSKFNADR .EQU $+1
CALL PANIC ; READ ONE SECTOR
#IF (DIAGENABLE & DIAGDISKIO)
DIAG(0) ; CLEAR DIAG LEDS
#ENDIF
#IF (LEDENABLE & LEDDISKIO)
LED($00)
#ENDIF
POP BC ; RESTORE COUNTERS
RET ; RETURN
;
HB_DSKBIT .DB 0 ; ACTIVE DISK UNIT
HB_IOBUF .DW 0 ; CURRENT IO BUFFER ADR
HB_IOBNK .DB 0 ; CURRENT IO BUFFER BANK ID
HB_DSKCMD:
HB_DSKUNIT .DB 0 ; CURRENT DISK UNIT
HB_DSKFUNC .DB 0 ; CURRENT DISK FUNCTION
;
#IF (DSKYENABLE)
;
;==================================================================================================
; DSKY DISK ACTIVITY MONITOR
;==================================================================================================
;
; THIS FUNCTION IS CALLED BY DISK DRIVERS JUST PRIOR TO
; THE START OF A DISK I/O OPERATION.
;
; THE CURRENT DISK UNIT NUMBER WILL BE DISPLAYED IN THE FIRST
; 2 SEG DISPLAYS. THE LOWER 24 BITS OF THE SECTOR WILL BE
; DISPLAYED IN THE LAST 6 SEG DISPLAYS.
;
; A DOT IS DISPLAYED TO SEPARATE THE UNIT NUMBER FROM THE ADDRESS
; DISPLAY. ALSO, A TRAILING DOT IS DISPLAYED IF THE I/O FUNCTION
; IS A WRITE.
;
; HL: ADDRESS OF 32-BIT SECTOR NUMBER (LITTLE-ENDIAN)
; ALL REGISTERS PERSERVED
;
HB_DSKACT:
PUSH AF
PUSH BC
PUSH DE
PUSH HL
LD DE,DSKY_HEXBUF+3 ; START AT END
LD B,3 ; 3 BYTES OF SECTOR ADDRESS
HB_DSKACT1:
LD A,(HL) ; GET FIRST BYTE
LD (DE),A ; AND STORE IN BUF
INC HL ; NEXT SRC BYTE
DEC DE ; NEXT DEST BYTE
DJNZ HB_DSKACT1 ; LOOP
LD A,(HB_DSKUNIT) ; GET DISK UNIT NUM
LD (DE),A ; PUT AT HEAD OF BUF
HB_DSKACT2:
LD HL,DSKY_HEXBUF ; BINARY BUF
LD DE,DSKY_BUF ; DISPLAY BUF
CALL DSKY_BIN2SEG ; CONVERT TO SEG DISPLAY BUF
LD A,(DSKY_BUF+1) ; SECOND SEGMENT
OR %10000000 ; TURN ON DOT
LD (DSKY_BUF+1),A ; SAVE IT
LD A,(HB_DSKFUNC) ; GET CURRENT I/O FUNCTION
CP BF_DIOWRITE ; IS IT A WRITE?
JR NZ,HB_DSKACT3 ; IF NOT, NO DOT, SKIP AHEAD
LD A,(DSKY_BUF+7) ; LAST SEGMENT
OR %10000000 ; TURN ON DOT
LD (DSKY_BUF+7),A ; SAVE IT
HB_DSKACT3:
EX DE,HL ; SEG DISPLAY BUF TO HL
CALL DSKY_SHOW ; DISPLAY ON DSKY
POP HL
POP DE
POP BC
POP AF
RET
;
; THIS IS THE CHS VARIANT OF THE ABOVE. THIS IS USED BY CHS ORIENTED
; DISK DRIVERS (BASICALLY JUST FLOPPY).
;
; THE CURRENT DISK UNIT NUMBER WILL BE DISPLAYED IN THE FIRST
; 2 SEG DISPLAYS. THE TRACK, HEAD, AND SECTOR WILL BE DISPLAYED IN
; THE LAST 6 SEG DISPLAYS
;
; HL: ADDRESS OF CYL,HD,SEC IN THE FORMAT CCSH
; ALL REGISTERS PRESERVED
;
HB_DSKACTCHS:
PUSH AF
PUSH BC
PUSH DE
PUSH HL
LD DE,DSKY_HEXBUF ; START OF HEX BUF
LD A,(HB_DSKUNIT) ; GET DISK UNIT NUM
LD (DE),A ; PUT AT HEAD OF BUF
INC DE ; NEXT BYTE OF BUF
LD A,(HL) ; LSB OF TRACK
LD (DE),A ; ADD TO BUF
INC DE ; NEXT BYTE OF BUF
INC HL ; BUMP TO HEAD
INC HL ; "
INC HL ; "
LD A,(HL) ; GET HEAD
LD (DE),A ; ADD TO BUF
INC DE ; NEXT BYTE OF BUF
DEC HL ; BACK TO SECTOR
LD A,(HL) ; GET SECTOR
LD (DE),A ; ADD TO BUF
JR HB_DSKACT2
;
#ENDIF
;
;==================================================================================================
; REAL TIME CLOCK DEVICE DISPATCHER
;==================================================================================================
;
; ROUTE CALL TO REAL TIME CLOCK DRIVER
; B: FUNCTION
;
RTC_DISPATCH:
PUSH HL ; SAVE INCOMING HL
LD HL,(RTC_DISPADR) ;
EX (SP),HL
RET
;
RTC_DISPERR:
CALL SYSCHK
LD A,ERR_NOHW
OR A
RET
;
; SET RTC DISPATCH ADDRESS, USED BY RTC DRIVERS DURING INIT
; BC HAS ADDRESS OF DISPATCH ADDRESS
; WILL ONLY SAVE THE FIRST ADDRESS SET
;
RTC_SETDISP:
LD (RTC_DISPADR),BC ; SAVE THE ADDRESS
OR $FF ; FLAG ACTIVE VALUE
LD (RTC_DISPACT),A ; SAVE IT
RET ; AND DONE
;
;
;
RTC_DISPADR .DW RTC_DISPERR ; RTC DISPATCH ADDRESS
RTC_DISPACT .DB 0 ; SET WHEN DISPADR SET
;
;==================================================================================================
; VIDEO DISPLAY ADAPTER DEVICE DISPATCHER
;==================================================================================================
;
; ROUTE CALL TO SPECIFIED VDA DEVICE DRIVER
; B: FUNCTION
; C: UNIT NUMBER
;
VDA_DISPATCH:
PUSH IY ; SAVE INCOMING IY
LD IY,VDA_TBL ; POINT IY TO START OF DIO TABLE
CALL HB_DISPCALL ; GO TO GENERIC API CALL CODE
POP IY ; RESTORE IY
RET ; AND DONE
;
; ADD AN ENTRY TO THE VDA UNIT TABLE (SEE HB_ADDENT FOR DETAILS)
;
VDA_ADDENT:
LD HL,VDA_TBL ; POINT TO VDA TABLE
JP HB_ADDENT ; ... AND GO TO COMMON CODE
;
; HBIOS VIDEO DEVICE UNIT TABLE
;
; TABLE IS BUILT DYNAMICALLY BY EACH DRIVER DURING INITIALIZATION.
; THE TABLE IS PREFIXED BY TWO BYTES. TABLE - 1 CONTAINS THE CURRENT
; NUMBER OF ENTRIES. TABLE - 2 CONTAINS THE MAXIMUM NUMBER OF ENTRIES.
; TABLE - 3 CONTAINS THE NUMBER OF CIO FUNCTION IDS
; EACH ENTRY IS DEFINED AS:
;
; WORD DRIVER FUNCTION TABLE ADDRESS
; WORD UNIT SPECIFIC DATA (TYPICALLY A DEVICE INSTANCE DATA ADDRESS)
;
VDA_FNCNT .EQU 16 ; NUMBER OF VDA FUNCS (FOR RANGE CHECK)
VDA_MAX .EQU 16 ; UP TO 16 UNITS
VDA_SIZ .EQU VDA_MAX * 4 ; EACH ENTRY IS 4 BYTES
;
.DB VDA_FNCNT ; VDA FUNCTION COUNT (FOR RANGE CHECK)
.DB VDA_MAX ; MAX ENTRY COUNT TABLE PREFIX
VDA_CNT .DB 0 ; ENTRY COUNT PREFIX
VDA_TBL .FILL VDA_SIZ,0 ; SPACE FOR ENTRIES
;
;
;==================================================================================================
; SOUND ADAPTER DEVICE DISPATCHER
;==================================================================================================
;
; ROUTE CALL TO SPECIFIED SOUND DEVICE DRIVER
; B: FUNCTION
; C: UNIT NUMBER
;
SND_DISPATCH:
PUSH IY ; SAVE INCOMING IY
LD IY, SND_TBL ; POINT IY TO START OF DIO TABLE
CALL HB_DISPCALL ; GO TO GENERIC API CALL CODE
POP IY ; RESTORE IY
RET ; AND DONE
;
; ADD AN ENTRY TO THE SND UNIT TABLE (SEE HB_ADDENT FOR DETAILS)
;
SND_ADDENT:
LD HL, SND_TBL ; POINT TO SND TABLE
JP HB_ADDENT ; ... AND GO TO COMMON CODE
;
; HBIOS VIDEO DEVICE UNIT TABLE
;
; TABLE IS BUILT DYNAMICALLY BY EACH DRIVER DURING INITIALIZATION.
; THE TABLE IS PREFIXED BY TWO BYTES. TABLE - 1 CONTAINS THE CURRENT
; NUMBER OF ENTRIES. TABLE - 2 CONTAINS THE MAXIMUM NUMBER OF ENTRIES.
; TABLE - 3 CONTAINS THE NUMBER OF SND FUNCTION IDS
; EACH ENTRY IS DEFINED AS:
;
; WORD DRIVER FUNCTION TABLE ADDRESS
; WORD UNIT SPECIFIC DATA (TYPICALLY A DEVICE INSTANCE DATA ADDRESS)
;
SND_FNCNT .EQU 8 ; NUMBER OF SND FUNCS (FOR RANGE CHECK)
SND_MAX .EQU 3 ; UP TO 2 UNITS
SND_SIZ .EQU SND_MAX * 4 ; EACH ENTRY IS 4 BYTES
;
.DB SND_FNCNT ; SND FUNCTION COUNT (FOR RANGE CHECK)
.DB SND_MAX ; MAX ENTRY COUNT TABLE PREFIX
SND_CNT .DB 0 ; ENTRY COUNT PREFIX
SND_TBL .FILL SND_SIZ,0 ; SPACE FOR ENTRIES
;
;==================================================================================================
; SYSTEM FUNCTION DISPATCHER
;==================================================================================================
;
; B: FUNCTION
;
SYS_DISPATCH:
LD A,B ; GET REQUESTED FUNCTION
AND $0F ; ISOLATE SUB-FUNCTION
JP Z,SYS_RESET ; $F0
DEC A
JP Z,SYS_VER ; $F1
DEC A
JP Z,SYS_SETBNK ; $F2
DEC A
JP Z,SYS_GETBNK ; $F3
DEC A
JP Z,SYS_SETCPY ; $F4
DEC A
JP Z,SYS_BNKCPY ; $F5
DEC A
JP Z,SYS_ALLOC ; $F6
DEC A
JP Z,SYS_FREE ; $F7
DEC A
JP Z,SYS_GET ; $F8
DEC A
JP Z,SYS_SET ; $F9
DEC A
JP Z,SYS_PEEK ; $FA
DEC A
JP Z,SYS_POKE ; $FB
DEC A
JP Z,SYS_INT ; $FC
DEC A
;
; RESTART SYSTEM
; SUBFUNCTION IN C
;
SYS_RESET:
LD A,C ; GET REQUESTED SUB-FUNCTION
CP BF_SYSRES_INT
JR Z,SYS_RESINT
CP BF_SYSRES_WARM
JR Z,SYS_RESWARM
CP BF_SYSRES_COLD
JR Z,SYS_RESCOLD
CP BF_SYSRES_USER
JR Z,SYS_RESUSER
CALL SYSCHK
LD A,ERR_NOFUNC
OR A ; SIGNAL ERROR
RET
;
; SOFT RESET HBIOS, RELEASE HEAP MEMORY NOT USED BY HBIOS
;
SYS_RESINT:
;
; RESET THE HEAP
LD HL,(HEAPCURB) ; GET HBIOS HEAP THRESHOLD
LD (CB_HEAPTOP),HL ; RESTORE HEAP TOP
;;
; ; MAKE SURE THE PROPER RESET VECTOR IS AT ADDRESS $0000
; LD HL,$0040 ; USER RESET CODE STUB
; LD ($0001),HL ; OPERAND OF JP AT $0000
;
XOR A
RET
;
; GO BACK TO ROM BOOT LOADER
;
SYS_RESWARM:
;
#IF (ROMSIZE == 0)
JR SYS_RESCOLD
#ENDIF
;
CALL SYS_RESINT
;
#IF (MEMMGR == MM_Z280)
JP INITSYS4
#ELSE
; PERFORM BANK CALL TO OS IMAGES BANK IN ROM
LD SP,HBX_LOC ; STACK JUST BELOW HBIOS PROXY
LD A,BID_IMG0 ; CHAIN TO OS IMAGES BANK
LD IX,0 ; ENTER AT ADDRESS 0
CALL HBX_BNKCALL ; GO THERE
HALT ; WE SHOULD NEVER COME BACK!
#ENDIF
;
; RESTART SYSTEM AS THOUGH POWER HAD JUST BEEN TURNED ON
;
SYS_RESCOLD:
;
#IF (ROMSIZE == 0)
LD DE,STR_RESTART
CALL Z,WRITESTR
DI
HALT
#ENDIF
;
#IF (MEMMGR == MM_Z280)
JP Z280_RESTART
#ELSE
DI
LD SP,HBX_LOC ; STACK JUST BELOW HBIOS PROXY
LD A,BID_BOOT ; BOOT BANK
LD IX,0 ; ADDRESS ZERO
CALL HB_BNKCALL ; DOES NOT RETURN
#ENDIF
;
; HOOK CALLED WHEN A USERLAND RESET IS INVOKED, TYPICALLY VIA A JUMP
; TO LOGICAL CPU ADDRESS $0000
;
; CREDIT TO PHILLIP STEVENS FOR SUGGESTING AND SIGNIFICANT CONTRIBUTIONS
; TO THE Z180 INVALID OPCODE TRAP ENHANCEMENT.
;
SYS_RESUSER:
;
#IF (CPUFAM == CPU_Z180)
;
IN0 A,(Z180_ITC) ; GET ITC REGISTER
XOR $80 ; PRECLEAR TRAP BIT
RET M ; IF TRAP BIT NOT SET, DONE
;
; HANDLE INVALID OPCODE
DEC HL ; BACK UP TO OPCODE START
BIT 6,A ; CHECK UFO BIT (2 BYTE OPCODE)
JR Z,SYS_RESUSER1 ; IF NOT, ALL SET
DEC HL ; OTHERWISE, BACK UP 1 MORE BYTE
;
SYS_RESUSER1:
OUT0 (Z180_ITC),A ; SAVE IT
;
CALL PRTSTRD ; PRINT ERROR TAG
.TEXT "\r\n\r\n+++ INVALID Z180 OPCODE @$"
CALL PRTHEXWORDHL ; PRINT OPCODE ADDRESS
PRTS("H:$") ; FORMATTING
;
LD B,8 ; SHOW 8 BYTES
SYS_RESUSER2:
PUSH BC ; SAVE BC
PUSH HL ; SAVE HL
LD A,(HB_INVBNK) ; GET BYTE FROM INVOKING BANK
LD D,A ; PUT IN D
CALL SYS_PEEK ; PEEK TO GET BYTE VALUE
LD A,E ; PUT IN A
CALL PC_SPACE ; FORMATTING
CALL PRTHEXBYTE ; DISPLAY BYTE
POP HL ; RECOVER HL
POP BC ; RECOVER BC
INC HL ; NEXT BYTE
DJNZ SYS_RESUSER2 ; LOOP TIL DONE
JP NEWLINE ; FORMATTING & EXIT
;
#ENDIF
;
RET ; ELSE RETURN WITH USER RESET VECTOR IN HL
;
; GET THE CURRENT HBIOS VERSION
; ON INPUT, C=0
; RETURNS VERSION IN DE AS BCD
; D: MAJOR VERION IN TOP 4 BITS, MINOR VERSION IN LOW 4 BITS
; E: UPDATE VERION IN TOP 4 BITS, PATCH VERSION IN LOW 4 BITS
; L: PLATFORM ID
;
SYS_VER:
LD DE,0 | (RMJ << 12) | (RMN << 8) | (RUP << 4) | RTP
LD L,PLATFORM
XOR A
RET
;
; SET ACTIVE MEMORY BANK AND RETURN PREVIOUSLY ACTIVE MEMORY BANK
; NOTE THAT IT GOES INTO EFFECT AS HBIOS FUNCTION IS EXITED
; HERE, WE JUST SET THE CURRENT BANK
; CALLER MUST ESTABLISH UPPER MEMORY STACK BEFORE INVOKING THIS FUNCTION!
;
SYS_SETBNK:
#IF (MEMMGR == MM_Z280)
; FOR Z280 MEMMGR, WE ARE IN SYSTEM MODE HERE, SO WE CAN UPDATE
; THE USER MODE BANK WITHOUT IMPACTING THE RUNNING CODE. IT
; TAKE EFFECT UPON RETURN TO USER MODE.
LD A,(HB_INVBNK) ; GET PREVIOUS BANK
PUSH AF ; SAVE IT
LD A,C ; NEW BANK TO A
LD (HB_INVBNK),A ; UPDATE INVBNK
LD B,$00 ; FIRST USER PDR
CALL Z280_BNKSEL ; DO IT
POP AF ; RECOVER PREV BANK
LD C,A ; PREVIOUS BANK TO C
XOR A ; SIGNAL SUCCESS
RET ; DONE
#ELSE
PUSH HL ; SAVE INCOMING HL
LD HL,HB_INVBNK ; POINT TO HBIOS INVOKE BANK ID ADDRESS
LD A,(HL) ; GET EXISTING BANK ID TO A
LD (HL),C ; UPDATE INVOKE BANK TO NEW BANK ID
LD C,A ; PUT PREVIOUS BANK ID IN C FOR RETURN
POP HL ; RESTORE ORIGINAL HL
XOR A ; SIGNAL SUCCESS
RET ; DONE
#ENDIF
;
; GET ACTIVE MEMORY BANK
;
SYS_GETBNK:
LD A,(HB_INVBNK) ; GET THE ACTIVE MEMORY BANK
LD C,A ; MOVE TO C
XOR A ; SIGNAL SUCCESS
RET
;
; SET BANKS AND LENGTH FOR INTERBANK MEMORY COPY (BNKCPY)
; ENTRY: E=SOURCE BANK ID
; D=DEST BANK ID
; HL=COPY LENGTH (IN BYTES)
;
SYS_SETCPY:
LD A,E
LD (HB_SRCBNK),A ; RECORD THE SOURCE BANK
LD A,D
LD (HB_DSTBNK),A ; RECORD THE DESTINATION BANK
LD (HB_CPYLEN),HL ; RECORD THE COPY LENGTH
XOR A
RET
;
; PERFORM MEMORY COPY POTENTIALLY ACROSS BANKS
; ENTRY: HL=SOURCE ADDRESS
; DE=DESTINATION ADDRESS
; NOTE: SRC/DEST BANK & COPY LENGTH MUST BE SET VIA SETCPY
;
SYS_BNKCPY:
PUSH HL ; SAVE INCOMING HL
LD HL,(HB_CPYLEN) ; HL := COPY LEN (SAVED IN SETCPY)
EX (SP),HL ; RESTORE HL & SET (SP) TO COPY LEN
POP BC ; BC := COPY LEN
CALL HB_BNKCPY
XOR A
RET
;
; ALLOCATE HL BYTES OF MEMORY FROM HBIOS HEAP
; RETURNS POINTER TO ALLOCATED MEMORY IN HL
; ON SUCCESS RETURN A == 0, AND Z SET
; ON FAILURE A <> 0 AND NZ SET AND HL TRASHED
; ALL OTHER REGISTERS PRESERVED
;
SYS_ALLOC:
JP HB_ALLOC
;
; FREE HEAP MEMORY BY SIMPLY RELEASING ALL
; MEMORY BEYOND POINTER IN HL.
; ON SUCCESS RETURN A == 0, AND Z SET
; ON FAILURE A <> 0 AND NZ SET AND HL TRASHED
; ALL OTHER REGISTERS PRESERVED
;
SYS_FREE:
CALL SYSCHK ; NOT YET IMPLEMENTED
LD A,ERR_NOTIMPL ; NOT YET INMPLEMENTED
OR A ; SET FLAGS
RET
;
; GET SYSTEM INFORMATION
; ITEM TO RETURN INDICATED IN C
;
SYS_GET:
LD A,C ; GET REQUESTED SUB-FUNCTION
CP BF_SYSGET_CIOCNT
JP Z,SYS_GETCIOCNT
CP BF_SYSGET_CIOFN
JP Z,SYS_GETCIOFN
CP BF_SYSGET_DIOCNT
JP Z,SYS_GETDIOCNT
CP BF_SYSGET_DIOFN
JP Z,SYS_GETDIOFN
CP BF_SYSGET_RTCCNT
JP Z,SYS_GETRTCCNT
CP BF_SYSGET_VDACNT
JP Z,SYS_GETVDACNT
CP BF_SYSGET_VDAFN
JP Z,SYS_GETVDAFN
CP BF_SYSGET_SNDCNT
JP Z, SYS_GETSNDCNT
CP BF_SYSGET_SNDFN
JP Z,SYS_GETSNDFN
CP BF_SYSGET_TIMER
JP Z,SYS_GETTIMER
CP BF_SYSGET_SECS
JP Z,SYS_GETSECS
CP BF_SYSGET_BOOTINFO
JP Z,SYS_GETBOOTINFO
CP BF_SYSGET_CPUINFO
JP Z,SYS_GETCPUINFO
CP BF_SYSGET_MEMINFO
JP Z,SYS_GETMEMINFO
CP BF_SYSGET_BNKINFO
JP Z,SYS_GETBNKINFO
CP BF_SYSGET_CPUSPD
JP Z,SYS_GETCPUSPD
CALL SYSCHK
LD A,ERR_NOFUNC ; SIGNAL ERROR
OR A ; SET FLAGS
RET
;
; GET TIMER
; RETURNS:
; DE:HL: TIMER VALUE (32 BIT)
;
SYS_GETTIMER:
LD HL,HB_TICKS
HB_DI
CALL LD32
HB_EI
LD C, TICKFREQ
XOR A
RET
;
; GET SECONDS
; RETURNS:
; DE:HL: SECONDS VALUE (32 BIT)
; C: NUM TICKS WITHIN CURRENT SECOND
;
SYS_GETSECS:
LD HL,HB_SECS
HB_DI
CALL LD32
LD A,(HB_SECTCK)
HB_EI
NEG ; CONVERT DOWNCOUNTER TO UPCOUNTER
ADD A,TICKFREQ
LD C,A
XOR A
RET
;
; GET BOOT INFORMATION
; RETURNS:
; L: BOOT BANK ID
; DE: BOOT DISK VOLUME (UNIT/SLICE)
;
SYS_GETBOOTINFO:
LD A,(CB_BOOTBID)
LD L,A
LD DE,(CB_BOOTVOL)
XOR A
RET
;
; GET CPU INFORMATION
; RETURNS:
; H: Z80 CPU VARIANT
; L: CPU SPEED IN MHZ
; DE: CPU SPEED IN KHZ
;
SYS_GETCPUINFO:
LD A,(HB_CPUTYPE)
LD H,A
LD A,(CB_CPUMHZ)
LD L,A
LD DE,(CB_CPUKHZ)
LD BC,(HB_CPUOSC)
XOR A
RET
;
; GET MEMORY INFORMATION
; RETURNS:
; D: COUNT OF ROM BANKS
; E: COUNT OF RAM BANKS
;
SYS_GETMEMINFO:
LD A,(CB_ROMBANKS)
LD D,A
LD A,(CB_RAMBANKS)
LD E,A
XOR A
RET
;
; GET BANK CONFIGURATION INFORMATION
; RETURNS:
; D: HBIOS BANK ID
; E: USER BANK ID
;
SYS_GETBNKINFO:
LD A,(CB_BIDBIOS)
LD D,A
LD A,(CB_BIDUSR)
LD E,A
XOR A
RET
;
; GET SYSTEM CPU SPEED ORMANCE ATTRIBUTES
; RETURNS:
; L: CLOCK MULT (0:HALF, 1:FULL, 2: DOUBLE)
; D: MEMORY WAIT STATES
; E: I/O WAIT STATES
;
SYS_GETCPUSPD:
;
#IF (((PLATFORM == PLT_SBC) | (PLATFORM == PLT_MBC)) & (CPUSPDCAP==SPD_HILO))
LD A,(HB_RTCVAL)
BIT 3,A
#IF (PLATFORM == PLT_SBC)
XOR %00001000 ; SBC SPEED BIT IS INVERTED
#ENDIF
LD L,0 ; ASSUME HALF SPEED
JR Z,SYS_GETCPUSPD1
LD L,1
SYS_GETCPUSPD1:
LD DE,$FFFF ; UNKNOWN WAIT STATES
;
XOR A
RET
#ENDIF
;
#IF (CPUFAM == CPU_Z180)
IN0 A,(Z180_CMR) ; GET CLOCK MULTIPLIER
RLCA ; ROTATE BIT TO BIT 0
AND %00000001 ; ISOLATE IT
LD H,A ; SAVE IN H
IN0 A,(Z180_CCR) ; GET CLOCK CONTROL
RLCA ; ROTATE BIT TO BIT 0
AND %00000001 ; ISOLATE IT
LD L,A ; SAVE IN L
XOR A ; CLEAR ACCUM
ADD A,H ; ADD IN CMR BIT
ADD A,L ; ADD IN CCR BIT
LD L,A ; SAVE RESULT IN L
;
; DCNTL = MMII????
IN0 A,(Z180_DCNTL) ; GET WAIT STATES
RLCA ; ROTATE MEM WS BITS
RLCA ; ... TO LOW BITS
PUSH AF ; SAVE FOR NOW
AND %00000011 ; ISOLATE BITS
LD D,A ; PUT IN D
POP AF ; RECOVER A
RLCA ; ROTATE I/O WS BITS
RLCA ; ... TO LOW BITS
AND %00000011 ; ISOLATE BITS
INC A ; ADD 1 FOR BUILT-IN WS
LD E,A ; PUT IN E
;
XOR A
RET
#ENDIF
;
OR $FF
RET
;
; GET SERIAL UNIT COUNT
;
SYS_GETCIOCNT:
LD A,(CIO_CNT) ; GET DEVICE COUNT (FIRST BYTE OF LIST)
LD E,A ; PUT IT IN E
XOR A ; SIGNALS SUCCESS
RET
;
; GET SERIAL UNIT API FN ADR AND DATA ADR
; ENTRY:
; D: FUNCTION
; E: UNIT
; RETURNS:
; HL: FUNCTION ADDRESS
; DE: DATA BLOB ADDRESS
;
SYS_GETCIOFN:
BIT 7,E ; CHECK FOR SPECIAL UNIT CODE
CALL NZ,SYS_GETCIOFN1 ; IF SO, HANDLE IT
LD IY,CIO_TBL ; POINT TO UNIT TABLE
JP SYS_GETFN ; GO TO COMMON CODE
;
SYS_GETCIOFN1:
LD A,(CB_CONDEV) ; UNIT $80 -> CONSOLE UNIT
LD E,A ; REPLACE UNIT VALUE IN C
RET ; AND BACK TO REGULAR FLOW
;
;
; GET DISK UNIT COUNT
;
SYS_GETDIOCNT:
LD A,(DIO_CNT) ; GET DEVICE COUNT (FIRST BYTE OF LIST)
LD E,A ; PUT IT IN E
XOR A ; SIGNALS SUCCESS
RET
;
; GET DISK UNIT API FN ADR AND DATA ADR
; ENTRY:
; D: FUNCTION
; E: UNIT
; RETURNS:
; HL: FUNCTION ADDRESS
; DE: DATA BLOB ADDRESS
;
SYS_GETDIOFN:
LD IY,DIO_TBL ; POINT TO UNIT TABLE
JP SYS_GETFN ; GO TO COMMON CODE
;
; GET RTC UNIT COUNT
;
SYS_GETRTCCNT:
LD E,0 ; ASSUME 0 RTC DEVICES
LD A,(RTC_DISPACT) ; IS RTC ACTIVE?
OR A ; SET FLAGS
JR Z,SYS_GETRTCCNT1 ; IF NONE, DONE
INC E ; SET ONE DEVICE
SYS_GETRTCCNT1:
XOR A ; SIGNALS SUCCESS
RET
;
; GET VIDEO UNIT COUNT
;
SYS_GETVDACNT:
LD A,(VDA_CNT) ; GET DEVICE COUNT (FIRST BYTE OF LIST)
LD E,A ; PUT IT IN E
XOR A ; SIGNALS SUCCESS
RET
;
; GET VIDEO UNIT API FN ADR AND DATA ADR
; ENTRY:
; D: FUNCTION
; E: UNIT
; RETURNS:
; HL: FUNCTION ADDRESS
; DE: DATA BLOB ADDRESS
;
SYS_GETVDAFN:
LD IY,VDA_TBL ; POINT TO UNIT TABLE
JP SYS_GETFN ; GO TO COMMON CODE
;
; GET SOUND UNIT COUNT
;
SYS_GETSNDCNT:
LD A,(SND_CNT) ; GET DEVICE COUNT (FIRST BYTE OF LIST)
LD E,A ; PUT IT IN E
XOR A ; SIGNALS SUCCESS
RET
;
; GET SOUND UNIT API FN ADR AND DATA ADR
; ENTRY:
; D: FUNCTION
; E: UNIT
; RETURNS:
; HL: FUNCTION ADDRESS
; DE: DATA BLOB ADDRESS
;
SYS_GETSNDFN:
LD IY,SND_TBL ; POINT TO UNIT TABLE
JP SYS_GETFN ; GO TO COMMON CODE
;
; SHARED CODE TO COMPLETE A FUNCTION LOOKUP
; ENTRY:
; IY: DISPATCH FUNCTION TABLE
; D: FUNCTION ID
; E: UNIT NUMBER
; EXIT:
; HL: DRIVER FUNCTION ADDRESS
; DE: DRIVER UNIT DATA ADDRESS
;
SYS_GETFN:
LD A,D ; GET FUNC NUM FROM D
LD B,A ; AND PUT IN B
LD A,E ; GET UNIT NUM FROM E
LD C,A ; AND PUT IN C
CALL HB_DISPCALC ; CALC FN ADR & BLOB ADR
PUSH IY ; MOVE DATA ADR
POP DE ; ... TO DE
RET ; AF STILL HAS RESULT OF CALC
;
; SET SYSTEM PARAMETERS
; PARAMETER(S) TO SET INDICATED IN C
;
SYS_SET:
LD A,C ; GET REQUESTED SUB-FUNCTION
CP BF_SYSSET_TIMER
JR Z,SYS_SETTIMER
CP BF_SYSSET_SECS
JR Z,SYS_SETSECS
CP BF_SYSSET_BOOTINFO
JR Z,SYS_SETBOOTINFO
CP BF_SYSSET_CPUSPD
JR Z,SYS_SETCPUSPD
CALL SYSCHK
LD A,ERR_NOFUNC ; SIGNAL ERROR
OR A ; SET FLAGS
RET
;
; SET BOOT INFORMATION
; ON ENTRY:
; L: BOOT BANK ID
; DE: BOOT DISK VOLUME (UNIT/SLICE)
;
SYS_SETBOOTINFO:
LD A,L
LD (CB_BOOTBID),A
LD (CB_BOOTVOL),DE
XOR A
RET
;
; SET TIMER
; ON ENTRY:
; DE:HL: TIMER VALUE (32 BIT)
;
SYS_SETTIMER:
LD BC,HB_TICKS
HB_DI
CALL ST32
HB_EI
XOR A
RET
;
; SET SECS
; ON ENTRY:
; DE:HL: SECONDS VALUE (32 BIT)
;
SYS_SETSECS:
LD BC,HB_SECS
HB_DI
CALL ST32
HB_EI
XOR A
RET
;
; SET SYSTEM CPU SPEED ATTRIBUTES
; ON ENTRY:
; L: CLOCK MULT (0:HALF, 1:FULL, 2: DOUBLE)
; D: MEMORY WAIT STATES
; E: I/O WAIT STATES
;
SYS_SETCPUSPD:
;
#IF (((PLATFORM == PLT_SBC) | (PLATFORM == PLT_MBC)) & (CPUSPDCAP==SPD_HILO))
;
; NOTE: WAIT STATE SETTINGS ARE IGNORED FOR Z80
;
LD A,L ; CLK SPD TO ACCUM
CP $FF ; NO CHANGE?
JR Z,SYS_SETCPUSPD3 ; DONE IF SO
LD C,%00000000 ; HALF SPEED
CP 0
JR Z,SYS_SETCPUSPD1
LD C,%00001000 ; FULL SPEED
CP 1
JR Z,SYS_SETCPUSPD1
JR SYS_SETCPUSPD_ERR ; SPD NOT SUPPORTED
SYS_SETCPUSPD1:
LD A,(HB_RTCVAL)
AND ~%00001000 ; CLEAR SPEED BIT
OR C ; IMPLEMENT NEW SPEED BIT
#IF (PLATFORM == PLT_SBC)
; SBC SPEED BIT IS INVERTED, ADJUST IT
LD A,C
XOR %00001000
LD C,A
#ENDIF
LD (HB_RTCVAL),A ; SAVE IN SHADOW REGISTER
OUT (RTCIO),A ; UPDATE HARDWARE REGISTER
;
; UPDATE THE CURRENT CPU SPEED IN HCB!
LD A,L
LD HL,(HB_CPUOSC) ; ASSUME FULL SPEED
CP 1 ; CHECK FOR 1 (FULL SPEED)
JR Z,SYS_SETCPUSPD2 ; IF SO, ALL DONE
; ADJUST HL TO REFLECT HALF SPEED OPERATION
SRL H ; ADJUST HL ASSUMING
RR L ; HALF SPEED OPERATION
;
SYS_SETCPUSPD2:
;
; HL SHOULD NOW HAVE FINAL CPU RUNNING SPEED IN KHZ.
; UPDATE CB_CPUMHZ/CB_CPUKHZ WITH THIS VALUE.
;
LD (CB_CPUKHZ),HL ; UPDATE CPUKHZ
LD DE,1000 ; SET UP TO DIV BY 1000 FOR MHZ
CALL DIV16 ; BC=CPU MHZ, HL=REMAINDER
LD DE,500 ; SET UP TO ROUND UP
XOR A ; IF WITHIN 500 KHZ
SBC HL,DE ; REMAINDER - 500
CCF ; COMPLEMENT CF
ADC A,C ; C -> A; ADD CF FOR ROUNDING
LD (CB_CPUMHZ),A ; SAVE IT
;
; REINIT DELAY ROUTINE
LD A,(CB_CPUMHZ) ; CPU SPEED TO ACCUM AND INIT
CALL DELAY_INIT ; .. SPEED COMPENSATED DELAY
;
SYS_SETCPUSPD3:
XOR A
RET
#ENDIF
;
#IF (CPUFAM == CPU_Z180)
PUSH DE ; SAVE WAIT STATES FOR NOW
; BEFORE IMPLEMENTING THE NEW CPU SPEED, WE SWITCH THE
; WAIT STATES TO MAXIMUM BECAUSE WE MAY BE IMPLEMENTING
; SLOWER WAIT STATES REQUIRED BY THE NEW SPEED. WE SAVE
; THE ORIGINAL WAIT STATES REGISTER VALUE ON STACK
IN0 A,(Z180_DCNTL) ; GET CURRENT REGISTER VALUE
LD E,A ; PUT IN E
PUSH DE ; SAVE FOR LATER
OR %11110000 ; MAX WAIT STATES
OUT0 (Z180_DCNTL),A ; DO IT
;
LD A,L ; NEW CLK SPD TO ACCUM
CP $FF ; NO CHANGE?
JR Z,SYS_SETCPUSPD2B ; IF SO, SKIP TO WAIT STATES
;
LD B,0 ; B HAS BIT FOR CMR
LD C,0 ; C HAS BIT FOR CCR
CP 2 ; DOUBLE SPEED?
JR C,SYS_SETCPUSPD1 ; <2?, SKIP AHEAD
LD B,%10000000 ; SET CMR BIT
SYS_SETCPUSPD1:
CP 1 ; FULL SPEED?
JR C,SYS_SETCPUSPD2 ; <1?, SKIP AHEAD
LD C,%10000000 ; SET CCR BIT
SYS_SETCPUSPD2:
;
IN0 A,(Z180_CMR)
AND ~%10000000
OR B
OUT0 (Z180_CMR),A
;
IN0 A,(Z180_CCR)
AND ~%10000000
OR C
OUT0 (Z180_CCR),A
;
; UPDATE THE CURRENT CPU SPEED IN HCB!
LD A,L ; SETTING TO A
LD HL,(HB_CPUOSC) ; START WITH CPU OSC VALUE
; ADJUST HL TO REFLECT HALF SPEED OPERATION
SRL H ; ADJUST HL ASSUMING
RR L ; HALF SPEED OPERATION
OR A ; CHECK FOR HALF SPEED
JR Z,SETCPUSPD2A ; IF SO, DONE
; ADJUST HL TO REFLECT FULL SPEED OPERATION
SLA L
RL H
CP 1 ; CHECK FOR FULL SPEED
JR Z,SETCPUSPD2A ; IF SO DONE
; ADJUST HL TO REFLECT DOUBLE SPEED OPERATION
SLA L
RL H
;
SETCPUSPD2A:
;
; HL SHOULD NOW HAVE FINAL CPU RUNNING SPEED IN KHZ.
; UPDATE CB_CPUMHZ/CB_CPUKHZ WITH THIS VALUE.
;
LD (CB_CPUKHZ),HL ; UPDATE CPUKHZ
LD DE,1000 ; SET UP TO DIV BY 1000 FOR MHZ
CALL DIV16 ; BC=CPU MHZ, HL=REMAINDER
LD DE,500 ; SET UP TO ROUND UP
XOR A ; IF WITHIN 500 KHZ
SBC HL,DE ; REMAINDER - 500
CCF ; COMPLEMENT CF
ADC A,C ; C -> A; ADD CF FOR ROUNDING
LD (CB_CPUMHZ),A ; SAVE IT
;
SYS_SETCPUSPD2B:
; NOW IMPLEMENT ANY WAIT STATE CHANGES.
POP HL ; INIT L WITH ORIG DCNTL VALUE
POP DE ; RECOVER WAIT STATES
LD A,D ; GET MEM WS
CP $FF ; SKIP?
JR Z,SYS_SETCPUSPD3 ; IF SO, GO AHEAD
AND %00000011 ; JUST TWO BITS
RRCA ; MEM WS IS TOP TWO BITS
RRCA
LD H,A ; MOVE WS BITS TO H
LD A,L ; CUR VALUE TO A
AND %00111111 ; MASK OFF MEM WS BITS
OR H ; SET NEW MEM WS BITS
LD L,A ; BACK TO L
;
SYS_SETCPUSPD3:
;
LD A,E ; GET I/O WS
CP $FF ; SKIP?
JR Z,SYS_SETCPUSPD4 ; IF SO, GO AHEAD
DEC A ; ADJUST FOR BUILT-IN I/O WS
AND %00000011 ; JUST TWO BITS
RRCA ; I/O WS IS BITS 5-4
RRCA
RRCA
RRCA
LD H,A ; MOVE WS BITS TO H
LD A,L ; CUR VALUE TO A
AND %11001111 ; MASK OFF I/O WS BITS
OR H ; SET NEW I/O WS BITS
LD L,A ; BACK TO L
;
SYS_SETCPUSPD4:
LD A,L ; WORKING VALUE TO A
OUT0 (Z180_DCNTL),A ; IMPLEMENT NEW VALUE
;
; REINIT DELAY ROUTINE
LD A,(CB_CPUMHZ) ; CPU SPEED TO ACCUM AND INIT
CALL DELAY_INIT ; .. SPEED COMPENSATED DELAY
;
#IF ((INTMODE == 2) & (Z180_TIMER))
; THE Z180 TIMER IS BASED ON CPU SPEED. SO HERE
; WE RECOMPUTE THE TIMER CONSTANTS BASED ON THE NEW SPEED.
XOR A ; ALL BITS ZERO
OUT0 (Z180_TCR),A ; ... INHIBITS TIMER OPERATION
LD HL,(CB_CPUKHZ) ; 50HZ = 18432000 / 20 / 50 / X, SO X = CPU KHZ
OUT0 (Z180_TMDR0L),L ; INITIALIZE TIMER 0 DATA REGISTER
OUT0 (Z180_TMDR0H),H
DEC HL ; RELOAD OCCURS *AFTER* ZERO
OUT0 (Z180_RLDR0L),L ; INITIALIZE TIMER 0 RELOAD REGISTER
OUT0 (Z180_RLDR0H),H
LD A,%00010001 ; ENABLE TIMER0 INT AND DOWN COUNTING
OUT0 (Z180_TCR),A
#ENDIF
;
#IF (ASCIENABLE)
; RESET THE ASCI PORTS IN CASE SPEED CHANGED!
; N.B., THIS WILL FAIL IF THE CURRENT BAUD RATE
; IS IMPOSSIBLE TO IMPLEMENT AT THE NEW CPU SPEED!!!
LD DE,-1
LD IY,ASCI0_CFG
CALL ASCI_INITDEV
LD DE,-1
LD IY,ASCI1_CFG
CALL ASCI_INITDEV
#ENDIF
;
XOR A
RET
#ENDIF
;
SYS_SETCPUSPD_ERR:
OR $FF ; NOT SUPPORTED
RET
;
; RETURN A BYTE OF MEMORY FROM SPECIFIED BANK
; ENTRY: D=BANK ID, HL=ADDRESS
; RETURN: E=BYTE VALUE
;
; IF WE ARE USING INTERRUPT MODE 1, WE NEED TO PREVENT INTERRUPTS
; BECAUSE THE LOW MEMORY BANK CONTAINING THE IM1 VECTOR WILL PROBABLY
; GET BANKED OUT DURING THE PEEK PROCESSING.
;
SYS_PEEK:
#IF (INTMODE == 1)
#IF (CPUFAM == CPU_Z280)
PUSH IY
LD C,Z280_MSR
LDCTL IY,(C)
PUSH IY
HB_DI
#ELSE
LD A,I ; SAVE THE INTERRUPT STATUS
DI ; COPY IFF2 TO P/V FLAG
PUSH AF
#ENDIF
#ENDIF
CALL HBX_PEEK ; IMPLEMENTED IN PROXY
#IF (INTMODE == 1)
#IF (CPUFAM == CPU_Z280)
LD C,Z280_MSR
POP IY
LDCTL (C),IY
POP IY
#ELSE
POP AF ; RECALL INITIAL INTERRUPT STATUS
JP PO,$+4 ; RETURN TO INITIAL STATE
EI ; *** DO NOT USE HB_EI HERE ***
#ENDIF
#ENDIF
XOR A
RET
;
; WRITE A BYTE OF MEMORY TO SPECIFIED BANK
; ENTRY: D=BANK ID, HL=ADDRESS IN HBIOS BANK, E=BYTE VALUE
;
; IF WE ARE USING INTERRUPT MODE 1, WE NEED TO PREVENT INTERRUPTS
; BECAUSE THE LOW MEMORY BANK CONTAINING THE IM1 VECTOR WILL PROBABLY
; GET BANKED OUT DURING THE POKE PROCESSING.
;
SYS_POKE:
#IF (INTMODE == 1)
#IF (CPUFAM == CPU_Z280)
PUSH IY
LD C,Z280_MSR
LDCTL IY,(C)
PUSH IY
HB_DI
#ELSE
LD A,I ; SAVE THE INTERRUPT STATUS
HB_DI ; COPY IFF2 TO P/V FLAG
PUSH AF
#ENDIF
#ENDIF
CALL HBX_POKE ; IMPLEMENTED IN PROXY
#IF (INTMODE == 1)
#IF (CPUFAM == CPU_Z280)
LD C,Z280_MSR
POP IY
LDCTL (C),IY
POP IY
#ELSE
POP AF ; RECALL INITIAL INTERRUPT STATUS
JP PO,$+4 ; RETURN TO INITIAL STATE
EI ; *** DO NOT USE HB_EI HERE ***
#ENDIF
#ENDIF
XOR A
RET
;
; INTERRUPT MANAGEMENT FUNCTIONS
; SUBFUNCTION IN C
;
SYS_INT:
LD A,C ; GET REQUESTED SUB-FUNCTION
CP BF_SYSINT_INFO
JR Z,SYS_INTINFO
CP BF_SYSINT_GET
JR Z,SYS_INTGET
CP BF_SYSINT_SET
JR Z,SYS_INTSET
CALL SYSCHK
LD A,ERR_NOFUNC ; SIGNAL ERROR
OR A ; SET FLAGS
RET
;
; GET INTERRUPT SYSTEM INFORMATION
; RETURN D:=INTERRUPT MODE, E:=INT VEC TABLE SIZE
;
SYS_INTINFO:
LD D,INTMODE ; D := ACTIVE INTERRUPT MODE
#IF (INTMODE == 0)
LD E,0 ; 0 ENTRIES IF INTERRUPTS DISABLED
#ENDIF
#IF (INTMODE == 1)
LD A,(HB_IM1CNT) ; RETURN IM1 CALL LIST SIZE
LD E,A
#ENDIF
#IF (INTMODE == 2)
LD E,HBX_IVTCNT ; RETURN INT VEC TABLE SIZE
#ENDIF
XOR A ; INDICATE SUCCESS
RET ; AND DONE
;
; ROUTINE SHARED BY INT GET/SET. RETURNS ADDRESS OF VECTOR FOR SPECIFIED LIST / TABLE
; POSITION. ZF SET ON RETURN FOR SUCCESS, ELSE ERROR.
;
SYS_INTVECADR:
#IF (INTMODE == 0)
CALL SYSCHK ; INVALID FOR INT MODE 0
LD A,ERR_BADCFG ; SIGNAL ERROR
OR A ; SET FLAGS
RET
#ENDIF
#IF (INTMODE == 1)
LD A,(HB_IM1CNT) ; GET CURRENT ENTRY COUNT
INC A ; ALLOW FOR EXTRA ENTRY TO APPEND AT END
LD C,A ; SAVE IN C FOR COMPARE
#ENDIF
#IF (INTMODE == 2)
LD C,HBX_IVTCNT ; GET CURRENT ENTRY COUNT
#ENDIF
LD A,E ; INCOMING INDEX POSITION TO A
CP C ; COMPARE TO VECTOR COUNT
JR C,SYS_INTGET1 ; CONTINUE IF POSITION IN RANGE
CALL SYSCHK ; ELSE ERROR
LD A,ERR_RANGE ; SIGNAL ERROR
OR A ; SET FLAGS
RET
SYS_INTGET1:
OR A ; CLEAR CARRY
RLA ; ADJUST FOR TABLE ENTRY
RLA ; SIZE OF 4 BYTES
INC A ; BUMP TO ADR FIELD
LD H,0
LD L,A
LD DE,HB_IVT ; DE := START OF VECTOR TABLE
ADD HL,DE ; HL := ADR OF VECTOR
XOR A ; INDICATE SUCCESS
RET
;
; RETURN THE INTERRUPT VECTOR FOR A SPECIFIED POSITION IN THE INT VECTOR LIST / TABLE
; ENTRY: E=LIST/TABLE POSITION
; RETURN: HL=INTERRUPT VECTOR
;
SYS_INTGET:
CALL SYS_INTVECADR ; GET VECTOR ADDRESS
RET NZ ; BAIL OUT ON ERROR
LD A,(HL) ; DEREF HL TO GET VECTOR
INC HL
LD H,(HL)
LD L,A
XOR A ; SIGNAL SUCCESS
RET ; DONE
;
; SET AN INTERRUPT VECTOR FOR A SPECIFIED POSITION IN THE INT VECTOR LIST / TABLE
; ENTRY: E=LIST/TABLE POSITION, HL=NEW INTERRUPT VECTOR
; RETURN: HL=PREVIOUS INTERRUPT VECTOR, DE=ADR OF INT ROUTING ENGINE FOR IM2
;
SYS_INTSET:
PUSH HL ; SAVE NEW VECTOR
CALL SYS_INTVECADR ; GET VECTOR ADDRESS
JR Z,SYS_INTSET1 ; CONTINUE IF OK
POP HL ; FIX STACK
RET NZ ; BAIL OUT ON ERROR
SYS_INTSET1:
PUSH HL ; SAVE VECTOR ADDRESS
LD A,(HL) ; DEREF HL TO GET PREV VECTOR
INC HL
LD H,(HL)
LD L,A
EX (SP),HL ; (SP) := PREV VEC, HL := VEC ADR
POP DE ; DE := PREV VEC
POP BC ; BC := NEW VEC
LD (HL),C ; SAVE LSB
INC HL
LD (HL),B ; SAVE MSB
EX DE,HL ; HL := PREV VEC
XOR A ; SIGNAL SUCCESS
RET ; DONE
;
;==================================================================================================
; GLOBAL HBIOS FUNCTIONS
;==================================================================================================
;
; COMMON ROUTINE THAT IS CALLED BY CHARACTER IO DRIVERS WHEN
; AN IDLE CONDITION IS DETECTED (WAIT FOR INPUT/OUTPUT)
;
CIO_IDLE:
PUSH AF ; PRESERVE AF
LD A,(IDLECOUNT) ; GET CURRENT IDLE COUNT
DEC A ; DECREMENT
LD (IDLECOUNT),A ; SAVE UPDATED VALUE
CALL Z,IDLE ; IF ZERO, DO IDLE PROCESSING
POP AF ; RECOVER AF
RET
;
#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
;
;
;
#IF (MEMMGR == MM_Z280)
;
Z280_TIMINT:
; DISCARD REASON CODE
INC SP
INC SP
;
; SAVE INCOMING REGISTERS
PUSH AF
PUSH BC
PUSH DE
PUSH HL
;
; CALL PRIMARY TIMER LOGIC ON EVERY OTHER INT
LD A,(Z280_TIMCTR)
XOR $FF
LD (Z280_TIMCTR),A
CALL Z,HB_TIMINT
;
; 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,$FE ; NEW COUNTER/TIMER I/O PAGE
LDCTL (C),HL
;
; CLEAR END OF COUNT CONDITION TO RESET INTERRUPT
IN A,(Z280_CT0_CMDST) ; GET STATUS
RES 1,A ; CLEAR CC
OUT (Z280_CT0_CMDST),A ; SET C/T 0
;
; 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
;
; RESTORE REGISTERS
POP HL
POP DE
POP BC
POP AF
;
RETIL
;
Z280_TIMCTR .DB 0 ; USED TO DIVIDE TIMER INTS
;
#ENDIF
;
;
;
HB_TIMINT:
#IF FALSE ; *DEBUG*
LD HL,HB_TIMDBGCNT
INC (HL)
LD A,(HL)
OUT (DIAGPORT),A
JR HB_TIMDBG1
HB_TIMDBGCNT .DB 0
HB_TIMDBG1:
#ENDIF ; *DEBUG*
;
; TIMER HANDLER VECTORS
; THESE CAN BE HOOKED AS DESIRED BY DRIVERS
;
VEC_TICK:
JP HB_TICK ; TICK PROCESSING VECTOR
VEC_SECOND:
JP HB_SECOND ; SECOND PROCESSING VECTOR
;
; TIMER HANDLERS
;
HB_TICK:
; INCREMENT TICK COUNTER (32 BIT)
LD HL,HB_TICKS ; POINT TO TICK COUNTER
CALL INC32HL
LD HL,HB_SECTCK ; POINT TO SECONDS TICK COUNTER
DEC (HL) ; COUNTDOWN ONE SECOND OF TICKS
JR NZ,HB_TICK1 ; NOT DONE, SKIP AHEAD
LD A,TICKFREQ ; TICKS PER SECOND
LD (HL),A ; RESET COUNTDOWN REGISTER
CALL VEC_SECOND ; DO SECONDS PROCESSING VIA VECTOR
;
HB_TICK1:
;
#IF (CPUFAM == CPU_Z180)
; ACK/RESET Z180 TIMER INTERRUPT
IN0 A,(Z180_TCR)
IN0 A,(Z180_TMDR0L)
#ENDIF
;
#IF (WDOGMODE != WDOG_NONE)
; PULSE WATCHDOG
OUT (WDOGIO),A ; VALUE IS IRRELEVANT
#ENDIF
;
#IF MKYENABLE
CALL MKY_INT
#ENDIF
;
OR $FF ; NZ SET TO INDICATE INT HANDLED
RET
;
HB_SECOND:
; INCREMENT SECONDS COUNTER
LD HL,HB_SECS ; POINT TO SECONDS COUNTER
JP INC32HL ; INCREMENT AND RETURN
;
; 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
;
RETIL ; RETURN FROM INT
;
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
;
LDUP A,(HL) ; BYTE FROM USER SPACE
;
; 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:
LDUP A,(HL) ; BYTE FROM USER SPACE
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
RETIL ; RETURN FROM INT
;
HB_MSRSAV .DW 0 ; SAVED MSR
HB_RCSAV .DW 0 ; SAVED REASON CODE
;
Z280_PRIVSTR .TEXT "\r\n\r\n*** Privileged Instruction @$"
;
#ENDIF
;
; COMMON API FUNCTION DISPATCH CODE
;
; ON ENTRY B IS API FUNCTION NUMBER AND C IS UNIT #
; (INDEX INTO XXX_TBL OF UNITS) AND IY POINTS TO START OF UNIT TABLE.
; USE UNIT # IN C TO LOOKUP XXX_TBL ENTRY. THE XXX_TBL
; ENTRY CONTAINS THE START OF THE DRIVER FUNCTION TABLE AND
; THE DEVICE SPECIFIC INSTANCE DATA (BLOB). SET IY TO BLOB ADDRESS
; AND CALL THE SPECIFIC FUNCTION REQUESTED IN THE DRIVER.
;
HB_DISPCALL:
PUSH HL ; SAVE INCOMING HL VALUE
CALL HB_DISPCALC ; IY = BLOB ADR, HL = FN ADR
JR NZ,HB_DISPCALL1 ; ABORT ON ERROR
EX (SP),HL ; RESTORE HL & FN ADR TO TOS
RET ; JUMP TO FN ADR
HB_DISPCALL1:
POP HL ; RECOVER HL
RET ; AND DONE
;
; ENTRY: BC=FUNC/UNIT, IY=DISPATCH TABLE
; EXIT: HL=FUNC ADR, IY=DATA BLOB ADR
;
HB_DISPCALC:
; CHECK INCOMING UNIT INDEX IN C FOR VALIDITY
LD A,C ; A := INCOMING DISK UNIT INDEX
CP (IY-1) ; COMPARE TO COUNT
JR NC,HB_UNITERR ; HANDLE INVALID UNIT INDEX
; CHECK FUNCTION INDEX FOR VALIDITY
LD A,B ; A := INCOMING FUNCTION NUMBER
AND $0F ; LOW NIBBLE ONLY FOR FUNC INDEX
CP (IY-3) ; CHECK FN NUM AGAINST MAX
JR NC,HB_FUNCERR ; HANDLE FN NUM OUT OF RANGE ERROR
; BUMP IY TO ACTUAL XXX_TBL ENTRY FOR INCOMING UNIT INDEX
PUSH BC ; SAVE BC
LD B,0 ; MSB IS ALWAYS ZERO
RLC C ; MULTIPLY UNIT INDEX
RLC C ; ... BY 4 FOR TABLE ENTRY OFFSET
ADD IY,BC ; SET IY TO ENTRY ADDRESS
POP BC ; RESTORE BC
; DERIVE DRIVER FUNC ADR TO CALL
;PUSH HL ; SAVE INCOMING HL
LD L,(IY+0) ; COPY DRIVER FUNC TABLE
LD H,(IY+1) ; ... START TO HL
RLCA ; CONV UNIT (STILL IN A) TO FN ADR OFFSET
CALL ADDHLA ; HL NOW HAS DRIVER FUNC TBL START ADR
LD A,(HL) ; DEREFERENCE HL
INC HL ; ... TO GET
LD H,(HL) ; ... ACTUAL
LD L,A ; ... TARGET FUNCTION ADDRESS
;EX (SP),HL ; RESTORE HL, FUNC ADR ON STACK
; GET UNIT INSTANCE DATA BLOB ADDRESS TO IY
;PUSH HL ; SAVE INCOMING HL
PUSH HL ; SAVE FUNC ADR
LD L,(IY+2) ; HL := DATA BLOB ADDRESS
LD H,(IY+3) ; ...
EX (SP),HL ; RESTORE HL, BLOB ADR ON TOS
POP IY ; IY := BLOB ADR
XOR A ; SIGNAL SUCCESS
RET ; JUMP TO DRIVER FUNC ADR ON TOS
;
HB_FUNCERR:
CALL SYSCHK
LD A,ERR_NOFUNC ; SIGNAL ERROR
OR A ; SET FLAGS
RET
;
HB_UNITERR:
CALL SYSCHK
LD A,ERR_NOUNIT ; SIGNAL ERROR
OR A ; SET FLAGS
RET
;
; ADD AN ENTRY TO THE UNIT TABLE AT ADDRESS IN HL
; BC: DRIVER FUNCTION TABLE
; DE: ADDRESS OF UNIT INSTANCE DATA
; RETURN
; A: UNIT NUMBER ASSIGNED
;
HB_ADDENT:
DEC HL ; POINT TO ENTRY COUNT
LD A,(HL) ; GET ENTRY COUNT
PUSH AF ; SAVE VALUE TO RETURN AS ENTRY NUM AT END
INC A ; INCREMENT TO ACCOUNT FOR NEW ENTRY
DEC HL ; POINT TO ENTRY MAX
CP (HL) ; COMPARE MAX TO CURRENT COUNT (COUNT - MAX)
CALL NC,PANIC ; OVERFLOW
INC HL ; POINT TO COUNT
LD (HL),A ; SAVE NEW COUNT
INC HL ; POINT TO START OF TABLE
DEC A ; CONVERT A FROM ENTRY COUNT TO ENTRY INDEX
RLCA ; MULTIPLY BY 4
RLCA ; ... TO GET BYTE OFFSET OF ENTRY
CALL ADDHLA ; MAKE HL POINT TO ACTUAL ENTRY ADDRESS
PUSH BC ; GET TABLE ENTRY ADDRESS TO BC
EX (SP),HL ; ... AND DISPATCH ADDRESS TO HL
POP BC ; ... SO THAT DE:HL HAS 32 BIT ENTRY
CALL ST32 ; LD (BC),DE:HL STORES THE ENTRY
POP AF ; RETURN ENTRY INDEX (UNIT NUMBER ASSIGNED)
RET
;
; ALLOCATE HL BYTES OF MEMORY ON THE HEAP
; RETURNS POINTER TO ALLOCATED SPACE IN HL
; ON SUCCESS RETURN A == 0, AND Z SET
; ON FAILURE A <> 0 AND NZ SET AND HL TRASHED
; ALL OTHER REGISTERS PRESERVED
;
; A 4 BYTE HEADER IS PLACED IN FRONT OF THE ALLOCATED MEMORY
; - DWORD: SIZE OF MEMROY ALLOCATED (DOES NOT INCLUDE 4 BYTE HEADER)
; - DWORD: ADDRESS WHERE ALLOC WAS CALLED (VALUE ON TOP OF STACK AT CALL)
;
HB_ALLOC:
; SAVE ALLOC SIZE AND REFERENCE ADR FOR SUBSEQUENT HEADER CONSTRUCTION
LD (HB_TMPSZ),HL ; SAVE INCOMING SIZE REQUESTED
; USE EX (SP),HL INSTEAD????
POP HL ; GET RETURN ADDRESS
LD (HB_TMPREF),HL ; SAVE AS REFERENCE
; USE EX (SP),HL INSTEAD????
PUSH HL ; PUT IT BACK ON STACK
LD HL,(HB_TMPSZ) ; RECOVER INCOMING MEM SIZE PARM
;
; CALC NEW HEAP TOP AND HANDLE OUT-OF-SPACE ERROR
PUSH DE ; SAVE INCOMING DE
LD DE,4 ; SIZE OF HEADER
ADD HL,DE ; ADD IT IN
JR C,HB_ALLOC1 ; ERROR ON OVERFLOW
LD DE,(CB_HEAPTOP) ; CURRENT HEAP TOP
ADD HL,DE ; ADD IT IN, HL := NEW HEAP TOP
JR C,HB_ALLOC1 ; ERROR ON OVERFLOW
BIT 7,H ; TEST PAST END OF BANK (>= 32K)
JR NZ,HB_ALLOC1 ; ERROR IF PAST END
;
; SAVE NEW HEAP TOP
LD DE,(CB_HEAPTOP) ; GET ORIGINAL HEAP TOP
LD (CB_HEAPTOP),HL ; SAVE NEW HEAP TOP
;
; SET HEADER VALUES
EX DE,HL ; HEADER ADR TO HL
LD DE,(HB_TMPSZ) ; GET THE ORIG SIZE REQUESTED
LD (HL),E ; SAVE SIZE (LSB)
INC HL ; BUMP HEADER POINTER
LD (HL),D ; SAVE SIZE (MSB)
INC HL ; BUMP HEADER POINTER
LD DE,(HB_TMPREF) ; GET THE REFERENCE ADR
LD (HL),E ; SAVE REF ADR (LSB)
INC HL ; BUMP HEADER POINTER
LD (HL),D ; SAVE REF ADR (MSB)
INC HL ; BUMP HEADER POINTER
;
; RETURN SUCCESS, HL POINTS TO START OF ALLOCATED MEMORY (PAST HEADER)
POP DE ; RESTORE INCOMING DE
XOR A ; SIGNAL SUCCESS
RET ; AND RETURN
;
HB_ALLOC1:
; ERROR RETURN
POP DE ; RESTORE INCOMING DE
CALL SYSCHK
LD A,ERR_NOMEM ; SIGNAL ERROR
OR A ; SET FLAGS
RET
;
HB_TMPSZ .DW 0
HB_TMPREF .DW 0
;
;==================================================================================================
; 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 (RC2014)
; 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
ADD HL,A ; 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)
;
; USE Z280 PHYSICAL MEMORY DMA COPY TO PERFORM AN INTERBANK COPY.
; COPY FROM (HB_SRCBNK):(HL) TO (HB_DSTBNK):(DE) FOR BC BYTES. BOTH
; HB_SRCBNK AND HB_DSTBNK MUST BE INITIALIZED PRIOR TO CALLING THIS
; ROUTINE.
;
; ADDRESSES ARE TRANSLATED FROM LOGICAL (Z80) TO PHYSICAL (Z280) TO
; SETUP THE DMA COPY PARAMETERS. IF THE SOURCE OR DESTINATION RANGE
; CROSSES OVER THE BANKED/COMMON BOUNDARY AT $8000, THEN SPECIAL STEPS
; MUST BE TAKEN BECAUSE THE BANKED AND COMMON AEAS ARE PROBABLY NOT
; SEQUENTIALLY LOCATED IN PHYSICAL MEMORY. TWO ENTRY POINTS ARE
; PROVIDED. Z280_BNKCPY IS MUCH FASTER, BUT DOES NOT ACCOUNT FOR THE
; COPY RANGES CROSSING OVER THE BANKED/COMMON BOUNDARY (WORKS GREAT
; FOR ANY COPY KNOWN TO STAY WITHIN IT'S OWN AREA). Z280_BNKCPYX
; WILL HANDLE COPIES WHERE THE SOURCE AND/OR DESTINATION RANGES
; CROSS OVER THE BANKED/COMMON MEMORY BOUNDARY. IT DOES THIS BY
; BREAKING UP THE COPY REQUESTS INTO MULTIPLE REQUESTS THAT ALL FIT
; WITHIN A SINGLE BANKED/COMMON MEMORY SEGMENT AND CALLING Z280_BNKCPY
; ITERATIVELY UNTIL THE COPY IS COMPLETE.
;
; THERE IS ESSENTIALLY NO PROTECTION FOR CALLING THESE ROUTINES WITH
; INVALID PARAMETERS. FOR EXAMPLE, A REQUEST TO COPY $2000 BYTES
; STARTING AT $F000 EXCEEDS THE SIZE OF THE Z80 MEMORY SPACES AND
; RESULTS IN UNDEFINED BEHAVIOR.
;
; THE COPY IS ALWAYS DONE FROM START TO END. IF THE SOURCE AND
; DESTINATION RANGES OVERLAP, THEN YOU MUST TAKE THIS INTO ACCOUNT.
;
; THE ROUTINE FUNCTIONS LOGICALLY LIKE THE Z80 LDIR INSTRUCTION. ON
; RETURN THE SOURCE (HL) AND DESTINATION (DE) REGISTERS WILL BE LEFT
; POINTING TO THE NEXT BYTE THAT WOULD BE COPIED IF THE COPY ROUTINE
; CONTINUED. BC WILL BE 0. AF IS UNDEFINED.
;
#IF (MEMMGR == MM_Z280)
;
; ADJUST THE LENGTH OF THE COPY SUCH THAT BOTH THE SOURCE AND
; DESTINATION RANGES DO NOT CROSS OVER THE BANKED/COMMON MEMORY
; BOUNDARY. CALL Z280_BNKCPY TO DO AS MANY ITERATIONS AS NEEDED TO
; COMPLETE THE COPY.
;
;
Z280_BNKCPYX:
LD (Z280_BNKCPY_LEN),BC ; SAVE LENGTH
;
CALL Z280_BNKCPY_XOVER ; ADJUST FOR XOVER AS NEEDED
EX DE,HL ; SWAP SOURCE/DEST
CALL Z280_BNKCPY_XOVER ; ADJUST FOR XOVER AS NEEDED
EX DE,HL ; SWAP BACK
;
; DO THE WORK, SAVE THE LEN OF THIS ITERATION
PUSH BC ; SAVE ITER LENGTH
CALL Z280_BNKCPY ; DO THE WORK
;
;;; *DEBUG* SIMULATE CALL TO Z280_BNKCPY
;;CALL NEWLINE
;;CALL REGDMP ; *DEBUG*
;;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
;;; END *DEBUG*
;
POP BC ; RECOVER ITER LENGTH
;
; ACCUNT FOR WORK ACTUALLY PERFORMED
PUSH HL ; SAVE SOURCE ADR
LD HL,(Z280_BNKCPY_LEN) ; GET PENDING LENGTH
OR A ; CLEAR CARRY
SBC HL,BC ; SUBTRACT WHAT WE DID
PUSH HL ; MOVE NEW PENDING LEN
POP BC ; TO BC
POP HL ; RECOVER SOURCE ADR
;
; SEE IF WE NEED TO ITERATE
LD A,B ; IS LENGTH
OR C ; ... NOW ZERO?
RET Z ; IF SO, ALL DONE
JR Z280_BNKCPYX ; ELSE ITERATE UNTIL DONE
;
Z280_BNKCPY_LEN .DW 0 ; TEMP STORAGE FOR BC
;
Z280_BNKCPY_XOVER:
; DETECT XOVER IN RANGE AND ADJUST COPY LEN IF SO
; HL=START, BC=LEN
; BC IS REDUCED AS NEEDED TO AVOID XOVER
BIT 7,H ; START ABOVE 32K?
RET NZ ; YES, NO XOVER
PUSH HL ; SAVE START ADR
ADD HL,BC ; ADD COPY LEN
DEC HL ; CONVERT TO "LAST" BYTE OF RANGE
BIT 7,H ; ABOVE 32K?
POP HL ; RESTORE HL
RET Z ; IF NOT, NO XOVER
;
; START IS BELOW 32K, END IS OVER 32K, XOVER IN SOURCE!
; REDUCE LENGTH TO AVOID IT
; COMPUTE (32K - START) FOR NEW LEN
PUSH DE ; SAVE DEST (DE)
PUSH HL ; SAVE START (HL)
LD DE,$8000
EX DE,HL ; DE=START, HL=32K
OR A ; CLEAR CARRY
SBC HL,DE ; HL = NEW LEN
PUSH HL ; MOVE NEW LEN
POP BC ; ... TO BC
POP HL ; RECOVER START
POP DE ; RECOVER DEST
RET ; RETURN
;
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 (IOPRSAV),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:
INW HL,(C) ; WORD INPUT
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,(IOPRSAV) ; 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.
; RC2014 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
RETIL ; RETURN FROM INT
#ENDIF
;
;==================================================================================================
; DEVICE DRIVERS
;==================================================================================================
;
#IF (DSRTCENABLE)
ORG_DSRTC .EQU $
#INCLUDE "dsrtc.asm"
SIZ_DSRTC .EQU $ - ORG_DSRTC
.ECHO "DSRTC occupies "
.ECHO SIZ_DSRTC
.ECHO " bytes.\n"
#ENDIF
;
#IF (BQRTCENABLE)
ORG_BQRTC .EQU $
#INCLUDE "bqrtc.asm"
SIZ_BQRTC .EQU $ - ORG_BQRTC
.ECHO "BQRTC occupies "
.ECHO SIZ_BQRTC
.ECHO " bytes.\n"
#ENDIF
#IF (SIMRTCENABLE)
ORG_SIMRTC .EQU $
#INCLUDE "simrtc.asm"
SIZ_SIMRTC .EQU $ - ORG_SIMRTC
.ECHO "SIMRTC occupies "
.ECHO SIZ_SIMRTC
.ECHO " bytes.\n"
#ENDIF
#IF (DS7RTCENABLE & (DS7RTCMODE=DS7RTCMODE_PCF))
ORG_PCF8584 .EQU $
#INCLUDE "pcf8584.asm"
SIZ_PCF8584 .EQU $ - ORG_PCF8584
.ECHO "PCF8584 occupies "
.ECHO SIZ_PCF8584
.ECHO " bytes.\n"
#ENDIF
#IF (DS7RTCENABLE)
ORG_DS7RTC .EQU $
#INCLUDE "ds7rtc.asm"
SIZ_DS7RTC .EQU $ - ORG_DS7RTC
.ECHO "DS7RTC occupies "
.ECHO SIZ_DS7RTC
.ECHO " bytes.\n"
#ENDIF
;
#IF (INTRTCENABLE)
ORG_INTRTC .EQU $
#INCLUDE "intrtc.asm"
SIZ_INTRTC .EQU $ - ORG_INTRTC
.ECHO "INTRTC occupies "
.ECHO SIZ_INTRTC
.ECHO " bytes.\n"
#ENDIF
;
#IF (RP5RTCENABLE)
ORG_RP5RTC .EQU $
#INCLUDE "rp5rtc.asm"
SIZ_RP5RTC .EQU $ - ORG_RP5RTC
.ECHO "RP5RTC occupies "
.ECHO SIZ_RP5RTC
.ECHO " bytes.\n"
#ENDIF
#IF (ASCIENABLE)
ORG_ASCI .EQU $
#INCLUDE "asci.asm"
SIZ_ASCI .EQU $ - ORG_ASCI
.ECHO "ASCI occupies "
.ECHO SIZ_ASCI
.ECHO " bytes.\n"
#ENDIF
;
#IF (UARTENABLE)
ORG_UART .EQU $
#INCLUDE "uart.asm"
SIZ_UART .EQU $ - ORG_UART
.ECHO "UART occupies "
.ECHO SIZ_UART
.ECHO " bytes.\n"
#ENDIF
;
#IF (DUARTENABLE)
ORG_DUART .EQU $
#INCLUDE "duart.asm"
SIZ_DUART .EQU $ - ORG_DUART
.ECHO "DUART occupies "
.ECHO SIZ_DUART
.ECHO " bytes.\n"
#ENDIF
;
#IF (SIOENABLE)
ORG_SIO .EQU $
#INCLUDE "sio.asm"
SIZ_SIO .EQU $ - ORG_SIO
.ECHO "SIO occupies "
.ECHO SIZ_SIO
.ECHO " bytes.\n"
#ENDIF
;
#IF (ACIAENABLE)
ORG_ACIA .EQU $
#INCLUDE "acia.asm"
SIZ_ACIA .EQU $ - ORG_ACIA
.ECHO "ACIA occupies "
.ECHO SIZ_ACIA
.ECHO " bytes.\n"
#ENDIF
;
#IF (Z2UENABLE)
ORG_Z2U .EQU $
#INCLUDE "z2u.asm"
SIZ_Z2U .EQU $ - ORG_Z2U
.ECHO "Z2U occupies "
.ECHO SIZ_Z2U
.ECHO " bytes.\n"
#ENDIF
;
#IF (VGAENABLE)
ORG_VGA .EQU $
#INCLUDE "vga.asm"
SIZ_VGA .EQU $ - ORG_VGA
.ECHO "VGA occupies "
.ECHO SIZ_VGA
.ECHO " bytes.\n"
#ENDIF
;
#IF (CVDUENABLE)
ORG_CVDU .EQU $
#INCLUDE "cvdu.asm"
SIZ_CVDU .EQU $ - ORG_CVDU
.ECHO "CVDU occupies "
.ECHO SIZ_CVDU
.ECHO " bytes.\n"
#ENDIF
;
#IF (VDUENABLE)
ORG_VDU .EQU $
#INCLUDE "vdu.asm"
SIZ_VDU .EQU $ - ORG_VDU
.ECHO "VDU occupies "
.ECHO SIZ_VDU
.ECHO " bytes.\n"
#ENDIF
;
#IF (TMSENABLE)
ORG_TMS .EQU $
#INCLUDE "tms.asm"
SIZ_TMS .EQU $ - ORG_TMS
.ECHO "TMS occupies "
.ECHO SIZ_TMS
.ECHO " bytes.\n"
#ENDIF
;
#IF (NECENABLE)
ORG_NEC .EQU $
;#INCLUDE "nec.asm"
SIZ_NEC .EQU $ - ORG_NEC
.ECHO "NEC occupies "
.ECHO SIZ_NEC
.ECHO " bytes.\n"
#ENDIF
;
#IF (DMAENABLE)
ORG_DMA .EQU $
#INCLUDE "dma.asm"
SIZ_DMA .EQU $ - ORG_DMA
.ECHO "DMA occupies "
.ECHO SIZ_DMA
.ECHO " bytes.\n"
#ENDIF
;
; FONTS AREA
;
ORG_FONTS .EQU $
;
.ECHO "FONTS"
;
#IFDEF USEFONT8X8
FONT8X8:
#IF USELZSA2
#INCLUDE "font8x8c.asm"
#ELSE
#INCLUDE "font8x8u.asm"
#ENDIF
.ECHO " 8X8"
#ENDIF
;
#IFDEF USEFONT8X11
FONT8X11:
#IF USELZSA2
#INCLUDE "font8x11c.asm"
#ELSE
#INCLUDE "font8x11u.asm"
#ENDIF
.ECHO " 8X11"
#ENDIF
;
#IFDEF USEFONT8X16
FONT8X16:
#IF USELZSA2
#INCLUDE "font8x16c.asm"
#ELSE
#INCLUDE "font8x16u.asm"
#ENDIF
.ECHO " 8X16"
#ENDIF
;
#IFDEF USEFONTCGA
FONTCGA:
#IF USELZSA2
#INCLUDE "fontcgac.asm"
#ELSE
#INCLUDE "fontcgau.asm"
#ENDIF
.ECHO " CGA"
#ENDIF
;
SIZ_FONTS .EQU $ - ORG_FONTS
.ECHO " occupy "
.ECHO SIZ_FONTS
.ECHO " bytes.\n"
;
#IF (CVDUENABLE | VGAENABLE) | (TMSENABLE & (TMSMODE == TMSMODE_RCKBD))
ORG_KBD .EQU $
#INCLUDE "kbd.asm"
SIZ_KBD .EQU $ - ORG_KBD
.ECHO "KBD occupies "
.ECHO SIZ_KBD
.ECHO " bytes.\n"
#ENDIF
;
#IF (VDUENABLE | (TMSENABLE & (TMSMODE == TMSMODE_N8)))
ORG_PPK .EQU $
#INCLUDE "ppk.asm"
SIZ_PPK .EQU $ - ORG_PPK
.ECHO "PPK occupies "
.ECHO SIZ_PPK
.ECHO " bytes.\n"
#ENDIF
;
#IF (MKYENABLE)
ORG_MKY .EQU $
#INCLUDE "mky.asm"
SIZ_MKY .EQU $ - ORG_MKY
.ECHO "MKY occupies "
.ECHO SIZ_MKY
.ECHO " bytes.\n"
#ENDIF
;
#IF (PRPENABLE)
ORG_PRP .EQU $
#INCLUDE "prp.asm"
SIZ_PRP .EQU $ - ORG_PRP
.ECHO "PRP occupies "
.ECHO SIZ_PRP
.ECHO " bytes.\n"
#ENDIF
;
#IF (PPPENABLE)
ORG_PPP .EQU $
#INCLUDE "ppp.asm"
SIZ_PPP .EQU $ - ORG_PPP
.ECHO "PPP occupies "
.ECHO SIZ_PPP
.ECHO " bytes.\n"
#ENDIF
;
#IF (MDENABLE)
ORG_MD .EQU $
#INCLUDE "md.asm"
SIZ_MD .EQU $ - ORG_MD
.ECHO "MD occupies "
.ECHO SIZ_MD
.ECHO " bytes.\n"
#ENDIF
;
#IF (FDENABLE)
ORG_FD .EQU $
#INCLUDE "fd.asm"
SIZ_FD .EQU $ - ORG_FD
.ECHO "FD occupies "
.ECHO SIZ_FD
.ECHO " bytes.\n"
#ENDIF
;
#IF (RFENABLE)
ORG_RF .EQU $
#INCLUDE "rf.asm"
SIZ_RF .EQU $ - ORG_RF
.ECHO "RF occupies "
.ECHO SIZ_RF
.ECHO " bytes.\n"
#ENDIF
;
#IF (IDEENABLE)
ORG_IDE .EQU $
#INCLUDE "ide.asm"
SIZ_IDE .EQU $ - ORG_IDE
.ECHO "IDE occupies "
.ECHO SIZ_IDE
.ECHO " bytes.\n"
#ENDIF
;
#IF (PPIDEENABLE)
ORG_PPIDE .EQU $
#INCLUDE "ppide.asm"
SIZ_PPIDE .EQU $ - ORG_PPIDE
.ECHO "PPIDE occupies "
.ECHO SIZ_PPIDE
.ECHO " bytes.\n"
#ENDIF
;
#IF (SDENABLE)
ORG_SD .EQU $
#INCLUDE "sd.asm"
SIZ_SD .EQU $ - ORG_SD
.ECHO "SD occupies "
.ECHO SIZ_SD
.ECHO " bytes.\n"
#ENDIF
;
#IF (HDSKENABLE)
ORG_HDSK .EQU $
#INCLUDE "hdsk.asm"
SIZ_HDSK .EQU $ - ORG_HDSK
.ECHO "HDSK occupies "
.ECHO SIZ_HDSK
.ECHO " bytes.\n"
#ENDIF
;
#IF (TERMENABLE)
ORG_TERM .EQU $
#INCLUDE "term.asm"
SIZ_TERM .EQU $ - ORG_TERM
.ECHO "TERM occupies "
.ECHO SIZ_TERM
.ECHO " bytes.\n"
#ENDIF
;
;#IF (SPKENABLE & DSRTCENABLE)
#IF (SPKENABLE)
ORG_SPK .EQU $
#INCLUDE "spk.asm"
SIZ_SPK .EQU $ - ORG_SPK
.ECHO "SPK occupies "
.ECHO SIZ_SPK
.ECHO " bytes.\n"
#ENDIF
;
#IF (PIOENABLE)
ORG_PIO .EQU $
#INCLUDE "pio.asm"
SIZ_PIO .EQU $ - ORG_PIO
.ECHO "PIO occupies "
.ECHO SIZ_PIO
.ECHO " bytes.\n"
#ENDIF
;
#IF (CENENABLE)
ORG_CEN .EQU $
#INCLUDE "cen.asm"
SIZ_CEN .EQU $ - ORG_CEN
.ECHO "CEN occupies "
.ECHO SIZ_CEN
.ECHO " bytes.\n"
#ENDIF
;
#IF (PIO_4P | PIO_ZP | PIO_SBC)
ORG_PIO .EQU $
#INCLUDE "pio.asm"
SIZ_PIO .EQU $ - ORG_PIO
.ECHO "PIO occupies "
.ECHO SIZ_PIO
.ECHO " bytes.\n"
#ENDIF
;
#IF (UFENABLE)
ORG_UF .EQU $
#INCLUDE "uf.asm"
SIZ_UF .EQU $ - ORG_UF
.ECHO "UF occupies "
.ECHO SIZ_UF
.ECHO " bytes.\n"
#ENDIF
#IF (KIOENABLE)
ORG_KIO .EQU $
#INCLUDE "kio.asm"
SIZ_KIO .EQU $ - ORG_KIO
.ECHO "KIO occupies "
.ECHO SIZ_KIO
.ECHO " bytes.\n"
#ENDIF
#IF (CTCENABLE)
ORG_CTC .EQU $
#INCLUDE "ctc.asm"
SIZ_CTC .EQU $ - ORG_CTC
.ECHO "CTC occupies "
.ECHO SIZ_CTC
.ECHO " bytes.\n"
#ENDIF
#IF (SN76489ENABLE)
ORG_SN76489 .EQU $
#INCLUDE "sn76489.asm"
SIZ_SN76489 .EQU $ - ORG_SN76489
.ECHO "SN76489 occupies "
.ECHO SIZ_SN76489
.ECHO " bytes.\n"
#ENDIF
#IF (AY38910ENABLE)
ORG_AY38910 .EQU $
#INCLUDE "ay38910.asm"
SIZ_AY38910 .EQU $ - ORG_AY38910
.ECHO "AY38910 occupies "
.ECHO SIZ_AY38910
.ECHO " bytes.\n"
#ENDIF
;
.ECHO "RTCDEF="
.ECHO RTCDEF
.ECHO "\n"
;
#DEFINE USEDELAY
#INCLUDE "util.asm"
#INCLUDE "time.asm"
#INCLUDE "bcd.asm"
#INCLUDE "decode.asm"
#INCLUDE "encode.asm"
;
#IF (WBWDEBUG == USEXIO)
#INCLUDE "xio.asm"
#ENDIF
#IF (WBWDEBUG == USEMIO)
#INCLUDE "mio.asm"
#ENDIF
;
#IF (DSKYENABLE)
#DEFINE DSKY_KBD
#IF (DSKYMODE == DSKYMODE_V1)
#INCLUDE "dsky.asm"
#ENDIF
#IF (DSKYMODE == DSKYMODE_NG)
#INCLUDE "dskyng.asm"
#ENDIF
#ENDIF
;
; INCLUDE LZSA2 decompression engine if required.
;
#IF ((VGAENABLE | CVDUENABLE | TMSENABLE) & USELZSA2)
#INCLUDE "unlzsa2s.asm"
#ENDIF
;
; DETECT CPU SPEED USING DS-1302 RTC
;
HB_CPUSPD:
;
#IF (DSRTCENABLE & ((CPUFAM == CPU_Z80) | (CPUFAM == CPU_Z180)))
;
LD A,(DSRTC_STAT) ; GET RTC STATUS
OR A ; SET FLAGS
RET NZ ; NOT ZERO IS ERROR
;
HB_CPUSPD1:
#IF (CPUFAM == CPU_Z180)
; USE MEM W/S = 2 AND I/O W/S = 3 FOR TEST
IN0 A,(Z180_DCNTL)
PUSH AF
LD A,$B0
;LD A,$F0
OUT0 (Z180_DCNTL),A
#ENDIF
; WAIT FOR AN INITIAL TICK TO ALIGN, THEN WAIT
; FOR SECOND TICK AND TO GET A FULL ONE SECOND LOOP COUNT
CALL DSRTC_START
CALL HB_RDSEC ; GET SECONDS
LD (HB_CURSEC),A ; AND INIT CURSEC
CALL HB_WAITSEC ; WAIT FOR SECONDS TICK
LD (HB_CURSEC),A ; SAVE NEW VALUE
CALL HB_WAITSEC ; WAIT FOR SECONDS TICK
;
#IF (CPUFAM == CPU_Z180)
; RESTORE W/S SETTINGS FROM BEFORE TEST
POP AF
OUT0 (Z180_DCNTL),A
#ENDIF
;
LD A,H
OR L
RET Z ; FAILURE, USE DEFAULT CPU SPEED
;
; MOVE LOOP COUNT TO HL
PUSH DE
POP HL
;
; TIMES 4 FOR CPU SPEED IN KHZ
; RES 0,L ; GRANULARITY
SLA L
RL H
SLA L
RL H
;
; RETURN CURRENT CPU SPD (KHZ) IN HL
XOR A ; SIGNAL SUCCESS
RET
;
HB_WAITSEC:
; WAIT FOR SECONDS TICK
; RETURN SECS VALUE IN A, LOOP COUNT IN DE
LD DE,0 ; INIT LOOP COUNTER
HB_WAITSEC1:
;
#IF (CPUFAM == CPU_Z80)
; LOOP TARGET IS 4000 T-STATES, SO CPU FREQ IN KHZ = LOOP COUNT * 4
CALL DLY32
CALL DLY16
CALL DLY1 ; 27 TSTATES
SBC HL,HL ; 15 TSTATES
SBC HL,HL ; 15 TSTATES
INC HL ; 6 TSTATES
INC HL ; 6 TSTATES
#ENDIF
;
#IF (CPUFAM == CPU_Z180)
; LOOP TARGET IS 4000 T-STATES, SO CPU FREQ IN KHZ = LOOP COUNT * 4
CALL DLY2
ADD IX,BC ; 10 + 4 = 14 TSTATES
NOP ; 5 TSTATES
NOP ; 5 TSTATES
NOP ; 5 TSTATES
NOP ; 5 TSTATES
#ENDIF
;
PUSH DE ; SAVE COUNTER
CALL HB_RDSEC ; GET SECONDS
POP DE ; RESTORE COUNTER
INC DE ; BUMP COUNTER
LD HL,HB_CURSEC ; POINT TO COMP VALUE
CP (HL) ; TEST FOR CHANGE
RET NZ ; DONE IF TICK OCCURRED
LD A,D ; CHECK DE
OR E ; ... FOR OVERFLOW
RET Z ; TIMEOUT, SOMETHING IS WRONG
JR HB_WAITSEC1 ; LOOP
;
HB_RDSEC:
; READ SECONDS BYTE INTO A
LD E,$81 ; SECONDS REGISTER
CALL DSRTC_CMD ; SEND THE COMMAND
CALL DSRTC_GET ; READ THE REGISTER
CALL DSRTC_END ; FINISH IT
LD A,E ; VALUE TO A
RET
;
#ELSE
;
OR $FF ; SIGNAL ERROR
RET ; NO RTC, ABORT
;
#ENDIF
;
; SYSTEM CHECK: DUMP MACHINE STATE AND CONTINUE?
;
SYSCHK:
; CHECK DIAG LEVEL TO SEE IF WE SHOULD DISPLAY
PUSH AF ; PRESERVE INCOMING AF VALUE
LD A,(CB_DIAGLVL) ; GET DIAGNOSTIC LEVEL
CP DL_ERROR ; >= ERROR LEVEL
JR C,SYSCHK1 ; IF NOT, GO HOME
POP AF ; RESTORE INCOMING AF VALUE
;
; DISPLAY SYSCHK MESSAGE
PUSH DE ; PRESERVE DE VALUE
LD DE,STR_SYSCHK ; POINT TO PREFIX STRING
CALL WRITESTR ; PRINT IT
POP DE ; RESTORE DE VALUE
CALL XREGDMP ; DUMP REGISTERS
JR CONTINUE ; CHECK W/ USER
;
SYSCHK1:
; RETURN IF MESSAGING BYPASSED BY DIAG LEVEL
POP AF
RET
;
; 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
;==================================================================================================
; DISPLAY SUMMARY OF ATTACHED UNITS/DEVICES
;==================================================================================================
;
PRTSUM:
CALL NEWLINE2 ; SKIP A LINE
LD DE,PS_STRHDR ; POINT TO HEADER
CALL WRITESTR ; PRINT IT
;
LD C,BF_SYSGET_CIOCNT ; CHARACTER DEVICES
LD HL,PS_SERIAL
CALL PRT_ALLD
;
LD C,BF_SYSGET_DIOCNT ; DISK DRIVES
LD HL,PS_DISK
CALL PRT_ALLD
;
LD C,BF_SYSGET_VDACNT ; VIDEO DEVICES
LD HL,PS_VIDEO
CALL PRT_ALLD
;
LD C,BF_SYSGET_SNDCNT ; SOUND DEVICES
LD HL,PS_SOUND
CALL PRT_ALLD
RET
;
PRT_ALLD:
LD B,BF_SYSGET ; FUNC: SYSTEM INFO GET
RST 08 ; E := UNIT COUNT
LD B,E ; MOVE TO B FOR LOOP COUNT
LD A,E ; MOVE TO ACCUM
OR A ; SET FLAGS
RET Z ; IF NONE, JUST RETURN
LD C,0 ; C WILL BE UNIT INDEX
PRT_ALLD1:
PUSH BC ; SAVE LOOP CONTROL
PUSH DE
PUSH HL
CALL JPHL ; CALL THE ROUTINE PASSED IN HL
POP HL
POP DE
POP BC ; RESTORE LOOP CONTROL
INC C ; BUMP UNIT INDEX
DJNZ PRT_ALLD1 ; LOOP THRU ALL DEVICES
RET
;
; PRINT ONE LINE DISK UNIT/DEVICE INFO, DISK UNIT INDEX IN C
;
PS_DISK:
PUSH BC ; SAVE UNIT INDEX FOR LATER
;
; UNIT COLUMN
PRTS("Disk $")
LD A,C ; MOVE UNIT NUM TO A
CALL PRTDECB ; PRINT IT
CP 10 ; CHECK FOR MULTIPLE DIGITS
CALL C,PC_SPACE ; EXTRA SPACE IF NEEDED
PRTS(" $") ; PAD TO NEXT COLUMN
;
; DEVICE COLUMN
LD B,BF_DIODEVICE ; FUNC=GET DEVICE INFO, UNIT NUM STILL IN C
RST 08 ; DE:=DEVTYP/NUM, H:=DISK ATTRIBUTES
PUSH BC ; SAVE ATTRIBUTES
LD HL,PS_DDSTRREF ; POINT TO DISK DEVICE TYPE NAME TABLE
CALL PS_PRTDEV ; PRINT DISK DEVICE NMEMONIC PADDED TO FIELD WIDTH
POP DE ; RECOVER ATTRIBUTES TO DE
PUSH DE ; SAVE ATTRIBUTES AGAIN
CALL PS_PRTDT ; PRINT DISK TYPE
POP DE ; RESTORE ATTRIBUTES
POP BC ; RESTORE UNIT NUM
CALL PS_PRTDC ; PRINT DISK CAPACITY
;
CALL NEWLINE
RET
;
; PRINT DISK TYPE (DISK ATTRIBUTE IN E)
;
PS_PRTDT:
LD A,E ; ATTRIBUTES TO A
BIT 7,A ; FLOPPY BIT SET?
LD HL,PS_DTFLOP ; ASSUME FLOPPY
JR NZ,PS_PRTDT1 ; IF FLOPPY, JUMP AHEAD
RRCA ; SHIFT TYPE BITS
RRCA
RRCA
AND $07 ; AND ISOLATE THEM
RLCA ; X2 FOR WORD OFFSET IN STRING TABLE
LD HL,PS_DTSTRREF + 2 ; POINT TO STR REF TABLE (SKIP FLOPPY STRING)
CALL ADDHLA
LD A,(HL)
INC HL
LD H,(HL)
LD L,A
;
PS_PRTDT1:
CALL PS_PRT ; PRINT $ TERM STRING AT (HL), C:=CHARS PRINTED
LD A,18 ; 18 CHAR FIELD
SUB C
CALL PS_PAD ; PAD N SPACES (SPECIFIED IN A)
RET
;
; PRINT DISK CAPACITY (UNIT IN C, ATTRIBUTE IN E)
;
PS_PRTDC:
;
LD A,E ; ATTRIBUTE TO ACCUM
BIT 7,A ; TEST FOR FLOPPY
JR NZ,PS_PRTDC2 ; HANDLE FLOPPY
RRCA ; ISOLATE TYPE BITS
RRCA
RRCA
AND $07
CP 4 ; ROM DISK?
JR Z,PS_PRTDC1 ; PRINT CAPACITY IN KB
CP 5 ; RAM DISK?
JR Z,PS_PRTDC1 ; PRINT CAPACITY IN KB
CP 7 ; FLASH DISK?
JR Z,PS_PRTDC1 ; PRINT CAPACITY IN KB
;
; PRINT HARD DISK STORAGE SIZE IN MB
LD B,BF_DIOCAP ; HBIOS FUNC: GET CAPACTIY
RST 08 ; DE:HL := BLOCKS
JP NZ,PS_PRTNUL ; MEDIA PROBLEM
RES 7,D ; CLEAR LBA BIT
LD B,11 ; 11 BIT SHIFT TO CONVERT BLOCKS --> MB
CALL SRL32 ; RIGHT SHIFT
CALL PRTDEC ; PRINT LOW WORD IN DECIMAL (HIGH WORD DISCARDED)
PRTS("MB$") ; PRINT SUFFIX
CALL PC_COMMA
PRTS("LBA$") ; FOR NOW, WE ASSUME HARD DISK DOES LBA
RET ; DONE
;
PS_PRTDC1:
; PRINT ROM/RAM DISK CAPACITY IN KB
LD B,BF_DIOCAP ; HBIOS FUNC: GET CAPACTIY
RST 08 ; DE:HL := BLOCKS
JP NZ,PS_PRTNUL ; MEDIA PROBLEM
RES 7,D ; CLEAR LBA BIT
LD B,1 ; 11 BIT SHIFT TO CONVERT BLOCKS --> MB
CALL SRL32 ; RIGHT SHIFT
CALL PRTDEC ; PRINT LOW WORD IN DECIMAL (HIGH WORD DISCARDED)
PRTS("KB$") ; PRINT SUFFIX
CALL PC_COMMA
PRTS("LBA$") ; FOR NOW, WE ASSUME HARD DISK DOES LBA
RET ; DONE
;
PS_PRTDC2:
LD C,E ; ATTRIBUTE TO C FOR SAFE KEEPING
;
LD A,01100000B ; DISPLAY FORM FACTOR
LD DE,PS_FLP_FSTR ; WHICH IS DEFINED IN
CALL PRTIDXMSK ; BITS 5 AND 6.
;
LD A,00010000B ; DISPLAY SIDES
LD DE,PS_FLP_SSTR ; WHICH IS DEFINED
CALL PRTIDXMSK ; IN BIT 4
;
LD A,00001100B ; DISPLAY DENSITY
LD DE,PS_FLP_DSTR ; WHICH IS DEFINED IN
CALL PRTIDXMSK ; BITS 2 AND 3.
;
CALL PC_COMMA
PRTS("CHS$") ; FOR NOW, WE ASSUME HARD DISK DOES LBA
;
RET ; DONE
;
; PRINT ONE LINE SERIAL UNIT/DEVICE INFO, SERIAL UNIT INDEX IN C
;
PS_SERIAL:
PUSH BC ; SAVE UNIT INDEX FOR LATER
;
; UNIT COLUMN
PRTS("Char $")
LD A,C ; MOVE UNIT NUM TO A
CALL PRTDECB ; PRINT IT, ASSUME SINGLE DIGIT
PRTS(" $") ; PAD TO NEXT COLUMN
;
; DEVICE COLUMN
LD B,BF_CIODEVICE ; FUNC=GET DEVICE INFO, UNIT NUM STILL IN C
RST 08 ; DE:=DEVTYP/NUM, C:=DEVICE ATTRIBUTES
PUSH BC ; SAVE ATTRIBUTES
LD HL,PS_SDSTRREF ; POINT TO SERIAL DEVICE TYPE NAME TABLE
CALL PS_PRTDEV ; PRINT SERIAL DEVICE NMEMONIC PADDED TO FIELD WIDTH
POP BC ; RECOVER ATTRIBUTES
PUSH BC ; SAVE ATTRIBUTES AGAIN
CALL PS_PRTST ; PRINT SERIAL TYPE
POP BC ; RESTORE ATTRIBUTES
POP DE ; RESTORE UNIT NUM TO E
CALL PS_PRTSC ; PRINT SERIAL CONFIG
;
CALL NEWLINE
RET
;
; PRINT CHARACTER TYPE (SERIAL ATTRIBUTE IN E)
;
PS_PRTST:
LD HL,PS_STPPT
BIT 6,C
JR NZ,PS_PRTST1 ; PARALLEL TYPE?
LD HL,PS_STRS232 ; ASSUME RS-232
BIT 7,C ; 0=RS-232, 1=TERMINAL
JR Z,PS_PRTST1 ; HANDLE TERMINAL TYPE
LD HL,PS_STTERM ; TYPE IS TERMINAL
;
PS_PRTST1:
CALL PS_PRT ; PRINT $ TERM STRING AT (HL), C:=CHARS PRINTED
LD A,18 ; 18 CHAR FIELD
SUB C
CALL PS_PAD ; PAD N SPACES (SPECIFIED IN A)
RET
;
; PRINT SERIAL CONFIG (UNIT IN E, ATTRIBUTE IN C)
;
PS_PRTSC:
BIT 6,C ; PARALLEL TYPE?
JR NZ,PSPRTPC0
BIT 7,C ; 0=RS-232, 1=TERMINAL
JP NZ,PS_PRTSC1 ; PRINT TERMINAL CONFIG
;
; PRINT RS-232 CONFIG
LD B,BF_CIOQUERY ; HBIOS FUNC: GET CIO CONFIG
LD C,E ; SET SERIAL UNIT NUM
RST 08 ; DE:HL := BAUD RATE
LD A,D ; TEST FOR $FF
AND E
INC A ; SET Z IF DE == $FF
JP Z,PS_PRTNUL ; $FF == NO CONFIG DEFINED
;
PS_PRTSC0:
; PRINT BAUD RATE
PUSH DE ; PRESERVE DE
LD A,D
AND $1F ; ISOLATE ENCODED BAUD RATE
LD L,A ; PUT IN L
LD H,0 ; H IS ALWAYS ZERO
LD DE,75 ; BAUD RATE DECODE CONSTANT
CALL DECODE ; DE:HL := BAUD RATE
LD BC,HB_BCDTMP ; POINT TO TEMP BCD BUF
CALL BIN2BCD ; CONVERT TO BCD
CALL PRTBCD ; AND PRINT IN DECIMAL
POP DE ; RESTORE DE
;
; PRINT DATA BITS
PUSH DE ; PRESERVE DE
CALL PC_COMMA ; FORMATTING
LD A,E ; GET CONFIG BYTE
AND $03 ; ISOLATE DATA BITS VALUE
ADD A,'5' ; CONVERT TO CHARACTER
CALL COUT ; AND PRINT
POP DE ; RESTORE DE
;
; PRINT PARITY
PUSH DE ; PRESERVE DE
CALL PC_COMMA ; FORMATTING
LD A,E ; GET CONFIG BYTE
RRCA ; SHIFT RELEVANT BITS
RRCA ; ...
RRCA ; ...
AND $07 ; AND ISOLATE DATA BITS VALUE
LD HL,PS_STPARMAP ; CHARACTER LOOKUP TABLE
CALL ADDHLA ; APPLY OFFSET
LD A,(HL) ; GET CHARACTER
CALL COUT ; AND PRINT
POP DE ; RESTORE DE
;
; PRINT STOP BITS
CALL PC_COMMA ; FORMATTING
LD A,E ; GET CONFIG BYTE
RRCA ; SHIFT RELEVANT BITS
RRCA ; ...
AND $01 ; AND ISOLATE DATA BITS VALUE
ADD A,'1' ; MAKE IT A CHARACTER
CALL COUT ; AND PRINT
;
RET
;
PSPRTPC0:
LD B,BF_CIOQUERY ; HBIOS FUNC: GET CIO CONFIG
LD C,E ; SET PARALLEL UNIT NUM
RST 08 ; DE:HL := I/O SETTING
LD A,D ; TEST FOR $FF
AND E
INC A ; SET Z IF DE == $FF
JP Z,PS_PRTNUL ; $FF == NO CONFIG DEFINED
;
PS_PRTPC0:
LD C,E ; DISPLAY PIO TYPE
LD A,11000000B ; WHICH IS DEFINE BY
LD DE,PIO_MODE_STR ; BITS 6 AND 7
JP PRTIDXMSK
; RET ; TRICK RETURN
;
PS_PRTSC1:
; PRINT TERMINAL CONFIG
LD A,C ; GET ATTRIBUTE VALUE
CP $BF ; NO ATTACHED VDA
JR Z,PS_PRTSC2
PRTS("Video $") ; FORMATTING
AND $0F ; ISOLATE VIDEO UNIT NUM
CALL PRTDECB ; PRINT IT
CALL PC_COMMA
#IF (VDAEMU == EMUTYP_TTY)
PRTS("TTY$")
#ENDIF
#IF (VDAEMU == EMUTYP_ANSI)
PRTS("ANSI$")
#ENDIF
RET
;
PS_PRTSC2:
PRTS("PropTerm$") ; ASSUME PROPELLER
CALL PC_COMMA
PRTS("ANSI$")
RET
;
; PRINT ONE LINE VIDEO UNIT/DEVICE INFO, VIDEO UNIT INDEX IN C
;
PS_VIDEO:
PUSH BC ; SAVE UNIT INDEX FOR LATER
;
; UNIT COLUMN
PRTS("Video $")
LD A,C ; MOVE UNIT NUM TO A
CALL PRTDECB ; PRINT IT, ASSUME SINGLE DIGIT
PRTS(" $") ; PAD TO NEXT COLUMN
;
; DEVICE COLUMN
LD B,BF_VDADEV ; FUNC=GET DEVICE INFO, UNIT NUM STILL IN C
RST 08 ; DE:=DEVTYP/NUM, H:=DISK ATTRIBUTES
PUSH BC ; SAVE ATTRIBUTES
LD HL,PS_VDSTRREF ; POINT TO VIDEO DEVICE TYPE NAME TABLE
CALL PS_PRTDEV ; PRINT VIDEO DEVICE NMEMONIC PADDED TO FIELD WIDTH
POP DE ; RECOVER ATTRIBUTES
PUSH DE ; SAVE ATTRIBUTES AGAIN
CALL PS_PRTVT ; PRINT VIDEO TYPE
POP DE ; RESTORE ATTRIBUTES
POP BC ; RESTORE UNIT NUM
CALL PS_PRTVC ; PRINT VIDEO CONFIG
;
CALL NEWLINE
RET
;
; PRINT VIDEO TYPE (VIDEO ATTRIBUTE IN E)
;
PS_PRTVT:
LD HL,PS_VTCRT ; ASSUME CRT
CALL PS_PRT ; PRINT $ TERM STRING AT (HL), C:=CHARS PRINTED
LD A,18 ; 18 CHAR FIELD
SUB C
CALL PS_PAD ; PAD N SPACES (SPECIFIED IN A)
RET
;
; PRINT VIDEO CONFIG (UNIT IN C, ATTRIBUTE IN E)
;
PS_PRTVC:
PRTS("Text$")
CALL PC_COMMA
LD B,BF_VDAQRY ; FUNC: QUERY FOR VDA CONFIG
RST 08 ; D:=ROWS, E:=COLS
LD A,E
CALL PRTDECB
LD A,'x'
CALL COUT
LD A,D
CALL PRTDECB
RET
;
; PRINT SOUND CONFIG
;
PS_SOUND:
PUSH BC
; UNIT COLUMN
PRTS("Sound $")
LD A,C ; MOVE UNIT NUM TO A
CALL PRTDECB ; PRINT IT
CP 10 ; CHECK FOR MULTIPLE DIGITS
CALL C,PC_SPACE ; EXTRA SPACE IF NEEDED
PRTS(" $") ; PAD TO NEXT COLUMN
; DEVICE COLUMN
PUSH BC ;
LD E,C
XOR A
LD DE,PS_SDSND ; POINT TO DEVICE TYPE NAME TABLE
CALL PRTIDXDEA ; PRINT DEVICE NMEMONIC PADDED TO FIELD WIDTH
LD A,C ; MOVE UNIT NUM TO A
CALL PRTDECB ; PRINT IT
CALL PC_COLON
CP 10 ; CHECK FOR MULTIPLE DIGITS
CALL C,PC_SPACE ; EXTRA SPACE IF NEEDED
LD A,(PRTIDXCNT)
SUB 9+1
CPL
CALL PS_PAD
POP BC
; DEVICE TYPE
;
LD B,BF_SNDQUERY ; FUNC=GET DEVICE INFO, UNIT NUM STILL IN C
LD E,BF_SNDQ_DEV
RST 08
LD A,B
RLCA
RLCA
RLCA
RLCA
AND $0F
LD DE,PS_SDSN76489
CALL PRTIDXDEA
LD A,(PRTIDXCNT)
SUB 18+1
CPL
CALL PS_PAD ; PAD N SPACES (SPECIFIED IN A)
; DEVICE CHARACTERISTICS
;
LD B,BF_SNDQUERY ; FUNC=GET DEVICE INFO, UNIT NUM STILL IN C
LD E,BF_SNDQ_CHCNT
RST 08
LD A,B
CALL PRTDECB
LD A,'+'
CALL COUT
LD A,C
CALL PRTDECB
PRTS(" CHANNELS$")
CALL NEWLINE
;
POP BC
RET
;
; PRINT DEVICE NMEMONIC, DEVTYP/NUM SPECIFIED IN DE
;
PS_PRTDEV:
LD A,D
RRCA ; TYPE IS IN UPPER NIBBLE, MOVE TO LOWER NIBBLE
RRCA
RRCA
RRCA
RLCA ; X2 FOR WORD OFFSET IN STRING TABLE
CALL ADDHLA
LD A,(HL)
INC HL
LD H,(HL)
LD L,A
CALL PS_PRT ; PRINT $ TERM STRING AT (HL), C:=CHARS PRINTED
LD A,E ; NUM
CALL PRTDECB ; PRINT NUM, ASSUME 1 CHAR
CALL PC_COLON ; PRINT COLON
LD A,12 - 2 ; 12 CHAR FIELD - 1 POS FOR UNIT NUM AND 1 POS FOR COLON
SUB C
CALL PS_PAD ; PAD N SPACES (SPECIFIED IN A)
RET
;
; PRINT DEVICE NMEMONIC, DEVTYP/NUM SPECIFIED IN DE
;
PS_PRTNUL:
LD HL,PS_STRNUL
; FALL THRU TO PS_PRT
;
;
;
PS_PRT:
; PRINT STRING AT (HL), $ TERM, RETURN CHARS PRINTED IN C
LD C,0 ; INIT CHAR COUNT
PS_PRT1:
LD A,(HL) ; GET CHAR
INC HL ; BUMP INDEX
CP '$' ; TERM?
RET Z ; IF SO, DONE
CALL COUT ; PRINT IT
INC C ; BUMP COUNTER
JR PS_PRT1 ; AND LOOP
;
;
;
PS_PAD:
; PAD N SPACES SPECIFIED IN A
LD B,A
LD A,' '
PS_PAD1:
CALL COUT
DJNZ PS_PAD1
RET
;
HB_CPU_STR: .TEXT " Z80$"
.TEXT " Z80180$"
.TEXT " Z8S180-K$"
.TEXT " Z8S180-N$"
.TEXT " Z80280$"
;
PS_STRNUL .TEXT "--$" ; DISPLAY STRING FOR NUL VALUE
;
; DISK DEVICE STRINGS
;
PS_DDSTRREF:
.DW PS_DDMD, PS_DDFD, PS_DDRF, PS_DDIDE, PS_DDATAPI, PS_DDPPIDE
.DW PS_DDSD, PS_DDPRPSD, PS_DDPPPSD, PS_DDHDSK
;
PS_DDMD .TEXT "MD$"
PS_DDFD .TEXT "FD$"
PS_DDRF .TEXT "RF$"
PS_DDIDE .TEXT "IDE$"
PS_DDATAPI .TEXT "ATAPI$"
PS_DDPPIDE .TEXT "PPIDE$"
PS_DDSD .TEXT "SD$"
PS_DDPRPSD .TEXT "PRPSD$"
PS_DDPPPSD .TEXT "PPPSD$"
PS_DDHDSK .TEXT "HDSK$"
;
; DISK TYPE STRINGS
;
PS_DTSTRREF:
.DW PS_DTFLOP, PS_DTHARD, PS_DTCF, PS_DTSD
.DW PS_DTUSB, PS_DTROM, PS_DTRAM, PS_DTRF, PS_DTFSH
;
PS_DTFLOP .TEXT "Floppy Disk$"
PS_DTHARD .TEXT "Hard Disk$"
PS_DTCF .TEXT "CompactFlash$"
PS_DTSD .TEXT "SD Card$"
PS_DTUSB .TEXT "USB Drive$"
PS_DTROM .TEXT "ROM Disk$"
PS_DTRAM .TEXT "RAM Disk$"
PS_DTRF .TEXT "RAM Floppy$"
PS_DTFSH .TEXT "Flash Drive$"
PS_DTOTHER .TEXT "???$"
;
; FLOPPY ATTRIBUTE STRINGS
;
PS_FLP_FSTR: .TEXT "8\",$" ; PS_FLP8
.TEXT "5.25\",$" ; PS_FLP5
.TEXT "3.5\",$" ; PS_FLP3
.TEXT "???\",$" ; PS_FLPN
;
PS_FLP_SSTR: .TEXT "SS/$" ; PS_FLPSS
.TEXT "DS/$" ; PS_FLPDS
;
PS_FLP_DSTR: .TEXT "SD$" ; PS_FLPSD
.TEXT "DD$" ; PS_FLPDD
.TEXT "HD$" ; PS_FLPHD
.TEXT "ED$" ; PS_FLPED
;
; CHARACTER DEVICE STRINGS
;
PS_SDSTRREF:
.DW PS_SDUART, PS_SDASCI, PS_SDTERM, PS_SDPRPCON, PS_SDPPPCON
.DW PS_SDSIO, PS_SDACIA, PS_SDPIO, PS_SDUF, PS_SDDUART, PS_SDZ2U, PS_SDCEN
;
PS_SDUART .TEXT "UART$"
PS_SDASCI .TEXT "ASCI$"
PS_SDTERM .TEXT "TERM$"
PS_SDPRPCON .TEXT "PRPCON$"
PS_SDPPPCON .TEXT "PPPCON$"
PS_SDSIO .TEXT "SIO$"
PS_SDACIA .TEXT "ACIA$"
PS_SDPIO .TEXT "PIO$"
PS_SDUF .TEXT "UF$"
PS_SDDUART .TEXT "DUART$"
PS_SDZ2U .TEXT "Z2U$"
PS_SDCEN .TEXT "CEN$"
;
; CHARACTER SUB TYPE STRINGS
;
PS_STRS232 .TEXT "RS-232$"
PS_STTERM .TEXT "Terminal$"
PS_STPPT .TEXT "Parallel$"
;
PS_STPARMAP .DB "NONENMNS"
;
; PARALLEL TYPE STRINGS
;
PIO_MODE_STR: .TEXT "Output$"
.TEXT "Input$"
.TEXT "Bidirectional$"
.TEXT "BitCtrl$"
;
; VIDEO DEVICE STRINGS
;
PS_VDSTRREF:
.DW PS_VDVDU, PS_VDCVDU, PS_VDNEC, PS_VDTMS, PS_VDVGA
;
PS_VDVDU .TEXT "VDU$"
PS_VDCVDU .TEXT "CVDU$"
PS_VDNEC .TEXT "NEC$"
PS_VDTMS .TEXT "TMS$"
PS_VDVGA .TEXT "VGA$"
;
; VIDEO TYPE STRINGS
;
PS_VTCRT .TEXT "CRT$"
;
; SOUND DEVICE STRINGS
;
PS_SDSND .TEXT "SND$"
;
; SOUND TYPE STRINGS
;
PS_SDSN76489 .TEXT "SN76489$"
PS_SDAY38910 .TEXT "AY-3-8910$"
PS_SDBITMODE .TEXT "I/O PORT$"
;
; 0 1 2 3 4 5 6 7
; 01234567890123456789012345678901234567890123456789012345678901234567890123456789
PS_STRHDR .TEXT "Unit Device Type Capacity/Mode\r\n"
.TEXT "---------- ---------- ---------------- --------------------\r\n$"
;
;==================================================================================================
; CONSOLE CHARACTER I/O HELPER ROUTINES (REGISTERS PRESERVED)
;==================================================================================================
;
; OUTPUT CHARACTER FROM A
;
COUT:
; SAVE ALL INCOMING REGISTERS
PUSH AF
PUSH BC
PUSH DE
PUSH HL
;
; GET CURRENT CONSOLE UNIT
LD E,A ; TEMPORARILY STASH OUTPUT CHAR IN E
LD A,(CB_CONDEV) ; GET CONSOLE UNIT BYTE
CP $FF ; TEST FOR $FF (HBIOS NOT READY)
JR Z,COUT1 ; IF NOT READY, TRY DEBUG OUTPUT
;
; USE HBIOS
LD C,A ; CONSOLE UNIT TO C
LD B,BF_CIOOUT ; HBIOS FUNC: OUTPUT CHAR
CALL CIO_DISPATCH ; CALL CIO DISPATCHER DIRECTLY
JR COUT2 ; CONTINUE
;
COUT1:
;
#IF (WBWDEBUG == USEXIO)
LD A,E ; GET OUTPUT CHAR BACK TO ACCUM
CALL XIO_OUTC ; OUTPUT VIA XIO
#ENDIF
;
#IF (WBWDEBUG == USEMIO)
LD A,E
CALL MIO_OUTC ; OUTPUT VIA MIO
#ENDIF
;
COUT2:
; RESTORE ALL REGISTERS
POP HL
POP DE
POP BC
POP AF
RET
;
; INPUT CHARACTER TO A
;
CIN:
; SAVE INCOMING REGISTERS (AF IS OUTPUT)
PUSH BC
PUSH DE
PUSH HL
;
LD A,(CB_CONDEV) ; GET CONSOLE UNIT BYTE
CP $FF ; TEST FOR $FF (HBIOS NOT READY)
JR Z,CIN1 ; IF NOT READY, TRY DEBUG INPUT
;
; USE HBIOS
LD C,A ; CONSOLE UNIT TO C
LD B,BF_CIOIN ; HBIOS FUNC: INPUT CHAR
CALL CIO_DISPATCH ; CALL CIO DISPATCHER DIRECTLY
LD A,E ; RESULTANT CHAR TO A
JR CIN2 ; CONTINUE
;
CIN1:
;
#IF (WBWDEBUG == USEXIO)
CALL XIO_INC ; GET CHAR
#ENDIF
;
#IF (WBWDEBUG == USEMIO)
CALL MIO_INC ; GET CHAR
#ENDIF
;
CIN2:
;
; RESTORE REGISTERS (AF IS OUTPUT)
POP HL
POP DE
POP BC
RET
;
; RETURN INPUT STATUS IN A (0 = NO CHAR, !=0 CHAR WAITING)
;
CST:
; SAVE INCOMING REGISTERS (AF IS OUTPUT)
PUSH BC
PUSH DE
PUSH HL
;
LD A,(CB_CONDEV) ; GET CONSOLE UNIT BYTE
CP $FF ; TEST FOR $FF (HBIOS NOT READY)
JR Z,CST1 ; IIF NOT READY, TRY DEBUG DEBUG STATUS
;
; USE HBIOS
LD C,A ; CONSOLE UNIT TO C
LD B,BF_CIOIST ; HBIOS FUNC: INPUT STATUS
CALL CIO_DISPATCH ; CALL CIO DISPATCHER DIRECTLY
JR CST2 ; CONTINUE
;
CST1:
;
#IF (WBWDEBUG == USEXIO)
CALL XIO_IST ; GET STATUS
#ENDIF
;
#IF (WBWDEBUG == USEMIO)
CALL MIO_IST ; GET STATUS
#ENDIF
;
CST2:
; RESTORE REGISTERS (AF IS OUTPUT)
POP HL
POP DE
POP BC
RET
;
;==================================================================================================
; 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
;
; CONVERT AN HBIOS STANDARD HARD DISK CHS ADDRESS TO
; AN LBA ADDRESS. A STANDARD HBIOS HARD DISK IS ASSUMED
; TO HAVE 16 SECTORS PER TRACK AND 16 HEADS PER CYLINDER.
;
; INPUT: HL=TRACK, D=HEAD, E=SECTOR
; OUTPUT: DE:HL=32 BIT LBA ADDRESS (D:7 IS NOT SET IN THE RESULT)
;
HB_CHS2LBA:
;
LD A,D ; HEAD TO A
RLCA ; LEFT SHIFT TO HIGH NIBBLE
RLCA ; ... DEPENDS ON HIGH
RLCA ; ... NIBBLE BEING 0 SINCE
RLCA ; ... IT ROTATES INTO LOW NIBBLE
OR E ; COMBINE WITH SECTOR (HIGH NIBBLE MUST BE ZERO)
LD D,0
LD E,H
LD H,L
LD L,A
XOR A
RET
;
;==================================================================================================
; HBIOS GLOBAL DATA
;==================================================================================================
;
IDLECOUNT .DB 0
;
HEAPCURB .DW 0 ; MARK HEAP ADDRESS AFTER INITIALIZATION
;
HB_TICKS .FILL 4,0 ; 32 BIT TICK COUNTER
HB_SECTCK .DB TICKFREQ ; TICK COUNTER FOR FRACTIONAL SECONDS
HB_SECS .FILL 4,0 ; 32 BIT SECONDS COUNTER
;
HB_CPUTYPE .DB 0 ; 0=Z80, 1=80180, 2=SL1960, 3=ASCI BRG
HB_CPUOSC .DW CPUOSC ; ACTUAL CPU HARDWARE OSC FREQ IN KHZ
;
HB_BATCOND .DB 0 ; BATTERY CONDITION (0=LOW, 1=OK)
;
RTCDEFVAL .DB RTCDEF ; STORAGE FOR RTC DEFAULT VALUE
;
#IF (BT_REC_TYPE != BT_REC_NONE)
HB_BOOT_REC .DB 0 ; BOOT MODE (0=NORMAL, 1=RECOVERY MODE)
#ENDIF
;
STR_BANNER .DB "RomWBW HBIOS 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_RESTART .TEXT "\r\n\r\n>>> Press hardware reset button to restart system\r\n\r\n$"
;
#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_CURSEC .DB 0 ; CURRENT SECOND (TEMP)
;
HB_BCDTMP .FILL 5,0 ; BCD NUMBER STORAGE (TEMP)
;
HB_WRKBUF .FILL 512,0 ; INTERNAL DISK BUFFER
;
HB_END .EQU $
;
SLACK .EQU BNKTOP - $
.ECHO "HBIOS space remaining: "
.ECHO SLACK
.ECHO " bytes.\n"
;
#IFDEF ROMBOOT
#IF (ROMSIZE > 0)
.FILL SLACK
#ENDIF
#ENDIF
;
.END