Dir cleanup

This commit is contained in:
2025-10-01 23:44:19 +02:00
parent bf9a88805f
commit 1b2237f2f7
39 changed files with 84 additions and 74 deletions

71
src/Makefile Normal file
View File

@@ -0,0 +1,71 @@
%.bin: %.asm
nasm $< -o $@
crc16.s: crc16.c
ia16-elf-gcc -S -Os -o crc16.s crc16.c
crc16.bin: crc16.s
ia16-elf-gcc -o crc16.bin -Os -nostdlib crc16.s
CC = ia16-elf-gcc
CXX = ia16-elf-gcc
LD = ia16-elf-gcc
CXXFLAGS = -mregparmcall -ffunction-sections -Os -flto
CFLAGS = -mregparmcall -ffunction-sections -Os -flto
LDFLAGS = -mregparmcall -Wl,--gc-sections -Os -nostdlib -flto
%.elf:
$(LD) $(LDFLAGS) $(CPPFLAGS) -o $@ $^
%.com: %.elf
ia16-elf-objcopy -O binary $< $@
bootsectors = fat12boot.bin wozmon.bin
$(bootsectors):
ia16-elf-objcopy -O binary $< $@
fat12boot.elf: fat12boot.o fat12.o bootsect.S
fat12boot.elf: LDFLAGS += -T bootsect.ld
fat12boot.bin: fat12boot.elf
stdlib.o: CFLAGS := $(filter-out -flto, $(CFLAGS))
polmon.elf: LDFLAGS += -T flat1000.ld
polmon.elf: polmon.o stdlib.o
polio.elf: LDFLAGS += -T flat0600.ld
polio.elf: polio-main.o polio.s fat12.o paracomm.o stdlib.o
wozmon.o: polmon.cc
wozmon.o: CPPFLAGS = -DWOZMON=1
wozmon.o:
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
wozmon.elf: wozmon.o bootsect.S
wozmon.elf: LDFLAGS += -T bootsect.ld
wozmon.elf: CPPFLAGS += -DNOBPB
wozmon.bin: wozmon.elf
paracli.elf: LDFLAGS += -T doscom.ld
paracli.elf: paracli.s paracomm.o
dos-programs = mushroom.elf hello.elf
$(dos-programs): LDFLAGS += -T doscom.ld
mushroom.elf: mushroom.s
hello.elf: hello.o stdlib.o crt0.s
polos.img: fat12boot.bin polmon.com polio.com mushroom.com hello.com
dd if=/dev/zero of=$@ bs=512 count=720
mformat -i $@ -t 40 -h 2 -s 9
mcopy -i $@ polio.com ::/
mcopy -i $@ polmon.com ::/
mcopy -i $@ mushroom.com ::/
mcopy -i $@ hello.com ::/
dd if=fat12boot.bin of=$@ conv=notrunc
.PHONY: clean
clean: ## Remove generated files
rm -rf *.bin *.elf *.o *.com polos.img

11
src/add.asm Normal file
View File

@@ -0,0 +1,11 @@
BITS 16
CPU 8086
_start:
mov bp, sp
mov si, [bp+6]
mov ax, [si]
mov si, [bp+4]
mov dx, [si]
sub ax, dx
retf 4

29
src/bootsect.S Normal file
View File

@@ -0,0 +1,29 @@
.arch i8086,jumps
.code16
.section .init
.global _start
_start:
#ifndef NOBPB
jmp l0
nop
.ascii "IBM 3.1"
bpb_bytespersect: .word 512
bpb_sectspercluster: .byte 2
bpb_reservedsects: .word 1
bbp_fats: .byte 2
bpb_rootentries: .word 112
bpb_localsectors: .word 720
bpb_mediadescr: .byte 0xfd
bpb_sectsperfat: .word 2
l0:
#endif // NOBPB
cli
xor %ax, %ax
mov %ax, %ds
mov %ax, %ss
mov $0x7c00, %sp
sti
jmp main

25
src/bootsect.ld Normal file
View File

@@ -0,0 +1,25 @@
SECTIONS {
. = 0x7c00;
.text : {
KEEP(*(.init))
*(.text*)
}
.rodata : {
*(.rodata*)
}
.bss (NOLOAD) : {
* (.bss)
}
.data (NOLOAD) : {
*(.data)
}
. = 0x7dfe;
.magic : {
SHORT(0xAA55);
}
}

35
src/call.asm Normal file
View File

@@ -0,0 +1,35 @@
BITS 16
CPU 8086
org 0x7300
PARAMS equ 0x7380
; dw 0: target offset
; dw 2: target segment
; dw 4: number of params
; dw 6: param 0
; ...
_start:
push bx
push si
push di
push bp
mov si, PARAMS
mov bx, 0x6
xor cx, cx
a1:
cmp cx, [es:si+4]
jge a2
lea ax, [es:si+bx]
push ax
add bl, 2
inc cl
jmp a1
a2:
call far [es:si]
pop bp
pop di
pop si
pop bx
ret

22
src/copy.asm Normal file
View File

@@ -0,0 +1,22 @@
BITS 16
CPU 8086
main:
mov bp, sp
mov si, [bp+4]
mov cx, [si]
mov si, [bp+6]
mov di, [si]
mov si, [bp+8]
mov si, [si]
push ds
push es
mov ax, cs
mov ds, ax
mov es, ax
cld
rep movsw
pop es
pop ds
retf 6

33
src/crc16.c Normal file
View File

@@ -0,0 +1,33 @@
#include <stdint.h>
#define kDataAddr ((uint8_t*)0x0000)
uint16_t crc16(int data_len) {
const uint8_t* data = kDataAddr;
uint16_t crc = 0xFFFF;
for (unsigned int i = 0; i < data_len; ++i) {
uint16_t dbyte = data[i];
crc ^= dbyte << 8;
for (unsigned char j = 0; j < 8; ++j) {
uint16_t mix = crc & 0x8000;
crc = (crc << 1);
if (mix)
crc = crc ^ 0x1021;
}
}
return crc;
}
asm(".global main \n"
"main: \n"
" push %bp \n"
" mov %sp, %bp \n"
" mov 8(%bp), %si \n"
" push (%si) \n"
" call crc16 \n"
" add $0x2, %sp \n"
" mov 6(%bp), %di \n"
" mov %ax, (%di) \n"
" pop %bp \n"
" lret $4 \n");

57
src/crc16.s Normal file
View File

