From 8b434f4434e8e9264e7ce1488533a3dc8aab6257 Mon Sep 17 00:00:00 2001 From: Paul Mathieu Date: Mon, 29 Sep 2025 18:00:55 +0200 Subject: [PATCH] Add paracli, a parallel port comm utility --- Makefile | 6 ++- doscom.ld | 20 +++++++++ paracli.s | 80 +++++++++++++++++++++++++++++++++ paracomm.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++ paracomm.h | 19 ++++++++ 5 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 doscom.ld create mode 100644 paracli.s create mode 100644 paracomm.c create mode 100644 paracomm.h diff --git a/Makefile b/Makefile index e21197f..4fde358 100644 --- a/Makefile +++ b/Makefile @@ -48,11 +48,15 @@ wozmon.elf: CPPFLAGS += -DNOBPB wozmon.bin: wozmon.elf -polos.img: fat12boot.bin polmon.com polio.com +paracli.elf: LDFLAGS += -T doscom.ld +paracli.elf: paracli.s paracomm.o + +polos.img: fat12boot.bin polmon.com polio.com paracli.com dd if=/dev/zero of=$@ bs=512 count=720 mformat -i $@ -t 40 -h 2 -s 9 mcopy -i $@ polio.com ::/ mcopy -i $@ polmon.com ::/ + mcopy -i $@ paracli.com ::/ dd if=fat12boot.bin of=$@ conv=notrunc .PHONY: clean diff --git a/doscom.ld b/doscom.ld new file mode 100644 index 0000000..d63889a --- /dev/null +++ b/doscom.ld @@ -0,0 +1,20 @@ +SECTIONS { + . = 0x100; + + .text : { + KEEP(*(.init)) + *(.text*) + } + + .rodata : { + *(.rodata*) + } + + .bss (NOLOAD) : { + * (.bss) + } + + .data : { + *(.data) + } +} diff --git a/paracli.s b/paracli.s new file mode 100644 index 0000000..d04cf7f --- /dev/null +++ b/paracli.s @@ -0,0 +1,80 @@ + .arch i8086,jumps + .code16 + .intel_syntax noprefix + + .section .init + .global _start +_start: + mov ax, cs + mov ds, ax + mov es, ax + cli + mov ss, ax + mov sp, 0x100 + sti + jmp main + + .section .text.parcb +parcb: + push bx + push si + push bp + mov si, ax +1: + test dx, dx + jz 0f + mov al, [si] + mov ah, 0x0e + mov bh, 0 + int 0x10 + dec dx + inc si + jmp 1b +0: + pop bp + pop si + pop bx + ret + + .section .rodata.l0 +.l0: .ascii "> " +.l1: .ascii "." + +delay = 500 + + .section .text.main + .global main +main: + mov ax, 0x0002 + int 0x10 + mov ax, offset parcb + call paracomm_init +0: + mov ah, 0x01 + int 0x16 + jz 1f + mov ah, 0x00 + int 0x16 + mov ah, 0 + call paracomm_send + test ax, ax + jz 0b +1: + call paracomm_nextbyte + mov dx, 0x3bc + out dx, al + add dl, 2 + mov al, 1 + out dx, al + mov cx, delay +2: loop 2b + mov al, 0 + out dx, al + mov cx, delay +3: loop 3b + dec dl + in al, dx + mov cl, 4 + shr al, cl + call paracomm_feed + jmp 0b diff --git a/paracomm.c b/paracomm.c new file mode 100644 index 0000000..557f8b4 --- /dev/null +++ b/paracomm.c @@ -0,0 +1,127 @@ +#include "paracomm.h" + +#include + +#define kMosiIdleByte 0x00 +#define kMosiStartByte 0x42 +#define kMisoIdleNibble 0x0 +#define kMisoStartNibble 0xa + +static uint8_t mosi_workbuf[2][256]; +static uint8_t mosi_workbuf_idx; +static uint8_t mosi_workbuf_size; + +static uint8_t* mosi_sendbuf; +static uint8_t mosi_size; +static uint8_t mosi_sent; + +static uint8_t mosi_state; + +static uint8_t miso_recvbuf[256]; +static uint8_t miso_size; +static uint8_t miso_received_nibbles; +static uint8_t miso_state; +static void (*miso_cb)(const uint8_t*, uint8_t); + +static void swapbuffers() { + mosi_sendbuf = mosi_workbuf[mosi_workbuf_idx]; + mosi_size = mosi_workbuf_size; + + mosi_workbuf_size = 0; + mosi_workbuf_idx = (mosi_workbuf_idx + 1) % 2; +} + +uint8_t paracomm_nextbyte() { + switch (mosi_state) { + case 0: + if (mosi_size == 0) { + swapbuffers(); + if (mosi_size == 0) { + return kMosiIdleByte; + } + } + mosi_state = 1; + return kMosiStartByte; + case 1: + // assert(mosi_size > 0) + mosi_sent = 0; + mosi_state = 2; + return mosi_size; + case 2: + { + uint8_t b = mosi_sendbuf[mosi_sent]; + mosi_sent += 1; + + if (mosi_sent == mosi_size) { + swapbuffers(); + mosi_state = 0; + } + + return b; + } + } + + __builtin_unreachable(); +} + +void paracomm_feed(uint8_t n) { + switch (miso_state) { + case 0: + if (n == kMisoStartNibble) { + miso_state = 1; + } else if (n == kMisoIdleNibble) { + } else { + // error: spurious nibble + } + break; + case 1: + miso_size = n; + miso_state = 2; + break; + case 2: + miso_size += n << 4; + miso_received_nibbles = 0; + miso_state = 3; + break; + case 3: + { + uint8_t idx = miso_received_nibbles / 2; + if (miso_received_nibbles % 2 == 0) { + miso_recvbuf[idx] = n; + } else { + miso_recvbuf[idx] += n << 4; + } + miso_received_nibbles += 1; + + if (miso_received_nibbles == 2*miso_size) { + if (miso_cb != 0) { + miso_cb(miso_recvbuf, miso_size); + } + miso_state = 0; + } + } + break; + } +} + +void paracomm_init(miso_cb_t cb) { + mosi_size = 0; + mosi_workbuf_idx = 0; + mosi_workbuf_size = 0; + mosi_state = 0; + + miso_state = 0; + miso_cb = cb; +} + +int paracomm_send(uint8_t b) { + if (mosi_workbuf_size == 256) { + return -1; + } + + uint8_t* buff = mosi_workbuf[mosi_workbuf_idx]; + buff[mosi_workbuf_size] = b; + mosi_workbuf_size += 1; + + return 0; +} diff --git a/paracomm.h b/paracomm.h new file mode 100644 index 0000000..f150163 --- /dev/null +++ b/paracomm.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +typedef void (*miso_cb_t)(const uint8_t* buff, uint8_t size); + +/** Must call first **/ +void paracomm_init(miso_cb_t miso_cb); + +/** Sends a single byte. + * Returns: 0 if no error, non-0 otherwise. + */ +int paracomm_send(uint8_t b); + +/** Call after reading a nibble from the port */ +void paracomm_feed(uint8_t n); + +/** Yields the next byte to send out the port */ +uint8_t paracomm_nextbyte();