For now, only a dummy app, but it's emulated properly with libz80. Exciting times!pull/10/head
@@ -0,0 +1,3 @@ | |||||
# User applications | |||||
This folder contains code designed to be loaded and ran in RAM. |
@@ -0,0 +1,15 @@ | |||||
# z80 assembler | |||||
This is probably the most critical part of the Collapse OS project. If this app | |||||
can be brought to completion, it pretty much makes the project a success because | |||||
it ensures self-reproduction. | |||||
## Running on a "modern" machine | |||||
To be able to develop zasm efficiently, [libz80][libz80] is used to run zasm | |||||
on a modern machine. The code lives in `emul` and ran be built with `make`, | |||||
provided that you have a copy libz80 living in `emul/libz80`. | |||||
The resulting `zasm` binary takes asm code in stdin and spits binary in stdout. | |||||
[libz80]: https://github.com/ggambetta/libz80 |
@@ -0,0 +1,4 @@ | |||||
libz80 | |||||
wrapper.h | |||||
zasm.h | |||||
zasm |
@@ -0,0 +1,11 @@ | |||||
zasm: zasm.c libz80/libz80.so wrapper.h zasm.h | |||||
cc $< -l z80 -L./libz80 -Wl,-rpath ./libz80 -o $@ | |||||
libz80/libz80.so: libz80/Makefile | |||||
make -C libz80 | |||||
wrapper.h: wrapper.asm | |||||
scas -o - $< | ./bin2c.sh WRAPPER > $@ | |||||
zasm.h: ../zasm.asm | |||||
scas -o - $< | ./bin2c.sh ZASM > $@ |
@@ -0,0 +1,5 @@ | |||||
#!/bin/sh | |||||
echo "unsigned char $1[] = { " | |||||
xxd -i - | |||||
echo " };" |
@@ -0,0 +1,13 @@ | |||||
; setup the stack | |||||
ld hl, 0xffff | |||||
ld sp, hl | |||||
; zasm input | |||||
ld hl, 0x9000 | |||||
; zasm output | |||||
ld hl, 0xc000 | |||||
call zasm | |||||
; signal the emulator we're done | |||||
out (0), a | |||||
halt | |||||
zasm: | |||||
; beginning of the code |
@@ -0,0 +1,64 @@ | |||||
#include <stdint.h> | |||||
#include "libz80/z80.h" | |||||
#include "wrapper.h" | |||||
#include "zasm.h" | |||||
/* zasm is a "pure memory" application. It starts up being told memory location | |||||
* to read and memory location to write. | |||||
* | |||||
* This program works be writing stdin in a specific location in memory, run | |||||
* zasm in a special wrapper, wait until we receive the stop signal, then | |||||
* spit the contents of the dest memory to stdout. | |||||
*/ | |||||
static Z80Context cpu; | |||||
static uint8_t mem[0xffff]; | |||||
static int running; | |||||
static uint8_t io_read(int unused, uint16_t addr) | |||||
{ | |||||
return 0; | |||||
} | |||||
static void io_write(int unused, uint16_t addr, uint8_t val) | |||||
{ | |||||
// zasm doesn't do any IO. If we receive any IO, it means that we're done | |||||
// because the wrapper told us through an "out" | |||||
running = 0; | |||||
} | |||||
static uint8_t mem_read(int unused, uint16_t addr) | |||||
{ | |||||
return mem[addr]; | |||||
} | |||||
static void mem_write(int unused, uint16_t addr, uint8_t val) | |||||
{ | |||||
mem[addr] = val; | |||||
} | |||||
int main() | |||||
{ | |||||
// initialize memory | |||||
int wrapperlen = sizeof(WRAPPER); | |||||
for (int i=0; i<wrapperlen; i++) { | |||||
mem[i] = WRAPPER[i]; | |||||
} | |||||
int zasm = sizeof(ZASM); | |||||
for (int i=0; i<zasm; i++) { | |||||
mem[i+wrapperlen] = ZASM[i]; | |||||
} | |||||
// Run! | |||||
running = 1; | |||||
Z80RESET(&cpu); | |||||
cpu.ioRead = io_read; | |||||
cpu.ioWrite = io_write; | |||||
cpu.memRead = mem_read; | |||||
cpu.memWrite = mem_write; | |||||
while (running) { | |||||
Z80Execute(&cpu); | |||||
} | |||||
printf("and... %d!\n", mem[0x100]); | |||||
return 0; | |||||
} |
@@ -0,0 +1,4 @@ | |||||
; dummy code, to test emulator | |||||
ld a, 0x42 | |||||
ld (0x100), a | |||||
ret |