@@ -0,0 +1,57 @@
.arch i8086,jumps
.code16
.att_syntax prefix
#NO_APP
#APP
.section .text.init
.global _start
_start:
.section .text.main
.global main
main:
push %bp
mov %sp, %bp
mov 8(%bp), %si
push (%si)
call crc16
add $0x2, %sp
mov 6(%bp), %di
mov %ax, (%di)
pop %bp
lret $4
#NO_APP
.text
.global crc16
.type crc16, @function
crc16:
pushw %bp
movw %sp, %bp
xorw %bx, %bx
movw $-1, %ax
.L2:
cmpw 4(%bp), %bx
jne .L5
popw %bp
ret
.L5:
movb %cs:(%bx), %dl
movb $8, %cl
shlw %cl, %dx
xorw %dx, %ax
movb %cl, %dl
.L4:
movw %ax, %cx
shlw $1, %cx
testw %ax, %ax
xchgw %ax, %cx
jge .L3
xorw $4129, %ax
.L3:
decb %dl
jne .L4
incw %bx
jmp .L2
.size crc16, .-crc16
.ident "GCC: (GNU) 6.3.0"

8
src/crt0.s Normal file
View File

@@ -0,0 +1,8 @@
.arch i8086,jumps
.code16
.intel_syntax noprefix
.section .init
.global _start
_start:
jmp main

66
src/debug.asm Normal file
View File

@@ -0,0 +1,66 @@
BITS 16
CPU 8086
org 0x7000
_start:
jmp main
hexdigits:
db "0123456789abcdef"
putc:
push bx
push bp
mov ah, 0x0e
xor bh, bh
int 0x10
pop bp
pop bx
ret
printnibble:
push si
push bx
mov si, hexdigits
xor bh, bh
mov bl, al
and bl, 0xf
mov al, cs:[si+bx]
call putc
pop bx
pop si
ret
printi8:
push bx
mov bl, al
mov cl, 4
shr al, cl
call printnibble
mov al, bl
call printnibble
pop bx
ret
printi16:
push bx
mov bx, ax
mov al, ah
call printi8
mov al, bl
call printi8
pop bx
ret
main:
mov bp, sp
mov si, [bp+4]
mov si, [si]
mov ax, [si]
call printi16
mov al, 0x0d
call putc
mov al, 0x0a
call putc
retf 2

20
src/doscom.ld Normal file
View File

@@ -0,0 +1,20 @@
SECTIONS {
. = 0x100;
.text : {
KEEP(*(.init))
*(.text*)
}
.rodata : {
*(.rodata*)
}
.bss (NOLOAD) : {
* (.bss)
}
.data (NOLOAD) : {
*(.data)
}
}

25
src/dosdbt.asm Normal file
View File

@@ -0,0 +1,25 @@
BITS 16
CPU 8086
diskpointer equ 0x1e*4
dbtbase equ 0x100
_start:
jmp main
main:
push ds
mov si, diskpointer
lds si, [si]
mov di, dbtbase
mov cx, 0x0a
cld
rep movsb
pop ds
mov al, 9 ; sectors per track
mov di, dbtbase
mov [di+4], al
mov di, diskpointer
mov [di], word dbtbase
mov [di+2], ds
retf

135
src/fat12.c Normal file
View File

@@ -0,0 +1,135 @@
#include <stdint.h>
#include <stdlib.h>
#define kSectorsPerCluster 2
#define kSectorsPerTrack 9
#define kHeads 2
#define kFatSizeSectors 2
#define kRootDirSizeSectors 7
#define kDataRegionStartSector (1 + kFatSizeSectors * 2 + kRootDirSizeSectors)
typedef struct {
char name[8];
char ext[3];
uint8_t attr;
uint8_t dontcare[14];
uint16_t cluster;
uint32_t size;
} direntry;
static uint8_t* gFat;
static direntry* gRootdir;
static int readsector(int c, int h, int s, uint8_t* addr) {
register uint8_t* dest asm("bx") = addr;
register uint8_t nsects asm("al") = 1;
register uint8_t func asm("ah") = 0x02;
register uint8_t sect asm("cl") = s;
register uint8_t cyl asm("ch") = c;
register uint8_t head asm("dh") = h;
register uint8_t drive asm("dl") = 0;
register uint16_t seg asm("es") = 0;
register uint8_t ret asm("ah");
for (int i = 0; i < 3; i++) {
asm volatile("int $0x13"
: "=r"(ret)
: "r"(dest), "r"(nsects), "r"(func), "r"(sect), "r"(cyl),
"r"(head), "r"(drive), "r"(seg));
if (ret != 0x80) {
break;
}
}
return ret;
}
static int readcluster(int cluster) {
int offs = cluster * 3 / 2;
if (cluster % 2) {
// high nibble is lsb + whole byte
return ((gFat[offs] & 0xf0) >> 4) + (gFat[offs + 1] << 4);
} else {
return gFat[offs] + ((gFat[offs + 1] & 0x0f) << 8);
}
}
static void cluster2chs(int cluster, int* c, int* h, int* s) {
int logicalsector =
kDataRegionStartSector + (cluster - 2) * kSectorsPerCluster;
*s = (logicalsector % kSectorsPerTrack) + 1;
*h = (logicalsector / kSectorsPerTrack) % kHeads;
*c = logicalsector / (kHeads * kSectorsPerTrack);
}
static int strncmp(const char* s1, const char* s2, size_t len) {
for (int i = 0; i < len && s1[i]; i++) {
if (s1[i] != s2[i]) {
return 1;
}
}
// XXX: really we should return 0 only if *s1 == *s2 here too
return 0;
}
static int loadfile(direntry* entry, void* addr) {
int cluster = entry->cluster;
for (int i = 0; i < entry->size; i += 1024) {
int c, h, s;
cluster2chs(cluster, &c, &h, &s);
if (readsector(c, h, s, addr + i)) {
return -5;
}
s++;
if (s > 9) {
s = 1;
h++;
}
if (h > 1) {
h = 0;
c++;
}
if (readsector(c, h, s, addr + i + 512)) {
return -5;
};
cluster = readcluster(cluster);
}
return 0;
}
int fat12_init(void* fat_addr, void* rootdir_addr) {
if (readsector(0, 0, 2, fat_addr)) {
return -2;
}
if (readsector(0, 0, 6, rootdir_addr)) {
return -3;
}
gFat = fat_addr;
gRootdir = rootdir_addr;
return 0;
}
int fat12_readfile(const char* name, void* addr) {
direntry* file = 0;
for (int i = 0; i < 16; i++) {
direntry* entry = &gRootdir[i];
if (entry->name[0] == 0) {
break;
}
if (!strncmp(entry->name, name, 11)) {
file = entry;
break;
}
}
if (!file) {
return -4;
}
return loadfile(file, addr);
}

