@@ -1,2 +1,3 @@ | |||||
/forth | /forth | ||||
/forth.bin | /forth.bin | ||||
/pcat |
@@ -1,4 +1,4 @@ | |||||
TARGETS = forth | |||||
TARGETS = forth pcat | |||||
OBJS = cpu.o | OBJS = cpu.o | ||||
CDIR = ../../cvm | CDIR = ../../cvm | ||||
STAGE = $(CDIR)/stage | STAGE = $(CDIR)/stage | ||||
@@ -13,6 +13,9 @@ forth: forth.c forth.bin $(OBJS) $(BLKFS) | |||||
forth.bin: xcomp.fs $(STAGE) | forth.bin: xcomp.fs $(STAGE) | ||||
$(CDIR)/stage < xcomp.fs > $@ | $(CDIR)/stage < xcomp.fs > $@ | ||||
pcat: pcat.c $(OBJS) $(BLKFS) | |||||
$(CC) -DBLKFS_PATH=\"`pwd`/$(BLKFS)\" pcat.c $(OBJS) -lncurses -o $@ | |||||
$(BLKFS): $(STAGE) | $(BLKFS): $(STAGE) | ||||
$(STAGE): | $(STAGE): | ||||
@@ -1,8 +1,28 @@ | |||||
# 8086 emulator | # 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; | return 1; | ||||
} | } | ||||
void reset86() { | |||||
void reset86(uint16_t startip) { | |||||
segregs[regcs] = 0; | segregs[regcs] = 0; | ||||
ip = 0; | |||||
ip = startip; | |||||
hltstate = 0; | hltstate = 0; | ||||
} | } |
@@ -52,4 +52,4 @@ void writew86 (uint32_t addr32, uint16_t value); | |||||
uint8_t read86 (uint32_t addr32); | uint8_t read86 (uint32_t addr32); | ||||
uint16_t readw86 (uint32_t addr32); | uint16_t readw86 (uint32_t addr32); | ||||
int exec86(int execloops); // returns 0 if halted | 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[4] = int4; | ||||
INTHOOKS[5] = int5; | INTHOOKS[5] = int5; | ||||
INTHOOKS[6] = int6; | INTHOOKS[6] = int6; | ||||
reset86(); | |||||
reset86(0); | |||||
fprintf(stderr, "Using blkfs %s\n", BLKFS_PATH); | fprintf(stderr, "Using blkfs %s\n", BLKFS_PATH); | ||||
blkfp = fopen(BLKFS_PATH, "r+"); | blkfp = fopen(BLKFS_PATH, "r+"); | ||||
if (!blkfp) { | 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 | CDIR = $(BASE)/cvm | ||||
BLKPACK = $(BASE)/tools/blkpack | BLKPACK = $(BASE)/tools/blkpack | ||||
STAGE = $(CDIR)/stage | STAGE = $(CDIR)/stage | ||||
EMUL = $(BASE)/emul/8086/pcat | |||||
.PHONY: all | .PHONY: all | ||||
all: $(TARGET) | all: $(TARGET) | ||||
@@ -10,7 +11,7 @@ os.bin: xcomp.fs $(STAGE) blkfs | |||||
$(STAGE) blkfs < xcomp.fs > $@ | $(STAGE) blkfs < xcomp.fs > $@ | ||||
$(BLKPACK): | $(BLKPACK): | ||||
$(MAKE) -C ../tools | |||||
$(MAKE) -C $(BASE)/tools | |||||
blkfs: $(BLKPACK) | blkfs: $(BLKPACK) | ||||
$(BLKPACK) $(BASE)/blk blk > $@ | $(BLKPACK) $(BASE)/blk blk > $@ | ||||
@@ -25,9 +26,12 @@ $(TARGET): mbr.bin os.bin | |||||
cat mbr.bin os.bin > $@ | cat mbr.bin os.bin > $@ | ||||
dd if=blkfs of=$@ bs=512 seek=16 | dd if=blkfs of=$@ bs=512 seek=16 | ||||
$(EMUL): | |||||
$(MAKE) -C $(BASE)/emul/8086 | |||||
.PHONY: emul | .PHONY: emul | ||||
emul: $(TARGET) | |||||
qemu-system-i386 -drive file=$(TARGET),if=floppy,format=raw | |||||
emul: $(TARGET) $(EMUL) | |||||
$(EMUL) $(TARGET) | |||||
.PHONY: clean | .PHONY: clean | ||||
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 modern PC-compatible machine that can boot from a USB drive. | ||||
* A USB drive | * A USB drive | ||||
* qemu for emulation | |||||
## Build the binary | ## Build the binary | ||||
@@ -30,7 +29,12 @@ can run `VE`. | |||||
## Emulation | ## 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 | ## Running on a modern PC | ||||
@@ -2,7 +2,7 @@ | |||||
( AH=read sectors, AL=1 sector, BX=dest, | ( AH=read sectors, AL=1 sector, BX=dest, | ||||
CH=trackno CL=secno DH=head DL=drive ) | CH=trackno CL=secno DH=head DL=drive ) | ||||
FDSPT C@ /MOD ( AX BX sec trk ) | 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 ) | 8 LSHIFT ROT OR 1+ ( AX BX head CX ) | ||||
SWAP 8 LSHIFT 0x03 C@ ( boot drive ) OR ( AX BX CX DX ) | SWAP 8 LSHIFT 0x03 C@ ( boot drive ) OR ( AX BX CX DX ) | ||||
13H 2DROP 2DROP | 13H 2DROP 2DROP | ||||