From 623e01d1695f057ebe14e3490f230414372b47d1 Mon Sep 17 00:00:00 2001 From: JJ Bliss Date: Fri, 24 Apr 2026 22:21:26 -0400 Subject: [PATCH] progress towards working screen device --- Makefile | 2 +- src/main.ha | 135 ++++++-- uxn/uxn.ha | 861 +++++++++++++++++++++++++++++----------------------- 3 files changed, 603 insertions(+), 395 deletions(-) diff --git a/Makefile b/Makefile index 89a8b8d..43c2be1 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ HARE_SOURCES != find . -name '*.ha' build: build/meadow run: build - build/meadow factor.rom + build/meadow pixel.rom build/meadow: $(HARE_SOURCES) hare build $(LIBS) -o build/meadow src/ diff --git a/src/main.ha b/src/main.ha index 9cb1f7a..e3d5341 100644 --- a/src/main.ha +++ b/src/main.ha @@ -2,6 +2,10 @@ use sdl3; use sdl3::image; use sdl3::ttf; use types::c; +use os; +use io; +use fmt; +use strings; use uxn; def WIDTH = 640; @@ -23,20 +27,6 @@ export fn main() void = { const img = image::LoadTexture(render, c::nulstr("./assets/mascot.jpg\0"))!; defer sdl3::DestroyTexture(img); - ttf::Init()!; - defer ttf::Quit(); - - let font = ttf::OpenFontIO( - sdl3::IOFromConstMem(&tiny_ttf, len(tiny_ttf))!, true, 18.0)!; - defer ttf::CloseFont(font); - - let text_color = sdl3::Color { r = 0, g = 255, b = 0, a = 255 }; - let text = ttf::RenderText_Blended(font, c::nulstr("Hello World!\0"), - 0, text_color)!; - let text_texture = sdl3::CreateTextureFromSurface(render, text)!; - sdl3::DestroySurface(text); - defer sdl3::DestroyTexture(text_texture); - let w = 0.0f32, h = 0.0f32; sdl3::GetTextureSize(img, &w, &h)!; sdl3::get_error()!; @@ -45,10 +35,119 @@ export fn main() void = { let x = 0, y = 0; let xd = 1, yd = 1; + + const screen_w = WIDTH; + const screen_h = HEIGHT; + //Create blank texture for screen + const meadow_texture = sdl3::CreateTexture(render, + sdl3::PixelFormat::XRGB8888, + sdl3::TextureAccess::STATIC, + screen_w, + screen_h)!; + defer sdl3::DestroyTexture(meadow_texture); + // const pixeldata: [WIDTH * HEIGHT]u32 = [0...]; + let pixeldata: *[uxn::MAXWIDTH * uxn::MAXHEIGHT]u32 = alloc([0...])!; //TODO figure out actual size + let run = true; let ev = sdl3::Event { ... }; - uxn::uxnrun(); + if(len(os::args) < 2){ + fmt::printf("usage: %s file.rom [args..]\n")!; + return; + }; + let path = os::args[1]; + let state: *uxn::uxn = uxn::uxn_init(path)!; + uxn::uxn_reset(state); for (run) { + //UXN stuff + if(state.running){ + // fmt::printf("Next Step!\n")!; + uxn::uxn_step(state)!; + }else{ + // fmt::printf("Not Running!\n")!; + if(state.console_vector != 0){ + let args = os::args[2..]; + let i: u8 = 0; + let argcount = len(args): u8; + for (let arg .. args){ + // fmt::println("Console input args")!; + for(let char: u8 .. strings::toutf8(arg)){ + // fmt::printfln("Console input arg char: {:x}", char)!; + match (uxn::console_input(char,2,state)) { + case done => + yield done; + case let val: u8 => + fmt::fatalf("Unhandled Opcode: {:x}", val); + }; + }; + let ctype: u8 = if(i == (argcount - 1)){ + yield 4; + } else { + yield 3; + }; + match (uxn::console_input('\n',ctype,state)) { + case done => + yield done; + case let val: u8 => + fmt::fatalf("Unhandled Opcode: {:x}", val); + }; + i+=1; //TODO using i here seems inelegant + }; + for( state.dev[0x0f] == 0; i+=1 ){ + let buf: [1]u8 = [0]; + let count = io::read(os::stdin, buf)!; + // fmt::println("Read input")!; + if(count != 0) { + match (uxn::console_input(buf[0],1,state)) { + case done => + yield done; + case let val: u8 => + fmt::fatalf("Unhandled Opcode: {:x}", val); + }; + // fmt::println("Retuned from console_input")!; + }; + }; + match (uxn::console_input('\n',4,state)) { //TODO should this run? + case done => + yield done; + case let val: u8 => + fmt::fatalf("Unhandled Opcode: {:x}", val); + }; + }; + }; + if(state.screen_size_changed){ + let dims = uxn::get_window_size(state); + fmt::printfln("Setting window to width: {:x} height: {:x}", dims.width, dims.height)!; + if((dims.width != 0) && (dims.height !=0)){ + //TODO, check current dims + + sdl3::SetWindowSize(win,dims.width: int,dims.height: int)!; + }; + + + state.screen_size_changed = false; + }; + + if(state.screen_update){ + let pcount = 0; + for(let x: u16 = 0; x < screen_w: u16; x+=1 ){ + for(let y: u16 = 0; y < screen_h: u16; y+=1 ){ + const colshort = uxn::get_color(x,y,state); + //take 16-bit color and transform to 32-bit number + //XRGB -> XXRXGXBX + const r = (colshort & 0x0F00): u32 << (3*4); + const g = (colshort & 0x00F0): u32 << (2*4); + const b = (colshort & 0x000F): u32 << (1*4); + const color: u32 = r | g | b; + // const data = pixeldata: *[*]u32; // + pixeldata[pcount] = color; + pcount +=1; + }; + }; + state.screen_update = false; + }; + + + //Render stuff for (sdl3::PollEvent(&ev)) { if (ev.event_type == sdl3::EventType::QUIT) { run = false; @@ -64,10 +163,8 @@ export fn main() void = { h = h: f32, })!; - let dst = sdl3::FRect { y = y: f32, ... }; - sdl3::GetTextureSize(text_texture, &dst.w, &dst.h)!; - dst.x = x: f32 + (w: f32 - dst.w) / 2.0; - sdl3::RenderTexture(render, text_texture, null, &dst)!; + sdl3::UpdateTexture(meadow_texture, null, pixeldata: *opaque, 32)!; + sdl3::RenderTexture(render, meadow_texture, null, null)!; sdl3::RenderPresent(render)!; sdl3::Delay(1000 / 60); diff --git a/uxn/uxn.ha b/uxn/uxn.ha index 71d57a6..ad0f4bf 100644 --- a/uxn/uxn.ha +++ b/uxn/uxn.ha @@ -10,26 +10,59 @@ use strings; // let ptr: [2] u8 = [0...]; // let stk: [2][0x100] u8 = [[0...],[0...]]; -type uxn = struct { + +export type uxn = struct { pc: u16, console_vector: u16, + screen_vector: u16, ram: [0x10000] u8, dev: [0x100] u8, ptr: [2] u8, stk: [2][0x100] u8, + running: bool, + screen_size_changed: bool, + screen_update: bool, + screen: uxn_screen, + palette: [4] u16, }; +//TODO find a way to use these below +export def MAXWIDTH = 4096; +export def MAXHEIGHT = 2160; +//Screen consists of two layers of 2-bit pixels +export type uxn_screen = ([MAXWIDTH][MAXHEIGHT]u8, [MAXWIDTH][MAXHEIGHT]u8); + +const reset_vector: u16 = 0x0100; + fn initialize_uxn_mem() *uxn = { return alloc(uxn{ - pc = 0x0100, + pc = reset_vector, console_vector = 0, + screen_vector = 0, ram = [0...], dev = [0...], ptr = [0...], - stk = [[0...],[0...]] + stk = [[0...],[0...]], + running = false, + screen_size_changed = false, + screen_update = false, + screen = ([[0...]...],[[0...]...]), + palette = [0...], })!; }; + +export fn get_window_size(state: *uxn) struct{width: u16, height: u16} = { + let width_high = state.dev[0x22]; + let width_low = state.dev[0x23]; + let height_high = state.dev[0x24]; + let height_low = state.dev[0x25]; + + let result = struct {width: u16 = short_from_bytes(width_high,width_low), + height: u16 = short_from_bytes(height_high,height_low) }; + return result; +}; + fn emu_dei(port: u8, state: *uxn) u8 = { let val = state.dev[port]; // fmt::printfln("Read {:x} from port {:x}", val, port)!; @@ -46,23 +79,73 @@ fn emu_deo(port: u8, value: u8, state: *uxn) void = { let low = value; state.console_vector = short_from_bytes(high,low); // fmt::printfln("Setting console_vector to: {:x}", console_vector)!; - return; case 0x18 => fmt::print(value: rune)!; - return; case 0x19 => fmt::error(value)!; - return; + case 0x21 => + let high = state.dev[0x20]; + let low = value; + state.screen_vector = short_from_bytes(high,low); + case 0x2e => + draw_pixel(value,state); + case 0x2f => + yield; case => - return; + if( port == 0x22 || + port == 0x23 || + port == 0x24 || + port == 0x25 ) { + state.screen_size_changed = true; + }else if( port == 0x09 || //TODO maybe also trigger on high bytes + port == 0x0b || + port == 0x0d ){ + regenerate_palettes(state); + + }; + }; }; +fn regenerate_palettes(state: *uxn) void = { + const r = short_from_bytes(state.dev[0x08],state.dev[0x09]); + const g = short_from_bytes(state.dev[0x0a],state.dev[0x0b]); + const b = short_from_bytes(state.dev[0x0c],state.dev[0x0d]); -type evalerror = !void; -type unhandled = !u8; + + state.palette[0] = ((r & 0xf000) >> 4) | ((g & 0xf000) >> 8) | ((b & 0xf000) >> 12); + state.palette[1] = ((r & 0x0f00) >> 0) | ((g & 0x0f00) >> 4) | ((b & 0x0f00) >> 8); + state.palette[2] = ((r & 0x00f0) << 4) | ((g & 0x00f0) >> 0) | ((b & 0x00f0) >> 4); + state.palette[3] = ((r & 0x000f) << 8) | ((g & 0x000f) << 4) | ((b & 0x000f) >> 0); +}; -type error = !(evalerror | unhandled); + +fn draw_pixel(value: u8, state: *uxn) void = { + //TODO handle fill + const color = value & 0b00000011; + const x = short_from_bytes(state.dev[0x28],state.dev[0x29]); + const y = short_from_bytes(state.dev[0x2a],state.dev[0x2b]); + if((value & 0b01000000) == 0){ + state.screen.0[x][y] = color; + } else { + state.screen.1[x][y] = color; + }; + state.screen_update = true; +}; + +export fn get_color(x: u16, y: u16, state: *uxn) u16 = { + // check layer 1 + let color = state.screen.1[x][y] & 0b00000011; + if(color == 0){ + color = state.screen.0[x][y]; + }; + return state.palette[color]; +}; + +export type evalerror = !void; +export type unhandled = !u8; + +export type error = !(evalerror | unhandled); type simple_op = void; type literal_op = struct { short: bool, ret: bool}; @@ -295,169 +378,165 @@ fn pop_or_get(inst: normal_op, off: u8, state: *uxn) (u8 | u16) = { fn uxn_eval(new_pc: u16, state: *uxn) (done | error ) = { // let a: u16 = 0, b: u16 = 0, c: u16 = 0, x: [2]u16 = [0...], y: [2]u16 = [0...], z: [2]u16 = [0...]; state.pc = new_pc; - for(true){ - // fmt::printfln("Starting eval with pc: {:x}", pc)!; - let readval = state.ram[state.pc]; - let inst: instruction = get_instruction(readval)?; - state.pc += 1; //TODO verify all pc changes - - match(inst) { - // /* BRK */ case 0x00: return 1; - case BRK => - // fmt::println("Break!")!; - return done; - // /* JCI */ case 0x20: if(DEC(0)) { IMM state.pc += a; } else pc += 2; break; - case JCI => - let val: u8 = pop_from_stack(false, false, false, state): u8; - if(val != 0) { - state.pc += 2 + short_from_bytes(state.ram[state.pc],state.ram[state.pc+1]); //TODO, is this signed? - }else{ - state.pc += 2; - }; - // /* JMI */ case 0x40: IMM pc += a; break; - case JMI => - state.pc = state.pc + 2 + short_from_bytes(state.ram[state.pc],state.ram[state.pc+1]); //TODO is this signed? - // /* JSI */ case 0x60: IMM PUx(pc, 1, 1) pc += a; break; - case JSI => - push_to_stack(true, state.pc + 2, state); - state.pc = state.pc + 2 + short_from_bytes(state.ram[state.pc],state.ram[state.pc+1]); //TODO is this signed? + state.running = true; + for(state.running){ + uxn_step(state)?; + }; + return done; +}; - // /* LIT */ case 0x80: INC(0) = ram[pc++]; break; - case let lit_inst: LIT => - push_to_stack(lit_inst.ret, state.ram[state.pc], state); +export fn uxn_step(state: *uxn) (done | error) = { + // fmt::printfln("Starting eval with pc: {:x}", pc)!; + let readval = state.ram[state.pc]; + let inst: instruction = get_instruction(readval)?; + state.pc += 1; //TODO verify all pc changes + + match(inst) { + // /* BRK */ case 0x00: return 1; + case BRK => + // fmt::println("Break!")!; + state.running = false; + return done; + // /* JCI */ case 0x20: if(DEC(0)) { IMM state.pc += a; } else pc += 2; break; + case JCI => + let val: u8 = pop_from_stack(false, false, false, state): u8; + if(val != 0) { + state.pc += 2 + short_from_bytes(state.ram[state.pc],state.ram[state.pc+1]); //TODO, is this signed? + }else{ + state.pc += 2; + }; + // /* JMI */ case 0x40: IMM pc += a; break; + case JMI => + state.pc = state.pc + 2 + short_from_bytes(state.ram[state.pc],state.ram[state.pc+1]); //TODO is this signed? + // /* JSI */ case 0x60: IMM PUx(pc, 1, 1) pc += a; break; + case JSI => + push_to_stack(true, state.pc + 2, state); + state.pc = state.pc + 2 + short_from_bytes(state.ram[state.pc],state.ram[state.pc+1]); //TODO is this signed? + + // /* LIT */ case 0x80: INC(0) = ram[pc++]; break; + case let lit_inst: LIT => + push_to_stack(lit_inst.ret, state.ram[state.pc], state); + state.pc = state.pc + 1; + if(lit_inst.short) { + push_to_stack(lit_inst.ret, state.ram[state.pc],state); state.pc = state.pc + 1; - if(lit_inst.short) { - push_to_stack(lit_inst.ret, state.ram[state.pc],state); - state.pc = state.pc + 1; - }; - // /* INC */ OPC(0x01,POx(a,d),PUx(a + 1,d,r)) - case let inc_inst: INC => - let val = match(pop_from_stack(inc_inst.keep,inc_inst.ret,inc_inst.short,state)) { - //TODO this whole match statement seems unnecessary - case let byte: u8 => - yield byte + 1; - case let short: u16 => - yield short + 1; - }; - - push_to_stack(inc_inst.ret,val,state); - // /* POP */ OPC(0x02,ptr[r] -= 1 + d;,{}) - case let pop_inst: POP => - if(!pop_inst.keep){ - pop_from_stack(false,pop_inst.ret,pop_inst.short,state); - }; - // /* NIP */ OPC(0x03,GOT(x) ptr[r] -= 1 + d;,PUT(x,r)) - case let nip_inst: NIP => - let top = pop_or_get(nip_inst,0,state); - let bot = pop_or_get(nip_inst,1,state); - push_to_stack(nip_inst.ret, top, state); - // /* SWP */ OPC(0x04,GOT(x) GOT(y),PUT(x,r) PUT(y,r)) - case let swp_inst: SWP => - let top = pop_or_get(swp_inst,0,state); - let bot = pop_or_get(swp_inst,1,state); - - push_to_stack(swp_inst.ret, top, state); - push_to_stack(swp_inst.ret, bot, state); - // /* ROT */ OPC(0x05,GOT(x) GOT(y) GOT(z),PUT(y,r) PUT(x,r) PUT(z,r)) - case let rot_inst: ROT => - let top = pop_or_get(rot_inst,0,state); - let mid = pop_or_get(rot_inst,1,state); - let bot = pop_or_get(rot_inst,2,state); - - push_to_stack(rot_inst.ret, mid, state); - push_to_stack(rot_inst.ret, top, state); - push_to_stack(rot_inst.ret, bot, state); - // /* DUP */ OPC(0x06,GOT(x),PUT(x,r) PUT(x,r)) - case let dup_inst: DUP => - let val = pop_from_stack(dup_inst.keep, dup_inst.ret, dup_inst.short, state); - push_to_stack(dup_inst.ret, val, state); - push_to_stack(dup_inst.ret, val, state); - // /* OVR */ OPC(0x07,GOT(x) GOT(y),PUT(y,r) PUT(x,r) PUT(y,r)) - case let ovr_inst: OVR => - let top = pop_or_get(ovr_inst,0,state); - let bot = pop_or_get(ovr_inst,1,state); - - push_to_stack(ovr_inst.ret, bot, state); - push_to_stack(ovr_inst.ret, top, state); - push_to_stack(ovr_inst.ret, bot, state); - // /* EQU */ OPC(0x08,POx(a,d) POx(b,d),PUx(b == a,0,r)) - case let equ_inst: EQU => - let top = pop_or_get(equ_inst,0,state); - let bot = pop_or_get(equ_inst,1,state); - - //TODO, I think this works, but it feels unclean with the casting - let val: u8 = if(top: u16 == bot: u16){ - yield 1; - } else { - yield 0; - }; - push_to_stack(equ_inst.ret, val, state); - // /* NEQ */ OPC(0x09,POx(a,d) POx(b,d),PUx(b != a,0,r)) - case let neq_inst: NEQ => - let top = pop_or_get(neq_inst,0,state); - let bot = pop_or_get(neq_inst,1,state); - - //TODO, I think this works, but it feels unclean with the casting - let val: u8 = if(top: u16 != bot: u16){ - yield 1; - } else { - yield 0; - }; - push_to_stack(neq_inst.ret, val, state); - // /* GTH */ OPC(0x0a,POx(a,d) POx(b,d),PUx(b > a,0,r)) - case let gth_inst: GTH => - let top = pop_or_get(gth_inst,0,state); - let bot = pop_or_get(gth_inst,1,state); - - //TODO, I think this works, but it feels unclean with the casting - let val: u8 = if(bot: u16 > top: u16){ - yield 1; - } else { - yield 0; - }; - push_to_stack(gth_inst.ret, val, state); - // /* LTH */ OPC(0x0b,POx(a,d) POx(b,d),PUx(b < a,0,r)) - case let lth_inst: LTH => - let top = pop_or_get(lth_inst,0,state); - let bot = pop_or_get(lth_inst,1,state); - - //TODO, I think this works, but it feels unclean with the casting - let val: u8 = if(bot: u16 < top: u16){ - yield 1; - } else { - yield 0; - }; - push_to_stack(lth_inst.ret, val, state); - // /* JMP */ OPC(0x0c,POx(a,d),MOV) - case let jmp_inst: JMP => - let addr = pop_or_get(jmp_inst,0,state); - match(addr) { - case let b: u8 => - state.pc = state.pc + b: u16; //TODO verify if pc changes are right - case let s: u16 => - state.pc = s; - }; + }; + // /* INC */ OPC(0x01,POx(a,d),PUx(a + 1,d,r)) + case let inc_inst: INC => + let val = match(pop_from_stack(inc_inst.keep,inc_inst.ret,inc_inst.short,state)) { + //TODO this whole match statement seems unnecessary + case let byte: u8 => + yield byte + 1; + case let short: u16 => + yield short + 1; + }; - // /* JCN */ OPC(0x0d,POx(a,d) POx(b,0),if(b) MOV) - case let jcn_inst: JCN => - let addr = pop_or_get(jcn_inst,0,state); - let cond: u8 = pop_or_get(normal_op {keep = jcn_inst.keep, - ret = jcn_inst.ret, - short = false}, - if(jcn_inst.short){ yield 2;} else { yield 1; }, state ): u8; - if( cond != 0) { - match(addr) { - case let b: u8 => - let off = b: i8: i32; - state.pc = (state.pc: i32 + off): u16; //TODO verify if pc changes are right - case let s: u16 => - state.pc = s; - }; - }; - // /* JSR */ OPC(0x0e,POx(a,d),PUx(pc,1,!r) MOV) - case let jsr_inst: JSR => - let addr = pop_or_get(jsr_inst,0,state); - push_to_stack(true, state.pc, state); + push_to_stack(inc_inst.ret,val,state); + // /* POP */ OPC(0x02,ptr[r] -= 1 + d;,{}) + case let pop_inst: POP => + if(!pop_inst.keep){ + pop_from_stack(false,pop_inst.ret,pop_inst.short,state); + }; + // /* NIP */ OPC(0x03,GOT(x) ptr[r] -= 1 + d;,PUT(x,r)) + case let nip_inst: NIP => + let top = pop_or_get(nip_inst,0,state); + let bot = pop_or_get(nip_inst,1,state); + push_to_stack(nip_inst.ret, top, state); + // /* SWP */ OPC(0x04,GOT(x) GOT(y),PUT(x,r) PUT(y,r)) + case let swp_inst: SWP => + let top = pop_or_get(swp_inst,0,state); + let bot = pop_or_get(swp_inst,1,state); + + push_to_stack(swp_inst.ret, top, state); + push_to_stack(swp_inst.ret, bot, state); + // /* ROT */ OPC(0x05,GOT(x) GOT(y) GOT(z),PUT(y,r) PUT(x,r) PUT(z,r)) + case let rot_inst: ROT => + let top = pop_or_get(rot_inst,0,state); + let mid = pop_or_get(rot_inst,1,state); + let bot = pop_or_get(rot_inst,2,state); + + push_to_stack(rot_inst.ret, mid, state); + push_to_stack(rot_inst.ret, top, state); + push_to_stack(rot_inst.ret, bot, state); + // /* DUP */ OPC(0x06,GOT(x),PUT(x,r) PUT(x,r)) + case let dup_inst: DUP => + let val = pop_from_stack(dup_inst.keep, dup_inst.ret, dup_inst.short, state); + push_to_stack(dup_inst.ret, val, state); + push_to_stack(dup_inst.ret, val, state); + // /* OVR */ OPC(0x07,GOT(x) GOT(y),PUT(y,r) PUT(x,r) PUT(y,r)) + case let ovr_inst: OVR => + let top = pop_or_get(ovr_inst,0,state); + let bot = pop_or_get(ovr_inst,1,state); + + push_to_stack(ovr_inst.ret, bot, state); + push_to_stack(ovr_inst.ret, top, state); + push_to_stack(ovr_inst.ret, bot, state); + // /* EQU */ OPC(0x08,POx(a,d) POx(b,d),PUx(b == a,0,r)) + case let equ_inst: EQU => + let top = pop_or_get(equ_inst,0,state); + let bot = pop_or_get(equ_inst,1,state); + + //TODO, I think this works, but it feels unclean with the casting + let val: u8 = if(top: u16 == bot: u16){ + yield 1; + } else { + yield 0; + }; + push_to_stack(equ_inst.ret, val, state); + // /* NEQ */ OPC(0x09,POx(a,d) POx(b,d),PUx(b != a,0,r)) + case let neq_inst: NEQ => + let top = pop_or_get(neq_inst,0,state); + let bot = pop_or_get(neq_inst,1,state); + + //TODO, I think this works, but it feels unclean with the casting + let val: u8 = if(top: u16 != bot: u16){ + yield 1; + } else { + yield 0; + }; + push_to_stack(neq_inst.ret, val, state); + // /* GTH */ OPC(0x0a,POx(a,d) POx(b,d),PUx(b > a,0,r)) + case let gth_inst: GTH => + let top = pop_or_get(gth_inst,0,state); + let bot = pop_or_get(gth_inst,1,state); + + //TODO, I think this works, but it feels unclean with the casting + let val: u8 = if(bot: u16 > top: u16){ + yield 1; + } else { + yield 0; + }; + push_to_stack(gth_inst.ret, val, state); + // /* LTH */ OPC(0x0b,POx(a,d) POx(b,d),PUx(b < a,0,r)) + case let lth_inst: LTH => + let top = pop_or_get(lth_inst,0,state); + let bot = pop_or_get(lth_inst,1,state); + + //TODO, I think this works, but it feels unclean with the casting + let val: u8 = if(bot: u16 < top: u16){ + yield 1; + } else { + yield 0; + }; + push_to_stack(lth_inst.ret, val, state); + // /* JMP */ OPC(0x0c,POx(a,d),MOV) + case let jmp_inst: JMP => + let addr = pop_or_get(jmp_inst,0,state); + match(addr) { + case let b: u8 => + state.pc = state.pc + b: u16; //TODO verify if pc changes are right + case let s: u16 => + state.pc = s; + }; + + // /* JCN */ OPC(0x0d,POx(a,d) POx(b,0),if(b) MOV) + case let jcn_inst: JCN => + let addr = pop_or_get(jcn_inst,0,state); + let cond: u8 = pop_or_get(normal_op {keep = jcn_inst.keep, + ret = jcn_inst.ret, + short = false}, + if(jcn_inst.short){ yield 2;} else { yield 1; }, state ): u8; + if( cond != 0) { match(addr) { case let b: u8 => let off = b: i8: i32; @@ -465,216 +544,225 @@ fn uxn_eval(new_pc: u16, state: *uxn) (done | error ) = { case let s: u16 => state.pc = s; }; - - // /* STH */ OPC(0x0f,GOT(x),PUT(x,!r)) - case let sth_inst: STH => - let val = pop_or_get(sth_inst,0,state); - push_to_stack(!sth_inst.ret, val, state); - // /* LDZ */ OPC(0x10,POx(a,0),PEK(a, x, 0xff)) - case let ldz_inst: LDZ => - let addr: u16 = pop_from_stack(ldz_inst.keep, ldz_inst.ret, false, state): u16 & 0x00FF; - let val = if(ldz_inst.short){ - yield short_from_bytes(state.ram[addr],state.ram[addr+1 & 0x00FF]); - } else { - yield state.ram[addr]; - }; - push_to_stack(ldz_inst.ret, val, state); - // /* STZ */ OPC(0x11,POx(a,0) GOT(y),POK(a, y, 0xff)) - case let stz_inst: STZ => - let addr: u16 = pop_or_get(normal_op{short = false, keep = stz_inst.keep, ret = stz_inst.ret}, 0, state): u16 & 0x00FF; - let val = if(stz_inst.short){ - let low = pop_or_get(normal_op{short = false, keep = stz_inst.keep, ret = stz_inst.ret}, 1, state): u8; - let high = pop_or_get(normal_op{short = false, keep = stz_inst.keep, ret = stz_inst.ret}, 2, state): u8; - yield short_from_bytes(high,low); + }; + // /* JSR */ OPC(0x0e,POx(a,d),PUx(pc,1,!r) MOV) + case let jsr_inst: JSR => + let addr = pop_or_get(jsr_inst,0,state); + push_to_stack(true, state.pc, state); + match(addr) { + case let b: u8 => + let off = b: i8: i32; + state.pc = (state.pc: i32 + off): u16; //TODO verify if pc changes are right + case let s: u16 => + state.pc = s; + }; + + // /* STH */ OPC(0x0f,GOT(x),PUT(x,!r)) + case let sth_inst: STH => + let val = pop_or_get(sth_inst,0,state); + push_to_stack(!sth_inst.ret, val, state); + // /* LDZ */ OPC(0x10,POx(a,0),PEK(a, x, 0xff)) + case let ldz_inst: LDZ => + let addr: u16 = pop_from_stack(ldz_inst.keep, ldz_inst.ret, false, state): u16 & 0x00FF; + let val = if(ldz_inst.short){ + yield short_from_bytes(state.ram[addr],state.ram[addr+1 & 0x00FF]); + } else { + yield state.ram[addr]; + }; + push_to_stack(ldz_inst.ret, val, state); + // /* STZ */ OPC(0x11,POx(a,0) GOT(y),POK(a, y, 0xff)) + case let stz_inst: STZ => + let addr: u16 = pop_or_get(normal_op{short = false, keep = stz_inst.keep, ret = stz_inst.ret}, 0, state): u16 & 0x00FF; + let val = if(stz_inst.short){ + let low = pop_or_get(normal_op{short = false, keep = stz_inst.keep, ret = stz_inst.ret}, 1, state): u8; + let high = pop_or_get(normal_op{short = false, keep = stz_inst.keep, ret = stz_inst.ret}, 2, state): u8; + yield short_from_bytes(high,low); - }else{ - yield pop_or_get(stz_inst, 1, state): u8; - }; - match (val) { - case let b: u8 => - state.ram[addr] = b; - case let s: u16 => - let high = (s >> 8 ): u8; - let low = s: u8; - state.ram[addr] = high; - state.ram[addr+1 & 0x00FF] = low; - //TODO verify correct endianness - }; - // /* LDR */ OPC(0x12,POx(a,0),PEK(pc + (Sint8)a, x, 0xffff)) - case let ldr_inst: LDR => - let reladdr = (pop_from_stack(ldr_inst.keep, ldr_inst.ret, false, state): u16): i8; - let addr: u16 = (state.pc: u32: i32 + reladdr): u16; - let val = if(ldr_inst.short){ - yield short_from_bytes(state.ram[addr],state.ram[addr+1]); - } else { - yield state.ram[addr]; - }; - push_to_stack(ldr_inst.ret, val, state); - // /* STR */ OPC(0x13,POx(a,0) GOT(y),POK(pc + (Sint8)a, y, 0xffff)) - case let str_inst: STR => - let reladdr = (pop_from_stack(str_inst.keep, str_inst.ret, false, state): u16): i8; - let addr: u16 = (state.pc: u32: i32 + reladdr): u16; - let val = pop_from_stack(str_inst.keep, str_inst.ret, str_inst.short, state); - match (val) { - case let b: u8 => - state.ram[addr] = b; - case let s: u16 => - let high = (s >> 8 ): u8; - let low = s: u8; - state.ram[addr] = high; - state.ram[addr+1] = low; - //TODO verify correct endianness - }; - // /* LDA */ OPC(0x14,POx(a,1),PEK(a, x, 0xffff)) - case let lda_inst: LDA => - let addr: u16 = pop_from_stack(lda_inst.keep, lda_inst.ret, true, state): u16; - let val = if(lda_inst.short){ - yield short_from_bytes(state.ram[addr],state.ram[addr+1]); - } else { - yield state.ram[addr]; - }; - push_to_stack(lda_inst.ret, val, state); - // /* STA */ OPC(0x15,POx(a,1) GOT(y),POK(a, y, 0xffff)) - case let sta_inst: STA => - let addr: u16 = pop_from_stack(sta_inst.keep, sta_inst.ret, true, state): u16; - let val = pop_from_stack(sta_inst.keep, sta_inst.ret, sta_inst.short, state); - match (val) { - case let b: u8 => - state.ram[addr] = b; - case let s: u16 => - let high = (s >> 8 ): u8; - let low = s: u8; - state.ram[addr] = high; - state.ram[addr+1] = low; - //TODO verify correct endianness - }; - // /* DEI */ OPC(0x16,POx(a,0),DEI(a, x)) - case let dei_inst: DEI => - // fmt::println("DEI Instruction")!; - let port: u8 = pop_from_stack(dei_inst.keep, dei_inst.ret, false, state): u8; - let val = if(dei_inst.short){ - yield short_from_bytes(emu_dei(port,state),emu_dei(port+1,state)); - }else{ - yield emu_dei(port,state); - }; - push_to_stack(dei_inst.ret,val,state); - // /* DEO */ OPC(0x17,POx(a,0) GOT(y),DEO(a, y)) - case let deo_inst: DEO => - let port: u8 = pop_or_get(normal_op{short = false, keep = deo_inst.keep, ret = deo_inst.ret}, 0, state): u8; - if(deo_inst.short){ - let low = pop_or_get(normal_op{short = false, keep = deo_inst.keep, ret = deo_inst.ret}, 1, state): u8; - let high = pop_or_get(normal_op{short = false, keep = deo_inst.keep, ret = deo_inst.ret}, 2, state): u8; - emu_deo(port,high,state); - emu_deo(port+1,low,state); - }else{ - let val = pop_or_get(deo_inst, 1, state): u8; - emu_deo(port,val,state); - }; - // /* ADD */ OPC(0x18,POx(a,d) POx(b,d),PUx(b + a, d,r)) - case let add_inst: ADD => - let top = pop_or_get(add_inst,0,state); - let bot = pop_or_get(add_inst,1,state); - let res = if(!add_inst.short){ - yield (top: u8 + bot: u8); - } else { - yield (top: u16 + bot: u16); - }; - push_to_stack(add_inst.ret, res, state); - // /* SUB */ OPC(0x19,POx(a,d) POx(b,d),PUx(b - a, d,r)) - case let sub_inst: SUB => - let top = pop_or_get(sub_inst,0,state); - let bot = pop_or_get(sub_inst,1,state); - let res = if(!sub_inst.short){ - yield (bot: u8 - top: u8); - } else { - yield (bot: u16 - top: u16); - }; - push_to_stack(sub_inst.ret, res, state); - // /* MUL */ OPC(0x1a,POx(a,d) POx(b,d),PUx(b * a, d,r)) - case let mul_inst: MUL => - let top = pop_or_get(mul_inst,0,state); - let bot = pop_or_get(mul_inst,1,state); - let res = if(!mul_inst.short){ - yield (bot: u8 * top: u8); - } else { - yield (bot: u16 * top: u16); - }; - push_to_stack(mul_inst.ret, res, state); - // /* DIV */ OPC(0x1b,POx(a,d) POx(b,d),PUx(a ? b / a : 0, d,r)) - case let div_inst: DIV => - let top = pop_or_get(div_inst,0,state); - let bot = pop_or_get(div_inst,1,state); - - let res = if(!div_inst.short){ - assert(bot is u8); - assert(top is u8); - if(top: u8 == 0) yield top; - yield (bot: u8 / top: u8): u8; - } else { - assert(bot is u16); - assert(top is u16); - if(top: u16 == 0) yield top; - yield (bot: u16 / top: u16): u16; - };//TODO handle rounding and edge cases with division - push_to_stack(div_inst.ret, res, state); - // /* AND */ OPC(0x1c,POx(a,d) POx(b,d),PUx(b & a, d,r)) - case let and_inst: AND => - let top = pop_or_get(and_inst,0,state); - let bot = pop_or_get(and_inst,1,state); - let res = if(!and_inst.short){ - yield (bot: u8 & top: u8); - } else { - yield (bot: u16 & top: u16); - }; - push_to_stack(and_inst.ret, res, state); - // /* ORA */ OPC(0x1d,POx(a,d) POx(b,d),PUx(b | a, d,r)) - case let ora_inst: ORA => - let top = pop_or_get(ora_inst,0,state); - let bot = pop_or_get(ora_inst,1,state); - let res = if(!ora_inst.short){ - yield (bot: u8 | top: u8); - } else { - yield (bot: u16 | top: u16); - }; - push_to_stack(ora_inst.ret, res, state); - // /* EOR */ OPC(0x1e,POx(a,d) POx(b,d),PUx(b ^ a, d,r)) - case let eor_inst: EOR => - let top = pop_or_get(eor_inst,0,state); - let bot = pop_or_get(eor_inst,1,state); - let res = if(!eor_inst.short){ - yield (bot: u8 ^ top: u8); - } else { - yield (bot: u16 ^ top: u16); - }; - push_to_stack(eor_inst.ret, res, state); - // /* SFT */ OPC(0x1f,POx(a,0) POx(b,d),PUx(b >> (a & 0xf) << (a >> 4), d,r)) - case let sft_inst: SFT => - let shifts: u8 = pop_or_get(normal_op{short = false, keep = sft_inst.keep, ret = sft_inst.ret}, 0, state): u8; - let rightshifts = shifts & 0b00001111; - let leftshifts = (shifts & 0b11110000) >> 4; - if(sft_inst.short){ //TODO, this seems like it can be cleaned up a bit - let low = pop_or_get(normal_op{short = false, keep = sft_inst.keep, ret = sft_inst.ret}, 1, state): u8; - let high = pop_or_get(normal_op{short = false, keep = sft_inst.keep, ret = sft_inst.ret}, 2, state): u8; - let val = short_from_bytes(high,low); - let res = (val >> rightshifts ) << leftshifts; - push_to_stack(sft_inst.ret, res, state); - }else{ - let val = pop_or_get(sft_inst, 1, state): u8; - let res = (val >> rightshifts ) << leftshifts; - push_to_stack(sft_inst.ret, res, state); - }; + }else{ + yield pop_or_get(stz_inst, 1, state): u8; + }; + match (val) { + case let b: u8 => + state.ram[addr] = b; + case let s: u16 => + let high = (s >> 8 ): u8; + let low = s: u8; + state.ram[addr] = high; + state.ram[addr+1 & 0x00FF] = low; + //TODO verify correct endianness + }; + // /* LDR */ OPC(0x12,POx(a,0),PEK(pc + (Sint8)a, x, 0xffff)) + case let ldr_inst: LDR => + let reladdr = (pop_from_stack(ldr_inst.keep, ldr_inst.ret, false, state): u16): i8; + let addr: u16 = (state.pc: u32: i32 + reladdr): u16; + let val = if(ldr_inst.short){ + yield short_from_bytes(state.ram[addr],state.ram[addr+1]); + } else { + yield state.ram[addr]; + }; + push_to_stack(ldr_inst.ret, val, state); + // /* STR */ OPC(0x13,POx(a,0) GOT(y),POK(pc + (Sint8)a, y, 0xffff)) + case let str_inst: STR => + let reladdr = (pop_from_stack(str_inst.keep, str_inst.ret, false, state): u16): i8; + let addr: u16 = (state.pc: u32: i32 + reladdr): u16; + let val = pop_from_stack(str_inst.keep, str_inst.ret, str_inst.short, state); + match (val) { + case let b: u8 => + state.ram[addr] = b; + case let s: u16 => + let high = (s >> 8 ): u8; + let low = s: u8; + state.ram[addr] = high; + state.ram[addr+1] = low; + //TODO verify correct endianness + }; + // /* LDA */ OPC(0x14,POx(a,1),PEK(a, x, 0xffff)) + case let lda_inst: LDA => + let addr: u16 = pop_from_stack(lda_inst.keep, lda_inst.ret, true, state): u16; + let val = if(lda_inst.short){ + yield short_from_bytes(state.ram[addr],state.ram[addr+1]); + } else { + yield state.ram[addr]; + }; + push_to_stack(lda_inst.ret, val, state); + // /* STA */ OPC(0x15,POx(a,1) GOT(y),POK(a, y, 0xffff)) + case let sta_inst: STA => + let addr: u16 = pop_from_stack(sta_inst.keep, sta_inst.ret, true, state): u16; + let val = pop_from_stack(sta_inst.keep, sta_inst.ret, sta_inst.short, state); + match (val) { + case let b: u8 => + state.ram[addr] = b; + case let s: u16 => + let high = (s >> 8 ): u8; + let low = s: u8; + state.ram[addr] = high; + state.ram[addr+1] = low; + //TODO verify correct endianness + }; + // /* DEI */ OPC(0x16,POx(a,0),DEI(a, x)) + case let dei_inst: DEI => + // fmt::println("DEI Instruction")!; + let port: u8 = pop_from_stack(dei_inst.keep, dei_inst.ret, false, state): u8; + let val = if(dei_inst.short){ + yield short_from_bytes(emu_dei(port,state),emu_dei(port+1,state)); + }else{ + yield emu_dei(port,state); + }; + push_to_stack(dei_inst.ret,val,state); + // /* DEO */ OPC(0x17,POx(a,0) GOT(y),DEO(a, y)) + case let deo_inst: DEO => + let port: u8 = pop_or_get(normal_op{short = false, keep = deo_inst.keep, ret = deo_inst.ret}, 0, state): u8; + if(deo_inst.short){ + let low = pop_or_get(normal_op{short = false, keep = deo_inst.keep, ret = deo_inst.ret}, 1, state): u8; + let high = pop_or_get(normal_op{short = false, keep = deo_inst.keep, ret = deo_inst.ret}, 2, state): u8; + emu_deo(port,high,state); + emu_deo(port+1,low,state); + }else{ + let val = pop_or_get(deo_inst, 1, state): u8; + emu_deo(port,val,state); + }; + // /* ADD */ OPC(0x18,POx(a,d) POx(b,d),PUx(b + a, d,r)) + case let add_inst: ADD => + let top = pop_or_get(add_inst,0,state); + let bot = pop_or_get(add_inst,1,state); + let res = if(!add_inst.short){ + yield (top: u8 + bot: u8); + } else { + yield (top: u16 + bot: u16); + }; + push_to_stack(add_inst.ret, res, state); + // /* SUB */ OPC(0x19,POx(a,d) POx(b,d),PUx(b - a, d,r)) + case let sub_inst: SUB => + let top = pop_or_get(sub_inst,0,state); + let bot = pop_or_get(sub_inst,1,state); + let res = if(!sub_inst.short){ + yield (bot: u8 - top: u8); + } else { + yield (bot: u16 - top: u16); + }; + push_to_stack(sub_inst.ret, res, state); + // /* MUL */ OPC(0x1a,POx(a,d) POx(b,d),PUx(b * a, d,r)) + case let mul_inst: MUL => + let top = pop_or_get(mul_inst,0,state); + let bot = pop_or_get(mul_inst,1,state); + let res = if(!mul_inst.short){ + yield (bot: u8 * top: u8); + } else { + yield (bot: u16 * top: u16); + }; + push_to_stack(mul_inst.ret, res, state); + // /* DIV */ OPC(0x1b,POx(a,d) POx(b,d),PUx(a ? b / a : 0, d,r)) + case let div_inst: DIV => + let top = pop_or_get(div_inst,0,state); + let bot = pop_or_get(div_inst,1,state); + + let res = if(!div_inst.short){ + assert(bot is u8); + assert(top is u8); + if(top: u8 == 0) yield top; + yield (bot: u8 / top: u8): u8; + } else { + assert(bot is u16); + assert(top is u16); + if(top: u16 == 0) yield top; + yield (bot: u16 / top: u16): u16; + };//TODO handle rounding and edge cases with division + push_to_stack(div_inst.ret, res, state); + // /* AND */ OPC(0x1c,POx(a,d) POx(b,d),PUx(b & a, d,r)) + case let and_inst: AND => + let top = pop_or_get(and_inst,0,state); + let bot = pop_or_get(and_inst,1,state); + let res = if(!and_inst.short){ + yield (bot: u8 & top: u8); + } else { + yield (bot: u16 & top: u16); + }; + push_to_stack(and_inst.ret, res, state); + // /* ORA */ OPC(0x1d,POx(a,d) POx(b,d),PUx(b | a, d,r)) + case let ora_inst: ORA => + let top = pop_or_get(ora_inst,0,state); + let bot = pop_or_get(ora_inst,1,state); + let res = if(!ora_inst.short){ + yield (bot: u8 | top: u8); + } else { + yield (bot: u16 | top: u16); + }; + push_to_stack(ora_inst.ret, res, state); + // /* EOR */ OPC(0x1e,POx(a,d) POx(b,d),PUx(b ^ a, d,r)) + case let eor_inst: EOR => + let top = pop_or_get(eor_inst,0,state); + let bot = pop_or_get(eor_inst,1,state); + let res = if(!eor_inst.short){ + yield (bot: u8 ^ top: u8); + } else { + yield (bot: u16 ^ top: u16); + }; + push_to_stack(eor_inst.ret, res, state); + // /* SFT */ OPC(0x1f,POx(a,0) POx(b,d),PUx(b >> (a & 0xf) << (a >> 4), d,r)) + case let sft_inst: SFT => + let shifts: u8 = pop_or_get(normal_op{short = false, keep = sft_inst.keep, ret = sft_inst.ret}, 0, state): u8; + let rightshifts = shifts & 0b00001111; + let leftshifts = (shifts & 0b11110000) >> 4; + if(sft_inst.short){ //TODO, this seems like it can be cleaned up a bit + let low = pop_or_get(normal_op{short = false, keep = sft_inst.keep, ret = sft_inst.ret}, 1, state): u8; + let high = pop_or_get(normal_op{short = false, keep = sft_inst.keep, ret = sft_inst.ret}, 2, state): u8; + let val = short_from_bytes(high,low); + let res = (val >> rightshifts ) << leftshifts; + push_to_stack(sft_inst.ret, res, state); + }else{ + let val = pop_or_get(sft_inst, 1, state): u8; + let res = (val >> rightshifts ) << leftshifts; + push_to_stack(sft_inst.ret, res, state); + }; - case => - return readval: unhandled; - }; + case => + return readval: unhandled; }; - - - // return evalerror; + return done; }; -fn console_input(c: u8, ctype: u8, state: *uxn) (done | error) = { +export fn console_input(c: u8, ctype: u8, state: *uxn) (done | error) = { state.dev[0x12] = c; state.dev[0x17] = ctype; if(state.console_vector != 0){ @@ -697,7 +785,30 @@ fn strerror(err: error) str = { }; }; - +export fn uxn_init(path: str) ( *uxn | error ) = { + let romfile = match (os::open(path,fs::flag::RDONLY)) { + case let file: io::file => + yield file; + case let err: fs::error => + fmt::fatalf("Error opening {}: {}", path, fs::strerror(err)); + }; + let state: *uxn = initialize_uxn_mem(); + //Copy rom into ram + let bytesread = match (io::read(romfile,state.ram[0x100..0xff00])){ + case let bytes: size => + yield bytes; + case let eof: io::EOF => + fmt::fatalf("Empty Rom"); + case let err: io::error => + fmt::fatalf("Error reading: {}", io::strerror(err)); + }; + io::close(romfile)!; //TODO, isn't there some kind of oversized roms? + return state; +}; +export fn uxn_reset(state: *uxn) void = { + state.pc = reset_vector; + state.running = true; +}; export fn uxnrun() void = { if(len(os::args) < 2){ fmt::printf("usage: %s file.rom [args..]\n")!;