@@ -1,2 +1,3 @@ | |||
/forth | |||
/forth.bin | |||
/pcat |
@@ -1,4 +1,4 @@ | |||
TARGETS = forth | |||
TARGETS = forth pcat | |||
OBJS = cpu.o | |||
CDIR = ../../cvm | |||
STAGE = $(CDIR)/stage | |||
@@ -13,6 +13,9 @@ forth: forth.c forth.bin $(OBJS) $(BLKFS) | |||
forth.bin: xcomp.fs $(STAGE) | |||
$(CDIR)/stage < xcomp.fs > $@ | |||
pcat: pcat.c $(OBJS) $(BLKFS) | |||
$(CC) -DBLKFS_PATH=\"`pwd`/$(BLKFS)\" pcat.c $(OBJS) -lncurses -o $@ | |||
$(BLKFS): $(STAGE) | |||
$(STAGE): | |||
@@ -1,8 +1,28 @@ | |||
# 8086 emulator | |||
This is a work in progress. The goal is to have something lighter than QEMU to | |||
run Collapse OS on and also something that is easier to plug with my stuff. | |||
Something I could run without BIOS (have my own studs for INT 10 and INT 13). | |||
This folder contains emulator for 8086 binaries of Collapse OS. The bulk of | |||
it is a fork of Fake86 by Mike Chambers. | |||
My first try was with 8086tiny, but this code is messy. Fake86 is a bit bigger, | |||
but also cleaner. | |||
`forth` is an imaginary hardware used for userspace development and testing. | |||
This machine has an imaginary interrupt API and does not conform to PC/AT. | |||
`pcat` is a very simple PC/AT emulator. The BIOS interrupt hooks implemented in | |||
it only cover Collapse OS' own needs. | |||
## Requirements | |||
You need `ncurses` to build the `forth` executable. In debian-based distros, | |||
it's `libncurses5-dev`. | |||
## Build | |||
Run `make` and it builds the `forth` and `pcat` interpreters. | |||
## Usage | |||
The `./forth` executable here works like the one in `/cvm`, except that it runs | |||
under an emulated 8086 machine instead of running natively. Refer to | |||
`/cvm/README.md` for details. | |||
`pcat` needs to be suppied a path to a floppy disk image with a proper MBR. | |||
`disk.bin` provided by the `pcat` recipe is sufficient. |
@@ -3399,8 +3399,8 @@ int exec86(int execloops) { | |||
return 1; | |||
} | |||
void reset86() { | |||
void reset86(uint16_t startip) { | |||
segregs[regcs] = 0; | |||
ip = 0; | |||
ip = startip; | |||
hltstate = 0; | |||
} |
@@ -52,4 +52,4 @@ void writew86 (uint32_t addr32, uint16_t value); | |||
uint8_t read86 (uint32_t addr32); | |||
uint16_t readw86 (uint32_t addr32); | |||
int exec86(int execloops); // returns 0 if halted | |||
void reset86(); | |||
void reset86(uint16_t startip); |
@@ -93,7 +93,7 @@ int main(int argc, char *argv[]) | |||
INTHOOKS[4] = int4; | |||
INTHOOKS[5] = int5; | |||
INTHOOKS[6] = int6; | |||
reset86(); | |||
reset86(0); | |||
fprintf(stderr, "Using blkfs %s\n", BLKFS_PATH); | |||
blkfp = fopen(BLKFS_PATH, "r+"); | |||
if (!blkfp) { | |||
@@ -0,0 +1,127 @@ | |||
#include <stdint.h> | |||
#include <stdio.h> | |||
#include <unistd.h> | |||
#include <curses.h> | |||
#include "cpu.h" | |||
#define WCOLS 80 | |||
#define WLINES 25 | |||
#define SEC_PER_TRK 0x3f | |||
#define TRK_PER_HEAD 0xff | |||
extern uint8_t byteregtable[8]; | |||
extern union _bytewordregs_ regs; | |||
extern uint16_t segregs[4]; | |||
extern INTHOOK INTHOOKS[0x100]; | |||
static FILE *fp; | |||
WINDOW *bw, *dw, *w; | |||
static void int10() { | |||
uint8_t cmd = regs.byteregs[regah]; | |||
uint8_t al = regs.byteregs[regal]; | |||
uint16_t dx = regs.wordregs[regdx]; | |||
switch (cmd) { | |||
case 0x02: // at-xy | |||
wmove(w, dx&0xff, dx>>8); | |||
break; | |||
case 0x0e: // emit | |||
if (al >= 0x20 || al == '\n') { | |||
wechochar(w, al); | |||
} else if (al == 0x08) { | |||
int y, x; getyx(w, y, x); | |||
wmove(w, y, x-1); | |||
} | |||
break; | |||
} | |||
} | |||
static void int13() { | |||
uint8_t cmd = regs.byteregs[regah]; | |||
uint8_t al = regs.byteregs[regal]; | |||
uint8_t cl = regs.byteregs[regcl]; | |||
uint8_t ch = regs.byteregs[regch]; | |||
uint8_t dh = regs.byteregs[regdh]; | |||
uint16_t bx = regs.wordregs[regbx]; | |||
switch (cmd) { | |||
case 0x02: // read disk sector(s) | |||
case 0x03: // write disk sector(s) | |||
// CL = sector number (1-based), AL = sector count | |||
// DH = head number, CH = track number | |||
// ES:BX = dest addr | |||
fseek(fp, ((((dh*TRK_PER_HEAD)+ch)*SEC_PER_TRK)+cl-1)*512, SEEK_SET); | |||
for (int i=0; i<(al*512); i++) { | |||
if (cmd == 0x03) { // write | |||
fputc(getmem8(segregs[reges], bx+i), fp); | |||
} else { // read | |||
putmem8(segregs[reges], bx+i, fgetc(fp)); | |||
} | |||
} | |||
break; | |||
case 0x08: // poll sectors per track / per head | |||
// we just report a lot of them | |||
regs.wordregs[regcx] = SEC_PER_TRK; | |||
regs.byteregs[regdh] = TRK_PER_HEAD-1; | |||
break; | |||
} | |||
} | |||
static void int16() { | |||
int c; | |||
// debug_panel(); | |||
c = wgetch(w); | |||
regs.byteregs[regal] = c; | |||
} | |||
static void usage() | |||
{ | |||
fprintf(stderr, "Usage: ./pcat /path/to/fd\n"); | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
if (argc < 2) { | |||
usage(); | |||
return 1; | |||
} | |||
INTHOOKS[0x10] = int10; | |||
INTHOOKS[0x13] = int13; | |||
INTHOOKS[0x16] = int16; | |||
reset86(0x7c00); | |||
// initialize memory | |||
fp = fopen(argv[1], "r"); | |||
if (!fp) { | |||
fprintf(stderr, "Can't open %s\n", argv[1]); | |||
return 1; | |||
} | |||
// read MBR into RAM | |||
for (int i=0; i<512; i++) { | |||
int c = getc(fp); | |||
if (c != EOF) { | |||
write86(0x7c00+i, c); | |||
} | |||
} | |||
uint16_t magic = readw86(0x7dfe); | |||
if (magic != 0xaa55) { | |||
fprintf(stderr, "Invalid MBR magic %x\n", magic); | |||
return 1; | |||
} | |||
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 (exec86(100)) { | |||
//debug_panel(); | |||
} | |||
nocbreak(); echo(); delwin(w); delwin(bw); delwin(dw); endwin(); | |||
printf("\nDone!\n"); | |||
//emul_printdebug(); | |||
fclose(fp); | |||
return 0; | |||
} |
@@ -3,6 +3,7 @@ BASE = ../.. | |||
CDIR = $(BASE)/cvm | |||
BLKPACK = $(BASE)/tools/blkpack | |||
STAGE = $(CDIR)/stage | |||
EMUL = $(BASE)/emul/8086/pcat | |||
.PHONY: all | |||
all: $(TARGET) | |||
@@ -10,7 +11,7 @@ os.bin: xcomp.fs $(STAGE) blkfs | |||
$(STAGE) blkfs < xcomp.fs > $@ | |||
$(BLKPACK): | |||
$(MAKE) -C ../tools | |||
$(MAKE) -C $(BASE)/tools | |||
blkfs: $(BLKPACK) | |||
$(BLKPACK) $(BASE)/blk blk > $@ | |||
@@ -25,9 +26,12 @@ $(TARGET): mbr.bin os.bin | |||
cat mbr.bin os.bin > $@ | |||
dd if=blkfs of=$@ bs=512 seek=16 | |||
$(EMUL): | |||
$(MAKE) -C $(BASE)/emul/8086 | |||
.PHONY: emul | |||
emul: $(TARGET) | |||
qemu-system-i386 -drive file=$(TARGET),if=floppy,format=raw | |||
emul: $(TARGET) $(EMUL) | |||
$(EMUL) $(TARGET) | |||
.PHONY: clean | |||
clean: | |||
@@ -12,7 +12,6 @@ is bootable on a modern PC-compatible machine. | |||
* A modern PC-compatible machine that can boot from a USB drive. | |||
* A USB drive | |||
* qemu for emulation | |||
## Build the binary | |||
@@ -30,7 +29,12 @@ can run `VE`. | |||
## Emulation | |||
You can run the built binary in qemu using `make emul`. | |||
You can run the built binary in Collapse OS' 8086 emulator using `make emul`. | |||
The 8086 emulator is barbone. If you prefer to use it on a more realistic | |||
setting, use QEMU. The command is: | |||
qemu-system-i386 -drive file=disk.bin,if=floppy,format=raw | |||
## Running on a modern PC | |||
@@ -2,7 +2,7 @@ | |||
( AH=read sectors, AL=1 sector, BX=dest, | |||
CH=trackno CL=secno DH=head DL=drive ) | |||
FDSPT C@ /MOD ( AX BX sec trk ) | |||
FDHEADS C@ /MOD ( AX BX sec head trk ) | |||
FDHEADS C@ /MOD SWAP ( AX BX sec head trk ) | |||
8 LSHIFT ROT OR 1+ ( AX BX head CX ) | |||
SWAP 8 LSHIFT 0x03 C@ ( boot drive ) OR ( AX BX CX DX ) | |||
13H 2DROP 2DROP | |||