Compare commits

..

7 Commits

Author SHA1 Message Date
Virgil Dupras
c0b7b45570 emul: fix backspace
it was broken in the move to ncurses.
2020-05-23 14:47:19 -04:00
Virgil Dupras
7f3e55cb51 emul: add live register stats in the corner 2020-05-23 14:42:36 -04:00
Virgil Dupras
08b0c56ff6 emul: run Collapse OS inside a limited, fixed window
This will allow us to implement AT-XY, paving the way to all sorts
of nice new things.
2020-05-23 14:23:03 -04:00
Virgil Dupras
1d4b75add2 emul: deduplicate some code 2020-05-23 10:08:40 -04:00
Virgil Dupras
93c6d150e2 emul: don't embed forth.bin in emul binaries
Read the contents of forth.bin at runtime. This allows us to get
rid of the bin2c tooling.
2020-05-23 09:54:26 -04:00
Virgil Dupras
8d3da4c0de emul: stop embedding blkfs in binaries
Instead, embed absolute path to blkfs. Having to rebuild the stage
binary at every change in blkfs is getting tedious.
2020-05-23 09:36:10 -04:00
Virgil Dupras
f884918d73 emul: use ncurses in /emul/forth
It doesn't change anything featurewise, but this change is in
preparation for the addition of an eventual AT-XY.
2020-05-23 08:36:55 -04:00
8 changed files with 152 additions and 185 deletions

View File

@ -1,6 +1,5 @@
TARGETS = forth stage
TARGETS = forth stage blkfs
OBJS = emul.o libz80/libz80.o
BIN2C = ../tools/bin2c
BLKPACK = ../tools/blkpack
BLKUNPACK = ../tools/blkunpack
@ -10,37 +9,29 @@ all: $(TARGETS)
$(BLKPACK):
$(MAKE) -C ../tools
.PHONY: $(BIN2C) $(BLKUNPACK)
$(BIN2C): $(BLKPACK)
.PHONY: $(BLKUNPACK)
$(BLKUNPACK): $(BLKPACK)
# not dependent on forth.bin to avoid circular deps.
forth-bin.h: $(BIN2C)
$(BIN2C) KERNEL < forth.bin > $@
stage: stage.c $(OBJS) forth-bin.h blkfs-bin.h
stage: stage.c $(OBJS)
$(CC) stage.c $(OBJS) -o $@
blkfs: $(BLKPACK)
$(BLKPACK) ../blk > $@
blkfs-bin.h: blkfs $(BIN2C)
$(BIN2C) BLKFS < blkfs > $@
forth: forth.c $(OBJS) forth-bin.h blkfs-bin.h
$(CC) forth.c $(OBJS) -o $@
forth: forth.c $(OBJS)
$(CC) forth.c $(OBJS) -lncurses -o $@
libz80/libz80.o: libz80/z80.c
$(MAKE) -C libz80/codegen opcodes
$(CC) -Wall -ansi -g -c -o libz80/libz80.o libz80/z80.c
emul.o: emul.c
$(CC) -c -o emul.o emul.c
$(CC) -DFBIN_PATH=\"`pwd`/forth.bin\" -DBLKFS_PATH=\"`pwd`/blkfs\" -c -o emul.o emul.c
.PHONY: updatebootstrap
updatebootstrap: stage xcomp.fs
./stage < xcomp.fs > forth.bin
updatebootstrap: stage xcomp.fs pack
./stage < xcomp.fs | tee forth.bin > /dev/null
.PHONY: pack
pack:
@ -52,5 +43,4 @@ unpack:
.PHONY: clean
clean:
rm -f $(TARGETS) emul.o *-bin.h stage{1,2}.bin blkfs
$(MAKE) -C ../tools clean
rm -f $(TARGETS) emul.o *-bin.h blkfs

View File

