use sdl3; use sdl3::image; use sdl3::ttf; use types::c; use os; use io; use bufio; use fmt; use unix; use unix::poll; use strings; use uxn; def WIDTH = 640; def HEIGHT = 480; def DEFAULT_SCALE = 3; def MAX_CONSOLE_INPUT = 0x1000; export fn main() void = { sdl3::Init(sdl3::InitFlags::VIDEO | sdl3::InitFlags::EVENTS | sdl3::InitFlags::GAMEPAD)!; defer sdl3::Quit(); let scale = DEFAULT_SCALE; const win = sdl3::CreateWindow( c::nulstr("Meadow\0"), WIDTH*scale, HEIGHT*scale, sdl3::WindowFlags::RESIZABLE | sdl3::WindowFlags::MOUSE_CAPTURE | sdl3::WindowFlags::INPUT_FOCUS | sdl3::WindowFlags::MOUSE_FOCUS )!; defer sdl3::DestroyWindow(win); sdl3::SetWindowMinimumSize(win,0,0)!; const render = sdl3::CreateRenderer(win, null)!; defer sdl3::DestroyRenderer(render); sdl3::SetRenderDrawColor(render, 0, 0, 128, 255)!; sdl3::SetRenderVSync(render,1)!; const screen_w = WIDTH; const screen_h = HEIGHT; //Create blank texture for screen const meadow_texture = sdl3::CreateTexture(render, sdl3::PixelFormat::XRGB4444, sdl3::TextureAccess::STATIC, screen_w, screen_h)!; defer sdl3::DestroyTexture(meadow_texture); sdl3::SetTextureScaleMode(meadow_texture, sdl3::ScaleMode::NEAREST)!; // const pixeldata: [WIDTH * HEIGHT]u32 = [0...]; let pixeldata: *[uxn::MAXWIDTH * uxn::MAXHEIGHT]u16 = alloc([0...])!; //TODO figure out actual size defer free (pixeldata); let run = true; let ev = sdl3::Event { ... }; if(len(os::args) < 2){ fmt::printf("usage: %s file.rom [args..]\n")!; return; }; let path = os::args[1]; //Setting up pollfds for uni::poll const consolefd: []unix::poll::pollfd = [unix::poll::pollfd { fd = os::stdin_file, events = unix::poll::event::POLLIN, revents = 0, } ]; let state: *uxn::uxn = uxn::uxn_init(path)!; uxn::uxn_reset(state); let next_refresh: u64 = 0; const perf_freq: u64 = sdl3::GetPerformanceFrequency(); const frame_interval = perf_freq / 60; const ms_interval = perf_freq / 1000; for (run) { const now: u64 = sdl3::GetPerformanceCounter(); if(state.running){ // fmt::printf("Next Step!\n")!; // uxn::uxn_step(state)!; yield; }else{ if(state.console_vector != 0){ // fmt::printf("Console!\n")!; 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 }; const pollr = unix::poll::poll(consolefd, unix::poll::NONBLOCK); if(pollr > 0){ // fmt::println("Input!")!; let buf: [MAX_CONSOLE_INPUT]u8 = [0...]; io::read(os::stdin_file, buf)!; for( let b .. buf ){ // fmt::printfln("Input: {}",buf[0])!; match (uxn::console_input(b,1,state)) { case done => yield done; case let val: u8 => fmt::fatalf("Unhandled Opcode: {:x}", val); }; // uxn::console_input(b,1,state); }; }; }; }; if(now >= next_refresh){ next_refresh = now + frame_interval; if(state.screen_vector != 0){ // fmt::println("Executing screen vector")!; match (uxn::uxn_eval(state.screen_vector, state)) { case done => yield; case uxn::quit => run = false; case let err: uxn::error => fmt::fatalf("Error evaluating: {}", uxn::strerror(err)); }; }; }; 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)){ sdl3::DestroyTexture(meadow_texture); free(pixeldata); pixeldata = alloc([0...])!; //TODO figure out actual size let w: int = dims.width: int; let h: int = dims.height: int; fmt::printfln("Setting window to width: {} height: {}", w, h)!; sdl3::SetWindowSize(win,w*scale,h*scale)!; meadow_texture = sdl3::CreateTexture(render, sdl3::PixelFormat::XRGB4444, sdl3::TextureAccess::STATIC, w, h)!; sdl3::SetTextureScaleMode(meadow_texture, sdl3::ScaleMode::NEAREST)!; state.screen_update = true; }; state.screen_size_changed = false; }; if(state.screen_update){ const dims = uxn::get_window_size(state); for(let y: u16 = 0; y <= dims.height: u16; y+=1 ){ for(let x: u16 = 0; x <= dims.width: u16; x+=1 ){ let colshort: u16 = uxn::get_color(x,y,state); // fmt::printfln("Pixel x: {} y: {} color: {:x} pcount: {:x}", x, y, colshort, pcount)!; const index: u32 = y: u32 * dims.width: u32 + x: u32; // if(index < 10) fmt::printfln("pcount: {} index: {} x: {} y: {}", pcount, index, x, y)!; pixeldata[index] = colshort; }; }; const pitch: int = dims.width: int * 2; // fmt::printfln("Pitch is {}", pitch)!; if(pitch > 0) sdl3::UpdateTexture(meadow_texture, null, pixeldata, pitch)!; sdl3::RenderClear(render)!; sdl3::RenderTexture(render, meadow_texture, null, null)!; sdl3::RenderPresent(render)!; state.screen_update = false; }; //Render stuff for (sdl3::PollEvent(&ev)) { // fmt::println("Handling event")!; switch (ev.event_type) { case sdl3::EventType::QUIT => run = false; case sdl3::EventType::WINDOW_RESIZED => let dims = uxn::get_window_size(state); let w = ev.window.data1 / scale: i32; let h = ev.window.data2 / scale: i32; const wmod = w % scale: i32; const hmod = h % scale: i32; if((wmod !=0) || (hmod !=0)){ w = w-wmod; h = h-hmod; sdl3::SetWindowSize(win,w,h)!; }; if((dims.width != w: u16 / scale: u16) || (dims.height != h: u16 / scale: u16)){ // fmt::println("Dimensions not allowed!")!; //TODO handle this maybe uxn::set_window_size(w: u16,h: u16,state); sdl3::DestroyTexture(meadow_texture); free(pixeldata); pixeldata = alloc([0...])!; //TODO figure out actual size let old_w: int = dims.width: int; let old_h: int = dims.height: int; // fmt::printfln("User setting window to width: {} height: {}", w, h)!; sdl3::SetWindowSize(win,w*scale: i32,h*scale: i32)!; meadow_texture = sdl3::CreateTexture(render, sdl3::PixelFormat::XRGB4444, sdl3::TextureAccess::STATIC, w, h)!; sdl3::SetTextureScaleMode(meadow_texture, sdl3::ScaleMode::NEAREST)!; state.screen_update = true; }; case sdl3::EventType::MOUSE_MOTION => // fmt::printfln("Mouse Event x: {} y: {}", ev.motion.x, ev.motion.y)!; const x = ev.motion.x / scale: f32; const y = ev.motion.y / scale: f32; uxn::set_mouse_motion(x: u16,y: u16,state); if(state.mouse_vector != 0 ){ match (uxn::uxn_eval(state.mouse_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::MOUSE_BUTTON_DOWN => // fmt::printfln("Mouse Down: 0x{:x}", ev.button.button: u8 )!; const b = 1 << (ev.button.button: u8 - 1); uxn::set_mouse_down(b,state); if(state.mouse_vector != 0 ){ match (uxn::uxn_eval(state.mouse_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::MOUSE_BUTTON_UP => // fmt::printfln("Mouse Up: 0x{:x}", ev.button.button: u8 )!; const b = 1 << (ev.button.button: u8 - 1); uxn::set_mouse_up(b,state); if(state.mouse_vector != 0 ){ match (uxn::uxn_eval(state.mouse_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::MOUSE_WHEEL => fmt::printfln("Mouse Wheel!")!; case sdl3::EventType::KEY_DOWN => const key = ev.key.key; const scancode = ev.key.scancode; const mods = ev.key.mod; const sym = sdl3::GetKeyFromScancode(scancode,mods,false); uxn::set_key_down(sym: u8,state); // Handle Debug if(key == sdl3::K_F2){ fmt::println("Debug Print:")!; uxn::print_stack_debug(state); }; //Handle keyboard controller keys // 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)!; sdl3::RenderPresent(render)!; case sdl3::EventType::WINDOW_SHOWN => sdl3::RenderClear(render)!; sdl3::RenderTexture(render, meadow_texture, null, null)!; sdl3::RenderPresent(render)!; case sdl3::EventType::WINDOW_PIXEL_SIZE_CHANGED => sdl3::RenderClear(render)!; sdl3::RenderTexture(render, meadow_texture, null, null)!; sdl3::RenderPresent(render)!; case sdl3::EventType::WINDOW_MOUSE_ENTER => sdl3::RenderClear(render)!; sdl3::RenderTexture(render, meadow_texture, null, null)!; sdl3::HideCursor()!; case sdl3::EventType::WINDOW_MOUSE_LEAVE => sdl3::RenderClear(render)!; sdl3::RenderTexture(render, meadow_texture, null, null)!; sdl3::ShowCursor()!; case => // fmt::printfln("Unhandled SDL event: 0x{:x} , {}", ev.event_type, ev.event_type )!; yield; }; }; //TODO handle this in a better place if(state.dev[0x0f] != 0) run = false; // sdl3::RenderClear(render)!; // sdl3::RenderTexture(render, meadow_texture, null, null)!; // sdl3::RenderPresent(render)!; // sdl3::Delay(1000 / 60); match(sdl3::get_error()){ case void => yield; case let err: sdl3::error => fmt::printfln("SDL Error: {}", sdl3::strerror(err))!; }; }; };