I've had good bit of fun writing software for CF bootstrapping. George's version of savebyte is very helpful because it allows decent size program to be created in zero page with 256 bytes of opcodes streaming from CF's master boot record. I tried several methods. George has suggested first creating a tiny program in RAM that reads the remaining data from CF's master boot record as the second level bootstrap program. That was quite convoluted, but worked quite well once I've figured out the multiple levels of bootstraps. Below is the program that worked reliably.
The first half (above the two lines of ****) is the tiny program created in RAM at $800-$80b. The tiny program executes and copies remaining data (2nd level bootstrap) in CF to $900 and then jumps into $900. The 2nd level bootstrap copies the monitor program previously stored in multiple sectors of CF disk to RAM and then jumps into the monitor. Whew!
Code:
.macro savebyte val,addr
LDA #val
STA addr
.endmacro
.ORG $b300
;This is the opcode stream for master boot record
; it will be executed as stream of opcode for 65c02
NOP ;reset vector plus extra padding
NOP
NOP
NOP
NOP
.byte 3 ;illegal instruction executed in 1 cycle
;for synchronization purpose
savebyte $ad,$800 ;xx: LDA $e000
savebyte $00,$801
savebyte $e0,$802
savebyte $9d,$803 ;STA $900,x
savebyte $00,$804
savebyte $09,$805
savebyte $e8,$806 ;INX
savebyte $d0,$807 ;BNE xx
savebyte $f7,$808
savebyte $4c,$809 ;JMP $900
savebyte $00,$80a
savebyte $09,$80b
LDX #0 ;initialize regX here to save program space
JMP $800
;program above is executed as it streams out of CF data port
; .org $800
;this is the first stage loader, loading 256 bytes of following data into 0x900
; then jump into 0x900
; xx:
; AD 00 80 LDA $e000 ;read from CF
; 9D 00 09 STA $900,x
; E8 INX
; D0 F7 BNE xx
; 4C 00 09 JMP $900
;xx:
; LDA $e000 ;read from CF
; STA $900,x
; INX
; BNE xx
; JMP $900
;*********************** program below is loaded into $900 by program above *************
;************************ then start the program at $900 *********************
;The hardware is now in normal mode where serial is $8000-$83FF, CF is $8400-$87FF
;zero page locations 0xc0 and 0xc1 are indirect index of address to be loaded
STZ $c0 ;put zero in $c0 (LSB)
LDA #$b4 ;put $b4 in $c1 (MSB)
STA $c1
LDX #2 ;CHS mode, read sectors $2-$d, start from sector 0x2
moresectx:
LDA #1 ;sector count of 1
STA CFsectcnt
STX CF07 ;X contains the sector to be read
LDA #$20 ;read CF command
STA CFstat
readdrqx:
LDA CFstat ;check data request bit set before read CF data
BMI readdrqx
AND #8
BEQ readdrqx
blk1stx:
LDA CFdata
STA ($c0) ;save, starting from 0xB400
INC $c0
BNE blk1stx
INC $c1 ;next 256 bytes
INX ;next CF sector
CPX #$d
BNE moresectx
JMP $b400 ;start location of CRCMon
.align 256,3
Frankly that was too convoluted for me. I can barely keep it straight in my head right now, never mind a few months from now. So I decided to reduced one level of bootstrapping by putting the 2nd level bootstrap in the 1st level. This is possible because the 2nd level bootstrap is 46 bytes, so it can be created with 256 bytes of opcode stream with spare. This simpler bootstrap also worked well and hopefully I can still explain it a few months later.
The end result for both methods are the same; the hardware signed on with monitor prompt a fraction of second after power on.
That was fun. Now I'll go back to work on the hardware as either TTL implementation or GAL22V10 design.
Code:
.macro savebyte val,addr
LDA #val
STA addr
.endmacro
.ORG $b300
;This is the opcode stream save to master boot record
; it will be executed as stream of opcode for 65c02
NOP ;reset vector plus extra padding
NOP
NOP
NOP
NOP
.byte 3 ;illegal instruction executed in 1 cycle
;for synchronization purpose
;delayx:
savebyte $AD,$00 ;LDA CFstat
savebyte $07,$01
savebyte $84,$02
savebyte $E8,$03 ;INX
savebyte $D0,$04 ;BNE delayx
savebyte $FA,$05
savebyte $A2,$06 ;LDX #2
savebyte $02,$07
savebyte $8C,$08 ;moresectx: STY CFsectcnt
savebyte $02,$09
savebyte $84,$0A
savebyte $8E,$0B ;STX CF07
savebyte $03,$0C
savebyte $84,$0D
savebyte $A9,$0E ;LDA #$20
savebyte $20,$0F
savebyte $8D,$10 ;STA CFstat
savebyte $07,$11
savebyte $84,$12
savebyte $AD,$13 ;readdrqx: LDA CFstat
savebyte $07,$14
savebyte $84,$15
savebyte $30,$16 ;BMI readdrqx
savebyte $FB,$17
savebyte $29,$18 ;AND #8
savebyte $08,$19
savebyte $F0,$1A ;BEQ readdrqx
savebyte $F7,$1B
savebyte $AD,$1C ;blkx: LDA CFdata
savebyte $00,$1D
savebyte $84,$1E
savebyte $92,$1F ;STA ($c0)
savebyte $C0,$20
savebyte $E6,$21 ;INC $c0
savebyte $C0,$22
savebyte $D0,$23 ;BNE blkx
savebyte $F7,$24
savebyte $E6,$25 ;INC $c1
savebyte $C1,$26
savebyte $E8,$27 ;INX
savebyte $E0,$28 ;CPX #$e
savebyte $0E,$29
savebyte $D0,$2A ;BNE moresectx
savebyte $DC,$2B
savebyte $4C,$2C
savebyte $00,$2D
savebyte $B4,$2E
;to save program space, initialization of zero page as well as registers can be done
; as instruction streams out
STZ $c0 ;set up ZP as pointer from CF to memory
LDA #$b4 ;$b400 is starting of monitor
STA $c1
LDX #0
LDY #1 ;regY is always 1 to write to CF sector count reg
.align 253,03
JMP $0
;*********************** program below is created in zero page by program above *************
; CF is still in CHS mode. Sector 2 to sector $D correspond to monitor from $b400-$bfff
;
;000000r 1 .org $0
;000000 1 ; CF is still in CHS mode. Sector 2 to sector $D correspond to monitor from $b400-$bfff
;000000 1 delayx: ;value of $c0 is zero here
;000000 1 ;delay is needed for nDASP to negate and switch to normal mode
;000000 1 AD 07 84 LDA CFstat ;read clear the CF FIFO
;000003 1 E8 INX
;000004 1 D0 FA BNE delayx
;000006 1 A2 02 LDX #2 ;start from sector 2
;000008 1 ; ZP $c0,$c1 are already initialized, regY is 1
;000008 1 moresectx:
;000008 1 ; zero page locations 0xc0 and 0xc1 are indirect index of address to be loaded
;000008 1 8C 02 84 STY CFsectcnt ;sector count of 1
;00000B 1 8E 03 84 STX CF07
;00000E 1 A9 20 LDA #$20 ;read CF command
;000010 1 8D 07 84 STA CFstat
;000013 1 readdrqx:
;000013 1 AD 07 84 LDA CFstat ;check data request bit set before read CF data
;000016 1 30 FB BMI readdrqx ;spin on BSY set
;000018 1 29 08 AND #8 ;spin on DRQ
;00001A 1 F0 F7 BEQ readdrqx
;00001C 1 blkx:
;00001C 1 AD 00 84 LDA CFdata
;00001F 1 92 C0 STA ($c0) ;save, starting from 0xB400
;000021 1 E6 C0 INC $c0
;000023 1 D0 F7 BNE blkx
;000025 1 E6 C1 INC $c1 ;next 256 bytes
;000027 1 E8 INX ;next CF sector
;000028 1 E0 0E CPX #$e
;00002A 1 D0 DC BNE moresectx
;00002C 1 4C 00 B4 JMP $b400 ;start location of CRCMon