Close

Update CBIOS

A project log for My CP/M V3

Version 3 of My CP/M uses an UART (rather than firmware serial) and 256kb or 512kb Flash ROM chips as disks

agp.cooperagp.cooper 07/31/2021 at 07:060 Comments

Updated Boot

First I have to update boot.asm, the code to load CP/M into memory. The CP/M V2 only had 32kb of RAM so the CP/M source was located at 0x8000. As CP/M V3 has 56k of RAM so the CP/M source is located at 0xE000.

Here is the boot assembly code (i8085):

;   boot.asm: Load CP/M from ROM

;   Calc CPM addresses
msize       equ     56                  ; 56k RAM
sectors     equ     16                  ; sectors per track for my system
systrks     equ     4                   ; my boot system has 4 tracks of 16 sectors 
bias        equ     (msize-20-1)*1024   ; calculation for my system
ccp         equ     3400h+bias
cstart      equ     4a00h+bias

    org     0e000h                      ; executed in ROM after shift down
    jmp     0e003h                      ; reset bootstrap
boot:
    di                                  ; disable interrupts                 
    lxi     sp,ccp-0080h                ; convenient place
    lxi     d,ccp-0080h                 ; start of boot dst
    lxi     h,0e000h                    ; start of ROM (boot src)
    mvi     c,sectors                   ; sectors per track
    mvi     b,systrks                   ; sytem track count
    push    b                           ; save 
boot$nextsect:
    mvi     b,128                       ; sector size
boot$copysect:
    mov     a,m
    inx     h
    stax    d
    inx     d
    dcr     b
    jnz     boot$copysect
    dcr     c
    jnz     boot$nextsect
    pop     b
    dcr     b
    push    b
    jnz     boot$nextsect
;   Cold boot
    jmp     cstart
    end

 Updated CBIOS

CBIOS has a number of alterations to suit the new hardware design:

;   Calc CPM addresses
msize   equ     56                      ; RAM capacity
sectors equ     16                      ; sectors per track for my system
systrks equ     4                       ; my boot system has 4 tracks of 16 sectors
bias    equ     (msize-20-1)*1024       ; adjusted for my system
cbase   equ     3400h+bias              ; base of ccp
fbase   equ     cbase+800h              ; base of bdos
bdos    equ     cbase+806h              ; bdos entry
cbios   equ     cbase+1600h             ; base of custom bios (cold boot entry)
spbase  equ     msize*1024              ; sp base (use top of RAM)
usrdsk  equ     0004h                   ; current user and disk number
iobyte  equ     0003h                   ; intel i/o byte
rst7.5  equ     003ch                   ; interrupt to read serial data input

lf      equ     0ah                     ; line feed
cr      equ     0dh                     ; carriage return

;    Custom CP/M 2.2 BIOS
        org     cbios

;       jump vector for individual subroutines
        jmp     cboot                   ; cold start
wstart:
        jmp     wboot                   ; warm start
        jmp     const                   ; console status
        jmp     conin                   ; console character in
        jmp     conout                  ; console character out
        jmp     list                    ; list character out
        jmp     punch                   ; punch character out
        jmp     reader                  ; reader character out
        jmp     home                    ; move head to home position
        jmp     seldsk                  ; select disk
        jmp     settrk                  ; set track number
        jmp     setsec                  ; set sector number
        jmp     setdma                  ; set dma address
        jmp     read                    ; read disk
        jmp     write                   ; write disk
        jmp     listst                  ; return list status
        jmp     sectran                 ; sector translate

;       System uses a two Flash AT29C020-90B with 2048 x 128b pages (256k)
;       Set as 128 tracks of 16 sectors of 128 bytes
;       System uses tracks 0 to 3 (64 sectors)
;       Directory is track 4 and 7 (128 entries)
;       Leaving 240kb file storage using 2048b blocks per chip

dpbase:
;       disk Parameter header for disk A (system)
        dw      trans,  0000h
        dw      0000h,  0000h
        dw      dirbf,  dpblk256s
        dw      chk00,  all00
;       disk parameter header for disk B (system)
        dw      trans,  0000h
        dw      0000h,  0000h
        dw      dirbf,  dpblk256s
        dw      chk01,  all01;
;       disk parameter header for disk C (data) - Not installed
        dw      trans,  0000h
        dw      0000h,  0000h
        dw      dirbf,  dpblk256d
        dw      chk02,  all02