15
src/fat12.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
/*
* Arguments:
* - fat_addr: 1 KiB
* - rootdir_addr: 3.5 KiB
*/
int fat12_init(void* fat_addr, void* rootdir_addr);
/*
* Returns:
* -4 if file is not found
* -5 if there is a disk error
*/
int fat12_readfile(const char* name, void* addr);

53
src/fat12boot.c Normal file
View File

@@ -0,0 +1,53 @@
#include <stdint.h>
#include <stdlib.h>
#include "fat12.h"
#define kPolmonAddress ((void*)0x0600)
#define kFatAddress ((void*)0x1000)
#define kRootDirAddress ((void*)0x1200)
static int putchar(int c) {
register uint8_t khar asm("al") = c;
register uint8_t func asm("ah") = 0x0e;
register uint8_t page asm("bh") = 0;
asm volatile("int $0x10" ::"r"(khar), "r"(func), "r"(page) : "bp");
return c;
}
static int puts(const char* msg) {
while (*msg) {
putchar(*msg++);
}
return 0;
}
__attribute__((noreturn)) static void die(const char* msg) {
puts(msg);
while (1) {
}
__builtin_unreachable();
}
__attribute__((noreturn)) static void jump(void* addr) {
asm volatile("ljmp $0,%0" ::"i"(addr));
__builtin_unreachable();
}
__attribute__((noreturn)) static void loadpolmon() {
if (fat12_init(kFatAddress, kRootDirAddress)) {
die("fi");
}
while (fat12_readfile("POLIO COM", kPolmonAddress)) {
asm volatile("mov $00, %%ah \n\t"
"int $0x16 \n\t" ::
: "ax");
}
jump(kPolmonAddress);
}
int main() { loadpolmon(); }

20
src/flat0600.ld Normal file
View File

@@ -0,0 +1,20 @@
SECTIONS {
. = 0x0600;
.text : {
KEEP(*(.init))
*(.text*)
}
.rodata : {
*(.rodata*)
}
.bss (NOLOAD) : {
* (.bss)
}
.data (NOLOAD) : {
*(.data)
}
}

20
src/flat1000.ld Normal file
View File

@@ -0,0 +1,20 @@
SECTIONS {
. = 0x1000;
.text : {
KEEP(*(.init))
*(.text*)
}
.rodata : {
*(.rodata*)
}
.bss (NOLOAD) : {
* (.bss)
}
.data (NOLOAD) : {
*(.data)
}
}

63
src/format.asm Normal file
View File

@@ -0,0 +1,63 @@
BITS 16
CPU 8086
buffer equ 0xe000
sectors equ 9
_start:
jmp main
formattrack:
push bp
push bx
push di
mov bp, sp
push ax ; track
push cx ; head
mov bx, buffer
xor cx, cx
l0:
cmp cl, sectors
jnl l1
mov di, cx
and di, 0x0f ; max 15 sectors
shl di, 1
shl di, 1 ; di = cl*4
lea di, [bx+di]
mov al, [bp-2] ; track number
mov [di+0], al
mov al, [bp-4] ; head number
mov [di+1], al
mov al, cl ; sector number
inc al
mov [di+2], al
mov [di+3], byte 0x02 ; 512 bytes per sector
inc cl
jmp l0
l1:
mov ah, 0x05 ; format track
mov al, sectors
mov dl, 0 ; first drive
mov dh, [bp-4] ; head number
mov ch, [bp-2] ; track number
mov cl, 1 ; sector number (first sector?)
int 0x13
add sp, 4
pop di
pop bx
pop bp
ret
main:
mov bp, sp
mov si, [bp+8]
mov ax, [si]
mov si, [bp+6]
mov cx, [si]
call formattrack
mov si, [bp+4]
mov [si], ax
retf 6

48
src/ftp.asm Normal file
View File

@@ -0,0 +1,48 @@
CPU 8086
chunksize equ 0x20
devicedelay equ 0x08
_start:
push bp
mov bp, sp
sub sp, 8
mov [bp-2], word chunksize ; recv size
mov [bp-4], word 0xf000 ; recv addr
mov [bp-6], word 0x0001 ; send size
mov [bp-8], word 0x7000 ; send addr
mov bx, [bp-8]
mov [bx], byte 0x42
l0:
cmp word [bp-4], 0xf600
jb l1
l2:
mov sp, bp
pop bp
ret
l1:
mov ah, 0x07
int 0x80
test al, al
jnz l1
push word [bp-2]
push word [bp-4]
mov ah, 0x05
int 0x80
test al, al
jz l2
mov ah, 0
add [bp-4], ax
push word [bp-6]
push word [bp-8]
mov ah, 0x06
int 0x80
add sp, 8
mov cx, devicedelay
l3:
mov ah, 0x07
push cx
int 0x80
pop cx
loop l3 ; give the device some time to answer
jmp l0

25
src/hello.asm Normal file
View File

@@ -0,0 +1,25 @@
BITS 16
CPU 8086
org 0x7200
_start:
jmp main
hw:
db "Hello, world!", 0x0d, 0x0a, 0
main:
xor bx, bx
mov si, hw
printloop:
mov al, cs:[si]
test al, al
jz done
mov ah, 0x0e
int 0x10
inc si
jmp printloop
done:
retf

5
src/hello.c Normal file
View File

@@ -0,0 +1,5 @@
#include <stdio.h>
int main() {
puts("Hello, world!\r\n");
}

55
src/mrbridge.py Normal file
View File

@@ -0,0 +1,55 @@
import argparse
import serial
import threading
import time
import websocket
def parse_args():
parser = argparse.ArgumentParser(description="Bridge a serial port to a websocket")
parser.add_argument("port", help="path to the serial port")
parser.add_argument("--baudrate", default=115200)
parser.add_argument("--escape", action='store_true')
parser.add_argument("ws", help="URL of the websocket")
return parser.parse_args()
def device_thread(device, ws):
while True:
line = device.readline()
ws.send(line)
def slowwrite(device, data):
for b in data:
device.write([b])
time.sleep(0.01)
def ws_thread(device, ws):
while True:
data = ws.recv();
slowwrite(device, data.replace(b'\n', b'\r\n'))
def main():
args = parse_args()
device = serial.Serial(args.port, baudrate=args.baudrate)
ws = websocket.create_connection(args.ws)
wst = threading.Thread(target=ws_thread, args=(device, ws), daemon=True)
wst.start()
if args.escape:
device.write([0x03])
try:
device_thread(device, ws)
except KeyboardInterrupt:
pass
finally:
if args.escape:
device.write([0x02])
if __name__ == "__main__":
main()

261
src/mushroom.s Normal file
View File

