SE-VGA/cpusnoop.sv

149 lines
6.4 KiB
Systemverilog

/******************************************************************************
* SE-VGA
* CPU Bus Snoop
* techav
* 2021-04-06
******************************************************************************
* Watches for writes to frame buffer memory addresses and copies that data
* into VRAM
*****************************************************************************/
module cpusnoop (
input wire nReset, // System Reset signal
input wire pixClock, // 25.175MHz Pixel Clock
input logic [2:0] seq, // Sequence count (low 3 bits of hCount)
input logic [22:0] 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 wire cpuClk, // CPU Clock
output logic [14:0] vramAddr, // VRAM Address Bus
output logic [7:0] vramDataOut,// VRAM Data Bus Output
output wire nvramWE, // VRAM Write strobe
input logic [2:0] ramSize // CPU RAM size selection
);
/* framebuffer starts $5900 below the top of RAM
* ramSize is used to mask the cpuAddr bits [21:9] 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 bufferStart ramTop+1 ramSize Valid? Installed SIMMs
* $7 $3fa700 $400000 4.0MB Y [ 1MB 1MB ][ 1MB 1MB ]
* $6 $37a700 $380000 3.5MB N
* $5 $2fa700 $300000 3.0MB N
* $4 $27a700 $280000 2.5MB Y [ 1MB 1MB ][256kB 256kB]
* $3 $1fa700 $200000 2.0MB Y [ 1MB 1MB ][ --- --- ]
* $2 $17a700 $180000 1.5MB N
* $1 $0fa700 $100000 1.0MB Y [256kB 256kB][256kB 256kB]
* $0 $07a700 $080000 0.5MB Y [256kB 256kB][ --- --- ]
*/
wire pendWriteLo; // low byte write to VRAM pending
wire pendWriteHi; // high byte write to VRAM pending
logic [13:0] addrCache; // store address for cpu writes to framebuffer
logic [7:0] dataCacheLo; // store data for cpu writes to low byte
logic [7:0] dataCacheHi; // store data for cpu writes to high byte
wire cpuBufSel; // is CPU accessing frame buffer?
// when cpu addresses the framebuffer, set our enable signal
always_comb begin
if(ramSize == cpuAddr[20:18] && cpuAddr[22:21] == 2'b00 && cpuAddr[17:14] == 4'b1111) begin
cpuBufSel <= 1'b1;
end else begin
cpuBufSel <= 1'b0;
end
end
// when cpu addresses the framebuffer, save the address
always @(negedge ncpuAS or negedge nReset) begin
if(nReset == 1'b0) begin
addrCache <= 0;
end else begin
// here we match our ramSize jumpers and constants to confirm
// the CPU is accessing the primary frame buffer
if(cpuBufSel == 1'b1) begin
// We have a match, so subtract constant $1380 from the
// cpu address and store the result in addrCache register.
// Constant $1380 corresponds to $2700 shifted right by 1.
// Once the selection bits above are masked out, we're left
// with buffer addresses starting with $2700
// e.g. with 4MB of RAM, fram buffer starts at $3FA700
// buffer address: 0011 1111 1010 0111 0000 0000 = $3FA700
// vram addr mask: 0000 0000 0011 1111 1111 1111 - $003FFF
// vram address: 0000 0000 0010 0111 0000 0000 = $002700
// Since CPU is 16-bit and does not provide A0, our cpuAddr
// signals are shifted right by one, so we need to do the same
// to our offset before subtracting it from cpuAddr
// offset: 0000 0000 0010 0111 0000 0000 = $002700
// shifted offset: 0000 0000 0001 0011 1000 0000 = $001380
addrCache <= cpuAddr[13:0] - 14'h1380;
end
end
end
// when cpu addresses the framebuffer, save high byte
always @(negedge ncpuUDS or negedge nReset) begin
if(nReset == 1'b0) begin
dataCacheHi <= 8'h0;
end else begin
if(cpuBufSel == 1'b1 && cpuRnW == 1'b0) begin
dataCacheHi <= cpuData[15:8];
end
end
end
// when cpu addresses the framebuffer, save low byte
always @(negedge ncpuLDS or negedge nReset) begin
if(nReset == 1'b0) begin
dataCacheLo <= 8'h0;
end else begin
if(cpuBufSel == 1'b1 && cpuRnW == 1'b0) begin
dataCacheLo <= cpuData[7:0];
end
end
end
// set pending flags for cpu accesses & clear when that cycle comes back around
always @(negedge pixClock or negedge nReset) begin
if(nReset == 1'b0) begin
pendWriteLo <= 1'b0;
pendWriteHi <= 1'b0;
end else begin
if(cpuBufSel == 1'b1 && cpuRnW == 1'b0) begin
if(ncpuUDS == 1'b0) begin
pendWriteHi <= 1'b1;
end
if(ncpuLDS == 1'b0) begin
pendWriteLo <= 1'b1;
end
end else begin
if(seq == 3'h1) begin
pendWriteLo <= 1'b0;
end
if(seq == 3'h2) begin
pendWriteHi <= 1'b0;
end
end
end
end
always_comb begin
vramAddr[14:1] <= addrCache[13:0];
if(pendWriteLo == 1'b1 && seq == 3'h1) begin
vramAddr[0] <= 1'b0;
nvramWE <= 1'b0;
vramDataOut <= dataCacheLo;
end else if(pendWriteHi == 1'b1 && seq == 3'h2) begin
vramAddr[0] <= 1'b1;
nvramWE <= 1'b0;
vramDataOut <= dataCacheHi;
end else begin
vramAddr[0] <= 1'b0;
nvramWE <= 1'b1;
vramDataOut <= 8'h0;
end
end
endmodule