diff --git a/se-xga.sv b/se-xga.sv index 76b06d0..9d66adf 100644 --- a/se-xga.sv +++ b/se-xga.sv @@ -40,19 +40,11 @@ logic [10:0] hCount; // 0..1343 logic [9:0] vCount; // 0..805 wire nhSyncInner; -// horizontal counter -always @(negedge pixClk or negedge nReset) begin - if(!nReset) hCount <= 0; - else begin - if(hCount < 11'd1343) hCount <= hCount + 11'd1; - else hCount <= 11'd0; - end -end - -// vertical counter -always @(negedge nhSyncInner or negedge nReset) begin - if(!nReset) vCount <= 0; +// 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 @@ -96,138 +88,79 @@ end * 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 - P0 = 4'b0000, // VRAM Read 0 - P1 = 4'b0001, // VRAM Read 1 - P2 = 4'b0011, // Idle 0 - P3 = 4'b0010, // Idle 1 - P4 = 4'b0110, // VRAM Write Upper 0 - P5 = 4'b0111, // VRAM Write Upper 1 - P6 = 4'b0101, // VRAM Write Lower 0 - P7 = 4'b0100, // VRAM Write Lower 1 - P8 = 4'b1100, // VIA Write 0 - P9 = 4'b1101, // VIA Write 1 - P10 = 4'b1111, // undefined - P11 = 4'b1110, // undefined - P12 = 4'b1010, // undefined - P13 = 4'b1011, // undefined - P14 = 4'b1001, // undefined - P15 = 4'b1000; // undefined + 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 <= P0; + if(!nReset) pState <= S2; // resync on reset by jumping to idle state else begin - case (pState) - P0 : begin - // first VRAM read state, always move to P1 - pState <= P1; - end - P1 : begin - // move to appropriate VRAM write state or idle - if(cpuUWriteReq && !cpuUWriteSrv) pState <= P4; - else if(cpuLWriteReq && !cpuLWriteSrv) pState <= P6; - else if(cpuVIAReq && !cpuVIASrv) pState <= P8; - else pState <= P2; - end - P2 : begin - // first idle state. - // we'll use this state to make sure we're synchronized with - // the tick-tock clock states, so if we've made it here on a - // tock state, stay here until the next tick state. - if(tick) pState <= P3; - else pState <= P2; - end - P3 : begin - // second idle state. Here is where things get fun. - case (vidSeq) - 7 : begin - pState <= P0; - end - 6 : begin - if(cpuUWriteReq && !cpuUWriteSrv && !cpuLWriteReq) pState <= P4; - else if(cpuLWriteReq && !cpuLWriteSrv) pState <= P6; - else if(cpuVIAReq && !cpuVIASrv) pState <= P8; - else pState <= P2; - end - default: begin - if(cpuUWriteReq && !cpuUWriteSrv) pState <= P4; - else if(cpuLWriteReq && !cpuLWriteSrv) pState <= P6; - else if(cpuVIAReq && !cpuVIASrv) pState <= P8; - else pState <= P2; - end - endcase - end - P4 : begin - // first VRAM Write Upper state, always move to P5 - pState <= P5; - end - P5 : begin - // second VRAM Write Upper state, - if(vidSeq == 7) pState <= P0; - else if(cpuBufAddr && !ncpuLDS) pState <= P6; - else pState <= P2; - end - P6 : begin - // first VRAM Write Lower state, always move to P7 - pState <= P7; - end - P7 : begin - // second VRAM Write Lower state - if(vidSeq == 7) pState <= P0; - else pState <= P2; - end - P8 : begin - // first VIA write state, always move to P9 - pState <= P9; - end - P9 : begin - // second VIA write state - vidBufSel <= ~cpuData[14]; - if(vidSeq == 7) pState <= P0; - else pState <= P2; - end - default: begin - // how did we end up here? We need to align with the sequence - // counter before we move to S0 - if(vidSeq == 7 && tock) pState <= P0; - else if(tick) pState <= P3; - else pState <= P2; + 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 signal combination, based on the state machine above +// primary VRAM signal combination, based on the primary state machine always_comb begin - // VRAM Read strobe - if((pState == P0 || pState == P1) && vidActive) nvramOE <= 0; + // VRAM Read Strobe + if((pState == S0 || pState == S1) && hLoad) nvramOE <= 0; else nvramOE <= 1; - // VRAM Write strobe - if(pState == P4 || pState == P6) nvramWE <= 0; + // VRAM Write Strobe + if(pState == S3 || pState == S5) nvramWE <= 0; else nvramWE <= 1; - - // VRAM Chip Enable signals + + // VRAM Chip Enable Signals case(pState) - P0, P1 : begin - if(vidActive) begin - nvramCE0 <= hCount[4]; - nvramCE1 <= ~hCount[4]; + S0, S1: begin + if(hLoad) begin + nvramCE0 <= ~vidBufSel; + nvramCE1 <= vidBufSel; end else begin nvramCE0 <= 1; nvramCE1 <= 1; end end - P4, P5 : begin - nvramCE0 <= 0; - nvramCE1 <= 1; - end - P6, P7 : begin - nvramCE0 <= 1; - nvramCE1 <= 0; + S3, S4, S5, S6: begin + nvramCE0 <= ~cpuBufSel; + nvramCE1 <= cpuBufSel; end default: begin nvramCE0 <= 1; @@ -235,30 +168,37 @@ always_comb begin end endcase - // VRAM Address bus + // VRAM Address Bus case(pState) - P0, P1 : begin + S0, S1: begin + // address bus for read cycles if(hLoad) begin - vramAddr[14] <= vidBufSel; - vramAddr[13:5] <= vCount[9:1]; - vramAddr[4:0] <= hCount[9:5]; + vramAddr[14:6] <= vCount[9:1]; + vramAddr[5:0] <= hCount[9:4]; end else begin vramAddr <= 0; end end - P4, P5, P6, P7 : begin - vramAddr[14] <= cpuBufSel; - vramAddr[13:0] <= cpuAddr[14:1] - 14'h1380; + S3, S4: begin + // address bus for upper write cycles + vramAddr[14:1] <= cpuAddr[14:1] - 14'h1380; + vramAddr[0] <= 0; + end + S5, S6: begin + // address bus for lower write cycles + vramAddr[14:1] <= cpuAddr[14:1] - 14'h1380; + vramAddr[0] <= 1; end default: begin + // address bus for idle cycles vramAddr <= 0; end endcase // VRAM Data bus case(pState) - P4, P5 : vramData <= cpuData[15:8]; - P6, P7 : vramData <= cpuData[7:0]; + S3, S4 : vramData <= cpuData[15:8]; + S5, S6 : vramData <= cpuData[7:0]; default: vramData <= 8'hZ; endcase end @@ -270,39 +210,27 @@ end * signals and see the strapped pattern output on screen. *****************************************************************************/ logic [8:0] vidData; // the video data we are displaying -wire [2:0] vidSeq; // sequence counter, derived from hCount -wire tick, tock; // even/odd pulses of pixel clock divided by 2 +//wire [2:0] vidSeq; // sequence counter, derived from hCount +//wire tick, tock; // even/odd pulses of pixel clock divided by 2 -assign vidSeq = hCount[3:1]; -assign tick = !hCount[0]; -assign tock = hCount[0]; - -// for some reason changing this function to use pState==P1 instead of the old -// tock && vidSeq==0 caused utilization to jump up 10 macrocells, and the -// monitor reported sync out of range. No idea what happened there so we'll -// leave this function as it is, since it seems to be working. -always @(negedge pixClk or negedge nReset) begin - if(!nReset) vidData <= 0; - else begin - if(tock && hLoad && vidSeq == 3'd0) begin - //if(pState == P1 && hLoad) begin - // store the VRAM data in vidData[7:0] - vidData[7:0] <= vramData; - end else if(tick && hLoad) begin - // shift vidData - vidData[8:1] <= vidData[7:0]; - vidData[0] <= 0; - end +// 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] <= 0; end end +// final video output always_comb begin - // here is where the shifted video data actually gets output 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 @@ -332,34 +260,49 @@ end // keep track of pending CPU write requests and whether they have been serviced wire cpuUWriteReq, cpuLWriteReq, cpuVIAReq; reg cpuUWriteSrv, cpuLWriteSrv, cpuVIASrv; -wire cpuBufSel = ~cpuAddr[15]; +wire cpuBufSel; wire cpuBufAddr; reg vidBufSel; // these are some helpful signals that shortcut the CPU buffer & VIA addresses always_comb begin - // remember cpuAddr is shifted right by one since 68000 does not output A0 if(!ncpuAS && !cpuRnW - && cpuAddr[23:22] == 2'b00 // initial constant - && ramSize == cpuAddr[21:19] // ram size selection - && cpuAddr[18:16] == 3'b111 // trailing constant - // next bit is main/alt select - && (cpuAddr[14:1] >= 14'h1380 // bottom of buffer range (0x2700>>1) - && cpuAddr[14:1] <= 14'h3e3f) // top of buffer range (0x7C70>>1) - ) begin - cpuBufAddr <= 1'b1; + && !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 + && (cpuAddr[14] || cpuAddr[13]) // if neither 13|14 then not buffer + ) begin + cpuBufAddr <= 1; end else begin - cpuBufAddr <= 1'b0; + 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; - if(!ncpuAS && !cpuRnW && !ncpuUDS - && cpuAddr[23:19] == 5'h1D - && cpuAddr[12:8] == 5'h1F) cpuVIAReq <= 1; + // 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 @@ -376,20 +319,18 @@ always @(posedge pixClk or posedge ncpuAS) begin cpuLWriteSrv <= 0; cpuVIASrv <= 0; end else begin - if(cpuUWriteReq && pState == P4) cpuUWriteSrv <= 1; - if(cpuLWriteReq && pState == P6) cpuLWriteSrv <= 1; - if(cpuVIAReq && pState == P8) cpuVIASrv <= 1; + if(cpuUWriteReq && pState == S3) cpuUWriteSrv <= 1; + if(cpuLWriteReq && pState == S5) cpuLWriteSrv <= 1; + if(cpuVIAReq && pState == S7) cpuVIASrv <= 1; end end end -// when servicing a CPU VIA request, read the CPU data bus to set the video -// buffer selection bit. Main: 1, Alt: 0 -/*always @(posedge pixClk or negedge nReset) begin - if(!nReset) vidBufSel <= 1; - else if(pState == P8) begin - vidBufSel <= ~cpuData[14]; - 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