From 789fa1306cc543b23f83ed3d8203e4cb01cfcc8d Mon Sep 17 00:00:00 2001 From: Paul Mathieu Date: Sun, 14 Sep 2025 15:34:16 +0200 Subject: [PATCH] Initial commit: try make binaries --- Dockerfile | 29 ++++++++++ Makefile | 48 +++++++++++++++ README.md | 13 +++++ crc16.c | 34 +++++++++++ crt0.c | 19 ++++++ readfloppy.asm | 35 +++++++++++ wozmon.cc | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ writefloppy.asm | 35 +++++++++++ 8 files changed, 364 insertions(+) create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 crc16.c create mode 100644 crt0.c create mode 100644 readfloppy.asm create mode 100644 wozmon.cc create mode 100644 writefloppy.asm diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4eee2e0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +FROM ubuntu:jammy AS deps + +RUN apt-get update && apt-get install -y software-properties-common +RUN add-apt-repository ppa:tkchia/build-ia16 +RUN apt-get update && apt-get install -y gcc-ia16-elf +RUN apt-get install nasm make + + +FROM deps AS dev + +WORKDIR /workspace +RUN adduser --home /workspace paul +USER 1000:1000 + + +FROM deps AS build + +ARG TARGET + +ADD . /workspace +WORKDIR /workspace +RUN make ${TARGET} + + +FROM scratch AS export + +ARG TARGET + +COPY --from=build /workspace/${TARGET} / diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d6870ea --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +dev-image = 5150-dev + +writefloppy.bin: writefloppy.asm + nasm writefloppy.asm -o writefloppy.bin + +readfloppy.bin: readfloppy.asm + nasm readfloppy.asm -o readfloppy.bin + +crc16.s: crc16.c + ia16-elf-gcc -S -Os -o crc16.s crc16.c + +crc16.bin: crc16.s crt0.c 5150.ld + ia16-elf-gcc -o crc16.bin -Os -nostdlib crc16.s crt0.c + +wozmon.s: wozmon.cc + ia16-elf-gcc -S -Os -o wozmon.s wozmon.cc + +wozmon.bin: wozmon.s crt0.c 5150.ld + ia16-elf-gcc -o wozmon.bin -Os -nostdlib wozmon.s crt0.c + truncate -s 510 wozmon.bin + printf "\125\252" >> wozmon.bin + +.PHONY: clean +clean: ## Remove generated files + rm -rf wozmon.bin crc16.bin readfloppy.bin writefloppy.bin + +.PHONY: dev-image +dev-image: + docker build -t $(dev-image) --target dev . + +.PHONY: dev +dev: dev-image ## Launch a dev container + docker run -it --rm -v $(CURDIR):/workspace $(dev-image) + + +.PHONY: binaries +binaries: ## Build all binaries + 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=crc16.bin -o . --target=export . + docker build --build-arg TARGET=wozmon.bin -o . --target=export . + + +.PHONY: help +help: ## Show this help + @echo Noteworthy targets: + @egrep '^[a-zA-Z_-]+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' +.DEFAULT_GOAL := help diff --git a/README.md b/README.md new file mode 100644 index 0000000..4233fe0 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +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 `ia14-elf-gcc` + +The reason is uh, because you see, I did things. + +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. + +Also the wozmon binary contains special care to make it into a boot sector (0xaa55 magic). diff --git a/crc16.c b/crc16.c new file mode 100644 index 0000000..2e1e6e3 --- /dev/null +++ b/crc16.c @@ -0,0 +1,34 @@ +#include + +#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 6(%bp), %si \n" + " push (%si) \n" + " call crc16 \n" + " mov 8(%bp), %di \n" + " mov %ax, (%di) \n" + " pop %bp \n" + " lret $4 \n" + ); diff --git a/crt0.c b/crt0.c new file mode 100644 index 0000000..d8acec6 --- /dev/null +++ b/crt0.c @@ -0,0 +1,19 @@ +#include +#include + +int main(); + +asm ( + ".section .init \n" + ".global _start \n" + "_start: \n" + " jmp main \n" +); + +void* memset(void* ptr, int val, size_t len) { + uint8_t* p = ptr; + while(len--) { + *p++ = val; + } + return ptr; +} diff --git a/readfloppy.asm b/readfloppy.asm new file mode 100644 index 0000000..a69e710 --- /dev/null +++ b/readfloppy.asm @@ -0,0 +1,35 @@ +BITS 16 + +org 0xff00 + + +; actual entry point of the program, must be present +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: head, cylinder, sector, out +mov si, [bp+14] +mov dh, [si] +mov si, [bp+12] +mov ch, [si] +mov si, [bp+10] +mov cl, [si] + +int 0x13 + +mov si, [bp+8] +mov [si], ax + +pop es +pop bp +retf 8 ; 6 is 2x the number of parameters on the stack diff --git a/wozmon.cc b/wozmon.cc new file mode 100644 index 0000000..95685ed --- /dev/null +++ b/wozmon.cc @@ -0,0 +1,151 @@ +#include + +namespace { + +uint8_t getc() { + register uint8_t c asm ("al"); + asm volatile ( + "mov $0x00, %%ah\n" + "int $0x16\n" + : "=r" (c) + ); + return c; +} + +void putc(uint8_t c) { + asm volatile ( + "mov %0, %%al\n" + "mov $0x0e, %%ah\n" + "int $0x10\n" + :: "r" (c) + ); +} + +void Jump(uint32_t addr) { + auto jump = reinterpret_cast(addr); + jump(); +} + +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'); +} + +uint32_t ReadHex(const char* buf) { + uint32_t out = 0; + while (*buf == ' ') { + buf++; + } + for (int i = 0; i < 8; i++) { + uint8_t c = ReadHexNibble(*buf); + if (c > 0xf) { + break; + } + out = (out << 4) + c; + buf++; + } + + return out; +} + +void WriteHexNibble(uint8_t c) { + if (c > 9) { + putc('a' + c - 10); + } else { + putc('0' + c); + } +} + +void WriteUint32(uint32_t a) { + for (int i = 0; i < 8; i++) { + WriteHexNibble((a >> 28) & 0xf); + a <<= 4; + } +} + +void WriteUint8(uint8_t a) { + WriteHexNibble(a >> 4); + WriteHexNibble(a & 0xf); +} + +void Dump(uint32_t addr, int count) { + for (int i = 0; i < count; i++) { + putc(' '); + WriteUint8(*reinterpret_cast(addr + i)); + } +} + +void DumpHex(uint32_t addr) { + addr &= 0xfffffffc; + WriteUint32(addr); + putc(':'); + Dump(addr, 4); + putc('\r'); + putc('\n'); +} + +int FindChar(const char* buf, uint8_t c) { + int found = 0; + while (*buf) { + if (*buf == c) { + return found; + } + found++; + buf++; + } + return -1; +} + +void wozmon() { + uint32_t cur_addr = 0; + uint32_t cur_data = 0; + + char inbuf[64] = {}; + char* inptr = inbuf; + + while (1) { + uint8_t c = getc(); + putc(c); // echo + if (c == '\r') { + *inptr = 0; + if (inptr == inbuf) { + cur_addr += 4; + } else if (FindChar(inbuf, 'r') >= 0) { + Jump(cur_addr); + } else { + cur_addr = ReadHex(inbuf); + putc('\n'); + } + DumpHex(cur_addr); + int assigned = FindChar(inbuf, ':'); + if (assigned >= 0) { + cur_data = ReadHex(inbuf + assigned + 1); + *(reinterpret_cast(cur_addr)) = cur_data; + } + inptr = inbuf; + } else if (c == kBackspace) { + inptr--; + if (inptr < inbuf) { + inptr = inbuf; + continue; + } + putc(kOtherBackspace); + putc(' '); + putc(kOtherBackspace); + } else { + *inptr++ = c; + } + } +} + +} // namespace + +int main() { + wozmon(); +} diff --git a/writefloppy.asm b/writefloppy.asm new file mode 100644 index 0000000..bdf2e3c --- /dev/null +++ b/writefloppy.asm @@ -0,0 +1,35 @@ +BITS 16 + +org 0xfc00 + + +; actual entry point of the program, must be present +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: head, cylinder, sector, out +mov si, [bp+14] +mov dh, [si] +mov si, [bp+12] +mov ch, [si] +mov si, [bp+10] +mov cl, [si] + +int 0x13 + +mov si, [bp+8] +mov [si], ax + +pop es +pop bp +retf 8 ; 6 is 2x the number of parameters on the stack