mirror of
https://github.com/techav-homebrew/SE-VGA.git
synced 2024-12-27 09:31:06 +00:00
Mostly working
This commit is contained in:
parent
1137244a39
commit
a9b119fb6d
2590
Compiled/sevga.jed
2590
Compiled/sevga.jed
File diff suppressed because it is too large
Load Diff
Binary file not shown.
342
old/se-xga.sv
342
old/se-xga.sv
@ -1,342 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
* SE-VGA
|
|
||||||
* Top-level module
|
|
||||||
* techav
|
|
||||||
* 2021-10-12
|
|
||||||
******************************************************************************
|
|
||||||
* This is ... mostly working. It has some write glitches and a vertical line
|
|
||||||
* five pixels from the left side of the screen.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
module sevga (
|
|
||||||
input wire nReset, // System reset signal
|
|
||||||
input wire pixClk, // 65MHz pixel clock
|
|
||||||
output wire nhSync, // HSync signal
|
|
||||||
output wire nvSync, // VSync signal
|
|
||||||
output wire vidOut, // 1-bit Monochrome video signal
|
|
||||||
|
|
||||||
output logic [14:0] vramAddr, // VRAM Address bus
|
|
||||||
inout logic [7:0] vramData, // VRAM Data bus
|
|
||||||
output wire nvramOE, // VRAM Read strobe
|
|
||||||
output wire nvramWE, // VRAM Write strobe
|
|
||||||
output wire nvramCE0, // VRAM Main chip select signal
|
|
||||||
output wire nvramCE1, // VRAM Alt chip select signal
|
|
||||||
|
|
||||||
input logic [23:1] cpuAddr, // CPU Address bus
|
|
||||||
input logic [15:0] cpuData, // CPU Data bus
|
|
||||||
input wire ncpuAS, // CPU Address Strobe signal
|
|
||||||
input wire ncpuUDS, // CPU Upper Data Strobe signal
|
|
||||||
input wire ncpuLDS, // CPU Lower Data Strobe signal
|
|
||||||
input wire cpuRnW, // CPU Read/Write select signal
|
|
||||||
input logic [2:0] ramSize // Select installed RAM size
|
|
||||||
);
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* Initial Video Signal Timing
|
|
||||||
* The following four functions establish the basic XGA signal timing and
|
|
||||||
* assert the horizontal and vertical sync signals as appropriate.
|
|
||||||
* These functions are the minimum required for a signal presence detect test.
|
|
||||||
*****************************************************************************/
|
|
||||||
logic [10:0] hCount; // 0..1343
|
|
||||||
logic [9:0] vCount; // 0..805
|
|
||||||
wire nhSyncInner;
|
|
||||||
|
|
||||||
// Primary video sync counters -- Now more synchronous!
|
|
||||||
always @(negedge pixClk) begin
|
|
||||||
if(hCount < 11'd1343) hCount <= hCount + 11'd1;
|
|
||||||
else begin
|
|
||||||
hCount <= 11'd0;
|
|
||||||
if(vCount < 10'd805) vCount <= vCount + 10'd1;
|
|
||||||
else vCount <= 10'd0;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// horizontal and vertical sync signals
|
|
||||||
always_comb begin
|
|
||||||
//if(hCount >= 11'd1048 && hCount < 11'd1184) nhSyncInner <= 0;
|
|
||||||
if(hCount >= 11'd1052 && hCount < 11'd1187) nhSyncInner <= 0;
|
|
||||||
else nhSyncInner <= 1;
|
|
||||||
nhSync <= nhSyncInner;
|
|
||||||
|
|
||||||
if(vCount >= 10'd729 && vCount < 10'd735) nvSync <= 0;
|
|
||||||
else nvSync <= 1;
|
|
||||||
end
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* Useful signals
|
|
||||||
* Here we break out a few useful signals, derived from the timing above, that
|
|
||||||
* will help us elsewhere.
|
|
||||||
*****************************************************************************/
|
|
||||||
wire hActive, vActive; // active video signals. vidout black when negated
|
|
||||||
wire vidActive; // active when both hActive and vActive asserted
|
|
||||||
wire hLoad; // load pixel data from vram when asserted
|
|
||||||
|
|
||||||
assign vidActive = hActive & vActive;
|
|
||||||
|
|
||||||
always_comb begin
|
|
||||||
if(hCount >= 3 && hCount < 1027) hActive <= 1;
|
|
||||||
else hActive <= 0;
|
|
||||||
|
|
||||||
if(vCount >= 0 && vCount < 684) vActive <= 1;
|
|
||||||
else vActive <= 0;
|
|
||||||
|
|
||||||
if(hCount >= 0 && hCount < 1024 && vActive) hLoad <= 1;
|
|
||||||
else hLoad <= 0;
|
|
||||||
end
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* Primary State Machine
|
|
||||||
* This is the primary state machine which runs the entire system, handling
|
|
||||||
* VRAM reads, VRAM writes, VIA writes, and idle states
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
// used to align primary state machine with horizontal counter
|
|
||||||
wire [3:0] vSeq = hCount[3:0];
|
|
||||||
|
|
||||||
// define state machine states (Gray code)
|
|
||||||
parameter
|
|
||||||
S0 = 4'b0000, // VRAM Read 0
|
|
||||||
S1 = 4'b0001, // VRAM Read 1
|
|
||||||
S2 = 4'b0011, // Idle
|
|
||||||
S3 = 4'b0010, // VRAM Write Upper 0
|
|
||||||
S4 = 4'b0110, // VRAM Write Upper 1
|
|
||||||
S5 = 4'b0111, // VRAM Write Lower 0
|
|
||||||
S6 = 4'b0101, // VRAM Write Lower 1
|
|
||||||
S7 = 4'b0100, // VIA Write
|
|
||||||
S8 = 4'b1100, // VSync (to be added later)
|
|
||||||
S9 = 4'b1101, // undefined
|
|
||||||
S10 = 4'b1111, // undefined
|
|
||||||
S11 = 4'b1110, // undefined
|
|
||||||
S12 = 4'b1010, // undefined
|
|
||||||
S13 = 4'b1011, // undefined
|
|
||||||
S14 = 4'b1001, // undefined
|
|
||||||
S15 = 4'b1000; // undefined
|
|
||||||
|
|
||||||
logic [3:0] pState;
|
|
||||||
|
|
||||||
// And here is the much simplified primary state machine
|
|
||||||
always @(negedge pixClk or negedge nReset) begin
|
|
||||||
if(!nReset) pState <= S2; // resync on reset by jumping to idle state
|
|
||||||
else begin
|
|
||||||
case(pState)
|
|
||||||
S0: pState <= S1; // first VRAM read state, always move to S1
|
|
||||||
S3: pState <= S4; // first UDS write state, always move to S4
|
|
||||||
S5: pState <= S6; // first LDS write state, always move to S6
|
|
||||||
/*S7: begin
|
|
||||||
|
|
||||||
pState <= S2;
|
|
||||||
end*/
|
|
||||||
S2: begin
|
|
||||||
// here is where everything actually happens.
|
|
||||||
if(vSeq == 4'hF) pState <= S0; // time for a read state
|
|
||||||
else if(cpuUWriteReq && !cpuUWriteSrv && vSeq < 4'hD) pState <= S3;
|
|
||||||
else if(cpuLWriteReq && !cpuLWriteSrv && vSeq < 4'hD) pState <= S5;
|
|
||||||
else if(cpuVIAReq && !cpuVIASrv && vSeq < 4'hE) pState <= S7;
|
|
||||||
else pState <= S2;
|
|
||||||
end
|
|
||||||
default: pState <= S2; // everyone ends up at S2 (idle)
|
|
||||||
endcase
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// primary VRAM signal combination, based on the primary state machine
|
|
||||||
always_comb begin
|
|
||||||
// VRAM Read Strobe
|
|
||||||
if((pState == S0 || pState == S1) && hLoad) nvramOE <= 0;
|
|
||||||
else nvramOE <= 1;
|
|
||||||
|
|
||||||
// VRAM Write Strobe
|
|
||||||
if(pState == S3 || pState == S5) nvramWE <= 0;
|
|
||||||
else nvramWE <= 1;
|
|
||||||
|
|
||||||
// VRAM Chip Enable Signals
|
|
||||||
case(pState)
|
|
||||||
S0, S1: begin
|
|
||||||
if(hLoad) begin
|
|
||||||
nvramCE0 <= ~vidBufSel;
|
|
||||||
nvramCE1 <= vidBufSel;
|
|
||||||
end else begin
|
|
||||||
nvramCE0 <= 1;
|
|
||||||
nvramCE1 <= 1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
S3, S4, S5, S6: begin
|
|
||||||
nvramCE0 <= ~cpuBufSel;
|
|
||||||
nvramCE1 <= cpuBufSel;
|
|
||||||
end
|
|
||||||
default: begin
|
|
||||||
nvramCE0 <= 1;
|
|
||||||
nvramCE1 <= 1;
|
|
||||||
end
|
|
||||||
endcase
|
|
||||||
|
|
||||||
// VRAM Address Bus
|
|
||||||
case(pState)
|
|
||||||
S0, S1: begin
|
|
||||||
// address bus for read cycles
|
|
||||||
if(hLoad) begin
|
|
||||||
vramAddr[14:6] <= vCount[9:1];
|
|
||||||
vramAddr[5:0] <= hCount[9:4];
|
|
||||||
end else begin
|
|
||||||
vramAddr <= 0;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
S3, S4: begin
|
|
||||||
// address bus for upper write cycles
|
|
||||||
vramAddr[14:1] <= cpuAddrShift;
|
|
||||||
vramAddr[0] <= 0;
|
|
||||||
end
|
|
||||||
S5, S6: begin
|
|
||||||
// address bus for lower write cycles
|
|
||||||
vramAddr[14:1] <= cpuAddrShift;
|
|
||||||
vramAddr[0] <= 1;
|
|
||||||
end
|
|
||||||
default: begin
|
|
||||||
// address bus for idle cycles
|
|
||||||
vramAddr <= 0;
|
|
||||||
end
|
|
||||||
endcase
|
|
||||||
|
|
||||||
// VRAM Data bus
|
|
||||||
case(pState)
|
|
||||||
S3, S4 : vramData <= cpuData[15:8];
|
|
||||||
S5, S6 : vramData <= cpuData[7:0];
|
|
||||||
default: vramData <= 8'hZ;
|
|
||||||
endcase
|
|
||||||
end
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* Video Output Sequencing
|
|
||||||
* Here is the primary video output shift register sequencing.
|
|
||||||
* With these functions in place, it should be possible to strap the VRAM data
|
|
||||||
* signals and see the strapped pattern output on screen.
|
|
||||||
*****************************************************************************/
|
|
||||||
logic [8:0] vidData; // the video data we are displaying
|
|
||||||
|
|
||||||
// output shift register
|
|
||||||
always @(posedge pixClk) begin
|
|
||||||
if(pState == S1 && hLoad) begin
|
|
||||||
// store VRAM data in shift register
|
|
||||||
vidData[7:0] <= vramData;
|
|
||||||
end else if(!hCount[0] && vidActive) begin
|
|
||||||
// shift out video data
|
|
||||||
vidData[8:1] <= vidData[7:0];
|
|
||||||
vidData[0] <= 1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// final video output
|
|
||||||
always_comb begin
|
|
||||||
if(vidActive) vidOut <= ~vidData[8];
|
|
||||||
else vidOut <= 0;
|
|
||||||
end
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* CPU Bus Snooping
|
|
||||||
* Watch the CPU bus for writes to the video buffer regions of memory and write
|
|
||||||
* that data to VRAM. VRAM write cycles can occur during vidSeq 1 through 7.
|
|
||||||
* High-order bytes are passed to VRAM on tick states and low-order bytes are
|
|
||||||
* passed to VRAM on tock states. After the VRAM writes are complete, state
|
|
||||||
* machine waits for the CPU cycle to end before returning to idle.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
/* Main framebuffer starts $5900 below the top of RAM, alt frame buffer is
|
|
||||||
* $8000 below the main frame buffer
|
|
||||||
* ramSize is used to mask the CPU Address bits [21:19] to select the amount
|
|
||||||
* of memory installed in the computer. Not all possible ramSize selections
|
|
||||||
* are valid memory sizes when using 30-pin SIMMs in the Mac SE.
|
|
||||||
* They may be possible using PDS RAM expansion cards.
|
|
||||||
* ramSize mainBuffer altBuffer ramTop+1 ramSize Valid? Installed SIMMs
|
|
||||||
* $7 $3fa700 $3f2700 $400000 4.0MB Y [ 1MB 1MB ][ 1MB 1MB ]
|
|
||||||
* $6 $37a700 $372700 $380000 3.5MB N
|
|
||||||
* $5 $2fa700 $2f2700 $300000 3.0MB N
|
|
||||||
* $4 $27a700 $272700 $280000 2.5MB Y [ 1MB 1MB ][256kB 256kB]
|
|
||||||
* $3 $1fa700 $1f2700 $200000 2.0MB Y [ 1MB 1MB ][ --- --- ]
|
|
||||||
* $2 $17a700 $172700 $180000 1.5MB N
|
|
||||||
* $1 $0fa700 $0f2700 $100000 1.0MB Y [256kB 256kB][256kB 256kB]
|
|
||||||
* $0 $07a700 $072700 $080000 0.5MB Y [256kB 256kB][ --- --- ]
|
|
||||||
*/
|
|
||||||
|
|
||||||
// keep track of pending CPU write requests and whether they have been serviced
|
|
||||||
wire cpuUWriteReq, cpuLWriteReq, cpuVIAReq;
|
|
||||||
reg cpuUWriteSrv, cpuLWriteSrv, cpuVIASrv;
|
|
||||||
wire cpuBufSel;
|
|
||||||
wire cpuBufAddr;
|
|
||||||
reg vidBufSel;
|
|
||||||
wire [13:0] cpuAddrShift = cpuAddr[14:1] - 14'h1380;
|
|
||||||
wire cpuBufRange;
|
|
||||||
|
|
||||||
// these are some helpful signals that shortcut the CPU buffer & VIA addresses
|
|
||||||
always_comb begin
|
|
||||||
/*if(cpuAddr[14:1] >= 14'h1380
|
|
||||||
&& cpuAddr[14:1] < 14'h3E40) cpuBufRange <= 1;
|
|
||||||
else cpuBufRange <= 0;*/
|
|
||||||
cpuBufRange <= (cpuAddr[14:1] >= 14'h1380) & (cpuAddr[14:1] < 14'h3E40);
|
|
||||||
if(!ncpuAS && !cpuRnW
|
|
||||||
&& !cpuAddr[23] && !cpuAddr[22] // first two bits always 0
|
|
||||||
&& !(cpuAddr[21] ^ ramSize[2]) // compare with RAM Size bits
|
|
||||||
&& !(cpuAddr[20] ^ ramSize[1])
|
|
||||||
&& !(cpuAddr[19] ^ ramSize[0])
|
|
||||||
&& cpuAddr[18] && cpuAddr[17] // next three bits always 1
|
|
||||||
&& cpuAddr[16] // skip 15, it selects buffers
|
|
||||||
&& cpuBufRange // only select buffer addresses
|
|
||||||
) begin
|
|
||||||
cpuBufAddr <= 1;
|
|
||||||
end else begin
|
|
||||||
cpuBufAddr <= 0;
|
|
||||||
end
|
|
||||||
cpuBufSel <= ~cpuAddr[15]; // address bit 15 selects buffer
|
|
||||||
|
|
||||||
if(cpuBufAddr && !ncpuUDS) cpuUWriteReq <= 1;
|
|
||||||
else cpuUWriteReq <= 0;
|
|
||||||
if(cpuBufAddr && !ncpuLDS) cpuLWriteReq <= 1;
|
|
||||||
else cpuLWriteReq <= 0;
|
|
||||||
|
|
||||||
// VIA is in address block $E8,0000 - $EF,FFFF
|
|
||||||
// VIA register select pins (RS[3:0]) are wired to cpuAddr[12:9]
|
|
||||||
// VIA Output Register A is selected when RS[3:0]==$F
|
|
||||||
/*if(!ncpuAS && !cpuRnW && !ncpuUDS
|
|
||||||
&& cpuAddr[23] && cpuAddr[22] // VIA Address Select
|
|
||||||
&& cpuAddr[21] && !cpuAddr[20]
|
|
||||||
&& cpuAddr[19]
|
|
||||||
&& cpuAddr[12] && cpuAddr[11] // VIA ORA
|
|
||||||
&& cpuAddr[10] && cpuAddr[9]
|
|
||||||
) cpuVIAReq <= 1;
|
|
||||||
else cpuVIAReq <= 0;*/
|
|
||||||
// Mac ROM addresses Data Register A as vBase+vBufA:
|
|
||||||
// $EF,E1FE + (512*15) = $EF,FFFE
|
|
||||||
// shift right by one because no A0 and we get $77,FFFF
|
|
||||||
// This bit is giving me hell, so let's expand it
|
|
||||||
if(ncpuAS==0 && cpuRnW==0 && ncpuUDS==0
|
|
||||||
&& cpuAddr == 22'h77FFFF) cpuVIAReq <= 1;
|
|
||||||
else cpuVIAReq <= 0;
|
|
||||||
end
|
|
||||||
|
|
||||||
// if there's an active CPU request and we've reached the state for servicing
|
|
||||||
// that CPU request, then set a flag to mark that we have serviced it
|
|
||||||
always @(posedge pixClk or posedge ncpuAS) begin
|
|
||||||
if(ncpuAS) begin
|
|
||||||
cpuUWriteSrv <= 0;
|
|
||||||
cpuLWriteSrv <= 0;
|
|
||||||
cpuVIASrv <= 0;
|
|
||||||
end else begin
|
|
||||||
if(ncpuAS) begin
|
|
||||||
cpuUWriteSrv <= 0;
|
|
||||||
cpuLWriteSrv <= 0;
|
|
||||||
cpuVIASrv <= 0;
|
|
||||||
end else begin
|
|
||||||
if(cpuUWriteReq && pState == S3) cpuUWriteSrv <= 1;
|
|
||||||
if(cpuLWriteReq && pState == S5) cpuLWriteSrv <= 1;
|
|
||||||
if(cpuVIAReq && pState == S7) cpuVIASrv <= 1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// store the video buffer selection bit
|
|
||||||
always @(posedge pixClk or negedge nReset) begin
|
|
||||||
if(!nReset) vidBufSel <= 0;
|
|
||||||
// fine. no video buffer select. we use Main only.
|
|
||||||
//else if(pState == S7) vidBufSel <= ~cpuData[14];
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
229
old/se-xga_bad.sv
Normal file
229
old/se-xga_bad.sv
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* SE-VGA
|
||||||
|
* Top-level module
|
||||||
|
* techav
|
||||||
|
* 2021-10-16
|
||||||
|
******************************************************************************
|
||||||
|
* Trying again again again
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
module sevga (
|
||||||
|
input wire nReset, // System reset signal
|
||||||
|
input wire pixClk, // 65MHz pixel clock
|
||||||
|
output reg nhSync, // HSync signal
|
||||||
|
output reg nvSync, // VSync signal
|
||||||
|
output reg vidOut, // 1-bit Monochrome video signal
|
||||||
|
|
||||||
|
output logic [14:0] vramAddr, // VRAM Address bus
|
||||||
|
inout logic [7:0] vramData, // VRAM Data bus
|
||||||
|
output reg nvramOE, // VRAM Read strobe
|
||||||
|
output reg nvramWE, // VRAM Write strobe
|
||||||
|
output reg nvramCE0, // VRAM Main chip select signal
|
||||||
|
output reg nvramCE1, // VRAM Alt chip select signal
|
||||||
|
|
||||||
|
input wire [23:1] cpuAddr, // CPU Address bus
|
||||||
|
input wire [15:0] cpuData, // CPU Data bus
|
||||||
|
input wire ncpuAS, // CPU Address Strobe signal
|
||||||
|
input wire ncpuUDS, // CPU Upper Data Strobe signal
|
||||||
|
input wire ncpuLDS, // CPU Lower Data Strobe signal
|
||||||
|
input wire cpuRnW, // CPU Read/Write select signal
|
||||||
|
input logic [2:0] ramSize // Select installed RAM size
|
||||||
|
);
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* Initial Video Signal Timing
|
||||||
|
* The following functions establish the basic XGA signal timing and
|
||||||
|
* assert the horizontal and vertical sync signals as appropriate.
|
||||||
|
* These functions are the minimum required for a signal presence detect test.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
// Primary sync counters
|
||||||
|
logic [10:0] hCount; // 0..1343
|
||||||
|
logic [9:0] vCount; // 0..805
|
||||||
|
always @(negedge pixClk) begin
|
||||||
|
if(hCount < 1343) hCount <= hCount + 11'h1;
|
||||||
|
else begin
|
||||||
|
hCount <= 0;
|
||||||
|
if(vCount <= 805) vCount <= vCount + 10'h1;
|
||||||
|
else vCount <= 0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Horizontal sync
|
||||||
|
always @(negedge pixClk) begin
|
||||||
|
if(hCount == 0) nhSync <= 1;
|
||||||
|
else if(hCount == 1052) nhSync <= 0;
|
||||||
|
else if(hCount == 1186) nhSync <= 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Vertical sync
|
||||||
|
always @(negedge pixClk) begin
|
||||||
|
if(vCount == 0) nvSync <= 1;
|
||||||
|
else if(vCount == 729) nvSync <= 0;
|
||||||
|
else if(vCount == 734) nvSync <= 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* Useful signals
|
||||||
|
* Here we break out a few useful signals, derived from the timing above, that
|
||||||
|
* will help us elsewhere.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
// Horizontal active
|
||||||
|
reg hActive;
|
||||||
|
always @(negedge pixClk) begin
|
||||||
|
if(hCount == 0) hActive <= 1;
|
||||||
|
else if(hCount == 1023) hActive <= 0;
|
||||||
|
else if(hCount == 1343) hActive <= 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Vertical active
|
||||||
|
reg vActive;
|
||||||
|
always @(negedge pixClk) begin
|
||||||
|
if(vCount == 0) vActive <= 1;
|
||||||
|
else if(vCount == 683) vActive <= 0;
|
||||||
|
else if(vCount == 805) vActive <= 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Horizontal fetch active
|
||||||
|
// asserted just before active video to enable video data pre-fetch
|
||||||
|
reg fhActive;
|
||||||
|
always @(negedge pixClk) begin
|
||||||
|
if(hCount == 0) fhActive <= 1;
|
||||||
|
else if(hCount == 1022) fhActive <= 0;
|
||||||
|
else if(hCount == 1342) fhActive <= 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Vertical fetch active
|
||||||
|
//
|
||||||
|
reg fvActive;
|
||||||
|
always @(negedge pixClk) begin
|
||||||
|
if(vCount == 0) fvActive <= 1;
|
||||||
|
else if(vCount == 684) fvActive <= 0;
|
||||||
|
if(vCount == 805) fvActive <= 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// combined active signals
|
||||||
|
wire vidActive = hActive & vActive;
|
||||||
|
wire fetchActive = fhActive & fvActive;
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* VRAM State Machine
|
||||||
|
* Coordinates VRAM load/store actions
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
// rising edge signals: nvramWE, nvramOE, nvramCE[1:0]
|
||||||
|
// falling edge signals: vramAddr, vramData
|
||||||
|
|
||||||
|
// VRAM read signal
|
||||||
|
//always @(posedge pixClk) begin nvramOE <= ~(hCount == 7); end
|
||||||
|
|
||||||
|
// VRAM write signal
|
||||||
|
always @(posedge pixClk) begin
|
||||||
|
if(hCount[3:1] == 0) nvramWE <= 1;
|
||||||
|
else if(hCount[3:1] == 1) nvramWE <= 0;
|
||||||
|
else if(hCount[3:1] == 6) nvramWE <= 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// VRAM data/address busses
|
||||||
|
always @(negedge pixClk) begin
|
||||||
|
if(hCount[0] && !hCount[1]) begin
|
||||||
|
case(hCount[3:2])
|
||||||
|
3: begin
|
||||||
|
// start read cycle
|
||||||
|
vramData <= 8'hZ;
|
||||||
|
vramAddr[14:6] <= vCount[9:1];
|
||||||
|
vramAddr[5:0] <= hCount[9:4];
|
||||||
|
end
|
||||||
|
default: begin
|
||||||
|
// write slots
|
||||||
|
vramAddr[14:1] <= cpuAddr[14:1] - 14'h1380;
|
||||||
|
if(!ncpuUDSr && !cpuLDSsrv) begin
|
||||||
|
vramAddr[0] <= 0;
|
||||||
|
vramData <= cpuData[15:8];
|
||||||
|
end else if(!ncpuLDSr && !cpuLDSsrv) begin
|
||||||
|
vramAddr[0] <= 1;
|
||||||
|
vramData <= cpuData[7:0];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// VRAM chip enable signals
|
||||||
|
reg cpuUDSsrv, cpuLDSsrv;
|
||||||
|
always @(posedge pixClk) begin
|
||||||
|
if(hCount[3:1] == 7 && fetchActive) begin
|
||||||
|
nvramCE0 <= vidBufSel;
|
||||||
|
nvramCE1 <= ~vidBufSel;
|
||||||
|
nvramOE <= 0;
|
||||||
|
end else if(!hCount[0] && hCount[1]) begin
|
||||||
|
// write cycle
|
||||||
|
if(!ncpuUDSr && !cpuUDSsrv) begin
|
||||||
|
nvramCE0 <= ~cpuAddr[15];
|
||||||
|
nvramCE1 <= cpuAddr[15];
|
||||||
|
cpuUDSsrv <= 1;
|
||||||
|
end else if(!ncpuLDSr && !cpuLDSsrv) begin
|
||||||
|
nvramCE0 <= ~cpuAddr[15];
|
||||||
|
nvramCE1 <= cpuAddr[15];
|
||||||
|
cpuLDSsrv <= 1;
|
||||||
|
end else begin
|
||||||
|
nvramCE0 <= 1;
|
||||||
|
nvramCE1 <= 1;
|
||||||
|
end
|
||||||
|
nvramOE <= 1;
|
||||||
|
end else begin
|
||||||
|
nvramCE0 <= 1;
|
||||||
|
nvramCE1 <= 1;
|
||||||
|
nvramOE <= 1;
|
||||||
|
end
|
||||||
|
// reset the upper/lower serve signals when cycle ended by CPU
|
||||||
|
if(ncpuLDS) cpuLDSsrv <= 0;
|
||||||
|
if(ncpuUDS) cpuUDSsrv <= 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Video data shift register & output
|
||||||
|
reg [7:0] vidShiftr;
|
||||||
|
always @(negedge pixClk) begin
|
||||||
|
if(hCount[3:0] == 4'hF) vidShiftr <= ~vramData;
|
||||||
|
else if(hCount[0]) begin
|
||||||
|
vidShiftr[7:1] <= vidShiftr[6:0];
|
||||||
|
vidShiftr[0] <= 0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
always_comb begin
|
||||||
|
if(vidActive) vidOut = vidShiftr[7];
|
||||||
|
else vidOut <= 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* CPU Bus Snooping
|
||||||
|
* Watches the CPU bus and aligns its operations with the pixel clock
|
||||||
|
*****************************************************************************/
|
||||||
|
reg ncpuUDSr, ncpuLDSr;
|
||||||
|
always @(negedge pixClk) begin
|
||||||
|
// this condition evaluates true when cpu is writing to video buffer
|
||||||
|
if(!ncpuAS && !cpuRnW
|
||||||
|
&& !cpuAddr[23] && !cpuAddr[22]
|
||||||
|
&& !(cpuAddr[21] ^ ramSize[2])
|
||||||
|
&& !(cpuAddr[20] ^ ramSize[1])
|
||||||
|
&& !(cpuAddr[19] ^ ramSize[0])
|
||||||
|
&& cpuAddr[18] && cpuAddr[17]
|
||||||
|
&& cpuAddr[16]
|
||||||
|
&& ((cpuAddr[14:1] >= 14'h1380)
|
||||||
|
&& (cpuAddr[14:1] < 14'h3E40)))
|
||||||
|
begin
|
||||||
|
if(!ncpuUDS) ncpuUDSr <= 0;
|
||||||
|
else ncpuUDSr <= 1;
|
||||||
|
if(!ncpuLDS) ncpuLDSr <= 0;
|
||||||
|
else ncpuLDSr <= 1;
|
||||||
|
end else begin
|
||||||
|
ncpuUDSr <= 1;
|
||||||
|
ncpuLDSr <= 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// hold low for now
|
||||||
|
reg vidBufSel = 0;
|
||||||
|
|
||||||
|
endmodule
|
419
se-xga.sv
419
se-xga.sv
@ -2,27 +2,28 @@
|
|||||||
* SE-VGA
|
* SE-VGA
|
||||||
* Top-level module
|
* Top-level module
|
||||||
* techav
|
* techav
|
||||||
* 2021-10-16
|
* 2021-10-12
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Trying again again again
|
* This is ... mostly working. It has some write glitches and a vertical line
|
||||||
|
* five pixels from the left side of the screen.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
module sevga (
|
module sevga (
|
||||||
input wire nReset, // System reset signal
|
input wire nReset, // System reset signal
|
||||||
input wire pixClk, // 65MHz pixel clock
|
input wire pixClk, // 65MHz pixel clock
|
||||||
output reg nhSync, // HSync signal
|
output wire nhSync, // HSync signal
|
||||||
output reg nvSync, // VSync signal
|
output wire nvSync, // VSync signal
|
||||||
output reg vidOut, // 1-bit Monochrome video signal
|
output wire vidOut, // 1-bit Monochrome video signal
|
||||||
|
|
||||||
output logic [14:0] vramAddr, // VRAM Address bus
|
output logic [14:0] vramAddr, // VRAM Address bus
|
||||||
inout logic [7:0] vramData, // VRAM Data bus
|
inout logic [7:0] vramData, // VRAM Data bus
|
||||||
output reg nvramOE, // VRAM Read strobe
|
output wire nvramOE, // VRAM Read strobe
|
||||||
output reg nvramWE, // VRAM Write strobe
|
output wire nvramWE, // VRAM Write strobe
|
||||||
output reg nvramCE0, // VRAM Main chip select signal
|
output wire nvramCE0, // VRAM Main chip select signal
|
||||||
output reg nvramCE1, // VRAM Alt chip select signal
|
output wire nvramCE1, // VRAM Alt chip select signal
|
||||||
|
|
||||||
input wire [23:1] cpuAddr, // CPU Address bus
|
input logic [23:1] cpuAddr, // CPU Address bus
|
||||||
input wire [15:0] cpuData, // CPU Data bus
|
input logic [15:0] cpuData, // CPU Data bus
|
||||||
input wire ncpuAS, // CPU Address Strobe signal
|
input wire ncpuAS, // CPU Address Strobe signal
|
||||||
input wire ncpuUDS, // CPU Upper Data Strobe signal
|
input wire ncpuUDS, // CPU Upper Data Strobe signal
|
||||||
input wire ncpuLDS, // CPU Lower Data Strobe signal
|
input wire ncpuLDS, // CPU Lower Data Strobe signal
|
||||||
@ -32,35 +33,33 @@ module sevga (
|
|||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* Initial Video Signal Timing
|
* Initial Video Signal Timing
|
||||||
* The following functions establish the basic XGA signal timing and
|
* The following four functions establish the basic XGA signal timing and
|
||||||
* assert the horizontal and vertical sync signals as appropriate.
|
* assert the horizontal and vertical sync signals as appropriate.
|
||||||
* These functions are the minimum required for a signal presence detect test.
|
* These functions are the minimum required for a signal presence detect test.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// Primary sync counters
|
|
||||||
logic [10:0] hCount; // 0..1343
|
logic [10:0] hCount; // 0..1343
|
||||||
logic [9:0] vCount; // 0..805
|
logic [9:0] vCount; // 0..805
|
||||||
|
wire nhSyncInner;
|
||||||
|
|
||||||
|
// Primary video sync counters -- Now more synchronous!
|
||||||
always @(negedge pixClk) begin
|
always @(negedge pixClk) begin
|
||||||
if(hCount < 1343) hCount <= hCount + 11'h1;
|
if(hCount < 11'd1343) hCount <= hCount + 11'd1;
|
||||||
else begin
|
else begin
|
||||||
hCount <= 0;
|
hCount <= 11'd0;
|
||||||
if(vCount <= 805) vCount <= vCount + 10'h1;
|
if(vCount < 10'd805) vCount <= vCount + 10'd1;
|
||||||
else vCount <= 0;
|
else vCount <= 10'd0;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// Horizontal sync
|
// horizontal and vertical sync signals
|
||||||
always @(negedge pixClk) begin
|
always_comb begin
|
||||||
if(hCount == 0) nhSync <= 1;
|
//if(hCount >= 11'd1048 && hCount < 11'd1184) nhSyncInner <= 0;
|
||||||
else if(hCount == 1052) nhSync <= 0;
|
if(hCount >= 11'd1052 && hCount < 11'd1187) nhSyncInner <= 0;
|
||||||
else if(hCount == 1186) nhSync <= 1;
|
else nhSyncInner <= 1;
|
||||||
end
|
nhSync <= nhSyncInner;
|
||||||
|
|
||||||
// Vertical sync
|
if(vCount >= 10'd729 && vCount < 10'd735) nvSync <= 0;
|
||||||
always @(negedge pixClk) begin
|
else nvSync <= 1;
|
||||||
if(vCount == 0) nvSync <= 1;
|
|
||||||
else if(vCount == 729) nvSync <= 0;
|
|
||||||
else if(vCount == 734) nvSync <= 0;
|
|
||||||
end
|
end
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
@ -68,162 +67,276 @@ end
|
|||||||
* Here we break out a few useful signals, derived from the timing above, that
|
* Here we break out a few useful signals, derived from the timing above, that
|
||||||
* will help us elsewhere.
|
* will help us elsewhere.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
wire hActive, vActive; // active video signals. vidout black when negated
|
||||||
|
wire vidActive; // active when both hActive and vActive asserted
|
||||||
|
wire hLoad; // load pixel data from vram when asserted
|
||||||
|
|
||||||
// Horizontal active
|
assign vidActive = hActive & vActive;
|
||||||
reg hActive;
|
|
||||||
always @(negedge pixClk) begin
|
always_comb begin
|
||||||
if(hCount == 0) hActive <= 1;
|
if(hCount >= 3 && hCount < 1027) hActive <= 1;
|
||||||
else if(hCount == 1023) hActive <= 0;
|
else hActive <= 0;
|
||||||
else if(hCount == 1343) hActive <= 1;
|
|
||||||
|
if(vCount >= 0 && vCount < 684) vActive <= 1;
|
||||||
|
else vActive <= 0;
|
||||||
|
|
||||||
|
if(hCount >= 0 && hCount < 1024 && vActive) hLoad <= 1;
|
||||||
|
else hLoad <= 0;
|
||||||
end
|
end
|
||||||
|
|
||||||
// Vertical active
|
|
||||||
reg vActive;
|
|
||||||
always @(negedge pixClk) begin
|
|
||||||
if(vCount == 0) vActive <= 1;
|
|
||||||
else if(vCount == 683) vActive <= 0;
|
|
||||||
else if(vCount == 805) vActive <= 1;
|
|
||||||
end
|
|
||||||
|
|
||||||
// Horizontal fetch active
|
|
||||||
// asserted just before active video to enable video data pre-fetch
|
|
||||||
reg fhActive;
|
|
||||||
always @(negedge pixClk) begin
|
|
||||||
if(hCount == 0) fhActive <= 1;
|
|
||||||
else if(hCount == 1022) fhActive <= 0;
|
|
||||||
else if(hCount == 1342) fhActive <= 1;
|
|
||||||
end
|
|
||||||
|
|
||||||
// Vertical fetch active
|
|
||||||
//
|
|
||||||
reg fvActive;
|
|
||||||
always @(negedge pixClk) begin
|
|
||||||
if(vCount == 0) fvActive <= 1;
|
|
||||||
else if(vCount == 684) fvActive <= 0;
|
|
||||||
if(vCount == 805) fvActive <= 1;
|
|
||||||
end
|
|
||||||
|
|
||||||
// combined active signals
|
|
||||||
wire vidActive = hActive & vActive;
|
|
||||||
wire fetchActive = fhActive & fvActive;
|
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* VRAM State Machine
|
* Primary State Machine
|
||||||
* Coordinates VRAM load/store actions
|
* This is the primary state machine which runs the entire system, handling
|
||||||
|
* VRAM reads, VRAM writes, VIA writes, and idle states
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// rising edge signals: nvramWE, nvramOE, nvramCE[1:0]
|
// used to align primary state machine with horizontal counter
|
||||||
// falling edge signals: vramAddr, vramData
|
wire [3:0] vSeq = hCount[3:0];
|
||||||
|
|
||||||
// VRAM read signal
|
// define state machine states (Gray code)
|
||||||
//always @(posedge pixClk) begin nvramOE <= ~(hCount == 7); end
|
parameter
|
||||||
|
S0 = 4'b0000, // VRAM Read 0
|
||||||
|
S1 = 4'b0001, // VRAM Read 1
|
||||||
|
S2 = 4'b0011, // Idle
|
||||||
|
S3 = 4'b0010, // VRAM Write Upper 0
|
||||||
|
S4 = 4'b0110, // VRAM Write Upper 1
|
||||||
|
S5 = 4'b0111, // VRAM Write Lower 0
|
||||||
|
S6 = 4'b0101, // VRAM Write Lower 1
|
||||||
|
S7 = 4'b0100, // VIA Write
|
||||||
|
S8 = 4'b1100, // VSync (to be added later)
|
||||||
|
S9 = 4'b1101, // undefined
|
||||||
|
S10 = 4'b1111, // undefined
|
||||||
|
S11 = 4'b1110, // undefined
|
||||||
|
S12 = 4'b1010, // undefined
|
||||||
|
S13 = 4'b1011, // undefined
|
||||||
|
S14 = 4'b1001, // undefined
|
||||||
|
S15 = 4'b1000; // undefined
|
||||||
|
|
||||||
// VRAM write signal
|
logic [3:0] pState;
|
||||||
always @(posedge pixClk) begin
|
|
||||||
if(hCount[3:1] == 0) nvramWE <= 1;
|
|
||||||
else if(hCount[3:1] == 1) nvramWE <= 0;
|
|
||||||
else if(hCount[3:1] == 6) nvramWE <= 1;
|
|
||||||
end
|
|
||||||
|
|
||||||
// VRAM data/address busses
|
// And here is the much simplified primary state machine
|
||||||
always @(negedge pixClk) begin
|
always @(negedge pixClk or negedge nReset) begin
|
||||||
if(hCount[0] && !hCount[1]) begin
|
if(!nReset) pState <= S2; // resync on reset by jumping to idle state
|
||||||
case(hCount[3:2])
|
else begin
|
||||||
3: begin
|
case(pState)
|
||||||
// start read cycle
|
S0: pState <= S1; // first VRAM read state, always move to S1
|
||||||
vramData <= 8'hZ;
|
S3: pState <= S4; // first UDS write state, always move to S4
|
||||||
vramAddr[14:6] <= vCount[9:1];
|
S5: pState <= S6; // first LDS write state, always move to S6
|
||||||
vramAddr[5:0] <= hCount[9:4];
|
/*S7: begin
|
||||||
end
|
|
||||||
default: begin
|
pState <= S2;
|
||||||
// write slots
|
end*/
|
||||||
vramAddr[14:1] <= cpuAddr[14:1] - 14'h1380;
|
S2: begin
|
||||||
if(!ncpuUDSr && !cpuLDSsrv) begin
|
// here is where everything actually happens.
|
||||||
vramAddr[0] <= 0;
|
if(vSeq == 4'hF) pState <= S0; // time for a read state
|
||||||
vramData <= cpuData[15:8];
|
else if(cpuUWriteReq && !cpuUWriteSrv && vSeq < 4'hD) pState <= S3;
|
||||||
end else if(!ncpuLDSr && !cpuLDSsrv) begin
|
else if(cpuLWriteReq && !cpuLWriteSrv && vSeq < 4'hD) pState <= S5;
|
||||||
vramAddr[0] <= 1;
|
else if(cpuVIAReq && !cpuVIASrv && vSeq < 4'hE) pState <= S7;
|
||||||
vramData <= cpuData[7:0];
|
else pState <= S2;
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
default: pState <= S2; // everyone ends up at S2 (idle)
|
||||||
endcase
|
endcase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// VRAM chip enable signals
|
// primary VRAM signal combination, based on the primary state machine
|
||||||
reg cpuUDSsrv, cpuLDSsrv;
|
always_comb begin
|
||||||
always @(posedge pixClk) begin
|
// VRAM Read Strobe
|
||||||
if(hCount[3:1] == 7 && fetchActive) begin
|
if((pState == S0 || pState == S1) && hLoad) nvramOE <= 0;
|
||||||
nvramCE0 <= vidBufSel;
|
else nvramOE <= 1;
|
||||||
nvramCE1 <= ~vidBufSel;
|
|
||||||
nvramOE <= 0;
|
// VRAM Write Strobe
|
||||||
end else if(!hCount[0] && hCount[1]) begin
|
if(pState == S3 || pState == S5) nvramWE <= 0;
|
||||||
// write cycle
|
else nvramWE <= 1;
|
||||||
if(!ncpuUDSr && !cpuUDSsrv) begin
|
|
||||||
nvramCE0 <= ~cpuAddr[15];
|
// VRAM Chip Enable Signals
|
||||||
nvramCE1 <= cpuAddr[15];
|
case(pState)
|
||||||
cpuUDSsrv <= 1;
|
S0, S1: begin
|
||||||
end else if(!ncpuLDSr && !cpuLDSsrv) begin
|
if(hLoad) begin
|
||||||
nvramCE0 <= ~cpuAddr[15];
|
nvramCE0 <= ~vidBufSel;
|
||||||
nvramCE1 <= cpuAddr[15];
|
nvramCE1 <= vidBufSel;
|
||||||
cpuLDSsrv <= 1;
|
end else begin
|
||||||
end else begin
|
nvramCE0 <= 1;
|
||||||
|
nvramCE1 <= 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S3, S4, S5, S6: begin
|
||||||
|
nvramCE0 <= ~cpuBufSel;
|
||||||
|
nvramCE1 <= cpuBufSel;
|
||||||
|
end
|
||||||
|
default: begin
|
||||||
nvramCE0 <= 1;
|
nvramCE0 <= 1;
|
||||||
nvramCE1 <= 1;
|
nvramCE1 <= 1;
|
||||||
end
|
end
|
||||||
nvramOE <= 1;
|
endcase
|
||||||
end else begin
|
|
||||||
nvramCE0 <= 1;
|
// VRAM Address Bus
|
||||||
nvramCE1 <= 1;
|
case(pState)
|
||||||
nvramOE <= 1;
|
S0, S1: begin
|
||||||
end
|
// address bus for read cycles
|
||||||
// reset the upper/lower serve signals when cycle ended by CPU
|
if(hLoad) begin
|
||||||
if(ncpuLDS) cpuLDSsrv <= 0;
|
vramAddr[14:6] <= vCount[9:1];
|
||||||
if(ncpuUDS) cpuUDSsrv <= 0;
|
vramAddr[5:0] <= hCount[9:4];
|
||||||
|
end else begin
|
||||||
|
vramAddr <= 0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
S3, S4: begin
|
||||||
|
// address bus for upper write cycles
|
||||||
|
vramAddr[14:1] <= cpuAddrShift;
|
||||||
|
vramAddr[0] <= 0;
|
||||||
|
end
|
||||||
|
S5, S6: begin
|
||||||
|
// address bus for lower write cycles
|
||||||
|
vramAddr[14:1] <= cpuAddrShift;
|
||||||
|
vramAddr[0] <= 1;
|
||||||
|
end
|
||||||
|
default: begin
|
||||||
|
// address bus for idle cycles
|
||||||
|
vramAddr <= 0;
|
||||||
|
end
|
||||||
|
endcase
|
||||||
|
|
||||||
|
// VRAM Data bus
|
||||||
|
case(pState)
|
||||||
|
S3, S4 : vramData <= cpuData[15:8];
|
||||||
|
S5, S6 : vramData <= cpuData[7:0];
|
||||||
|
default: vramData <= 8'hZ;
|
||||||
|
endcase
|
||||||
end
|
end
|
||||||
|
|
||||||
// Video data shift register & output
|
/******************************************************************************
|
||||||
reg [7:0] vidShiftr;
|
* Video Output Sequencing
|
||||||
always @(negedge pixClk) begin
|
* Here is the primary video output shift register sequencing.
|
||||||
if(hCount[3:0] == 4'hF) vidShiftr <= ~vramData;
|
* With these functions in place, it should be possible to strap the VRAM data
|
||||||
else if(hCount[0]) begin
|
* signals and see the strapped pattern output on screen.
|
||||||
vidShiftr[7:1] <= vidShiftr[6:0];
|
*****************************************************************************/
|
||||||
vidShiftr[0] <= 0;
|
logic [8:0] vidData; // the video data we are displaying
|
||||||
|
|
||||||
|
// output shift register
|
||||||
|
always @(posedge pixClk) begin
|
||||||
|
if(pState == S1 && hLoad) begin
|
||||||
|
// store VRAM data in shift register
|
||||||
|
vidData[7:0] <= vramData;
|
||||||
|
end else if(!hCount[0] && vidActive) begin
|
||||||
|
// shift out video data
|
||||||
|
vidData[8:1] <= vidData[7:0];
|
||||||
|
vidData[0] <= 1;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// final video output
|
||||||
always_comb begin
|
always_comb begin
|
||||||
if(vidActive) vidOut = vidShiftr[7];
|
if(vidActive) vidOut <= ~vidData[8];
|
||||||
else vidOut <= 0;
|
else vidOut <= 0;
|
||||||
end
|
end
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* CPU Bus Snooping
|
* CPU Bus Snooping
|
||||||
* Watches the CPU bus and aligns its operations with the pixel clock
|
* Watch the CPU bus for writes to the video buffer regions of memory and write
|
||||||
|
* that data to VRAM. VRAM write cycles can occur during vidSeq 1 through 7.
|
||||||
|
* High-order bytes are passed to VRAM on tick states and low-order bytes are
|
||||||
|
* passed to VRAM on tock states. After the VRAM writes are complete, state
|
||||||
|
* machine waits for the CPU cycle to end before returning to idle.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
reg ncpuUDSr, ncpuLDSr;
|
|
||||||
always @(negedge pixClk) begin
|
/* Main framebuffer starts $5900 below the top of RAM, alt frame buffer is
|
||||||
// this condition evaluates true when cpu is writing to video buffer
|
* $8000 below the main frame buffer
|
||||||
|
* ramSize is used to mask the CPU Address bits [21:19] to select the amount
|
||||||
|
* of memory installed in the computer. Not all possible ramSize selections
|
||||||
|
* are valid memory sizes when using 30-pin SIMMs in the Mac SE.
|
||||||
|
* They may be possible using PDS RAM expansion cards.
|
||||||
|
* ramSize mainBuffer altBuffer ramTop+1 ramSize Valid? Installed SIMMs
|
||||||
|
* $7 $3fa700 $3f2700 $400000 4.0MB Y [ 1MB 1MB ][ 1MB 1MB ]
|
||||||
|
* $6 $37a700 $372700 $380000 3.5MB N
|
||||||
|
* $5 $2fa700 $2f2700 $300000 3.0MB N
|
||||||
|
* $4 $27a700 $272700 $280000 2.5MB Y [ 1MB 1MB ][256kB 256kB]
|
||||||
|
* $3 $1fa700 $1f2700 $200000 2.0MB Y [ 1MB 1MB ][ --- --- ]
|
||||||
|
* $2 $17a700 $172700 $180000 1.5MB N
|
||||||
|
* $1 $0fa700 $0f2700 $100000 1.0MB Y [256kB 256kB][256kB 256kB]
|
||||||
|
* $0 $07a700 $072700 $080000 0.5MB Y [256kB 256kB][ --- --- ]
|
||||||
|
*/
|
||||||
|
|
||||||
|
// keep track of pending CPU write requests and whether they have been serviced
|
||||||
|
wire cpuUWriteReq, cpuLWriteReq, cpuVIAReq;
|
||||||
|
reg cpuUWriteSrv, cpuLWriteSrv, cpuVIASrv;
|
||||||
|
wire cpuBufSel;
|
||||||
|
wire cpuBufAddr;
|
||||||
|
reg vidBufSel;
|
||||||
|
wire [13:0] cpuAddrShift = cpuAddr[14:1] - 14'h1380;
|
||||||
|
wire cpuBufRange;
|
||||||
|
|
||||||
|
// these are some helpful signals that shortcut the CPU buffer & VIA addresses
|
||||||
|
always_comb begin
|
||||||
|
/*if(cpuAddr[14:1] >= 14'h1380
|
||||||
|
&& cpuAddr[14:1] < 14'h3E40) cpuBufRange <= 1;
|
||||||
|
else cpuBufRange <= 0;*/
|
||||||
|
cpuBufRange <= (cpuAddr[14:1] >= 14'h1380) & (cpuAddr[14:1] < 14'h3E40);
|
||||||
if(!ncpuAS && !cpuRnW
|
if(!ncpuAS && !cpuRnW
|
||||||
&& !cpuAddr[23] && !cpuAddr[22]
|
&& !cpuAddr[23] && !cpuAddr[22] // first two bits always 0
|
||||||
&& !(cpuAddr[21] ^ ramSize[2])
|
&& !(cpuAddr[21] ^ ramSize[2]) // compare with RAM Size bits
|
||||||
&& !(cpuAddr[20] ^ ramSize[1])
|
&& !(cpuAddr[20] ^ ramSize[1])
|
||||||
&& !(cpuAddr[19] ^ ramSize[0])
|
&& !(cpuAddr[19] ^ ramSize[0])
|
||||||
&& cpuAddr[18] && cpuAddr[17]
|
&& cpuAddr[18] && cpuAddr[17] // next three bits always 1
|
||||||
&& cpuAddr[16]
|
&& cpuAddr[16] // skip 15, it selects buffers
|
||||||
&& ((cpuAddr[14:1] >= 14'h1380)
|
&& cpuBufRange // only select buffer addresses
|
||||||
&& (cpuAddr[14:1] < 14'h3E40)))
|
) begin
|
||||||
begin
|
cpuBufAddr <= 1;
|
||||||
if(!ncpuUDS) ncpuUDSr <= 0;
|
|
||||||
else ncpuUDSr <= 1;
|
|
||||||
if(!ncpuLDS) ncpuLDSr <= 0;
|
|
||||||
else ncpuLDSr <= 1;
|
|
||||||
end else begin
|
end else begin
|
||||||
ncpuUDSr <= 1;
|
cpuBufAddr <= 0;
|
||||||
ncpuLDSr <= 1;
|
end
|
||||||
|
cpuBufSel <= ~cpuAddr[15]; // address bit 15 selects buffer
|
||||||
|
|
||||||
|
if(cpuBufAddr && !ncpuUDS) cpuUWriteReq <= 1;
|
||||||
|
else cpuUWriteReq <= 0;
|
||||||
|
if(cpuBufAddr && !ncpuLDS) cpuLWriteReq <= 1;
|
||||||
|
else cpuLWriteReq <= 0;
|
||||||
|
|
||||||
|
// VIA is in address block $E8,0000 - $EF,FFFF
|
||||||
|
// VIA register select pins (RS[3:0]) are wired to cpuAddr[12:9]
|
||||||
|
// VIA Output Register A is selected when RS[3:0]==$F
|
||||||
|
/*if(!ncpuAS && !cpuRnW && !ncpuUDS
|
||||||
|
&& cpuAddr[23] && cpuAddr[22] // VIA Address Select
|
||||||
|
&& cpuAddr[21] && !cpuAddr[20]
|
||||||
|
&& cpuAddr[19]
|
||||||
|
&& cpuAddr[12] && cpuAddr[11] // VIA ORA
|
||||||
|
&& cpuAddr[10] && cpuAddr[9]
|
||||||
|
) cpuVIAReq <= 1;
|
||||||
|
else cpuVIAReq <= 0;*/
|
||||||
|
// Mac ROM addresses Data Register A as vBase+vBufA:
|
||||||
|
// $EF,E1FE + (512*15) = $EF,FFFE
|
||||||
|
// shift right by one because no A0 and we get $77,FFFF
|
||||||
|
// This bit is giving me hell, so let's expand it
|
||||||
|
if(ncpuAS==0 && cpuRnW==0 && ncpuUDS==0
|
||||||
|
&& cpuAddr == 22'h77FFFF) cpuVIAReq <= 1;
|
||||||
|
else cpuVIAReq <= 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
// if there's an active CPU request and we've reached the state for servicing
|
||||||
|
// that CPU request, then set a flag to mark that we have serviced it
|
||||||
|
always @(posedge pixClk or posedge ncpuAS) begin
|
||||||
|
if(ncpuAS) begin
|
||||||
|
cpuUWriteSrv <= 0;
|
||||||
|
cpuLWriteSrv <= 0;
|
||||||
|
cpuVIASrv <= 0;
|
||||||
|
end else begin
|
||||||
|
if(ncpuAS) begin
|
||||||
|
cpuUWriteSrv <= 0;
|
||||||
|
cpuLWriteSrv <= 0;
|
||||||
|
cpuVIASrv <= 0;
|
||||||
|
end else begin
|
||||||
|
if(cpuUWriteReq && pState == S3) cpuUWriteSrv <= 1;
|
||||||
|
if(cpuLWriteReq && pState == S5) cpuLWriteSrv <= 1;
|
||||||
|
if(cpuVIAReq && pState == S7) cpuVIASrv <= 1;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// hold low for now
|
// store the video buffer selection bit
|
||||||
reg vidBufSel = 0;
|
always @(posedge pixClk or negedge nReset) begin
|
||||||
|
if(!nReset) vidBufSel <= 0;
|
||||||
|
// fine. no video buffer select. we use Main only.
|
||||||
|
//else if(pState == S7) vidBufSel <= ~cpuData[14];
|
||||||
|
end
|
||||||
|
|
||||||
endmodule
|
endmodule
|
Loading…
Reference in New Issue
Block a user