@ -3,6 +3,11 @@
This folder contains a couple of tools running under the [libz80][libz80]
emulator.
## Requirements
You need `ncurses` to build the `forth` executable. In debian-based distros,
it's `libncurses5-dev`.
## Not real hardware
In the few emulated apps described below, we don't try to emulate real hardware
@ -20,16 +25,25 @@ First, make sure that the `libz80` git submodule is checked out. If not, run
After that, you can run `make` and it builds the `forth` interpreter.
Run `./forth` to get the COllapse OS prompt. Type `0 LIST` for help.
## Usage
Run `./forth` to get the Collapse OS prompt. Type `0 LIST` for help.
The program is a curses interface with a limited, fixed size so that it can
provide a AT-XY interface (which is yet to implement).
## Problems?
If the libz80-wrapped zasm executable works badly (hangs, spew garbage, etc.),
If the `forth` executable works badly (hangs, spew garbage, etc.),
it's probably because you've broken your bootstrap binaries. They're easy to
mistakenly break. To verify if you've done that, look at your git status. If
`forth.bin` is modified, try resetting it and then run `make clean all`. Things
should go better afterwards.
A modified `blkfs` can also break things (although even with a completely broken
blkfs, you should still get to prompt), you might want to run `make pack` to
ensure that the `blkfs` file is in sync with the contents of the `blk/` folder.
If that doesn't work, there's also the nuclear option of `git reset --hard`
and `git clean -fxd`.

View File