@@ -0,0 +1,261 @@
.arch i8086,jumps
.code16
.intel_syntax noprefix
.section .init
.global _start
_start:
mov ax, cs
mov ds, ax
mov es, ax
cli
mov ss, ax
mov sp, 0x100
sti
jmp main
.section .bss
kBufSize = 77
kDividerRow = 23
kRecvBufSize = 128
curpos: .word 0
inputsize: .byte 0
escaped: .byte 0
inputbuf: .zero kBufSize
recvbuf: .zero kRecvBufSize
# XXX: funky call
# inputs:
# si: string offset
# cl: string length
# clobbers: ax, bh, dx
.section .text.printn
printn:
push bx
push bp
mov bh, 0
1:
test cl, cl
jz 0f
mov al, [si]
cmpb escaped, 0
je 3f
cmp al, 'm'
jne 5f
movb escaped, 0
jmp 5f
3:
cmp al, 0x1b # ESC
jne 4f
movb escaped, 1
jmp 5f
4:
mov ah, 0x0e # write tty
int 0x10
push cx
mov ah, 0x03 # get cursor
int 0x10
cmp dh, kDividerRow
jb 2f
push dx
mov ah, 0x08 # read color
int 0x10
mov bh, ah
xor cx, cx
mov dx, 0x164f
mov ax, 0x0601 # scroll up, 1 line
int 0x10
pop dx
dec dh
mov bh, 0
mov ah, 0x02 # set cursor
int 0x10
2:
pop cx
5:
inc si
dec cl
jmp 1b
0:
pop bp
pop bx
ret
# XXX: funky call
# inputs:
# si: string offset
# cl: string length
# clobbers: ax, bh, dx
.section .text.addtext
addtext:
push di
push cx
mov bh, 0
mov ah, 0x03 # get cursor pos
int 0x10
pop cx
push dx
mov di, offset curpos
mov dx, [di]
mov ah, 0x02 # set cursor
int 0x10
call printn
mov ah, 0x03 # get cursor pos
int 0x10
mov [di], dx
pop dx
mov ah, 0x02 # set cursor
int 0x10
pop di
ret
.section .text.sendbuffer
sendbuffer:
push bp
mov bp, sp
sub sp, 2
mov al, inputsize
mov [bp-2], al
movb inputsize, 0
mov si, offset .l0
mov cl, 2
call addtext
mov si, offset inputbuf
mov cl, [bp-2]
call addtext
mov si, offset .l1
mov cl, 2
call addtext
mov si, offset inputbuf
mov cl, [bp-2]
call parasend
# XXX: should check cx in case not everything was sent
mov al, ' '
mov dx, 0x1802 # [24; 2]
call drawchar
mov ah, 0x02
int 0x10 # mov cursor back to start of line
add sp, 2
pop bp
ret
# draw until end of line
# al: char
# dh: row
# dl: start col
.section .text.drawchar
drawchar:
push ax
mov bh, 0
mov ah, 0x02 # set cursor
int 0x10
mov cx, 80
sub cl, dl
pop ax
mov ah, 0x0a
int 0x10
ret
.section .text.parasend
parasend:
mov ah, 0x06 # send parallel
push cx
push si
int 0x80
mov cx, 1
push cx
mov cx, offset .l1 + 1
push cx
mov ah, 0x06 # send parallel
int 0x80
add sp, 8
ret
prompt:
mov bh, 0
mov dx, 0x1800
mov ah, 0x02
int 0x10
mov ah, 0x0e
mov al, '>'
int 0x10
mov ah, 0x0e
mov al, ' '
int 0x10
ret
.section .rodata
.l0: .ascii "> "
.l1: .byte 0x0d, 0x0a
.l2: .ascii "MUSHRoom 8086 client 0.1\r\n"
l2len = . - offset .l2
.section .text.main
.global main
main:
movb inputsize, 0 # inputsize = 0
movw curpos, 0 # mov buffer cursor to [0;0]
movb escaped, 0
mov ax, 0x0002
int 0x10
mov si, offset .l2
mov cl, l2len
call addtext
mov dx, 0x1700 # [23; 0]
mov al, 0xc4 # horizontal box line
call drawchar
call prompt
0:
mov ah, 0x01
int 0x16 # check buffer
jz 1f # if empty, do a round of comms
mov ah, 0x00
int 0x16 # read 1 key
cmp al, 0x08 # backspace
je 5f
cmp al, 0x0d # CR
je 4f
mov di, offset inputbuf
mov si, offset inputsize
mov bh, 0
mov bl, [si]
cmp bl, kBufSize # stop accepting input at end of screen
jge 1f # do a round of comms
mov [di+bx], al # store character in buffer
incb [si] # increment buffer size
mov ah, 0x0e
mov bh, 0
int 0x10 # local echo
jmp 0b
5:
mov bl, inputsize
test bl, bl
jz 0b
decb inputsize
mov bh, 0
mov ah, 0x0e
int 0x10 # backspace
mov ax, 0x0e20
int 0x10
mov ax, 0x0e08
int 0x10
jmp 0b
4:
call sendbuffer # send input buffer
1:
mov ah, 0x07 # do parallel comms
int 0x80
mov ax, kRecvBufSize
push ax
mov ax, offset recvbuf
push ax
mov ah, 0x05 # recv
int 0x80
test al, al
mov cl, al
pop si
pop bx
jz 0b
call addtext
jmp 0b

80
src/paracli.s Normal file
View File

@@ -0,0 +1,80 @@
.arch i8086,jumps
.code16
.intel_syntax noprefix
.section .init
.global _start
_start:
mov ax, cs
mov ds, ax
mov es, ax
cli
mov ss, ax
mov sp, 0x100
sti
jmp main
.section .text.parcb
parcb:
push bx
push si
push bp
mov si, ax
1:
test dx, dx
jz 0f
mov al, [si]
mov ah, 0x0e
mov bh, 0
int 0x10
dec dx
inc si
jmp 1b
0:
pop bp
pop si
pop bx
ret
.section .rodata.l0
.l0: .ascii "> "
.l1: .ascii "."
delay = 500
.section .text.main
.global main
main:
mov ax, 0x0002
int 0x10
mov ax, offset parcb
call paracomm_init
0:
mov ah, 0x01
int 0x16
jz 1f
mov ah, 0x00
int 0x16
mov ah, 0
call paracomm_send
test ax, ax
jz 0b
1:
call paracomm_nextbyte
mov dx, 0x3bc
out dx, al
add dl, 2
mov al, 1
out dx, al
mov cx, delay
2: loop 2b
mov al, 0
out dx, al
mov cx, delay
3: loop 3b
dec dl
in al, dx
mov cl, 4
shr al, cl
call paracomm_feed
jmp 0b

