From 3a32a4dec07422b262b655f4072313a1711f6117 Mon Sep 17 00:00:00 2001 From: JJ Bliss Date: Mon, 27 Apr 2026 16:32:13 -0400 Subject: [PATCH] Fixed color modes --- src/main.ha | 124 ++++++++++++++++++++++++++++++++++----- uxn/uxn.ha | 164 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 253 insertions(+), 35 deletions(-) diff --git a/src/main.ha b/src/main.ha index 5eace44..ee6c40d 100644 --- a/src/main.ha +++ b/src/main.ha @@ -13,7 +13,7 @@ def HEIGHT = 480; def DEFAULT_SCALE = 3; export fn main() void = { - sdl3::Init(sdl3::InitFlags::VIDEO | sdl3::InitFlags::EVENTS)!; + sdl3::Init(sdl3::InitFlags::VIDEO | sdl3::InitFlags::EVENTS | sdl3::InitFlags::GAMEPAD)!; defer sdl3::Quit(); let scale = DEFAULT_SCALE; @@ -65,10 +65,11 @@ export fn main() void = { const now: u64 = sdl3::GetPerformanceCounter(); if(state.running){ // fmt::printf("Next Step!\n")!; - uxn::uxn_step(state)!; + // uxn::uxn_step(state)!; + yield; }else{ - // fmt::printf("Not Running!\n")!; if(state.console_vector != 0){ + // fmt::printf("Console!\n")!; let args = os::args[2..]; let i: u8 = 0; let argcount = len(args): u8; @@ -96,10 +97,12 @@ export fn main() void = { }; i+=1; //TODO using i here seems inelegant }; - for( state.dev[0x0f] == 0; i+=1 ){ + // for( state.dev[0x0f] == 0; i+=1 ){ let buf: [1]u8 = [0]; - let count = io::read(os::stdin, buf)!; - // fmt::println("Read input")!; + fmt::println("About to read input")!; + // let count = io::read(os::stdin, buf)!; + let count = 0; + fmt::println("Read input")!; if(count != 0) { match (uxn::console_input(buf[0],1,state)) { case done => @@ -109,13 +112,15 @@ export fn main() void = { }; // 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); - }; + // }; + // fmt::println("Extra input thing?!")!; + // 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); + // }; + // fmt::println("Finished Console!")!; }; }; @@ -270,6 +275,96 @@ export fn main() void = { }; case sdl3::EventType::MOUSE_WHEEL => fmt::printfln("Mouse Wheel!")!; + case sdl3::EventType::KEY_DOWN => + uxn::set_key_down(ev.key.key: u8,state); + //Handle keyboard controller keys + const key = ev.key.key; + // Ctrl -> A 0x01 + if(key == sdl3::K_LCTRL || key == sdl3::K_RCTRL){ + uxn::set_controller_down(0x01,state); + }; + // Alt -> B 0x02 + if(key == sdl3::K_LALT || key == sdl3::K_RALT){ + uxn::set_controller_down(0x02,state); + }; + // Shift -> Select 0x04 + if(key == sdl3::K_LSHIFT || key == sdl3::K_RSHIFT){ + uxn::set_controller_down(0x04,state); + }; + // Home -> Start 0x08 + if(key == sdl3::K_HOME){ + uxn::set_controller_down(0x08,state); + }; + // Up -> Up 0x10 + if(key == sdl3::K_UP){ + uxn::set_controller_down(0x10,state); + }; + // Down -> Down 0x20 + if(key == sdl3::K_DOWN){ + uxn::set_controller_down(0x20,state); + }; + // Left -> Left 0x40 + if(key == sdl3::K_LEFT){ + uxn::set_controller_down(0x40,state); + }; + // Right -> Right 0x80 + if(key == sdl3::K_RIGHT){ + uxn::set_controller_down(0x80,state); + }; + match (uxn::uxn_eval(state.controller_vector, state)) { + case done => + yield; + case uxn::quit => + run = false; + case let err: uxn::error => + fmt::fatalf("Error evaluating: {}", uxn::strerror(err)); + }; + uxn::clear_key_down(state); + + case sdl3::EventType::KEY_UP => + const key = ev.key.key; + // Ctrl -> A 0x01 + if(key == sdl3::K_LCTRL || key == sdl3::K_RCTRL){ + uxn::set_controller_up(0x01,state); + }; + // Alt -> B 0x02 + if(key == sdl3::K_LALT || key == sdl3::K_RALT){ + uxn::set_controller_up(0x02,state); + }; + // Shift -> Select 0x04 + if(key == sdl3::K_LSHIFT || key == sdl3::K_RSHIFT){ + uxn::set_controller_up(0x04,state); + }; + // Home -> Start 0x08 + if(key == sdl3::K_HOME){ + uxn::set_controller_up(0x08,state); + }; + // Up -> Up 0x10 + if(key == sdl3::K_UP){ + uxn::set_controller_up(0x10,state); + }; + // Down -> Down 0x20 + if(key == sdl3::K_DOWN){ + uxn::set_controller_up(0x20,state); + }; + // Left -> Left 0x40 + if(key == sdl3::K_LEFT){ + uxn::set_controller_up(0x40,state); + }; + // Right -> Right 0x80 + if(key == sdl3::K_RIGHT){ + uxn::set_controller_up(0x80,state); + }; + match (uxn::uxn_eval(state.controller_vector, state)) { + case done => + yield; + case uxn::quit => + run = false; + case let err: uxn::error => + fmt::fatalf("Error evaluating: {}", uxn::strerror(err)); + }; + + case sdl3::EventType::WINDOW_EXPOSED => sdl3::RenderClear(render)!; sdl3::RenderTexture(render, meadow_texture, null, null)!; @@ -296,7 +391,8 @@ export fn main() void = { }; }; - + //TODO handle this in a better place + if(state.dev[0x0f] != 0) run = false; // sdl3::RenderClear(render)!; // sdl3::RenderTexture(render, meadow_texture, null, null)!; diff --git a/uxn/uxn.ha b/uxn/uxn.ha index 7e7214f..2d4f21d 100644 --- a/uxn/uxn.ha +++ b/uxn/uxn.ha @@ -17,7 +17,8 @@ export type uxn = struct { console_vector: u16, screen_vector: u16, mouse_vector: u16, - ram: [0x10000] u8, + controller_vector: u16, + ram: [BANKS_CAP] u8, dev: [0x100] u8, ptr: [2] u8, stk: [2][0x100] u8, @@ -34,6 +35,12 @@ export def SCREENRATE = 60; //Screen consists of two layers of 2-bit pixels export type uxn_screen = ([MAXWIDTH][MAXHEIGHT]u8, [MAXWIDTH][MAXHEIGHT]u8); +def NUMBANKS = 0x10; +def BANKSIZE = 0x10000; +def BANKS_CAP = NUMBANKS * BANKSIZE; +const banksize: u32 = BANKSIZE; +const numbanks: u16 = NUMBANKS; + const reset_vector: u16 = 0x0100; const colormodes: [4][16] u8 = [ @@ -48,6 +55,7 @@ fn initialize_uxn_mem() *uxn = { console_vector = 0, screen_vector = 0, mouse_vector = 0, + controller_vector = 0, ram = [0...], dev = [0...], ptr = [0...], @@ -156,6 +164,10 @@ fn emu_deo(port: u8, value: u8, state: *uxn) void = { draw_pixel(value,state); case 0x2f => draw_sprite(value,state); + case 0x81 => + let high = state.dev[0x80]; + let low = value; + state.controller_vector = short_from_bytes(high,low); case 0x91 => let high = state.dev[0x90]; let low = value; @@ -176,6 +188,41 @@ fn emu_deo(port: u8, value: u8, state: *uxn) void = { }; }; +fn deo_expansion(addr: u16, state: *uxn) void = { + const op = state.ram[addr]; + const length = short_from_bytes(state.ram[addr+1],state.dev[addr+2]); + switch(op) { + case 0x00 => //fill + const bank = short_from_bytes(state.ram[addr+3],state.dev[addr+4]); + const addr = short_from_bytes(state.ram[addr+5],state.dev[addr+6]); + const value = state.ram[addr+7]; + if(bank < numbanks) for(let i: u16 =0; i < length; i+=1){ + state.ram[bank * banksize + addr + i] = value; + }; + case 0x01 => //cpyl + const srcbank = short_from_bytes(state.ram[addr+3],state.dev[addr+4]); + const srcaddr = short_from_bytes(state.ram[addr+5],state.dev[addr+6]); + const dstbank = short_from_bytes(state.ram[addr+7],state.dev[addr+8]); + const dstaddr = short_from_bytes(state.ram[addr+9],state.dev[addr+10]); + if(srcbank < numbanks && dstbank < numbanks) for(let i: u16 =0; i < length; i+=1){ + const readval = state.ram[srcbank * banksize + srcaddr + i]; + state.ram[dstbank * banksize + dstaddr + i] = readval; + }; + case 0x02 => //cpr TODO are these actually different?? Either way need to not use for loop + const srcbank = short_from_bytes(state.ram[addr+3],state.dev[addr+4]); + const srcaddr = short_from_bytes(state.ram[addr+5],state.dev[addr+6]); + const dstbank = short_from_bytes(state.ram[addr+7],state.dev[addr+8]); + const dstaddr = short_from_bytes(state.ram[addr+9],state.dev[addr+10]); + if(srcbank < numbanks && dstbank < numbanks) for(let i: u16 =0; i < length; i+=1){ + const readval = state.ram[srcbank * banksize + srcaddr + i]; + state.ram[dstbank * banksize + dstaddr + i] = readval; + }; + case => + fmt::fatalf("Unknown expansion op: 0x{:x}",op); + }; + +}; + export fn set_mouse_motion(x: u16, y: u16, state: *uxn) void = { state.dev[0x92] = (x >> 8): u8; state.dev[0x93] = (x): u8; @@ -194,7 +241,24 @@ export fn set_mouse_up(button: u8, state: *uxn) void = { const new = cur & invbutton; state.dev[0x96] = new; }; +export fn set_controller_down(button: u8, state: *uxn) void = { + const cur = state.dev[0x82]; + const new = cur | button; + state.dev[0x82] = new; +}; +export fn set_controller_up(button: u8, state: *uxn) void = { + const cur = state.dev[0x82]; + const invbutton = 0xff ^ button; + const new = cur & invbutton; + state.dev[0x82] = new; +}; +export fn set_key_down(key: u8, state: *uxn) void = { + state.dev[0x83] = key; +}; +export fn clear_key_down(state: *uxn) void = { + state.dev[0x83] = 0; +}; 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]); @@ -347,21 +411,26 @@ fn copy_sprite_to_screen(bpp2: bool, colors: u8, x: u16, y: u16, addr: u16, flip let xoff: u16 = if(flipx){ yield 7; } else { yield 0; }; let yoff: u16 = if(flipy){ yield 7; } else { yield 0; }; - + const drawZero = ((colors%5 != 0)); const pixels: []u8 = if(bpp2) { yield get_pixels_from_2bpp_sprite(addr,state); } else {yield get_pixels_from_1bpp_sprite(addr,state);}; for(let p .. pixels){ - const color = colormodes[p][colors]; - - if((x < MAXWIDTH) && (y < MAXHEIGHT)){ - if(layer1) { state.screen.1[x+xoff][y+yoff] = color; } - else {state.screen.0[x+xoff][y+yoff] = color; }; + let color = colormodes[p][colors]; + + + if(drawZero || p > 0){ + if((x < MAXWIDTH) && (y < MAXHEIGHT)){ + if(layer1) { + state.screen.1[x+xoff][y+yoff] = color; + } else { + state.screen.0[x+xoff][y+yoff] = color; + }; + }; }; - if(flipx){ xoff -= 1; }else { @@ -760,10 +829,23 @@ export fn uxn_step(state: *uxn) (done | error) = { 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; + // let val: u8 = if(top: u16 == bot: u16){ + // yield 1; + // } else { + // yield 0; + // }; + let val: u8 = if(!equ_inst.short){ + yield if(bot: u8 == top: u8){ + yield 1; + } else { + yield 0; + }; } else { - yield 0; + yield if(bot: u16 == top: 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)) @@ -772,10 +854,23 @@ export fn uxn_step(state: *uxn) (done | error) = { 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; + // let val: u8 = if(top: u16 != bot: u16){ + // yield 1; + // } else { + // yield 0; + // }; + let val: u8 = if(!neq_inst.short){ + yield if(bot: u8 != top: u8){ + yield 1; + } else { + yield 0; + }; } else { - yield 0; + yield if(bot: u16 != top: 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)) @@ -784,10 +879,23 @@ export fn uxn_step(state: *uxn) (done | error) = { 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; + // let val: u8 = if(bot: u16 > top: u16){ + // yield 1; + // } else { + // yield 0; + // }; + let val: u8 = if(!gth_inst.short){ + yield if(bot: u8 > top: u8){ + yield 1; + } else { + yield 0; + }; } else { - yield 0; + yield 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)) @@ -796,10 +904,23 @@ export fn uxn_step(state: *uxn) (done | error) = { 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; + // let val: u8 = if(bot: u16 < top: u16){ + // yield 1; + // } else { + // yield 0; + // }; + let val: u8 = if(!lth_inst.short){ + yield if(bot: u8 < top: u8){ + yield 1; + } else { + yield 0; + }; } else { - yield 0; + yield 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) @@ -951,6 +1072,7 @@ export fn uxn_step(state: *uxn) (done | error) = { let res = if(!add_inst.short){ yield (top: u8 + bot: u8); } else { + // fmt::printfln("ADD2 0x{:x} + 0x{:x} = 0x{:x}",bot,top,top: u16 + bot: u16)!; yield (top: u16 + bot: u16); }; push_to_stack(add_inst.ret, res, state); @@ -1077,7 +1199,7 @@ export fn uxn_init(path: str) ( *uxn | error ) = { }; let state: *uxn = initialize_uxn_mem(); //Copy rom into ram - let bytesread = match (io::read(romfile,state.ram[0x100..0xff00])){ + let bytesread = match (io::read(romfile,state.ram[0x100..(BANKS_CAP - 0x100)])){ case let bytes: size => yield bytes; case let eof: io::EOF =>