Compare commits
18 Commits
0917aa1280
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b9646681e | |||
| 0e18958341 | |||
| aeee8131e7 | |||
| 8cd6652b45 | |||
| 3e471f1390 | |||
| d7e0f7b759 | |||
| aaefcc864d | |||
| a1275671b3 | |||
| 84ea4d5cae | |||
| 8818606867 | |||
| 3d90cbadf8 | |||
| 1208f1f66e | |||
| 91cf76c251 | |||
| 540f42b21b | |||
| 5e08ee648b | |||
| 798b3e8e6b | |||
| 05b733b296 | |||
| ec3fe0e07d |
6
Makefile
6
Makefile
@@ -11,13 +11,13 @@ dev: dev-image ## Launch a dev container
|
||||
|
||||
.PHONY: 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=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=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=writeio.bin -o . --target=export .
|
||||
|
||||
|
||||
120
README.md
120
README.md
@@ -1,37 +1,123 @@
|
||||
5150 stuff
|
||||
==========
|
||||
|
||||
The binary programs here use multiple conventions (more Fun!):
|
||||
- .asm files use NASM syntax and are built with `nasm xx.asm`
|
||||
- .s files use GAS syntax, they are built with `ia16-elf-gcc`
|
||||
## Layout of a bootable polOS floppy disk
|
||||
|
||||
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.
|
||||
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.
|
||||
* _fat12boot.bin_: the boot sector. It loads _polio.com_ from a FAT12-formatted
|
||||
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
|
||||
```
|
||||
bochs
|
||||
```
|
||||
to launch the simulator. You'll need to `c` it in the console for it to start.
|
||||
* switch to segment _0x200_
|
||||
* load file _ftpget.com_ to _cur-seg:0100_
|
||||
|
||||
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
|
||||
|
||||
* [Colab](https://colab.research.google.com/drive/1xGKYQJLKyabcSNYOiPumf9_-bbCbXke1?usp=sharing)
|
||||
* [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
79
ftpserve.py
Normal 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
|
||||
40
mrbridge.py
40
mrbridge.py
@@ -4,12 +4,29 @@ import threading
|
||||
import time
|
||||
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():
|
||||
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(
|
||||
"--port", help="path to the serial port", default=kDefaultSerialPort
|
||||
)
|
||||
parser.add_argument("--baudrate", default=kDefaultBaudrate)
|
||||
parser.add_argument("ws", help="URL of the websocket")
|
||||
return parser.parse_args()
|
||||
|
||||
@@ -28,28 +45,23 @@ def slowwrite(device, data):
|
||||
|
||||
def ws_thread(device, ws):
|
||||
while True:
|
||||
data = ws.recv();
|
||||
slowwrite(device, data.replace(b'\n', b'\r\n'))
|
||||
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)
|
||||
device = getserial(**vars(args))
|
||||
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__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
@@ -18,17 +18,17 @@ LDFLAGS = -mregparmcall -Wl,--gc-sections -Os -nostdlib -flto
|
||||
fat12boot.bin:
|
||||
$(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.bin: fat12boot.elf
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
paracli.elf: LDFLAGS += -T doscom.ld
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
.global _start
|
||||
_start:
|
||||
#ifndef NOBPB
|
||||
jmp l0
|
||||
jmp 0f
|
||||
nop
|
||||
|
||||
.ascii "IBM 3.1"
|
||||
@@ -18,7 +18,7 @@ bpb_localsectors: .word 720
|
||||
bpb_mediadescr: .byte 0xfd
|
||||
bpb_sectsperfat: .word 2
|
||||
|
||||
l0:
|
||||
0:
|
||||
#endif // NOBPB
|
||||
cli
|
||||
xor %ax, %ax
|
||||
|
||||
@@ -19,15 +19,15 @@ _start:
|
||||
mov bx, 0x6
|
||||
xor cx, cx
|
||||
a1:
|
||||
cmp cx, [es:si+4]
|
||||
cmp cx, [si+4]
|
||||
jge a2
|
||||
lea ax, [es:si+bx]
|
||||
lea ax, [si+bx]
|
||||
push ax
|
||||
add bl, 2
|
||||
inc cl
|
||||
jmp a1
|
||||
a2:
|
||||
call far [es:si]
|
||||
call far [si]
|
||||
pop bp
|
||||
pop di
|
||||
pop si
|
||||
|
||||
45
src/crc16.asm
Normal file
45
src/crc16.asm
Normal 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
|
||||
57
src/crc16.s
57
src/crc16.s
@@ -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"
|
||||
190
src/fat12.c
190
src/fat12.c
@@ -1,12 +1,18 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "polos.h"
|
||||
|
||||
#define kSectorsPerCluster 2
|
||||
#define kSectorsPerTrack 9
|
||||
#define kHeads 2
|
||||
#define kTracks 40
|
||||
#define kFatSizeSectors 2
|
||||
#define kRootDirSizeSectors 7
|
||||
#define kBytesPerCluster 1024
|
||||
#define kDataRegionStartSector (1 + kFatSizeSectors * 2 + kRootDirSizeSectors)
|
||||
#define kNumClusters ((kSectorsPerTrack * kHeads * kTracks - kDataRegionStartSector) / kSectorsPerCluster + 2)
|
||||
|
||||
typedef struct {
|
||||
char name[8];
|
||||
@@ -22,23 +28,12 @@ typedef struct {
|
||||
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");
|
||||
|
||||
static uint8_t rwsector(uint8_t w, int c, int h, int s, void* addr) {
|
||||
uint16_t ret;
|
||||
uint16_t cmd = w ? 0x0301 : 0x0201;
|
||||
// retry for a total of up to 3 attempts if drive not ready
|
||||
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));
|
||||
ret = __readwritesector__(cmd, addr, c, h, s) >> 8;
|
||||
if (ret != 0x80) {
|
||||
break;
|
||||
}
|
||||
@@ -47,6 +42,14 @@ static int readsector(int c, int h, int s, uint8_t* addr) {
|
||||
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) {
|
||||
int offs = cluster * 3 / 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) {
|
||||
int logicalsector =
|
||||
kDataRegionStartSector + (cluster - 2) * kSectorsPerCluster;
|
||||
@@ -65,19 +90,9 @@ static void cluster2chs(int cluster, int* c, int* h, int* s) {
|
||||
*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) {
|
||||
for (int i = 0; i < entry->size; i += kBytesPerCluster) {
|
||||
int c, h, s;
|
||||
cluster2chs(cluster, &c, &h, &s);
|
||||
if (readsector(c, h, s, addr + i)) {
|
||||
@@ -88,10 +103,10 @@ static int loadfile(direntry* entry, void* addr) {
|
||||
s = 1;
|
||||
h++;
|
||||
}
|
||||
if (h > 1) {
|
||||
h = 0;
|
||||
c++;
|
||||
}
|
||||
// if (h > 1) { // this will never happen
|
||||
// h = 0;
|
||||
// c++;
|
||||
// }
|
||||
if (readsector(c, h, s, addr + i + 512)) {
|
||||
return -5;
|
||||
};
|
||||
@@ -100,6 +115,94 @@ static int loadfile(direntry* entry, void* addr) {
|
||||
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) {
|
||||
if (readsector(0, 0, 2, fat_addr)) {
|
||||
return -2;
|
||||
@@ -115,21 +218,22 @@ int fat12_init(void* fat_addr, void* rootdir_addr) {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
direntry* file = findfile(name);
|
||||
if (!file) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
29
src/ftpget.c
29
src/ftpget.c
@@ -5,6 +5,16 @@
|
||||
|
||||
#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[]) {
|
||||
if (argc < 2) {
|
||||
return -1;
|
||||
@@ -17,17 +27,26 @@ int main(int argc, uint16_t argv[]) {
|
||||
chunksize = argv[2];
|
||||
}
|
||||
|
||||
uint8_t ok = 0x42;
|
||||
uint8_t ack = 0x42;
|
||||
int i;
|
||||
|
||||
for (int i = 0; i < size;) {
|
||||
runcomms(kUntilIdle);
|
||||
for (i = 0; i < size;) {
|
||||
// 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) {
|
||||
break;
|
||||
}
|
||||
write(kLpt1, &ok, 1);
|
||||
write(kLpt1, &ack, 1);
|
||||
i += len;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SECTIONS {
|
||||
. = 0x1000;
|
||||
. = 0x1200;
|
||||
|
||||
.text : {
|
||||
KEEP(*(.init))
|
||||
@@ -244,7 +244,7 @@ main:
|
||||
4:
|
||||
call sendbuffer # send input buffer
|
||||
1:
|
||||
mov ah, 0x07 # do parallel comms
|
||||
mov ax, 0x0700 # do parallel comms
|
||||
int 0x80
|
||||
mov ax, kRecvBufSize
|
||||
push ax
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#define kFatAddr ((void*)0x1a00)
|
||||
#define kRootDirAddr ((void*)0x1c00)
|
||||
#define kPolmonAddr ((void*)0x1000)
|
||||
#define kPolmonAddr ((void*)0x1200)
|
||||
#define kInt80Vector ((uint16_t*)(0x80 * 4))
|
||||
#define kParallelDataPort 0x3bc
|
||||
#define kParallelStatusPort 0x3bd
|
||||
@@ -18,13 +18,15 @@
|
||||
void dosdbt();
|
||||
void int80h();
|
||||
|
||||
uint8_t parabuf[kRecvBufferSize];
|
||||
uint8_t parasize;
|
||||
static uint8_t parabuf[kRecvBufferSize];
|
||||
static 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;
|
||||
if (parasize + size > sizeof(parabuf)) {
|
||||
size = sizeof(parabuf) - parasize;
|
||||
}
|
||||
memcpy(parabuf + parasize, buf, size);
|
||||
parasize += size;
|
||||
}
|
||||
|
||||
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 {
|
||||
uint8_t mosib = paracomm_nextbyte();
|
||||
__outb__(kParallelDataPort, mosib);
|
||||
@@ -65,7 +67,7 @@ uint8_t xferpara(uint8_t until_idle) {
|
||||
return paracomm_busy();
|
||||
}
|
||||
|
||||
uint8_t parasend(uint8_t* addr, uint8_t size) {
|
||||
uint16_t parasend(uint8_t* addr, uint8_t size) {
|
||||
uint8_t i;
|
||||
for (i = 0; i < size; i++) {
|
||||
if (paracomm_send(addr[i])) {
|
||||
@@ -75,7 +77,7 @@ uint8_t parasend(uint8_t* addr, uint8_t size) {
|
||||
return i;
|
||||
}
|
||||
|
||||
uint8_t pararecv(uint8_t* buf, uint8_t size) {
|
||||
uint16_t pararecv(uint8_t* buf, uint8_t size) {
|
||||
if (parasize == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
43
src/polio.s
43
src/polio.s
@@ -40,20 +40,6 @@ dosdbt: # assumes es=0
|
||||
|
||||
.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
|
||||
@@ -70,6 +56,7 @@ readsector:
|
||||
mov ch, [bp+2] # c
|
||||
mov dh, [bp+4] # h
|
||||
mov cl, [bp+6] # s
|
||||
mov dl, 0 # drive
|
||||
int 0x13
|
||||
ret
|
||||
|
||||
@@ -121,8 +108,19 @@ readfile:
|
||||
add ax, si
|
||||
mov dx, [bp+2]
|
||||
add dx, si
|
||||
call fat12_readfile
|
||||
ret
|
||||
jmp fat12_readfile
|
||||
|
||||
# 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
|
||||
sendpara:
|
||||
@@ -132,8 +130,7 @@ sendpara:
|
||||
mov ax, [bp+0]
|
||||
add ax, si
|
||||
mov dx, [bp+2]
|
||||
call parasend
|
||||
ret
|
||||
jmp parasend
|
||||
|
||||
# params: ds:addr, size
|
||||
recvpara:
|
||||
@@ -143,7 +140,10 @@ recvpara:
|
||||
mov ax, [bp+0]
|
||||
add ax, si
|
||||
mov dx, [bp+2]
|
||||
call pararecv
|
||||
jmp pararecv
|
||||
|
||||
dummy:
|
||||
mov ax, -1
|
||||
ret
|
||||
|
||||
.section .text.int80h
|
||||
@@ -154,6 +154,7 @@ int80h:
|
||||
push bp
|
||||
push ds
|
||||
push es
|
||||
mov bp, sp
|
||||
xor cx, cx
|
||||
mov es, cx
|
||||
mov ds, cx
|
||||
@@ -162,7 +163,6 @@ int80h:
|
||||
cmp cl, offset int80h_entries
|
||||
jge 0f
|
||||
mov si, cx
|
||||
mov bp, sp
|
||||
add bp, 16 # 10 for us, 6 for the interrupt
|
||||
call cs:[int80h_table+si]
|
||||
jmp 1f
|
||||
@@ -178,7 +178,7 @@ int80h:
|
||||
|
||||
.section .rodata.int80h
|
||||
int80h_table:
|
||||
.word offset copy # 0x00
|
||||
.word offset dummy # 0x00
|
||||
.word offset readsector # 0x01
|
||||
.word offset writesector # 0x02
|
||||
.word offset formattrack # 0x03
|
||||
@@ -186,5 +186,6 @@ int80h_table:
|
||||
.word offset recvpara # 0x05
|
||||
.word offset sendpara # 0x06
|
||||
.word offset xferpara # 0x07
|
||||
.word offset writefile # 0x08
|
||||
|
||||
int80h_entries = . - int80h_table
|
||||
|
||||
160
src/polmon.cc
160
src/polmon.cc
@@ -2,6 +2,8 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "polos.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kDumpSize = 16;
|
||||
@@ -81,21 +83,6 @@ void WriteUint16ln(uint16_t a) {
|
||||
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__() {
|
||||
asm volatile("movw $0x40, %ax \n\t"
|
||||
"mov %ax, %ds \n\t"
|
||||
@@ -140,23 +127,6 @@ void ClearScreen() {
|
||||
: "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');
|
||||
@@ -194,34 +164,41 @@ uint16_t __readfile__(const char* name, uint16_t addr) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint16_t LaunchFile(const char* name, uint8_t len, uint8_t argc,
|
||||
uint16_t* argv) {
|
||||
constexpr uint16_t kSegment = 0x200;
|
||||
uint16_t LoadFile(const char* name, uint8_t len, uint16_t seg) {
|
||||
constexpr uint16_t kAddress = 0x100;
|
||||
constexpr uint16_t kHeaderAddress = (kSegment << 4);
|
||||
constexpr uint16_t kFlatAddress = (kSegment << 4) + kAddress;
|
||||
constexpr uint16_t kArgvAddress = 0x0004;
|
||||
// trampoline:
|
||||
// call 0x100
|
||||
// retf
|
||||
// XXX: if you change kAddress, the trampoline needs to be updated
|
||||
constexpr uint8_t kTrampoline[] = {0xe8, 0xfd, 0x00, 0xcb};
|
||||
|
||||
uint16_t headeraddr = (seg << 4);
|
||||
uint16_t flataddr = headeraddr + kAddress;
|
||||
|
||||
char fatname[11];
|
||||
tofatname(name, len, fatname);
|
||||
|
||||
uint16_t r = __readfile__(fatname, kFlatAddress);
|
||||
uint16_t r = __readfile__(fatname, flataddr);
|
||||
if (r) {
|
||||
memcpy(fatname + 8, "COM", 3);
|
||||
r = __readfile__(fatname, kFlatAddress);
|
||||
r = __readfile__(fatname, flataddr);
|
||||
}
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
auto* ptr = reinterpret_cast<uint8_t*>(kHeaderAddress);
|
||||
auto* ptr = reinterpret_cast<uint8_t*>(headeraddr);
|
||||
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);
|
||||
|
||||
register uint16_t ac asm("ax") = argc;
|
||||
@@ -235,8 +212,13 @@ uint16_t LaunchFile(const char* name, uint8_t len, uint8_t argc,
|
||||
"xchg %%cx, %%sp \n\t"
|
||||
"sti \n\t"
|
||||
"push %%cx \n\t"
|
||||
"lcall %2,$0 \n\t"
|
||||
"push %2 \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, %%es \n\t"
|
||||
"cli \n\t"
|
||||
@@ -244,7 +226,7 @@ uint16_t LaunchFile(const char* name, uint8_t len, uint8_t argc,
|
||||
"mov %%cx, %%ss \n\t"
|
||||
"sti"
|
||||
: "+r"(ac), "+r"(av)
|
||||
: "i"(kSegment), "i"(kAddress)
|
||||
: "r"(seg), "i"(kAddress)
|
||||
: "bx", "cx", "memory", "cc");
|
||||
|
||||
return ac;
|
||||
@@ -258,7 +240,7 @@ asm(" .section .text.cbreak \n"
|
||||
" xor %ax, %ax \n"
|
||||
" mov %ax, %ds \n"
|
||||
" cli \n"
|
||||
" mov %ax, %ds \n"
|
||||
" mov %ax, %ss \n"
|
||||
" mov $0x2000, %sp \n"
|
||||
" sti \n"
|
||||
" pushf \n"
|
||||
@@ -275,13 +257,43 @@ void InstallCtrlBreak() {
|
||||
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 dump = true;
|
||||
for (const char* ptr = buf; *ptr;) {
|
||||
if (*ptr == 's') {
|
||||
cur_addr = 0;
|
||||
ptr += 1;
|
||||
cur_seg = ReadUint16(ptr);
|
||||
cur_addr = 0;
|
||||
ReadArgs(ptr, &cur_seg, 1);
|
||||
} else if (*ptr == '$') {
|
||||
__basic__();
|
||||
} else if (*ptr == 'j') {
|
||||
@@ -292,44 +304,27 @@ bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
|
||||
WriteUint16ln(ret);
|
||||
} else if (*ptr == 'i') {
|
||||
dump = false;
|
||||
ptr++;
|
||||
uint16_t args[8]; // ought to be enough for everybody
|
||||
uint8_t nargs = 0;
|
||||
ptr++;
|
||||
int fun = ReadUint8(ptr) << 8;
|
||||
fun += ReadUint8(ptr);
|
||||
for (; *ptr && nargs < 8;) {
|
||||
if (*ptr == ' ') {
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
args[nargs++] = ReadUint16(ptr);
|
||||
}
|
||||
uint16_t ret = Int80h(fun, nargs, args);
|
||||
int nargs = ReadArgs(ptr, args, 8);
|
||||
uint16_t fun = args[0];
|
||||
uint16_t ret = Int80h(fun, nargs - 1, args + 1);
|
||||
WriteUint16ln(ret);
|
||||
} else if (*ptr == 'l') {
|
||||
dump = false;
|
||||
ClearScreen();
|
||||
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') {
|
||||
dump = false;
|
||||
ptr++;
|
||||
uint16_t args[8]; // ought to be enough for everybody
|
||||
uint8_t nargs = 0;
|
||||
ptr++;
|
||||
while (*ptr == ' ') {
|
||||
ptr++;
|
||||
}
|
||||
const char* fname = ptr;
|
||||
int l;
|
||||
for (l = 0; *ptr && *ptr != ' '; l++, ptr++) {
|
||||
}
|
||||
for (; *ptr && nargs < 8;) {
|
||||
if (*ptr == ' ') {
|
||||
ptr++;
|
||||
continue;
|
||||
}
|
||||
args[nargs++] = ReadUint16(ptr);
|
||||
}
|
||||
uint16_t ret = LaunchFile(fname, l, nargs, args);
|
||||
int nargs = ReadArgs(ptr, args, 8);
|
||||
uint16_t ret = Exec(cur_seg, nargs, args);
|
||||
WriteUint16ln(ret);
|
||||
} else if (*ptr == 'w') {
|
||||
dump = false;
|
||||
@@ -344,6 +339,17 @@ bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
|
||||
__set_far_u8__(addr, cur_seg, d);
|
||||
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 {
|
||||
cur_addr = ReadUint16(ptr);
|
||||
}
|
||||
|
||||
39
src/polos.h
39
src/polos.h
@@ -8,6 +8,9 @@
|
||||
|
||||
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");
|
||||
@@ -18,3 +21,39 @@ inline static uint8_t __get_far_u8__(uint16_t addr, uint16_t 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);
|
||||
}
|
||||
|
||||
|
||||
18
src/stdlib.c
18
src/stdlib.c
@@ -46,7 +46,17 @@ void* memset(void* ptr, int val, size_t len) {
|
||||
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 a2 asm("cx") = arg2;
|
||||
register uint8_t ah asm("ah") = fun;
|
||||
@@ -69,7 +79,7 @@ int runcomms(char until_idle) {
|
||||
asm volatile("mov $0x07, %%ah \n\t"
|
||||
"int $0x80 \n\t"
|
||||
: "=r"(ret)
|
||||
: "r"(until_idle)
|
||||
: "r"(ui)
|
||||
: "bx", "cx", "dx", "cc", "memory");
|
||||
|
||||
return ret;
|
||||
@@ -79,12 +89,12 @@ int read(int fd, void* buf, size_t size) {
|
||||
if (fd != kLpt1) {
|
||||
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) {
|
||||
if (fd != kLpt1) {
|
||||
return -1;
|
||||
}
|
||||
return _int80h_2(0x06, (uint16_t)buf, size);
|
||||
return int80h_2(0x06, (uint16_t)buf, size);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user