138
src/paracomm.c Normal file
View File

@@ -0,0 +1,138 @@
#include "paracomm.h"
#include <stdlib.h>
#include <string.h>
#define kMosiIdleByte 0x00
#define kMosiStartByte 0x42
#define kMisoIdleNibble 0x0
#define kMisoStartNibble 0xa
static uint8_t mosi_workbuf[2][256];
static uint8_t mosi_workbuf_idx;
static uint8_t mosi_workbuf_size;
static uint8_t* mosi_sendbuf;
static uint8_t mosi_size;
static uint8_t mosi_sent;
static uint8_t mosi_state;
static uint8_t miso_recvbuf[256];
static uint8_t miso_size;
static uint8_t miso_received_nibbles;
static uint8_t miso_state;
static void swapbuffers() {
mosi_sendbuf = mosi_workbuf[mosi_workbuf_idx];
mosi_size = mosi_workbuf_size;
mosi_workbuf_size = 0;
mosi_workbuf_idx = (mosi_workbuf_idx + 1) % 2;
}
uint8_t paracomm_nextbyte() {
switch (mosi_state) {
case 0:
if (mosi_size == 0) {
swapbuffers();
if (mosi_size == 0) {
return kMosiIdleByte;
}
}
mosi_state = 1;
return kMosiStartByte;
case 1:
// assert(mosi_size > 0)
mosi_sent = 0;
mosi_state = 2;
return mosi_size;
case 2: {
uint8_t b = mosi_sendbuf[mosi_sent];
mosi_sent += 1;
if (mosi_sent == mosi_size) {
swapbuffers();
mosi_state = 0;
}
return b;
}
}
__builtin_unreachable();
}
void paracomm_feed(uint8_t n) {
switch (miso_state) {
case 0:
if (n == kMisoStartNibble) {
miso_state = 1;
} else if (n == kMisoIdleNibble) {
} else {
// error: spurious nibble
}
break;
case 1:
miso_size = n;
miso_state = 2;
break;
case 2:
miso_size += n << 4;
miso_received_nibbles = 0;
miso_state = 3;
break;
case 3: {
uint8_t idx = miso_received_nibbles / 2;
if (miso_received_nibbles % 2 == 0) {
miso_recvbuf[idx] = n;
} else {
miso_recvbuf[idx] += n << 4;
}
miso_received_nibbles += 1;
if (miso_received_nibbles == 2 * miso_size) {
miso_state = 0;
}
} break;
}
}
void paracomm_init() {
mosi_size = 0;
mosi_workbuf_idx = 0;
mosi_workbuf_size = 0;
mosi_state = 0;
miso_state = 0;
}
int paracomm_send(uint8_t b) {
if (mosi_workbuf_size == 256) {
return -1;
}
uint8_t* buff = mosi_workbuf[mosi_workbuf_idx];
buff[mosi_workbuf_size] = b;
mosi_workbuf_size += 1;
return 0;
}
uint8_t paracomm_recv(uint8_t* buf, uint8_t size) {
if (miso_state != 0) {
return 0;
}
if (size > miso_size) {
size = miso_size;
}
memcpy(buf, miso_recvbuf, size);
memcpy(miso_recvbuf, miso_recvbuf+size, miso_size - size);
miso_size -= size;
return size;
}
uint8_t paracomm_busy() {
return mosi_state != 0 || miso_state != 0;
}

25
src/paracomm.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include <stdint.h>
/** Must call first **/
void paracomm_init();
/** Sends a single byte.
* Returns: 0 if no error, non-0 otherwise.
*/
int paracomm_send(uint8_t b);
/** Receive some bytes.
* Returns: number of bytes copied
*/
uint8_t paracomm_recv(uint8_t* buf, uint8_t size);
/** Call after reading a nibble from the port */
void paracomm_feed(uint8_t n);
/** Yields the next byte to send out the port */
uint8_t paracomm_nextbyte();
/** Returns non-zero if busy */
uint8_t paracomm_busy();

93
src/polio-main.c Normal file
View File

@@ -0,0 +1,93 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "fat12.h"
#include "paracomm.h"
#define kFatAddr ((void*)0x1a00)
#define kRootDirAddr ((void*)0x1c00)
#define kPolmonAddr ((void*)0x1000)
#define kInt80Vector ((uint16_t*)(0x80 * 4))
#define kParallelDataPort 0x3bc
#define kParallelStatusPort 0x3bd
#define kParallelControlPort 0x3be
#define kParaDelay 400
#define kRecvBufferSize 256
void dosdbt();
void int80h();
uint8_t parabuf[kRecvBufferSize];
uint8_t parasize;
static void paracb(const uint8_t* buf, uint8_t size) {
// we'll mercilessly erase old data with new data
memcpy(parabuf, buf, size);
parasize = size;
}
static void die() {
while (1) {
}
}
static void __outb__(uint16_t port, uint8_t data) {
register uint16_t p asm("dx") = port;
register uint8_t d asm("al") = data;
asm volatile("outb %0, %1" ::"r"(d), "r"(p));
}
static uint8_t __inb__(uint16_t port) {
register uint16_t p asm("dx") = port;
register uint8_t d asm("al");
asm volatile("inb %1, %0" : "=r"(d) : "r"(p));
return d;
}
static void __delay__(int n) {
for (int i = 0; i < n; i++) {
asm volatile("");
}
}
uint8_t xferpara() {
uint8_t mosib = paracomm_nextbyte();
__outb__(kParallelDataPort, mosib);
__outb__(kParallelControlPort, 1);
__delay__(kParaDelay);
__outb__(kParallelControlPort, 0);
__delay__(kParaDelay);
uint8_t mison = __inb__(kParallelStatusPort) >> 4;
paracomm_feed(mison);
return paracomm_busy();
}
uint8_t parasend(uint8_t* addr, uint8_t size) {
uint8_t i;
for (i = 0; i < size; i++) {
if (paracomm_send(addr[i])) {
break;
}
}
return i;
}
int main() {
dosdbt();
parasize = 0;
paracomm_init(paracb);
fat12_init(kFatAddr, kRootDirAddr);
uint16_t* int80v = kInt80Vector;
int80v[0] = (uint16_t)int80h;
int80v[1] = 0;
if (fat12_readfile("POLMON COM", kPolmonAddr)) {
die();
}
asm volatile("jmp *%0" ::"r"(kPolmonAddr));
}

191
src/polio.s Normal file
View File

