progress towards working screen device

This commit is contained in:
JJ Bliss
2026-04-24 22:21:26 -04:00
parent 45fe345074
commit 623e01d169
3 changed files with 603 additions and 395 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ HARE_SOURCES != find . -name '*.ha'
build: build/meadow build: build/meadow
run: build run: build
build/meadow factor.rom build/meadow pixel.rom
build/meadow: $(HARE_SOURCES) build/meadow: $(HARE_SOURCES)
hare build $(LIBS) -o build/meadow src/ hare build $(LIBS) -o build/meadow src/
+116 -19
View File
@@ -2,6 +2,10 @@ use sdl3;
use sdl3::image; use sdl3::image;
use sdl3::ttf; use sdl3::ttf;
use types::c; use types::c;
use os;
use io;
use fmt;
use strings;
use uxn; use uxn;
def WIDTH = 640; def WIDTH = 640;
@@ -23,20 +27,6 @@ export fn main() void = {
const img = image::LoadTexture(render, c::nulstr("./assets/mascot.jpg\0"))!; const img = image::LoadTexture(render, c::nulstr("./assets/mascot.jpg\0"))!;
defer sdl3::DestroyTexture(img); 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; let w = 0.0f32, h = 0.0f32;
sdl3::GetTextureSize(img, &w, &h)!; sdl3::GetTextureSize(img, &w, &h)!;
sdl3::get_error()!; sdl3::get_error()!;
@@ -45,10 +35,119 @@ export fn main() void = {
let x = 0, y = 0; let x = 0, y = 0;
let xd = 1, yd = 1; 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 run = true;
let ev = sdl3::Event { ... }; 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) { 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)) { for (sdl3::PollEvent(&ev)) {
if (ev.event_type == sdl3::EventType::QUIT) { if (ev.event_type == sdl3::EventType::QUIT) {
run = false; run = false;
@@ -64,10 +163,8 @@ export fn main() void = {
h = h: f32, h = h: f32,
})!; })!;
let dst = sdl3::FRect { y = y: f32, ... }; sdl3::UpdateTexture(meadow_texture, null, pixeldata: *opaque, 32)!;
sdl3::GetTextureSize(text_texture, &dst.w, &dst.h)!; sdl3::RenderTexture(render, meadow_texture, null, null)!;
dst.x = x: f32 + (w: f32 - dst.w) / 2.0;
sdl3::RenderTexture(render, text_texture, null, &dst)!;
sdl3::RenderPresent(render)!; sdl3::RenderPresent(render)!;
sdl3::Delay(1000 / 60); sdl3::Delay(1000 / 60);
+128 -17
View File
@@ -10,26 +10,59 @@ use strings;
// let ptr: [2] u8 = [0...]; // let ptr: [2] u8 = [0...];
// let stk: [2][0x100] u8 = [[0...],[0...]]; // let stk: [2][0x100] u8 = [[0...],[0...]];
type uxn = struct {
export type uxn = struct {
pc: u16, pc: u16,
console_vector: u16, console_vector: u16,
screen_vector: u16,
ram: [0x10000] u8, ram: [0x10000] u8,
dev: [0x100] u8, dev: [0x100] u8,
ptr: [2] u8, ptr: [2] u8,
stk: [2][0x100] 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 = { fn initialize_uxn_mem() *uxn = {
return alloc(uxn{ return alloc(uxn{
pc = 0x0100, pc = reset_vector,
console_vector = 0, console_vector = 0,
screen_vector = 0,
ram = [0...], ram = [0...],
dev = [0...], dev = [0...],
ptr = [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 = { fn emu_dei(port: u8, state: *uxn) u8 = {
let val = state.dev[port]; let val = state.dev[port];
// fmt::printfln("Read {:x} from port {:x}", val, 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; let low = value;
state.console_vector = short_from_bytes(high,low); state.console_vector = short_from_bytes(high,low);
// fmt::printfln("Setting console_vector to: {:x}", console_vector)!; // fmt::printfln("Setting console_vector to: {:x}", console_vector)!;
return;
case 0x18 => case 0x18 =>
fmt::print(value: rune)!; fmt::print(value: rune)!;
return;
case 0x19 => case 0x19 =>
fmt::error(value)!; 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 => 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;
type error = !(evalerror | unhandled); 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);
};
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 simple_op = void;
type literal_op = struct { short: bool, ret: bool}; type literal_op = struct { short: bool, ret: bool};
@@ -295,7 +378,14 @@ fn pop_or_get(inst: normal_op, off: u8, state: *uxn) (u8 | u16) = {
fn uxn_eval(new_pc: u16, state: *uxn) (done | error ) = { 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...]; // 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; state.pc = new_pc;
for(true){ state.running = true;
for(state.running){
uxn_step(state)?;
};
return done;
};
export fn uxn_step(state: *uxn) (done | error) = {
// fmt::printfln("Starting eval with pc: {:x}", pc)!; // fmt::printfln("Starting eval with pc: {:x}", pc)!;
let readval = state.ram[state.pc]; let readval = state.ram[state.pc];
let inst: instruction = get_instruction(readval)?; let inst: instruction = get_instruction(readval)?;
@@ -305,6 +395,7 @@ fn uxn_eval(new_pc: u16, state: *uxn) (done | error ) = {
// /* BRK */ case 0x00: return 1; // /* BRK */ case 0x00: return 1;
case BRK => case BRK =>
// fmt::println("Break!")!; // fmt::println("Break!")!;
state.running = false;
return done; return done;
// /* JCI */ case 0x20: if(DEC(0)) { IMM state.pc += a; } else pc += 2; break; // /* JCI */ case 0x20: if(DEC(0)) { IMM state.pc += a; } else pc += 2; break;
case JCI => case JCI =>
@@ -668,13 +759,10 @@ fn uxn_eval(new_pc: u16, state: *uxn) (done | error ) = {
case => case =>
return readval: unhandled; return readval: unhandled;
}; };
return done;
}; };
export fn console_input(c: u8, ctype: u8, state: *uxn) (done | error) = {
// return evalerror;
};
fn console_input(c: u8, ctype: u8, state: *uxn) (done | error) = {
state.dev[0x12] = c; state.dev[0x12] = c;
state.dev[0x17] = ctype; state.dev[0x17] = ctype;
if(state.console_vector != 0){ 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 = { export fn uxnrun() void = {
if(len(os::args) < 2){ if(len(os::args) < 2){
fmt::printf("usage: %s file.rom [args..]\n")!; fmt::printf("usage: %s file.rom [args..]\n")!;