Initial commit: try make binaries

This commit is contained in:
Paul Mathieu 2025-09-14 15:34:16 +02:00
commit 789fa1306c
8 changed files with 364 additions and 0 deletions

29
Dockerfile Normal file
View File

@ -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} /

48
Makefile Normal file
View File

@ -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

13
README.md Normal file
View File

@ -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).

34
crc16.c Normal file
View File

@ -0,0 +1,34 @@
#include <stdint.h>
#define kDataAddr ((uint8_t*) 0x0000)
uint16_t crc16(int data_len) {
const uint8_t* data = kDataAddr;
uint16_t crc = 0xFFFF;
for (unsigned int i = 0; i < data_len; ++i) {
uint16_t dbyte = data[i];
crc ^= dbyte << 8;
for (unsigned char j = 0; j < 8; ++j) {
uint16_t mix = crc & 0x8000;
crc = (crc << 1);
if (mix)
crc = crc ^ 0x1021;
}
}
return crc;
}
asm (
".global main \n"
"main: \n"
" push %bp \n"
" mov %sp, %bp \n"
" mov 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"
);

19
crt0.c Normal file
View File

@ -0,0 +1,19 @@
#include <stdint.h>
#include <string.h>
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;
}

35
readfloppy.asm Normal file
View File

@ -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

151
wozmon.cc Normal file
View File

@ -0,0 +1,151 @@
#include <cstdint>
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<void (*)()>(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<uint8_t*>(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<uint32_t*>(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();
}

35
writefloppy.asm Normal file
View File

@ -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