Compare commits

..

19 Commits

Author SHA1 Message Date
6b9646681e readme: add a few links 2025-10-24 11:09:22 -04:00
0e18958341 Update README.md 2025-10-13 15:48:26 +02:00
aeee8131e7 Update README.md 2025-10-13 14:47:53 +02:00
8cd6652b45 Update README.md 2025-10-13 14:41:34 +02:00
3e471f1390 Update README.md 2025-10-13 14:36:49 +02:00
d7e0f7b759 Polish mrbridge and add ftpserve 2025-10-13 13:57:16 +02:00
aaefcc864d polio is getting bigger 2025-10-06 22:52:28 +02:00
a1275671b3 polio: exit copy, add writefile 2025-10-06 22:51:59 +02:00
84ea4d5cae polmon: add kopy and split launch into load+run 2025-10-06 22:51:32 +02:00
8818606867 polmon: fix restoring ss in cbreak 2025-10-06 22:50:34 +02:00
3d90cbadf8 polmon: cleanup 2025-10-06 22:49:52 +02:00
1208f1f66e fat12: can now write files 2025-10-06 22:49:17 +02:00
91cf76c251 fix ftpget 2025-10-06 22:46:40 +02:00
540f42b21b Update ake binaries set 2025-10-06 22:43:37 +02:00
5e08ee648b polio: fix read/writesector 2025-10-06 22:42:21 +02:00
798b3e8e6b call: remove useless segment override 2025-10-06 22:41:12 +02:00
05b733b296 bootsect: minor tweak 2025-10-06 22:40:28 +02:00
ec3fe0e07d Old crc16 is now a .asm 2025-10-06 22:40:10 +02:00
0917aa1280 Ok, last changes tonight for real 2025-10-04 01:27:59 +02:00
19 changed files with 655 additions and 303 deletions

View File

@@ -11,25 +11,16 @@ dev: dev-image ## Launch a dev container
.PHONY: binaries .PHONY: binaries
binaries: ## Build all small binaries binaries: ## Build all small binaries
docker build --build-arg TARGET=call.bin -o . --target=export .
docker build --build-arg TARGET=readfloppy.bin -o . --target=export . docker build --build-arg TARGET=readfloppy.bin -o . --target=export .
docker build --build-arg TARGET=writefloppy.bin -o . --target=export . docker build --build-arg TARGET=writefloppy.bin -o . --target=export .
docker build --build-arg TARGET=copy.bin -o . --target=export .
docker build --build-arg TARGET=format.bin -o . --target=export .
docker build --build-arg TARGET=crc16.bin -o . --target=export . docker build --build-arg TARGET=crc16.bin -o . --target=export .
docker build --build-arg TARGET=hello.bin -o . --target=export . docker build --build-arg TARGET=hello.bin -o . --target=export .
docker build --build-arg TARGET=copy.bin -o . --target=export .
docker build --build-arg TARGET=call.bin -o . --target=export .
docker build --build-arg TARGET=format.bin -o . --target=export .
docker build --build-arg TARGET=readio.bin -o . --target=export . docker build --build-arg TARGET=readio.bin -o . --target=export .
docker build --build-arg TARGET=writeio.bin -o . --target=export . docker build --build-arg TARGET=writeio.bin -o . --target=export .
.PHONY: polos
polos: ## Build polOS components
docker build --build-arg TARGET=fat12boot.bin -o . --target=export .
docker build --build-arg TARGET=polio.com -o . --target=export .
docker build --build-arg TARGET=polmon.com -o . --target=export .
docker build --build-arg TARGET=mushroom.com -o . --target=export .
docker build --build-arg TARGET=hello.com -o . --target=export .
docker build --build-arg TARGET=ftpget.com -o . --target=export .
.PHONY: floppy .PHONY: floppy
floppy: ## Make a bootable floppy image floppy: ## Make a bootable floppy image
docker build --build-arg TARGET=polos.img -o . --target=export . docker build --build-arg TARGET=polos.img -o . --target=export .

120
README.md
View File

@@ -1,37 +1,123 @@
5150 stuff 5150 stuff
========== ==========
The binary programs here use multiple conventions (more Fun!): ## Layout of a bootable polOS floppy disk
- .asm files use NASM syntax and are built with `nasm xx.asm`
- .s files use GAS syntax, they are built with `ia16-elf-gcc`
The reason is uh, because you see, I did things. The floppy disk is FAT12 formatted. It must contain at least the following items:
The .asm came first and was copy pasta from the internet. * _fat12boot.bin_: the boot sector. It loads _polio.com_ from a FAT12-formatted
The .s files came from the output of `ia16-elf-gcc -S` when I couldn't be bothered to write programs in assembly but still needed to make them compatible with BASIC's *CALL* instruction. drive 0 to address _0x0600_ and jumps to it.
* _polio.com_: the basic OS functions. It serves all interrupt 0x80 functions
(see below) and loads _polmon.com_ to address _0x1200_, then jumps to it.
* _polmon.com_: a barebones memory utility. See below for all it can do.
Also the wozmon binary contains special care to make it into a boot sector (0xaa55 magic). ## polOS int 0x80
* function in AH
* optional argument in AL
* other arguments on the stack
* return value in AX
| AH | AL | stack arguments | return | description |
| --- | --- | --- | --- | --- |
| _0x01_ | | _addr_, _c_, _h_, _s_ | error code in AH | __readsector__: reads 1 floppy sector at _c/h/s_ into _0000:addr_ |
| _0x02_ | | _addr_, _c_, _h_, _s_ | error code in AH | __writesector__: writes 1 floppy sector at _c/h/s_ from _0000:addr_ |
| 0x03 | | _c_, _h_ | error code in AH | __formattrack__: formats 1 track at c/h |
| 0x04 | | _fname_, _addr_ | non-zero if error | __readfile__: reads file with name at _ds:fname_ into _ds:addr_ |
| 0x05 | | _addr_, _size_ | bytes received, < 0 if error | __recvpara__: receive at most _size_ bytes from parallel port into _ds:addr_ |
| 0x06 | | _addr_, _size_ | bytes sent, < 0 if error | __sendpara__: send at most _size_ bytes to parallel port from _ds:addr_ |
| 0x07 | _until-idle_ | | 0 if idle | __runpara__: run a round of parallel comms if _AL=0_, keep going until idle if _AL!=0_ |
| 0x08 | | _fname_, _addr_, _size_ | error code in AH | __writefile__: writes _size_ bytes from _ds:addr_ into file with name at _ds:fname_ |
## polmon commands
Commands are a single letter followed by an optional space and arguments, with optional spaces between them.
A command can be prefixed with up to 4 hex digits, which will then change the current address.
* (no command): dumps 16 bytes at the current address. If no address is given, also increments the current address by 16.
* `w bytes:[u8]*`: writes bytes in hex to the current address.
* `k src:u16 size:u16`: copy _size_ bytes of memory from _cur-seg_:_src_ to _cur-seg:cur-addr_
* `s seg:u16`: changes the current segment
* `j`: calls a function at the current address. ABI calling conventions below.
* `l file:str`: loads a program with given name into the current segment, address _0x0100_.
* `r args:[u16]*`: runs the program in the current segment with arguments.
* `i ax:u16 args:[u16]*`: call an int 0x80 function with given AX and stack args. See section above.
The _r_, _j_ and _i_ commands display the value of the AX register after returning to polmon.
### ABI convention for (j)umping to functions
* AX, BX, CX, DX are caller-saved, all others are callee-saved
* first 3 arguments passed in AX, DX and CX, others are pushed in reverse order (former argument in lower memory)
* return value in AX
### ABI for programs
See example in _hello.com_
## Examples
### Launching mushroom client
Anyway, just type
``` ```
make binaries s 200
l mushroom.com
r
``` ```
and stop whining. ### FTP a file and store it to the floppy disk
You can also type
``` ```
make floppy s 200
l ftpget.com
``` ```
then * switch to segment _0x200_
``` * load file _ftpget.com_ to _cur-seg:0100_
bochs
```
to launch the simulator. You'll need to `c` it in the console for it to start.
Here you need to run this on the host:
```
python ftpserve.py src/hello.com
```
Then on polOS:
```
r f000 200
s 0
6000
w 48454c4c4f202020434f4d
i 0800 6000 f000 200
```
* run previously loaded `ftpget.com`: store up to _0x200_ (512) bytes to address _0000:f000_
* set _cur-seg_ to _0x0000_
* set _cur-add_ to _0x6000_
* write file name in ascii: `HELLO COM` to _0000:6000_
* call int 0x80 0x08 (writefile) with args: _fname_, _addr_, _size_
We can now test that the program got transferred correctly:
```
s 300
l hello.com
r
```
### But where is my `ls` command?
You can just inspect memory at _0000:1c00_, that's the root directory. Each entry is _0x20_ (32) bytes long and starts with 11 characters that constitute the file name.
## Useful stuff ## Useful stuff
* [Colab](https://colab.research.google.com/drive/1xGKYQJLKyabcSNYOiPumf9_-bbCbXke1?usp=sharing) * [Colab](https://colab.research.google.com/drive/1xGKYQJLKyabcSNYOiPumf9_-bbCbXke1?usp=sharing)
* [Arduino pinout](https://docs.google.com/spreadsheets/d/1jgKhr-0MFtY_bFZL9xYwVsxFPt4u_5ItV8Rj7FQ7Kj4/edit) * [Arduino pinout](https://docs.google.com/spreadsheets/d/1jgKhr-0MFtY_bFZL9xYwVsxFPt4u_5ItV8Rj7FQ7Kj4/edit)
* [DOS 3.1 disk images](https://dl.winworldpc.com/Abandonware%20Operating%20Systems/PC/DOS/IBM/IBM%20PC-DOS%203.1%20%285.25%29.7z)
* [DOS 3.1 manual](https://dl.winworldpc.com/IBM%20PC-DOS%203.10%20Manuals%20Feb85.7z)
* [IBM 5150 BIOS ROM](https://www.minuszerodegrees.net/bios/BIOS_IBM5150_19OCT81_5700671_U33.BIN)
* [IBM Cassette BASIC C1.00](https://www.minuszerodegrees.net/rom/bin/IBM/IBM%205150%20-%20Cassette%20BASIC%20version%20C1.00.zip)
* [IBM 5150 technical ref](https://www.minuszerodegrees.net/manuals/IBM/IBM_5150_Technical_Reference_6025005_AUG81.pdf)
* [Ruud's diagnostic ROM](https://minuszerodegrees.net/ruuds_diagnostic_rom/bin/ruuds_diagnostic_rom_v5.4_8kb.zip)
* [Supersoft diagnostic ROM](https://www.minuszerodegrees.net/supersoft_landmark/Supersoft_PCXT_8KB.bin)

79
ftpserve.py Normal file
View File

@@ -0,0 +1,79 @@
import argparse
import logging
import os
import serial
kCrc16Polynomial = 0x1021
kCrc16Preset = 0xFFFF
kDefaultSerialPort = None
kDefaultBaudrate = 115200
kDefaultChunkSize = 0x20
logger = logging.getLogger(__name__)
def crc16(data):
crc = kCrc16Preset
for c in data:
crc ^= c << 8
for j in range(8):
mix = crc & 0x8000
crc <<= 1
if mix:
crc ^= kCrc16Polynomial
crc &= 0xFFFF
return crc
def getserial(port=None, baudrate=None, **kwargs):
if port is not None:
return serial.Serial(port, baudrate)
# return first /dev/ttyACMx found
for i in range(10):
try:
return serial.Serial(f"/dev/ttyACM{i}", baudrate)
except serial.SerialException:
pass
raise RuntimeError("No serial device available.")
def ftpserve(file, device, chunksize=0x20):
with open(file, "rb") as f:
data = f.read()
device.reset_input_buffer()
logger.info(f"Serving file {file}")
for i in range(0, len(data), chunksize):
device.write(data[i : i + chunksize])
r = device.read()
if r != b"\x42":
raise RuntimeError(f"wrong response {r}")
logger.info(f"ack for chunk @{i}")
logger.info(f"Done serving file {file}")
crc = crc16(data)
logger.info(f"File length: {len(data):04x}, crc16: {crc:04x}")
def parse_args():
parser = argparse.ArgumentParser(description="Serve a file to ftpget.")
parser.add_argument("file", help="file to serve")
parser.add_argument("--port", help="path to the serial port")
parser.add_argument("--baudrate", default=kDefaultBaudrate)
parser.add_argument("--chunksize", default=kDefaultChunkSize)
return parser.parse_args()
def main():
logging.basicConfig(level=logging.INFO)
args = parse_args()
device = getserial(**vars(args))
ftpserve(args.file, device, args.chunksize)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass

View File

@@ -4,12 +4,29 @@ import threading
import time import time
import websocket import websocket
kDefaultSerialPort = None
kDefaultBaudrate = 115200
def getserial(port=None, baudrate=None, **kwargs):
if port is not None:
return serial.Serial(port, baudrate)
# return first /dev/ttyACMx found
for i in range(10):
try:
return serial.Serial(f"/dev/ttyACM{i}", baudrate)
except serial.SerialException:
pass
raise RuntimeError("No serial device available.")
def parse_args(): def parse_args():
parser = argparse.ArgumentParser(description="Bridge a serial port to a websocket") parser = argparse.ArgumentParser(description="Bridge a serial port to a websocket")
parser.add_argument("port", help="path to the serial port") parser.add_argument(
parser.add_argument("--baudrate", default=115200) "--port", help="path to the serial port", default=kDefaultSerialPort
parser.add_argument("--escape", action='store_true') )
parser.add_argument("--baudrate", default=kDefaultBaudrate)
parser.add_argument("ws", help="URL of the websocket") parser.add_argument("ws", help="URL of the websocket")
return parser.parse_args() return parser.parse_args()
@@ -28,28 +45,23 @@ def slowwrite(device, data):
def ws_thread(device, ws): def ws_thread(device, ws):
while True: while True:
data = ws.recv(); data = ws.recv()
slowwrite(device, data.replace(b'\n', b'\r\n')) slowwrite(device, data.replace(b"\n", b"\r\n"))
def main(): def main():
args = parse_args() args = parse_args()
device = serial.Serial(args.port, baudrate=args.baudrate) device = getserial(**vars(args))
ws = websocket.create_connection(args.ws) ws = websocket.create_connection(args.ws)
wst = threading.Thread(target=ws_thread, args=(device, ws), daemon=True) wst = threading.Thread(target=ws_thread, args=(device, ws), daemon=True)
wst.start() wst.start()
if args.escape: device_thread(device, ws)
device.write([0x03])
try:
device_thread(device, ws)
except KeyboardInterrupt:
pass
finally:
if args.escape:
device.write([0x02])
if __name__ == "__main__": if __name__ == "__main__":
main() try:
main()
except KeyboardInterrupt:
pass

View File

@@ -1,50 +1,46 @@
%.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 CC = ia16-elf-gcc
CXX = ia16-elf-gcc CXX = ia16-elf-gcc
LD = ia16-elf-gcc LD = ia16-elf-gcc
OBJCOPY = ia16-elf-objcopy
CXXFLAGS = -mregparmcall -ffunction-sections -Os -flto CXXFLAGS = -mregparmcall -ffunction-sections -Os -flto
CFLAGS = -mregparmcall -ffunction-sections -Os -flto CFLAGS = -mregparmcall -ffunction-sections -Os -flto
LDFLAGS = -mregparmcall -Wl,--gc-sections -Os -nostdlib -flto LDFLAGS = -mregparmcall -Wl,--gc-sections -Os -nostdlib -flto
%.bin: %.asm
nasm $< -o $@
%.elf: %.elf:
$(LD) $(LDFLAGS) $(CPPFLAGS) -o $@ $^ $(LD) $(LDFLAGS) $(CPPFLAGS) -o $@ $^
%.com: %.elf %.com: %.elf
ia16-elf-objcopy -O binary $< $@ $(OBJCOPY) -O binary $< $@
fat12boot.bin: fat12boot.bin:
ia16-elf-objcopy -O binary $< $@ $(OBJCOPY) -O binary $< $@
fat12boot.elf: fat12boot.o fat12.o bootsect.S fat12boot.elf: fat12boot.o fat12.o bootsect.S stdlib.o
fat12boot.elf: LDFLAGS += -T bootsect.ld fat12boot.elf: LDFLAGS += -T bootsect.ld
fat12boot.bin: fat12boot.elf fat12boot.bin: fat12boot.elf
stdlib.o: CFLAGS := $(filter-out -flto, $(CFLAGS)) stdlib.o: CFLAGS := $(filter-out -flto, $(CFLAGS))
polmon.elf: LDFLAGS += -T flat1000.ld polmon.elf: LDFLAGS += -T init.ld
polmon.elf: polmon.o stdlib.o polmon.elf: polmon.o stdlib.o
polio.elf: LDFLAGS += -T flat0600.ld polio.elf: LDFLAGS += -T flat0600.ld -mprotected-mode
polio.elf: polio-main.o polio.s fat12.o paracomm.o stdlib.o polio.elf: polio-main.o polio.s fat12.o paracomm.o stdlib.o
paracli.elf: LDFLAGS += -T doscom.ld paracli.elf: LDFLAGS += -T doscom.ld
paracli.elf: paracli.s paracomm.o paracli.elf: paracli.s paracomm.o
dos-programs = mushroom.com hello.com ftpget.com dos-programs = mushroom.com hello.com ftpget.com crc16.com
$(dos-programs:.com=.elf): LDFLAGS += -T doscom.ld $(dos-programs:.com=.elf): LDFLAGS += -T doscom.ld
mushroom.elf: mushroom.s mushroom.elf: mushroom.s
hello.elf: hello.o stdlib.o crt0.s hello.elf: hello.o stdlib.o crt0.s
ftpget.elf: ftpget.o stdlib.o crt0.s ftpget.elf: ftpget.o stdlib.o crt0.s
crc16.elf: crc16.o stdlib.o crt0.s
polos.img: fat12boot.bin polmon.com polio.com $(dos-programs) polos.img: fat12boot.bin polmon.com polio.com $(dos-programs)
dd if=/dev/zero of=$@ bs=512 count=720 dd if=/dev/zero of=$@ bs=512 count=720

View File

@@ -5,7 +5,7 @@
.global _start .global _start
_start: _start:
#ifndef NOBPB #ifndef NOBPB
jmp l0 jmp 0f
nop nop
.ascii "IBM 3.1" .ascii "IBM 3.1"
@@ -18,7 +18,7 @@ bpb_localsectors: .word 720
bpb_mediadescr: .byte 0xfd bpb_mediadescr: .byte 0xfd
bpb_sectsperfat: .word 2 bpb_sectsperfat: .word 2
l0: 0:
#endif // NOBPB #endif // NOBPB
cli cli
xor %ax, %ax xor %ax, %ax

View File

@@ -19,15 +19,15 @@ _start:
mov bx, 0x6 mov bx, 0x6
xor cx, cx xor cx, cx
a1: a1:
cmp cx, [es:si+4] cmp cx, [si+4]
jge a2 jge a2
lea ax, [es:si+bx] lea ax, [si+bx]
push ax push ax
add bl, 2 add bl, 2
inc cl inc cl
jmp a1 jmp a1
a2: a2:
call far [es:si] call far [si]
pop bp pop bp
pop di pop di
pop si pop si

45
src/crc16.asm Normal file
View File

@@ -0,0 +1,45 @@
CPU 8086
_start:
push bp
mov bp, sp
mov si, [bp+8]
push word [si]
mov si, [bp+10]
push word [si]
call crc16
mov di, [bp+6]
mov [di], ax
mov sp, bp
pop bp
retf 6
crc16:
push bp
mov bp, sp
mov bx, [bp+4]
add [bp+6], bx
mov ax, -1
.L2:
cmp bx, [bp+6]
jne .L5
pop bp
ret
.L5:
mov dl, [bx]
mov cl, 0x8
shl dx, cl
xor ax, dx
mov dl, cl
.L4:
mov cx, ax
shl cx, 1
test ax, ax
xchg cx, ax
jge .L3
xor ax, 4129
.L3:
dec dl
jne .L4
inc bx
jmp .L2

View File

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

View File

@@ -1,57 +0,0 @@
.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"

View File

@@ -1,12 +1,18 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "polos.h"
#define kSectorsPerCluster 2 #define kSectorsPerCluster 2
#define kSectorsPerTrack 9 #define kSectorsPerTrack 9
#define kHeads 2 #define kHeads 2
#define kTracks 40
#define kFatSizeSectors 2 #define kFatSizeSectors 2
#define kRootDirSizeSectors 7 #define kRootDirSizeSectors 7
#define kBytesPerCluster 1024
#define kDataRegionStartSector (1 + kFatSizeSectors * 2 + kRootDirSizeSectors) #define kDataRegionStartSector (1 + kFatSizeSectors * 2 + kRootDirSizeSectors)
#define kNumClusters ((kSectorsPerTrack * kHeads * kTracks - kDataRegionStartSector) / kSectorsPerCluster + 2)
typedef struct { typedef struct {
char name[8]; char name[8];
@@ -22,23 +28,12 @@ typedef struct {
static uint8_t* gFat; static uint8_t* gFat;
static direntry* gRootdir; static direntry* gRootdir;
static int readsector(int c, int h, int s, uint8_t* addr) { static uint8_t rwsector(uint8_t w, int c, int h, int s, void* addr) {
register uint8_t* dest asm("bx") = addr; uint16_t ret;
register uint8_t nsects asm("al") = 1; uint16_t cmd = w ? 0x0301 : 0x0201;
register uint8_t func asm("ah") = 0x02; // retry for a total of up to 3 attempts if drive not ready
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++) { for (int i = 0; i < 3; i++) {
asm volatile("int $0x13" ret = __readwritesector__(cmd, addr, c, h, s) >> 8;
: "=r"(ret)
: "r"(dest), "r"(nsects), "r"(func), "r"(sect), "r"(cyl),
"r"(head), "r"(drive), "r"(seg));
if (ret != 0x80) { if (ret != 0x80) {
break; break;
} }
@@ -47,6 +42,14 @@ static int readsector(int c, int h, int s, uint8_t* addr) {
return ret; return ret;
} }
static uint8_t readsector(int c, int h, int s, void* addr) {
return rwsector(0, c, h, s, addr);
}
static uint8_t writesector(int c, int h, int s, void* addr) {
return rwsector(1, c, h, s, addr);
}
static int readcluster(int cluster) { static int readcluster(int cluster) {
int offs = cluster * 3 / 2; int offs = cluster * 3 / 2;
if (cluster % 2) { if (cluster % 2) {
@@ -57,6 +60,28 @@ static int readcluster(int cluster) {
} }
} }
static int writecluster(int cluster, int value) {
int offs = cluster * 3 / 2;
if (cluster % 2) {
gFat[offs] = (gFat[offs] & 0x0f) + ((value & 0x0f) << 4);
gFat[offs+1] = value >> 4;
// high nibble is lsb + whole byte
} else {
gFat[offs] = value & 0xff;
gFat[offs+1] = (gFat[offs+1] & 0xf0) + (value >> 8);
}
return value;
}
static int firstfreecluster() {
for (int i = 2; i < kNumClusters; i++) {
if (readcluster(i) == 0) {
return i;
}
}
return -1;
}
static void cluster2chs(int cluster, int* c, int* h, int* s) { static void cluster2chs(int cluster, int* c, int* h, int* s) {
int logicalsector = int logicalsector =
kDataRegionStartSector + (cluster - 2) * kSectorsPerCluster; kDataRegionStartSector + (cluster - 2) * kSectorsPerCluster;
@@ -65,19 +90,9 @@ static void cluster2chs(int cluster, int* c, int* h, int* s) {
*c = logicalsector / (kHeads * kSectorsPerTrack); *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) { static int loadfile(direntry* entry, void* addr) {
int cluster = entry->cluster; int cluster = entry->cluster;
for (int i = 0; i < entry->size; i += 1024) { for (int i = 0; i < entry->size; i += kBytesPerCluster) {
int c, h, s; int c, h, s;
cluster2chs(cluster, &c, &h, &s); cluster2chs(cluster, &c, &h, &s);
if (readsector(c, h, s, addr + i)) { if (readsector(c, h, s, addr + i)) {
@@ -88,10 +103,10 @@ static int loadfile(direntry* entry, void* addr) {
s = 1; s = 1;
h++; h++;
} }
if (h > 1) { // if (h > 1) { // this will never happen
h = 0; // h = 0;
c++; // c++;
} // }
if (readsector(c, h, s, addr + i + 512)) { if (readsector(c, h, s, addr + i + 512)) {
return -5; return -5;
}; };
@@ -100,6 +115,94 @@ static int loadfile(direntry* entry, void* addr) {
return 0; return 0;
} }
static direntry* findfile(const char* name) {
direntry* file = 0;
for (int i = 0; i < 16; i++) {
direntry* entry = &gRootdir[i];
if (!strncmp(entry->name, name, 11)) {
file = entry;
break;
}
}
return file;
}
static direntry* newfile(const char* name) {
direntry* file = 0;
for (int i = 0; i < 16; i++) {
direntry* entry = &gRootdir[i];
if (entry->name[0] == 0) {
file = entry;
break;
}
}
if (!file) {
return file;
}
memcpy(file->name, name, 11);
file->size = 0;
file->cluster = 0;
return file;
}
static int allocatecluster(int previous) {
int cluster = firstfreecluster();
writecluster(cluster, 0xfff);
if (previous != 0) {
writecluster(previous, cluster);
}
return cluster;
}
static void freeclusterchain(int cluster) {
if (cluster < 2 || cluster >= 0xf00) {
return;
}
freeclusterchain(readcluster(cluster));
writecluster(cluster, 0);
}
static int savefile(direntry* file, void* addr, int size) {
freeclusterchain(file->cluster);
file->cluster = 0;
int cluster = 0;
for (int i = 0; i < size; i += kBytesPerCluster) {
cluster = allocatecluster(cluster);
if (file->cluster == 0) {
file->cluster = cluster;
}
int c, h, s;
cluster2chs(cluster, &c, &h, &s);
if (writesector(c, h, s, addr + i)) {
return -7;
}
s++;
if (s > 9) {
s = 1;
h++;
}
// if (h > 1) { // no cluster will cross a cylinder boundary
// h = 0;
// c++;
// }
if (writesector(c, h, s, addr + i + 512)) {
return -7;
};
}
file->size = size;
if (writesector(0, 0, 2, gFat)) {
return -8;
}
if (writesector(0, 0, 6, gRootdir)) {
return -9;
}
return 0;
}
int fat12_init(void* fat_addr, void* rootdir_addr) { int fat12_init(void* fat_addr, void* rootdir_addr) {
if (readsector(0, 0, 2, fat_addr)) { if (readsector(0, 0, 2, fat_addr)) {
return -2; return -2;
@@ -115,21 +218,22 @@ int fat12_init(void* fat_addr, void* rootdir_addr) {
} }
int fat12_readfile(const char* name, void* addr) { int fat12_readfile(const char* name, void* addr) {
direntry* file = 0; direntry* file = findfile(name);
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) { if (!file) {
return -4; return -4;
} }
return loadfile(file, addr); return loadfile(file, addr);
} }
int fat12_writefile(const char* name, void* addr, int size) {
direntry* file = findfile(name);
if (!file) {
file = newfile(name);
}
if (!file) {
// no moar space
return -6;
}
return savefile(file, addr, size);
}

View File

@@ -5,6 +5,16 @@
#define kDefaultChunkSize 0x20 #define kDefaultChunkSize 0x20
static int recv(uint8_t* buf, uint8_t size) {
uint16_t ds = 0;
asm volatile ("push %%ds \n\t"
"mov %0, %%ds \n\t"
"pop %0 \n" : "+r"(ds));
int len = read(kLpt1, buf, size);
asm volatile ("mov %0, %%ds" :: "r"(ds));
return len;
}
int main(int argc, uint16_t argv[]) { int main(int argc, uint16_t argv[]) {
if (argc < 2) { if (argc < 2) {
return -1; return -1;
@@ -17,17 +27,26 @@ int main(int argc, uint16_t argv[]) {
chunksize = argv[2]; chunksize = argv[2];
} }
uint8_t ok = 0x42; uint8_t ack = 0x42;
int i;
for (int i = 0; i < size; ) { for (i = 0; i < size;) {
runcomms(kUntilIdle);
// delay? // delay?
int len = read(kLpt1, dest + i, chunksize); int len;
for (int j = 0; j < 3; j++) {
int r = runcomms(kUntilIdle);
len = recv(dest + i, chunksize);
if (len > 0) {
break;
}
}
if (len <= 0) { if (len <= 0) {
break; break;
} }
write(kLpt1, &ok, 1); write(kLpt1, &ack, 1);
i += len; i += len;
} }
return i;
} }

View File

@@ -1,5 +1,5 @@
SECTIONS { SECTIONS {
. = 0x1000; . = 0x1200;
.text : { .text : {
KEEP(*(.init)) KEEP(*(.init))

View File

@@ -244,7 +244,7 @@ main:
4: 4:
call sendbuffer # send input buffer call sendbuffer # send input buffer
1: 1:
mov ah, 0x07 # do parallel comms mov ax, 0x0700 # do parallel comms
int 0x80 int 0x80
mov ax, kRecvBufSize mov ax, kRecvBufSize
push ax push ax

View File

@@ -7,7 +7,7 @@
#define kFatAddr ((void*)0x1a00) #define kFatAddr ((void*)0x1a00)
#define kRootDirAddr ((void*)0x1c00) #define kRootDirAddr ((void*)0x1c00)
#define kPolmonAddr ((void*)0x1000) #define kPolmonAddr ((void*)0x1200)
#define kInt80Vector ((uint16_t*)(0x80 * 4)) #define kInt80Vector ((uint16_t*)(0x80 * 4))
#define kParallelDataPort 0x3bc #define kParallelDataPort 0x3bc
#define kParallelStatusPort 0x3bd #define kParallelStatusPort 0x3bd
@@ -18,13 +18,15 @@
void dosdbt(); void dosdbt();
void int80h(); void int80h();
uint8_t parabuf[kRecvBufferSize]; static uint8_t parabuf[kRecvBufferSize];
uint8_t parasize; static uint8_t parasize;
static void paracb(const uint8_t* buf, uint8_t size) { static void paracb(const uint8_t* buf, uint8_t size) {
// we'll mercilessly erase old data with new data if (parasize + size > sizeof(parabuf)) {
memcpy(parabuf, buf, size); size = sizeof(parabuf) - parasize;
parasize = size; }
memcpy(parabuf + parasize, buf, size);
parasize += size;
} }
static void die() { static void die() {
@@ -51,7 +53,7 @@ static void __delay__(int n) {
} }
} }
uint8_t xferpara(uint8_t until_idle) { uint16_t xferpara(uint8_t until_idle) {
do { do {
uint8_t mosib = paracomm_nextbyte(); uint8_t mosib = paracomm_nextbyte();
__outb__(kParallelDataPort, mosib); __outb__(kParallelDataPort, mosib);
@@ -65,7 +67,7 @@ uint8_t xferpara(uint8_t until_idle) {
return paracomm_busy(); return paracomm_busy();
} }
uint8_t parasend(uint8_t* addr, uint8_t size) { uint16_t parasend(uint8_t* addr, uint8_t size) {
uint8_t i; uint8_t i;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (paracomm_send(addr[i])) { if (paracomm_send(addr[i])) {
@@ -75,7 +77,7 @@ uint8_t parasend(uint8_t* addr, uint8_t size) {
return i; return i;
} }
uint8_t pararecv(uint8_t* buf, uint8_t size) { uint16_t pararecv(uint8_t* buf, uint8_t size) {
if (parasize == 0) { if (parasize == 0) {
return 0; return 0;
} }

View File

@@ -40,20 +40,6 @@ dosdbt: # assumes es=0
.section .text.int80stuff .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 ## floppy stuff
# same as read but with a different int13h:ah value # same as read but with a different int13h:ah value
@@ -70,6 +56,7 @@ readsector:
mov ch, [bp+2] # c mov ch, [bp+2] # c
mov dh, [bp+4] # h mov dh, [bp+4] # h
mov cl, [bp+6] # s mov cl, [bp+6] # s
mov dl, 0 # drive
int 0x13 int 0x13
ret ret
@@ -121,8 +108,19 @@ readfile:
add ax, si add ax, si
mov dx, [bp+2] mov dx, [bp+2]
add dx, si add dx, si
call fat12_readfile jmp fat12_readfile
ret
# params: ds:fname, ds:src, size
writefile:
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
mov cx, [bp+4]
jmp fat12_writefile
# params: ds:addr, size # params: ds:addr, size
sendpara: sendpara:
@@ -132,8 +130,7 @@ sendpara:
mov ax, [bp+0] mov ax, [bp+0]
add ax, si add ax, si
mov dx, [bp+2] mov dx, [bp+2]
call parasend jmp parasend
ret
# params: ds:addr, size # params: ds:addr, size
recvpara: recvpara:
@@ -143,7 +140,10 @@ recvpara:
mov ax, [bp+0] mov ax, [bp+0]
add ax, si add ax, si
mov dx, [bp+2] mov dx, [bp+2]
call pararecv jmp pararecv
dummy:
mov ax, -1
ret ret
.section .text.int80h .section .text.int80h
@@ -154,6 +154,7 @@ int80h:
push bp push bp
push ds push ds
push es push es
mov bp, sp
xor cx, cx xor cx, cx
mov es, cx mov es, cx
mov ds, cx mov ds, cx
@@ -162,7 +163,6 @@ int80h:
cmp cl, offset int80h_entries cmp cl, offset int80h_entries
jge 0f jge 0f
mov si, cx mov si, cx
mov bp, sp
add bp, 16 # 10 for us, 6 for the interrupt add bp, 16 # 10 for us, 6 for the interrupt
call cs:[int80h_table+si] call cs:[int80h_table+si]
jmp 1f jmp 1f
@@ -178,7 +178,7 @@ int80h:
.section .rodata.int80h .section .rodata.int80h
int80h_table: int80h_table:
.word offset copy # 0x00 .word offset dummy # 0x00
.word offset readsector # 0x01 .word offset readsector # 0x01
.word offset writesector # 0x02 .word offset writesector # 0x02
.word offset formattrack # 0x03 .word offset formattrack # 0x03
@@ -186,5 +186,6 @@ int80h_table:
.word offset recvpara # 0x05 .word offset recvpara # 0x05
.word offset sendpara # 0x06 .word offset sendpara # 0x06
.word offset xferpara # 0x07 .word offset xferpara # 0x07
.word offset writefile # 0x08
int80h_entries = . - int80h_table int80h_entries = . - int80h_table

View File

@@ -2,6 +2,8 @@
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include "polos.h"
namespace { namespace {
constexpr int kDumpSize = 16; constexpr int kDumpSize = 16;
@@ -81,21 +83,6 @@ void WriteUint16ln(uint16_t a) {
puts("\r\n"); puts("\r\n");
} }
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__() { __attribute__((noreturn)) inline static void __basic__() {
asm volatile("movw $0x40, %ax \n\t" asm volatile("movw $0x40, %ax \n\t"
"mov %ax, %ds \n\t" "mov %ax, %ds \n\t"
@@ -140,23 +127,6 @@ void ClearScreen() {
: "ax", "cc"); : "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) { char toupper(char c) {
if (c >= 'a' && c <= 'z') { if (c >= 'a' && c <= 'z') {
return c - ('a' - 'A'); return c - ('a' - 'A');
@@ -164,15 +134,17 @@ char toupper(char c) {
return c; return c;
} }
void tofatname(const char* name, char* fatname) { void tofatname(const char* name, uint8_t len, char* fatname) {
int j = 0;
for (int i = 0; i < 11; i++) { for (int i = 0; i < 11; i++) {
if (*name == 0 || *name == '.') { if (j == len || name[j] == '.') {
fatname[i] = ' '; fatname[i] = ' ';
} else { } else {
fatname[i] = toupper(*name++); fatname[i] = toupper(name[j]);
j++;
} }
if (i == 7 && *name == '.') { if (i == 7 && name[j] == '.') {
name++; j++;
} }
} }
} }
@@ -192,29 +164,41 @@ uint16_t __readfile__(const char* name, uint16_t addr) {
return ret; return ret;
} }
uint16_t LaunchFile(const char* name, uint8_t argc, uint16_t* argv) { uint16_t LoadFile(const char* name, uint8_t len, uint16_t seg) {
constexpr uint16_t kSegment = 0x200;
constexpr uint16_t kAddress = 0x100; constexpr uint16_t kAddress = 0x100;
constexpr uint16_t kHeaderAddress = (kSegment << 4);
constexpr uint16_t kFlatAddress = (kSegment << 4) + kAddress;
constexpr uint16_t kArgvAddress = 0x0004;
// trampoline: // trampoline:
// call 0x100 // call 0x100
// retf // retf
// XXX: if you change kAddress, the trampoline needs to be updated // XXX: if you change kAddress, the trampoline needs to be updated
constexpr uint8_t kTrampoline[] = {0xe8, 0xfd, 0x00, 0xcb}; constexpr uint8_t kTrampoline[] = {0xe8, 0xfd, 0x00, 0xcb};
char fatname[11]; uint16_t headeraddr = (seg << 4);
tofatname(name, fatname); uint16_t flataddr = headeraddr + kAddress;
uint16_t r = __readfile__(fatname, kFlatAddress); char fatname[11];
tofatname(name, len, fatname);
uint16_t r = __readfile__(fatname, flataddr);
if (r) {
memcpy(fatname + 8, "COM", 3);
r = __readfile__(fatname, flataddr);
}
if (r) { if (r) {
return r; return r;
} }
auto* ptr = reinterpret_cast<uint8_t*>(kHeaderAddress); auto* ptr = reinterpret_cast<uint8_t*>(headeraddr);
memcpy(ptr, kTrampoline, sizeof(kTrampoline)); memcpy(ptr, kTrampoline, sizeof(kTrampoline));
auto* argp = reinterpret_cast<uint16_t*>(kHeaderAddress + kArgvAddress);
return 0;
}
uint16_t Exec(uint16_t seg, uint8_t argc, uint16_t* argv) {
constexpr uint16_t kAddress = 0x100;
constexpr uint16_t kArgvAddress = 0x0004;
uint16_t headeraddr = (seg << 4);
auto* argp = reinterpret_cast<uint16_t*>(headeraddr + kArgvAddress);
memcpy(argp, argv, argc * 2); memcpy(argp, argv, argc * 2);
register uint16_t ac asm("ax") = argc; register uint16_t ac asm("ax") = argc;
@@ -228,8 +212,13 @@ uint16_t LaunchFile(const char* name, uint8_t argc, uint16_t* argv) {
"xchg %%cx, %%sp \n\t" "xchg %%cx, %%sp \n\t"
"sti \n\t" "sti \n\t"
"push %%cx \n\t" "push %%cx \n\t"
"lcall %2,$0 \n\t" "push %2 \n\t"
"xor %%cx, %%cx \n\t" "xor %%cx, %%cx \n\t"
"push %%cx \n\t"
"mov %%sp, %%bx \n\t"
"lcall *(%%bx) \n\t"
"pop %%cx \n\t"
"pop %%dx \n\t"
"mov %%cx, %%ds \n\t" "mov %%cx, %%ds \n\t"
"mov %%cx, %%es \n\t" "mov %%cx, %%es \n\t"
"cli \n\t" "cli \n\t"
@@ -237,7 +226,7 @@ uint16_t LaunchFile(const char* name, uint8_t argc, uint16_t* argv) {
"mov %%cx, %%ss \n\t" "mov %%cx, %%ss \n\t"
"sti" "sti"
: "+r"(ac), "+r"(av) : "+r"(ac), "+r"(av)
: "i"(kSegment), "i"(kAddress) : "r"(seg), "i"(kAddress)
: "bx", "cx", "memory", "cc"); : "bx", "cx", "memory", "cc");
return ac; return ac;
@@ -251,7 +240,7 @@ asm(" .section .text.cbreak \n"
" xor %ax, %ax \n" " xor %ax, %ax \n"
" mov %ax, %ds \n" " mov %ax, %ds \n"
" cli \n" " cli \n"
" mov %ax, %ds \n" " mov %ax, %ss \n"
" mov $0x2000, %sp \n" " mov $0x2000, %sp \n"
" sti \n" " sti \n"
" pushf \n" " pushf \n"
@@ -268,13 +257,43 @@ void InstallCtrlBreak() {
vec[1] = 0; vec[1] = 0;
} }
// j
// w [u8]*
// i [u16] [u16]*
// r str [u16]*
// k u16 u16 u16
int ReadArgs(const char*& ptr, uint16_t* args, int size) {
int nargs = 0;
while (*ptr && nargs < size) {
if (*ptr == ' ') {
ptr += 1;
continue;
}
args[nargs] = ReadUint16(ptr);
nargs += 1;
}
return nargs;
}
int ReadString(const char*& ptr, const char*& str) {
while (*ptr == ' ') {
ptr++;
}
str = ptr;
int l;
for (l = 0; *ptr && *ptr != ' '; l++, ptr++) {
}
return l;
}
bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) { bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
bool dump = true; bool dump = true;
for (const char* ptr = buf; *ptr;) { for (const char* ptr = buf; *ptr;) {
if (*ptr == 's') { if (*ptr == 's') {
cur_addr = 0;
ptr += 1; ptr += 1;
cur_seg = ReadUint16(ptr); cur_addr = 0;
ReadArgs(ptr, &cur_seg, 1);
} else if (*ptr == '$') { } else if (*ptr == '$') {
__basic__(); __basic__();
} else if (*ptr == 'j') { } else if (*ptr == 'j') {
@@ -285,43 +304,27 @@ bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
WriteUint16ln(ret); WriteUint16ln(ret);
} else if (*ptr == 'i') { } else if (*ptr == 'i') {
dump = false; dump = false;
uint16_t args[8]; // ought to be enough for everybody
uint8_t nargs = 0;
ptr++; ptr++;
int fun = ReadUint8(ptr) << 8; uint16_t args[8]; // ought to be enough for everybody
fun += ReadUint8(ptr); int nargs = ReadArgs(ptr, args, 8);
for (; *ptr && nargs < 8;) { uint16_t fun = args[0];
if (*ptr == ' ') { uint16_t ret = Int80h(fun, nargs - 1, args + 1);
ptr++;
continue;
}
args[nargs++] = ReadUint16(ptr);
}
uint16_t ret = Int80h(fun, nargs, args);
WriteUint16ln(ret); WriteUint16ln(ret);
} else if (*ptr == 'l') { } else if (*ptr == 'l') {
dump = false; dump = false;
ClearScreen();
ptr++; ptr++;
const char* fname;
int l = ReadString(ptr, fname);
uint16_t ret = LoadFile(fname, l, cur_seg);
if (ret) {
WriteUint16ln(ret);
}
} else if (*ptr == 'r') { } else if (*ptr == 'r') {
dump = false; dump = false;
uint16_t args[8]; // ought to be enough for everybody
uint8_t nargs = 0;
ptr++; ptr++;
while (*ptr == ' ') { uint16_t args[8]; // ought to be enough for everybody
ptr++; int nargs = ReadArgs(ptr, args, 8);
} uint16_t ret = Exec(cur_seg, nargs, args);
const char* fname = ptr;
for (; *ptr && *ptr != ' '; ptr++) {
}
for (; *ptr && nargs < 8;) {
if (*ptr == ' ') {
ptr++;
continue;
}
args[nargs++] = ReadUint16(ptr);
}
uint16_t ret = LaunchFile(fname, nargs, args);
WriteUint16ln(ret); WriteUint16ln(ret);
} else if (*ptr == 'w') { } else if (*ptr == 'w') {
dump = false; dump = false;
@@ -336,6 +339,17 @@ bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
__set_far_u8__(addr, cur_seg, d); __set_far_u8__(addr, cur_seg, d);
addr++; addr++;
} }
} else if (*ptr == 'k') {
dump = false;
ptr++;
uint16_t args[2];
// TODO: implement copy on non-zero segments
if (cur_seg == 0 && ReadArgs(ptr, args, 2) == 2) {
auto* src = reinterpret_cast<uint8_t*>(args[0]);
uint16_t size = args[1];
auto* dest = reinterpret_cast<uint8_t*>(cur_addr);
memcpy(dest, src, size);
}
} else { } else {
cur_addr = ReadUint16(ptr); cur_addr = ReadUint16(ptr);
} }

View File

@@ -1,7 +1,59 @@
#pragma once #pragma once
#include <stdint.h>
#define kLpt1 0x10 #define kLpt1 0x10
#define kUntilIdle 1 #define kUntilIdle 1
#define kOnce 0 #define kOnce 0
int runcomms(char until_idle); int runcomms(char until_idle);
int putchar(int);
int puts(const char*);
inline static uint8_t __get_far_u8__(uint16_t addr, uint16_t seg) {
register uint16_t ad asm("di") = addr;
register uint8_t ret asm("al");
asm("mov %2, %%es \t\n"
"movb %%es:(%%di), %%al \t\n"
: "=r"(ret)
: "r"(ad), "r"(seg)
: "es");
return ret;
}
inline static void __set_far_u8__(uint16_t addr, uint16_t seg, uint8_t val) {
register uint16_t ad asm("di") = addr;
register uint8_t v asm("al") = val;
asm("mov %0, %%es \t\n"
"stosb \n\t" ::"r"(seg), "r"(ad), "r"(v) : "es", "memory");
}
inline static uint16_t __readwritesector__(uint16_t cmd, uint8_t* addr, uint8_t c, uint8_t h, uint8_t s) {
register uint8_t* dest asm("bx") = addr;
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 ret asm("ax");
asm volatile("xor %%ax, %%ax \n\t"
"mov %%ax, %%es \n\t"
"mov %1, %%ax \n\t"
"int $0x13"
: "=r"(ret)
: "ir"(cmd), "r"(dest), "r"(sect), "r"(cyl),
"r"(head), "r"(drive)
);
return ret;
}
inline static uint16_t __readsector__(uint8_t* addr, uint8_t c, uint8_t h, uint8_t s) {
return __readwritesector__(0x0201, addr, c, h, s);
}
inline static uint16_t __writesector__(uint8_t* addr, uint8_t c, uint8_t h, uint8_t s) {
return __readwritesector__(0x0301, addr, c, h, s);
}

View File

@@ -46,7 +46,17 @@ void* memset(void* ptr, int val, size_t len) {
return ptr; return ptr;
} }
int _int80h_2(uint8_t fun, uint16_t arg1, uint16_t arg2) { 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 int80h_2(uint8_t fun, uint16_t arg1, uint16_t arg2) {
register uint16_t a1 asm("dx") = arg1; register uint16_t a1 asm("dx") = arg1;
register uint16_t a2 asm("cx") = arg2; register uint16_t a2 asm("cx") = arg2;
register uint8_t ah asm("ah") = fun; register uint8_t ah asm("ah") = fun;
@@ -69,7 +79,7 @@ int runcomms(char until_idle) {
asm volatile("mov $0x07, %%ah \n\t" asm volatile("mov $0x07, %%ah \n\t"
"int $0x80 \n\t" "int $0x80 \n\t"
: "=r"(ret) : "=r"(ret)
: "r"(until_idle) : "r"(ui)
: "bx", "cx", "dx", "cc", "memory"); : "bx", "cx", "dx", "cc", "memory");
return ret; return ret;
@@ -79,12 +89,12 @@ int read(int fd, void* buf, size_t size) {
if (fd != kLpt1) { if (fd != kLpt1) {
return -1; return -1;
} }
return _int80h_2(0x05, (uint16_t)buf, size); return int80h_2(0x05, (uint16_t)buf, size);
} }
int write(int fd, void* buf, size_t size) { int write(int fd, void* buf, size_t size) {
if (fd != kLpt1) { if (fd != kLpt1) {
return -1; return -1;
} }
return _int80h_2(0x06, (uint16_t)buf, size); return int80h_2(0x06, (uint16_t)buf, size);
} }