@@ -0,0 +1,191 @@
.arch i8086,jumps
.code16
.intel_syntax noprefix
.section .init
.global _start
_start:
cli
mov ax, 0x2000 # stack address
mov sp, ax # ss should already be 0
sti
jmp main
diskpointer = 0x1e*4
dbtbase = 0x0500
.section .text.dosdbt
.global dosdbt
dosdbt: # assumes es=0
push si
push di
push ds
mov si, diskpointer
lds si, [si]
mov di, dbtbase
mov cx, 0x0a # size of the dbt
cld
rep movsb # move dbt to dbtbase
pop ds
mov al, 9 # sectors per track
mov si, dbtbase
mov [si+4], al # set number of sectors
mov di, diskpointer
mov [di], si # new int1eh offset
mov [di+2], ds # and segment
pop di
pop si
ret
.section .text.int80stuff
# near copy [param 2] bytes, ds:[param 0] -> ds:[param 1]
copy:
mov ax, [bp-14] # ds
mov es, ax
mov ds, ax
mov cl, 4
shl si, cl
mov si, [bp+0] # source
mov di, [bp+2] # destination
mov cx, [bp+4] # length
cld
rep movsb
ret
## floppy stuff
# same as read but with a different int13h:ah value
writesector:
mov ax, 0x0301 # write command, 1 sector
jmp 0f
# params: addr, c, h, s
# return status in ah, sectors read (1 or 0) in al
readsector:
mov ax, 0x0201 # read command, 1 sector
0:
mov bx, [bp+0] # addr
mov ch, [bp+2] # c
mov dh, [bp+4] # h
mov cl, [bp+6] # s
int 0x13
ret
# params: c, h
# returns: status in ah, 0 in al?
buffer = 0xe000 # buffer needs to be big enough for a whole track
# (for some reason), so 9 * 512 bytes = 0x1200 bytes
sectors = 9
formattrack:
mov bx, buffer
xor cx, cx
0:
cmp cl, sectors
jnl 1f
mov di, cx
and di, 0x0f # max 15 sectors
shl di, 1
shl di, 1 # di = cl*4
lea di, [bx+di]
mov al, [bp+0] # track number
mov [di+0], al
mov al, [bp+2] # head number
mov [di+1], al
mov al, cl # sector number
inc al
mov [di+2], al
movb [di+3], 0x02 # 512 bytes per sector
inc cl
jmp 0b
1:
mov ah, 0x05 # format track
mov al, sectors
mov dl, 0 # first drive
mov ch, [bp+0] # track number
mov dh, [bp+2] # head number
mov cl, 1 # sector number (first sector?)
int 0x13
ret
# params: ds:fname, ds:dest
readfile:
mov si, [bp-14] # ds
mov cl, 4
shl si, cl
mov ax, [bp+0]
add ax, si
mov dx, [bp+2]
add dx, si
call fat12_readfile
ret
# params: ds:addr, size
sendpara:
mov si, [bp-14] # ds
mov cl, 4
shl si, cl
mov ax, [bp+0]
add ax, si
mov dx, [bp+2]
call parasend
ret
# params: ds:addr, size
recvpara:
mov si, [bp-14] # ds
mov cl, 4
shl si, cl
mov ax, [bp+0]
add ax, si
mov dx, [bp+2]
call paracomm_recv
ret
.section .text.int80h
.global int80h
int80h:
push si
push di
push bp
push ds
push es
xor cx, cx
mov es, cx
mov ds, cx
shl ah
cmp ah, offset int80h_entries
jge 0f
mov al, ah
xor ah, ah
mov si, ax
mov bp, sp
lea bp, [bp + 16] # 10 for us, 6 for the interrupt
call cs:[int80h_table+si]
jmp 1f
0:
mov ax, -1
1:
pop es
pop ds
pop bp
pop di
pop si
iret
.section .rodata.int80h
int80h_table:
.word offset copy # 0x00
.word offset readsector # 0x01
.word offset writesector # 0x02
.word offset formattrack # 0x03
.word offset readfile # 0x04
.word offset recvpara # 0x05
.word offset sendpara # 0x06
.word offset xferpara # 0x07
int80h_entries = . - int80h_table

469
src/polmon.cc Normal file
View File

