commit 789fa1306cc543b23f83ed3d8203e4cb01cfcc8d Author: Paul Mathieu Date: Sun Sep 14 15:34:16 2025 +0200 Initial commit: try make binaries 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