From 3854e3a2ff1e7c7c16552111b15799df88ec62b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergiusz=20=27q3k=27=20Baza=C5=84ski?= Date: Thu, 20 Feb 2014 13:37:31 +0100 Subject: [PATCH] Ugly SDL based emulator, key support, still buggy --- Makefile | 8 +- cpu.v | 183 +++++++++++++++++++++++++++++++++++++-- emu.cpp | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ si.ch8 | Bin 0 -> 1301 bytes 4 files changed, 439 insertions(+), 10 deletions(-) create mode 100644 emu.cpp create mode 100644 si.ch8 diff --git a/Makefile b/Makefile index f9295d3..98e542d 100644 --- a/Makefile +++ b/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 diff --git a/cpu.v b/cpu.v index 97cfcbe..0339f7e 100644 --- a/cpu.v +++ b/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 diff --git a/emu.cpp b/emu.cpp new file mode 100644 index 0000000..4e90b44 --- /dev/null +++ b/emu.cpp @@ -0,0 +1,258 @@ +#include "Vcpu.h" +#include "Vcpu_cpu.h" +#include "verilated.h" + +#include "SDL/SDL.h" + +#include + +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); + } + +} diff --git a/si.ch8 b/si.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..3ada8dfe93a7d95925a9f52a1bf2cf385101bacd GIT binary patch literal 1301 zcmZWnQAk@?82)d1y_piamh`f#L!@1A(Y5IsX0;I_gJ9ar3bKc*%*)hU?J_db zHpya)flW574=cK`vDvNnWm{kNuoto79NUwy4is_bVa)mx!w{k+p1c1hX*=A&s%{wQRRi1nI9zSDv1 zJPLJiw$)+c()-6IejM1JczHl<1M;gJPYIVr@_U<${9*fTlx&tciEOvEkR3Vx2Doc) z3O)2`Rq?%YRUtWY*g(dLwdaA19X80Jse2cRyD{b%E?soEWt@3AL7PtV1uN2{^5bAUzCT&h3Dh;D&&*7BvD{v<+c;@^XuA) zH6HSE=u=q^X*C5})%ghW!|+LNgx~p!T>I8}=qU^;mivcc7~!vTqn!KhH+2OMYKU>} z7WaCs`!3{t3Co(DMbp%@ov?hq#Kw3RavLshi~)QI9XJ3ujyr|`=#Z-D5Jd}sK?w&y zqN{5F0f1Zu=vWYk&Uj5c4zRSe;x}5nN8?3TeX+>%05LyXLL80nN-d8cpE(0?F!?j$ z;AngSBt=?S04Va?lauMaqw#0;@3!nhP8<7Q{AvA*uxJCI#8**rxvYcnpQh&Kp1_lj z=jJ}2-T%9}d9S^_9bT+&KY#9(U$%OC-(FkWf~^-$lE#%uGJz_j>(EFpM&Xr;xN?2m}F*reTjZFfRof zTXCYrXT%-lj7$_oj4%ggWO!B(SbBMx@jOd1^z9a~JBLFMgmS+Om+1SMA3w7KqYs?a zRDua0R5#2%U2W1K5TL{qrcD9~g_vn27^Zq<`?MN015qPv5^98Xb=nNK{^xX9MFEQ0(9n^cvUT~Vjc{JvVlv!A|lx9~5`cIUbP literal 0 HcmV?d00001