Ugly SDL based emulator, key support, still buggy
parent
cd959b80f7
commit
3854e3a2ff
8
Makefile
8
Makefile
|
@ -1,11 +1,11 @@
|
|||
obj_dir/Vcpu.cpp: cpu.v test_main.cpp
|
||||
verilator -Wall -cc cpu.v --exe test_main.cpp
|
||||
verilator -Wall -cc cpu.v --exe emu.cpp
|
||||
|
||||
obj_dir/Vcpu: obj_dir/Vcpu.cpp test_main.cpp
|
||||
cd obj_dir; make -j -f Vcpu.mk Vcpu
|
||||
obj_dir/Vcpu: obj_dir/Vcpu.cpp emu.cpp
|
||||
cd obj_dir; LDFLAGS=-lSDL make -j -f Vcpu.mk Vcpu
|
||||
|
||||
test: obj_dir/Vcpu
|
||||
obj_dir/Vcpu
|
||||
obj_dir/Vcpu si.ch8
|
||||
|
||||
clean:
|
||||
rm -rf obj_dir
|
||||
|
|
183
cpu.v
183
cpu.v
|
@ -9,6 +9,7 @@
|
|||
module cpu (
|
||||
input reset,
|
||||
input cpu_clock,
|
||||
input hertz_clock,
|
||||
input [15:0] keypad
|
||||
);
|
||||
|
||||
|
@ -17,7 +18,9 @@ module cpu (
|
|||
// 16 8-bit V registers
|
||||
reg [7:0] vr[0:15]/* verilator public */;
|
||||
// 16-bit I register
|
||||
reg [15:0] i;
|
||||
reg [15:0] i/* verilator public */;
|
||||
// 12-bit memory-indexing version of I
|
||||
wire [11:0] ii = i[11:0];
|
||||
// 12-bit program counter
|
||||
reg [11:0] pc/* verilator public */;
|
||||
// 16-element, 12-bit stack
|
||||
|
@ -25,14 +28,20 @@ module cpu (
|
|||
// 4-bit stack pointer
|
||||
reg [3:0] sp;
|
||||
|
||||
// 8-bit DT (delay timer)
|
||||
reg [7:0] dt/* verilator public */;
|
||||
// 8-bit ST (sound timer)
|
||||
reg [7:0] st;
|
||||
|
||||
// internal cycle counter (0 - fetch)
|
||||
`define FETCH 0
|
||||
`define EXECUTE 1
|
||||
`define WRITEBACK 2
|
||||
reg [1:0] c;
|
||||
`define WAIT_KEY 3
|
||||
reg [2:0] c/* verilator public */;
|
||||
|
||||
// Instruction register
|
||||
reg [15:0] ins;
|
||||
reg [15:0] ins/* verilator public */;
|
||||
wire [3:0] family = ins[15:12];
|
||||
|
||||
// ALU operation
|
||||
|
@ -58,6 +67,8 @@ module cpu (
|
|||
sp <= 0;
|
||||
should_jmp <= 0;
|
||||
c <= 0;
|
||||
dt <= 0;
|
||||
dt <= 0;
|
||||
|
||||
vr[0] <= 0;
|
||||
vr[1] <= 0;
|
||||
|
@ -75,14 +86,37 @@ module cpu (
|
|||
vr[13] <= 0;
|
||||
vr[14] <= 0;
|
||||
vr[15] <= 0;
|
||||
|
||||
end else begin
|
||||
case(c)
|
||||
`FETCH: begin
|
||||
ins <= {ram[pc], ram[pc + 1]};
|
||||
c <= 1;
|
||||
|
||||
if (hertz_clock == 1) begin
|
||||
if (dt > 0)
|
||||
dt <= dt - 1;
|
||||
if (st > 0)
|
||||
st <= st - 1;
|
||||
end
|
||||
end
|
||||
`EXECUTE: begin
|
||||
// $display("TRACE %h @ %h", ins, pc);
|
||||
case (family)
|
||||
// Old native instructions - we implement them in
|
||||
// hardware
|
||||
0: begin
|
||||
if (ins == 16'h00E0) begin
|
||||
// clear screen
|
||||
$display("should clear, don't care.");
|
||||
end else if (ins == 16'h00EE) begin
|
||||
// return from subroutine
|
||||
jmp <= stack[sp-1];
|
||||
sp <= sp - 1;
|
||||
should_jmp <= 1;
|
||||
end else
|
||||
$display("Unknown opcode: %h @ %h", ins, pc);
|
||||
end
|
||||
// Jump to address
|
||||
1: begin
|
||||
should_jmp <= 1;
|
||||
|
@ -120,6 +154,8 @@ module cpu (
|
|||
6: vr[x] <= kk;
|
||||
// Vx += kk
|
||||
7: vr[x] <= vr[x] + kk;
|
||||
// These are implemented in the ALU
|
||||
8: begin end
|
||||
// Skip if Vx != Vy (9xy0)
|
||||
9: begin
|
||||
if (vr[x] != vr[y]) begin
|
||||
|
@ -136,7 +172,7 @@ module cpu (
|
|||
end
|
||||
// Set Vx to random and kk
|
||||
// TODO: undo fair dice roll
|
||||
12: vr[x] <= 42 & kk;
|
||||
12: begin vr[x] <= 42 & kk; $display("STUB: random"); end
|
||||
// TODO: draw sprites
|
||||
13: begin end
|
||||
// Key functions
|
||||
|
@ -151,10 +187,73 @@ module cpu (
|
|||
should_jmp <= 1;
|
||||
jmp <= pc + 4;
|
||||
end
|
||||
end
|
||||
end else $display("Unknown opcode: %h @ %h", ins, pc);
|
||||
end
|
||||
// Fxxx instructions (mostly timer related)
|
||||
15: begin
|
||||
case (kk)
|
||||
// load DT into Vx
|
||||
8'h07: vr[x] <= dt;
|
||||
// load Vx into DT
|
||||
8'h15: dt <= vr[x];
|
||||
// load Vx into ST
|
||||
8'h18: st <= vr[x];
|
||||
// add Vx to I
|
||||
8'h1E: begin i <= i + {8'b0, vr[x]}; $display("i, %h, vx %h", i, vr[x]); end
|
||||
// move Vx digit memory location to I
|
||||
8'h29: i <= 5 * vr[x];
|
||||
// store BCD rep of Vx into [I], [I+1], [I+2]
|
||||
8'h33: begin
|
||||
ram[ii+2] <= vr[x] / 100;
|
||||
ram[ii+1] <= (vr[x] / 10) % 10;
|
||||
ram[ii] <= vr[x] % 10;
|
||||
end
|
||||
// store V0 to Vx to [I], [I+1], ..., [I+x]
|
||||
8'h55: begin
|
||||
ram[ii] <= vr[0];
|
||||
if (x > 0) ram[ii+1] <= vr[1];
|
||||
if (x > 1) ram[ii+2] <= vr[2];
|
||||
if (x > 2) ram[ii+3] <= vr[3];
|
||||
if (x > 3) ram[ii+4] <= vr[4];
|
||||
if (x > 4) ram[ii+5] <= vr[5];
|
||||
if (x > 5) ram[ii+6] <= vr[6];
|
||||
if (x > 6) ram[ii+7] <= vr[7];
|
||||
if (x > 7) ram[ii+8] <= vr[8];
|
||||
if (x > 8) ram[ii+9] <= vr[9];
|
||||
if (x > 9) ram[ii+10] <= vr[10];
|
||||
if (x > 10) ram[ii+11] <= vr[11];
|
||||
if (x > 11) ram[ii+12] <= vr[12];
|
||||
if (x > 12) ram[ii+13] <= vr[13];
|
||||
if (x > 13) ram[ii+14] <= vr[14];
|
||||
if (x > 14) ram[ii+15] <= vr[15];
|
||||
end
|
||||
// read [I], [I+1], ..., [I+x] into V0 .. Vx
|
||||
8'h65: begin
|
||||
vr[0] <= ram[ii];
|
||||
if (x > 0) vr[1] <= ram[ii+1];
|
||||
if (x > 1) vr[1] <= ram[ii+2];
|
||||
if (x > 2) vr[1] <= ram[ii+3];
|
||||
if (x > 3) vr[1] <= ram[ii+4];
|
||||
if (x > 4) vr[1] <= ram[ii+5];
|
||||
if (x > 5) vr[1] <= ram[ii+6];
|
||||
if (x > 6) vr[1] <= ram[ii+7];
|
||||
if (x > 7) vr[1] <= ram[ii+8];
|
||||
if (x > 8) vr[1] <= ram[ii+9];
|
||||
if (x > 9) vr[1] <= ram[ii+10];
|
||||
if (x > 10) vr[1] <= ram[ii+11];
|
||||
if (x > 11) vr[1] <= ram[ii+12];
|
||||
if (x > 12) vr[1] <= ram[ii+13];
|
||||
if (x > 13) vr[1] <= ram[ii+14];
|
||||
if (x > 14) vr[1] <= ram[ii+15];
|
||||
end
|
||||
default: $display("Unknown opcode: %h @ %h", ins, pc);
|
||||
endcase
|
||||
end
|
||||
endcase
|
||||
c <= 2;
|
||||
if (family == 4'hF && kk == 8'h0A)
|
||||
c <= `WAIT_KEY;
|
||||
else
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
`WRITEBACK: begin
|
||||
if (should_jmp) begin
|
||||
|
@ -165,6 +264,77 @@ module cpu (
|
|||
should_jmp <= 0;
|
||||
c <= 0;
|
||||
end
|
||||
`WAIT_KEY: begin
|
||||
$display("waiting for key");
|
||||
if (keypad != 0) begin
|
||||
case (keypad)
|
||||
16'h1: begin
|
||||
vr[x] <= 0;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h2: begin
|
||||
vr[x] <= 1;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h4: begin
|
||||
vr[x] <= 2;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h8: begin
|
||||
vr[x] <= 3;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h10: begin
|
||||
vr[x] <= 4;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h20: begin
|
||||
vr[x] <= 5;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h40: begin
|
||||
vr[x] <= 6;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h80: begin
|
||||
vr[x] <= 7;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h100: begin
|
||||
vr[x] <= 8;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h200: begin
|
||||
vr[x] <= 9;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h400: begin
|
||||
vr[x] <= 10;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h800: begin
|
||||
vr[x] <= 11;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h1000: begin
|
||||
vr[x] <= 12;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h2000: begin
|
||||
vr[x] <= 13;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h4000: begin
|
||||
vr[x] <= 14;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
16'h8000: begin
|
||||
vr[x] <= 15;
|
||||
c <= `WRITEBACK;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
@ -195,6 +365,7 @@ module cpu (
|
|||
vr[15] <= {7'b0, vr[x][7]};
|
||||
vr[x] <= vr[x] << 1;
|
||||
end
|
||||
default: $display("Unknown opcode: %h @ %h", ins, pc);
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
#include "Vcpu.h"
|
||||
#include "Vcpu_cpu.h"
|
||||
#include "verilated.h"
|
||||
|
||||
#include "SDL/SDL.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
SDL_Surface *screen;
|
||||
Vcpu *top;
|
||||
|
||||
void putpixel(SDL_Surface *surface, uint16_t x, uint16_t y, uint32_t value)
|
||||
{
|
||||
uint8_t *pixel = (uint8_t *)surface->pixels;
|
||||
pixel += (y * surface->pitch) + (x * sizeof(uint32_t));
|
||||
(*((uint32_t *)pixel)) = value;
|
||||
}
|
||||
|
||||
uint8_t collision[64*32];
|
||||
void putpixel_c8(SDL_Surface *surface, uint8_t x, uint8_t y, uint8_t value)
|
||||
{
|
||||
value = collision[x + y * 64] ^ value;
|
||||
if (collision[x + y * 64] == 0 && value == 1)
|
||||
top->v->vr[15] = 1;
|
||||
else
|
||||
top->v->vr[15] = 0;
|
||||
collision[x + y * 64] = value;
|
||||
for (uint32_t xx = x*10; xx < (x+1)*10; xx++)
|
||||
{
|
||||
for (uint32_t yy = y * 10; yy < (y+1)*10; yy++)
|
||||
{
|
||||
if (value > 0)
|
||||
putpixel(surface, xx, yy, 0xFFFFFF);
|
||||
else
|
||||
putpixel(surface, xx, yy, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump_regs(void)
|
||||
{
|
||||
printf("-> regs:\n");
|
||||
for (int i = 0; i < 8; i++)
|
||||
printf(" %i: 0x%02x\n", i, top->v->vr[i]);
|
||||
}
|
||||
|
||||
void step(void)
|
||||
{
|
||||
top->cpu_clock = 0;
|
||||
top->eval();
|
||||
top->cpu_clock = 1;
|
||||
top->eval();
|
||||
// emulate draw instructions :>
|
||||
if (top->v->c == 2)
|
||||
{
|
||||
uint16_t ins = top->v->ins;
|
||||
if (((ins >> 12) & 0xF) == 0xD)
|
||||
{
|
||||
uint8_t x = top->v->vr[(ins >> 8) & 0xF];
|
||||
uint8_t y = top->v->vr[(ins >> 4) & 0xF];
|
||||
uint8_t n = ins & 0xF;
|
||||
uint16_t i = top->v->i;
|
||||
printf("[i] Drawing %i bytes from ram[%i] at %i, %i.\n", n, i, x, y);
|
||||
for (uint8_t nn = 0; nn < n; nn++)
|
||||
{
|
||||
uint8_t byte = top->v->ram[i];
|
||||
i++;
|
||||
for (uint8_t bit = 0; bit < 8; bit++)
|
||||
{
|
||||
uint8_t pixel = (byte >> (7-bit)) & 1;
|
||||
putpixel_c8(screen, x+bit, y+nn, pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void step3(void)
|
||||
{
|
||||
do
|
||||
{
|
||||
step();
|
||||
} while (top->v->c != 0);
|
||||
}
|
||||
|
||||
void run_instruction(uint16_t instruction)
|
||||
{
|
||||
top->v->ram[0x200] = instruction >> 8;
|
||||
top->v->ram[0x201] = instruction & 0xFF;
|
||||
top->v->pc = 0x200;
|
||||
}
|
||||
|
||||
void reset(void)
|
||||
{
|
||||
top->hertz_clock = 0;
|
||||
top->reset = 0;
|
||||
step3(); step3();
|
||||
top->reset = 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
memset(collision, 0, 64*32);
|
||||
SDL_Init(SDL_INIT_EVERYTHING);
|
||||
screen = SDL_SetVideoMode( 640, 320, 32, SDL_SWSURFACE );
|
||||
|
||||
Verilated::commandArgs(argc, argv);
|
||||
top = new Vcpu;
|
||||
reset();
|
||||
|
||||
// load digits into memory starting from zero
|
||||
// 0
|
||||
top->v->ram[0] = 0xF0;
|
||||
top->v->ram[1] = 0x90;
|
||||
top->v->ram[2] = 0x90;
|
||||
top->v->ram[3] = 0x90;
|
||||
top->v->ram[4] = 0xF0;
|
||||
|
||||
// 1
|
||||
top->v->ram[5] = 0x20;
|
||||
top->v->ram[6] = 0x60;
|
||||
top->v->ram[7] = 0x20;
|
||||
top->v->ram[8] = 0x20;
|
||||
top->v->ram[9] = 0x70;
|
||||
|
||||
// 2
|
||||
top->v->ram[10] = 0xF0;
|
||||
top->v->ram[11] = 0x10;
|
||||
top->v->ram[12] = 0xF0;
|
||||
top->v->ram[13] = 0x80;
|
||||
top->v->ram[14] = 0xF0;
|
||||
|
||||
// 3
|
||||
top->v->ram[15] = 0xF0;
|
||||
top->v->ram[16] = 0x10;
|
||||
top->v->ram[17] = 0xF0;
|
||||
top->v->ram[18] = 0x10;
|
||||
top->v->ram[19] = 0xF0;
|
||||
|
||||
// 4
|
||||
top->v->ram[20] = 0x90;
|
||||
top->v->ram[21] = 0x90;
|
||||
top->v->ram[22] = 0xF0;
|
||||
top->v->ram[23] = 0x10;
|
||||
top->v->ram[24] = 0x10;
|
||||
|
||||
// 5
|
||||
top->v->ram[25] = 0xF0;
|
||||
top->v->ram[26] = 0x80;
|
||||
top->v->ram[27] = 0xF0;
|
||||
top->v->ram[28] = 0x10;
|
||||
top->v->ram[29] = 0xF0;
|
||||
|
||||
// 6
|
||||
top->v->ram[30] = 0xF0;
|
||||
top->v->ram[31] = 0x80;
|
||||
top->v->ram[32] = 0xF0;
|
||||
top->v->ram[33] = 0x90;
|
||||
top->v->ram[34] = 0xF0;
|
||||
|
||||
// 7
|
||||
top->v->ram[35] = 0xF0;
|
||||
top->v->ram[36] = 0x10;
|
||||
top->v->ram[37] = 0x20;
|
||||
top->v->ram[38] = 0x40;
|
||||
top->v->ram[39] = 0x40;
|
||||
|
||||
// 8
|
||||
top->v->ram[40] = 0xF0;
|
||||
top->v->ram[41] = 0x90;
|
||||
top->v->ram[42] = 0xF0;
|
||||
top->v->ram[43] = 0x90;
|
||||
top->v->ram[44] = 0xF0;
|
||||
|
||||
// 9
|
||||
top->v->ram[45] = 0xF0;
|
||||
top->v->ram[46] = 0x90;
|
||||
top->v->ram[47] = 0xF0;
|
||||
top->v->ram[48] = 0x10;
|
||||
top->v->ram[49] = 0xF0;
|
||||
|
||||
// a
|
||||
top->v->ram[50] = 0xF0;
|
||||
top->v->ram[51] = 0x90;
|
||||
top->v->ram[52] = 0xF0;
|
||||
top->v->ram[53] = 0x90;
|
||||
top->v->ram[54] = 0x90;
|
||||
|
||||
// b
|
||||
top->v->ram[55] = 0xE0;
|
||||
top->v->ram[56] = 0x90;
|
||||
top->v->ram[57] = 0xE0;
|
||||
top->v->ram[58] = 0x90;
|
||||
top->v->ram[59] = 0xE0;
|
||||
|
||||
// c
|
||||
top->v->ram[60] = 0xF0;
|
||||
top->v->ram[61] = 0x80;
|
||||
top->v->ram[62] = 0x80;
|
||||
top->v->ram[63] = 0x80;
|
||||
top->v->ram[64] = 0xF0;
|
||||
|
||||
// d
|
||||
top->v->ram[65] = 0xE0;
|
||||
top->v->ram[66] = 0x90;
|
||||
top->v->ram[67] = 0x90;
|
||||
top->v->ram[68] = 0x90;
|
||||
top->v->ram[69] = 0xE0;
|
||||
|
||||
// e
|
||||
top->v->ram[70] = 0xF0;
|
||||
top->v->ram[71] = 0x80;
|
||||
top->v->ram[72] = 0xF0;
|
||||
top->v->ram[73] = 0x80;
|
||||
top->v->ram[74] = 0xF0;
|
||||
|
||||
// f
|
||||
top->v->ram[75] = 0xF0;
|
||||
top->v->ram[76] = 0x80;
|
||||
top->v->ram[77] = 0xF0;
|
||||
top->v->ram[78] = 0x80;
|
||||
top->v->ram[79] = 0x80;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s romfile\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
FILE *f = fopen(argv[1], "r");
|
||||
fread(top->v->ram + 0x200, 1, 0xe00, f);
|
||||
fclose(f);
|
||||
|
||||
top->v->pc = 0x200;
|
||||
|
||||
int last_tick = 0;
|
||||
int ticks_per_sec = 0;
|
||||
while (true)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
int time = tv.tv_sec;
|
||||
|
||||
ticks_per_sec++;
|
||||
if (time != last_tick)
|
||||
{
|
||||
printf("[i] %i Hz emulation\n", ticks_per_sec);
|
||||
ticks_per_sec = 0;
|
||||
printf("[i] DT: %i\n", top->v->dt);
|
||||
top->hertz_clock = 1;
|
||||
}
|
||||
last_tick = time;
|
||||
step3();
|
||||
if (top->hertz_clock)
|
||||
top->hertz_clock = 0;
|
||||
SDL_Flip(screen);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue