To be more specific, I'd like to know how to arrange access for both the CPU and the VGA subsystem from the same memory and fetch two bytes within one PHI2 cycle. The VGA generator needs one byte every 67.8ns while PHI2 takes double as much, 135.6ns.
This is the VGA generator I'd like to fit in.
Code:
///////////////////////
//VGA EQUATIONS START//
///////////////////////
@CARRY 1; "ripple-carry addition is fast enough
hcnt.ACLR = !N_RESET; "clear counter on active-high reset
vcnt.ACLR = !N_RESET; "clear counter on active-high reset
hcnt.CLK = CLOCK; "horz. pixel counter increments on each dot clock
vcnt.CLK = N_HSYNC; "line counter increments after every horiz. line
N_HSYNC.ASET = !N_RESET;
N_VSYNC.ASET = !N_RESET;
N_HSYNC.CLK = CLOCK;
N_VSYNC.CLK = N_HSYNC;
VADDR.CLK = CLOCK;
VADDR.ACLR = !N_RESET;
"horiz. pixel counter rolls-over after 469 pixels
WHEN (hcnt<468) THEN hcnt:=hcnt+1 ELSE hcnt:=0;
"horiz. sync is low during this interval to signal start of a new line
WHEN ((hcnt>=384)&(hcnt<441)) THEN N_HSYNC:=0 ELSE N_HSYNC:=1;
"vertical line counter rolls-over after 525 lines
WHEN (vcnt<524) THEN vcnt:=vcnt+1 ELSE vcnt:=0;
"vert sync is low during this interval to signal the start of a frame
WHEN ((vcnt>=490)&(vcnt<492)) THEN N_VSYNC:=0 ELSE N_VSYNC:=1;
"blank video outside of visible region: (0,0)->(320,400) left border 26 top border 40
WHEN (hcnt>=346)#(hcnt<26)#(vcnt>=440)#(vcnt<40) THEN blank=1 ELSE blank=0;
"store the blanking signal for use in the next pipeline stage
pblank.ACLR = !N_RESET;
pblank.CLK = CLOCK;
pblank := blank;
VDATA.ACLR = !N_RESET;
VDATA.CLK = CLOCK;
WHEN pblank==0 THEN VDATA := RDATA " when the video signal is blanked, the RGB value is forced to 0.
ELSE VDATA := [0,0,0,0,0,0,0,0]; " blanks edges of screen
WHEN vcnt==0 THEN VADDR := [0,0,0 ,0,0,0,0 ,0,0,0,0 ,0,0,0,0 ,0,0,0,0]
" set new page at end of frame
ELSE WHEN pblank==0
THEN VADDR := VADDR + 1 " inc video memory pointer during active display and reset to load RAM from EEPROM
ELSE WHEN double THEN VADDR := VADDR - 320 " on odd lines, draw the previous line again
ELSE VADDR := VADDR; " all other fclks, keep VADDr the same
double = ((vcnt>40) & (hcnt==467) & (vcnt0==1)); "decides which line to draw twice
RADDR = VADDR;
RDY = 0;
BE = 0;
N_MEMRD = 0;
N_MEMWR = 1;
N_RAMCS =0;
And this is the relevant portion where the VGA generator should be granted access.
Code:
// CPU RAM address bus control
RADDR = [CA18..CA0]; "Connect CPU address to RAM address bus
// CPU RAM data bus control (bi-directional) [taken from ABEL reference]
CDATA = RDATA; "RAM data moves to CPU data bus
RDATA = CDATA; "CPU data moves to RAM data bus
CDATA.oe = N_RW & PHI2 & N_RESET & "i.e. read from RAM
N_VIA0CS & N_VIA1CS; "When VIA's selected -> tri-state CDATA
RDATA.oe = !N_RW & PHI2; "i.e. write to RAM
// MEMORY Read Write Qualification
N_MEMRD = !(PHI2 & N_RW); "MEMORY Read qualified with PHI2
N_MEMWR = !(PHI2 & !N_RW); "MEMORY Write qualified with PHI2
The first thing I tried is to get access to the VGA while PHI2 is low.
That's easy enough for the address lines:
Code:
WHEN PHI2 THEN RADDR = [CA18..CA0] " On PHI2 high, place CPU address on Memory Address bus
ELSE RADDR = VADDR; " On PHI2 low, put Video data address on Memory Address Bus
The SRAM's data bus should be connected to the VIDEO data buffer during visible pixels and while PHI2 is low.
Code:
WHEN pblank==0 & !PHI2 THEN VDATA := RDATA " when the video signal is blanked, the RGB value is forced to 0.
ELSE VDATA := [0,0,0,0,0,0,0,0]; " blanks edges of screen
With those alterations memory access and address decoding etc. work correctly.
N_MEMRD should be low when PHI2 is low.
Code:
!N_MEMRD = ((PHI2 & N_RW) # (PHI2)); "MEMORY Read qualified with PHI2
Here's the first problem. The CPU crashes.
BigEd wrote:
If you only needed a single access per clock phase, it would be easier, as the 6502 doesn't need the bus during phi1 - see the Beeb's approach for example.
Yes, that's part of the problem, Daryl's SBC-3 uses composite video which only needs one byte per clock phase.
BigEd wrote:
One way is to use RDY to stall the CPU
I roughly tried that with:
Code:
WHEN pblank==0 THEN RDY := 0
ELSE RDY := 1;
WHEN pblank==0 THEN BE := 0
ELSE BE := 1;
But it seems the CPU doesn't like that and crashes.
BigEd wrote:
another way is to stop the CPU's clock (or, equivalently, stretch the clock)
I also tried that with:
Code:
WHEN (pblank==0 & PHI2) THEN PHI2= 1
ELSE PHI2 = DIV0; " normal clock rate
Same again, the CPU crashes. Btw. I don't use any interrupts.
BigEd wrote:
Note that the original 6502 and the 65816 both honour RDY only during read cycles.
I'm using the 65816. Should I use (VPA # VDA)?
BigEd wrote:
In either case, you need some logic in your CPLD to decouple the CPU databus and address bus and instead attach the VGA's busses to the RAM. But maybe that was already obvious!
There has been taken care of, but I don't rule out that there could still be a problem.
I think a good plan would be to first try stretching the clock or stopping the CPU. When that works, I could begin to fetch bytes for the VGA generator.