diff --git a/old/se-xga.sv b/old/se-xga.sv new file mode 100644 index 0000000..12c0e64 --- /dev/null +++ b/old/se-xga.sv @@ -0,0 +1,342 @@ +/****************************************************************************** + * 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 \ No newline at end of file diff --git a/rev3.sv b/rev3.sv deleted file mode 100644 index 1d7e836..0000000 --- a/rev3.sv +++ /dev/null @@ -1,230 +0,0 @@ -/****************************************************************************** - * 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 + 1; - else begin - hCount <= 0; - if(vCount <= 805) vCount <= vCount + 1; - else vCount <= 0; - end -end - -// Horizontal sync -always @(negedge pixClk) begin - if(hCount == 0) nhSyncR <= 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) nvramWEr <= 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; - //cpuLDSsrv <= 1; - vramData <= cpuData[15:8]; - end else if(!ncpuLDSr && !cpuLDSsrv) begin - vramAddr[0] <= 1; - //cpuLDSsrv <= 1; - vramData <= cpuData[7:0]; - end - end - endcase - end -end - -// VRAM chip enable signals -always @(posedge pixClk) begin - if(!hCount[0]) begin - if(hCount[3:1] == 7) begin - // read cycle - if(fetchActive) begin - nvramCE0 <= ~vidBufSel; - nvramCE1 <= vidBufSel; - end else begin - nvramCE0 <= 1; - nvramCE1 <= 1; - end - end else if(hCount[1]) begin - // write cycle - if(!ncpuUDSr && !cpuUDSsrv) begin - nvramCE0 <= ~vramAddr[15]; - nvramCE1 <= vramAddr[15]; - cpuUDSsrv <= 1; - end else if(!ncpuLDSr && !cpuLDSsrv) begin - nvramCE0 <= ~vramAddr[15]; - nvramCE1 <= vramAddr[15]; - cpuLDSsrv <= 1; - end else begin - nvramCE0 <= 1; - nvramCE1 <= 1; - end - end - end else begin - nvramCE0 <= 1; - nvramCE1 <= 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 diff --git a/se-xga.sv b/se-xga.sv index 8d57bef..e5a4cd1 100644 --- a/se-xga.sv +++ b/se-xga.sv @@ -2,27 +2,27 @@ * SE-VGA * Top-level module * techav - * 2021-08-04 + * 2021-10-16 ****************************************************************************** - * Pulls together all the smaller modules to form the SE-VGA adapter + * Trying again again again *****************************************************************************/ 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 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 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 + 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 logic [23:1] cpuAddr, // CPU Address bus - input logic [15:0] cpuData, // CPU Data bus + 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 @@ -32,33 +32,35 @@ module sevga ( /****************************************************************************** * Initial Video Signal Timing - * The following four functions establish the basic XGA signal timing and + * 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 -wire nhSyncInner; - -// Primary video sync counters -- Now more synchronous! always @(negedge pixClk) begin - if(hCount < 11'd1343) hCount <= hCount + 11'd1; + if(hCount < 1343) hCount <= hCount + 11'h1; else begin - hCount <= 11'd0; - if(vCount < 10'd805) vCount <= vCount + 10'd1; - else vCount <= 10'd0; + hCount <= 0; + if(vCount <= 805) vCount <= vCount + 10'h1; + else vCount <= 0; 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; +// Horizontal sync +always @(negedge pixClk) begin + if(hCount == 0) nhSync <= 1; + else if(hCount == 1052) nhSync <= 0; + else if(hCount == 1186) nhSync <= 1; +end - if(vCount >= 10'd729 && vCount < 10'd735) nvSync <= 0; - else nvSync <= 1; +// Vertical sync +always @(negedge pixClk) begin + if(vCount == 0) nvSync <= 1; + else if(vCount == 729) nvSync <= 0; + else if(vCount == 734) nvSync <= 0; end /****************************************************************************** @@ -66,276 +68,162 @@ end * 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; +// 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; + /****************************************************************************** - * Primary State Machine - * This is the primary state machine which runs the entire system, handling - * VRAM reads, VRAM writes, VIA writes, and idle states + * VRAM State Machine + * Coordinates VRAM load/store actions *****************************************************************************/ -// used to align primary state machine with horizontal counter -wire [3:0] vSeq = hCount[3:0]; +// rising edge signals: nvramWE, nvramOE, nvramCE[1:0] +// falling edge signals: vramAddr, vramData -// 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 +// VRAM read signal +//always @(posedge pixClk) begin nvramOE <= ~(hCount == 7); end -logic [3:0] pState; +// 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 -// 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; +// 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 - 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 +// 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 - 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 + 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 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; +// 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 - -// final video output always_comb begin - if(vidActive) vidOut <= ~vidData[8]; + if(vidActive) vidOut = vidShiftr[7]; 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. + * Watches the CPU bus and aligns its operations with the pixel clock *****************************************************************************/ - -/* 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); +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] // 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; + && !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 - 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 + ncpuUDSr <= 1; + ncpuLDSr <= 1; 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 +// hold low for now +reg vidBufSel = 0; endmodule \ No newline at end of file