diff --git a/libb/6502-posix.b b/libb/6502-posix.b index b71c8cbb..7a4f0389 100644 --- a/libb/6502-posix.b +++ b/libb/6502-posix.b @@ -1,13 +1,53 @@ exit(code) { - 0(code); + (*0xFFFC)(code); } - abort() { exit(69); } +assert(cond, msg) { + if (cond == 0) { + printf("Assertion failed: %s\n", msg); + abort(); + } +} +/* not correct at all */ +usleep(n) { + auto i, j; + if (n < 0) n = 32767; + i = 0; while (i < n>>2) { + __asm__("nop"); + i++; + } +} + +/* IO functions */ putchar(c) { - 0xFFEF(c); + extrn lchar; + lchar(0xD012, 0, c); +} + +getchar() { + extrn char; + return (char(0xD010, 0)); +} + +read(fd, buf, n) { + extrn lchar; + auto i, c; + + assert(fd == 0, "only fd==0 supported for read"); + i = 0; while (i < n) { + c = getchar(); + lchar(buf, i, c); + if (c == '\n') { + return (n); /* simulate terminal behaviour */ + } + if (c == 0xFF) return (i); + i++; + } + + return (n); } char __asm__( @@ -43,76 +83,7 @@ lchar __asm__( fputc(c, fd) { putchar(c); } - -/* TODO: actually allocate something */ -__heap_ptr 0x0200; -malloc(size) { - extrn printf; - auto ptr; - ptr = __heap_ptr; - __heap_ptr += size; - if (__heap_ptr >= 0x1000) { - printf("Allocation reached end: %p\nTODO: allow allocating more, implement free\n", __heap_ptr); - abort(); - } - return (ptr); -} -/* TODO: free someting? */ -realloc(ptr, size) { - return (malloc(size)); -} - -/* TODO: Try to implement this function with assembly - Problem with this implementation is that it is not - mapped to the operator - We cannot call this function `div` as it conflicts - with the `divmod` test -*/ -_div(a, b) { - auto d, sign; - sign = 0; - if (a < 0) { - sign = !sign; - a = -a; - } - if (b < 0) { - sign = !sign; - b = -a; - } - - d = 0; while(a >= b) { - a = a - b; - d++; - } - if (sign) d = -d; - return (d); -} -_udiv(a, b) { - auto d; - d = 0; while(a >= b | a < 0) { - a = a - b; - d++; - } - return (d); -} - -/* TODO: Try to implement this function with assembly - Problem with this implementation is that it is not - mapped to the operator */ -_rem (a, b) { - auto d; - while(a >= b) { - a = a - b; - } - return (a); -} -_urem(a, b) { - auto d; - while(a >= b | a < 0) { - a = a - b; - } - return (a); -} +fflush(); /* nop */ printn(n, b, sign) { auto a, c, d, __div, __rem; @@ -133,7 +104,17 @@ printn(n, b, sign) { putchar(c); } +stdin 0; +stdout 1; +stderr 1; + +fprintf(fd, str, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) { + assert((fd == 1) | (fd == 2), "only fd in [1,2] supported for fprintf"); + return (printf(str, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15)); +} + printf(str, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) { + extrn char; auto i, j, arg, c; i = 0; j = 0; @@ -143,7 +124,7 @@ printf(str, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) { c = char(str, i); while (c != 0) { if (c == '\n') { - putchar(0xD); // \r + putchar(0xD); /* \r */ } if(c == '%') { @@ -164,9 +145,9 @@ printf(str, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) { while (c = char(*arg, j++)) { putchar(c); } - } else if (c == 'z' | c == 'l') { /* hack for %zu %lu, % */ + } else if ((c == 'z') | (c == 'l')) { /* hack for %zu %lu, % */ c = '%'; - goto while_end; + goto continue; } else { putchar('%'); arg += 2; /* word size */ @@ -177,10 +158,91 @@ printf(str, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15) { } i++; c = char(str, i); - while_end:; + continue:; + } +} + +/* Math functions */ +_rand_seed 123456789; +rand() { + _rand_seed = 20077 * _rand_seed + 12345; + return (_rand_seed); +} + +atoi(str) { + extrn char; + + auto i, c, n, neg; + neg = 0; + n = 0; + + if (char(str, 0) == '-') { + neg = 1; + str++; + } + + i = 0; while (1) { + c = char(str, i); + if ((c == 0) | (c < '0') | (c > '9')) goto end; + n = n * 10 + (c - '0'); + i++; } + + end:; + if (neg) n = -n; + + return (n); } +/* TODO: Try to implement this function with assembly + Problem with this implementation is that it is not + mapped to the operator + We cannot call this function `div` as it conflicts + with the `divmod` test +*/ +_div(a, b) { + auto d, sign; + sign = 0; + if (a < 0) { + sign = !sign; + a = -a; + } + if (b < 0) { + sign = !sign; + b = -a; + } + + d = 0; while(a >= b) { + a = a - b; + d++; + } + if (sign) d = -d; + return (d); +} +_udiv(a, b) { + auto d; + d = 0; while(a >= b | a < 0) { + a = a - b; + d++; + } + return (d); +} + +/* TODO: Try to implement this function with assembly + Problem with this implementation is that it is not + mapped to the operator */ +_rem (a, b) { + return (a - a / b * b); +} +_urem(a, b) { + auto d; + while(a >= b | a < 0) { + a = a - b; + } + return (a); +} + +/* memory related functions */ strlen(s) { auto n; n = 0; @@ -194,7 +256,6 @@ toupper(c) { } -/* memory related functions */ memset(addr, val, size) { extrn lchar; auto i; @@ -204,3 +265,42 @@ memset(addr, val, size) { i += 1; } } + +memcpy(dest, src, n) { + extrn char, lchar; + auto i, rn; + rn = n; + if (n & 1) n--; + + i = 0; while (i < n) { + dest[i] = src[i]; + i++; + } + + if (rn & 1) lchar(dest, n-1, char(src, n-1)); + return (dest); +} + +/* TODO: actually allocate something */ +__heap_ptr 0x0E000; +malloc(size) { + extrn printf; + auto ptr; + ptr = __heap_ptr; + __heap_ptr += size; + if (__heap_ptr >= 0xFF00) { + printf("Allocation reached end: %p\nTODO: allow allocating more, implement free\n", __heap_ptr); + abort(); + } + return (ptr); +} +free() { + /* TODO: free someting */ +} +realloc(ptr, size) { + auto nptr; + nptr = malloc(size); + memcpy(nptr, ptr, size); + free(ptr); + return (nptr); +} diff --git a/src/codegen/mos6502.rs b/src/codegen/mos6502.rs index 7f0a09d4..a3c4d6c7 100644 --- a/src/codegen/mos6502.rs +++ b/src/codegen/mos6502.rs @@ -928,7 +928,7 @@ pub unsafe fn generate_function(name: *const c_char, loc: Loc, params_count: usi instr8(out, LDX, IMM, 0); instr8(out, CMP, IMM, 0); - instr8(out, BNE, REL, 5); + instr8(out, BNE, REL, 6); instr(out, TYA); instr8(out, CMP, IMM, 0); @@ -1523,6 +1523,18 @@ pub unsafe fn generate_data_section(out: *mut String_Builder, data: *const [u8]) } pub unsafe fn generate_entry(out: *mut String_Builder, asm: *mut Assembler) { + instr0(out, LDA, ABS); + add_reloc(out, RelocationKind::External{name: c!("$argv_descriptor"), offset: 3, byte: Byte::Both}, asm); + instr(out, PHA); + instr0(out, LDA, ABS); + add_reloc(out, RelocationKind::External{name: c!("$argv_descriptor"), offset: 2, byte: Byte::Both}, asm); + instr(out, PHA); + + instr0(out, LDY, ABS); + add_reloc(out, RelocationKind::External{name: c!("$argv_descriptor"), offset: 1, byte: Byte::Both}, asm); + instr0(out, LDA, ABS); + add_reloc(out, RelocationKind::External{name: c!("$argv_descriptor"), offset: 0, byte: Byte::Both}, asm); + instr0(out, JSR, ABS); add_reloc(out, RelocationKind::External{name: c!("main"), offset: 0, byte: Byte::Both, relative: false}, asm); @@ -1544,6 +1556,34 @@ pub unsafe fn generate_asm_funcs(out: *mut String_Builder, asm_funcs: *const [As } } +pub unsafe fn generate_argv(out: *mut String_Builder, arg0: *const c_char, run_args: *const [*const c_char], asm: *mut Assembler) { + let mut arr: Array = zeroed(); + da_append(&mut arr, (*asm).code_start + (*out).count as u16); + sb_appendf(out, c!("%s"), arg0); + write_byte(out, 0); + + for i in 0..run_args.len() { + da_append(&mut arr, (*asm).code_start + (*out).count as u16); + sb_appendf(out, c!("%s"), (*run_args)[i]); + write_byte(out, 0); + } + + da_append(&mut arr, 0); + let ptr = (*asm).code_start + (*out).count as u16; + for i in 0..arr.count { + write_word(out, *arr.items.add(i)); + } + + let fun_addr = (*out).count as u16; + da_append(&mut (*asm).externals, External { + name: c!("$argv_descriptor"), + addr: fun_addr, + }); + + write_word(out, 1 + run_args.len() as u16); + write_word(out, ptr); +} + pub unsafe fn usage(params: *const [Param]) { fprintf(stderr(), c!("mos6402 codegen for the B compiler\n")); fprintf(stderr(), c!("OPTIONS:\n")); @@ -1617,6 +1657,8 @@ pub unsafe fn generate_program( let data_start = (*gen).load_offset as u16 + (*out).count as u16; generate_data_section(out, da_slice((*p).data)); + generate_argv(out, arg0, run_args, &mut asm); + generate_globals(out, da_slice((*p).globals), &mut asm); log(Log_Level::INFO, c!("Generated size: 0x%x"), (*out).count as c_uint); @@ -1626,6 +1668,15 @@ pub unsafe fn generate_program( write_entire_file(program_path, (*out).items as *const c_void, (*out).count)?; log(Log_Level::INFO, c!("generated %s"), program_path); + // TODO: move this behind a flag, useful for debugging + let print_addresses = false; + if print_addresses { + for i in 0..asm.externals.count { + let ex = *asm.externals.items.add(i); + printf(c!("%s => $%04X\n"), ex.name, ex.addr as c_uint); + } + } + Some(()) } diff --git a/src/crust.rs b/src/crust.rs index 5a4b077f..edb785fd 100644 --- a/src/crust.rs +++ b/src/crust.rs @@ -99,6 +99,7 @@ pub mod libc { pub fn strlen(s: *const c_char) -> usize; pub fn strtoull(nptr: *const c_char, endptr: *mut*mut c_char, base: c_int) -> c_ulonglong; pub fn fwrite(ptr: *const c_void, size: usize, nmemb: usize, stream: *mut FILE) -> usize; + pub fn getchar() -> c_int; pub fn abort() -> !; pub fn strdup(s: *const c_char) -> *mut c_char; diff --git a/src/runner/mos6502.rs b/src/runner/mos6502.rs new file mode 100644 index 00000000..5a70683b --- /dev/null +++ b/src/runner/mos6502.rs @@ -0,0 +1,129 @@ +use crate::nob::*; +use crate::crust::libc::*; +use core::ffi::*; + +// load directly after stack page +pub const DEFAULT_LOAD_OFFSET: u16 = 0x0200; + +#[derive(Clone, Copy)] +pub struct Config { + pub load_offset: u16, +} + +pub mod fake6502 { + use core::mem::zeroed; + use super::*; + + pub static mut MEMORY: [u8; 1<<16] = unsafe { zeroed() }; + pub static mut stdout: *mut FILE = unsafe { zeroed() }; + + pub unsafe fn load_rom_at(rom: String_Builder, offset: u16) { + for i in 0..rom.count { + MEMORY[i + offset as usize] = *rom.items.add(i) as u8; + } + } + + #[no_mangle] + pub unsafe extern "C" fn read6502(address: u16) -> u8 { + // https://www.sbprojects.net/projects/apple1/wozmon.php + if address == 0xD011 { + // KBDCR: always set, key always available + // TODO: this is not very accurate, real Apple-1 + // progams would use this bit to check if key was + // pressed, we would have to check here if a byte + // is in stdin queue. + MEMORY[address as usize] |= 0b1000_0000; + } else if address == 0xD010 { + // TODO: if we want to be completely accurate, + // we would have to set bit 7 here and filter it + // out in B. + MEMORY[address as usize] = getchar() as i8 as u8; + } + + MEMORY[address as usize] + } + + #[no_mangle] + pub unsafe extern "C" fn write6502(address: u16, mut value: u8) { + // https://www.sbprojects.net/projects/apple1/wozmon.php + if address == 0xD012 { // DSP + fprintf(stdout, c!("%c"), value as c_int); + // accept immediatetely => clear bit 7 + value &= 0b0111_1111; + } + + if address == 0x0206 { + printf(c!("HERE! [0x0206 := $%x], pc := %p\n"), value as c_int, pc as c_int); + } + + MEMORY[address as usize] = value; + } + + extern "C" { + #[link_name = "reset6502"] + pub fn reset(); + #[link_name = "step6502"] + pub fn step(); + pub fn rts(); + pub static mut pc: u16; + pub static mut sp: u16; + pub static mut a: u8; + pub static mut x: u8; + pub static mut y: u8; + } + +} + +pub unsafe fn run_impl(output: *mut String_Builder, config: Config, stdout: *mut FILE) -> Option<()> { + fake6502::load_rom_at(*output, config.load_offset); + fake6502::reset(); + fake6502::pc = config.load_offset; + fake6502::stdout = stdout; + + // set reset to $0000 to exit on reset + fake6502::MEMORY[0xFFFC] = 0; + fake6502::MEMORY[0xFFFD] = 0; + + while fake6502::pc != 0 { // The convetion is stop executing when pc == $0000 + let prev_sp = fake6502::sp & 0xFF; + let opcode = fake6502::MEMORY[fake6502::pc as usize]; + fake6502::step(); + + // printf(c!("pc=%p\n"), fake6502::pc as c_int); + + let curr_sp = fake6502::sp & 0xFF; + if opcode == 0x48 && curr_sp > prev_sp { // PHA instruction + log(Log_Level::ERROR, c!("Stack overflow detected")); + log(Log_Level::ERROR, c!("SP changed from $%02X to $%02X after PHA instruction"), prev_sp as c_uint, curr_sp as c_uint); + return None; + } + } + // print exit code (in Y:A) + let code = ((fake6502::y as c_uint) << 8) | fake6502::a as c_uint; + log(Log_Level::INFO, c!("Exited with code %hd"), code); + + if code != 0 { + return None; + } + Some(()) +} + +pub unsafe fn run(output: *mut String_Builder, config: Config, program_path: *const c_char, stdout_path: Option<*const c_char>) -> Option<()> { + (*output).count = 0; + read_entire_file(program_path, output)?; + + let stdout = if let Some(stdout_path) = stdout_path { + let stdout = fopen(stdout_path, c!("wb")); + if stdout.is_null() { + return None + } + stdout + } else { + stdout() + }; + let result = run_impl(output, config, stdout); + if stdout_path.is_some() { + fclose(stdout); + } + result +} diff --git a/tests/ref.b b/tests/ref.b index cc4a45bc..7d8112d1 100644 --- a/tests/ref.b +++ b/tests/ref.b @@ -1,13 +1,15 @@ -write(ref, val) *ref = val; -read(ref) return (*ref); +// we cannot call these variables `read`, as some target may have +// a read() function in libb +rwrite(ref, val) *ref = val; +rread(ref) return (*ref); y; test1() { extrn printf; auto x; - write(&x, 69); - write(&y, 420); + rwrite(&x, 69); + rwrite(&y, 420); // printf("&x: %p\n", &x); // printf("&y: %p\n", &y); @@ -40,8 +42,8 @@ test3() { // check generated IR to confirm that printf( "xs: [%d, %d]\n", - read(xs + 0*8), - read(&xs[1*8]) + rread(xs + 0*8), + rread(&xs[1*8]) ); }