From 7caeaa4cd759f20a6149b437ee11318f90ca9591 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Mon, 2 Sep 2019 20:31:40 -0400 Subject: [PATCH] initial import from 8bitworkshop.com --- cpu6502.v | 1365 ++++++++++++++++++++++++++++++++++++++++++++ font_cp437_8x8.v | 278 +++++++++ hvsync_generator.v | 69 +++ mango1.hex | 256 +++++++++ mango1.v | 154 +++++ 5 files changed, 2122 insertions(+) create mode 100644 cpu6502.v create mode 100644 font_cp437_8x8.v create mode 100644 hvsync_generator.v create mode 100644 mango1.hex create mode 100644 mango1.v diff --git a/cpu6502.v b/cpu6502.v new file mode 100644 index 0000000..7931544 --- /dev/null +++ b/cpu6502.v @@ -0,0 +1,1365 @@ + +/* verilator lint_off CASEX */ +/* verilator lint_off CASEINCOMPLETE */ +/* verilator lint_off CASEOVERLAP */ +/* verilator lint_off SYNCASYNCNET */ + +/* + * verilog model of 6502 CPU. + * + * (C) Arlet Ottens, + * + * https://github.com/Arlet/verilog-6502/ + * + * Feel free to use this code in any project (commercial or not), as long as you + * keep this message, and the copyright notice. This code is provided "as is", + * without any warranties of any kind. + * + * Note that not all 6502 interface signals are supported (yet). The goal + * is to create an Acorn Atom model, and the Atom didn't use all signals on + * the main board. + * + * The data bus is implemented as separate read/write buses. Combine them + * on the output pads if external memory is required. + */ + +module cpu6502( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY ); + +input clk; // CPU clock +input reset; // reset signal +output reg [15:0] AB; // address bus +input [7:0] DI; // data in, read bus +output [7:0] DO; // data out, write bus +output WE; // write enable +input IRQ; // interrupt request +input NMI; // non-maskable interrupt request +input RDY; // Ready signal. Pauses CPU when RDY=0 + +/* + * internal signals + */ + +reg [15:0] PC; // Program Counter +reg [7:0] ABL; // Address Bus Register LSB +reg [7:0] ABH; // Address Bus Register MSB +wire [7:0] ADD; // Adder Hold Register (registered in ALU) + +reg [7:0] DIHOLD; // Hold for Data In +reg DIHOLD_valid; // +wire [7:0] DIMUX; // + +reg [7:0] IRHOLD; // Hold for Instruction register +reg IRHOLD_valid; // Valid instruction in IRHOLD + +reg [7:0] AXYS[3:0]; // A, X, Y and S register file + +reg C = 0; // carry flag (init at zero to avoid X's in ALU sim) +reg Z = 0; // zero flag +reg I = 0; // interrupt flag +reg D = 0; // decimal flag +reg V = 0; // overflow flag +reg N = 0; // negative flag +wire AZ; // ALU Zero flag +wire AV; // ALU overflow flag +wire AN; // ALU negative flag +wire HC; // ALU half carry + +reg [7:0] AI; // ALU Input A +reg [7:0] BI; // ALU Input B +wire [7:0] DI; // Data In +wire [7:0] IR; // Instruction register +reg [7:0] DO; // Data Out +reg WE; // Write Enable +reg CI; // Carry In +wire CO; // Carry Out +wire [7:0] PCH = PC[15:8]; +wire [7:0] PCL = PC[7:0]; + +reg NMI_edge = 0; // captured NMI edge + +reg [1:0] regsel; // Select A, X, Y or S register +wire [7:0] regfile = AXYS[regsel]; // Selected register output + +parameter + SEL_A = 2'd0, + SEL_S = 2'd1, + SEL_X = 2'd2, + SEL_Y = 2'd3; + +/* + * define some signals for watching in simulator output + */ + + +`ifdef SIM +wire [7:0] A = AXYS[SEL_A]; // Accumulator +wire [7:0] X = AXYS[SEL_X]; // X register +wire [7:0] Y = AXYS[SEL_Y]; // Y register +wire [7:0] S = AXYS[SEL_S]; // Stack pointer +`endif + +wire [7:0] P = { N, V, 2'b11, D, I, Z, C }; + +/* + * instruction decoder/sequencer + */ + +reg [5:0] state; + +/* + * control signals + */ + +reg PC_inc; // Increment PC +reg [15:0] PC_temp; // intermediate value of PC + +reg [1:0] src_reg; // source register index +reg [1:0] dst_reg; // destination register index + +reg index_y; // if set, then Y is index reg rather than X +reg load_reg; // loading a register (A, X, Y, S) in this instruction +reg inc; // increment +reg write_back; // set if memory is read/modified/written +reg load_only; // LDA/LDX/LDY instruction +reg store; // doing store (STA/STX/STY) +reg adc_sbc; // doing ADC/SBC +reg compare; // doing CMP/CPY/CPX +reg shift; // doing shift/rotate instruction +reg rotate; // doing rotate (no shift) +reg backwards; // backwards branch +reg cond_true; // branch condition is true +reg [2:0] cond_code; // condition code bits from instruction +reg shift_right; // Instruction ALU shift/rotate right +reg alu_shift_right; // Current cycle shift right enable +reg [3:0] op; // Main ALU operation for instruction +reg [3:0] alu_op; // Current cycle ALU operation +reg adc_bcd; // ALU should do BCD style carry +reg adj_bcd; // results should be BCD adjusted + +/* + * some flip flops to remember we're doing special instructions. These + * get loaded at the DECODE state, and used later + */ +reg bit_ins; // doing BIT instruction +reg plp; // doing PLP instruction +reg php; // doing PHP instruction +reg clc; // clear carry +reg sec; // set carry +reg cld; // clear decimal +reg sed; // set decimal +reg cli; // clear interrupt +reg sei; // set interrupt +reg clv; // clear overflow +reg brk; // doing BRK + +reg res; // in reset + +/* + * ALU operations + */ + +parameter + OP_OR = 4'b1100, + OP_AND = 4'b1101, + OP_EOR = 4'b1110, + OP_ADD = 4'b0011, + OP_SUB = 4'b0111, + OP_ROL = 4'b1011, + OP_A = 4'b1111; + +/* + * Microcode state machine. Basically, every addressing mode has its own + * path through the state machine. Additional information, such as the + * operation, source and destination registers are decoded in parallel, and + * kept in separate flops. + */ + +parameter + ABS0 = 6'd0, // ABS - fetch LSB + ABS1 = 6'd1, // ABS - fetch MSB + ABSX0 = 6'd2, // ABS, X - fetch LSB and send to ALU (+X) + ABSX1 = 6'd3, // ABS, X - fetch MSB and send to ALU (+Carry) + ABSX2 = 6'd4, // ABS, X - Wait for ALU (only if needed) + BRA0 = 6'd5, // Branch - fetch offset and send to ALU (+PC[7:0]) + BRA1 = 6'd6, // Branch - fetch opcode, and send PC[15:8] to ALU + BRA2 = 6'd7, // Branch - fetch opcode (if page boundary crossed) + BRK0 = 6'd8, // BRK/IRQ - push PCH, send S to ALU (-1) + BRK1 = 6'd9, // BRK/IRQ - push PCL, send S to ALU (-1) + BRK2 = 6'd10, // BRK/IRQ - push P, send S to ALU (-1) + BRK3 = 6'd11, // BRK/IRQ - write S, and fetch @ fffe + DECODE = 6'd12, // IR is valid, decode instruction, and write prev reg + FETCH = 6'd13, // fetch next opcode, and perform prev ALU op + INDX0 = 6'd14, // (ZP,X) - fetch ZP address, and send to ALU (+X) + INDX1 = 6'd15, // (ZP,X) - fetch LSB at ZP+X, calculate ZP+X+1 + INDX2 = 6'd16, // (ZP,X) - fetch MSB at ZP+X+1 + INDX3 = 6'd17, // (ZP,X) - fetch data + INDY0 = 6'd18, // (ZP),Y - fetch ZP address, and send ZP to ALU (+1) + INDY1 = 6'd19, // (ZP),Y - fetch at ZP+1, and send LSB to ALU (+Y) + INDY2 = 6'd20, // (ZP),Y - fetch data, and send MSB to ALU (+Carry) + INDY3 = 6'd21, // (ZP),Y) - fetch data (if page boundary crossed) + JMP0 = 6'd22, // JMP - fetch PCL and hold + JMP1 = 6'd23, // JMP - fetch PCH + JMPI0 = 6'd24, // JMP IND - fetch LSB and send to ALU for delay (+0) + JMPI1 = 6'd25, // JMP IND - fetch MSB, proceed with JMP0 state + JSR0 = 6'd26, // JSR - push PCH, save LSB, send S to ALU (-1) + JSR1 = 6'd27, // JSR - push PCL, send S to ALU (-1) + JSR2 = 6'd28, // JSR - write S + JSR3 = 6'd29, // JSR - fetch MSB + PULL0 = 6'd30, // PLP/PLA - save next op in IRHOLD, send S to ALU (+1) + PULL1 = 6'd31, // PLP/PLA - fetch data from stack, write S + PULL2 = 6'd32, // PLP/PLA - prefetch op, but don't increment PC + PUSH0 = 6'd33, // PHP/PHA - send A to ALU (+0) + PUSH1 = 6'd34, // PHP/PHA - write A/P, send S to ALU (-1) + READ = 6'd35, // Read memory for read/modify/write (INC, DEC, shift) + REG = 6'd36, // Read register for reg-reg transfers + RTI0 = 6'd37, // RTI - send S to ALU (+1) + RTI1 = 6'd38, // RTI - read P from stack + RTI2 = 6'd39, // RTI - read PCL from stack + RTI3 = 6'd40, // RTI - read PCH from stack + RTI4 = 6'd41, // RTI - read PCH from stack + RTS0 = 6'd42, // RTS - send S to ALU (+1) + RTS1 = 6'd43, // RTS - read PCL from stack + RTS2 = 6'd44, // RTS - write PCL to ALU, read PCH + RTS3 = 6'd45, // RTS - load PC and increment + WRITE = 6'd46, // Write memory for read/modify/write + ZP0 = 6'd47, // Z-page - fetch ZP address + ZPX0 = 6'd48, // ZP, X - fetch ZP, and send to ALU (+X) + ZPX1 = 6'd49; // ZP, X - load from memory + +`ifdef SIM + +/* + * easy to read names in simulator output + */ +reg [8*6-1:0] statename; + +always @* + case( state ) + DECODE: statename = "DECODE"; + REG: statename = "REG"; + ZP0: statename = "ZP0"; + ZPX0: statename = "ZPX0"; + ZPX1: statename = "ZPX1"; + ABS0: statename = "ABS0"; + ABS1: statename = "ABS1"; + ABSX0: statename = "ABSX0"; + ABSX1: statename = "ABSX1"; + ABSX2: statename = "ABSX2"; + INDX0: statename = "INDX0"; + INDX1: statename = "INDX1"; + INDX2: statename = "INDX2"; + INDX3: statename = "INDX3"; + INDY0: statename = "INDY0"; + INDY1: statename = "INDY1"; + INDY2: statename = "INDY2"; + INDY3: statename = "INDY3"; + READ: statename = "READ"; + WRITE: statename = "WRITE"; + FETCH: statename = "FETCH"; + PUSH0: statename = "PUSH0"; + PUSH1: statename = "PUSH1"; + PULL0: statename = "PULL0"; + PULL1: statename = "PULL1"; + PULL2: statename = "PULL2"; + JSR0: statename = "JSR0"; + JSR1: statename = "JSR1"; + JSR2: statename = "JSR2"; + JSR3: statename = "JSR3"; + RTI0: statename = "RTI0"; + RTI1: statename = "RTI1"; + RTI2: statename = "RTI2"; + RTI3: statename = "RTI3"; + RTI4: statename = "RTI4"; + RTS0: statename = "RTS0"; + RTS1: statename = "RTS1"; + RTS2: statename = "RTS2"; + RTS3: statename = "RTS3"; + BRK0: statename = "BRK0"; + BRK1: statename = "BRK1"; + BRK2: statename = "BRK2"; + BRK3: statename = "BRK3"; + BRA0: statename = "BRA0"; + BRA1: statename = "BRA1"; + BRA2: statename = "BRA2"; + JMP0: statename = "JMP0"; + JMP1: statename = "JMP1"; + JMPI0: statename = "JMPI0"; + JMPI1: statename = "JMPI1"; + endcase + +//always @( PC ) +// $display( "%t, PC:%04x IR:%02x A:%02x X:%02x Y:%02x S:%02x C:%d Z:%d V:%d N:%d P:%02x", $time, PC, IR, A, X, Y, S, C, Z, V, N, P ); + +`endif + + + +/* + * Program Counter Increment/Load. First calculate the base value in + * PC_temp. + */ +always @* + case( state ) + DECODE: if( (~I & IRQ) | NMI_edge ) + PC_temp = { ABH, ABL }; + else + PC_temp = PC; + + + JMP1, + JMPI1, + JSR3, + RTS3, + RTI4: PC_temp = { DIMUX, ADD }; + + BRA1: PC_temp = { ABH, ADD }; + + BRA2: PC_temp = { ADD, PCL }; + + BRK2: PC_temp = res ? 16'hfffc : + NMI_edge ? 16'hfffa : 16'hfffe; + + default: PC_temp = PC; + endcase + +/* + * Determine wether we need PC_temp, or PC_temp + 1 + */ +always @* + case( state ) + DECODE: if( (~I & IRQ) | NMI_edge ) + PC_inc = 0; + else + PC_inc = 1; + + ABS0, + ABSX0, + FETCH, + BRA0, + BRA2, + BRK3, + JMPI1, + JMP1, + RTI4, + RTS3: PC_inc = 1; + + BRA1: PC_inc = CO ^~ backwards; + + default: PC_inc = 0; + endcase + +/* + * Set new PC + */ +always @(posedge clk) + if( RDY ) + PC <= PC_temp + 16'(PC_inc); + +/* + * Address Generator + */ + +parameter + ZEROPAGE = 8'h00, + STACKPAGE = 8'h01; + +always @* + case( state ) + ABSX1, + INDX3, + INDY2, + JMP1, + JMPI1, + RTI4, + ABS1: AB = { DIMUX, ADD }; + + BRA2, + INDY3, + ABSX2: AB = { ADD, ABL }; + + BRA1: AB = { ABH, ADD }; + + JSR0, + PUSH1, + RTS0, + RTI0, + BRK0: AB = { STACKPAGE, regfile }; + + BRK1, + JSR1, + PULL1, + RTS1, + RTS2, + RTI1, + RTI2, + RTI3, + BRK2: AB = { STACKPAGE, ADD }; + + INDY1, + INDX1, + ZPX1, + INDX2: AB = { ZEROPAGE, ADD }; + + ZP0, + INDY0: AB = { ZEROPAGE, DIMUX }; + + REG, + READ, + WRITE: AB = { ABH, ABL }; + + default: AB = PC; + endcase + +/* + * ABH/ABL pair is used for registering previous address bus state. + * This can be used to keep the current address, freeing up the original + * source of the address, such as the ALU or DI. + */ +always @(posedge clk) + if( state != PUSH0 && state != PUSH1 && RDY && + state != PULL0 && state != PULL1 && state != PULL2 ) + begin + ABL <= AB[7:0]; + ABH <= AB[15:8]; + end + +/* + * Data Out MUX + */ +always @* + case( state ) + WRITE: DO = ADD; + + JSR0, + BRK0: DO = PCH; + + JSR1, + BRK1: DO = PCL; + + PUSH1: DO = php ? P : ADD; + + BRK2: DO = (IRQ | NMI_edge) ? (P & 8'b1110_1111) : P; + + default: DO = regfile; + endcase + +/* + * Write Enable Generator + */ + +always @* + case( state ) + BRK0, // writing to stack or memory + BRK1, + BRK2, + JSR0, + JSR1, + PUSH1, + WRITE: WE = 1; + + INDX3, // only if doing a STA, STX or STY + INDY3, + ABSX2, + ABS1, + ZPX1, + ZP0: WE = store; + + default: WE = 0; + endcase + +/* + * register file, contains A, X, Y and S (stack pointer) registers. At each + * cycle only 1 of those registers needs to be accessed, so they combined + * in a small memory, saving resources. + */ + +reg write_register; // set when register file is written + +always @* + case( state ) + DECODE: write_register = load_reg & ~plp; + + PULL1, + RTS2, + RTI3, + BRK3, + JSR0, + JSR2 : write_register = 1; + + default: write_register = 0; + endcase + +/* + * BCD adjust logic + */ + +always @(posedge clk) + adj_bcd <= adc_sbc & D; // '1' when doing a BCD instruction + +reg [3:0] ADJL; +reg [3:0] ADJH; + +// adjustment term to be added to ADD[3:0] based on the following +// adj_bcd: '1' if doing ADC/SBC with D=1 +// adc_bcd: '1' if doing ADC with D=1 +// HC : half carry bit from ALU +always @* begin + casex( {adj_bcd, adc_bcd, HC} ) + 3'b0xx: ADJL = 4'd0; // no BCD instruction + 3'b100: ADJL = 4'd10; // SBC, and digital borrow + 3'b101: ADJL = 4'd0; // SBC, but no borrow + 3'b110: ADJL = 4'd0; // ADC, but no carry + 3'b111: ADJL = 4'd6; // ADC, and decimal/digital carry + endcase +end + +// adjustment term to be added to ADD[7:4] based on the following +// adj_bcd: '1' if doing ADC/SBC with D=1 +// adc_bcd: '1' if doing ADC with D=1 +// CO : carry out bit from ALU +always @* begin + casex( {adj_bcd, adc_bcd, CO} ) + 3'b0xx: ADJH = 4'd0; // no BCD instruction + 3'b100: ADJH = 4'd10; // SBC, and digital borrow + 3'b101: ADJH = 4'd0; // SBC, but no borrow + 3'b110: ADJH = 4'd0; // ADC, but no carry + 3'b111: ADJH = 4'd6; // ADC, and decimal/digital carry + endcase +end + +/* + * write to a register. Usually this is the (BCD corrected) output of the + * ALU, but in case of the JSR0 we use the S register to temporarily store + * the PCL. This is possible, because the S register itself is stored in + * the ALU during those cycles. + */ +always @(posedge clk) + if( write_register & RDY ) + AXYS[regsel] <= (state == JSR0) ? DIMUX : { ADD[7:4] + ADJH, ADD[3:0] + ADJL }; + +/* + * register select logic. This determines which of the A, X, Y or + * S registers will be accessed. + */ + +always @* + case( state ) + INDY1, + INDX0, + ZPX0, + ABSX0 : regsel = index_y ? SEL_Y : SEL_X; + + + DECODE : regsel = dst_reg; + + BRK0, + BRK3, + JSR0, + JSR2, + PULL0, + PULL1, + PUSH1, + RTI0, + RTI3, + RTS0, + RTS2 : regsel = SEL_S; + + default: regsel = src_reg; + endcase + +/* + * ALU + */ + +ALU ALU( .clk(clk), + .op(alu_op), + .right(alu_shift_right), + .AI(AI), + .BI(BI), + .CI(CI), + .BCD(adc_bcd & (state == FETCH)), + .CO(CO), + .OUT(ADD), + .V(AV), + .Z(AZ), + .N(AN), + .HC(HC), + .RDY(RDY) ); + +/* + * Select current ALU operation + */ + +always @* + case( state ) + READ: alu_op = op; + + BRA1: alu_op = backwards ? OP_SUB : OP_ADD; + + FETCH, + REG : alu_op = op; + + DECODE, + ABS1: alu_op = 4'bx; + + PUSH1, + BRK0, + BRK1, + BRK2, + JSR0, + JSR1: alu_op = OP_SUB; + + default: alu_op = OP_ADD; + endcase + +/* + * Determine shift right signal to ALU + */ + +always @* + if( state == FETCH || state == REG || state == READ ) + alu_shift_right = shift_right; + else + alu_shift_right = 0; + +/* + * Sign extend branch offset. + */ + +always @(posedge clk) + if( RDY ) + backwards <= DIMUX[7]; + +/* + * ALU A Input MUX + */ + +always @* + case( state ) + JSR1, + RTS1, + RTI1, + RTI2, + BRK1, + BRK2, + INDX1: AI = ADD; + + REG, + ZPX0, + INDX0, + ABSX0, + RTI0, + RTS0, + JSR0, + JSR2, + BRK0, + PULL0, + INDY1, + PUSH0, + PUSH1: AI = regfile; + + BRA0, + READ: AI = DIMUX; + + BRA1: AI = ABH; // don't use PCH in case we're + + FETCH: AI = load_only ? 0 : regfile; + + DECODE, + ABS1: AI = 8'hxx; // don't care + + default: AI = 0; + endcase + + +/* + * ALU B Input mux + */ + +always @* + case( state ) + BRA1, + RTS1, + RTI0, + RTI1, + RTI2, + INDX1, + READ, + REG, + JSR0, + JSR1, + JSR2, + BRK0, + BRK1, + BRK2, + PUSH0, + PUSH1, + PULL0, + RTS0: BI = 8'h00; + + BRA0: BI = PCL; + + DECODE, + ABS1: BI = 8'hxx; + + default: BI = DIMUX; + endcase + +/* + * ALU CI (carry in) mux + */ + +always @* + case( state ) + INDY2, + BRA1, + ABSX1: CI = CO; + + DECODE, + ABS1: CI = 1'bx; + + READ, + REG: CI = rotate ? C : + shift ? 0 : inc; + + FETCH: CI = rotate ? C : + compare ? 1 : + (shift | load_only) ? 0 : C; + + PULL0, + RTI0, + RTI1, + RTI2, + RTS0, + RTS1, + INDY0, + INDX1: CI = 1; + + default: CI = 0; + endcase + +/* + * Processor Status Register update + * + */ + +/* + * Update C flag when doing ADC/SBC, shift/rotate, compare + */ +always @(posedge clk ) + if( shift && state == WRITE ) + C <= CO; + else if( state == RTI2 ) + C <= DIMUX[0]; + else if( ~write_back && state == DECODE ) begin + if( adc_sbc | shift | compare ) + C <= CO; + else if( plp ) + C <= ADD[0]; + else begin + if( sec ) C <= 1; + if( clc ) C <= 0; + end + end + +/* + * Update Z, N flags when writing A, X, Y, Memory, or when doing compare + */ + +always @(posedge clk) + if( state == WRITE ) + Z <= AZ; + else if( state == RTI2 ) + Z <= DIMUX[1]; + else if( state == DECODE ) begin + if( plp ) + Z <= ADD[1]; + else if( (load_reg & (regsel != SEL_S)) | compare | bit_ins ) + Z <= AZ; + end + +always @(posedge clk) + if( state == WRITE ) + N <= AN; + else if( state == RTI2 ) + N <= DIMUX[7]; + else if( state == DECODE ) begin + if( plp ) + N <= ADD[7]; + else if( (load_reg & (regsel != SEL_S)) | compare ) + N <= AN; + end else if( state == FETCH && bit_ins ) + N <= DIMUX[7]; + +/* + * Update I flag + */ + +always @(posedge clk) + if( state == BRK3 ) + I <= 1; + else if( state == RTI2 ) + I <= DIMUX[2]; + else if( state == REG ) begin + if( sei ) I <= 1; + if( cli ) I <= 0; + end else if( state == DECODE ) + if( plp ) I <= ADD[2]; + +/* + * Update D flag + */ +always @(posedge clk ) + if( state == RTI2 ) + D <= DIMUX[3]; + else if( state == DECODE ) begin + if( sed ) D <= 1; + if( cld ) D <= 0; + if( plp ) D <= ADD[3]; + end + +/* + * Update V flag + */ +always @(posedge clk ) + if( state == RTI2 ) + V <= DIMUX[6]; + else if( state == DECODE ) begin + if( adc_sbc ) V <= AV; + if( clv ) V <= 0; + if( plp ) V <= ADD[6]; + end else if( state == FETCH && bit_ins ) + V <= DIMUX[6]; + +/* + * Instruction decoder + */ + +/* + * IR register/mux. Hold previous DI value in IRHOLD in PULL0 and PUSH0 + * states. In these states, the IR has been prefetched, and there is no + * time to read the IR again before the next decode. + */ + + always @(posedge clk ) + if( reset ) + IRHOLD_valid <= 0; + else if( RDY ) begin + if( state == PULL0 || state == PUSH0 ) begin + IRHOLD <= DIMUX; + IRHOLD_valid <= 1; + end else if( state == DECODE ) + IRHOLD_valid <= 0; + end + +assign IR = (IRQ & ~I) | NMI_edge ? 8'h00 : + IRHOLD_valid ? IRHOLD : DIMUX; + +always @(posedge clk ) + if( RDY ) + DIHOLD <= DI; + +assign DIMUX = ~RDY ? DIHOLD : DI; + + +/* + * Microcode state machine + */ +always @(posedge clk or posedge reset) + if( reset ) + state <= BRK0; + else if( RDY ) case( state ) + DECODE : + casex ( IR ) + 8'b0000_0000: state <= BRK0; + 8'b0010_0000: state <= JSR0; + 8'b0010_1100: state <= ABS0; // BIT abs + 8'b0100_0000: state <= RTI0; // + 8'b0100_1100: state <= JMP0; + 8'b0110_0000: state <= RTS0; + 8'b0110_1100: state <= JMPI0; + 8'b0x00_1000: state <= PUSH0; + 8'b0x10_1000: state <= PULL0; + 8'b0xx1_1000: state <= REG; // CLC, SEC, CLI, SEI + 8'b1xx0_00x0: state <= FETCH; // IMM + 8'b1xx0_1100: state <= ABS0; // X/Y abs + 8'b1xxx_1000: state <= REG; // DEY, TYA, ... + 8'bxxx0_0001: state <= INDX0; + 8'bxxx0_01xx: state <= ZP0; + 8'bxxx0_1001: state <= FETCH; // IMM + 8'bxxx0_1101: state <= ABS0; // even E column + 8'bxxx0_1110: state <= ABS0; // even E column + 8'bxxx1_0000: state <= BRA0; // odd 0 column + 8'bxxx1_0001: state <= INDY0; // odd 1 column + 8'bxxx1_01xx: state <= ZPX0; // odd 4,5,6,7 columns + 8'bxxx1_1001: state <= ABSX0; // odd 9 column + 8'bxxx1_11xx: state <= ABSX0; // odd C, D, E, F columns + 8'bxxxx_1010: state <= REG; // A, TXA, ... NOP + endcase + + ZP0 : state <= write_back ? READ : FETCH; + + ZPX0 : state <= ZPX1; + ZPX1 : state <= write_back ? READ : FETCH; + + ABS0 : state <= ABS1; + ABS1 : state <= write_back ? READ : FETCH; + + ABSX0 : state <= ABSX1; + ABSX1 : state <= (CO | store | write_back) ? ABSX2 : FETCH; + ABSX2 : state <= write_back ? READ : FETCH; + + INDX0 : state <= INDX1; + INDX1 : state <= INDX2; + INDX2 : state <= INDX3; + INDX3 : state <= FETCH; + + INDY0 : state <= INDY1; + INDY1 : state <= INDY2; + INDY2 : state <= (CO | store) ? INDY3 : FETCH; + INDY3 : state <= FETCH; + + READ : state <= WRITE; + WRITE : state <= FETCH; + FETCH : state <= DECODE; + + REG : state <= DECODE; + + PUSH0 : state <= PUSH1; + PUSH1 : state <= DECODE; + + PULL0 : state <= PULL1; + PULL1 : state <= PULL2; + PULL2 : state <= DECODE; + + JSR0 : state <= JSR1; + JSR1 : state <= JSR2; + JSR2 : state <= JSR3; + JSR3 : state <= FETCH; + + RTI0 : state <= RTI1; + RTI1 : state <= RTI2; + RTI2 : state <= RTI3; + RTI3 : state <= RTI4; + RTI4 : state <= DECODE; + + RTS0 : state <= RTS1; + RTS1 : state <= RTS2; + RTS2 : state <= RTS3; + RTS3 : state <= FETCH; + + BRA0 : state <= cond_true ? BRA1 : DECODE; + BRA1 : state <= (CO ^ backwards) ? BRA2 : DECODE; + BRA2 : state <= DECODE; + + JMP0 : state <= JMP1; + JMP1 : state <= DECODE; + + JMPI0 : state <= JMPI1; + JMPI1 : state <= JMP0; + + BRK0 : state <= BRK1; + BRK1 : state <= BRK2; + BRK2 : state <= BRK3; + BRK3 : state <= JMP0; + + endcase + +/* + * Additional control signals + */ + +always @(posedge clk) + if( reset ) + res <= 1; + else if( state == DECODE ) + res <= 0; + +always @(posedge clk) + if( state == DECODE && RDY ) + casex( IR ) + 8'b0xx01010, // ASLA, ROLA, LSRA, RORA + 8'b0xxxxx01, // ORA, AND, EOR, ADC + 8'b100x10x0, // DEY, TYA, TXA, TXS + 8'b1010xxx0, // LDA/LDX/LDY + 8'b10111010, // TSX + 8'b1011x1x0, // LDX/LDY + 8'b11001010, // DEX + 8'b1x1xxx01, // LDA, SBC + 8'bxxx01000: // DEY, TAY, INY, INX + load_reg <= 1; + + default: load_reg <= 0; + endcase + +always @(posedge clk) + if( state == DECODE && RDY ) + casex( IR ) + 8'b1110_1000, // INX + 8'b1100_1010, // DEX + 8'b101x_xx10: // LDX, TAX, TSX + dst_reg <= SEL_X; + + 8'b0x00_1000, // PHP, PHA + 8'b1001_1010: // TXS + dst_reg <= SEL_S; + + 8'b1x00_1000, // DEY, DEX + 8'b101x_x100, // LDY + 8'b1010_x000: // LDY #imm, TAY + dst_reg <= SEL_Y; + + default: dst_reg <= SEL_A; + endcase + +always @(posedge clk) + if( state == DECODE && RDY ) + casex( IR ) + 8'b1011_1010: // TSX + src_reg <= SEL_S; + + 8'b100x_x110, // STX + 8'b100x_1x10, // TXA, TXS + 8'b1110_xx00, // INX, CPX + 8'b1100_1010: // DEX + src_reg <= SEL_X; + + 8'b100x_x100, // STY + 8'b1001_1000, // TYA + 8'b1100_xx00, // CPY + 8'b1x00_1000: // DEY, INY + src_reg <= SEL_Y; + + default: src_reg <= SEL_A; + endcase + +always @(posedge clk) + if( state == DECODE && RDY ) + casex( IR ) + 8'bxxx1_0001, // INDY + 8'b10x1_x110, // LDX/STX zpg/abs, Y + 8'bxxxx_1001: // abs, Y + index_y <= 1; + + default: index_y <= 0; + endcase + + +always @(posedge clk) + if( state == DECODE && RDY ) + casex( IR ) + 8'b100x_x1x0, // STX, STY + 8'b100x_xx01: // STA + store <= 1; + + default: store <= 0; + + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b0xxx_x110, // ASL, ROL, LSR, ROR + 8'b11xx_x110: // DEC/INC + write_back <= 1; + + default: write_back <= 0; + endcase + + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b101x_xxxx: // LDA, LDX, LDY + load_only <= 1; + default: load_only <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b111x_x110, // INC + 8'b11x0_1000: // INX, INY + inc <= 1; + + default: inc <= 0; + endcase + +always @(posedge clk ) + if( (state == DECODE || state == BRK0) && RDY ) + casex( IR ) + 8'bx11x_xx01: // SBC, ADC + adc_sbc <= 1; + + default: adc_sbc <= 0; + endcase + +always @(posedge clk ) + if( (state == DECODE || state == BRK0) && RDY ) + casex( IR ) + 8'b011x_xx01: // ADC + adc_bcd <= D; + + default: adc_bcd <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b0xxx_x110, // ASL, ROL, LSR, ROR (abs, absx, zpg, zpgx) + 8'b0xxx_1010: // ASL, ROL, LSR, ROR (acc) + shift <= 1; + + default: shift <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b11x0_0x00, // CPX, CPY (imm/zp) + 8'b11x0_1100, // CPX, CPY (abs) + 8'b110x_xx01: // CMP + compare <= 1; + + default: compare <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b01xx_xx10: // ROR, LSR + shift_right <= 1; + + default: shift_right <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b0x1x_1010, // ROL A, ROR A + 8'b0x1x_x110: // ROR, ROL + rotate <= 1; + + default: rotate <= 0; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b00xx_xx10: // ROL, ASL + op <= OP_ROL; + + 8'b0010_x100: // BIT zp/abs + op <= OP_AND; + + 8'b01xx_xx10: // ROR, LSR + op <= OP_A; + + 8'b1000_1000, // DEY + 8'b1100_1010, // DEX + 8'b110x_x110, // DEC + 8'b11xx_xx01, // CMP, SBC + 8'b11x0_0x00, // CPX, CPY (imm, zpg) + 8'b11x0_1100: op <= OP_SUB; + + 8'b010x_xx01, // EOR + 8'b00xx_xx01: // ORA, AND + op <= { 2'b11, IR[6:5] }; + + default: op <= OP_ADD; + endcase + +always @(posedge clk ) + if( state == DECODE && RDY ) + casex( IR ) + 8'b0010_x100: // BIT zp/abs + bit_ins <= 1; + + default: bit_ins <= 0; + endcase +/* + * special instructions + */ +always @(posedge clk ) + if( state == DECODE && RDY ) begin + php <= (IR == 8'h08); + clc <= (IR == 8'h18); + plp <= (IR == 8'h28); + sec <= (IR == 8'h38); + cli <= (IR == 8'h58); + sei <= (IR == 8'h78); + clv <= (IR == 8'hb8); + cld <= (IR == 8'hd8); + sed <= (IR == 8'hf8); + brk <= (IR == 8'h00); + end + +always @(posedge clk) + if( RDY ) + cond_code <= IR[7:5]; + +always @* + case( cond_code ) + 3'b000: cond_true = ~N; + 3'b001: cond_true = N; + 3'b010: cond_true = ~V; + 3'b011: cond_true = V; + 3'b100: cond_true = ~C; + 3'b101: cond_true = C; + 3'b110: cond_true = ~Z; + 3'b111: cond_true = Z; + endcase + + +reg NMI_1 = 0; // delayed NMI signal + +always @(posedge clk) + NMI_1 <= NMI; + +always @(posedge clk ) + if( NMI_edge && state == BRK3 ) + NMI_edge <= 0; + else if( NMI & ~NMI_1 ) + NMI_edge <= 1; + +endmodule + +/* + * ALU. + * + * AI and BI are 8 bit inputs. Result in OUT. + * CI is Carry In. + * CO is Carry Out. + * + * op[3:0] is defined as follows: + * + * 0011 AI + BI + * 0111 AI - BI + * 1011 AI + AI + * 1100 AI | BI + * 1101 AI & BI + * 1110 AI ^ BI + * 1111 AI + * + */ + +module ALU( clk, op, right, AI, BI, CI, CO, BCD, OUT, V, Z, N, HC, RDY ); + input clk; + input right; + input [3:0] op; // operation + input [7:0] AI; + input [7:0] BI; + input CI; + input BCD; // BCD style carry + output [7:0] OUT; + output CO; + output V; + output Z; + output N; + output HC; + input RDY; + +reg [7:0] OUT; +reg CO; +wire V; +wire Z; +reg N; +reg HC; + +reg AI7; +reg BI7; +reg [8:0] temp_logic; +reg [7:0] temp_BI; +reg [4:0] temp_l; +reg [4:0] temp_h; +wire [8:0] temp = { temp_h, temp_l[3:0] }; +wire adder_CI = (right | (op[3:2] == 2'b11)) ? 0 : CI; + +// calculate the logic operations. The 'case' can be done in 1 LUT per +// bit. The 'right' shift is a simple mux that can be implemented by +// F5MUX. +always @* begin + case( op[1:0] ) + 2'b00: temp_logic = {1'b0, AI | BI}; + 2'b01: temp_logic = {1'b0, AI & BI}; + 2'b10: temp_logic = {1'b0, AI ^ BI}; + 2'b11: temp_logic = {1'b0, AI}; + endcase + + if( right ) + temp_logic = { AI[0], CI, AI[7:1] }; +end + +// Add logic result to BI input. This only makes sense when logic = AI. +// This stage can be done in 1 LUT per bit, using carry chain logic. +always @* begin + case( op[3:2] ) + 2'b00: temp_BI = BI; // A+B + 2'b01: temp_BI = ~BI; // A-B + 2'b10: temp_BI = temp_logic[7:0]; // A+A + 2'b11: temp_BI = 0; // A+0 + endcase +end + +// HC9 is the half carry bit when doing BCD add +wire HC9 = BCD & (temp_l[3:1] >= 3'd5); + +// CO9 is the carry-out bit when doing BCD add +wire CO9 = BCD & (temp_h[3:1] >= 3'd5); + +// combined half carry bit +wire temp_HC = temp_l[4] | HC9; + +// perform the addition as 2 separate nibble, so we get +// access to the half carry flag +always @* begin + temp_l = temp_logic[3:0] + temp_BI[3:0] + 5'(adder_CI); + temp_h = temp_logic[8:4] + temp_BI[7:4] + 5'(temp_HC); +end + +// calculate the flags +always @(posedge clk) + if( RDY ) begin + AI7 <= AI[7]; + BI7 <= temp_BI[7]; + OUT <= temp[7:0]; + CO <= temp[8] | CO9; + N <= temp[7]; + HC <= temp_HC; + end + +assign V = AI7 ^ BI7 ^ CO ^ N; +assign Z = ~|OUT; + +endmodule + +// test module +module cpu6502_test_top(clk, reset, AB, DI, DO, WE); +input clk,reset; +output reg [15:0] AB; // address bus +output wire [7:0] DI; // data in, read bus +output wire [7:0] DO; // data out, write bus +output wire WE; // write enable +wire IRQ=0; // interrupt request +wire NMI=0; // non-maskable interrupt request +wire RDY=1; // Ready signal. Pauses CPU when RDY=0 + + cpu6502 cpu( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY ); + + always @(posedge clk) + begin + DI <= rom[AB[3:0]]; + end + + reg [7:0] rom[0:15]; + initial begin + rom = '{ + 8'ha0,8'h13, + 8'h88, + 8'hd0,8'hfd, + 0,0,0, 0,0, + 0,0, 0,0, 0,0 + }; + end + +endmodule diff --git a/font_cp437_8x8.v b/font_cp437_8x8.v new file mode 100644 index 0000000..e318844 --- /dev/null +++ b/font_cp437_8x8.v @@ -0,0 +1,278 @@ + +`ifndef FONT_CP437_H +`define FONT_CP437_H + +// PC font (code page 437) + +module font_cp437_8x8(addr, data); + + input [10:0] addr; + output [7:0] data; + + reg [7:0] bitarray[0:2047]; + + assign data = bitarray[addr]; + + initial begin/*{w:8,h:8,bpp:1,count:256}*/ + bitarray = '{ +8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00, //0 +8'h7e,8'h81,8'ha5,8'h81,8'hbd,8'h99,8'h81,8'h7e, //1 +8'h7e,8'hff,8'hdb,8'hff,8'hc3,8'he7,8'hff,8'h7e, //2 +8'h6c,8'hfe,8'hfe,8'hfe,8'h7c,8'h38,8'h10,8'h00, //3 +8'h10,8'h38,8'h7c,8'hfe,8'h7c,8'h38,8'h10,8'h00, //4 +8'h38,8'h7c,8'h38,8'hfe,8'hfe,8'hd6,8'h10,8'h38, //5 +8'h10,8'h10,8'h38,8'h7c,8'hfe,8'h7c,8'h10,8'h38, //6 +8'h00,8'h00,8'h18,8'h3c,8'h3c,8'h18,8'h00,8'h00, //7 +8'hff,8'hff,8'he7,8'hc3,8'hc3,8'he7,8'hff,8'hff, //8 +8'h00,8'h3c,8'h66,8'h42,8'h42,8'h66,8'h3c,8'h00, //9 +8'hff,8'hc3,8'h99,8'hbd,8'hbd,8'h99,8'hc3,8'hff, //10 +8'h0f,8'h07,8'h0f,8'h7d,8'hcc,8'hcc,8'hcc,8'h78, //11 +8'h3c,8'h66,8'h66,8'h66,8'h3c,8'h18,8'h7e,8'h18, //12 +8'h3f,8'h33,8'h3f,8'h30,8'h30,8'h70,8'hf0,8'he0, //13 +8'h7f,8'h63,8'h7f,8'h63,8'h63,8'h67,8'he6,8'hc0, //14 +8'h99,8'h5a,8'h3c,8'he7,8'he7,8'h3c,8'h5a,8'h99, //15 +8'h80,8'he0,8'hf8,8'hfe,8'hf8,8'he0,8'h80,8'h00, //16 +8'h02,8'h0e,8'h3e,8'hfe,8'h3e,8'h0e,8'h02,8'h00, //17 +8'h18,8'h3c,8'h7e,8'h18,8'h18,8'h7e,8'h3c,8'h18, //18 +8'h66,8'h66,8'h66,8'h66,8'h66,8'h00,8'h66,8'h00, //19 +8'h7f,8'hdb,8'hdb,8'h7b,8'h1b,8'h1b,8'h1b,8'h00, //20 +8'h7e,8'hc3,8'h78,8'hcc,8'hcc,8'h78,8'h8c,8'hf8, //21 +8'h00,8'h00,8'h00,8'h00,8'h7e,8'h7e,8'h7e,8'h00, //22 +8'h18,8'h3c,8'h7e,8'h18,8'h7e,8'h3c,8'h18,8'hff, //23 +8'h18,8'h3c,8'h7e,8'h18,8'h18,8'h18,8'h18,8'h00, //24 +8'h18,8'h18,8'h18,8'h18,8'h7e,8'h3c,8'h18,8'h00, //25 +8'h00,8'h18,8'h0c,8'hfe,8'h0c,8'h18,8'h00,8'h00, //26 +8'h00,8'h30,8'h60,8'hfe,8'h60,8'h30,8'h00,8'h00, //27 +8'h00,8'h00,8'hc0,8'hc0,8'hc0,8'hfe,8'h00,8'h00, //28 +8'h00,8'h24,8'h66,8'hff,8'h66,8'h24,8'h00,8'h00, //29 +8'h00,8'h18,8'h3c,8'h7e,8'hff,8'hff,8'h00,8'h00, //30 +8'h00,8'hff,8'hff,8'h7e,8'h3c,8'h18,8'h00,8'h00, //31 +8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00, //32 +8'h30,8'h78,8'h78,8'h30,8'h30,8'h00,8'h30,8'h00, //33 +8'h6c,8'h6c,8'h6c,8'h00,8'h00,8'h00,8'h00,8'h00, //34 +8'h6c,8'h6c,8'hfe,8'h6c,8'hfe,8'h6c,8'h6c,8'h00, //35 +8'h30,8'h7c,8'hc0,8'h78,8'h0c,8'hf8,8'h30,8'h00, //36 +8'h00,8'hc6,8'hcc,8'h18,8'h30,8'h66,8'hc6,8'h00, //37 +8'h38,8'h6c,8'h38,8'h76,8'hdc,8'hcc,8'h76,8'h00, //38 +8'h60,8'h60,8'hc0,8'h00,8'h00,8'h00,8'h00,8'h00, //39 +8'h18,8'h30,8'h60,8'h60,8'h60,8'h30,8'h18,8'h00, //40 +8'h60,8'h30,8'h18,8'h18,8'h18,8'h30,8'h60,8'h00, //41 +8'h00,8'h66,8'h3c,8'hff,8'h3c,8'h66,8'h00,8'h00, //42 +8'h00,8'h30,8'h30,8'hfc,8'h30,8'h30,8'h00,8'h00, //43 +8'h00,8'h00,8'h00,8'h00,8'h00,8'h70,8'h30,8'h60, //44 +8'h00,8'h00,8'h00,8'hfc,8'h00,8'h00,8'h00,8'h00, //45 +8'h00,8'h00,8'h00,8'h00,8'h00,8'h30,8'h30,8'h00, //46 +8'h06,8'h0c,8'h18,8'h30,8'h60,8'hc0,8'h80,8'h00, //47 +8'h78,8'hcc,8'hdc,8'hfc,8'hec,8'hcc,8'h78,8'h00, //48 +8'h30,8'hf0,8'h30,8'h30,8'h30,8'h30,8'hfc,8'h00, //49 +8'h78,8'hcc,8'h0c,8'h38,8'h60,8'hcc,8'hfc,8'h00, //50 +8'h78,8'hcc,8'h0c,8'h38,8'h0c,8'hcc,8'h78,8'h00, //51 +8'h1c,8'h3c,8'h6c,8'hcc,8'hfe,8'h0c,8'h0c,8'h00, //52 +8'hfc,8'hc0,8'hf8,8'h0c,8'h0c,8'hcc,8'h78,8'h00, //53 +8'h38,8'h60,8'hc0,8'hf8,8'hcc,8'hcc,8'h78,8'h00, //54 +8'hfc,8'hcc,8'h0c,8'h18,8'h30,8'h60,8'h60,8'h00, //55 +8'h78,8'hcc,8'hcc,8'h78,8'hcc,8'hcc,8'h78,8'h00, //56 +8'h78,8'hcc,8'hcc,8'h7c,8'h0c,8'h18,8'h70,8'h00, //57 +8'h00,8'h00,8'h30,8'h30,8'h00,8'h30,8'h30,8'h00, //58 +8'h00,8'h00,8'h30,8'h30,8'h00,8'h70,8'h30,8'h60, //59 +8'h18,8'h30,8'h60,8'hc0,8'h60,8'h30,8'h18,8'h00, //60 +8'h00,8'h00,8'hfc,8'h00,8'hfc,8'h00,8'h00,8'h00, //61 +8'h60,8'h30,8'h18,8'h0c,8'h18,8'h30,8'h60,8'h00, //62 +8'h78,8'hcc,8'h0c,8'h18,8'h30,8'h00,8'h30,8'h00, //63 +8'h7c,8'hc6,8'hde,8'hde,8'hde,8'hc0,8'h78,8'h00, //64 +8'h30,8'h78,8'hcc,8'hcc,8'hfc,8'hcc,8'hcc,8'h00, //65 +8'hfc,8'h66,8'h66,8'h7c,8'h66,8'h66,8'hfc,8'h00, //66 +8'h3c,8'h66,8'hc0,8'hc0,8'hc0,8'h66,8'h3c,8'h00, //67 +8'hfc,8'h6c,8'h66,8'h66,8'h66,8'h6c,8'hfc,8'h00, //68 +8'hfe,8'h62,8'h68,8'h78,8'h68,8'h62,8'hfe,8'h00, //69 +8'hfe,8'h62,8'h68,8'h78,8'h68,8'h60,8'hf0,8'h00, //70 +8'h3c,8'h66,8'hc0,8'hc0,8'hce,8'h66,8'h3e,8'h00, //71 +8'hcc,8'hcc,8'hcc,8'hfc,8'hcc,8'hcc,8'hcc,8'h00, //72 +8'h78,8'h30,8'h30,8'h30,8'h30,8'h30,8'h78,8'h00, //73 +8'h1e,8'h0c,8'h0c,8'h0c,8'hcc,8'hcc,8'h78,8'h00, //74 +8'he6,8'h66,8'h6c,8'h78,8'h6c,8'h66,8'he6,8'h00, //75 +8'hf0,8'h60,8'h60,8'h60,8'h62,8'h66,8'hfe,8'h00, //76 +8'hc6,8'hee,8'hfe,8'hd6,8'hc6,8'hc6,8'hc6,8'h00, //77 +8'hc6,8'he6,8'hf6,8'hde,8'hce,8'hc6,8'hc6,8'h00, //78 +8'h38,8'h6c,8'hc6,8'hc6,8'hc6,8'h6c,8'h38,8'h00, //79 +8'hfc,8'h66,8'h66,8'h7c,8'h60,8'h60,8'hf0,8'h00, //80 +8'h78,8'hcc,8'hcc,8'hcc,8'hdc,8'h78,8'h1c,8'h00, //81 +8'hfc,8'h66,8'h66,8'h7c,8'h78,8'h6c,8'he6,8'h00, //82 +8'h78,8'hcc,8'he0,8'h38,8'h1c,8'hcc,8'h78,8'h00, //83 +8'hfc,8'hb4,8'h30,8'h30,8'h30,8'h30,8'h78,8'h00, //84 +8'hcc,8'hcc,8'hcc,8'hcc,8'hcc,8'hcc,8'hfc,8'h00, //85 +8'hcc,8'hcc,8'hcc,8'hcc,8'hcc,8'h78,8'h30,8'h00, //86 +8'hc6,8'hc6,8'hc6,8'hd6,8'hfe,8'hee,8'hc6,8'h00, //87 +8'hc6,8'hc6,8'h6c,8'h38,8'h6c,8'hc6,8'hc6,8'h00, //88 +8'hcc,8'hcc,8'hcc,8'h78,8'h30,8'h30,8'h78,8'h00, //89 +8'hfe,8'hcc,8'h98,8'h30,8'h62,8'hc6,8'hfe,8'h00, //90 +8'h78,8'h60,8'h60,8'h60,8'h60,8'h60,8'h78,8'h00, //91 +8'hc0,8'h60,8'h30,8'h18,8'h0c,8'h06,8'h02,8'h00, //92 +8'h78,8'h18,8'h18,8'h18,8'h18,8'h18,8'h78,8'h00, //93 +8'h10,8'h38,8'h6c,8'hc6,8'h00,8'h00,8'h00,8'h00, //94 +8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'hff, //95 +8'h30,8'h30,8'h18,8'h00,8'h00,8'h00,8'h00,8'h00, //96 +8'h00,8'h00,8'h78,8'h0c,8'h7c,8'hcc,8'h76,8'h00, //97 +8'he0,8'h60,8'h7c,8'h66,8'h66,8'h66,8'hbc,8'h00, //98 +8'h00,8'h00,8'h78,8'hcc,8'hc0,8'hcc,8'h78,8'h00, //99 +8'h1c,8'h0c,8'h0c,8'h7c,8'hcc,8'hcc,8'h76,8'h00, //100 +8'h00,8'h00,8'h78,8'hcc,8'hfc,8'hc0,8'h78,8'h00, //101 +8'h38,8'h6c,8'h60,8'hf0,8'h60,8'h60,8'hf0,8'h00, //102 +8'h00,8'h00,8'h76,8'hcc,8'hcc,8'h7c,8'h0c,8'hf8, //103 +8'he0,8'h60,8'h6c,8'h76,8'h66,8'h66,8'he6,8'h00, //104 +8'h30,8'h00,8'h70,8'h30,8'h30,8'h30,8'h78,8'h00, //105 +8'h18,8'h00,8'h78,8'h18,8'h18,8'h18,8'hd8,8'h70, //106 +8'he0,8'h60,8'h66,8'h6c,8'h78,8'h6c,8'he6,8'h00, //107 +8'h70,8'h30,8'h30,8'h30,8'h30,8'h30,8'h78,8'h00, //108 +8'h00,8'h00,8'hec,8'hfe,8'hd6,8'hc6,8'hc6,8'h00, //109 +8'h00,8'h00,8'hf8,8'hcc,8'hcc,8'hcc,8'hcc,8'h00, //110 +8'h00,8'h00,8'h78,8'hcc,8'hcc,8'hcc,8'h78,8'h00, //111 +8'h00,8'h00,8'hdc,8'h66,8'h66,8'h7c,8'h60,8'hf0, //112 +8'h00,8'h00,8'h76,8'hcc,8'hcc,8'h7c,8'h0c,8'h1e, //113 +8'h00,8'h00,8'hd8,8'h6c,8'h6c,8'h60,8'hf0,8'h00, //114 +8'h00,8'h00,8'h7c,8'hc0,8'h78,8'h0c,8'hf8,8'h00, //115 +8'h10,8'h30,8'h7c,8'h30,8'h30,8'h34,8'h18,8'h00, //116 +8'h00,8'h00,8'hcc,8'hcc,8'hcc,8'hcc,8'h76,8'h00, //117 +8'h00,8'h00,8'hcc,8'hcc,8'hcc,8'h78,8'h30,8'h00, //118 +8'h00,8'h00,8'hc6,8'hc6,8'hd6,8'hfe,8'h6c,8'h00, //119 +8'h00,8'h00,8'hc6,8'h6c,8'h38,8'h6c,8'hc6,8'h00, //120 +8'h00,8'h00,8'hcc,8'hcc,8'hcc,8'h7c,8'h0c,8'hf8, //121 +8'h00,8'h00,8'hfc,8'h98,8'h30,8'h64,8'hfc,8'h00, //122 +8'h1c,8'h30,8'h30,8'he0,8'h30,8'h30,8'h1c,8'h00, //123 +8'h18,8'h18,8'h18,8'h00,8'h18,8'h18,8'h18,8'h00, //124 +8'he0,8'h30,8'h30,8'h1c,8'h30,8'h30,8'he0,8'h00, //125 +8'h76,8'hdc,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00, //126 +8'h10,8'h38,8'h6c,8'hc6,8'hc6,8'hc6,8'hfe,8'h00, //127 +8'h78,8'hcc,8'hc0,8'hcc,8'h78,8'h18,8'h0c,8'h78, //128 +8'h00,8'hcc,8'h00,8'hcc,8'hcc,8'hcc,8'h7e,8'h00, //129 +8'h1c,8'h00,8'h78,8'hcc,8'hfc,8'hc0,8'h78,8'h00, //130 +8'h7e,8'hc3,8'h3c,8'h06,8'h3e,8'h66,8'h3f,8'h00, //131 +8'hcc,8'h00,8'h78,8'h0c,8'h7c,8'hcc,8'h7e,8'h00, //132 +8'he0,8'h00,8'h78,8'h0c,8'h7c,8'hcc,8'h7e,8'h00, //133 +8'h30,8'h30,8'h78,8'h0c,8'h7c,8'hcc,8'h7e,8'h00, //134 +8'h00,8'h00,8'h7c,8'hc0,8'hc0,8'h7c,8'h06,8'h3c, //135 +8'h7e,8'hc3,8'h3c,8'h66,8'h7e,8'h60,8'h3c,8'h00, //136 +8'hcc,8'h00,8'h78,8'hcc,8'hfc,8'hc0,8'h78,8'h00, //137 +8'he0,8'h00,8'h78,8'hcc,8'hfc,8'hc0,8'h78,8'h00, //138 +8'hcc,8'h00,8'h70,8'h30,8'h30,8'h30,8'h78,8'h00, //139 +8'h7c,8'hc6,8'h38,8'h18,8'h18,8'h18,8'h3c,8'h00, //140 +8'he0,8'h00,8'h70,8'h30,8'h30,8'h30,8'h78,8'h00, //141 +8'hcc,8'h30,8'h78,8'hcc,8'hcc,8'hfc,8'hcc,8'h00, //142 +8'h30,8'h30,8'h00,8'h78,8'hcc,8'hfc,8'hcc,8'h00, //143 +8'h1c,8'h00,8'hfc,8'h60,8'h78,8'h60,8'hfc,8'h00, //144 +8'h00,8'h00,8'h7f,8'h0c,8'h7f,8'hcc,8'h7f,8'h00, //145 +8'h3e,8'h6c,8'hcc,8'hfe,8'hcc,8'hcc,8'hce,8'h00, //146 +8'h78,8'hcc,8'h00,8'h78,8'hcc,8'hcc,8'h78,8'h00, //147 +8'h00,8'hcc,8'h00,8'h78,8'hcc,8'hcc,8'h78,8'h00, //148 +8'h00,8'he0,8'h00,8'h78,8'hcc,8'hcc,8'h78,8'h00, //149 +8'h78,8'hcc,8'h00,8'hcc,8'hcc,8'hcc,8'h7e,8'h00, //150 +8'h00,8'he0,8'h00,8'hcc,8'hcc,8'hcc,8'h7e,8'h00, //151 +8'h00,8'hcc,8'h00,8'hcc,8'hcc,8'hfc,8'h0c,8'hf8, //152 +8'hc6,8'h38,8'h7c,8'hc6,8'hc6,8'h7c,8'h38,8'h00, //153 +8'hcc,8'h00,8'hcc,8'hcc,8'hcc,8'hcc,8'h78,8'h00, //154 +8'h18,8'h18,8'h7e,8'hc0,8'hc0,8'h7e,8'h18,8'h18, //155 +8'h38,8'h6c,8'h64,8'hf0,8'h60,8'he6,8'hfc,8'h00, //156 +8'hcc,8'hcc,8'h78,8'hfc,8'h30,8'hfc,8'h30,8'h00, //157 +8'hf0,8'hd8,8'hd8,8'hf4,8'hcc,8'hde,8'hcc,8'h0e, //158 +8'h0e,8'h1b,8'h18,8'h7e,8'h18,8'h18,8'hd8,8'h70, //159 +8'h1c,8'h00,8'h78,8'h0c,8'h7c,8'hcc,8'h7e,8'h00, //160 +8'h38,8'h00,8'h70,8'h30,8'h30,8'h30,8'h78,8'h00, //161 +8'h00,8'h1c,8'h00,8'h78,8'hcc,8'hcc,8'h78,8'h00, //162 +8'h00,8'h1c,8'h00,8'hcc,8'hcc,8'hcc,8'h7e,8'h00, //163 +8'h00,8'hf8,8'h00,8'hf8,8'hcc,8'hcc,8'hcc,8'h00, //164 +8'hfc,8'h00,8'hcc,8'hec,8'hfc,8'hdc,8'hcc,8'h00, //165 +8'h3c,8'h6c,8'h6c,8'h3e,8'h00,8'h7e,8'h00,8'h00, //166 +8'h3c,8'h66,8'h66,8'h3c,8'h00,8'h7e,8'h00,8'h00, //167 +8'h30,8'h00,8'h30,8'h60,8'hc0,8'hcc,8'h78,8'h00, //168 +8'h00,8'h00,8'h00,8'hfc,8'hc0,8'hc0,8'h00,8'h00, //169 +8'h00,8'h00,8'h00,8'hfc,8'h0c,8'h0c,8'h00,8'h00, //170 +8'hc6,8'hcc,8'hd8,8'h3e,8'h63,8'hce,8'h98,8'h1f, //171 +8'hc6,8'hcc,8'hd8,8'hf3,8'h67,8'hcf,8'h9f,8'h03, //172 +8'h00,8'h18,8'h00,8'h18,8'h18,8'h3c,8'h3c,8'h18, //173 +8'h00,8'h33,8'h66,8'hcc,8'h66,8'h33,8'h00,8'h00, //174 +8'h00,8'hcc,8'h66,8'h33,8'h66,8'hcc,8'h00,8'h00, //175 +8'h22,8'h88,8'h22,8'h88,8'h22,8'h88,8'h22,8'h88, //176 +8'h55,8'haa,8'h55,8'haa,8'h55,8'haa,8'h55,8'haa, //177 +8'hdd,8'h77,8'hdd,8'h77,8'hdd,8'h77,8'hdd,8'h77, //178 +8'h18,8'h18,8'h18,8'h18,8'h18,8'h18,8'h18,8'h18, //179 +8'h18,8'h18,8'h18,8'h18,8'hf8,8'h18,8'h18,8'h18, //180 +8'h18,8'h18,8'hf8,8'h18,8'hf8,8'h18,8'h18,8'h18, //181 +8'h36,8'h36,8'h36,8'h36,8'hf6,8'h36,8'h36,8'h36, //182 +8'h00,8'h00,8'h00,8'h00,8'hfe,8'h36,8'h36,8'h36, //183 +8'h00,8'h00,8'hf8,8'h18,8'hf8,8'h18,8'h18,8'h18, //184 +8'h36,8'h36,8'hf6,8'h06,8'hf6,8'h36,8'h36,8'h36, //185 +8'h36,8'h36,8'h36,8'h36,8'h36,8'h36,8'h36,8'h36, //186 +8'h00,8'h00,8'hfe,8'h06,8'hf6,8'h36,8'h36,8'h36, //187 +8'h36,8'h36,8'hf6,8'h06,8'hfe,8'h00,8'h00,8'h00, //188 +8'h36,8'h36,8'h36,8'h36,8'hfe,8'h00,8'h00,8'h00, //189 +8'h18,8'h18,8'hf8,8'h18,8'hf8,8'h00,8'h00,8'h00, //190 +8'h00,8'h00,8'h00,8'h00,8'hf8,8'h18,8'h18,8'h18, //191 +8'h18,8'h18,8'h18,8'h18,8'h1f,8'h00,8'h00,8'h00, //192 +8'h18,8'h18,8'h18,8'h18,8'hff,8'h00,8'h00,8'h00, //193 +8'h00,8'h00,8'h00,8'h00,8'hff,8'h18,8'h18,8'h18, //194 +8'h18,8'h18,8'h18,8'h18,8'h1f,8'h18,8'h18,8'h18, //195 +8'h00,8'h00,8'h00,8'h00,8'hff,8'h00,8'h00,8'h00, //196 +8'h18,8'h18,8'h18,8'h18,8'hff,8'h18,8'h18,8'h18, //197 +8'h18,8'h18,8'h1f,8'h18,8'h1f,8'h18,8'h18,8'h18, //198 +8'h36,8'h36,8'h36,8'h36,8'h37,8'h36,8'h36,8'h36, //199 +8'h36,8'h36,8'h37,8'h30,8'h3f,8'h00,8'h00,8'h00, //200 +8'h00,8'h00,8'h3f,8'h30,8'h37,8'h36,8'h36,8'h36, //201 +8'h36,8'h36,8'hf7,8'h00,8'hff,8'h00,8'h00,8'h00, //202 +8'h00,8'h00,8'hff,8'h00,8'hf7,8'h36,8'h36,8'h36, //203 +8'h36,8'h36,8'h37,8'h30,8'h37,8'h36,8'h36,8'h36, //204 +8'h00,8'h00,8'hff,8'h00,8'hff,8'h00,8'h00,8'h00, //205 +8'h36,8'h36,8'hf7,8'h00,8'hf7,8'h36,8'h36,8'h36, //206 +8'h18,8'h18,8'hff,8'h00,8'hff,8'h00,8'h00,8'h00, //207 +8'h36,8'h36,8'h36,8'h36,8'hff,8'h00,8'h00,8'h00, //208 +8'h00,8'h00,8'hff,8'h00,8'hff,8'h18,8'h18,8'h18, //209 +8'h00,8'h00,8'h00,8'h00,8'hff,8'h36,8'h36,8'h36, //210 +8'h36,8'h36,8'h36,8'h36,8'h3f,8'h00,8'h00,8'h00, //211 +8'h18,8'h18,8'h1f,8'h18,8'h1f,8'h00,8'h00,8'h00, //212 +8'h00,8'h00,8'h1f,8'h18,8'h1f,8'h18,8'h18,8'h18, //213 +8'h00,8'h00,8'h00,8'h00,8'h3f,8'h36,8'h36,8'h36, //214 +8'h36,8'h36,8'h36,8'h36,8'hf7,8'h36,8'h36,8'h36, //215 +8'h18,8'h18,8'hff,8'h00,8'hff,8'h18,8'h18,8'h18, //216 +8'h18,8'h18,8'h18,8'h18,8'hf8,8'h00,8'h00,8'h00, //217 +8'h00,8'h00,8'h00,8'h00,8'h1f,8'h18,8'h18,8'h18, //218 +8'hff,8'hff,8'hff,8'hff,8'hff,8'hff,8'hff,8'hff, //219 +8'h00,8'h00,8'h00,8'h00,8'hff,8'hff,8'hff,8'hff, //220 +8'hf0,8'hf0,8'hf0,8'hf0,8'hf0,8'hf0,8'hf0,8'hf0, //221 +8'h0f,8'h0f,8'h0f,8'h0f,8'h0f,8'h0f,8'h0f,8'h0f, //222 +8'hff,8'hff,8'hff,8'hff,8'h00,8'h00,8'h00,8'h00, //223 +8'h00,8'h00,8'h76,8'hdc,8'hc8,8'hdc,8'h76,8'h00, //224 +8'h00,8'h78,8'hcc,8'hf8,8'hcc,8'hf8,8'hc0,8'hc0, //225 +8'h00,8'hfe,8'hc6,8'hc0,8'hc0,8'hc0,8'hc0,8'h00, //226 +8'h00,8'hfe,8'h6c,8'h6c,8'h6c,8'h6c,8'h6c,8'h00, //227 +8'hfe,8'h66,8'h30,8'h18,8'h30,8'h66,8'hfe,8'h00, //228 +8'h00,8'h00,8'h7e,8'hcc,8'hcc,8'hcc,8'h78,8'h00, //229 +8'h00,8'h66,8'h66,8'h66,8'h66,8'h7c,8'h60,8'hc0, //230 +8'h00,8'h76,8'hdc,8'h18,8'h18,8'h18,8'h18,8'h00, //231 +8'hfc,8'h30,8'h78,8'hcc,8'hcc,8'h78,8'h30,8'hfc, //232 +8'h38,8'h6c,8'hc6,8'hfe,8'hc6,8'h6c,8'h38,8'h00, //233 +8'h38,8'h6c,8'hc6,8'hc6,8'h6c,8'h6c,8'hee,8'h00, //234 +8'h1c,8'h30,8'h18,8'h7c,8'hcc,8'hcc,8'h78,8'h00, //235 +8'h00,8'h00,8'h7e,8'hdb,8'hdb,8'h7e,8'h00,8'h00, //236 +8'h06,8'h0c,8'h7e,8'hdb,8'hdb,8'h7e,8'h60,8'hc0, //237 +8'h3c,8'h60,8'hc0,8'hfc,8'hc0,8'h60,8'h3c,8'h00, //238 +8'h78,8'hcc,8'hcc,8'hcc,8'hcc,8'hcc,8'hcc,8'h00, //239 +8'h00,8'hfc,8'h00,8'hfc,8'h00,8'hfc,8'h00,8'h00, //240 +8'h30,8'h30,8'hfc,8'h30,8'h30,8'h00,8'hfc,8'h00, //241 +8'h60,8'h30,8'h18,8'h30,8'h60,8'h00,8'hfc,8'h00, //242 +8'h18,8'h30,8'h60,8'h30,8'h18,8'h00,8'hfc,8'h00, //243 +8'h0e,8'h1b,8'h1b,8'h18,8'h18,8'h18,8'h18,8'h18, //244 +8'h18,8'h18,8'h18,8'h18,8'h18,8'hd8,8'hd8,8'h70, //245 +8'h30,8'h30,8'h00,8'hfc,8'h00,8'h30,8'h30,8'h00, //246 +8'h00,8'h72,8'h9c,8'h00,8'h72,8'h9c,8'h00,8'h00, //247 +8'h38,8'h6c,8'h6c,8'h38,8'h00,8'h00,8'h00,8'h00, //248 +8'h00,8'h00,8'h00,8'h18,8'h18,8'h00,8'h00,8'h00, //249 +8'h00,8'h00,8'h00,8'h00,8'h18,8'h00,8'h00,8'h00, //250 +8'h0f,8'h0c,8'h0c,8'h0c,8'hec,8'h6c,8'h3c,8'h1c, //251 +8'h78,8'h6c,8'h6c,8'h6c,8'h6c,8'h00,8'h00,8'h00, //252 +8'h78,8'h0c,8'h38,8'h60,8'h7c,8'h00,8'h00,8'h00, //253 +8'h00,8'h00,8'h3c,8'h3c,8'h3c,8'h3c,8'h00,8'h00, //254 +8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00 //255 + }; + end +endmodule + +`endif diff --git a/hvsync_generator.v b/hvsync_generator.v new file mode 100644 index 0000000..2458761 --- /dev/null +++ b/hvsync_generator.v @@ -0,0 +1,69 @@ + +`ifndef HVSYNC_GENERATOR_H +`define HVSYNC_GENERATOR_H + +/* +Video sync generator, used to drive a simulated CRT. +To use: +- Wire the hsync and vsync signals to top level outputs +- Add a 3-bit (or more) "rgb" output to the top level +*/ + +module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos); + + input clk; + input reset; + output reg hsync, vsync; + output display_on; + output reg [8:0] hpos; + output reg [8:0] vpos; + + // declarations for TV-simulator sync parameters + // horizontal constants + parameter H_DISPLAY = 256; // horizontal display width + parameter H_BACK = 23; // horizontal left border (back porch) + parameter H_FRONT = 7; // horizontal right border (front porch) + parameter H_SYNC = 23; // horizontal sync width + // vertical constants + parameter V_DISPLAY = 240; // vertical display height + parameter V_TOP = 5; // vertical top border + parameter V_BOTTOM = 14; // vertical bottom border + parameter V_SYNC = 3; // vertical sync # lines + // derived constants + parameter H_SYNC_START = H_DISPLAY + H_FRONT; + parameter H_SYNC_END = H_DISPLAY + H_FRONT + H_SYNC - 1; + parameter H_MAX = H_DISPLAY + H_BACK + H_FRONT + H_SYNC - 1; + parameter V_SYNC_START = V_DISPLAY + V_BOTTOM; + parameter V_SYNC_END = V_DISPLAY + V_BOTTOM + V_SYNC - 1; + parameter V_MAX = V_DISPLAY + V_TOP + V_BOTTOM + V_SYNC - 1; + + wire hmaxxed = (hpos == H_MAX) || reset; // set when hpos is maximum + wire vmaxxed = (vpos == V_MAX) || reset; // set when vpos is maximum + + // horizontal position counter + always @(posedge clk) + begin + hsync <= (hpos>=H_SYNC_START && hpos<=H_SYNC_END); + if(hmaxxed) + hpos <= 0; + else + hpos <= hpos + 1; + end + + // vertical position counter + always @(posedge clk) + begin + vsync <= (vpos>=V_SYNC_START && vpos<=V_SYNC_END); + if(hmaxxed) + if (vmaxxed) + vpos <= 0; + else + vpos <= vpos + 1; + end + + // display_on is set when beam is in "safe" visible frame + assign display_on = (hpos 0) begin + dshift[scroll] <= 0; // clear row when scrolling + scroll <= scroll + 1; + scnt <= scnt - 1; + end else if (te) begin + if (ti == 13) begin // CR, next row + scnt <= 32; + dofs <= ((dofs + 32) & ~31); + end else if (ti >= 32) begin // display char + dshift[dofs] <= ti; + if ((dofs & 31) == 31) scnt <= 32; // wrap around + dofs <= dofs + 1; + end + end + + // character generator from ROM + font_cp437_8x8 tile_rom( + .addr(char_addr), + .data(char_data) + ); + wire [9:0] nt_addr = {vpos[7:3], hpos[7:3]}; + wire [7:0] cur_char = dshift[nt_addr + scroll]; + wire [10:0] char_addr = {cur_char, vpos[2:0]}; + wire [7:0] char_data; + wire dot = char_data[~hpos[2:0]]; // video output + + // terminal ready output + // only possible at end of line, if not scrolling + assign tready = !reset && !te && scnt == 0 && hpos == 256; + + initial begin + integer i; + for (i=0; i<1024; i=i+1) dshift[i] = 0; // clear buffer + end + +endmodule + +module apple1_top(clk, reset, hsync, vsync, rgb, keycode, keystrobe); + + input clk, reset; + input [7:0] keycode; + output reg keystrobe; + output hsync, vsync; + output [2:0] rgb; + wire display_on; + wire [8:0] hpos; + wire [8:0] vpos; + + wire [15:0] AB; // address bus + wire [7:0] DI; // data in, read bus + wire [7:0] DO; // data out, write bus + wire WE; // write enable + wire IRQ=0; // interrupt request + wire NMI=0; // non-maskable interrupt request + wire RDY=1; // Ready signal. Pauses CPU when RDY=0 + + cpu6502 cpu( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY ); + + always @(posedge clk) + begin + casez (AB) + 16'h0zzz: DI <= ram[AB[11:0]]; + 16'hd010: begin + if (keycode >= 97+128 && keycode <= 122+128) + DI <= keycode - 32; // convert to uppercase + else + DI <= keycode; // keyboard data + keystrobe <= (keycode & 8'h80) != 0; // clear kbd buffer + end + 16'hd011: begin + DI <= keycode & 8'h80; // keyboard status + keystrobe <= 0; + end + 16'hd012: begin + DI <= {!tready, 7'b0}; // display status + end + 16'hffzz: DI <= monitor_rom[AB[7:0]]; + endcase + end + + always @(posedge clk) + if (WE) begin + casez (AB) + 16'hd010: begin end // + 16'hd011: begin end // + 16'hd012: begin end // handled by terminal module + 16'hd013: begin end // + 16'h0zzz: ram[AB[11:0]] <= DO; // write RAM + endcase + end + + reg [7:0] ram[4096]; // 1K of RAM + reg [7:0] monitor_rom[256]; // WozMon ROM + + initial begin + $readmemh("mango1.hex", monitor_rom); + end + + hvsync_generator hvsync_gen( + .clk(clk), + .reset(reset), + .hsync(hsync), + .vsync(vsync), + .display_on(display_on), + .hpos(hpos), + .vpos(vpos) + ); + + wire tready; // terminal ready + wire dot; // dot output + wire te = WE && AB == 16'hd012; // terminal enable (write) + signetics_term terminal(clk, reset, hpos, vpos, + tready, dot, + te, .ti(DO & 8'h7f)); + + wire r = display_on && 0; + wire g = display_on && dot; + wire b = display_on && 0; + assign rgb = {b,g,r}; + +endmodule