Files
RomWBW/Source/HBIOS/hbios.asm
b1ackmai1er 17cfce2a12 md updates
Trim flash portions of driver. with flash filesystem enabled,   rad routine now used flash drive code to read 4k secctors. Twice as slow as normal rom read routine.
2020-12-13 09:37:46 +08:00

4797 lines
116 KiB
NASM

;
;==================================================================================================
; 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"
;
; 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
;
;
;
#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)
#DEFINE LED(N) PUSH AF
#DEFCONT \ LD A,~N
#DEFCONT \ OUT (LEDPORT),A
#DEFCONT \ POP AF
#ELSE
#DEFINE LED(N) \;
#ENDIF
;
;
;
#IF (INTMODE == 0)
; NO INTERRUPT HANDLING
#DEFINE HB_DI ;
#DEFINE HB_EI ;
#ENDIF
#IF ((INTMODE == 1) | (INTMODE == 2))
; MODE 1 OR 2 INTERRUPT HANDLING
#DEFINE HB_DI DI
#DEFINE HB_EI EI
#ENDIF
#IF (INTMODE > 2)
.ECHO "*** ERROR: INVALID INTMODE SETTING!!!\n"
!!! ; FORCE AN ASSEMBLY ERROR
#ENDIF
;
;
;
#IF (CTCENABLE)
CTCA .EQU CTCBASE + 0 ; CTC: CHANNEL A REGISTER ADR
CTCB .EQU CTCBASE + 1 ; CTC: CHANNEL B REGISTER ADR
CTCC .EQU CTCBASE + 2 ; CTC: CHANNEL C REGISTER ADR
CTCD .EQU CTCBASE + 3 ; CTC: CHANNEL D REGISTER ADR
#ENDIF
;
; THIS EQUATE IS UPDATED BY DRIVER INCLUDES THAT SHARE THE RTC LATCH.
; AS DRIVER IS INCLUDED, IT WILL USE .SET TO SET ANY BITS THEY OWN
; AND WANT TO SET AS DEFAULT.
;
RTCDEF .EQU 0 ; ALLOWS DRIVERS TO SET BITS
;
;
;
#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
RETI ; 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
;
.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.
;
; HBX_INVOKE IS NOT RE-ENTRANT! HB_INVBNK CAN BE USED GLOBALLY TO DETERMINE
; IF HBIOS IS ALREADY ACTIVE. HB_INVBNK WILL HAVE A VALUE != $FF WHEN HBIOS
; IS ACTIVE. ON RETURN, HB_INVBNK IS SET TO $FF TO INDICATE HBIOS IS NOT
; ACTIVE.
;
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
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
#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 ; PURGE CACHES
.DB $ED,$65
#ENDIF
RET ; DONE
#ENDIF
#IF (MEMMGR == MM_N8)
BIT 7,A ; TEST BIT 7 FOR RAM VS. ROM
JR Z,HBX_ROM ; IF NOT SET, SELECT ROM PAGE
;
HBX_RAM:
RES 7,A ; CLEAR BIT 7 FROM ABOVE
RLCA ; SCALE SELECTOR TO
RLCA ; ... GO FROM Z180 4K PAGE SIZE
RLCA ; ... TO DESIRED 32K PAGE SIZE
OUT0 (Z180_BBR),A ; WRITE TO BANK BASE
LD A,N8_DEFACR | 80H ; SELECT RAM BY SETTING BIT 7
OUT0 (N8_ACR),A ; ... IN N8 ACR REGISTER
RET ; DONE
;
HBX_ROM:
OUT0 (N8_RMAP),A ; BANK INDEX TO N8 RMAP REGISTER
XOR A ; ZERO ACCUM
OUT0 (Z180_BBR),A ; ZERO BANK BASE
LD A,N8_DEFACR ; SELECT ROM BY CLEARING BIT 7
OUT0 (N8_ACR),A ; ... IN N8 ACR REGISTER
RET ; DONE
;
#ENDIF
#IF (MEMMGR == MM_Z180)
RLCA ; RAM FLAG TO CARRY FLAG AND BIT 0
JR NC,HBX_BNKSEL1 ; IF NC, WANT ROM PAGE, SKIP AHEAD
XOR %00100001 ; SET BIT FOR HI 512K, CLR BIT 0
HBX_BNKSEL1:
RLCA ; CONTINUE SHIFTING TO SCALE SELECTOR
RLCA ; FOR Z180 4K PAGE -> DESIRED 32K PAGE
OUT0 (Z180_BBR),A ; WRITE TO BANK BASE
RET ; DONE
#ENDIF
#IF (MEMMGR == MM_Z280)
; TBD
RET ; DONE
#ENDIF
#IF (MEMMGR == MM_ZRC)
BIT 7,A ; BIT 7 SET REQUESTS RAM PAGE
JR Z,HBX_ROM ; NOT SET, SELECT ROM PAGE
RES 7,A ; RAM PAGE REQUESTED: CLEAR ROM BIT
ADD A,$10 ; ADD 16 x 32K - RAM STARTS FROM 512K
;
HBX_ROM:
OUT ($1F),A ; HCS WRITE TO THE BANK CONTROL REGISTER
RET ; DONE
#ENDIF
;
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; 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 (CPUFAM == CPU_Z280)
PUSH IY
PUSH BC
;LD C,$00
LD C,Z280_MSR
;LDCTL IY,(C)
.DB $FD,$ED,$66
POP BC
PUSH IY
DI
#ELSE
LD A,I
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)
POP IY
PUSH BC
LD C,Z280_MSR
;LDCTL (C),IY
.DB $FD,$ED,$6E
POP BC
POP IY
#ELSE
POP AF
JP PO,$+4
EI
#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
;
; 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:
LD (HBX_TGTBNK),A ; STUFF TARGET BANK TO CALL INTO CODE BELOW
LD (HBX_TGTADR),IX ; STUFF ADDRESS TO CALL INTO CODE BELOW
LD A,(HB_CURBNK) ; GET CURRENT BANK
PUSH AF ; SAVE FOR RETURN
HBX_TGTBNK .EQU $ + 1
LD A,$FF ; LOAD BANK TO CALL ($FF OVERLAID AT ENTRY)
CALL HBX_BNKSEL ; ACTIVATE THE NEW BANK
HBX_TGTADR .EQU $ + 1
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
CALL HBX_BNKSEL
LD SP,0 ; RESTORE ORIGINAL STACK FRAME
HBX_PPSP .EQU $ - 2
RET
;
; 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)
.ECHO "*** ERROR: INTERRUPT STACK IS TOO SMALL!!!\n"
!!! ; FORCE AN ASSEMBLY ERROR
#ENDIF
;
#IF (INTMODE == 2)
;
; HBIOS INTERRUPT SLOT ASSIGNMENTS
;
; # Z80 Z180
; --- -------------- --------------
; 0 CTC0A INT1 -+
; 1 CTC0B INT2 |
; 2 CTC0C TIM0 |
; 3 CTC0D TIM1 |
; 4 DMA0 +- Z180 INTERNAL
; 5 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)
;
; COMMON INTERRUPT DISPATCHING CODE
; SETUP AND CALL HANDLER IN BIOS BANK
;
HBX_INT: ; COMMON INTERRUPT ROUTING CODE
;
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
;
; 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 5,0 ; FILLER, RESERVED FOR FUTURE HBIOS USE
.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 HB_ENTRYTBL + 256 - $
;
.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)
;
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:
DI ; NO INTERRUPTS
IM 1 ; INTERRUPT MODE 1
;
#IF (DIAGENABLE)
LD A,%00000001
OUT (DIAGPORT),A
#ENDIF
;
LD SP,HBX_LOC ; SETUP INITIAL STACK JUST BELOW HBIOS PROXY
;
#IF (CPUFAM == CPU_Z280)
;LD C,Z280_CCR ; CACHE CONTROL REGISTER
;LD HL,$0060 ; DISABLE INSTRUCTION CACHE
;;LDCTL (C),HL ; SET IT (8 BITS)
;.DB $ED,$6E
;;PCACHE ; PURGE ANY REMNANTS OF CACHE
;.DB $ED,$65
;
LD C,Z280_BTCR ; BUS TIMING AND CONTROL REGISTER
LD HL,$0033 ; 3 I/O WAIT STATES ADDED
;LD HL,$00F3 ; 3 I/O W/S & 3 INT ACK W/S
;LDCTL (C),HL ; SET IT (8 BITS)
.DB $ED,$6E
#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
; SEEMS COUNTER-INTUITIVE AND 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
#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_Z2)
; SET PAGING REGISTERS
#IFDEF ROMBOOT
XOR A
OUT (MPGSEL_0),A
INC A
OUT (MPGSEL_1),A
#ENDIF
LD A,62
OUT (MPGSEL_2),A
INC A
OUT (MPGSEL_3),A
; ENABLE PAGING
LD A,1
OUT (MPGENA),A
#ENDIF
;
DIAG(%00000011)
LED($00)
;
; CHECK BATTERY BACKUP STATUS BEFORE WE COPY PROXY TO UPPER MEMORY
;
; IF A DS1210 POWER CONTROLLER IS INSTALLED AND BATTERY BACKUP IS NOT INSTALLED
; OR IS LESS THAN 2V THEN THE DS1210 WILL BLOCK THE SECOND RAM ACCESS.
; FAILURE TO COMPLETE TWO RAM ACCESSES BEFORE INSTALLING PROXY WILL RESULT
; IN THE ROM ID BYTES NOT BEING COPIED CORRECTLY AND CP/M APPLICATIONS
; WILL NOT START CORRECTLY WHEN THEY CHECK THE ROM ID VERSION BYTES.
; THE BATTERY CONDITION VALUE IS TEMPORARILY STORED AT HBX_LOC - 1.
; IF THERE IS NO DS1210 IN THE SYSTEM, THE CODE BELOW DOES NO HARM.
;
DEC SP ; RESERVE A STACK BYTE
XOR A ; ZERO MEANS LOW BAT
LD (HBX_LOC - 1),A ; WRITE IT (SHOULD ALWAYS WORK)
INC A ; 1 MEANS BAT OK
LD (HBX_LOC - 1),A ; OVERWRITE IF NVC ALLOWS IT
;
; IF APPBOOT, SAVE CURRENT BANKID
;
; THIS IS NOT GOING TO WORK IF THE APP BOOT IMAGE IS LOADED
; USING THE UNA FAT32 LOADER. SHOULD PROBABLY CHECK THAT THERE
; IS A VALID ROMWBW PROXY IN MEMORY BEFORE DOING THIS. HOWEVER,
; THIS USE CASE IS PROBABLY NON-EXISTENT. THE IMG BOOT IMAGE
; SHOULD WORK FINE WITH THE UNA FAT32 LOADER.
;
#IFDEF APPBOOT
LD A,(HB_CURBNK)
DEC SP ; RESERVE A STACK BYTE
LD (HBX_LOC - 2),A ; SAVE BANK
PUSH AF ; ALSO ON STACK
#ENDIF
;
; INSTALL PROXY IN UPPER MEMORY
;
LD DE,HBX_LOC ; AS PER ABOVE
LD HL,HBX_IMG
LD BC,HBX_SIZ
LDIR
;
; IF APPBOOT, RESTORE CURRENT BANK ID
;
#IFDEF APPBOOT
POP AF
LD (HB_CURBNK),A
#ENDIF
;
; 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
CALL HBX_BNKCPY
;DI
;
; TRANSITION TO HBIOS IN RAM BANK
;
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!
;
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
;
DIAG(%00000111)
;
LD A,(HBX_LOC - 1) ; RECALL BATTERY STATE AND SAVE
LD (HB_BATCOND),A ; FOR FUTURE REFERENCE
;
LD SP,HBX_LOC ; RESET STACK SINCE WE DO NOT RETURN
LD A,TRUE ; ACCUM := TRUE
LD (HB_RAMFLAG),A ; SET RAMFLAG
;
; IF APPBOOT, WE NEED TO FIX UP A FEW THINGS IN PAGE ZERO
;
#IFDEF APPBOOT
;
; GET AND SAVE APP BOOT BANK ID
LD A,(HBX_LOC - 2)
LD (HB_APPBNK),A
; MAKE SURE RST 08 VECTOR IS RIGHT
LD A,$C3
LD ($0008),A
LD HL,HB_INVOKE
LD ($0009),HL
;
; MAKE SURE IM1 INT VECTOR IS RIGHT
#IF (INTMODE == 1)
; JP INT_IM1 IF INTERRUPT MODE ACTIVE
LD A,$C3
LD ($0038),A
LD HL,INT_IM1
LD ($0039),HL
#ELSE
; RETI ($ED, $4D) IF NON-INTERRUPT MODE
LD HL,$0038
LD (HL),$ED
INC HL
LD (HL),$4D
#ENDIF
#ENDIF
;
;==================================================================================================
; 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)
#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 (DSKYENABLE)
LD HL,MSG_HBVER + 5
LD A,(DSKY_NUMS + RMJ)
AND $7F
LD (HL),A
INC HL
LD A,(DSKY_NUMS + RMN)
AND $7F
LD (HL),A
INC HL
LD A,(DSKY_NUMS + RUP)
LD (HL),A
LD HL,MSG_HBVER
CALL DSKY_SHOWSEG
#ENDIF
;
#IF (WBWDEBUG == USEMIO) ; BUFFER OUTPUT UNTIL
CALL MIO_INIT ; WE GET TO BOOT MESSAGE
#ENDIF
;
#IF 0
;
; 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 (CPUFAM == CPU_Z180)
;
; AT BOOT, Z180 PHI IS OSC / 2
LD C,(CPUOSC / 2) / 1000000
LD DE,(CPUOSC / 2) / 1000
;
#IF (Z180_CLKDIV >= 1)
LD A,(HB_CPUTYPE) ; GET CPU TYPE
CP 2 ; Z8S180 REV K OR BETTER?
JR C,HB_CPU2 ; IF NOT, NOT POSSIBLE!
; SET CLOCK DIVIDE TO 1 RESULTING IN FULL XTAL SPEED
LD A,$80
OUT0 (Z180_CCR),A
; REFLECT SPEED CHANGE
LD C,CPUOSC / 1000000
LD DE,CPUOSC / 1000
#ENDIF
#IF (Z180_CLKDIV >= 2)
LD A,(HB_CPUTYPE) ; GET CPU TYPE
CP 3 ; Z8S180 REV N OR BETTER?
JR C,HB_CPU2 ; IF NOT, NOT POSSIBLE!
; SET CPU MULTIPLIER TO 1 RESULTING IN XTAL * 2 SPEED
; ALSO SET CCR AGAIN BECAUSE OF REPORTS THAT CCR
; *MUST* BE SET AFTER CMR.
LD A,$80
OUT0 (Z180_CMR),A ; CPU MULTIPLIER
OUT0 (Z180_CCR),A ; CLOCK DIVIDE
; REFLECT SPEED CHANGE
LD C,(CPUOSC * 2) / 1000000
LD DE,(CPUOSC * 2) / 1000
#ENDIF
;
HB_CPU2:
; SAVE CPU SPEED IN CONFIG BLOCK
LD A,C
LD (CB_CPUMHZ),A
LD (CB_CPUKHZ),DE
;
#ENDIF
;
DIAG(%00011111)
;
; PERFORM DYNAMIC CPU SPEED DERIVATION
;
CALL HB_CPUSPD ; CPU SPEED DETECTION
;
CALL DELAY_INIT ; INITIALIZE SPEED COMPENSATED DELAY FUNCTIONS
;
#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) ; GET IT
.DB $ED,$66
LD A,L ; PUT IN A
AND %00111100 ; CLEAR DC AND I/O FIELDS
OR Z280_INTWAIT << 6 ; SET INT ACK WAIT STATES
OR Z280_IOWAIT ; SET I/O WAIT STATES
LD L,A ; BACK TO L
;LDCTL (C),HL ; SET IT
.DB $ED,$6E
;
LD C,Z280_BTIR ; BUS TIMING AND INIT REG
;LDCTL HL,(C) ; GET IT
.DB $ED,$66
LD A,L ; PUT IN A
AND %11110011 ; CLEAR DC AND I/O FIELDS
OR Z280_MEMWAIT << 2 ; SET LOW MEM WAIT STATES
LD L,A ; BACK TO L
;LDCTL (C),HL ; SET IT
.DB $ED,$6E
;
#ENDIF
;
#IF (INTMODE == 2)
; SETUP Z80 IVT AND INT MODE 2
LD A,HBX_IVT >> 8 ; SETUP HI BYTE OF IVT ADDRESS
LD I,A ; ... AND PLACE IT IN I REGISTER
#IF (CPUFAM == CPU_Z180)
; SETUP Z180 IVT
XOR A ; SETUP LO BYTE OF IVT ADDRESS
OUT0 (Z180_IL),A ; ... AND PLACE IN Z180 IL REGISTER
#ENDIF
IM 2 ; SWITCH TO INT MODE 2
#ENDIF
;
#IF (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)
LD A,%11111001 ; RESET ALL DEVICES, SET DAISYCHAIN
OUT (KIOBASE+$0E),A ; DO IT
#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
;
; 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
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
;
; 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 0
;
; TEST DEBUG ***************************************************************************************
;
CALL NEWLINE
CALL REGDMP
;
; TEST DEBUG ***************************************************************************************
;
#ENDIF
;
; PRE-CONSOLE INITIALIZATION
;
LD A,FORCECON ; CALCULATE PRE-INIT TABLE ; A IS INDEX OF CONSOLE DEVICE ENTRY
RLCA ; ENTRY THAT WE WANT TO ; A IS OFFSET OF CONSOLE DEVICE ENTRY
LD DE,(HB_PCINITTBL) ; EXECUTE FIRST ; DE IS VALUE OF TOP ENTRY
LD HL,HB_PCINITTBL ; HL IS ADDRESS OF TOP OF TABLE
PUSH HL ; PUSH (1) TOP OF TABLE
PUSH DE ; PUSH (2) VALUE OF TOP ENTRY
PUSH HL ; PUSH (3) TOP OF TABLE
CALL ADDHLA ; HL IS ADDRESS OF DESIRED CONSOLE ENTRY
POP DE ; PLACE IT AT THE TOP OF THE ; POP (3) DE IS TOP OF TABLE
PUSH HL ; TABLE BY SWAPPING IT ; PUSH (3) ADDRESS OF DESIRED CONSOLE ENTRY
LDI ; WITH THE FIRST (DUMMY) ; COPY DESIRED ENTRY
LDI ; ENTRY ; ... TO TOP OF TABLE
POP HL ; POP (3) HL IS ADDRESS OF DESIRED CONSOLE ENTRY
POP DE ; POP (2) DE IS VALUE OF ORIGINAL TOP ENTRY
LD (HL),E ; SAVE DE OVER ORIGINAL ENTRY
INC HL
LD (HL),D
LD B,HB_PCINITTBLLEN
POP DE ; POP (1) DE IS ADDRESS OF TOP OF TABLE
#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 0
;
; TEST DEBUG ***************************************************************************************
;
CALL NEWLINE
CALL REGDMP
;
; TEST DEBUG ***************************************************************************************
;
#ENDIF
;
DIAG(%01111111)
LED($FF)
;
; 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 ; INITIALLY, FIRST SERIAL UNIT IS CONSOLE
LD (CB_CONDEV),A ; SAVE IT, ACTIVATES CONSOLE ON HBIOS
#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 0
;
; 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 0
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 0
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) ; GET IT
.DB $ED,$66
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
XOR A
#IF (CPUFAM == CPU_Z180)
LD A,Z180_MEMWAIT
#ENDIF
#IF (CPUFAM == CPU_Z280)
LD A,Z280_MEMWAIT
#ENDIF
CALL PRTDECB
CALL PRTSTRD
.TEXT " MEM W/S, $"
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
;
; DISPLAY MEMORY CONFIG
;
CALL NEWLINE
LD HL,ROMSIZE
CALL PRTDEC
CALL PRTSTRD
.TEXT "KB ROM, $"
LD HL,RAMSIZE
CALL PRTDEC
CALL PRTSTRD
.TEXT "KB RAM$"
;
#IF (CPUFAM == CPU_Z280)
CALL NEWLINE
PRTS("Z280: $")
PRTS("MSR=$")
LD C,Z280_MSR ; MASTER STATUS REGISTER
;LDTCL HL,(C) ; GET VALUE
.DB $ED,$66
CALL PRTHEXWORDHL
CALL PC_SPACE
PRTS("BTCR=$")
LD C,Z280_BTCR ; BUS TIMING AND CONTROL REGISTER
;LDTCL HL,(C) ; GET VALUE
.DB $ED,$66
CALL PRTHEXWORDHL
CALL PC_SPACE
PRTS("BTIR=$")
LD C,Z280_BTIR ; BUS TIMING AND CONTROL REGISTER
;LDTCL HL,(C) ; GET VALUE
.DB $ED,$66
CALL PRTHEXWORDHL
CALL PC_SPACE
PRTS("CCR=$")
LD C,Z280_CCR ; CACHE CONTROL REGISTER
;LDTCL HL,(C) ; GET VALUE
.DB $ED,$66
CALL PRTHEXWORDHL
#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
;
; 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))
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
;
; CHAIN TO OS LOADER
;
#IFDEF ROMBOOT
; PERFORM BANK CALL TO OS IMAGES BANK IN ROM
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!
#ELSE
; COPY OS IMAGE: BID_USR:<IMG START> --> BID_USR:0
;LD A,BID_USR
;LD (HB_SRCBNK),A
;LD (HB_DSTBNK),A
;LD HL,HB_END
;LD DE,0
;LD BC,$8000
;CALL HBX_BNKCPY
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 OF HBIOS
LD DE,0 ; TO USER ADDRESS 0
RST 08 ; DO IT
;
; PERFORM BANK CALL TO USER BANK
LD A,BID_USR ; CHAIN TO OS IMAGES BANK
LD IX,0 ; ENTER AT ADDRESS 0
CALL HBX_BNKCALL ; GO THERE
HALT ; WE SHOULD NEVER COME BACK!
;
#ENDIF
;
RET
;
; 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)
.DW UART_PREINIT
; .DW CALLDUMMY
#ENDIF
HB_PCINITRLEN .EQU (($ - HB_PCINIT_REC) / 2)
;
HB_INIT_REC:
#IF (PLATFORM == PLT_SBC)
.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:
.DW CALLDUMMY ; RESERVED FOR FORCING PRIMARY CONSOLE
#IF (ASCIENABLE)
.DW ASCI_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 (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 (CTCENABLE)
.DW CTC_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 (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 (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 (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 (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 0 ; *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 0 ; *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
;
#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
;
#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($FF)
#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
;
;==================================================================================================
; 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 15 ; 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
CALL SYSCHK
LD A,ERR_NOFUNC
OR A ; SIGNAL ERROR
RET
;
; SOFT RESET HBIOS, RELEASE HEAP MEMORY NOT USED BY HBIOS
;
SYS_RESINT:
LD HL,(HEAPCURB) ; GET HBIOS HEAP THRESHOLD
LD (CB_HEAPTOP),HL ; RESTORE HEAP TOP
XOR A
RET
;
; GO BACK TO ROM BOOT LOADER
;
SYS_RESWARM:
CALL SYS_RESINT
; 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!
;
; RESTART SYSTEM AS THOUGH POWER HAD JUST BEEN TURNED ON
;
SYS_RESCOLD:
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
;
; 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 EXTABLISH UPPER MEMORY STACK BEFORE INVOKING THIS FUNCTION!
;
SYS_SETBNK:
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
;
; 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
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)
XOR A
RET
;
; GET MEMORY INFORMATION
; RETURNS:
; D: COUNT OF ROM BANKS
; E: COUNT OF RAM BANKS
;
SYS_GETMEMINFO:
LD D,ROMSIZE / 32
LD E,RAMSIZE / 32
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 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
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
;
; 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,$00
LD C,Z280_MSR
;LDCTL IY,(C)
.DB $FD,$ED,$66
PUSH IY
DI
#ELSE
LD A,I
DI
PUSH AF
#ENDIF
#ENDIF
CALL HBX_PEEK ; IMPLEMENTED IN PROXY
#IF (INTMODE == 1)
#IF (CPUFAM == CPU_Z280)
;LD C,$00
LD C,Z280_MSR
POP IY
;LDCTL (C),IY
.DB $FD,$ED,$6E
POP IY
#ELSE
POP AF
JP PO,$+4
EI
#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)
.DB $FD,$ED,$66
PUSH IY
DI
#ELSE
LD A,I
DI
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
.DB $FD,$ED,$6E
POP IY
#ELSE
POP AF
JP PO,$+4
EI
#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
;
; TIMER HANDLER VECTORS
; THESE CAN BE HOOKED AS DESIRED BY DRIVERS
;
HB_TIMINT:
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 (PLATFORM == PLT_EZZ80)
; PULSE WATCHDOG
OUT (WDOGIO),A ; VALUE IS IRRELEVANT
#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 0 ; *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
;
; 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
;
;==================================================================================================
; 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 (INTRTCENABLE)
ORG_INTRTC .EQU $
#INCLUDE "intrtc.asm"
SIZ_INTRTC .EQU $ - ORG_INTRTC
.ECHO "INTRTC occupies "
.ECHO SIZ_INTRTC
.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 (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
;
; 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
;
SIZ_FONTS .EQU $ - ORG_FONTS
.ECHO " occupy "
.ECHO SIZ_FONTS
.ECHO " bytes.\n"
;
#IF (CVDUENABLE | VGAENABLE)
ORG_KBD .EQU $
#INCLUDE "kbd.asm"
SIZ_KBD .EQU $ - ORG_KBD
.ECHO "KBD occupies "
.ECHO SIZ_KBD
.ECHO " bytes.\n"
#ENDIF
;
#IF (VDUENABLE | (TMSENABLE & (PLATFORM == PLT_N8)))
ORG_PPK .EQU $
#INCLUDE "ppk.asm"
SIZ_PPK .EQU $ - ORG_PPK
.ECHO "PPK occupies "
.ECHO SIZ_PPK
.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 (PIO_4P | PIO_ZP | PPI_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 (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
;
#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
#INCLUDE "dsky.asm"
#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 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
;
LD (CB_CPUKHZ),HL
LD DE,1000
CALL DIV16
LD A,C
LD (CB_CPUMHZ),A
;
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
;
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/ROM 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
;DEC A
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
.DW PS_SDPRPCON, PS_SDPPPCON, PS_SDSIO, PS_SDACIA, PS_SDPIO,PS_SDUF,PS_SDDUART
;
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 "PORT$"
PS_SDUF .TEXT "UF$"
PS_SDDUART .TEXT "DUART$"
;
; 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
;
RTCVAL .DB RTCDEF ; SHADOW VALUE FOR RTC LATCH PORT
;
HB_BATCOND .DB 0 ; BATTERY CONDITION (0=LOW, 1=OK)
;
#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)? $"
;
#IF (DSKYENABLE) ; 'H','B','I','O',' ',' ',' ',' '
MSG_HBVER .DB $BE,$FF,$8A,$FB,$80,$80,$80,$80 ; "HBIO "
#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
.FILL SLACK
#ENDIF
;
.END