;------------------------------------------------------------------------------
; CP/M memory settings
;------------------------------------------------------------------------------
ccp .EQU CPMSTART ; Base of CCP
bdos .EQU ccp + 0806h ; Base of BDOS
bios .EQU ccp + 1600h ; Base of BIOS
;------------------------------------------------------------------------------
; Set CP/M low memory data, vector and buffer addresses.
;------------------------------------------------------------------------------
iobyte .EQU 03h ; Intel standard I/O definition byte.
userdrv .EQU 04h ; Current user number and drive.
tpabuf .EQU 80h ; Default I/O buffer and command line storage.
#if gpu
#include "GPU_EQU.ASM"
#endif
;------------------------------------------------------------------------------
; Supervisor Module Equates & Serial Identifiers
;------------------------------------------------------------------------------
CIC .EQU '%' ; Command Initiator Character
EOL .EQU '#' ; End Of Line character for serial comms
; Serial comms settings
;
SER_BUFSIZE .EQU 68
SER_FULLSIZE .EQU 50
SER_EMPTYSIZE .EQU 8
RTS_HIGH .EQU 0E8H
RTS_LOW .EQU 0EAH
SIOA_D .EQU $00
SIOA_C .EQU $02
SIOB_D .EQU $01
SIOB_C .EQU $03
; INTerrupts
;
int38 .EQU 38H
nmi .EQU 66H
; CP/M disk settings
;
blksiz .equ 4096 ; CP/M allocation size
hstsiz .equ 512 ; host disk sector size
hstspt .equ 32 ; host disk sectors/trk
hstblk .equ hstsiz/128 ; CP/M sects/host buff
cpmspt .equ hstblk * hstspt ; CP/M sectors/track
secmsk .equ hstblk-1 ; sector mask
;compute sector mask
;secshf .equ 2 ;log2(hstblk)
wrall .equ 0 ; write to allocated
wrdir .equ 1 ; write to directory
wrual .equ 2 ; write to unallocated
;------------------------------------------------------------------------------
; CF registers
;------------------------------------------------------------------------------
CF_CONTROL .EQU $FF ; CF control port
CF_BASE .EQU $10
CF_DATA .EQU CF_BASE + 0 ; Data (R/W)
CF_FEATURES .EQU CF_BASE + 1 ; Features (W)
CF_ERROR .EQU CF_BASE + 1 ; Error register (R)
CF_SECCOUNT .EQU CF_BASE + 2 ; Sector count (R/W)
CF_SECTOR .EQU CF_BASE + 3
CF_CYL_LOW .EQU CF_BASE + 4
CF_CYL_HI .EQU CF_BASE + 5
CF_HEAD .EQU CF_BASE + 6
CF_STATUS .EQU CF_BASE + 7 ; Status (R)
CF_COMMAND .EQU CF_BASE + 7 ; Command (W)
CF_LBA0 .EQU CF_BASE + 3 ; LBA bits 0-7 (R/W, LBA mode)
CF_LBA1 .EQU CF_BASE + 4 ; LBA bits 8-15 (R/W, LBA mode)
CF_LBA2 .EQU CF_BASE + 5 ; LBA bits 16-23 (R/W, LBA mode)
CF_LBA3 .EQU CF_BASE + 6 ; LBA bits 24-27 (R/W, LBA mode)
;------------------------------------------------------------------------------
;CF Features
;------------------------------------------------------------------------------
CF_8BIT .EQU 1
CF_NOCACHE .EQU 082H
;------------------------------------------------------------------------------
;CF Commands
;------------------------------------------------------------------------------
CF_RESET .EQU $04
CF_READ_SEC .EQU 020H
CF_WRITE_SEC .EQU 030H
CF_IDENTIFY .EQU 0ECH
CF_SET_FEAT .EQU 0EFH
;------------------------------------------------------------------------------
; Terminal equates
;------------------------------------------------------------------------------
;LF .EQU 0AH
;FF .EQU 0CH
;CR .EQU 0DH
;------------------------------------------------------------------------------
; MMU variables
;------------------------------------------------------------------------------
MMU_CTRL: .EQU $3C ; IO address for the MMU
MMU_Banks: .EQU $FF ; Number of available memory banks
MMU_A0: .EQU $38 ; IO address for Area 0 settings
MMU_A1: .EQU $39 ; IO address for Area 1 settings
MMU_A2: .EQU $3A ; IO address for Area 2 settings
MMU_A3: .EQU $3B ; IO address for Area 3 settings
;================================================================================================
.ORG 0050H ; 'Low Storage' scratch pad for MMU / variables
; Single-byte character buffers for use by HexToNum
CHR_BUF_D: .DS 1
CHR_BUF_E: .DS 1
CHR_BUF_M: .DS 1
CHR_BUF_O: .DS 1
; Single-byte character buffer for use by PHEX
PHX_BUF: .DS 1
MMU_Area0: .DS 1 ; Holds the current mapping for Area 0 (0-16K)
MMU_Area1: .DS 1 ; Holds the current mapping for Area 1 (16-32K)
MMU_Area2: .DS 1 ; Holds the current mapping for Area 2 (32-48K)
MMU_Area3: .DS 1 ; Holds the current mapping for Area 3 (48-64K)
MMU_Lock: .DS 1 ; Bit-mask for locked Areas (holding critical data like the stack, these MMU vars)
; LSN, 4 bits, corresponding to 4 Areas, 1-locked, 0-free - MSN not used
; Used by BANK command to prevent accidental trashing of system memory
CPMLDR_CLK: .EQU 0042H ; Temp location to store CLK for CPMLDR
;================================================================================================
.ORG bios ; BIOS origin (default E600h)
;================================================================================================
; BIOS jump table.
;================================================================================================
JP boot ; 0 Initialize.
wboote:
JP wboot ; 1 Warm boot.
JP const ; 2 Console status.
JP conin ; 3 Console input.
JP conout ; 4 Console OUTput.
JP list ; 5 List OUTput.
JP punch ; 6 punch OUTput.
JP reader ; 7 Reader input.
JP home ; 8 Home disk.
JP seldsk ; 9 Select disk.
JP settrk ; 10 Select track.
JP setsec ; 11 Select sector.
JP setdma ; 12 Set DMA ADDress.
JP read ; 13 Read 128 bytes.
JP write ; 14 Write 128 bytes.
JP listst ; 15 List status.
JP sectran ; 16 Sector translate.
;================================================================================================
; Disk parameter headers for disk 0 to 15
;================================================================================================
dpbase:
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb0,0000h,alv00
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv01
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv02
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv03
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv04
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv05
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv06
#if CPM64
.DW 0000h,0000h,0000h,0000h,dirbuf,dpbLast,0000h,alv07
#else
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv07
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv08
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv09
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv10
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv11
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv12
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv13
.DW 0000h,0000h,0000h,0000h,dirbuf,dpb,0000h,alv14
.DW 0000h,0000h,0000h,0000h,dirbuf,dpbLast,0000h,alv15
#endif
; First drive has a reserved track for CP/M
dpb0:
.DW 128 ; SPT - sectors per track
.DB 5 ; BSH - block shift factor
.DB 31 ; BLM - block mask
.DB 1 ; EXM - Extent mask
.DW 2043 ; (2047-4) DSM - Storage size (blocks - 1)
.DW 511 ; DRM - Number of directory entries - 1
.DB 240 ; AL0 - 1 bit set per directory block
.DB 0 ; AL1 - "
.DW 0 ; CKS - DIR check vector size (DRM+1)/4 (0=fixed disk)
.DW 1 ; OFF - Reserved tracks
dpb:
.DW 128 ; SPT - sectors per track
.DB 5 ; BSH - block shift factor
.DB 31 ; BLM - block mask
.DB 1 ; EXM - Extent mask
.DW 2047 ; DSM - Storage size (blocks - 1)
.DW 511 ; DRM - Number of directory entries - 1
.DB 240 ; AL0 - 1 bit set per directory block
.DB 0 ; AL1 - "
.DW 0 ; CKS - DIR check vector size (DRM+1)/4 (0=fixed disk)
.DW 0 ; OFF - Reserved tracks
; Last drive is smaller because CF is never full 64MB or 128MB
dpbLast:
.DW 128 ; SPT - sectors per track
.DB 5 ; BSH - block shift factor
.DB 31 ; BLM - block mask
.DB 1 ; EXM - Extent mask
.DW 1279 ; DSM - Storage size (blocks - 1)
; 511 = 2MB (for 128MB card), 1279 = 5MB (for 64MB card)
.DW 511 ; DRM - Number of directory entries - 1
.DB 240 ; AL0 - 1 bit set per directory block
.DB 0 ; AL1 - "
.DW 0 ; CKS - DIR check vector size (DRM+1)/4 (0=fixed disk)
.DW 0 ; OFF - Reserved tracks
;================================================================================================
; Cold boot
;================================================================================================
boot:
DI ; Disable interrupts.
LD SP,biosstack ; Set default stack.
; Set up the memory map as follows:
; Area 0 - Bank 0 (RAM)
; Area 1 - Bank 1 (RAM)
; Area 2 - Bank 2 (RAM)
LD A,0
OUT (MMU_A0),A ; Set Area 0 to Bank 0
LD A,1 ;
OUT (MMU_A1),A ; Set Area 1 to Bank 1
LD A,2 ;
OUT (MMU_A2),A ; Set Area 2 to Bank 2
; Update the stored values for the Area/Bank mappings
XOR A
LD (MMU_Area0),A ; Record Area 0 mapped to 00
LD A,1
LD (MMU_Area1),A ; Record Area 1 mapped to 01
LD A,2
LD (MMU_Area2),A ; Record Area 2 mapped to 02
; Initialise SIO
LD A,$00
OUT (SIOA_C),A
LD A,$18 ; Write into WR0: channel reset
OUT (SIOA_C),A
LD A,$04 ; Select Write Register 4 which
OUT (SIOA_C),A ; sets the serial baud rate
; Need to get system clock speed (CLK) which should be available
; to calculate baud rate based on 4 or 8 MHz system clock.
; If not available, default to 4 MHz clock divider (/32).
; This will maintain constant 125kbaud on Port A whatever the system clock.
LD A,(CLK) ; Get CLK speed
LD (CPMLDR_CLK),A ; Copy it to temp location where CP/M 3's CPMLDR will look for it
CP '2' ; 2 MHz clock - set baud at double default
JP Z,baud_2
CP '8' ; 8 MHz clock - set baud at half default
JP Z,baud_8
baud_4:
LD A,$84 ; (84h = CLK/32, 1 stop bit, no parity) ($84)
JP baudXt ; Jump to set baud rate
baud_2:
LD A,$44 ; CLK/16, 1 stop bit, no parity ($44)
JP baudXt ; Jump to set baud rate
baud_8:
LD A,$C4 ; CLK/64, 1 stop bit, no parity ($C4)
baudXt:
OUT (SIOA_C),A ; Set baud rate
LD A,$01 ; Select Write Register 1
OUT (SIOA_C),A
#if io_int
LD A,$18 ; Interrupt on all Rx chars
#else
LD A,$00 ; No interrupts
#endif
OUT (SIOA_C),A
LD A,$03 ; Select Write Register 3
OUT (SIOA_C),A
LD A,$E1 ; Rx 8 bits/char, Rx enable
OUT (SIOA_C),A
LD A,$05 ; Select Write Register 5
OUT (SIOA_C),A
LD A,RTS_LOW ; DTR, Tx 8 bits/char, Tx enable
OUT (SIOA_C),A
LD A,$00 ; Select Write Register 0
OUT (SIOB_C),A ;
LD A,$18 ; Channel reset
OUT (SIOB_C),A ;
LD A,$04 ; Select Write Register 4 which
OUT (SIOB_C),A ; sets the serial baud rate
LD A,$C4 ; CLK/64, 1 stop bit, no parity
OUT (SIOB_C),A ;
LD A,$01 ; Select Write Register 1
OUT (SIOB_C),A ;
#if io_int
LD A,$18 ; Interrupt on all Rx chars
#else
LD A,$00 ; No interrupts
#endif
OUT (SIOB_C),A ;
#if io_int
LD A,$02 ; Select Write Register 2
OUT (SIOB_C),A ;
LD A,$E0 ; INTERRUPT VECTOR
OUT (SIOB_C),A ;
#endif
LD A,$03 ; Select Write Register 3
OUT (SIOB_C),A ;
LD A,$E1 ; Rx 8 bits/char, Rx enable
OUT (SIOB_C),A ;
LD A,$05 ; Select Write Register 5
OUT (SIOB_C),A ;
LD A,RTS_LOW ; DTR, Tx 8 bits/char, Tx enable
OUT (SIOB_C),A ;
#if gpu
CALL INIT_GPU ; Initialise the GPU
DI
#endif
#if io_int
LD A,$FF ; Interrupt vector in page FF
LD I,A
#endif
CALL printInline
.BYTE $0C
.TEXT "uCOM CP/M BIOS "
.TEXT "1.6.104"
#if io_int
.TEXT "i"
#else
.TEXT "p"
#endif
#if gpu
.TEXT "-GPU"
#endif
.TEXT " by J.Nock 2017-20"
.BYTE CR,LF,CR,LF
.TEXT "CP/M 2.2 "
.TEXT "Copyright"
.TEXT " 1979 (c) by Digital Research"
.BYTE CR,LF,0
CALL CF_INIT
CALL CF_WAIT
LD A,CF_8BIT ; Set IDE to be 8-bit
OUT (CF_FEATURES),A
LD A,CF_SET_FEAT
OUT (CF_COMMAND),A
CALL CF_WAIT
LD A,CF_NOCACHE ; No write cache
OUT (CF_FEATURES),A
LD A,CF_SET_FEAT
OUT (CF_COMMAND),A
XOR A ; Clear I/O & drive bytes.
LD (userdrv),A
#if io_int
LD (serABufUsed),A
LD (serBBufUsed),A
LD HL,serABuf
LD (serAInPtr),HL
LD (serARdPtr),HL
LD HL,serBBuf
LD (serBInPtr),HL
LD (serBRdPtr),HL
EI
#endif
JP GOCPM
;================================================================================================
; Warm boot
;================================================================================================
wboot:
#if io_int
DI ; Disable interrupts.
#endif
LD SP,biosstack ; Set default stack.
; Interrupt vector in page FF
LD A,$FF
LD I,A
LD B,11 ; Number of sectors to reload
LD A,0
LD (hstsec),A
LD HL,ccp
rdSectors:
CALL CF_CMD_RDY
; Set LBA address
LD A,(hstsec)
OUT (CF_LBA0),A
LD A,0
OUT (CF_LBA1),A
OUT (CF_LBA2),A
LD A,0E0H
OUT (CF_LBA3),A
LD A,1
OUT (CF_SECCOUNT),A
PUSH BC
CALL CF_CMD_RDY
LD A,CF_READ_SEC
OUT (CF_COMMAND),A
CALL CF_DAT_RDY
LD C,4
rd4secs512:
LD B,128
rdByte512:
CALL CF_DAT_RDY
IN A,(CF_DATA)
LD (HL),A
INC HL
DEC B
JR NZ, rdByte512
DEC C
JR NZ,rd4secs512
POP BC
LD A,(hstsec)
INC A
LD (hstsec),A
DJNZ rdSectors
;================================================================================================
; Common code for cold and warm boot
;================================================================================================
GOCPM:
XOR A ; 0 to accumulator
LD (hstact),A ; host buffer inactive
LD (unacnt),A ; clear unalloc count
LD HL,serialInt ; Address of serial interrupt.
LD ($40),HL
LD HL,tpabuf ; Address of BIOS DMA buffer.
LD (dmaAddr),HL
LD A,0C3h ; Opcode for 'JP'.
LD (00h),A ; Load at start of RAM.
LD HL,wboote ; Address of jump for a warm boot.
LD (01h),HL
LD (05h),A ; Opcode for 'JP'.
LD HL,bdos ; Address of jump for the BDOS.
LD (06h),HL
LD A,(userdrv) ; Save new drive number (0).
LD C,A ; Pass drive number in C.
#if io_int
IM 2
EI ; Enable interrupts
#endif
JP ccp ; Start CP/M by jumping to the CCP.