;       disk parameter header for disk D (data) - Not installed
        dw      trans,  0000h
        dw      0000h,  0000h
        dw      dirbf,  dpblk256d
        dw      chk03,  all03

;       sector translate vector (i.e. no translation)
trans:
        db      1,  2,  3,  4           ; sectors  1,  2,  3,  4
        db      5,  6,  7,  8           ; sectors  5,  6,  7,  6
        db      9,  10, 11, 12          ; sectors  9, 10, 11, 12
        db      13, 14, 15, 16          ; sectors 13, 14, 15, 16

;       disk parameter block for system disk (256k) and 1k block size
dpblk256s:    
        dw      sectors                 ; SPT: sectors per track
        db      3                       ; BSH: block shift factor
        db      7                       ; BLM: block mask
        db      0                       ; EXM: extended block mask
        dw      247                     ; DSM: disk blocks - 1 
        dw      127                     ; DRM: directory max - 1
        db      240                     ; AL0: alloc 0 (=4 bits)
        db      0                       ; AL1: alloc 1
        dw      0                       ; CKS: check size (= not removable)
        dw      systrks                 ; OFF: track offset

;       disk parameter block for data disk (256k) and 1k block size
dpblk256d:    
        dw      sectors                 ; SPT: sectors per track
        db      3                       ; BSH: block shift factor
        db      7                       ; BLM: block mask
        db      0                       ; EXM: extended block mask
        dw      255                     ; DSM: disk blocks - 1
        dw      127                     ; DRM: directory max - 1
        db      240                     ; AL0: alloc 0 =(DRM+1)/32, =4 bits, =[11110000])
        db      0                       ; AL1: alloc 1 (=[00000000])
        dw      0                       ; CKS: check size (not removable)
        dw      0                       ; OFF: track offset (no system)

;       BIOS functions

cboot:    
        ; set cold boot defaults only
        di                              ; ? make it obvious
        lxi     sp,spbase               ; use space at top of RAM for stack

        xra     a                       ; zero in the accum
        sta     iobyte                  ; clear the iobyte
        sta     usrdsk                  ; select user and disk zero
        mvi     c,0                     ; select disk 0
        call    seldsk
        call    home                    ; Sets Disk and Track 0

        ; setup 82C52 SIO: 9600,8N1 on RST7.5 
        mvi     a,05bh                  ; set SOD low, reset RST7.5 and unmask RST7.5
        sim
        mvi     a,03Ch                  ; 8N1
        sta     0041h
        mvi     a,066h                  ; Use interrupt with DTR/CTS
        sta     0042h
        mvi     a,02Ah                  ; Set 9600 buad (using a 5MHz CPU clock)
        sta     0043h

        ; display logon message          
        lxi     h,signon                ; Show cold boot message
        call    prmsg                    ; print message
        jmp     gocpm                   ; initialize and go to cp/m

wboot:
        ; reload CP/M but skip boot on sector 1
        di                              ; ? make it obvious
        lxi     sp,spbase               ; use space at top of RAM for stack

        ; setup system disk
        mvi     c,0                     ; select disk 0
        call    seldsk
        call    home                    ; Sets Disk and Track 0
        ; load CP/M
        lxi     d,cbase                 ; start of boot dst
        lxi     h,0e080h                ; start of boot src (start at sector 2)
        mvi     c,sectors               ; sectors per track
        mvi     b,systrks               ; track count (four system tracks)
        push    b                       ; save 
        dcr     c                       ; skip sector 1
wboot$nextsect:
        mvi     b,128                   ; sector size
wboot$copysect:
        mov     a,m
        inx     h
        stax    d
        inx     d
        dcr     b
        jnz     wboot$copysect
        dcr     c
        jnz     wboot$nextsect
        pop     b
        dcr     b
        push    b
        jnz     wboot$nextsect
        jmp     gocpm

;       set parameters and go to cp/m
gocpm:
        mvi     a,0c3h                  ; c3h is a jmp instruction
        sta     0000h                   ; for jmp to wboot
        lxi     h,wstart                ; wboot entry point
        shld    0001h                   ; set address field for jmp at 00000h
        sta     0005h                   ; for jmp to bdos
        lxi     h,bdos                  ; bdos entry point
        shld    0006h                   ; address field of Jump at 0005h to bdos
        sta     rst7.5                  ; for jmp to getchar
        lxi     h,getchar               ; get serial data input entry point
        shld    rst7.5+1                ; set address field of ISR to get serial char
        lxi     b,80h                   ; default dma address is 80h
        call    setdma
        mvi     a,4                     ; define four drives
        sta     drives
        dcr     a                       ; test if usrdsk>=drives
        mov     c,a                     
        lda     usrdsk                  ; get current user and disk number
        cmp     c    
        jc      gocpm$end      
        mvi     a,00h
