Add tools/emul
This commit is contained in:
parent
02808572e6
commit
193e6e066c
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "tools/emul/libz80"]
|
||||
path = tools/emul/libz80
|
||||
url = https://github.com/ggambetta/libz80.git
|
@ -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
|
||||
|
2
tools/emul/.gitignore
vendored
Normal file
2
tools/emul/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/shell
|
||||
/shell-kernel.h
|
16
tools/emul/Makefile
Normal file
16
tools/emul/Makefile
Normal file
@ -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
|
26
tools/emul/README.md
Normal file
26
tools/emul/README.md
Normal file
@ -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.
|
5
tools/emul/bin2c.sh
Executable file
5
tools/emul/bin2c.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "unsigned char $1[] = { "
|
||||
xxd -i -
|
||||
echo " };"
|
1
tools/emul/libz80
Submodule
1
tools/emul/libz80
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 8a1f935daa3c288b68121051e8e45068684f80a4
|
102
tools/emul/shell.c
Normal file
102
tools/emul/shell.c
Normal file
@ -0,0 +1,102 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
#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<sizeof(SHELL_KERNEL); i++) {
|
||||
mem[i] = SHELL_KERNEL[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("Done!\n");
|
||||
termInfo.c_lflag |= ECHO;
|
||||
termInfo.c_lflag |= ICANON;
|
||||
tcsetattr(0, TCSAFLUSH, &termInfo);
|
||||
return 0;
|
||||
}
|
46
tools/emul/shell_.asm
Normal file
46
tools/emul/shell_.asm
Normal file
@ -0,0 +1,46 @@
|
||||
; named shell_.asm to avoid infinite include loop.
|
||||
RAMSTART .equ 0x4000
|
||||
RAMEND .equ 0x5000
|
||||
STDIO_PORT .equ 0x00
|
||||
|
||||
jr init
|
||||
|
||||
init:
|
||||
di
|
||||
; setup stack
|
||||
ld hl, RAMEND
|
||||
ld sp, hl
|
||||
call shellInit
|
||||
jp shellLoop
|
||||
|
||||
#include "core.asm"
|
||||
.define STDIO_GETC call emulGetC
|
||||
.define STDIO_PUTC call emulPutC
|
||||
STDIO_RAMSTART .equ RAMEND
|
||||
#include "stdio.asm"
|
||||
|
||||
BLOCKDEV_RAMSTART .equ STDIO_RAMEND
|
||||
BLOCKDEV_COUNT .equ 1
|
||||
#include "blockdev.asm"
|
||||
; List of devices
|
||||
.dw emulGetC, emulPutC, 0, 0
|
||||
|
||||
#include "blockdev_cmds.asm"
|
||||
|
||||
SHELL_RAMSTART .equ BLOCKDEV_RAMEND
|
||||
.define SHELL_IO_GETC call blkGetCW
|
||||
.define SHELL_IO_PUTC call blkPutC
|
||||
SHELL_EXTRA_CMD_COUNT .equ 2
|
||||
#include "shell.asm"
|
||||
.dw blkBselCmd, blkSeekCmd
|
||||
|
||||
emulGetC:
|
||||
; Blocks until a char is returned
|
||||
in a, (STDIO_PORT)
|
||||
cp a ; ensure Z
|
||||
ret
|
||||
|
||||
emulPutC:
|
||||
out (STDIO_PORT), a
|
||||
ret
|
||||
|
Loading…
Reference in New Issue
Block a user