From 193e6e066cab331572ee13b40f3e7c791dc493b8 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Thu, 9 May 2019 12:58:41 -0400 Subject: [PATCH] Add tools/emul --- .gitmodules | 3 ++ doc/emulate.md | 15 ++++--- tools/emul/.gitignore | 2 + tools/emul/Makefile | 16 +++++++ tools/emul/README.md | 26 +++++++++++ tools/emul/bin2c.sh | 5 +++ tools/emul/libz80 | 1 + tools/emul/shell.c | 102 ++++++++++++++++++++++++++++++++++++++++++ tools/emul/shell_.asm | 46 +++++++++++++++++++ 9 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 .gitmodules create mode 100644 tools/emul/.gitignore create mode 100644 tools/emul/Makefile create mode 100644 tools/emul/README.md create mode 100755 tools/emul/bin2c.sh create mode 160000 tools/emul/libz80 create mode 100644 tools/emul/shell.c create mode 100644 tools/emul/shell_.asm diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..1a3fac8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tools/emul/libz80"] + path = tools/emul/libz80 + url = https://github.com/ggambetta/libz80.git diff --git a/doc/emulate.md b/doc/emulate.md index eee6d2e..bb04312 100644 --- a/doc/emulate.md +++ b/doc/emulate.md @@ -1,9 +1,13 @@ -# Running Collapse OS on an emulated RC2014 +# Running Collapse OS on an emulator -To give Collapse OS a whirl or to use emulation as a development tool, I -recommend using Alan Cox's [RC2014 emulator][rc2014-emul]. It runs Collapse OS -fine. One caveat, however, is that it requires a ROM image bigger than 8K, so -you have to pad the binary. +The quickest way to give Collapse OS a whirl is to use `tools/emul` which is +built around [libz80][libz80]. Everything is set up, you just have to run +`make`. + +To emulate something at a lower level, I recommend using Alan Cox's [RC2014 +emulator][rc2014-emul]. It runs Collapse OS fine but you have to write the +glue code yourself. One caveat, also, is that it requires a ROM image bigger +than 8K, so you have to pad the binary. A working Makefile for a project with a glue code being called `main.asm` could look like: @@ -27,4 +31,5 @@ look like: `CTRL+\` stops the emulation. +[libz80]: https://github.com/ggambetta/libz80 [rc2014-emul]: https://github.com/EtchedPixels/RC2014 diff --git a/tools/emul/.gitignore b/tools/emul/.gitignore new file mode 100644 index 0000000..a455559 --- /dev/null +++ b/tools/emul/.gitignore @@ -0,0 +1,2 @@ +/shell +/shell-kernel.h diff --git a/tools/emul/Makefile b/tools/emul/Makefile new file mode 100644 index 0000000..02c601a --- /dev/null +++ b/tools/emul/Makefile @@ -0,0 +1,16 @@ +.PHONY: all +all: shell + +shell-kernel.h: shell_.asm + scas -o - -I ../../parts/z80 $< | ./bin2c.sh SHELL_KERNEL | tee $@ > /dev/null + +shell: shell.c libz80/libz80.o shell-kernel.h + cc $< libz80/libz80.o -o $@ + +libz80/libz80.o: libz80/z80.c + make -C libz80/codegen opcodes + gcc -Wall -ansi -g -c -o libz80/libz80.o libz80/z80.c + +.PHONY: clean +clean: + rm shell shell-kernel.h diff --git a/tools/emul/README.md b/tools/emul/README.md new file mode 100644 index 0000000..37cfe2a --- /dev/null +++ b/tools/emul/README.md @@ -0,0 +1,26 @@ +# emul + +This is an emulator for a virtual machine that is suitable for running Collapse +OS. The goal of this machine is not to emulate real hardware, but rather to +serve as a development platform. What we do here is we emulate the z80 part, +the 64K memory space and then hook some fake I/Os to stdin, stdout and a small +storage device that is suitable for Collapse OS's filesystem to run on. + +Through that, it becomes easier to develop userspace applications for Collapse +OS. + +We don't try to emulate real hardware to ease the development of device drivers +because so far, I don't see the advantage of emulation versus running code on +the real thing. + +## Usage + +First, make sure that the `libz80` git submodule is checked out. If not, run +`git submodule init && git submodule update`. + +The Makefile in this folder has multiple targets that all use libz80 as its +core. For example, `make shell` will build `./shell`, a vanilla Collapse OS +shell. `make zasm` will build a `./zasm` executable, and so on. + +See documentation is corresponding source files for usage documentation of +each target. diff --git a/tools/emul/bin2c.sh b/tools/emul/bin2c.sh new file mode 100755 index 0000000..62f0c09 --- /dev/null +++ b/tools/emul/bin2c.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "unsigned char $1[] = { " +xxd -i - +echo " };" diff --git a/tools/emul/libz80 b/tools/emul/libz80 new file mode 160000 index 0000000..8a1f935 --- /dev/null +++ b/tools/emul/libz80 @@ -0,0 +1 @@ +Subproject commit 8a1f935daa3c288b68121051e8e45068684f80a4 diff --git a/tools/emul/shell.c b/tools/emul/shell.c new file mode 100644 index 0000000..eb51930 --- /dev/null +++ b/tools/emul/shell.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include "libz80/z80.h" +#include "shell-kernel.h" + +/* Collapse OS vanilla shell + * + * Memory layout: + * + * 0x0000 - 0x3fff: ROM code from shell.asm + * 0x4000 - 0x4fff: Kernel memory + * 0x5000 - 0xffff: Userspace + * + * I/O Ports: + * + * 0 - stdin / stdout + */ + +// in sync with shell.asm +#define RAMSTART 0x4000 +#define STDIO_PORT 0x00 +#define STDIN_ST_PORT 0x01 + +static Z80Context cpu; +static uint8_t mem[0xffff]; +static int running; + +static uint8_t io_read(int unused, uint16_t addr) +{ + addr &= 0xff; + if (addr == STDIO_PORT) { + uint8_t c = getchar(); + if (c == EOF) { + running = 0; + } + return c; + } else { + fprintf(stderr, "Out of bounds I/O read: %d\n", addr); + return 0; + } +} + +static void io_write(int unused, uint16_t addr, uint8_t val) +{ + addr &= 0xff; + if (addr == STDIO_PORT) { + if (val == 0x04) { // CTRL+D + running = 0; + } else { + putchar(val); + } + } else { + fprintf(stderr, "Out of bounds I/O write: %d / %d\n", addr, val); + } +} + +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() +{ + // Turn echo off: the shell takes care of its own echoing. + struct termios termInfo; + if (tcgetattr(0, &termInfo) == -1) { + printf("Can't setup terminal.\n"); + return 1; + } + termInfo.c_lflag &= ~ECHO; + termInfo.c_lflag &= ~ICANON; + tcsetattr(0, TCSAFLUSH, &termInfo); + + + // initialize memory + for (int i=0; i