From 328453309a9a4febbed5612d8e9fbb3b69f70c73 Mon Sep 17 00:00:00 2001
From: b1ackmai1er <39449559+b1ackmai1er@users.noreply.github.com>
Date: Tue, 9 Apr 2019 23:26:55 +0800
Subject: [PATCH] Fix IM2 assembly error. Add 2nd PIO on ECB-ZP
---
hbios.asm | 3398 +++++++++++++++++++++++++++++++++++++++++++++++++++++
pio.asm | 695 +++++++++++
2 files changed, 4093 insertions(+)
create mode 100644 hbios.asm
create mode 100644 pio.asm
diff --git a/hbios.asm b/hbios.asm
new file mode 100644
index 00000000..69f95dfb
--- /dev/null
+++ b/hbios.asm
@@ -0,0 +1,3398 @@
+;
+;==================================================================================================
+; 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 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 ((PLATFORM == PLT_RC) | (PLATFORM == PLT_RC180))
+#DEFINE DIAGP $00
+#ENDIF
+;
+#IFDEF DIAGP
+#DEFINE DIAG(N) PUSH AF
+ #DEFCONT \ LD A,N
+ #DEFCONT \ OUT (DIAGP),A
+ #DEFCONT \ POP AF
+#ELSE
+#DEFINE DIAG(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
+;
+;
+;
+#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) 2015, 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
+;
+; 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 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
+;==================================================================================================
+;
+HBX_INVOKE:
+ LD (HBX_INVSP),SP ; SAVE ORIGINAL STACK FRAME
+ LD A,(HB_CURBNK) ; GET CURRENT BANK
+ LD (HB_INVBNK),A ; SAVE INVOCATION BANK
+
+ LD SP,HBX_TMPSTK ; USE SMALL TEMP STACK FRAME IN HI MEM FOR BANK SWITCH
+ 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_TMPSTK ; USE SMALL TEMP STACK FRAME IN HI MEM FOR BANK SWITCH
+ 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
+
+ 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 ((PLATFORM == PLT_SBC) | (PLATFORM == PLT_ZETA))
+ #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 ((PLATFORM == PLT_ZETA2) | (PLATFORM == PLT_RC) | (PLATFORM == PLT_RC180) | (PLATFORM == PLT_EZZ80))
+ 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
+ ;OUT (DIAGP),A ; *DEBUG*
+ INC A ;
+ OUT (MPGSEL_1),A ; BANK_1: 16K - 32K
+ RET ; DONE
+#ENDIF
+#IF (PLATFORM == PLT_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 (PLATFORM == PLT_MK4)
+ 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
+;
+;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+; Copy Data - Possibly between banks. This resembles CP/M 3, but
+; usage of the HL and DE registers is reversed.
+; Caller MUST ensure stack is already in high memory.
+; Caller MUST preset HBX_SRCBNK and HBX_DSTBNK.
+; Caller MUST disable ints if IM1 active
+; Enter:
+; HL = Source Address
+; DE = Destination Address
+; BC = Number of bytes to copy
+; Exit : None
+; Uses : AF,BC,DE,HL
+;
+;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+;
+HBX_BNKCPY:
+ 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
+ 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 TARGTET BANK IS PREPARED FOR THEM.
+; ON INPUT A=TARGET BANK, HL=TARGET ADDRESS
+;
+HBX_BNKCALL:
+ LD (HBX_TGTBNK),A ; STUFF TARGET BANK TO CALL INTO CODE BELOW
+ LD (HBX_TGTADR),HL ; 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
+; CALLER MUST DISABLE INTS IF IM1 AND ACCESSING PAGE W/O IM1 INT VECTOR
+;
+HBX_PEEK:
+ LD (HBX_PPSP),SP ; SAVE ORIGINAL STACK FRAME
+ LD SP,HBX_TMPSTK ; USE SMALL TEMP STACK FRAME IN HI MEM
+ 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_TMPSTK ; USE SMALL TEMP STACK FRAME IN HI MEM
+ 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
+;
+; SMALL TEMPORARY STACK FOR USE BY INVOKE, PEEK, AND POKE
+;
+ .FILL 20,$CC ; 10 LEVEL STACK
+HBX_TMPSTK .EQU $
+;
+; PRIVATE STACK AT END OF HBIOS CODE
+; OCCUPIES SPACE BEFORE IVT
+;
+HBX_STKSIZ .EQU $FF00 - $
+ .ECHO "HBIOS PROXY STACK space: "
+ .ECHO HBX_STKSIZ
+ .ECHO " bytes.\n"
+ .FILL HBX_STKSIZ,$FF
+HBX_STACK .EQU $
+;
+#IF (INTMODE == 2)
+;
+; HBIOS INTERRUPT VECTOR TABLE (16 ENTRIES)
+;
+HBX_IVT:
+ .DW INT_BAD ; IVT_INT1
+ .DW INT_BAD ; IVT_INT2
+ .DW INT_BAD ; IVT_TIM0
+ .DW INT_BAD ; IVT_TIM1
+ .DW INT_BAD ; IVT_DMA0
+ .DW INT_BAD ; IVT_DMA1
+ .DW INT_BAD ; IVT_CSIO
+ .DW INT_BAD ; IVT_SER0
+ .DW INT_BAD ; IVT_SER1
+ .DW INT_BAD ; IVT_PIO0
+ .DW INT_BAD ; IVT_PIO1
+ .DW INT_BAD ; IVT_PIO2
+ .DW INT_BAD ; IVT_PIO3
+ .DW INT_BAD ;
+ .DW INT_BAD ;
+ .DW INT_BAD ;
+;
+HBX_IVTCNT .EQU ($ - HBX_IVT) / 2
+;
+HBX_ITBL:
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+ .DW HB_BADINT
+#ENDIF
+;
+; INTERRUPT HANDLER STUBS
+;
+; THE FOLLOWING INTERRUPT STUBS RECEIVE CONTROL FROM THE
+; INTERRUPT, SETUP A HANDLER VECTOR IN HBIOS AND THEN
+; BRANCH TO THE COMMON INTERRUPT DISPATCHER
+;
+;
+INT_IM1:
+#IF (INTMODE == 1)
+ PUSH HL ; SAVE HL
+ LD HL,HB_IM1INT ; HL := IM1 INT HANDLER IN BIOS BANK
+ JR HBX_INT ; TO TO ROUTING CODE
+#ELSE
+ RETI ; UNEXPECTED INT, RET W/ INTS LEFT DISABLED
+#ENDIF
+;
+#IF (INTMODE == 2)
+;
+INT_BAD: ; BAD INTERRUPT HANDLER
+ PUSH HL ; SAVE HL
+ LD HL,HB_BADINT ; HL := INT HANDLER IN BIOS BANK
+ JR HBX_INT ; GO TO ROUTING CODE
+;
+INT_TIMER: ; TIMER INTERRUPT HANDLER
+ PUSH HL ; SAVE HL
+ LD HL,HB_TIMINT ; HL := INT ADR IN BIOS
+ JR HBX_INT ; GO TO ROUTING CODE
+;
+ #IF (SIOENABLE)
+INT_SIO: ; SIO INTERRUPT HANDLER
+ PUSH HL ; SAVE HL
+ LD HL,SIO_INT ; HL := SIO INT HANDLER IN BIOS BANK
+ JR HBX_INT ; GO TO ROUTING CODE
+ #ENDIF
+;
+#IF (PIO_ZP)
+INT_ZP0: ; PIO INTERRUPT HANDLER
+ PUSH HL ; SAVE HL
+ LD HL,PIO0INT ; HL := PIO INT HANDLER IN BIOS BANK
+ JR HBX_INT ; GO TO ROUTING CODE
+INT_ZP1
+ PUSH HL ; SAVE HL
+ LD HL,PIO1INT ; HL := PIO INT HANDLER IN BIOS BANK
+ JR HBX_INT ; GO TO ROUTING CODE
+#ENDIF
+;
+#IF (PIO_4P)
+INT_4P0: ; PIO INTERRUPT HANDLER
+ PUSH HL ; SAVE HL
+ LD HL,PIO2INT ; HL := PIO INT HANDLER IN BIOS BANK
+ JR HBX_INT ; GO TO ROUTING CODE
+INT_4P1:
+ PUSH HL ; SAVE HL
+ LD HL,PIO3INT ; HL := PIO INT HANDLER IN BIOS BANK
+ JR HBX_INT ; GO TO ROUTING CODE
+#ENDIF
+;
+#ENDIF
+;
+#IF (INTMODE > 0)
+;
+; COMMON INTERRUPT DISPATCHING CODE
+; SETUP AND CALL HANDLER IN BIOS BANK
+;
+HBX_INT: ; COMMON INTERRUPT ROUTING CODE
+;
+ LD (HBX_INT_SP),SP ; SAVE ORIGINAL STACK FRAME
+ LD SP,HBX_STACK ; USE 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_IMG0 ; HBIOS BANK
+ CALL HBX_BNKSEL_INT ; SELECT IT
+
+ CALL JPHL ; CALL INTERRUPT ROUTINE
+
+ 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
+;
+; FILL TO START OF BOUNCE BUFFER
+;
+HBX_INTFILL .EQU (HBX_XFC - HBX_BUFSIZ - $)
+ .ECHO "HBIOS INT space remaining: "
+ .ECHO HBX_INTFILL
+ .ECHO " bytes.\n"
+ .FILL HBX_INTFILL,$FF
+;
+; INTERBANK COPY BUFFER (64 BYTES)
+;
+HBX_BUF .FILL HBX_BUFSIZ,0
+;
+; HBIOS PROXY MGMT BLOCK (TOP 32 BYTES)
+;
+#IFDEF ROMBOOT
+ .DB BID_BOOT ; CURRENTLY ACTIVE LOW MEMORY BANK ID
+#ELSE
+ .DB BID_USR ; CURRENTLY ACTIVE LOW MEMORY BANK ID
+#ENDIF
+ .DB 0 ; BANK ACTIVE AT TIME OF HBIOS CALL INVOCATION
+ .DW 0 ; BNKCPY SOURCE ADDRESS
+ .DB BID_USR ; BNKCPY SOURCE BANK ID
+ .DW 0 ; BNKCPY DESTINATION ADDRESS
+ .DB BID_USR ; BNKCPY DESTINATION BANK ID
+ .DW 0 ; BNKCPY LENGTH
+ .FILL 6,0 ; FILLER, RESERVED FOR FUTURE HBIOS USE
+ JP HBX_INVOKE ; FIXED ADR ENTRY FOR HBX_INVOKE (ALT FOR RST 08)
+ JP HBX_BNKSEL ; FIXED ADR ENTRY FOR HBX_BNKSEL
+ JP HBX_BNKCPY ; FIXED ADR ENTRY FOR HBX_BNKCPY
+ JP HBX_BNKCALL ; FIXED ADR ENTRY FOR HBX_BNKCALL
+ .DW HBX_IDENT ; ADDRESS OF HBIOS PROXY START (DEPRECATED)
+ .DW HBX_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
+
+;
+;==================================================================================================
+; SYSTEM INITIALIZATION
+;==================================================================================================
+;
+HB_START:
+ DI ; NO INTERRUPTS
+ IM 1 ; INTERRUPT MODE 1
+;
+#IFDEF DIAGP
+ LD A,%00000001
+ OUT (DIAGP),A
+#ENDIF
+;
+ LD SP,HBX_LOC ; SETUP INITIAL STACK JUST BELOW HBIOS PROXY
+;
+#IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4) | (PLATFORM == PLT_RC180))
+ ; SET BASE FOR CPU IO REGISTERS
+ LD A,Z180_BASE
+ OUT0 (Z180_ICR),A
+
+ DIAG(%00000010)
+
+ ; DISABLE REFRESH
+ XOR A
+ OUT0 (Z180_RCR),A
+
+ ; MASK OFF TIMER INTERRUPTS
+ XOR A
+ OUT0 (Z180_TCR),A
+
+ ; SET DEFAULT CPU CLOCK MULTIPLIERS (XTAL / 2)
+ XOR A
+ OUT0 (Z180_CCR),A
+ OUT0 (Z180_CMR),A
+
+ ; SET DEFAULT WAIT STATES
+ LD A,$F0
+ OUT0 (Z180_DCNTL),A
+
+#IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4))
+ ; 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
+
+#IF (Z180_CLKDIV >= 1)
+ ; SET CLOCK DIVIDE TO 1 RESULTING IN FULL XTAL SPEED
+ LD A,$80
+ OUT0 (Z180_CCR),A
+#ENDIF
+
+#IF (Z180_CLKDIV >= 2)
+ ; SET CPU MULTIPLIER TO 1 RESULTING IN XTAL * 2 SPEED
+ LD A,$80
+ OUT0 (Z180_CMR),A
+#ENDIF
+
+#ENDIF
+;
+#IF ((PLATFORM == PLT_ZETA2) | (PLATFORM == PLT_RC) | (PLATFORM == PLT_RC180) | (PLATFORM == PLT_EZZ80))
+ ; 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)
+;
+; INSTALL PROXY IN UPPER MEMORY
+;
+
+;X1 .EQU $8000
+;X2 .EQU X1 + 2
+;X3 .EQU X2 + 2
+;X4 .EQU X3 + 2
+
+; LD HL,(HBX_IMG)
+; LD (X1),HL
+
+; LD HL,(HBX_IMG)
+; LD (X2),HL
+
+ LD HL,HBX_IMG
+ LD DE,HBX_LOC
+ LD BC,HBX_SIZ
+ LDIR
+
+; LD HL,(HBX_IMG)
+; LD (X3),HL
+
+; LD HL,(HBX_LOC)
+; LD (X4),HL
+
+;
+; 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
+;
+; TRANSITION TO HBIOS IN RAM BANK
+;
+ LD A,BID_BIOS ; BIOS BANK ID
+ LD HL,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 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
+ ; MAKE SURE RST 08 VECTOR IS RIGHT
+ LD A,$C3
+ LD ($0008),A
+ LD HL,HB_INVOKE
+ LD ($0009),HL
+;
+ ; MAKE SURE IM1 INT VECTOR IS RIGHT
+ #IF (INTMODE == 1)
+ ; JP INT_IM1 IF INTERRUPT MODE ACTIVE
+ LD A,$C3
+ LD ($0038),A
+ LD HL,INT_IM1
+ LD ($0039),HL
+ #ELSE
+ ; RETI ($ED, $4D) IF NON-INTERRUPT MODE
+ LD HL,($0038)
+ LD (HL),$ED
+ INC HL
+ LD (HL),$4D
+ #ENDIF
+#ENDIF
+;
+ DIAG(%00001111)
+;
+#IF (DSKYENABLE)
+ LD HL,MSG_HBVER
+ CALL DSKY_SHOWSEG
+#ENDIF
+;
+; PERFORM DYNAMIC CPU SPEED DERIVATION
+;
+ CALL HB_CPUSPD ; CPU SPEED DETECTION
+;
+#IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4) | (PLATFORM == PLT_RC180))
+;
+ ; SET DESIRED WAIT STATES
+ LD A,0 + (Z180_MEMWAIT << 6) | (Z180_IOWAIT << 4)
+ OUT0 (Z180_DCNTL),A
+;
+#ENDIF
+;
+ CALL DELAY_INIT ; INITIALIZE SPEED COMPENSATED DELAY FUNCTIONS
+;
+ DIAG(%00011111)
+;
+; 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)
+;
+; PRE-CONSOLE INITIALIZATION
+;
+#IF (ASCIENABLE)
+ CALL ASCI_PREINIT
+#ENDIF
+#IF (UARTENABLE)
+ CALL UART_PREINIT
+#ENDIF
+#IF (SIOENABLE)
+ CALL SIO_PREINIT
+#ENDIF
+#IF (ACIAENABLE)
+ CALL ACIA_PREINIT
+#ENDIF
+#IF (PIO_4P | PIO_ZP)
+ CALL PIO_PREINIT
+#ENDIF
+;
+ DIAG(%01111111)
+;
+; PRIOR TO THIS POINT, CONSOLE I/O WAS DIRECTED TO HARDWARE (XIO.ASM).
+; 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
+;
+; ANNOUNCE HBIOS
+;
+ CALL NEWLINE2
+ PRTX(STR_BANNER)
+;
+ 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
+;
+; SETUP INTERRUPT VECTORS, AS APPROPRIATE
+;
+;#IF (INTMODE == 1)
+; ; OVERLAY $0038 WITH JP INT_IM1
+; LD A,$C3 ; JP INSTRUCTION
+; LD ($0038),A ; INSTALL IT
+; LD HL,INT_IM1 ; DESTINATION ADDRESS
+; LD ($0039),HL ; INSTALL IT
+;#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 ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4) | (PLATFORM == PLT_RC180))
+ ; 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
+;
+ #IF (INTMODE == 2)
+ ;LD HL,INT_TIMER
+ ;LD (HBX_IVT),HL
+ #ENDIF
+;
+ #ENDIF
+;
+#ENDIF
+;
+#IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4) | (PLATFORM == PLT_RC180))
+;
+ #IF (INTMODE == 2)
+;
+ ; MASK ALL EXTERNAL INTERRUPTS FOR NOW
+ ;XOR A ; INT0-2 DISABLED
+ 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,INT_TIMER
+ LD (HBX_IVT + IVT_TIM0),HL
+
+ ; SETUP PERIODIC TIMER INTERRUPT ON TIMER 0
+ LD HL,(CB_CPUKHZ) ; 50HZ = 18432000 / 20 / 50 / X, SO X = CPU KHZ
+ LD B,0
+ LD C,Z180_RLDR0L ; INITIALIZE TIMER 0 RELOAD REGISTER
+ OUT (C),L
+ INC C
+ OUT (C),H
+ LD C,Z180_TMDR0L ; INITIALIZE TIMER 0 DATA REGISTER
+ OUT (C),L
+ INC C
+ OUT (C),H
+ LD A,%00010001 ; ENABLE TIMER0 INT AND DOWN COUNTING
+ OUT0 (Z180_TCR),A
+;
+ #ENDIF
+;
+#ENDIF
+;
+ HB_EI ; INTERRUPTS SHOULD BE OK NOW
+;
+; DISPLAY PLATFORM INFORMATION
+;
+ CALL NEWLINE2
+ PRTX(STR_PLATFORM)
+ PRTS(" @ $")
+ LD HL,(CB_CPUKHZ)
+ CALL PRTD3M ; PRINT AS DECIMAL WITH 3 DIGIT MANTISSA
+ PRTS("MHz$")
+#IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4) | (PLATFORM == PLT_RC180))
+ CALL PC_COMMA
+ PRTS(" IO=0x$")
+ LD A,Z180_BASE
+ CALL PRTHEXBYTE
+#ENDIF
+;
+; DISPLAY CPU CONFIG
+;
+ ;CALL PRTSTRD
+ ;.TEXT ", $"
+ CALL NEWLINE
+#IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4) | (PLATFORM == PLT_RC180))
+ LD A,Z180_MEMWAIT
+#ELSE
+ LD A,0
+#ENDIF
+ CALL PRTDECB
+ CALL PRTSTRD
+ .TEXT " MEM W/S, $"
+#IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4) | (PLATFORM == PLT_RC180))
+ LD A,Z180_IOWAIT + 1
+#ELSE
+ LD A,1
+#ENDIF
+ CALL PRTDECB
+ CALL PRTSTRD
+ .TEXT " I/O W/S$"
+#IF (INTMODE > 0)
+ CALL PRTSTRD
+ .TEXT ", INT MODE $"
+ LD A,INTMODE
+ CALL PRTDECB
+#ENDIF
+;
+; DISPLAY MEMORY CONFIG
+;
+ CALL NEWLINE
+ ;CALL PRTSTRD
+ ;.TEXT "MEMORY CONFIG: $"
+ LD HL,ROMSIZE
+ CALL PRTDEC
+ CALL PRTSTRD
+ .TEXT "KB ROM, $"
+ LD HL,RAMSIZE
+ CALL PRTDEC
+ CALL PRTSTRD
+ .TEXT "KB RAM$"
+;
+; PERFORM DEVICE INITIALIZATION
+;
+ CALL NEWLINE
+ LD B,HB_INITTBLLEN
+ LD DE,HB_INITTBL
+INITSYS1:
+ 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 INITSYS1
+;
+; 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_N8) & (PLATFORM != PLT_MK4) & (PLATFORM != PLT_RC) & (PLATFORM != PLT_RC180) & (PLATFORM != PLT_EZZ80))
+ IN A,(RTC) ; 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 HL,0 ; ENTER AT ADDRESS 0
+ CALL HBX_BNKCALL ; GO THERE
+ HALT ; WE SHOULD NEVER COME BACK!
+#ELSE
+ ; COPY OS IMAGE: BID_USR:
--> 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 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 HL,0 ; ENTER AT ADDRESS 0
+ CALL HBX_BNKCALL ; GO THERE
+ HALT ; WE SHOULD NEVER COME BACK!
+;
+#ENDIF
+;
+ RET
+;
+;==================================================================================================
+; TABLE OF INITIALIZATION ENTRY POINTS
+;==================================================================================================
+;
+HB_INITTBL:
+#IF (SPKENABLE)
+ .DW SPK_INIT ; AUDIBLE INDICATOR OF BOOT START
+#ENDIF
+#IF (AYENABLE)
+ .DW AY_INIT ; AUDIBLE INDICATOR OF BOOT START
+#ENDIF
+#IF (ASCIENABLE)
+ .DW ASCI_INIT
+#ENDIF
+#IF (UARTENABLE)
+ .DW UART_INIT
+#ENDIF
+#IF (SIOENABLE)
+ .DW SIO_INIT
+#ENDIF
+#IF (ACIAENABLE)
+ .DW ACIA_INIT
+#ENDIF
+#IF (SIMRTCENABLE)
+ .DW SIMRTC_INIT
+#ENDIF
+#IF (DSRTCENABLE)
+ .DW DSRTC_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
+;
+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_DISPCALL ; DO THE WORK
+;
+ ; CHECK STACK INTEGRITY
+ PUSH AF
+ LD A,(HB_STACK - HB_STKSIZ + $08)
+ CP $FF
+ CALL NZ,PANIC
+ LD A,$FF
+ LD (HB_STACK - HB_STKSIZ + $08),A
+ POP AF
+ RET
+HB_DISPCALL:
+;
+#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
+ CALL C,PANIC ; OBSOLETE!
+ CP BF_VDA + $10 ; $40-$4F: VIDEO DISPLAY ADAPTER
+ JP C,VDA_DISPATCH
+ CP BF_SYS ; SKIP TO BF_SYS VALUE AT $F0
+ CALL C,PANIC ; PANIC IF LESS THAN BF_SYS
+ JP SYS_DISPATCH ; OTHERWISE SYS CALL
+ CALL PANIC ; THIS SHOULD NEVER BE REACHED
+ 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 DIO 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
+;
+;==================================================================================================
+; REAL TIME CLOCK DEVICE DISPATCHER
+;==================================================================================================
+;
+; ROUTE CALL TO REAL TIME CLOCK DRIVER
+; B: FUNCTION
+;
+RTC_DISPATCH:
+#IF (SIMRTCENABLE)
+ JP SIMRTC_DISPATCH
+#ENDIF
+#IF (DSRTCENABLE)
+ JP DSRTC_DISPATCH
+#ENDIF
+ CALL PANIC
+;
+;==================================================================================================
+; 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
+;
+;==================================================================================================
+; 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
+ CALL PANIC ; INVALID
+;
+; SOFT RESET HBIOS, RELEASE HEAP MEMORY NOT USED BY HBIOS
+;
+SYS_RESET:
+ LD HL,(HEAPCURB) ; GET HBIOS HEAP THRESHOLD
+ LD (CB_HEAPTOP),HL ; RESTORE HEAP TOP
+ XOR A
+ RET
+;
+; 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
+#IF (INTMODE == 1)
+ DI
+#ENDIF
+ CALL HB_BNKCPY
+#IF (INTMODE == 1)
+ EI
+#ENDIF
+ 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 PANIC ; NOT YET IMPLEMENTED
+ OR $FF
+ RET
+;
+; GET SYSTEM INFORMATION
+; ITEM TO RETURN INDICATED IN C
+;
+SYS_GET:
+ LD A,C ; GET REQUESTED SUB-FUNCTION
+ CP BF_SYSGET_CIOCNT
+ JR Z,SYS_GETCIOCNT
+ CP BF_SYSGET_DIOCNT
+ JR Z,SYS_GETDIOCNT
+ CP BF_SYSGET_VDACNT
+ JR Z,SYS_GETVDACNT
+ CP BF_SYSGET_TIMER
+ JR Z,SYS_GETTIMER
+ CP BF_SYSGET_BOOTINFO
+ JR Z,SYS_GETBOOTINFO
+ CP BF_SYSGET_CPUINFO
+ JR Z,SYS_GETCPUINFO
+ CP BF_SYSGET_MEMINFO
+ JR Z,SYS_GETMEMINFO
+ CP BF_SYSGET_BNKINFO
+ JR Z,SYS_GETBNKINFO
+ OR $FF ; SIGNAL ERROR
+ RET
+;
+; GET TIMER
+; RETURNS:
+; DE:HL: TIMER VALUE (32 BIT)
+;
+SYS_GETTIMER:
+ LD HL,HB_TICKS
+ HB_DI
+ CALL LD32
+ HB_EI
+ 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 H,0 ; NOT YET DEFINED
+ 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 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 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
+;
+; 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_BOOTINFO
+ JR Z,SYS_SETBOOTINFO
+ OR $FF ; SIGNAL ERROR
+ 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
+;
+; RETURN A BYTE OF MEMORY FROM SPECIFIED BANK
+; ENTRY: D=BANK ID, HL=ADDRESS
+; RETURN: E=BYTE VALUE
+;
+SYS_PEEK:
+#IF (INTMODE == 1)
+ DI
+#ENDIF
+ CALL HBX_PEEK ; IMPLEMENTED IN PROXY
+#IF (INTMODE == 1)
+ EI
+#ENDIF
+ XOR A
+ RET
+;
+; WRITE A BYTE OF MEMORY TO SPECIFIED BANK
+; ENTRY: D=BANK ID, HL=ADDRESS IN HBIOS BANK, E=BYTE VALUE
+;
+SYS_POKE:
+#IF (INTMODE == 1)
+ DI
+#ENDIF
+ CALL HBX_POKE ; IMPLEMENTED IN PROXY
+#IF (INTMODE == 1)
+ EI
+#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
+ OR $FF ; SIGNAL ERROR
+ 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 PANIC ; INVALID FOR INT MODE 0
+ OR $FF
+ 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 PANIC ; ELSE ERROR
+ OR $FF
+ RET
+SYS_INTGET1:
+ OR A
+ RLA ; HL := (A * 2) FOR IM2
+#IF (INTMODE == 1)
+ RLA ; ... HL := (A * 4) + 1 FOR IM1
+ INC A
+#ENDIF
+ LD H,0
+ LD L,A
+#IF (INTMODE == 1)
+ LD DE,HB_IM1INT ; DE := START OF CALL LIST
+#ENDIF
+#IF (INTMODE == 2)
+ LD DE,HBX_IVT ; DE := START OF VECTOR TABLE
+#ENDIF
+ 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
+#IF (INTMODE == 2)
+ LD DE,HBX_INT ; DE := IM2 INT ROUTING ENGINE
+#ENDIF
+ 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)
+;
+; 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.
+;
+HB_IM1INT: ; IM1 DEVICE INTERRUPT HANDLER CALL LIST
+ 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
+;
+; 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_IM1INT ; POINTER FOR NEXT IM1 ENTRY
+;
+#ENDIF
+;
+; TIMER INTERRUPT
+;
+HB_TIMINT:
+ ; INCREMENT TICK COUNTER (32 BIT)
+ LD HL,HB_TICKS ; POINT TO TICK COUNTER
+ INC (HL)
+ JR NZ,HB_TIMINT1
+ INC HL
+ INC (HL)
+ JR NZ,HB_TIMINT1
+ INC HL
+ INC (HL)
+ JR NZ,HB_TIMINT1
+ INC HL
+ INC (HL)
+;
+HB_TIMINT1:
+;
+#IF 0
+;
+ LD HL,TEMPCNT
+ DEC (HL)
+ JR NZ,HB_TIMINT2
+ LD (HL),250
+;
+ LD A,'*'
+ CALL COUT
+ JR HB_TIMINT2
+;
+TEMPCNT .DB 250
+;
+#ENDIF
+;
+HB_TIMINT2:
+;
+#IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4) | (PLATFORM == PLT_RC180))
+ ; ACK/RESET Z180 TIMER INTERRUPT
+ IN0 A,(Z180_TCR)
+ IN0 A,(Z180_TMDR0L)
+#ENDIF
+;
+ OR $FF ; NZ SET TO INDICATE INT HANDLED
+ RET
+;
+; BAD INTERRUPT HANDLER
+;
+HB_BADINT:
+
+#IF 0 ; *DEBUG*
+ LD HL,HB_BADINTCNT
+ INC (HL)
+ LD A,(HL)
+ OUT (DIAGP),A
+ OR $FF
+ RET
+HB_BADINTCNT .DB 0
+#ENDIF ; *DEBUG*
+
+ CALL NEWLINE2
+ PRTS("+++ BAD INT: $")
+ CALL _REGDMP
+ ;CALL CONTINUE
+ OR $FF ; SIGNAL INTERRUPT HANDLED
+ RET
+;
+; COMMON API FUNCTION DISPATCH CODE
+;
+; ON ENTRY 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:
+ ; CHECK INCOMING UNIT INDEX IN C FOR VAILIDITY
+ LD A,C ; A := INCOMING DISK UNIT INDEX
+ CP (IY-1) ; COMPARE TO COUNT
+ JR NC,HB_DISPERR ; 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_DISPERR ; HANDLE FN NUM OUT OF RANGE ERROR
+
+ ; BUMP IY TO ACTUAL XXX_TBL ENTRY FOR INCOMING UNIT INDEX
+ 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
+
+ ; 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
+ 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
+
+ RET ; JUMP TO DRIVER FUNC ADR ON TOS
+;
+HB_DISPERR:
+ CALL PANIC ; PANIC
+ OR $FF ; SIGNAL ERROR
+ RET ; AND RETURN VIA DISPEXIT
+;
+; 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
+ OR $FF ; SIGNAL ERROR
+ RET ; AND RETURN
+;
+HB_TMPSZ .DW 0
+HB_TMPREF .DW 0
+;
+;==================================================================================================
+; DEVICE DRIVERS
+;==================================================================================================
+;
+#IF (SIMRTCENABLE)
+ORG_SIMRTC .EQU $
+ #INCLUDE "simrtc.asm"
+SIZ_SIMRTC .EQU $ - ORG_SIMRTC
+ .ECHO "SIMRTC occupies "
+ .ECHO SIZ_SIMRTC
+ .ECHO " bytes.\n"
+#ENDIF
+;
+#IF (DSRTCENABLE)
+ORG_DSRTC .EQU $
+ #INCLUDE "dsrtc.asm"
+SIZ_DSRTC .EQU $ - ORG_DSRTC
+ .ECHO "DSRTC occupies "
+ .ECHO SIZ_DSRTC
+ .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 (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
+;
+#IF (CVDUENABLE | VGAENABLE)
+ORG_FONTHI .EQU $
+ #INCLUDE "font_hi.asm"
+SIZ_FONTHI .EQU $ - ORG_FONTHI
+ .ECHO "FONTHI occupies "
+ .ECHO SIZ_FONTHI
+ .ECHO " bytes.\n"
+#ENDIF
+;
+#IF (TMSENABLE)
+ORG_FONTTMS .EQU $
+ #INCLUDE "font_tms.asm"
+SIZ_FONTTMS .EQU $ - ORG_FONTTMS
+ .ECHO "FONTTMS occupies "
+ .ECHO SIZ_FONTTMS
+ .ECHO " bytes.\n"
+#ENDIF
+;
+#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)
+ORG_SPK .EQU $
+ #INCLUDE "spk.asm"
+SIZ_SPK .EQU $ - ORG_SPK
+ .ECHO "SPK occupies "
+ .ECHO SIZ_SPK
+ .ECHO " bytes.\n"
+#ENDIF
+;
+#IF (AYENABLE)
+ORG_AY .EQU $
+ #INCLUDE "ay.asm"
+SIZ_AY .EQU $ - ORG_AY
+ .ECHO "AY occupies "
+ .ECHO SIZ_AY
+ .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
+
+;
+#DEFINE USEDELAY
+#INCLUDE "util.asm"
+#INCLUDE "time.asm"
+#INCLUDE "bcd.asm"
+#INCLUDE "decode.asm"
+;#INCLUDE "xio.asm"
+;
+#IF (DSKYENABLE)
+#DEFINE DSKY_KBD
+#INCLUDE "dsky.asm"
+#ENDIF
+;
+; DETECT CPU SPEED USING DS-1302 RTC
+;
+HB_CPUSPD:
+;
+#IF (DSRTCENABLE)
+;
+ CALL DSRTC_TSTCLK ; IS CLOCK RUNNING?
+ JR Z,HB_CPUSPD1 ; YES, CONTINUE
+ ; MAKE SURE CLOCK IS RUNNING
+ LD HL,DSRTC_TIMDEF
+ CALL DSRTC_TIM2CLK
+ LD HL,DSRTC_BUF
+ CALL DSRTC_WRCLK
+ CALL DSRTC_TSTCLK ; NOW IS CLOCK RUNNING?
+ RET NZ
+;
+HB_CPUSPD1:
+; LD B,8
+;HB_CPUSPDX:
+; PUSH BC
+
+ ; 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
+
+; PUSH DE
+; POP BC
+; CALL NEWLINE
+; CALL PRTHEXWORD
+
+; POP BC
+; DJNZ HB_CPUSPDX
+;
+ 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
+#IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4) | (PLATFORM == PLT_RC180))
+ SLA L
+ RL H
+#ENDIF
+ 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 ((PLATFORM == PLT_SBC) | (PLATFORM == PLT_ZETA) | (PLATFORM == PLT_ZETA2) | (PLATFORM == PLT_RC) | (PLATFORM == PLT_EZZ80))
+ ; LOOP TARGET IS 4000 T-STATES, SO CPU FREQ IN KHZ = LOOP COUNT * 4
+ CALL DLY32
+ CALL DLY8
+ CALL DLY2
+ JP $ + 3 ; 10 TSTATES
+ JP $ + 3 ; 10 TSTATES
+ JP $ + 3 ; 10 TSTATES
+ JP $ + 3 ; 10 TSTATES
+ ;LD A,R ; 9 TSTATES
+ INC BC ; 6 TSTATES
+ ;LD A,(BC) ; 7 TSTATES
+ ;NOP ; 4 TSTATES
+ NOP ; 4 TSTATES
+#ENDIF
+
+#IF ((PLATFORM == PLT_N8) | (PLATFORM == PLT_MK4) | (PLATFORM == PLT_RC180))
+ ; LOOP TARGET IS 8000 T-STATES, SO CPU FREQ IN KHZ = LOOP COUNT * 8
+ ;CALL DLY64
+ CALL DLY32
+ CALL DLY16
+ CALL DLY8
+ CALL DLY4
+ CALL DLY2
+ CALL DLY1 ; CALL (25TS) & RET (18TS) = 43TS
+ OR A ; 7 TSTATES
+ OR A ; 7 TSTATES
+ ;OR A ; 7 TSTATES
+ ;OR A ; 7 TSTATES
+ NOP ; 6 TSTATES
+ ;NOP ; 6 TSTATES
+ ;NOP ; 6 TSTATES
+ ;NOP ; 6 TSTATES
+ ;NOP ; 6 TSTATES
+#ENDIF
+;
+ PUSH DE
+ CALL HB_RDSEC ; GET SECONDS
+ POP DE
+ 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 HL
+ OR E ; ... FOR OVERFLOW
+ RET Z ; TIMEOUT, SOMETHING IS WRONG
+ JR HB_WAITSEC1 ; LOOP
+;
+HB_RDSEC:
+ ; READ SECONDS BYTE INTO A
+ LD C,$81 ; SECONDS REGISTER
+ CALL DSRTC_CMD ; SEND THE COMMAND
+ CALL DSRTC_GET ; READ THE REGISTER
+ CALL DSRTC_END ; FINISH IT
+ RET
+;
+#ELSE
+;
+ RET ; NO RTC, ABORT
+;
+#ENDIF
+;
+; 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
+;
+ ; PRINT DISK DEVICES
+ LD B,BF_SYSGET ; FUNC: SYSTEM INFO GET
+ LD C,BF_SYSGET_DIOCNT ; SUBFUNC: DISK UNIT COUNT
+ RST 08 ; E := DISK UNIT COUNT
+ LD B,E ; MOVE TO B FOR LOOP COUNT
+ LD A,E ; MOVE TO ACCUM
+ OR A ; SET FLAGS
+ JR Z,PRTSUM1A ; IF NONE, BYPASS
+ LD C,0 ; C WILL BE UNIT INDEX
+PRTSUM1:
+ PUSH BC ; SAVE LOOP CONTROL
+ CALL PS_DISK ; PRINT DISK INFO
+ POP BC ; RESTORE LOOP CONTROL
+ INC C ; BUMP DISK UNIT INDEX
+ DJNZ PRTSUM1 ; LOOP THRU ALL DISK DEVICES
+;
+PRTSUM1A:
+ ; PRINT SERIAL DEVICES
+ LD B,BF_SYSGET ; FUNC: SYSTEM INFO GET
+ LD C,BF_SYSGET_CIOCNT ; SUBFUNC: SERIAL UNIT COUNT
+ RST 08 ; E := SERIAL UNIT COUNT
+ LD B,E ; MOVE TO B FOR LOOP COUNT
+ LD A,E ; MOVE TO ACCUM
+ OR A ; SET FLAGS
+ JR Z,PRTSUM2A ; IF NONE, BYPASS
+ LD C,0 ; C WILL BE UNIT INDEX
+PRTSUM2:
+ PUSH BC ; SAVE LOOP CONTROL
+ CALL PS_SERIAL ; PRINT SERIAL INFO
+ POP BC ; RESTORE LOOP CONTROL
+ INC C ; BUMP SERIAL UNIT INDEX
+ DJNZ PRTSUM2 ; LOOP THRU ALL SERIAL DEVICES
+;
+PRTSUM2A:
+ ; PRINT VIDEO DEVICES
+ LD B,BF_SYSGET ; FUNC: SYSTEM INFO GET
+ LD C,BF_SYSGET_VDACNT ; SUBFUNC: VIDEO UNIT COUNT
+ RST 08 ; E := SERIAL UNIT COUNT
+ LD B,E ; MOVE TO B FOR LOOP COUNT
+ LD A,E ; MOVE TO ACCUM
+ OR A ; SET FLAGS
+ JR Z,PRTSUM3A ; IF NONE, BYPASS
+ LD C,0 ; C WILL BE UNIT INDEX
+PRTSUM3:
+ PUSH BC ; SAVE LOOP CONTROL
+ CALL PS_VIDEO ; PRINT VIDEO INFO
+ POP BC ; RESTORE LOOP CONTROL
+ INC C ; BUMP VIDEO UNIT INDEX
+ DJNZ PRTSUM3 ; LOOP THRU ALL VIDEO DEVICES
+;
+PRTSUM3A:
+ RET ; DONE
+;
+; 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, ASSUME SINGLE DIGIT
+ 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
+;
+ ; 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
+ ; PRINT FLOPPY TYPE
+ LD A,C ; ATTRIBUTE TO ACCUM
+ RLCA ; ISOLATE FORM FACTOR BITS
+ RLCA
+ RLCA
+ AND $03
+ LD DE,PS_FLP8 ; ASSUME 8"
+ CP 0
+ JR Z,PS_PRTDC2A
+ LD DE,PS_FLP5 ; ASSUME 5.25"
+ CP 1
+ JR Z,PS_PRTDC2A
+ LD DE,PS_FLP3 ; ASSUME 3.5"
+ CP 2
+ JR Z,PS_PRTDC2A
+ LD DE,PS_FLPN ; ASSUME OTHER"
+PS_PRTDC2A:
+ CALL WRITESTR
+ ; PRINT FLOPPY SIDES
+ LD A,C ; ATTRIBUTE TO ACCUM
+ LD DE,PS_FLPSS ; ASSUME SINGLE SIDED
+ BIT 4,A ; DS?
+ JR Z,PS_PRTDC2B
+ LD DE,PS_FLPDS ; DOUBLE SIDED
+PS_PRTDC2B:
+ CALL WRITESTR
+ ; PRINT FLOPPY DENSITY
+ LD A,C ; ATTRIBUTE TO ACCUM
+ RRCA ; ISOLATE DENSITY BITS
+ RRCA
+ AND $03
+ LD DE,PS_FLPSD ; SINGLE DENSITY
+ CP 0
+ JR Z,PS_PRTDC2C
+ LD DE,PS_FLPDD ; DOUBLE DENSITY
+ CP 1
+ JR Z,PS_PRTDC2C
+ LD DE,PS_FLPHD ; HIGH DENSITY
+ CP 2
+ JR Z,PS_PRTDC2C
+ LD DE,PS_FLPED ; EXTENDED DENSITY
+ CP 3
+ JR Z,PS_PRTDC2C
+PS_PRTDC2C:
+ CALL WRITESTR
+ 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 A,E
+ RLCA
+ RLCA
+ AND 00000011B
+ JR NZ,PSPRTPC1
+ PRTS("Output$")
+ RET
+PSPRTPC1:
+ DEC A
+ JR NZ,PSPRTPC2
+ PRTS("Input$")
+ RET
+PSPRTPC2:
+ DEC A
+ JR NZ,PSPRTPC3
+ PRTS("Bidirectional$")
+ RET
+PSPRTPC3:
+ DEC A
+ JR NZ,PSPRTPC4
+ PRTS("BitCtrl$")
+ RET
+PSPRTPC4:
+ PRTS("Undefined$")
+ RET
+
+PS_PRTSC1:
+ ; PRINT TERMINAL CONFIG
+ LD A,C ; GET ATTRIBUTE VALUE
+ CP $FF ; 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 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
+;
+;
+;
+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_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_DTOTHER .TEXT "???$"
+;
+; FLOPPY ATTRIBUTE STRINGS
+;
+PS_FLP8 .TEXT "8\",$"
+PS_FLP5 .TEXT "5.25\",$"
+PS_FLP3 .TEXT "3.5\",$"
+PS_FLPN .TEXT "???\",$"
+;
+PS_FLPSS .TEXT "SS/$"
+PS_FLPDS .TEXT "DS/$"
+;
+PS_FLPSD .TEXT "SD$"
+PS_FLPDD .TEXT "DD$"
+PS_FLPHD .TEXT "HD$"
+PS_FLPED .TEXT "ED$"
+;
+; SERIAL DEVICE STRINGS
+;
+PS_SDSTRREF:
+ .DW PS_SDUART, PS_SDASCI, PS_SDTERM,
+ .DW PS_SDPRPCON, PS_SDPPPCON, PS_SDSIO, PS_SDACIA, PS_SDPIO
+;
+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$"
+;
+; SERIAL TYPE STRINGS
+;
+PS_STRS232 .TEXT "RS-232$"
+PS_STTERM .TEXT "Terminal$"
+PS_STPPT .TEXT "Parallel$"
+;
+PS_STPARMAP .DB "NONENMNS"
+
+;
+; SERIAL TYPE STRINGS
+;
+;
+; 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$"
+;
+; VIDEO CONFIG STRINGS
+;
+;
+;
+; 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, USE XIO
+;
+ ; 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:
+ ;; USE XIO
+ ;LD A,E ; GET OUTPUT CHAR BACK TO ACCUM
+ ;CALL XIO_OUTC ; OUTPUT VIA XIO
+;
+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, USE XIO
+;
+ ; 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:
+ ;; USE XIO
+ ;CALL XIO_INC ; GET CHAR
+;
+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 ; IF NOT READY, USE XIO
+;
+ ; 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:
+ ;; USE XIO
+ ;CALL XIO_IST ; GET STATUS
+;
+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
+;
+STR_BANNER .DB "RetroBrew HBIOS v", BIOSVER, ", ", TIMESTAMP, "$"
+STR_PLATFORM .DB PLATFORM_NAME, "$"
+STR_SWITCH .DB "*** Activating CRT Console ***$"
+STR_BADINT .DB "\r\n*** BAD INT ***\r\n$"
+;
+#IF (DSKYENABLE)
+MSG_HBVER .DB $BE,$FF,$8A,$FB,$D7,$6D,$77,$B0 ; "HBIOS291"
+#ENDIF
+;
+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
diff --git a/pio.asm b/pio.asm
new file mode 100644
index 00000000..9a04177d
--- /dev/null
+++ b/pio.asm
@@ -0,0 +1,695 @@
+; PIO driver sets up the parallel port as a subtype of Serial/Char device.
+;
+;
+; HBIOS initializes driver by:
+;
+; 1) Performing Pre-initialization
+;
+; This involves setting up all the data structures decribing the devices.
+; If possible, do a hardware test to verify it is available for adding to available devices.
+;
+; 2) Performing individual device/unit initialization.
+;
+; Hardware initialization.
+; Configure to initial state or to a new state.
+; Implementation limitations:
+;
+; The fully functionality of the Z80 PIO can only be realized by using Z80 interrupt mode 2.
+; Registers cannot be interrogated for interrupts status and the originating interrupt
+; device cannot be determine.
+;
+; Full implementation of IM2 functionality in an ECB-ZP and ECB-4P board would require the
+; allocation of an interrupt handler for each chip channel. Thus, 10 interrupt handler
+; would be required to support this configuration. As the HBIOS only has an allocation of
+; 16, a full implmentation is impractical.
+;
+; The compromise solution is for the driver to allow IM2 only on the first PIO on each board.
+; So, a ECB-4PIO would be fully interrupt drived in all modes but a ECB-ZP would only allow
+; PIO 0 to be interrupt drived and PIO 1,2,3 would be limited to Bit mode or blind read and
+; writed to the input/output ports.
+;
+; Zilog PIO reset state:
+;
+; Both port mask registers are reset to inhibit All port data bits.
+; Port data bus lines are set to a high-impedance state and the Ready "handshake"
+; Mode 1 is automatically selected.
+; The vector address registers are not reset.
+; Both port interrupt enable flip-flops are reset.
+; Both port output registers are reset.
+;
+; Program a channel
+;
+; LD C,PIOBC ; C -> PIO Channel B control
+; LD B,5 ; 5 bytes to send
+; LD HL,PT ; HL -> Initialization data
+; OTIR ; send bytes
+;
+PIODEBUG .EQU 1
+;
+M_Output .EQU $00 << 6
+M_Input .EQU $01 << 6
+M_Bidir .EQU $02 << 6
+M_BitCtrl .EQU $03 << 6
+M_BitAllIn .EQU $FF
+M_BitAllOut .EQU $00
+;
+PIO_NONE .EQU 0
+PIO_ZPIO .EQU 1
+PIO_8255 .EQU 2
+PIO_PORT .EQU 3
+
+;INT_POOL .EQU HBX_IVT+IVT_PIO0
+
+INT_ALLOC .DB 0
+INT_N .EQU 00000000B
+#IF (INTMODE == 2)
+INT_Y .EQU INT_N
+INT_ALLOW .EQU 4
+#ELSE
+INT_Y .EQU 00000100B
+INT_ALLOW .EQU 0
+#ENDIF
+
+INT_0 .EQU 00000000B
+INT_1 .EQU 00000001B
+INT_2 .EQU 00000010B
+INT_3 .EQU 00000011B
+;
+;
+; SETUP THE DISPATCH TABLE ENTRIES
+;
+; WE CANT PRINT ANYTHING TO HBIOS CONSOLE AT THIS POINT
+; PIO_CNT HOLDS THE NUMBER OF DEVICED CALCULATED FROM THE NUMBER OF DEFPIO MACROS
+; PIO_CNT SHOULD INCREASE BY 2 FOR EVERY PIO CHIP ADDED.
+;
+PIO_PREINIT:
+ LD B,PIO_CNT ; LOOP CONTROL
+ LD C,0 ; PHYSICAL UNIT INDEX
+ XOR A ; ZERO TO ACCUM
+ LD (PIO_DEV),A ; CURRENT DEVICE NUMBER
+PIO_PREINIT0:
+ PUSH BC ; SAVE LOOP CONTROL
+ LD A,C ; PHYSICAL UNIT TO A
+ RLCA ; MULTIPLY BY CFG TABLE ENTRY SIZE (8 BYTES)
+ RLCA ; ...
+ RLCA ; ... TO GET OFFSET INTO CFG TABLE
+ LD HL,PIO_CFG ; POINT TO START OF CFG TABLE
+ CALL ADDHLA ; HL := ENTRY ADDRESS
+ PUSH HL ; SAVE IT
+ PUSH HL ; COPY CFG DATA PTR
+ POP IY ; ... TO IY
+ CALL PIO_INITUNIT ; HAND OFF TO GENERIC INIT CODE
+ POP DE ; GET ENTRY ADDRESS BACK, BUT PUT IN DE
+ POP BC ; RESTORE LOOP CONTROL
+;
+ LD A,(IY+1) ; GET THE PIO TYPE DETECTED
+ OR A ; SET FLAGS
+ JR Z,PIO_PREINIT2 ; SKIP IT IF NOTHING FOUND
+;
+ PUSH BC ; SAVE LOOP CONTROL
+ PUSH AF
+ DEC A
+ LD BC,PIO_FNTBL ; BC := FUNCTION TABLE ADDRESS
+ JR Z,TYPFND ; ADD ENTRY IF PIO FOUND, BC:D
+ DEC A
+ LD BC,PPI_FNTBL ; BC := FUNCTION TABLE ADDRESS
+ JR Z,TYPFND
+ DEC A
+ LD BC,PRT_FNTBL
+ JR NZ,TYPDUN
+TYPFND: CALL CIO_ADDENT ; ADD ENTRY IF PIO FOUND, BC:DE
+TYPDUN: POP AF
+ POP BC ; RESTORE LOOP CONTROL
+;
+PIO_PREINIT2:
+ INC C ; NEXT PHYSICAL UNIT
+ DJNZ PIO_PREINIT0 ; LOOP UNTIL DONE
+;
+#IF (INTMODE == 2)
+ ; SETUP PIO INTERRUPT VECTOR IN IVT
+ LD HL,PIO0INT
+ LD (HBX_IVT + IVT_PIO0),HL
+#ENDIF
+PIO_PREINIT3:
+ XOR A ; SIGNAL SUCCESS
+ RET ; AND RETURN
+;
+; WHEN WE GET HERE IY POINTS TO THE PIO_CFG TABLE WE ARE WORKING ON.
+;
+PIO_INITUNIT:
+ LD A,C ; SET THE UNIT NUMBER
+ LD (IY),A
+
+ LD DE,-1 ; LEAVE CONFIG ALONE
+ JP PIO_INITDEV ; IMPLEMENT IT AND RETURN
+; XOR A ; SIGNAL SUCCESS
+; RET ; AND RETURN
+;
+PIO_INIT:
+ LD B,PIO_CNT ; COUNT OF POSSIBLE PIO UNITS
+ LD C,0 ; INDEX INTO PIO CONFIG TABLE
+PIO_INIT1:
+ PUSH BC ; SAVE LOOP CONTROL
+
+ LD A,C ; PHYSICAL UNIT TO A
+ RLCA ; MULTIPLY BY CFG TABLE ENTRY SIZE (8 BYTES)
+ RLCA ; ...
+ RLCA ; ... TO GET OFFSET INTO CFG TABLE
+ LD HL,PIO_CFG ; POINT TO START OF CFG TABLE
+ CALL ADDHLA ; HL := ENTRY ADDRESS
+ PUSH HL ; COPY CFG DATA PTR
+ POP IY ; ... TO IY
+
+ LD A,(IY+1) ; GET PIO TYPE
+ OR A ; SET FLAGS
+ CALL NZ,PIO_PRTCFG ; PRINT IF NOT ZERO
+
+ PUSH DE
+ LD DE,$FFFF ; INITIALIZE DEVICE/CHANNEL
+ CALL PIO_INITDEV ; BASED ON DPW
+ POP DE
+
+ POP BC ; RESTORE LOOP CONTROL
+ INC C ; NEXT UNIT
+ DJNZ PIO_INIT1 ; LOOP TILL DONE
+;
+ XOR A ; SIGNAL SUCCESS
+ RET ; DONE
+;
+; EXAMPLE CODE
+;
+PIO_LPT:
+ IN A,($F6) ; get device status
+ AND $20 ; device ready?
+ JR Z,PIO_LPT ; no, busy wait
+ IN A,($F5) ; get transmit buffer register status ready?
+ AND $20 ; ready?
+ JR Z,PIO_LPT ; no, busy wait
+ LD A,C ; ready, char A for output through data port
+ OUT ($F0),A ; output char
+ RET
+
+
+
+; ------------------------------------
+; ZILOG PIO FUNCTION TABLE ROUTINES
+;-------------------------------------
+
+PIO_IN:
+ LD C,(IY+3)
+ IN A,(C)
+ LD E,A
+ XOR A ; SIGNAL SUCCESS
+ RET
+
+;
+PIO0INT:
+PIO1INT:
+PIO2INT:
+PIO3INT:
+PIO4INT:
+PIO5INT:
+PIO6INT:
+PIO7INT:
+PIO8INT:
+PIO9INT:
+ OR $FF ; NZ SET TO INDICATE INT HANDLED
+ RET
+;
+; ON ENTRY IY POINTS TO THE DEVICE RECORD
+; E CONTAINS THE CHARACTER TO OUTPUT
+; WE RETREIVE THE CMD PORT ADDRESS AND CALCULATE THE
+; DATA PORT AND WRITE THE CHARACTER TO IT.
+;
+PIO_OUT:
+ LD C,(IY+3)
+ OUT (C),E
+ XOR A ; SIGNAL SUCCESS
+ RET
+
+ LD C,(IY+3) ; GET PORT
+ LD A,(IY+4) ; GET MODE (B7B6)
+
+
+PIO_IST:
+ RET
+;
+PIO_OST:
+ RET
+;
+; PIO_INITDEV - Configure device.
+; If DE = FFFF then extract the configuration information from the table of devices and program the device using those settings.
+; Otherwise use the configuration information in DE to program those settings and save them in the device table
+;
+; SETUP PARAMETER WORD:
+; +-------------------------------+ +-------+-----------+---+-------+
+; | BIT CONTROL | | MODE | C2 C1 C0 | A | INT |
+; +-------------------------------+ --------------------+-----------+
+; F E D C B A 9 8 7 6 5 4 3 2 1 0
+; -- MSB (D REGISTER) -- -- LSB (E REGISTER) --
+;
+;
+; MSB = BIT MAP USE IN MODE 3
+; MODE B7 B6 = 00 Mode 0 Output
+; 01 Mode 1 Input
+; 10 Mode 2 Bidir
+; 11 Mode 3 Bit Mode
+; CHIP CHANNEL B5 B4 B3 001 Channel 1
+; 010 Channel 2
+; 100 Channel 3
+; INTERRUPT ALLOCATED B2 = 0 NOT ALLOCATED
+; = 1 IS ALLOCATED
+;
+; WHICH IVT IS ALLOCATES B1 B0 00 IVT_PIO0
+; 01 IVT_PIO1
+; 10 IVT_PIO2
+; 11 IVT_PIO3
+PIO_INITDEV:
+ ; TEST FOR -1 (FFFF) WHICH MEANS USE CURRENT CONFIG (JUST REINIT)
+ LD A,D ; TEST DE FOR
+ AND E ; ... VALUE OF -1
+ INC A ; ... SO Z SET IF -1
+ JR NZ,PIO_INITDEV1 ; IF DE == -1, REINIT CURRENT CONFIG
+;
+ ; LOAD EXISTING CONFIG TO REINIT
+ LD E,(IY+4) ; LOW BYTE
+ LD D,(IY+5) ; HIGH BYTE
+;
+PIO_INITDEV1:
+ ; WHICH DEVICE TYPE?
+ LD A,(IY+1)
+ CP PIO_ZPIO
+ JR Z,SET_PIO
+ CP PIO_8255
+ JR Z,SET_8255
+ CP PIO_PORT
+ JR Z,SET_PORT
+PIO_BAD:OR $FF ; UNKNOWN DEVICE
+ RET
+
+SET_PORT:
+ ; DEVICE TYPE IS I/O PORT SO JUST WRITE $00 TO IT
+ LD C,(IY+3)
+ OUT (C),A
+ XOR A
+ RET
+;
+SET_PIO:
+ ; DEVICE TYPE IS Z80 PIO SO DETERMINE MODE TO SET
+ ; BIDIR MODE CAN ONLY BE SET ON CHANNEL 0
+ ; BITCTRL MODE REQUIRES A I/O DIRECTION TO BE SET
+ LD C,(IY+3) ; GET DATA PORT
+ INC C ; POINT TO CMD
+ INC C ; PORT
+ LD A,(IY+4) ; GET MODE (B7B6)
+ AND 11010000B ; KEEP MODE & CHANNEL
+ CP 10010000B ; SET CH1 & BIDIR
+ JR Z,PIO_BAD ; CAN'T DO ON CH1
+ AND 11000000B ; $B0
+ OR 00001111B ; $0F
+ OUT (C),A ; SET MODE
+ CP (M_BitCtrl | $0F) ; IF MODE 3
+ JR NZ,SET_NM3
+ LD A,(IY+5) ; SET I/O
+ OUT (C),A ; FOR MODE 3
+SET_NM3:; INTERUPT HANDLING
+ LD A,(IY+4) ; CHECK IF INTERRUPT
+ BIT 2,A ; REQUEST BIT SET
+ JR Z,NOINT1
+
+ LD A,(INT_ALLOC) ; DO WE HAVE AN
+ CP INT_ALLOW+1 ; INTERRUPT FREE?
+ JR NC,BADSET
+
+ INC A ; ONE INTERRUPT
+ LD (INT_ALLOC),A ; USED
+
+ ; THE TRICKY BIT - SETUP THE RIGHT INTERRUPT VECTOR
+
+NOINT1: LD A,00000111B ; $07
+ OUT (C),A ; NO INTERRUPTS
+ DEC C
+ DEC C
+ LD A,$FF ; DEFAULT VALUE
+ OUT (C),A
+ XOR A
+ RET
+BADSET: LD A,$FF
+ RET
+
+SET_8255:
+ RET
+
+SET_BYE:
+ XOR A ; SIGNAL SUCCESS
+ RET
+;
+PIO_ZP0:
+PIO_ZP1:
+PIO_4P0:
+PIO_4P1:
+PIO_4P2:
+PIO_4P3:
+PIO_4P4:
+PIO_4P5:
+PIO_4P6:
+PIO_4P7:OR $FF ; NZ SET TO INDICATE INT HANDLED
+ RET
+;
+; ON ENTRY IY POINTS TO THE DEVICE RECORD
+; WE GET AND RETURN THE CONFIGURATION WORD IN DE
+;
+PIO_QUERY:
+PPI_QUERY:
+ LD E,(IY+4) ; FIRST CONFIG BYTE TO E
+ LD D,(IY+5) ; SECOND CONFIG BYTE TO D
+ XOR A ; SIGNAL SUCCESS
+ RET
+;
+; ON ENTRY IY POINTS TO THE DEVICE RECORD
+; FOR CHARACTER DEVICES BIT 6 OF ATTRIBUTE
+; INDICATES PARALLEL PORT IF 1 SO WE SET IT.
+;
+PIO_DEVICE:
+PPI_DEVICE:
+ LD D,CIODEV_PIO ; D := DEVICE TYPE
+ LD E,(IY) ; E := PHYSICAL UNIT
+ LD C,$40 ; C := ATTRIBUTE
+ XOR A ; SIGNAL SUCCESS
+ RET
+
+; ------------------------------------
+; i8255 FUNCTION TABLE ROUTINES
+;-------------------------------------
+
+PPI_IN:
+ XOR A ; SIGNAL SUCCESS
+ RET
+;
+PPI_OUT:
+ XOR A ; SIGNAL SUCCESS
+ RET
+;
+PPI_IST:
+ RET
+;
+PPI_OST:
+ RET
+;
+; PIO_INITDEV - Configure device.
+; If DE = FFFF then extract the configuratio information from the table of devices and program the device using those settings.
+; Otherwise use the configuration information in DE to program those settings and save them in the device table
+
+PPI_INITDEV:
+ XOR A ; SIGNAL SUCCESS
+ RET
+PPI_INT:OR $FF ; NZ SET TO INDICATE INT HANDLED
+ RET
+;
+PIO_PRTCFG:
+ ; ANNOUNCE PORT
+ CALL NEWLINE ; FORMATTING
+ PRTS("PIO$") ; FORMATTING
+ LD A,(IY) ; DEVICE NUM
+ CALL PRTDECB ; PRINT DEVICE NUM
+ PRTS(": IO=0x$") ; FORMATTING
+ LD A,(IY+3) ; GET BASE PORT
+ CALL PRTHEXBYTE ; PRINT BASE PORT
+;
+ ; PRINT THE PIO TYPE
+ CALL PC_SPACE ; FORMATTING
+ LD A,(IY+1) ; GET PIO TYPE BYTE
+ RLCA ; MAKE IT A WORD OFFSET
+ LD HL,PIO_TYPE_MAP ; POINT HL TO TYPE MAP TABLE
+ CALL ADDHLA ; HL := ENTRY
+ LD E,(HL) ; DEREFERENCE
+ INC HL ; ...
+ LD D,(HL) ; ... TO GET STRING POINTER
+ CALL WRITESTR ; PRINT IT
+;
+ ; ALL DONE IF NO PIO WAS DETECTED
+ LD A,(IY+1) ; GET SIO TYPE BYTE
+ OR A ; SET FLAGS
+ RET Z ; IF ZERO, NOT PRESENT
+;
+ PRTS(" MODE=$") ; FORMATTING
+ LD E,(IY+4) ; LOAD CONFIG
+ LD D,(IY+5) ; ... WORD TO DE
+ CALL PS_PRTPC0 ; PRINT CONFIG
+;
+ XOR A
+ RET
+;
+; WORKING VARIABLES
+;
+PIO_DEV .DB 0 ; DEVICE NUM USED DURING INIT
+;
+; DESCRIPTION OF DIFFERENT PORT TYPES
+;
+PIO_TYPE_MAP:
+ .DW PIO_STR_NONE
+ .DW PIO_STR_PIO
+ .DW PIO_STR_8255
+ .DW PIO_STR_PORT
+
+PIO_STR_NONE .DB "$"
+PIO_STR_PIO .DB "Zilog PIO$"
+PIO_STR_8255 .DB "i8255 PPI$"
+PIO_STR_PORT .DB "IO Port$"
+;
+; Z80 PIO PORT TABLE - EACH ENTRY IS FOR 1 CHIP I.E. TWO PORTS
+;
+#DEFINE DEFPIO(MPIO_TYPE,MPIO_BASE,MPIO_CH0,MPIO_CH1,MPIO_CH0X,MPIO_CH1X,MPIO_FT0,MPIO_FT1,MPIO_IN0,MPIO_IN1) \
+#DEFCONT \ .DB 0
+#DEFCONT \ .DB MPIO_TYPE
+#DEFCONT \ .DB 0
+#DEFCONT \ .DB MPIO_BASE
+#DEFCONT \ .DB (MPIO_CH0 | 00001000B | MPIO_IN0)
+#DEFCONT \ .DB MPIO_CH0X
+#DEFCONT \ .DW MPIO_FT0
+#DEFCONT \ .DB 0
+#DEFCONT \ .DB MPIO_TYPE
+#DEFCONT \ .DB 1
+#DEFCONT \ .DB MPIO_BASE+1
+#DEFCONT \ .DB (MPIO_CH1 | 00010000B | MPIO_IN1)
+#DEFCONT \ .DB MPIO_CH1X
+#DEFCONT \ .DW MPIO_FT1
+;
+; i8255 PORT TABLE - EACH ENTRY IS FOR 1 CHIP I.E. THREE PORTS
+;
+#DEFINE DEFPPI(MPPI_TYPE,MPPI_BASE,MPPI_CH1,MPPI_CH2,MPPI_CH3,MPPI_CH1X,MPPI_CH2X,MPPI_CH3X) \
+#DEFCONT \ .DB 0
+#DEFCONT \ .DB MPPI_TYPE
+#DEFCONT \ .DB 0
+#DEFCONT \ .DB MPPI_BASE+0
+#DEFCONT \ .DB (MPPI_CH1 | 00001000B)
+#DEFCONT \ .DB MPPI_CH1X
+#DEFCONT \ .DW
+#DEFCONT \ .DB 0
+#DEFCONT \ .DB MPPI_TYPE
+#DEFCONT \ .DB 1
+#DEFCONT \ .DB MPPI_BASE+2
+#DEFCONT \ .DB (MPPI_CH2 | 00010000B)
+#DEFCONT \ .DB MPPI_CH2X
+#DEFCONT \ .DW 0
+#DEFCONT \ .DB 0
+#DEFCONT \ .DB MPPI_TYPE
+#DEFCONT \ .DB 2
+#DEFCONT \ .DB MPPI_BASE+4
+#DEFCONT \ .DB (MPPI_CH3 | 00100000B)
+#DEFCONT \ .DB MPPI_CH3X
+#DEFCONT \ .DW 0
+;
+; HERE WE ACTUALLY DEFINE THE HARDWARE THAT THE HBIOS CAN ACCESS
+; THE INIT ROUTINES READ AND SET THE INITIAL MODES FROM THIS INFO
+;
+PIO_CFG:
+
+#IF PIO_ZP
+DEFPIO(PIO_ZPIO,PIOZBASE+0,M_Output,M_BitCtrl,M_BitAllOut,M_BitAllOut,PIO0FT,PIO1FT,INT_Y,INT_N)
+DEFPIO(PIO_ZPIO,PIOZBASE+4,M_Output,M_BitCtrl,M_BitAllOut,M_BitAllOut,PIO2FT,PIO3FT,INT_Y,INT_N)
+#ENDIF
+#IF PIO_4P
+DEFPIO(PIO_ZPIO,PIO4BASE+0,M_Output,M_BitCtrl,M_BitAllOut,M_BitAllOut,PIO4FT,PIO5FT,INT_N,INT_N)
+DEFPIO(PIO_ZPIO,PIO4BASE+4,M_Output,M_Input,M_BitAllOut,M_BitAllOut,PIO6FT,PIO7FT,INT_N,INT_N)
+DEFPIO(PIO_ZPIO,PIO4BASE+8,M_Output,M_Output,M_BitAllOut,M_BitAllOut,PIO8FT,PIO9FT,INT_N,INT_N)
+DEFPIO(PIO_ZPIO,PIO4BASE+12,M_Output,M_Output,M_BitAllOut,M_Output,PIO10FT,PIO11FT,INT_N,INT_N)
+#ENDIF
+; PPI_SBC & (PLATFORM == PLT_SBC) & (PPIDEMODE != PPIDEMODE_SBC))
+
+#IF PPI_SBC
+DEFPPI(PIO_8255,PPIBASE,M_Output,M_Output,M_Output,M_BitAllOut,M_BitAllOut,M_BitAllOut)
+#ENDIF
+;
+; ; PIO CHANNEL A
+; .DB 0 ; CIO DEVICE NUMBER (SET DURING PRE-INIT, THEN FIXED)
+; .DB 0 ; PIO TYPE (SET AT ASSEMBLY, FIXED)
+; .DB 0 ; FREE
+; .DB PIOBASE+2 ; BASE DATA PORT (DATA PORT) (SET AT ASSEMBLY, FIXED)
+; .DB 0 ; SPW - MODE 3 I/O DIRECTION BYTE (SET AT ASSEMBLE, SET WITH INIT)
+ .DB 0 ; SPW - MODE, CHANNEL (SET AT ASSEMBLY, SET WITH INIT, CHANNEL FIXED)
+; .DW 0 ; FUNCTION TABLE (SET AT ASSEMBLY, SET DURING PRE-INIT AND AT INIT)
+
+PIO_CNT .EQU ($ - PIO_CFG) / 8
+;
+
+; DRIVER FUNCTION TABLE FOR Z80 PIO's
+; EACH PIO NEEDS A FUNCTION TABLE
+; ECB-ZP : PIO0FT-PIO3FT
+; ECB-4P : PIO4FT-PIO11FT
+
+PIO_FNTBL:
+;
+#IF PIO_ZP
+PIO0FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO0FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+PIO1FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO1FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+PIO2FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO2FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+PIO3FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO3FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+#ENDIF
+;
+#IF PIO_4P
+PIO4FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO4FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+PIO5FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO5FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+PIO6FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO6FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+PIO7FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO7FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+PIO8FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO8FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+PIO9FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO9FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+PIO10FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO10FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+PIO11FT .DW PIO_IN
+ .DW PIO_OUT
+ .DW PIO_IST
+ .DW PIO_OST
+ .DW PIO_INITDEV
+ .DW PIO_QUERY
+ .DW PIO_DEVICE
+#IF (($ - PIO11FT) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PIO FUNCTION TABLE ***\n"
+#ENDIF
+#ENDIF
+;
+; DRIVER FUNCTION TABLE FOR i8255's
+;
+PPI_FNTBL:
+ .DW PPI_IN
+ .DW PPI_OUT
+ .DW PPI_IST
+ .DW PPI_OST
+ .DW PPI_INITDEV
+ .DW PPI_QUERY
+ .DW PPI_DEVICE
+#IF (($ - PPI_FNTBL) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PPI FUNCTION TABLE ***\n"
+#ENDIF
+;
+; DRIVER FUNCTION TABLE FOR I/O PORT
+;
+PRT_FNTBL:
+ .DW PPI_IN
+ .DW PPI_OUT
+ .DW PPI_IST
+ .DW PPI_OST
+ .DW PPI_INITDEV
+ .DW PPI_QUERY
+ .DW PPI_DEVICE
+#IF (($ - PRT_FNTBL) != (CIO_FNCNT * 2))
+ .ECHO "*** INVALID PPI FUNCTION TABLE ***\n"
+#ENDIF
\ No newline at end of file