use std use sdl const winwidth = 960 const winheight = 480 const scrwidth = 64 const scrheight = 32 const scrsz = scrwidth * scrheight const memsz = 4096 const stksz = 16 const regsz = 16 const fntoff = 0x50 const fntsz = 80 const fnt = [ 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0 0x20, 0x60, 0x20, 0x20, 0x70, // 1 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3 0x90, 0x90, 0xF0, 0x10, 0x10, // 4 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6 0xF0, 0x10, 0x20, 0x40, 0x40, // 7 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9 0xF0, 0x90, 0xF0, 0x90, 0x90, // A 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B 0xF0, 0x80, 0x80, 0x80, 0xF0, // C 0xE0, 0x90, 0x90, 0x90, 0xE0, // D 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E 0xF0, 0x80, 0xF0, 0x80, 0x80 // F ]; const clrscr = {b, n var i for i = 0; i < n; i++; b#[i] = 0 ;; } const loadprg = {b, fname var fd, i, buf match std.open(fname, std.Oread) | `std.Ok(n): fd = n | `std.Err(errno): std.fatal("could not open {}\n", fname) ;; buf = std.slalloc(0xfff - 0x200) match std.read(fd, buf) | `std.Ok(n): for i = 0; i < 0xfff - 0x200; i++; b#[i + 0x200] = (buf[i] : uint8) ;; | `std.Err(n): std.fatal("could not read {}\n", fname) ;; } const loadfnt = {b var i for i = 0; i < fntsz; i++; b#[i+fntoff] = fnt[i] ;; } const getkeys = { var state = sdl.getkbd() -> [ state#[sdl.SCANCODE_1], state#[sdl.SCANCODE_2], state#[sdl.SCANCODE_3], state#[sdl.SCANCODE_4], state#[sdl.SCANCODE_Q], state#[sdl.SCANCODE_W], state#[sdl.SCANCODE_E], state#[sdl.SCANCODE_R], state#[sdl.SCANCODE_A], state#[sdl.SCANCODE_S], state#[sdl.SCANCODE_D], state#[sdl.SCANCODE_F], state#[sdl.SCANCODE_Z], state#[sdl.SCANCODE_X], state#[sdl.SCANCODE_C], state#[sdl.SCANCODE_V], state#[sdl.SCANCODE_ESC], ] } const main = { var win, r, tex sdl.init(sdl.INIT_VIDEO) win = sdl.mkwin(("chip\0" : byte#), sdl.WIN_POS_UNSPEC, sdl.WIN_POS_UNSPEC, winwidth, winheight, sdl.WIN_OPENGL) r = sdl.mkrenderer(win, -1, sdl.RENDERER_ACCEL) tex = sdl.mktexture(r, sdl.PIXFMT_RGB332, sdl.TEXACCESS_STREAM, 64, 32) var gfx : uint8[scrsz] var mem : uint8[memsz] var reg : uint8[regsz] var stk : uint16[stksz] var pc : uint16 = 0x200 var sp : uint16 = 0 var I : uint16 = 0 var delay : uint8 = 0 var sound : uint8 = 0 var keys clrscr(&gfx, scrsz) loadprg(&mem, "/tmp/UFO") loadfnt(&mem) var opcode, arg while true opcode = mem[pc] arg = mem[pc + 1] keys = getkeys() sdl.delay(2) if keys[0x10] != 0 break ;; match (opcode & 0xf0) >> 4 | 0x0: // CLS if (opcode & 0x0f) == 0 && (arg & 0x0f) == 0 clrscr(&gfx, scrsz) // RET elif (opcode & 0x0f) == 0 if sp == 0 std.die("stack underflow") ;; sp-- pc = stk[sp] // SYS else std.die("RCA 1802 SYS unimplented") ;; // JP | 0x1: pc = (opcode & 0x0f : uint16) pc <<= 8 pc |= (arg : uint16) pc -= 2 // CALL | 0x2: if sp >= stksz std.die("stack overflow") ;; stk[sp] = pc sp++ pc = (opcode & 0x0f : uint16) pc <<= 8 pc |= (arg : uint16) pc -= 2 // SE | 0x3: if reg[opcode & 0x0f] == arg pc += 2 ;; // SNE | 0x4: if reg[opcode & 0x0f] != arg pc += 2 ;; // SE | 0x5: if reg[opcode & 0x0f] == reg[(arg & 0xf0) >> 4] pc += 2 ;; // LD | 0x6: reg[opcode & 0x0f] = arg // ADD (without overflow) | 0x7: reg[opcode & 0x0f] += arg | 0x8: match arg & 0x0f // LD | 0x0: reg[opcode & 0x0f] = reg[(arg & 0xf0) >> 4] // OR | 0x1: reg[opcode & 0x0f] |= reg[(arg & 0xf0) >> 4] // AND | 0x2: reg[opcode & 0x0f] &= reg[(arg & 0xf0) >> 4] // XOR | 0x3: reg[opcode & 0x0f] ^= reg[(arg & 0xf0) >> 4] // ADD | 0x4: if (0xff - reg[opcode & 0x0f]) < reg[(arg & 0xf0) >> 4] reg[0xf] = 1 else reg[0xf] = 0 ;; reg[opcode & 0x0f] += reg[(arg & 0xf0) >> 4] // SUB | 0x5: if (0xff - reg[opcode & 0x0f]) < reg[(arg & 0xf0) >> 4] reg[0xf] = 1 else reg[0xf] = 0 ;; reg[opcode & 0x0f] -= reg[(arg & 0xf0) >> 4] // SHR | 0x6: reg[0xf] = reg[opcode & 0x0f] & 1 reg[opcode & 0x0f] >>= 1 // SUBN | 0x7: if (0xff - reg[(arg & 0xf0) >> 4]) < reg[opcode & 0x0f] reg[0xf] = 1 else reg[0xf] = 0 ;; reg[opcode & 0x0f] = reg[(arg & 0xf0) >> 4] - reg[opcode & 0x0f] // SHL | 0xe: reg[0xf] = reg[opcode & 0x0f] & (1 << 7) reg[opcode & 0x0f] <<= 1 | _: std.die("unimplemented opcode") ;; // SNE | 0x9: if reg[opcode & 0x0f] != reg[(arg & 0xf0) >> 4] pc += 2 ;; // LD (I) | 0xa: I = (opcode & 0x0f : uint16) I <<= 8 I |= (arg : uint16) // JP + V0 | 0xb: pc = (opcode & 0x0f : uint16) pc <<= 8 pc |= (arg : uint16) pc += (reg[0] : uint16) pc -= 2 // RND | 0xc: // HACK: SIGFPE when calling std.rand(0, 256) reg[opcode & 0x0f] = (std.rand(0, 255) + std.rand(0, 2)) & arg // DRW | 0xd: var x = (reg[opcode & 0x0f] : uint16) var y = (reg[(arg & 0xf0) >> 4] : uint16) var n = (arg & 0x0f : uint16) var i, j reg[0xf] = 0 for i = 0; i < n; i++; for j = 0; j < 8; j++; if (y + i) * scrwidth + x + j < scrsz if mem[I + i] & ((0x80 >> j) : uint8) != 0 if gfx[(y + i) * scrwidth + x + j] != 0 reg[0xf] = 1 ;; gfx[(y + i) * scrwidth + x + j] ^= 0xff ;; ;; ;; ;; sdl.update(tex, (gfx[:] : void#), 64) sdl.copy(r, tex) sdl.present(r) | 0xe: // SKP if arg == 0x9e if keys[reg[opcode & 0x0f]] != 0 pc += 2 ;; // SKNP elif arg == 0xa1 if keys[reg[opcode & 0x0f]] == 0 pc += 2 ;; else std.die("unimplemented opcode") ;; | 0xf: match arg // LD | 0x07: reg[opcode & 0x0f] = delay // LD | 0x0a: std.die("unimplemented opcode") // LD | 0x15: delay = reg[opcode & 0x0f] // LD | 0x18: sound = reg[opcode & 0x0f] // ADD | 0x1e: I += (reg[opcode & 0x0f] : uint16) if I > 0xfff reg[0xf] = 1 ;; // LD | 0x29: I = fntoff + 5 * (reg[opcode & 0x0f] : uint16) // BCD | 0x33: mem[I] = reg[(opcode & 0x0f)] / 100 mem[I + 1] = (reg[(opcode & 0x0f)] % 100) / 10 // HACK: Assembler error on (opcode & 0x0f) % 10 mem[I + 2] = reg[(opcode & 0x0f)] - mem[I + 1] * 10 - mem[I] * 100 // LD | 0x55: var i for i = 0; i <= ((opcode & 0x0f) : uint16); i++; mem[I + i] = reg[i] ;; // LD | 0x65: var i for i = 0; i <= ((opcode & 0x0f) : uint16); i++; reg[i] = mem[I + i] ;; | _: std.die("unimplemented opcode") ;; | _ : std.die("unimplemented opcode") ;; pc += 2 if delay > 0 delay-- ;; if sound > 0 sound-- ;; ;; sdl.freerenderer(r) sdl.freewin(win) sdl.quit() }