gocpm$end:
        sta     usrdsk
        mov     c,a                     ; send to the ccp
        ei                              ; enable the interrupt system
        jmp     cbase                   ; go to cp/m


;       get serial character and put in the ring buffer (called by RST7.5)
;       assume a 10 MHz crystal: baud=9600,8N1
getchar:
        di                              ; no interrupts
        push    psw
        push    b
        push    d
        push    h
        lda     40h                     ; Read 82C52 register
; Save in ring buffer
getchar$5:
        lda     rput                    ; rbuf put ptr (0-f)
        mov     e,a                     
        lxi     h,rbuf
        mvi     d,00h
        dad     d
        inr     a
        ani     0fh
        sta     rput
        mov     m,c
        pop     h
        pop     d
        pop     b             
        pop     psw
        ei
        ret

signon:                                    ; signon message
    db    cr,lf,lf
    db    'my CP/M 2.2 V3'
    db    cr,lf,0

; print zero delimited string pointed to by 
prmsg:    
        mov    a,m
        ora    a
        rz                              ; exit on zero
;       more to print
        push h
        mov    c,a
        call putchar
        pop    h
        inx    h
        jmp    prmsg


;       console status, return 0ffh if character ready, 00h if not
const:
        lda     rput
        mov     c,a
        lda     rget
        sub     c
        rz
        mvi     a,0ffh
        ret

;       get console character into A (blocking)
conin:
        lda     rget
        mov     c,a
        lda     rput
        sub     c
        jz      conin                   ; wait for a character  
        lxi     h,rbuf
        mvi     b,00h
        dad     b
        mov     a,c
        inr     a
        ani     0fh
        sta     rget
        mov     a,m
        ani     7fh
        mov     c,a
        ret


; put character to serial out from register C
; assumes a 10 MHz crystal: baud=9600,8,N,1
putchar:
        push psw
        mov  a,c
        sta  0040h
        pop  psw
        ret
conout:
        di                              ; Acccept no serial in while serial out 
        call    putchar                 ; ? putchar does not alter interrupts
        ei
        ret

;       list character from register c
list:
        ret                             ; null subroutine

;       return list status (0 if not ready, 1 if ready)
listst:
        xra    a                        ; 0 is always ok to return
        ret

;       punch character from register C
punch:    
        ret                             ; null subroutine

;       reader character into register a from reader device
reader:
        mvi    a,1ah                    ; enter end of file for now (replace later)
        ani    7fh                      ; remember to strip parity bit
        ret

;       move to the track 0 position of current drive
home:
;       translate this call into a settrk call with parameter 0
        mvi     c,0                     ; select track 0
        mvi     b,0
        call    settrk
        ret                             ; we will move to 00 on first read/write

;       select disk given by register c
seldsk:
        lxi     h,0000h                 ; error return code
        lda     drives
        cmp     c
        rc                              ; out of range
        rz                              ; out of range
        mov     a,c
        sta     diskno
;       select disk selected but page is reset!
        rlc                             ; provision for four disks
        rlc
        ani     0c0h                    ; resets page to 0
        out     0ffh                    ; output to disk/page register
;    compute Disk Parameter Header address
        lda     diskno
        mov     l, a                    ; l=disk number 0, 1, 2, 3
        mvi     h, 0                    ; high order zero
        dad     h
        dad     h
        dad     h
        dad     h
        lxi     d,dpbase
        dad     d                       ; hl=dpbase+diskno*16
        ret
;
settrk:     ; set track given by registers 
        mov     h,b
        mov     l,c
        shld    track
        ret
;
setsec:     ; set sector given by register c
        mov     a, c
        sta     sector
        ret

;       translate the sector  using the translate table [de]
sectran:
        xchg                            ; set hl=translation table
        dad     b                       ; set hl=hl+sector
        mov     l,m                     ; get translated sector
        mvi     h,0                     ; ? sector numbers between 1 and 255
        ret                             ; return with translated sector in 

setdma:
;       set dma address given by registers 
        mov     h,b                     ; high order address
        mov     l,c                     ; low order address
        shld    dmaad                   ; save the address
        ret


;       calculate disk number, disk page, track and sector address
;       returns  = DDTT TTTT 111T TSSS S000 0000

calcdskadr:
;       set disk number (0-3)
        lda     diskno                  ; load diskno number
        rrc
        rrc
        ani     0c0h
        mov     c,a                     ; DD00 0000
;       set low byte of track number
        lda     track                   ; load low byte of track number
        rrc
        rrc
        ani     03fh
        ora     c                       ; DDTT TTTT
        mov     c,a                     ; save
;       set low nibble of sector number
        lda     sector                  ; load sector number
        dcr     a                       ; less one for memory address
        stc
        rar
        mov     d,a                     ; 1SSS SSSS
        mvi     a,00h
        rar                             ; S000 0000
        mov     e,a                     ; save
        mvi     a,0e7h
        ora     d                       ; 1110 0SSS
;       set low nibble of low byte of track number
        lda     track                   ; load low byte of track number
        rlc
        rlc
        rlc
        ani     18h
        ora     d                       ; 111T TSSS
        mov     d,a                     ; save
        ret

;       read sector from disk
read:
        lhld    dmaad                   ; set DMA address 
        call    calcdskadr              ; calculate disk address 
        mov     a,c                     ; out (set) disk number and page
        out     0ffh
        mvi     b,80h                   ; sector size
read$1:
        ldax    d
        inx     d
        mov     m,a
        inx     h
        dcr     b
        jnz     read$1
        xra     a                       ; always succeeds
        ret

;       write sector from disk
write:
        call    calcdskadr              ; calculate disk address 
        lhld    dmaad                   ; set DMA address 
        mov     a,c                     ; out (set) disk number and page
        out     0ffh
        mvi     b,80h                   ; sector size
        xchg                            ; swap destination and source

        di                              ; ? timing critial
write$page:                             ; write page (128b)
        ldax    d
        inx     d
        mov     m,a
        inx     h
        dcr     b
        jnz     write$page
        push    psw                     ; reset rst7.5
        mvi     a,010h
        sim
        pop     psw
        ei

;       wait for flash
        dcx     h
write$wait:
        cmp     m
        jnz     write$wait
        inx     h
        xra     a                       ; always succeeds
        ret

;       BIOS data area
drives: ds      1                       ; number of disk drives 1-16 max
diskno: ds      1                       ; disk number 0-15 max
track:  ds      2                       ; track number 0-65535 max
sector: ds      1                       ; sector number 1-255 max
dmaad:  ds      2                       ; direct memory address

;       scratch ram area for bdos use
dirbf:  ds      128                     ; scratch directory area
all00:  ds      31                      ; allocation vector 0 (=DSM/8+1)
all01:  ds      31                      ; allocation vector 1 (=DSM/8+1)
all02:  ds      32                      ; allocation vector 2 (=DSM/8+1)
all03:  ds      32                      ; allocation vector 3 (=DSM/8+1)
chk00:  ds      16                      ; check vector 0 (CKS=0, therefore not used)
chk01:  ds      16                      ; check vector 1 (CKS=0, therefore not used)
chk02:  ds      16                      ; check vector 2 (CKS=0, therefore not used)
chk03:  ds      16                      ; check vector 3 (CKS=0, therefore not used)

;       Serial use:
rbuf:   ds      16                      ; ring buffer for serial data
rget:   db      0                       ; ring buffer get pointer
rput:   db      0                       ; ring buffer put pointer
bitcnt: db      33                      ; bit count for 9600 baud and 10MHz crystal

        end


 I willdouble check the code tomorrow before uploading the code to the system disk (e.i. W29C020).

Reblocking

Once I get the W29C020 working I need to code the reblocking for the W29C040 as it has a 256b page size (rather than 128b as per the W29C020).

MyCP/M EMU

I rebuilt my EMU for the new hardware. Updated the Intel HEX import of HEX16 files. Picked up a number of errors with CBIOS. On the EMU it now displays the bootup message.

Some problems with reading the console which seems to be an EMU error. The interrupt is working but there seems to be a memory conflict somewhere.

I am tempted to program the Flash chip and try my luck. No luck.

I have been working a few hours every few days on this and I have tracked down the problem to a jump into the "read" cbios routine. Still difficult to work out where the jump came from. Basically, you can trace the code but finding the error in millions of instructions is difficult. Still working on it.

TBC... 

AlanX

Discussions