@@ -0,0 +1,469 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#ifndef WOZMON
#define WOZMON 0
#endif
#if WOZMON
#define BACKSPACE 0
#define ASCIIDUMP 0
#define CLEARSCREENCMD 0
#define INT80H 0
#define SHOWTITLE 0
#define BOOTSTRAP 0
#define LAUNCH 0
#define CTRLBREAK 0
#endif // WOZMON
#ifndef BACKSPACE
#define BACKSPACE 1
#endif
#ifndef ASCIIDUMP
#define ASCIIDUMP 1
#endif
#ifndef CLEARSCREENCMD
#define CLEARSCREENCMD 1
#endif
#ifndef INT80H
#define INT80H 1
#endif
#ifndef SHOWTITLE
#define SHOWTITLE 1
#endif
#ifndef BOOTSTRAP
#define BOOTSTRAP 1
#endif
#ifndef LAUNCH
#define LAUNCH 1
#endif
#ifndef CTRLBREAK
#define CTRLBREAK 1
#endif
namespace {
#if WOZMON
int getchar() {
register char c asm("al");
asm volatile("movb $0x00, %%ah\n\t"
"int $0x16"
: "=r"(c)::"ah", "cc");
return c;
}
void putchar(int c) {
asm volatile("push %%bp \n\t"
"mov %0, %%ax \n\t"
"movb $0x0e, %%ah \n\t"
"movb $0, %%bh \n\t"
"int $0x10 \n\t"
"pop %%bp \n\t" ::"r"(c)
: "ax", "bh", "cc");
}
#endif // WOZMON
constexpr int kDumpSize = 16;
// arguments on the stack in reverse order
extern "C" uint16_t Int80h(uint8_t fun, int nargs);
asm(".section .text.Int80h \n"
"Int80h: \n"
" push %si \n"
" push %bp \n"
" mov %al, %ah \n"
" mov %sp, %bp \n"
" lea 6(%bp), %si \n" // 4 for pushed stuff, 2 for return addr
"0: \n"
" test %dx, %dx \n"
" jz 1f \n"
" push (%si) \n"
" dec %dx \n"
" add $2, %si \n"
" jmp 0b \n"
"1: \n"
" int $0x80 \n"
" mov %bp, %sp \n"
" pop %bp \n"
" pop %si \n"
" ret"
// return value in ax
);
constexpr uint8_t kBackspace = 0x7f;
constexpr uint8_t kOtherBackspace = 0x08;
uint8_t ReadHexNibble(uint8_t c) {
// lowercase only
if (c <= '9') {
return c - '0';
}
return 10 + (c - 'a');
}
uint16_t ReadUintN(int n, const char* buf) {
uint16_t out = 0;
for (int i = 0; i < n; i++) {
if (buf[i] == 0) {
break;
}
out <<= 4;
out += ReadHexNibble(buf[i]);
}
return out;
}
uint8_t ReadUint8(const char* buf) { return ReadUintN(2, buf); }
uint16_t ReadUint16(const char* buf) { return ReadUintN(4, buf); }
void WriteHexNibble(uint8_t c) {
if (c > 9) {
putchar('a' + c - 10);
} else {
putchar('0' + c);
}
}
void WriteUint8(uint8_t a) {
WriteHexNibble(a >> 4);
WriteHexNibble(a & 0xf);
}
void WriteUint16(uint16_t a) {
WriteUint8(a >> 8);
WriteUint8(a & 0xff);
}
uint8_t __get_far_u8__(uint16_t addr, uint16_t seg) {
register uint16_t ad asm("si") = addr;
register uint16_t sg asm("ds") = seg;
register uint8_t ret asm("al");
asm("lodsb \n\t" : "=r"(ret) : "r"(ad), "r"(sg));
return ret;
}
void __set_far_u8__(uint16_t addr, uint16_t seg, uint8_t val) {
register uint16_t ad asm("di") = addr;
register uint16_t sg asm("es") = seg;
register uint8_t v asm("al") = val;
asm("stosb \n\t" ::"r"(sg), "r"(ad), "r"(v) : "memory");
}
__attribute__((noreturn)) inline static void __basic__() {
asm volatile("movw $0x40, %ax \n\t"
"mov %ax, %ds \n\t"
"int $0x18");
__builtin_unreachable();
}
void Dump(uint16_t addr, uint16_t seg, int count) {
for (int i = 0; i < count; i++) {
putchar(' ');
uint8_t d = __get_far_u8__(addr + i, seg);
WriteUint8(d);
}
#if ASCIIDUMP
putchar(' ');
putchar(' ');
for (int i = 0; i < count; i++) {
uint8_t d = __get_far_u8__(addr + i, seg);
if (d > 31 && d < 127) {
putchar(d);
} else {
putchar('.');
}
}
#endif
}
void DumpHex(uint16_t addr, uint16_t seg) {
addr &= -kDumpSize;
putchar('[');
WriteUint16(seg);
putchar(':');
WriteUint16(addr);
putchar(']');
putchar(':');
Dump(addr, seg, kDumpSize);
putchar('\r');
putchar('\n');
}
void ClearScreen() {
asm volatile("movw $0x0002, %%ax \n\t"
"int $0x10" ::
: "ax", "cc");
}
void Status(uint8_t status) {
asm volatile("xorb %%bh, %%bh \n\t"
"movb $0x03, %%ah \n\t"
"int $0x10 \n\t"
"mov %%dx, %%di \n\t"
"movb $0x02, %%ah \n\t"
"movw $77, %%dx \n\t"
"int $0x10 \n\t"
"movb %0, %%al \n\t"
"call %1 \n\t"
"movb $0x02, %%ah \n\t"
"movw %%di, %%dx \n\t"
"int $0x10" ::"rm"(status),
"l"(WriteUint8)
: "ax", "bh", "dx", "cx", "di", "cc");
}
char toupper(char c) {
if (c >= 'a' && c <= 'z') {
return c - ('a' - 'A');
}
return c;
}
void tofatname(const char* name, char* fatname) {
for (int i = 0; i < 11; i++) {
if (*name == 0 || *name == '.') {
fatname[i] = ' ';
} else {
fatname[i] = toupper(*name++);
}
if (i == 7 && *name == '.') {
name++;
}
}
}
void LaunchFile(const char* name) {
constexpr uint16_t kSegment = 0x200;
constexpr uint16_t kAddress = 0x100;
constexpr uint16_t kHeaderAddress = (kSegment << 4);
constexpr uint16_t kFlatAddress = (kSegment << 4) + kAddress;
constexpr uint8_t kTrampoline[] = { 0xe8, 0xfd, 0x00, 0xcb };
char fatname[11];
tofatname(name, fatname);
auto* ptr = reinterpret_cast<uint8_t*>(kHeaderAddress);
memcpy(ptr, kTrampoline, sizeof(kTrampoline));
asm volatile("mov %1, %%ax \n\t"
"push %%ax \n\t"
"mov %0, %%ax \n\t"
"push %%ax \n\t"
"mov $04, %%ah \n\t"
"int $0x80 \n\t"
"add $4, %%sp \n\t"
"mov %2, %%ax \n\t"
"mov %%ax, %%ds \n\t"
"mov %%ax, %%es \n\t"
"cli \n\t"
"mov %%ax, %%ss \n\t"
"mov %3, %%ax \n\t"
"xchg %%ax, %%sp \n\t"
"sti \n\t"
"push %%ax \n\t"
"lcall %2,$0 \n\t"
"xor %%ax, %%ax \n\t"
"mov %%ax, %%ds \n\t"
"mov %%ax, %%es \n\t"
"cli \n\t"
"pop %%sp \n\t"
"mov %%ax, %%ss \n\t"
"sti"
:: "rmi"(fatname),
"rmi"(kFlatAddress), "i"(kSegment), "i"(kHeaderAddress)
: "ax", "bx", "cx", "dx", "memory");
}
#if CTRLBREAK
constexpr uint16_t kCtrlBreakVector = (0x1b * 4);
extern "C" void cbreak();
asm(" .section .text.cbreak \n"
"cbreak: \n"
" xor %ax, %ax \n"
" mov %ds, %ax \n"
" cli \n"
" mov %ss, %ax \n"
" mov $0x2000, %sp \n"
" sti \n"
" pushf \n"
" push %ax \n"
" mov $0x1000, %ax \n"
" push %ax \n"
" mov $0x20, %al \n"
" out %al, $0x20 \n"
" iret \n");
void InstallCtrlBreak() {
auto vec = reinterpret_cast<void (**)()>(kCtrlBreakVector);
vec[0] = cbreak;
vec[1] = 0;
}
#endif // CTRLBREAK
bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
bool dump = true;
for (const char* ptr = buf; *ptr;) {
if (*ptr == 's') {
cur_addr = 0;
cur_seg = ReadUint16(ptr + 1);
ptr += 5;
} else if (*ptr == '$') {
__basic__();
} else if (*ptr == 'j') {
dump = false;
ptr++;
auto jump = reinterpret_cast<uint16_t (*)()>(cur_addr);
uint16_t ret = jump();
WriteUint16(ret);
putchar('\r');
putchar('\n');
#if INT80H
} else if (*ptr == 'i') {
dump = false;
int nargs = 0;
ptr++;
int fun = ReadUint8(ptr);
ptr += 2;
for (; *ptr;) {
if (*ptr == ' ') {
ptr++;
continue;
}
uint16_t d = ReadUint16(ptr);
asm volatile("push %0" ::"r"(d));
nargs++;
ptr += 4;
}
uint16_t ret = Int80h(fun, nargs);
asm volatile("shl %0 \n\t"
"add %0, %%sp" ::"r"(nargs));
WriteUint16(ret);
putchar('\r');
putchar('\n');
#endif // INT80H
#if CLEARSCREENCMD
} else if (*ptr == 'l') {
dump = false;
ClearScreen();
ptr++;
#endif // CLEARSCREENCMD
#if LAUNCH
} else if (*ptr == 'r') {
dump = false;
ptr++;
while (*ptr == ' ') {
ptr++;
}
int l = 0;
for (; ptr[l]; l++) {
}
LaunchFile(ptr);
ptr += l;
#endif // LAUNCH
} else if (*ptr == 'w') {
dump = false;
uint16_t addr = cur_addr;
ptr += 1;
for (; *ptr;) {
if (*ptr == ' ') {
ptr++;
continue;
}
uint8_t d = ReadUint8(ptr);
__set_far_u8__(addr, cur_seg, d);
addr++;
ptr += 2;
}
} else {
cur_addr = ReadUint16(ptr);
ptr += 4;
}
}
return dump;
}
void DisplayPrompt() {
putchar('>');
putchar(' ');
}
void polmon() {
uint16_t cur_addr = 0;
uint16_t cur_seg = 0;
char inbuf[64];
bool first = true;
char* inptr = inbuf;
ClearScreen();
#if SHOWTITLE
puts("PolMon for Workgroups 3.1\r\n");
#endif // SHOWTITLE
DisplayPrompt();
while (1) {
uint8_t c = getchar();
putchar(c); // echo
if (c == '\r') {
*inptr = 0;
if (inbuf[0] == 0) {
if (!first) {
cur_addr += kDumpSize;
}
} else {
putchar('\n');
}
if (ParseCommand(inbuf, cur_addr, cur_seg)) {
DumpHex(cur_addr, cur_seg);
};
first = false;
inptr = inbuf;
DisplayPrompt();
#if BACKSPACE
} else if (c == kBackspace || c == kOtherBackspace) {
inptr--;
if (inptr < inbuf) {
inptr = inbuf;
putchar(' ');
continue;
}
putchar(' ');
putchar(kOtherBackspace);
#endif
} else {
*inptr = c;
inptr++;
}
}
}
} // namespace
int main() {
#if CTRLBREAK
InstallCtrlBreak();
#endif // CTRLBREAK
polmon();
}
#if BOOTSTRAP
__attribute__((section(".init"), noreturn, used)) void _start() {
main();
__builtin_unreachable();
}
#endif // BOOTSTRAP

