Merge pull request #4 from spytheman/improvements
Fix compilation with latest V, separate updates from the display refresh rate, improve keyboard handling, run `v fmt -w .`, enable running on macos and windows.
This commit is contained in:
commit
0424fa56b1
290
src/chip8.v
290
src/chip8.v
|
|
@ -1,38 +1,26 @@
|
||||||
import rand
|
import rand
|
||||||
|
|
||||||
const keyboard := {
|
const keyboard = {
|
||||||
0x1 : 0x1 //1 - 1
|
0x31: 0x01 // keycode 1 - 1
|
||||||
0x2 : 0x2 //2 - 2
|
0x32: 0x02 // keycode 2 - 2
|
||||||
0x3 : 0x3 //3 - 3
|
0x33: 0x03 // keycode 3 - 3
|
||||||
0x4 : 0x43 //4 - C
|
0x34: 0x0C // keycode 4 - C
|
||||||
0x51 : 0x4 //Q - 4
|
0x51: 0x04 // keycode Q - 4
|
||||||
0x71 : 0x4 //q - 4
|
0x57: 0x05 // keycode W - 5
|
||||||
0x57 : 0x5 //W - 5
|
0x45: 0x06 // keycode E - 6
|
||||||
0x77 : 0x5 //w - 5
|
0x52: 0x0D // keycode R - D
|
||||||
0x45 : 0x6 //E - 6
|
0x41: 0x07 // keycode A - 7
|
||||||
0x65 : 0x6 //e - 6
|
0x53: 0x08 // keycode S - 8
|
||||||
0x52 : 0x44 //R - D
|
0x44: 0x09 // keycode D - 9
|
||||||
0x72 : 0x44 //r - D
|
0x46: 0x0E // keycode F - E
|
||||||
0x41 : 0x7 //A - 7
|
0x5A: 0x0A // keycode Z - A
|
||||||
0x61 : 0x7 //a - 7
|
0x58: 0x00 // keycode X - 0
|
||||||
0x53 : 0x8 //S - 8
|
0x43: 0x0B // keycode C - B
|
||||||
0x73 : 0x8 //s - 8
|
0x56: 0x0F // keycode V - F
|
||||||
0x44 : 0x9 //D - 9
|
|
||||||
0x64 : 0x9 //d - 9
|
|
||||||
0x46 : 0x45 //F - E
|
|
||||||
0x66 : 0x45 //f - E
|
|
||||||
0x5A : 0x41 //Z - A
|
|
||||||
0x7A : 0x41 //z - A
|
|
||||||
0x58 : 0x30 //X - 0
|
|
||||||
0x78 : 0x30 //x - 0
|
|
||||||
0x43 : 0x42 //C - B
|
|
||||||
0x63 : 0x42 //c - B
|
|
||||||
0x56 : 0x46 //V - F
|
|
||||||
0x76 : 0x46 //v - F
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const font := [
|
const font = [
|
||||||
[u8(0xF0), u8(0x90), u8(0x90), u8(0x90), u8(0xF0)], // 0
|
[u8(0xF0), u8(0x90), u8(0x90), u8(0x90), u8(0xF0)], // 0
|
||||||
[u8(0x20), u8(0x60), u8(0x20), u8(0x20), u8(0x70)], // 1
|
[u8(0x20), u8(0x60), u8(0x20), u8(0x20), u8(0x70)], // 1
|
||||||
[u8(0xF0), u8(0x10), u8(0xF0), u8(0x80), u8(0xF0)], // 2
|
[u8(0xF0), u8(0x10), u8(0xF0), u8(0x80), u8(0xF0)], // 2
|
||||||
[u8(0xF0), u8(0x10), u8(0xF0), u8(0x10), u8(0xF0)], // 3
|
[u8(0xF0), u8(0x10), u8(0xF0), u8(0x10), u8(0xF0)], // 3
|
||||||
|
|
@ -47,39 +35,38 @@ const font := [
|
||||||
[u8(0xF0), u8(0x80), u8(0x80), u8(0x80), u8(0xF0)], // C
|
[u8(0xF0), u8(0x80), u8(0x80), u8(0x80), u8(0xF0)], // C
|
||||||
[u8(0xE0), u8(0x90), u8(0x90), u8(0x90), u8(0xE0)], // D
|
[u8(0xE0), u8(0x90), u8(0x90), u8(0x90), u8(0xE0)], // D
|
||||||
[u8(0xF0), u8(0x80), u8(0xF0), u8(0x80), u8(0xF0)], // E
|
[u8(0xF0), u8(0x80), u8(0xF0), u8(0x80), u8(0xF0)], // E
|
||||||
[u8(0xF0), u8(0x80), u8(0xF0), u8(0x80), u8(0x80)], // F
|
[u8(0xF0), u8(0x80), u8(0xF0), u8(0x80), u8(0x80)], // F
|
||||||
]
|
]
|
||||||
|
|
||||||
struct Screen{
|
struct Screen {
|
||||||
pub:
|
pub:
|
||||||
display_width int = 64
|
display_width int = 64
|
||||||
display_height int = 32
|
display_height int = 32
|
||||||
mut:
|
mut:
|
||||||
display [32][64]u8
|
display [32][64]u8
|
||||||
}
|
}
|
||||||
|
|
||||||
const mem_size = 4096
|
const mem_size = 4096
|
||||||
const num_of_registers = 16
|
const num_of_registers = 16
|
||||||
const f = 15
|
const f = 15
|
||||||
|
|
||||||
struct Chip8{
|
struct Chip8 {
|
||||||
cpu_clock u8 = 9
|
cpu_clock u8 = 9
|
||||||
pub mut:
|
pub mut:
|
||||||
ram [mem_size]u8
|
ram [mem_size]u8
|
||||||
v [num_of_registers]u8
|
v [num_of_registers]u8
|
||||||
screen Screen
|
screen Screen
|
||||||
pc u16 = 0x200
|
pc u16 = 0x200
|
||||||
i u16
|
i u16
|
||||||
stack Stack
|
stack Stack
|
||||||
delay_timer u8
|
delay_timer u8
|
||||||
sound_timer u8
|
sound_timer u8
|
||||||
is_draw bool
|
is_draw bool
|
||||||
key u8
|
key u8
|
||||||
cycles u8
|
cycles u8
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut chip Chip8) start_cpu(){
|
fn (mut chip Chip8) start_cpu() {
|
||||||
|
|
||||||
chip.screen = Screen{}
|
chip.screen = Screen{}
|
||||||
|
|
||||||
// load font in the memory
|
// load font in the memory
|
||||||
|
|
@ -88,39 +75,33 @@ fn (mut chip Chip8) start_cpu(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut chip Chip8) run(){
|
fn (mut chip Chip8) run() {
|
||||||
mut instruction := chip.fetch()
|
mut instruction := chip.fetch()
|
||||||
chip.decode_and_run(instruction)
|
chip.decode_and_run(instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@[direct_array_access]
|
||||||
fn (mut chip Chip8) set_ram(instructions []u8, index u16) {
|
fn (mut chip Chip8) set_ram(instructions []u8, index u16) {
|
||||||
|
|
||||||
mut j := index
|
mut j := index
|
||||||
|
|
||||||
for i := 0; i < instructions.len; i++ {
|
for i := 0; i < instructions.len; i++ {
|
||||||
chip.ram[j] = instructions[i]
|
chip.ram[j] = instructions[i]
|
||||||
j++
|
j++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut chip Chip8) fetch() u16{
|
@[direct_array_access]
|
||||||
|
fn (mut chip Chip8) fetch() u16 {
|
||||||
mut instruction := u16(0x00)
|
mut instruction := u16(0x00)
|
||||||
|
|
||||||
mut half_instruction := chip.ram[chip.pc]
|
mut half_instruction := chip.ram[chip.pc]
|
||||||
|
|
||||||
instruction = instruction | half_instruction
|
instruction = instruction | half_instruction
|
||||||
instruction = instruction << 8
|
instruction = instruction << 8
|
||||||
half_instruction = chip.ram[chip.pc + 1]
|
half_instruction = chip.ram[chip.pc + 1]
|
||||||
|
|
||||||
instruction = instruction | half_instruction
|
instruction = instruction | half_instruction
|
||||||
|
|
||||||
return instruction
|
return instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@[direct_array_access]
|
||||||
fn (mut chip Chip8) decode_and_run(instruction u16) {
|
fn (mut chip Chip8) decode_and_run(instruction u16) {
|
||||||
|
|
||||||
mut nnn, mut nn, mut n, mut x, mut y := 0x00, 0x00, 0x00, 0x00, 0x00
|
mut nnn, mut nn, mut n, mut x, mut y := 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
|
||||||
mut opcode_msb := instruction & 0xF000
|
mut opcode_msb := instruction & 0xF000
|
||||||
|
|
@ -128,49 +109,45 @@ fn (mut chip Chip8) decode_and_run(instruction u16) {
|
||||||
mut is_jump := false
|
mut is_jump := false
|
||||||
chip.is_draw = false
|
chip.is_draw = false
|
||||||
|
|
||||||
if chip.delay_timer > 0 { chip.delay_timer-- }
|
if chip.delay_timer > 0 {
|
||||||
if chip.sound_timer > 0 { chip.sound_timer-- }
|
chip.delay_timer--
|
||||||
|
}
|
||||||
match opcode_msb{
|
if chip.sound_timer > 0 {
|
||||||
|
chip.sound_timer--
|
||||||
|
}
|
||||||
|
|
||||||
|
match opcode_msb {
|
||||||
0x0000 {
|
0x0000 {
|
||||||
|
|
||||||
match opcode_lsb {
|
match opcode_lsb {
|
||||||
0xEE {
|
0xEE {
|
||||||
chip.pc = chip.stack.pop() or { panic(err) }
|
chip.pc = chip.stack.pop() or { panic(err) }
|
||||||
// Returns from a subroutine
|
// Returns from a subroutine
|
||||||
}
|
}
|
||||||
|
|
||||||
0xE0 {
|
0xE0 {
|
||||||
chip.is_draw = true
|
chip.is_draw = true
|
||||||
for i := 0; i < chip.screen.display_height; i++{
|
for i := 0; i < chip.screen.display_height; i++ {
|
||||||
for j := 0; j < chip.screen.display_width; j++ {
|
for j := 0; j < chip.screen.display_width; j++ {
|
||||||
chip.screen.display[i][j] = 0
|
chip.screen.display[i][j] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 0NNN {
|
||||||
//0NNN {
|
|
||||||
// nnn = instruction & 0x0FFF
|
// nnn = instruction & 0x0FFF
|
||||||
// Calls machine code routine
|
// Calls machine code routine
|
||||||
//}
|
//}
|
||||||
|
else {
|
||||||
else{
|
|
||||||
nnn = instruction & 0x0FFF
|
nnn = instruction & 0x0FFF
|
||||||
//panic('Invalid instruction! 0x${instruction.hex()}')
|
// panic('Invalid instruction! 0x${instruction.hex()}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
0x1000 {
|
0x1000 {
|
||||||
nnn = instruction & 0x0FFF
|
nnn = instruction & 0x0FFF
|
||||||
chip.pc = u16(nnn)
|
chip.pc = u16(nnn)
|
||||||
is_jump = true
|
is_jump = true
|
||||||
// Jumps to address NNN
|
// Jumps to address NNN
|
||||||
}
|
}
|
||||||
|
|
||||||
0x2000 {
|
0x2000 {
|
||||||
|
|
||||||
nnn = instruction & 0x0FFF
|
nnn = instruction & 0x0FFF
|
||||||
|
|
||||||
chip.stack.push(chip.pc)
|
chip.stack.push(chip.pc)
|
||||||
|
|
@ -178,47 +155,47 @@ fn (mut chip Chip8) decode_and_run(instruction u16) {
|
||||||
is_jump = true
|
is_jump = true
|
||||||
// Calls subroutine at NNN
|
// Calls subroutine at NNN
|
||||||
}
|
}
|
||||||
|
|
||||||
0x3000 {
|
0x3000 {
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
nn = instruction & 0x00FF
|
nn = instruction & 0x00FF
|
||||||
|
|
||||||
if chip.v[x] == nn { chip.pc += 2 }
|
if chip.v[x] == nn {
|
||||||
|
chip.pc += 2
|
||||||
|
}
|
||||||
// Skips the next instruction if VX equals NN
|
// Skips the next instruction if VX equals NN
|
||||||
}
|
}
|
||||||
|
|
||||||
0x4000 {
|
0x4000 {
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
nn = instruction & 0x00FF
|
nn = instruction & 0x00FF
|
||||||
|
|
||||||
if chip.v[x] != nn { chip.pc += 2 }
|
if chip.v[x] != nn {
|
||||||
|
chip.pc += 2
|
||||||
|
}
|
||||||
// Skips the next instruction if VX does not equal NN
|
// Skips the next instruction if VX does not equal NN
|
||||||
}
|
}
|
||||||
|
|
||||||
0x5000 {
|
0x5000 {
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
y = (instruction & 0x00F0) >> 4
|
y = (instruction & 0x00F0) >> 4
|
||||||
|
|
||||||
if chip.v[x] == chip.v[y] { chip.pc += 2 }
|
if chip.v[x] == chip.v[y] {
|
||||||
|
chip.pc += 2
|
||||||
|
}
|
||||||
// Skips the next instruction if VX equals VY
|
// Skips the next instruction if VX equals VY
|
||||||
}
|
}
|
||||||
|
|
||||||
0x6000 {
|
0x6000 {
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
nn = instruction & 0x00FF
|
nn = instruction & 0x00FF
|
||||||
|
|
||||||
chip.v[x] = u8(nn)
|
chip.v[x] = u8(nn)
|
||||||
// Sets VX to NN
|
// Sets VX to NN
|
||||||
}
|
}
|
||||||
|
|
||||||
0x7000 {
|
0x7000 {
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
nn = instruction & 0x00FF
|
nn = instruction & 0x00FF
|
||||||
|
|
||||||
chip.v[x] += u8(nn)
|
chip.v[x] += u8(nn)
|
||||||
// Adds NN to VX (carry flag is not changed)
|
// Adds NN to VX (carry flag is not changed)
|
||||||
}
|
}
|
||||||
|
|
||||||
0x8000 {
|
0x8000 {
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
y = (instruction & 0x00F0) >> 4
|
y = (instruction & 0x00F0) >> 4
|
||||||
|
|
@ -229,125 +206,105 @@ fn (mut chip Chip8) decode_and_run(instruction u16) {
|
||||||
chip.v[x] = chip.v[y]
|
chip.v[x] = chip.v[y]
|
||||||
// Sets VX to the value of VY
|
// Sets VX to the value of VY
|
||||||
}
|
}
|
||||||
|
|
||||||
0x01 {
|
0x01 {
|
||||||
chip.v[x] |= chip.v[y]
|
chip.v[x] |= chip.v[y]
|
||||||
// Sets VX to VX or VY. (bitwise OR operation).
|
// Sets VX to VX or VY. (bitwise OR operation).
|
||||||
}
|
}
|
||||||
|
|
||||||
0x02 {
|
0x02 {
|
||||||
chip.v[x] &= chip.v[y]
|
chip.v[x] &= chip.v[y]
|
||||||
// Sets VX to VX and VY. (bitwise AND operation)
|
// Sets VX to VX and VY. (bitwise AND operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
0x03 {
|
0x03 {
|
||||||
chip.v[x] ^= chip.v[y]
|
chip.v[x] ^= chip.v[y]
|
||||||
// Sets VX to VX xor VY
|
// Sets VX to VX xor VY
|
||||||
}
|
}
|
||||||
|
|
||||||
0x04 {
|
0x04 {
|
||||||
|
|
||||||
xy := chip.v[x] + chip.v[y]
|
xy := chip.v[x] + chip.v[y]
|
||||||
|
|
||||||
if xy > 255 {
|
if xy > 255 {
|
||||||
chip.v[f] = 1
|
chip.v[f] = 1
|
||||||
}else{
|
} else {
|
||||||
chip.v[f] = 0
|
chip.v[f] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
chip.v[x] = (xy & 0xFF)
|
chip.v[x] = (xy & 0xFF)
|
||||||
// Adds VY to VX. VF is set to 1 when there's an overflow, and to 0 when there is not.
|
// Adds VY to VX. VF is set to 1 when there's an overflow, and to 0 when there is not.
|
||||||
}
|
}
|
||||||
|
|
||||||
0x05 {
|
0x05 {
|
||||||
|
|
||||||
if chip.v[x] > chip.v[y] {
|
if chip.v[x] > chip.v[y] {
|
||||||
chip.v[f] = 1
|
chip.v[f] = 1
|
||||||
}else{
|
} else {
|
||||||
chip.v[f] = 0
|
chip.v[f] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
chip.v[x] -= chip.v[y]
|
chip.v[x] -= chip.v[y]
|
||||||
// VY is subtracted from VX. VF is set to 0 when there's an underflow, and 1 when there is not. (i.e. VF set to 1 if VX >= VY and 0 if not)
|
// VY is subtracted from VX. VF is set to 0 when there's an underflow, and 1 when there is not. (i.e. VF set to 1 if VX >= VY and 0 if not)
|
||||||
}
|
}
|
||||||
|
|
||||||
0x06 {
|
0x06 {
|
||||||
if chip.v[x] % 2 == 1 {
|
if chip.v[x] % 2 == 1 {
|
||||||
chip.v[f] = 1;
|
chip.v[f] = 1
|
||||||
}
|
} else {
|
||||||
else {
|
chip.v[f] = 0
|
||||||
chip.v[f] = 0;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
chip.v[x] = chip.v[x] >> 1
|
chip.v[x] = chip.v[x] >> 1
|
||||||
// Stores the least significant bit of VX in VF and then shifts VX to the right by 1
|
// Stores the least significant bit of VX in VF and then shifts VX to the right by 1
|
||||||
}
|
}
|
||||||
|
|
||||||
0x07 {
|
0x07 {
|
||||||
|
|
||||||
xy := chip.v[y] - chip.v[x]
|
xy := chip.v[y] - chip.v[x]
|
||||||
|
|
||||||
if chip.v[y] > chip.v[x] {
|
if chip.v[y] > chip.v[x] {
|
||||||
chip.v[f] = 1
|
chip.v[f] = 1
|
||||||
}else{
|
} else {
|
||||||
chip.v[f] = 0
|
chip.v[f] = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
chip.v[x] = xy
|
chip.v[x] = xy
|
||||||
// Sets VX to VY minus VX. VF is set to 0 when there's an underflow, and 1 when there is not. (i.e. VF set to 1 if VY >= VX).
|
// Sets VX to VY minus VX. VF is set to 0 when there's an underflow, and 1 when there is not. (i.e. VF set to 1 if VY >= VX).
|
||||||
}
|
}
|
||||||
|
|
||||||
0x0E {
|
0x0E {
|
||||||
|
if (chip.v[x] & 10000000) == 1 {
|
||||||
if (chip.v[x] & 10000000) == 1 {
|
chip.v[f] = 1
|
||||||
chip.v[f] = 1;
|
} else {
|
||||||
}
|
chip.v[f] = 0
|
||||||
else {
|
}
|
||||||
chip.v[f] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
chip.v[x] = chip.v[x] << 1
|
chip.v[x] = chip.v[x] << 1
|
||||||
// Stores the most significant bit of VX in VF and then shifts VX to the left by 1.
|
// Stores the most significant bit of VX in VF and then shifts VX to the left by 1.
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
else{
|
|
||||||
panic('Invalid instruction! 0x${instruction.hex()}')
|
panic('Invalid instruction! 0x${instruction.hex()}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
0x9000 {
|
0x9000 {
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
y = (instruction & 0x00F0) >> 4
|
y = (instruction & 0x00F0) >> 4
|
||||||
|
|
||||||
if chip.v[x] != chip.v[y] { chip.pc += 2 }
|
if chip.v[x] != chip.v[y] {
|
||||||
|
chip.pc += 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
0xA000 {
|
0xA000 {
|
||||||
nnn = instruction & 0x0FFF
|
nnn = instruction & 0x0FFF
|
||||||
|
|
||||||
chip.i = u16(nnn)
|
chip.i = u16(nnn)
|
||||||
}
|
}
|
||||||
|
|
||||||
0xB000 {
|
0xB000 {
|
||||||
nnn = instruction & 0x0FFF
|
nnn = instruction & 0x0FFF
|
||||||
|
|
||||||
chip.pc = u16(nnn + chip.v[0])
|
chip.pc = u16(nnn + chip.v[0])
|
||||||
is_jump = true
|
is_jump = true
|
||||||
}
|
}
|
||||||
|
|
||||||
0xC000 {
|
0xC000 {
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
nn = instruction & 0x00FF
|
nn = instruction & 0x00FF
|
||||||
|
|
||||||
randint := rand.intn(256) or { panic(err) }
|
randint := rand.intn(256) or { panic(err) }
|
||||||
|
|
||||||
chip.v[x] = u8(randint & nn)
|
chip.v[x] = u8(randint & nn)
|
||||||
}
|
}
|
||||||
|
|
||||||
0xD000 {
|
0xD000 {
|
||||||
|
|
||||||
chip.is_draw = true
|
chip.is_draw = true
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
y = (instruction & 0x00F0) >> 4
|
y = (instruction & 0x00F0) >> 4
|
||||||
|
|
@ -359,12 +316,11 @@ fn (mut chip Chip8) decode_and_run(instruction u16) {
|
||||||
chip.v[f] = 0
|
chip.v[f] = 0
|
||||||
|
|
||||||
for y_coord := 0; y_coord < n; y_coord++ {
|
for y_coord := 0; y_coord < n; y_coord++ {
|
||||||
|
|
||||||
pixel := chip.ram[chip.i + y_coord]
|
pixel := chip.ram[chip.i + y_coord]
|
||||||
|
|
||||||
for x_coord := 0; x_coord < 8; x_coord++ {
|
for x_coord := 0; x_coord < 8; x_coord++ {
|
||||||
|
if (regvy + y_coord) < chip.screen.display_height
|
||||||
if (regvy + y_coord) < chip.screen.display_height && (regvx + x_coord) < chip.screen.display_width {
|
&& (regvx + x_coord) < chip.screen.display_width {
|
||||||
if (pixel & (0x80 >> x_coord)) != 0 {
|
if (pixel & (0x80 >> x_coord)) != 0 {
|
||||||
if chip.screen.display[regvy + y_coord][regvx + x_coord] == 1 {
|
if chip.screen.display[regvy + y_coord][regvx + x_coord] == 1 {
|
||||||
chip.v[f] = 1
|
chip.v[f] = 1
|
||||||
|
|
@ -376,98 +332,90 @@ fn (mut chip Chip8) decode_and_run(instruction u16) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
0xE000 {
|
0xE000 {
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
opcode_lsb = instruction & 0x00FF
|
opcode_lsb = instruction & 0x00FF
|
||||||
|
|
||||||
match opcode_lsb {
|
match opcode_lsb {
|
||||||
|
|
||||||
0x9E {
|
0x9E {
|
||||||
|
if chip.key == chip.v[x] {
|
||||||
if chip.key == chip.v[x] { chip.pc += 2 }
|
chip.pc += 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
0xA1 {
|
0xA1 {
|
||||||
|
if chip.key != chip.v[x] {
|
||||||
if chip.key != chip.v[x] { chip.pc += 2 }
|
chip.pc += 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
else{
|
|
||||||
panic('Invalid instruction 0x${instruction.hex()}')
|
panic('Invalid instruction 0x${instruction.hex()}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
0xF000 {
|
0xF000 {
|
||||||
x = (instruction & 0x0F00) >> 8
|
x = (instruction & 0x0F00) >> 8
|
||||||
opcode_lsb = instruction & 0x00FF
|
opcode_lsb = instruction & 0x00FF
|
||||||
|
|
||||||
match opcode_lsb {
|
match opcode_lsb {
|
||||||
0x07{
|
0x07 {
|
||||||
chip.v[x] = chip.delay_timer
|
chip.v[x] = chip.delay_timer
|
||||||
}
|
}
|
||||||
|
0x0A {
|
||||||
0x0A{
|
|
||||||
chip.v[x] = chip.key
|
chip.v[x] = chip.key
|
||||||
}
|
}
|
||||||
|
0x15 {
|
||||||
0x15{
|
|
||||||
chip.delay_timer = chip.v[x]
|
chip.delay_timer = chip.v[x]
|
||||||
}
|
}
|
||||||
|
0x18 {
|
||||||
0x18{
|
|
||||||
chip.sound_timer = chip.v[x]
|
chip.sound_timer = chip.v[x]
|
||||||
}
|
}
|
||||||
|
0x1E {
|
||||||
0x1E{
|
|
||||||
chip.i += chip.v[x]
|
chip.i += chip.v[x]
|
||||||
}
|
}
|
||||||
|
0x29 {
|
||||||
0x29{
|
|
||||||
chip.i = u16(chip.v[x] * 0x5)
|
chip.i = u16(chip.v[x] * 0x5)
|
||||||
}
|
}
|
||||||
|
0x33 {
|
||||||
0x33{
|
|
||||||
|
|
||||||
chip.ram[chip.i] = u8(chip.v[x] / 100)
|
chip.ram[chip.i] = u8(chip.v[x] / 100)
|
||||||
chip.ram[chip.i + 1] = u8((u8(chip.v[x] / 10)) % 10)
|
chip.ram[chip.i + 1] = u8((u8(chip.v[x] / 10)) % 10)
|
||||||
chip.ram[chip.i + 2] = u8(chip.v[x] % 100) % 10
|
chip.ram[chip.i + 2] = u8(chip.v[x] % 100) % 10
|
||||||
}
|
}
|
||||||
|
0x55 {
|
||||||
0x55{
|
|
||||||
|
|
||||||
for i := chip.v[0]; i <= x; i++ {
|
for i := chip.v[0]; i <= x; i++ {
|
||||||
chip.ram[chip.i + i] = chip.v[i]
|
chip.ram[chip.i + i] = chip.v[i]
|
||||||
}
|
}
|
||||||
chip.i = u16(x + 1)
|
chip.i = u16(x + 1)
|
||||||
}
|
}
|
||||||
|
0x65 {
|
||||||
0x65{
|
|
||||||
for i := chip.v[0]; i <= x; i++ {
|
for i := chip.v[0]; i <= x; i++ {
|
||||||
chip.v[x] = chip.ram[chip.i + i]
|
chip.v[x] = chip.ram[chip.i + i]
|
||||||
}
|
}
|
||||||
chip.i = u16(x + 1)
|
chip.i = u16(x + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
panic('Invalid instruction! 0x${instruction.hex()}')
|
panic('Invalid instruction! 0x${instruction.hex()}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
panic('Invalid instruction! 0x${instruction.hex()}')
|
panic('Invalid instruction! 0x${instruction.hex()}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !is_jump { chip.pc += 2 }
|
if !is_jump {
|
||||||
|
chip.pc += 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut chip Chip8) update_timers(){
|
fn (mut chip Chip8) update_timers() {
|
||||||
if chip.delay_timer > 0 { chip.delay_timer-- }
|
if chip.delay_timer > 0 {
|
||||||
if chip.sound_timer > 0 { chip.sound_timer-- }
|
chip.delay_timer--
|
||||||
|
}
|
||||||
|
if chip.sound_timer > 0 {
|
||||||
|
chip.sound_timer--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut chip Chip8) set_key(key int){
|
@[direct_array_access]
|
||||||
|
fn (mut chip Chip8) set_key(key int) {
|
||||||
chip.key = u8(keyboard[key])
|
chip.key = u8(keyboard[key])
|
||||||
}
|
}
|
||||||
134
src/main.v
134
src/main.v
|
|
@ -3,107 +3,85 @@ module main
|
||||||
import os
|
import os
|
||||||
import gg
|
import gg
|
||||||
import gx
|
import gx
|
||||||
|
import time
|
||||||
|
|
||||||
struct Emulator{
|
struct Emulator {
|
||||||
|
pub mut:
|
||||||
pub mut:
|
chip8 Chip8
|
||||||
chip8 Chip8
|
graphic &gg.Context = unsafe { nil }
|
||||||
graphic &gg.Context = unsafe { nil }
|
|
||||||
is_graphic bool = is_graphic()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut emulator Emulator) draw_block(i f32, j f32) {
|
fn (mut emulator Emulator) draw_block(i f32, j f32) {
|
||||||
emulator.graphic.draw_rect_filled(j,i, f32(20), f32(20), gx.rgb(0,255,0))
|
emulator.graphic.draw_rect_filled(j, i, f32(20), f32(20), gx.rgb(0, 255, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut emulator Emulator) load_rom() !{
|
fn (mut emulator Emulator) load_rom() ! {
|
||||||
|
if os.args.len <= 1 {
|
||||||
arguments := os.args.clone()
|
return error('ROM path not especified!')
|
||||||
|
|
||||||
if arguments.len > 1 {
|
|
||||||
|
|
||||||
mut file := os.open(arguments[1])!
|
|
||||||
defer { file.close() }
|
|
||||||
|
|
||||||
println(' Loading ROM in the memory...\n')
|
|
||||||
load_animate()
|
|
||||||
|
|
||||||
mut instructions := file.read_bytes(1024)
|
|
||||||
mut index := u16(0x200)
|
|
||||||
emulator.chip8.set_ram(instructions, index)
|
|
||||||
|
|
||||||
println('ROM successfully loaded into memory!')
|
|
||||||
|
|
||||||
}else{
|
|
||||||
panic('ROM path not especified!')
|
|
||||||
}
|
}
|
||||||
|
mut file := os.open(os.args[1])!
|
||||||
|
defer { file.close() }
|
||||||
|
println('Loading ROM in the memory...')
|
||||||
|
mut instructions := file.read_bytes(1024)
|
||||||
|
mut index := u16(0x200)
|
||||||
|
emulator.chip8.set_ram(instructions, index)
|
||||||
|
println('ROM successfully loaded into memory!')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frame(mut emulator Emulator){
|
fn frame(mut emulator Emulator) {
|
||||||
|
|
||||||
emulator.graphic.begin()
|
emulator.graphic.begin()
|
||||||
|
for y in 0 .. emulator.chip8.screen.display_height {
|
||||||
emulator.chip8.run()
|
for x in 0 .. emulator.chip8.screen.display_width {
|
||||||
emulator.chip8.cycles++;
|
|
||||||
emulator.chip8.update_timers()
|
|
||||||
|
|
||||||
display_height := emulator.chip8.screen.display_height
|
|
||||||
display_width := emulator.chip8.screen.display_width
|
|
||||||
|
|
||||||
for y := 0; y < display_height; y++ {
|
|
||||||
for x := 0; x < display_width; x++ {
|
|
||||||
|
|
||||||
pixel := emulator.chip8.screen.display[y][x]
|
pixel := emulator.chip8.screen.display[y][x]
|
||||||
|
|
||||||
if pixel == 1 {
|
if pixel == 1 {
|
||||||
emulator.draw_block(f32((y)*20), f32((x)*20))
|
emulator.draw_block(f32(y * 20), f32(x * 20))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if emulator.chip8.cpu_clock == emulator.chip8.cycles {
|
|
||||||
emulator.chip8.cycles = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
emulator.graphic.end()
|
emulator.graphic.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut emulator Emulator) show_display(){
|
fn (mut emulator Emulator) run(ms_per_tick int) {
|
||||||
emulator.graphic.run()
|
for {
|
||||||
|
emulator.chip8.run()
|
||||||
|
emulator.chip8.cycles++
|
||||||
|
emulator.chip8.update_timers()
|
||||||
|
if emulator.chip8.cpu_clock == emulator.chip8.cycles {
|
||||||
|
emulator.chip8.cycles = 0
|
||||||
|
}
|
||||||
|
time.sleep(ms_per_tick * time.millisecond)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_graphic() bool{
|
fn on_event(e &gg.Event, mut emulator Emulator) {
|
||||||
return os.environ()['DISPLAY'] != ''
|
if e.typ == .key_down {
|
||||||
|
x := int(e.key_code)
|
||||||
|
// eprintln('>>> e.typ: ${e.typ} | e.key_code: ${e.key_code} | x: ${x} | x.hex(): ${x.hex()}')
|
||||||
|
emulator.chip8.set_key(x)
|
||||||
|
}
|
||||||
|
if e.typ == .key_up {
|
||||||
|
emulator.chip8.set_key(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
||||||
mut emulator := &Emulator{
|
mut emulator := &Emulator{
|
||||||
chip8 : Chip8{}
|
chip8: Chip8{}
|
||||||
}
|
|
||||||
|
|
||||||
if emulator.is_graphic {
|
|
||||||
|
|
||||||
emulator.load_rom()!
|
|
||||||
emulator.chip8.start_cpu()
|
|
||||||
|
|
||||||
emulator.graphic = gg.new_context(
|
|
||||||
bg_color: gx.rgb(0, 0, 0)
|
|
||||||
width: 1280
|
|
||||||
height: 640
|
|
||||||
window_title: 'V CHIP-8 Emulator'
|
|
||||||
user_data: emulator
|
|
||||||
frame_fn : frame
|
|
||||||
event_fn: on_event
|
|
||||||
)
|
|
||||||
|
|
||||||
emulator.show_display()
|
|
||||||
|
|
||||||
}else{
|
|
||||||
panic('System is not graphic!')
|
|
||||||
}
|
}
|
||||||
}
|
emulator.load_rom()!
|
||||||
|
emulator.chip8.start_cpu()
|
||||||
fn on_event(e &gg.Event, mut emulator Emulator){
|
emulator.graphic = gg.new_context(
|
||||||
emulator.chip8.set_key(e.char_code)
|
bg_color: gx.rgb(0, 0, 0)
|
||||||
|
width: 1280
|
||||||
|
height: 640
|
||||||
|
window_title: 'V CHIP-8 Emulator'
|
||||||
|
user_data: emulator
|
||||||
|
frame_fn: frame
|
||||||
|
event_fn: on_event
|
||||||
|
)
|
||||||
|
// Ensure a constant rate of updates to the emulator, no matter
|
||||||
|
// what the refresh rate is, by running the updates in a separate
|
||||||
|
// independent thread:
|
||||||
|
spawn emulator.run(8)
|
||||||
|
emulator.graphic.run()
|
||||||
}
|
}
|
||||||
35
src/utils.v
35
src/utils.v
|
|
@ -1,40 +1,29 @@
|
||||||
module main
|
module main
|
||||||
|
|
||||||
import time
|
struct Stack {
|
||||||
|
pub mut:
|
||||||
struct Stack{
|
addresses [16]u16
|
||||||
pub mut:
|
i_control int
|
||||||
addresses [16]u16
|
|
||||||
i_control int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (stack Stack) is_empty() bool{
|
fn (stack Stack) is_empty() bool {
|
||||||
return stack.i_control == 0
|
return stack.i_control == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut stack Stack) push(address u16){
|
@[direct_array_access]
|
||||||
|
fn (mut stack Stack) push(address u16) {
|
||||||
stack.addresses[stack.i_control] = address
|
stack.addresses[stack.i_control] = address
|
||||||
stack.i_control++
|
stack.i_control++
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut stack Stack) pop() !u16{
|
@[direct_array_access]
|
||||||
|
fn (mut stack Stack) pop() !u16 {
|
||||||
if stack.is_empty() {
|
if stack.is_empty() {
|
||||||
return error('Stack is empty!')
|
return error('Stack is empty!')
|
||||||
}else{
|
} else {
|
||||||
val := stack.addresses[stack.i_control-1]
|
val := stack.addresses[stack.i_control - 1]
|
||||||
stack.addresses[stack.i_control-1] = 0
|
stack.addresses[stack.i_control - 1] = 0
|
||||||
stack.i_control--
|
stack.i_control--
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn load_animate() {
|
|
||||||
|
|
||||||
mut bars := ['|','/','-','\\']
|
|
||||||
|
|
||||||
for i := 0; i < 4000; i++ {
|
|
||||||
print('[${bars[i%4]}]\r ')
|
|
||||||
time.sleep(400000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue