Hi,
several times there have been topics regarding bootstrapping a 6502 (or other systems) and the issues regarding slow ROMs in fast systems. Currently I have solved that using a DP-RAM that is used to bootstrap the 6502 and at the same time is the frame buffer for the VGA output.
But now I wanted to make a system without a video controller and with much less components. So I came up with the following solution.
Attachment:
File comment: CPU, Memory, Decoder and AVR
bootstrap-design.pdf [24.75 KiB]
Downloaded 234 times
Note that this is only a concept. Before I jump into this I first wanted to check if this really is feasible or if there is something that is obviously wrong or will not work.
The memory decoder is a simple decoder that supports two memory maps selected with IML (short form for Initial Machine Load). When IML is Low addresses $0000 to e.g. $BFFF will select the RAM for read and writes, addresses $C000..$C0FF decode to one of the IO select signals and for addresses $C100..$FFFF selects the RAM only for read accesses. When IML is High the RAM is only selected for write cycles, but not only for $0000..$BFFF but as well for $C100..$FFFF. Bootstrap is achieved via the AVR that controls RESET and PHI and during startup makes the 6502 think that he reads a ROM image, when in fact the AVR just emits the bytes that would be expected.
Here is the AVR code I have written so far for that purpose.
Code:
;
; Inject a ROM loader loop
;
;
; 0300- A9 00 LDA #$00
; 0302- 8D AA AA STA $AAAA
; 0305- 4C 00 03 JMP $0300
;
.def zero r2
.def ff r3
.def temp r17
.equ LDA 0xA9
.equ STA 0x8D
.equ JMP 0x4C
.equ PHI2 3
.equ RESET 1
.equ IML 2 ; Initial machine load
.equ ROMSTART 0xD000 ; Or whereever the 6502 ROM starts
;--------------------------------------------------------------------------
;
; Initialization of AVR
;
reset:
clr zero
ldi temp, 0xff
mov ff, temp
ldi
cbi PORTB, PHI2
sbi DDRB, PHI2 ; PHI2 is output
cbi DDRB, RESET
cbi PORTB, RESET ; Let the pull-up do it's job
sbi DDRD, IML
ldi zl, low(2*romimage)
ldi zh, high(2*romimgea)
ldi xl, low(ROMSTART)
ldi xh, high(ROMSTART)
sbi PORTD, IML ; Activate boot mode decoder
;--------------------------------------------------------------------------
;
;
;
sbi DDRB, RESET ; Activate Reset
sbi PORTB, PHI2 ; Generate some PHI2 cycles
nop ; Need at least 2 cycles
nop
nop
cbi PORTB, PHI2
nop
nop
nop
sbi PORTB, PHI2
nop
nop
nop
cbi PORTB, PHI2
nop
nop
nop
sbi PORTB, PHI2
nop
nop
nop
cbi PORTB, PHI2
nop
nop
nop
sbi PORTB, PHI2
nop
nop
nop
cbi PORTB, PHI2
nop
nop
nop
sbi PORTB, PHI2
cbi DDRB, RESET ; Release Reset, when a positive
nop ; Edge is detected the 6502 will
nop ; start a reset cycle
nop
;--------------------------------------------------------------------------
;
; The eight reset cycles
;
cbi PORTB, PHI2 ; Cycle 0
nop
nop
nop
sbi PORTB, PHI2
nop
nop
nop
cbi PORTB, PHI2 ; Cycle 1
nop
nop
nop
sbi PORTB, PHI2
nop
nop
nop
cbi PORTB, PHI2 ; Cycle 2
nop
nop
nop
sbi PORTB, PHI2
nop
nop
nop
cbi PORTB, PHI2 ; Cycle 3
nop
nop
nop
sbi PORTB, PHI2
nop
nop
nop
cbi PORTB, PHI2 ; Cycle 4
nop
nop
nop
sbi PORTB, PHI2
nop
nop
nop
cbi PORTB, PHI2 ; Cycle 5
nop
nop
nop
sbi PORTB, PHI2
nop
nop
nop
jmp setv ; Cycle 6 and 7 read the reset
; vector value which is the same
; as the JMP will loop to, so use
; this code.
;--------------------------------------------------------------------------
;
; Generate one cycle: Opcode LDA immediate
;
;
cbi PORTB, PHI2 ; PHI2 = Low
out DDRC, zero ; Disable buffer
out DDRD, zero ;
;
ldi temp, LDA ; Get Opcode
out PORTC, temp ; Set Buffer
cbi PORTD, 6 ; and
sbi PORTD, 7 ; Set PORT bits
sbi PORTB, PHI2 ; PHI2 = High
out DDRC, ff ; Enable buffer
out DDRD, ff
nop ; Give CPU some time
nop
nop
;--------------------------------------------------------------------------
;
; Generate one cycle: Immediate Value
;
cbi PORTB, PHI2 ; PHI2 = Low
out DDRC, zero ; Disable buffer
out DDRD, zero ;
;
lpm temp, Z+ ; Get immediate value
out PORTC, temp ; Set Buffer
cbi PORTD, 6 ; and
sbrc temp, 6
sbi PORTD, 6
cbi PORTD, 7
sbrc temp, 7
sbi PORTD, 7 ; Set PORT Bits
sbi PORTB, PHI2 ; PHI2 = High
out DDRC, ff ; Enable Buffer
out DDRD, ff
nop ; Give CPU some time
nop
nop
;--------------------------------------------------------------------------
;
; Generate one cycle: Opcode STA absolute
;
;
cbi PORTB, PHI2 ; PHI2 = Low
out DDRC, zero ; Disable buffer
out DDRD, zero ;
;
ldi temp, STA ; Get Opcode
out PORTC, temp ; Set Buffer
cbi PORTD, 6 ; and
sbi PORTD, 7 ; Set PORT bits
sbi PORTB, PHI2 ; PHI2 = High
out DDRC, ff ; Enable buffer
out DDRD, ff
nop ; Give CPU some time
nop
nop
;--------------------------------------------------------------------------
;
; Generate one cycle: low byte address
;
cbi PORTB, PHI2 ; PHI2 = Low
out DDRC, zero ; Disable buffer
out DDRD, zero ;
;
out PORTC, xl ; Get Low Byte
cbi PORTD, 6 ; and
sbrc xl, 6
sbi PORTD, 6
cbi PORTD, 7
sbrc xl, 7
sbi PORTD, 7 ; Set PORT Bits
sbi PORTB, PHI2 ; PHI2 = High
out DDRC, ff ; Enable Buffer
out DDRD, ff
nop ; Give CPU some time
nop
nop
;--------------------------------------------------------------------------
;
; Generate one cycle: high byte address
;
cbi PORTB, PHI2 ; PHI2 = Low
out DDRC, zero ; Disable buffer
out DDRD, zero ;
;
out PORTC, xh ; Get Low Byte
cbi PORTD, 6 ; and
sbrc xh, 6
sbi PORTD, 6
cbi PORTD, 7
sbrc xh, 7
sbi PORTD, 7 ; Set PORT Bits
sbi PORTB, PHI2 ; PHI2 = High
out DDRC, ff ; Enable Buffer
out DDRD, ff
nop ; Give CPU some time
nop
nop
;--------------------------------------------------------------------------
;
; Generate one cycle: write byte to SRAM
;
cbi PORTB, PHI2 ; PHI2 = Low
out DDRC, zero ; Disable buffer
out DDRD, zero ;
nop
nop
nop
nop
sbi PORTB, PHI2 ; PHI2 = High
nop ; Give CPU some time
nop
nop
nop
nop
nop
nop
nop
adiw xh:xl, 1
breq done ; Stop after writing to $FFFF
;--------------------------------------------------------------------------
;
; Generate one cycle: Opcode JMP absolute
;
;
cbi PORTB, PHI2 ; PHI2 = Low
out DDRC, zero ; Disable buffer
out DDRD, zero ;
;
ldi temp, JMP ; Get Opcode
out PORTC, temp ; Set Buffer
sbi PORTD, 6 ; and
cbi PORTD, 7 ; Set PORT bits
sbi PORTB, PHI2 ; PHI2 = High
out DDRC, ff ; Enable buffer
out DDRD, ff
nop ; Give CPU some time
nop
nop
;--------------------------------------------------------------------------
;
; Generate one cycle: low byte address of $0300
;
setv:
cbi PORTB, PHI2 ; PHI2 = Low
out DDRC, zero ; Disable buffer
out DDRD, zero ;
;
out PORTC, zero
cbi PORTD, 6
cbi PORTD, 7
sbi PORTB, PHI2 ; PHI2 = High
out DDRC, ff ; Enable Buffer
out DDRD, ff
nop ; Give CPU some time
nop
nop
;--------------------------------------------------------------------------
;
; Generate one cycle: high byte address of $0300
;
cbi PORTB, PHI2 ; PHI2 = Low
out DDRC, zero ; Disable buffer
out DDRD, zero ;
;
ldi temp, 0x03
out PORTC, temp ; Get Low Byte
cbi PORTD, 6 ; and
cbi PORTD, 7
sbi PORTB, PHI2 ; PHI2 = High
out DDRC, ff ; Enable Buffer
out DDRD, ff
nop ; Give CPU some time
nop
nop
jmp loop
;--------------------------------------------------------------------------
;
; Finished loading bootrom
;
done cbi PORTB, PHI2 ;
;--------------------------------------------------------------------------
;
; Now the upper memory should be filled with the ROM image
;
;
cbi PORTD, IML ; Activate normal memory decocder
sbi DDRD, RESET ; Assert RESET
;
; Now we can programm Timer 2 to act as source for PHI2
; the maximum clock frequency we can achieve is AVRCLK/2
; a modern AVR supports a clock up to 20MHz, you can easily over
; clock it to 25MHz other options would use an external clock
; that would have to be multiplexed by the GAL using IML as select
;
;
; Timer is started in CTC mode with TOP=OCR2A and no prescaler
; this gives an output of AVRCLK/2 as PHI2 for the 6502
;
out OCR2A, zero
ldi temp, (COM2A1<<0) | (COCM2A0<<1) | (COM2B1<<0) | (COCM2B0<<1) | (WGM21<<1) | (WGM20<<0)
out TCCR2A, temp
ldi temp, (WGM22<<0) | (CS22<<0) | (CS21<<0) | (CS20<<1)
out TCCR2B, temp
;
; Now the real reset starts, so we need to wait at least 2 PHI2 cycles
; before we de-assert RESET
;
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
cbi DDRD, RESET ; De-Assert RESET
;
; Now the 6502 executes the normal RESET with the vector in the loaded
; ROM Image.
;
.... ; And the AVR can do what ever we
.... ; want him to do.
The trick here is not to control the address bus. Instead after a reset of the AVR we control the PHI2 via Software and put out the bytes the 6502 would read when we really had a ROM. The ROM we present is rather small, it is a RESET vector and 3 instructions that just write immediate data to the memory in a loop. As the RAM is write enabled this loop can now load a ROM image. After the image is loaded IML is de-asserted and the 6502 is RESET and start the ROM with it's own reset vector