@ -1,13 +1,26 @@
/* Common code between shell, zasm and runbin.
/* Common code between forth and stage binaries.
They all run on the same kind of virtual machine: A z80 CPU, 64K of RAM/ROM.
*/
#include <string.h>
#include "emul.h"
// Port for block reads. Write 2 bytes, MSB first, on that port and then
// read 1024 bytes from the DATA port.
#define BLK_PORT 0x03
#define BLKDATA_PORT 0x04
#ifndef BLKFS_PATH
#error BLKFS_PATH needed
#endif
#ifndef FBIN_PATH
#error FBIN_PATH needed
#endif
static Machine m;
static ushort traceval = 0;
static uint16_t blkid = 0;
static FILE *blkfp;
static uint8_t io_read(int unused, uint16_t addr)
{
@ -32,6 +45,23 @@ static void io_write(int unused, uint16_t addr, uint8_t val)
}
}
static void iowr_blk(uint8_t val)
{
blkid <<= 8;
blkid |= val;
fseek(blkfp, blkid*1024, SEEK_SET);
}
static uint8_t iord_blkdata()
{
return getc(blkfp);
}
static void iowr_blkdata(uint8_t val)
{
putc(val, blkfp);
}
static uint8_t mem_read(int unused, uint16_t addr)
{
return m.mem[addr];
@ -50,7 +80,26 @@ static void mem_write(int unused, uint16_t addr, uint8_t val)
Machine* emul_init()
{
fprintf(stderr, "Using blkfs %s\n", BLKFS_PATH);
blkfp = fopen(BLKFS_PATH, "r+");
if (!blkfp) {
fprintf(stderr, "Can't open\n");
return NULL;
}
// initialize memory
memset(m.mem, 0, 0x10000);
FILE *bfp = fopen(FBIN_PATH, "r");
if (!bfp) {
fprintf(stderr, "Can't open forth.bin\n");
return NULL;
}
int i = 0;
int c = getc(bfp);
while (c != EOF) {
m.mem[i++] = c;
c = getc(bfp);
}
fclose(bfp);
m.ramstart = 0;
m.minsp = 0xffff;
m.maxix = 0;
@ -63,9 +112,16 @@ Machine* emul_init()
m.cpu.memWrite = mem_write;
m.cpu.ioRead = io_read;
m.cpu.ioWrite = io_write;
m.iowr[BLK_PORT] = iowr_blk;
m.iord[BLKDATA_PORT] = iord_blkdata;
m.iowr[BLKDATA_PORT] = iowr_blkdata;
return &m;
}
void emul_deinit()
{
fclose(blkfp);
}
bool emul_step()
{
@ -117,6 +173,12 @@ void emul_memdump()
fclose(fp);
}
void emul_debugstr(char *s)
{
sprintf(s, "SP %04x (%04x) IX %04x (%04x)",
m.cpu.R1.wr.SP, m.minsp, m.cpu.R1.wr.IX, m.maxix);
}
void emul_printdebug()
{
fprintf(stderr, "Min SP: %04x\n", m.minsp);

View File

@ -29,9 +29,11 @@ typedef enum {
} Tristate;
Machine* emul_init();
void emul_deinit();
bool emul_step();
bool emul_steps(unsigned int steps);
void emul_loop();
void emul_trace(ushort addr);
void emul_memdump();
void emul_debugstr(char *s);
void emul_printdebug();

View File

@ -1,11 +1,12 @@
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <curses.h>
#include <termios.h>
#include "emul.h"
#include "forth-bin.h"
#include "blkfs-bin.h"
#define WCOLS 80
#define WLINES 32
// in sync with glue.asm
#define RAMSTART 0x900
#define STDIO_PORT 0x00
@ -18,13 +19,26 @@
#define BLKDATA_PORT 0x04
static FILE *fp;
static FILE *blkfp;
static int retcode = 0;
static uint16_t blkid = 0;
WINDOW *bw, *dw, *w;
void debug_panel()
{
char buf[30];
emul_debugstr(buf);
mvwaddnstr(dw, 0, 0, buf, 30);
wrefresh(dw);
}
static uint8_t iord_stdio()
{
int c = getc(fp);
int c;
if (fp != NULL) {
c = getc(fp);
} else {
debug_panel();
c = wgetch(w);
}
if (c == EOF) {
c = 4; // ASCII EOT
}
@ -33,7 +47,16 @@ static uint8_t iord_stdio()
static void iowr_stdio(uint8_t val)
{
putchar(val);
if (fp != NULL) {
putchar(val);
} else {
if (val >= 0x20 || val == '\n') {
wechochar(w, val);
} else if (val == 0x08) {
int y, x; getyx(w, y, x);
wmove(w, y, x-1);
}
}
}
static void iowr_ret(uint8_t val)
@ -41,95 +64,46 @@ static void iowr_ret(uint8_t val)
retcode = val;
}
static void iowr_blk(uint8_t val)
{
blkid <<= 8;
blkid |= val;
if (blkfp != NULL) {
fseek(blkfp, blkid*1024, SEEK_SET);
}
}
static uint8_t iord_blkdata()
{
uint8_t res = 0;
if (blkfp != NULL) {
int c = getc(blkfp);
if (c != EOF) {
res = c;
}
}
return res;
}
static void iowr_blkdata(uint8_t val)
{
if (blkfp != NULL) {
putc(val, blkfp);
}
}
int main(int argc, char *argv[])
{
bool tty = false;
struct termios termInfo;
Machine *m = emul_init();
if (m == NULL) {
return 1;
}
m->ramstart = RAMSTART;
m->iord[STDIO_PORT] = iord_stdio;
m->iowr[STDIO_PORT] = iowr_stdio;
m->iowr[RET_PORT] = iowr_ret;
w = NULL;
if (argc == 2) {
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Can't open %s\n", argv[1]);
return 1;
}
while (emul_step());
fclose(fp);
} else if (argc == 1) {
fp = stdin;
tty = isatty(fileno(stdin));
if (tty) {
// Turn echo off: the shell takes care of its own echoing.
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);
fp = NULL;
initscr(); cbreak(); noecho(); nl(); clear();
// border window
bw = newwin(WLINES+2, WCOLS+2, 0, 0);
wborder(bw, 0, 0, 0, 0, 0, 0, 0, 0);
wrefresh(bw);
// debug panel
dw = newwin(1, 30, LINES-1, COLS-30);
w = newwin(WLINES, WCOLS, 1, 1);
scrollok(w, 1);
while (emul_steps(1000)) {
debug_panel();
}
nocbreak(); echo(); delwin(w); delwin(bw); delwin(dw); endwin();
printf("\nDone!\n");
emul_printdebug();
} else {
fprintf(stderr, "Usage: ./forth [filename]\n");
return 1;
retcode = 1;
}
blkfp = fopen("blkfs", "r+");
if (blkfp) {
fprintf(stderr, "Using blkfs file\n");
} else {
blkfp = fmemopen((char*)BLKFS, sizeof(BLKFS), "r");
fprintf(stderr, "Using in-memory read-only blkfs\n");
}
Machine *m = emul_init();
m->ramstart = RAMSTART;
m->iord[STDIO_PORT] = iord_stdio;
m->iowr[STDIO_PORT] = iowr_stdio;
m->iowr[RET_PORT] = iowr_ret;
m->iowr[BLK_PORT] = iowr_blk;
m->iord[BLKDATA_PORT] = iord_blkdata;
m->iowr[BLKDATA_PORT] = iowr_blkdata;
// initialize memory
for (int i=0; i<sizeof(KERNEL); i++) {
m->mem[i] = KERNEL[i];
}
// Run!
while (emul_step());
if (tty) {
printf("\nDone!\n");
termInfo.c_lflag |= ECHO;
termInfo.c_lflag |= ICANON;
tcsetattr(0, TCSAFLUSH, &termInfo);
emul_printdebug();
}
if (blkfp != NULL) {
fclose(blkfp);
}
fclose(fp);
emul_deinit();
return retcode;
}

View File

@ -2,8 +2,6 @@
#include <stdio.h>
#include <unistd.h>
#include "emul.h"
#include "forth-bin.h"
#include "blkfs-bin.h"
/* Staging binaries
@ -25,10 +23,6 @@ trouble of compiling defs to binary.
// To know which part of RAM to dump, we listen to port 2, which at the end of
// its compilation process, spits its HERE addr to port 2 (MSB first)
#define HERE_PORT 0x02
// Port for block reads. Write 2 bytes, MSB first, on that port and then
// read 1024 bytes from the DATA port.
#define BLK_PORT 0x03
#define BLKDATA_PORT 0x04
static int running;
// We support double-pokes, that is, a first poke to tell where to start the
@ -36,8 +30,6 @@ static int running;
// then ending HERE and we start at sizeof(KERNEL).
static uint16_t start_here = 0;
static uint16_t end_here = 0;
static uint16_t blkid = 0;
static unsigned int blkpos = 0;
static uint8_t iord_stdio()
{
@ -61,32 +53,16 @@ static void iowr_here(uint8_t val)
end_here |= val;
}
static void iowr_blk(uint8_t val)
{
blkid <<= 8;
blkid |= val;
blkpos = blkid * 1024;
}
static uint8_t iord_blkdata()
{
return BLKFS[blkpos++];
}
int main(int argc, char *argv[])
{
Machine *m = emul_init();
if (m == NULL) {
return 1;
}
m->ramstart = RAMSTART;
m->iord[STDIO_PORT] = iord_stdio;
m->iowr[STDIO_PORT] = iowr_stdio;
m->iowr[HERE_PORT] = iowr_here;
m->iowr[BLK_PORT] = iowr_blk;
m->iord[BLKDATA_PORT] = iord_blkdata;
// initialize memory
for (int i=0; i<sizeof(KERNEL); i++) {
m->mem[i] = KERNEL[i];
}
// Run!
running = 1;
@ -96,6 +72,7 @@ int main(int argc, char *argv[])
for (int i=start_here; i<end_here; i++) {
putchar(m->mem[i]);
}
emul_deinit();
emul_printdebug();
return 0;
}

View File

@ -2,13 +2,12 @@ MEMDUMP_TGT = memdump
UPLOAD_TGT = upload
TTYSAFE_TGT = ttysafe
PINGPONG_TGT = pingpong
BIN2C_TGT = bin2c
EXEC_TGT = exec
BLKPACK_TGT = blkpack
BLKUNPACK_TGT = blkunpack
BLKUP_TGT = blkup
TARGETS = $(MEMDUMP_TGT) $(UPLOAD_TGT) \
$(TTYSAFE_TGT) $(PINGPONG_TGT) $(BIN2C_TGT) $(EXEC_TGT) $(BLKPACK_TGT) \
$(TTYSAFE_TGT) $(PINGPONG_TGT) $(EXEC_TGT) $(BLKPACK_TGT) \
$(BLKUNPACK_TGT) $(BLKUP_TGT)
OBJS = common.o
@ -22,7 +21,6 @@ $(MEMDUMP_TGT): $(MEMDUMP_TGT).c
$(UPLOAD_TGT): $(UPLOAD_TGT).c
$(TTYSAFE_TGT): $(TTYSAFE_TGT).c
$(PINGPONG_TGT): $(PINGPONG_TGT).c
$(BIN2C_TGT): $(BIN2C_TGT).c
$(EXEC_TGT): $(EXEC_TGT).c
$(BLKPACK_TGT): $(BLKPACK_TGT).c
$(BLKUNPACK_TGT): $(BLKUNPACK_TGT).c

View File

@ -1,50 +0,0 @@
/*
* Copyright (c) 2020 Byron Grobe
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define BUFSZ 32
static const char intro[] = "static const unsigned char %s[] = {\n ";
int main(int argc, char **argv) {
int n;
int col = 0;
uint8_t buf[BUFSZ];
if (argc < 2) {
fprintf(stderr, "Specify a name for the data structure...\n");
return 1;
}
printf(intro, argv[1]);
while(!feof(stdin)) {
n = fread(buf, 1, BUFSZ, stdin);
for(int i = 0; i < n; ++i) {
if (col+4 >= 76) {
printf("\n ");
col = 0;
}
printf("0x%.2x, ", buf[i]);
col += 6;
}
}
printf("};\n");
}