32
src/readfloppy.asm Normal file
View File

@@ -0,0 +1,32 @@
BITS 16
CPU 8086
start:
; sp and all segment registers need to be saved
push bp ; and we also save bp (??)
push es
mov bp, sp ; we'll need that to access parameters
xor dx, dx ; drive 0, head 0
mov es, dx ; es = 0
mov bx, 0xf000 ; store the read sector there
mov ax, 0x0201 ; read, 1 sector
; params: cylinder, head, sector, out
mov si, [bp+14]
mov ch, [si]
mov si, [bp+12]
mov dh, [si]
mov si, [bp+10]
mov cl, [si]
int 0x13
mov si, [bp+8]
mov [si], ax
pop es
pop bp
retf 8 ; 2x the number of parameters on the stack

10
src/readio.asm Normal file
View File

@@ -0,0 +1,10 @@
BITS 16
CPU 8086
_start:
mov bp, sp
mov si, [bp+4]
mov dx, [si]
xor ax, ax
in al, dx
retf 2

10
src/runcomm.asm Normal file
View File

@@ -0,0 +1,10 @@
CPU 8086
mov cx, 0x100
a:
mov ah, 0x07
int 0x80
test al, al
loopnz a
b:
ret

45
src/stdlib.c Normal file
View File

@@ -0,0 +1,45 @@
#include <stdint.h>
#include <stdlib.h>
int getchar() {
register char c asm("al");
asm volatile("movb $0x00, %%ah\n\t"
"int $0x16"
: "=r"(c)::"ah", "cc");
return c;
}
int putchar(int c) {
asm volatile("push %%bp \n\t"
"mov %0, %%ax \n\t"
"movb $0x0e, %%ah \n\t"
"movb $0, %%bh \n\t"
"int $0x10 \n\t"
"pop %%bp \n\t" ::"r"(c)
: "ax", "bh", "cc");
return 0;
}
int puts(const char* s) {
while (*s) {
putchar(*s++);
}
return 0;
}
void* memcpy(void* dest, const void* src, size_t n) {
uint8_t* d = dest;
const uint8_t* s = src;
for (int i = 0; i < n; i++) {
d[i] = s[i];
}
return dest;
}
void* memset(void* ptr, int val, size_t len) {
uint8_t* p = ptr;
while (len--) {
*p++ = val;
}
return ptr;
}

13
src/trampoline.asm Normal file
View File

@@ -0,0 +1,13 @@
CPU 8086
src equ 0xf000
dest equ 0x0600
size equ 0x0600
_start:
mov cx, size
mov si, src
mov di, dest
rep movsb
mov ax, dest
jmp ax

32
src/writefloppy.asm Normal file
View File

@@ -0,0 +1,32 @@
BITS 16
CPU 8086
start:
; sp and all segment registers need to be saved
push bp ; and we also save bp (??)
push es
mov bp, sp ; we'll need that to access parameters
xor dx, dx ; drive 0, head 0
mov es, dx ; es = 0
mov bx, 0xf000 ; store the read sector there
mov ax, 0x0301 ; write, 1 sector
; params: cylinder, head, sector, out
mov si, [bp+14]
mov ch, [si]
mov si, [bp+12]
mov dh, [si]
mov si, [bp+10]
mov cl, [si]
int 0x13
mov si, [bp+8]
mov [si], ax
pop es
pop bp
retf 8 ; 2x the number of parameters on the stack

12
src/writeio.asm Normal file
View File

@@ -0,0 +1,12 @@
BITS 16
CPU 8086
_start:
mov bp, sp
mov si, [bp+6]
mov dx, [si]
xor ax, ax
mov si, [bp+4]
mov al, [si]
out dx, al
retf 4