Compare commits

...

14 Commits

26 changed files with 1210 additions and 446 deletions

9
.clang-format Normal file
View File

@@ -0,0 +1,9 @@
---
Language: Cpp
BasedOnStyle: LLVM
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
IndentWidth: 4
InsertNewlineAtEOF: true
PointerAlignment: Left
SpacesBeforeTrailingComments: 2

4
.gitignore vendored
View File

@@ -1,3 +1,5 @@
*.bin
*.o
*.elf
*.com
polos.img
polmon.com

View File

@@ -1,7 +1,7 @@
dev-image = 5150-dev
%.bin: %.asm
nasm $? -o $@
nasm $< -o $@
crc16.s: crc16.c
ia16-elf-gcc -S -Os -o crc16.s crc16.c
@@ -19,23 +19,23 @@ LDFLAGS = -mregparmcall -Wl,--gc-sections -Os -nostdlib -flto
%.elf:
$(LD) $(LDFLAGS) $(CPPFLAGS) -o $@ $^
bootsectors = fat12boot.bin wozmon.bin
%.com: %.elf
ia16-elf-objcopy -O binary $< $@
bootsectors = fat12boot.bin wozmon.bin
$(bootsectors):
ia16-elf-objcopy -O binary $? $@
truncate -s 510 $@
printf "\125\252" >> $@
ia16-elf-objcopy -O binary $< $@
fat12boot.elf: fat12boot.o fat12.o bootsect.S
fat12boot.elf: LDFLAGS += -T bootsect.ld
fat12boot.bin: fat12boot.elf
polmon.elf: LDFLAGS += -T flat0600.ld
polmon.elf: polmon.o
polmon.elf: LDFLAGS += -T flat1000.ld
polmon.elf: polmon.o stdlib.o
polmon.com: polmon.elf
ia16-elf-objcopy -O binary $? $@
polio.elf: LDFLAGS += -T flat0600.ld
polio.elf: polio.s fat12.o
wozmon.o: polmon.cc
wozmon.o: CPPFLAGS = -DWOZMON=1
@@ -48,15 +48,20 @@ wozmon.elf: CPPFLAGS += -DNOBPB
wozmon.bin: wozmon.elf
polos.img: fat12boot.bin polmon.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 $@ polmon.com ::/polmon.com
mcopy -i $@ polio.com ::/
mcopy -i $@ polmon.com ::/
mcopy -i $@ paracli.com ::/
dd if=fat12boot.bin of=$@ conv=notrunc
.PHONY: clean
clean: ## Remove generated files
rm -rf *.bin polmon.com polos.img
rm -rf *.bin *.elf *.o *.com polos.img
.PHONY: dev-image
dev-image:
@@ -68,7 +73,7 @@ dev: dev-image ## Launch a dev container
.PHONY: binaries
binaries: ## Build all binaries
binaries: ## Build all small 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 .
@@ -76,11 +81,9 @@ binaries: ## Build all binaries
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=debug.bin -o . --target=export .
docker build --build-arg TARGET=dosdbt.bin -o . --target=export .
docker build --build-arg TARGET=format.bin -o . --target=export .
docker build --build-arg TARGET=polmon.com -o . --target=export .
docker build --build-arg TARGET=fat12boot.bin -o . --target=export .
docker build --build-arg TARGET=readio.bin -o . --target=export .
docker build --build-arg TARGET=writeio.bin -o . --target=export .
.PHONY: floppy
floppy: ## Make a bootable floppy image

193
arduino/kbd/kbd.cc Normal file
View File

@@ -0,0 +1,193 @@
// keyboard wiring diagram:
// https://www.minuszerodegrees.net/5150/misc/5150_keyboard_reset.jpg
#include "Arduino.h"
constexpr int kClockPin = PC6;
constexpr int kDataPin = PC4;
constexpr int kBitDelayMicros = 100;
constexpr int kCodeDelayMicros = 200;
constexpr int kKeyDelayMicros = 100;
namespace {
void sendCode(int code) {
// preconditions: clock and data pins are INPUT
while (digitalRead(kDataPin) == LOW || digitalRead(kClockPin) == LOW) {
// wait. we're not allowed to send
}
digitalWrite(kDataPin, LOW);
digitalWrite(kClockPin, LOW);
pinMode(kClockPin, OUTPUT);
delayMicroseconds(kBitDelayMicros);
pinMode(kClockPin, INPUT);
// delayMicroseconds(kDelayMicros);
for (int i = 0; i < 8; i++) {
if ((code & (1 << i)) == 0) {
// send a 0
pinMode(kDataPin, OUTPUT);
} // else do nothing, it's already a 1
delayMicroseconds(kBitDelayMicros);
pinMode(kClockPin, OUTPUT);
delayMicroseconds(kBitDelayMicros);
pinMode(kDataPin, INPUT);
pinMode(kClockPin, INPUT);
}
delayMicroseconds(kCodeDelayMicros);
}
enum State {
kReady,
kMaybeReset,
};
constexpr int kResetDelay = 19;
constexpr int kLetterCodes[] = {
0x1E, // 'a'
0x30, 0x2E, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24,
0x25, 0x26, 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F,
0x14, 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2c,
};
constexpr int kNumberCodes[] = {
0x0B, // '0'
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
};
void sendNormalCode(int code) {
sendCode(code);
sendCode(code | 0x80);
}
void sendShiftCode(int code) {
sendCode(42); // left shift
sendNormalCode(code);
sendCode(42 | 0x80);
}
void sendCtrlAltDel() {
sendCode(0x1d); // ctrl
sendCode(0x38); // alt
sendCode(0x53); // del
sendCode(0x53 | 0x80);
sendCode(0x38 | 0x80);
sendCode(0x1d | 0x80);
}
void sendCtrlBreak() {
sendCode(0x1d); // ctrl
sendCode(0x46); // break
sendCode(0x46 | 0x80);
sendCode(0x1d | 0x80);
}
} // namespace
void sendAsciiChar(int c) {
if (c >= 'a' && c <= 'z') {
return sendNormalCode(kLetterCodes[c - 'a']);
} else if (c >= '0' && c <= '9') {
return sendNormalCode(kNumberCodes[c - '0']);
} else if (c >= 'A' && c <= 'Z') {
return sendShiftCode(kLetterCodes[c - 'A']);
}
switch (c) {
case '-':
return sendNormalCode(12);
case '=':
return sendNormalCode(13);
case '[':
return sendNormalCode(26);
case ']':
return sendNormalCode(27);
case ';':
return sendNormalCode(39);
case '\'':
return sendNormalCode(40);
case ',':
return sendNormalCode(51);
case '.':
return sendNormalCode(52);
case '/':
return sendNormalCode(53);
case ' ':
return sendNormalCode(57);
case '\n':
return sendNormalCode(28);
case '!':
return sendShiftCode(2);
case '@':
return sendShiftCode(3);
case '#':
return sendShiftCode(4);
case '$':
return sendShiftCode(5);
case '%':
return sendShiftCode(6);
case '^':
return sendShiftCode(7);
case '&':
return sendShiftCode(8);
case '*':
return sendShiftCode(9);
case '(':
return sendShiftCode(10);
case ')':
return sendShiftCode(11);
case '_':
return sendShiftCode(12);
case '+':
return sendShiftCode(13);
case '{':
return sendShiftCode(26);
case '}':
return sendShiftCode(27);
case ':':
return sendShiftCode(39);
case '"':
return sendShiftCode(40);
case '<':
return sendShiftCode(51);
case '>':
return sendShiftCode(52);
case '?':
return sendShiftCode(53);
case 0:
return sendCtrlAltDel();
case 27:
return sendCtrlBreak(); // escape
}
}
void setupKbd() {
pinMode(kClockPin, INPUT);
pinMode(kDataPin, INPUT);
}
void checkKbdReset() {
static State state = kReady;
static int lastclocklow = 0;
int clockp = digitalRead(kClockPin);
if (state == State::kReady && clockp == LOW) {
state = State::kMaybeReset;
lastclocklow = millis();
} else if (state == State::kMaybeReset) {
if (clockp == HIGH && millis() - lastclocklow > kResetDelay) {
delay(1);
state = State::kReady;
sendCode(0xaa);
Serial.println("Reset!");
}
}
}

5
arduino/kbd/kbd.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
void sendAsciiChar(int c);
void setupKbd();
void checkKbdReset();

View File

@@ -1,190 +1,39 @@
// keyboard wiring diagram: https://www.minuszerodegrees.net/5150/misc/5150_keyboard_reset.jpg
#include "kbd.h"
#include "paracomm.h"
#include "parallel.h"
constexpr int kClockPin = PC6;
constexpr int kDataPin = PC4;
constexpr int kBitDelayMicros = 100;
constexpr int kCodeDelayMicros = 200;
constexpr int kKeyDelayMicros = 100;
ParaComms pc;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(kClockPin, INPUT);
pinMode(kDataPin, INPUT);
setupKbd();
setupParallel();
pc = ParaComms{
.mosi_cb =
[](uint8_t *data, uint8_t len) {
for (int i = 0; i < len; i++) {
Serial.printf("%c", data[i]);
pc.SendByte(data[i]); // echo
if (data[i] == '\r') {
pc.SendByte('\n');
}
}
},
};
Serial.println("kbd 0.1");
}
// 0. reset
// 0.0. clock line will be held low externally for 20 ms
// when back up, keyboard needs to clock 0xAA as a response
//
// 1. operation
// 1.0. just directly clock out scan codes of pressed keys
void sendCode(int code) {
Serial.printf("sending code 0x%02x\n", code);
// preconditions: clock and data pins are INPUT
while (digitalRead(kDataPin) == LOW || digitalRead(kClockPin) == LOW) {
// wait. we're not allowed to send
}
digitalWrite(kDataPin, LOW);
digitalWrite(kClockPin, LOW);
pinMode(kClockPin, OUTPUT);
delayMicroseconds(kBitDelayMicros);
pinMode(kClockPin, INPUT);
// delayMicroseconds(kDelayMicros);
for (int i = 0; i < 8; i++) {
if ((code & (1 << i)) == 0) {
// send a 0
pinMode(kDataPin, OUTPUT);
} // else do nothing, it's already a 1
delayMicroseconds(kBitDelayMicros);
pinMode(kClockPin, OUTPUT);
delayMicroseconds(kBitDelayMicros);
pinMode(kDataPin, INPUT);
pinMode(kClockPin, INPUT);
}
delayMicroseconds(kCodeDelayMicros);
}
enum State {
kReady,
kMaybeReset,
};
constexpr int kResetDelay = 19;
State state = State::kReady;
int lastclocklow = 0;
constexpr int kLetterCodes[] = {
0x1E, // 'a'
0x30,
0x2E,
0x20,
0x12,
0x21,
0x22,
0x23,
0x17,
0x24,
0x25,
0x26,
0x32,
0x31,
0x18,
0x19,
0x10,
0x13,
0x1F,
0x14,
0x16,
0x2F,
0x11,
0x2D,
0x15,
0x2c,
};
constexpr int kNumberCodes[] = {
0x0B, // '0'
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
};
void sendNormalCode(int code) {
sendCode(code);
sendCode(code | 0x80);
}
void sendShiftCode(int code) {
sendCode(42); // left shift
sendNormalCode(code);
sendCode(42 | 0x80);
}
void sendCtrlAltDel() {
sendCode(0x1d); // ctrl
sendCode(0x38); // alt
sendCode(0x53); // del
sendCode(0x53 | 0x80);
sendCode(0x38 | 0x80);
sendCode(0x1d | 0x80);
}
void sendCtrlBreak() {
sendCode(0x1d); // ctrl
sendCode(0x46); // break
sendCode(0x46 | 0x80);
sendCode(0x1d | 0x80);
}
void sendAsciiChar(int c) {
if (c >= 'a' && c <= 'z') {
return sendNormalCode(kLetterCodes[c - 'a']);
} else if (c >= '0' && c <= '9') {
return sendNormalCode(kNumberCodes[c - '0']);
} else if (c >= 'A' && c <= 'Z') {
return sendShiftCode(kLetterCodes[c - 'A']);
}
switch (c) {
case '-': return sendNormalCode(12);
case '=': return sendNormalCode(13);
case '[': return sendNormalCode(26);
case ']': return sendNormalCode(27);
case ';': return sendNormalCode(39);
case '\'': return sendNormalCode(40);
case ',': return sendNormalCode(51);
case '.': return sendNormalCode(52);
case '/': return sendNormalCode(53);
case ' ': return sendNormalCode(57);
case '\n': return sendNormalCode(28);
case '!': return sendShiftCode(2);
case '@': return sendShiftCode(3);
case '#': return sendShiftCode(4);
case '$': return sendShiftCode(5);
case '%': return sendShiftCode(6);
case '^': return sendShiftCode(7);
case '&': return sendShiftCode(8);
case '*': return sendShiftCode(9);
case '(': return sendShiftCode(10);
case ')': return sendShiftCode(11);
case '_': return sendShiftCode(12);
case '+': return sendShiftCode(13);
case '{': return sendShiftCode(26);
case '}': return sendShiftCode(27);
case ':': return sendShiftCode(39);
case '"': return sendShiftCode(40);
case '<': return sendShiftCode(51);
case '>': return sendShiftCode(52);
case '?': return sendShiftCode(53);
case 0: return sendCtrlAltDel();
case 27: return sendCtrlBreak(); // escape
}
}
void loop() {
static int led_counter = 0;
static int led = HIGH;
static int mode = 0; // 0 = keyboard, 1 = parallel
if (led_counter > 400000) {
led_counter = 0;
led = (led == HIGH) ? LOW : HIGH;
@@ -192,22 +41,27 @@ void loop() {
}
led_counter += 1;
int clockp = digitalRead(kClockPin);
if (state == State::kReady && clockp == LOW) {
state = State::kMaybeReset;
lastclocklow = millis();
} else if (state == State::kMaybeReset) {
if (clockp == HIGH && millis() - lastclocklow > kResetDelay) {
delay(1);
state = State::kReady;
sendCode(0xaa);
Serial.println("Reset!");
}
checkKbdReset();
uint8_t mosib;
if (strobeOccurred(mosib)) {
pc.FeedMosiData(mosib);
uint8_t misob = pc.NextMisoNibble();
writeParallel(misob);
}
if (Serial.available() > 0) {
int c = Serial.read();
if (c == 2) {
mode = 0;
} else if (c == 3) {
mode = 1;
} else {
if (mode == 0) {
sendAsciiChar(c);
//delayMicroseconds(kKeyDelayMicros);
} else {
pc.SendByte(c);
}
}
}
}

124
arduino/kbd/paracomm.h Normal file
View File

@@ -0,0 +1,124 @@
#pragma once
#include <stdint.h>
struct ParaComms {
using MessageCallback = void (*)(uint8_t *, uint8_t);
enum State {
kIdle,
kLength,
kLength2, // for miso
kData,
// kCrc, // ?
};
static constexpr uint8_t kMosiStartByte = 0x42;
static constexpr uint8_t kIdleByte = 0x00;
static constexpr uint8_t kMisoStartNibble = 0xa;
static constexpr uint8_t kMisoIdleNibble = 0x0;
MessageCallback mosi_cb = nullptr;
uint8_t *miso_buffer = nullptr;
uint8_t miso_size = 0;
uint8_t miso_nibbles_sent = 0;
State miso_state = kIdle;
// double work buffer. one being sent, one being written to
uint8_t miso_workbuf[2][256];
uint8_t miso_workbuf_idx = 0;
uint8_t miso_workbuf_size = 0;
uint8_t mosi_buffer[256];
uint8_t mosi_size = 0;
uint8_t mosi_received = 0;
State mosi_state = kIdle;
void SwapBuffers() {
miso_buffer = miso_workbuf[miso_workbuf_idx];
miso_size = miso_workbuf_size;
miso_workbuf_idx = (miso_workbuf_idx + 1) % 2;
miso_workbuf_size = 0;
}
int SendByte(uint8_t b) {
if (miso_workbuf_size == 256) {
return -1; // sorry we're full
}
uint8_t *buff = miso_workbuf[miso_workbuf_idx];
buff[miso_workbuf_size] = b;
miso_workbuf_size += 1;
return 0;
}
void FeedMosiData(uint8_t b) {
switch (mosi_state) {
case kIdle:
// check start byte
if (b == kMosiStartByte) {
mosi_state = kLength;
} else if (b == kIdleByte) {
// just twiddling our thumb drives
} else {
// Serial.printf("sp: %02x\n", b);
}
break;
case kLength:
// assert(b > 0)
mosi_size = b;
mosi_received = 0;
mosi_state = kData;
break;
case kData:
mosi_buffer[mosi_received] = b;
mosi_received += 1;
if (mosi_received == mosi_size) {
if (mosi_cb != nullptr) {
mosi_cb(mosi_buffer, mosi_received);
}
mosi_state = kIdle;
}
case kLength2: // error
return;
}
}
uint8_t NextMisoNibble() {
switch (miso_state) {
case kIdle:
if (miso_size == 0) {
SwapBuffers();
if (miso_size == 0) {
return kMisoIdleNibble;
}
}
miso_state = kLength;
return kMisoStartNibble;
case kLength:
// assert(miso_size > 0);
miso_state = kLength2;
return miso_size & 0xf;
case kLength2:
miso_nibbles_sent = 0;
miso_state = kData;
return miso_size >> 4;
case kData: {
uint8_t b = miso_buffer[miso_nibbles_sent / 2];
if (miso_nibbles_sent % 2 == 0) {
b &= 0xf;
} else {
b >>= 4;
}
miso_nibbles_sent += 1;
if (miso_nibbles_sent == miso_size * 2) {
SwapBuffers();
miso_state = kIdle;
}
return b;
}
}
__builtin_unreachable();
}
};

95
arduino/kbd/parallel.cc Normal file
View File

@@ -0,0 +1,95 @@
#include "parallel.h"
#include <stdint.h>
#include "Arduino.h"
namespace {
constexpr int kParallelDataPins[] = {
D9, D8, D7, D6, D5, D4, D3, D2,
};
constexpr int kParallelBusyPin = D14;
constexpr int kParallelAckPin = D15;
constexpr int kParallelPaperoutPin = D12;
constexpr int kParallelSelectPin = D11;
constexpr int kParallelNibblePins[] = {
kParallelSelectPin,
kParallelPaperoutPin,
kParallelAckPin,
kParallelBusyPin,
};
constexpr bool kParallelNibbleInverted[] = {
false,
false,
false,
true,
};
constexpr int kParallelStrobePin = D10;
volatile bool strobe_pending = false;
volatile uint8_t strobe_byte = 0;
void strobeIsr() {
strobe_pending = true;
strobe_byte = readParallel();
}
} // namespace
uint8_t readParallel() {
uint8_t out = 0;
for (int i = 0; i < 8; i++) {
if (digitalReadFast(digitalPinToPinName(kParallelDataPins[i])) ==
HIGH) {
out |= (1 << i);
}
}
if (digitalReadFast(digitalPinToPinName(kParallelStrobePin)) == HIGH) {
out |= (1 << 8);
}
return out;
}
#define BIT(x, n) ((x & (1 << n)) ? HIGH : LOW)
void writeParallel(uint8_t nibble) {
for (int i = 0; i < 4; i++) {
int lvl = BIT(nibble, i);
if (kParallelNibbleInverted[i]) {
lvl = lvl == HIGH ? LOW : HIGH;
}
digitalWriteFast(digitalPinToPinName(kParallelNibblePins[i]), lvl);
}
}
bool strobeOccurred(uint8_t &byte) {
bool pending;
{
noInterrupts();
pending = strobe_pending;
byte = strobe_byte;
strobe_pending = false;
interrupts();
}
return pending;
}
void setupParallel() {
for (int i = 0; i < sizeof(kParallelDataPins) / sizeof(int); i++) {
pinMode(kParallelDataPins[i], INPUT);
}
for (int i = 0; i < sizeof(kParallelNibblePins) / sizeof(int); i++) {
pinMode(kParallelNibblePins[i], OUTPUT);
digitalWrite(kParallelNibblePins[i], HIGH);
}
pinMode(kParallelStrobePin, INPUT);
attachInterrupt(digitalPinToInterrupt(kParallelStrobePin), strobeIsr,
FALLING);
}

14
arduino/kbd/parallel.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include <stdint.h>
/** Returns the byte present on D0~D7 */
uint8_t readParallel();
/** Writes a nibble on status pins */
void writeParallel(uint8_t nibble);
void setupParallel();
/** Returns true and sets the byte if a strobe was received */
bool strobeOccurred(uint8_t &byte);

View File

@@ -17,4 +17,9 @@ SECTIONS {
.data : {
*(.data)
}
. = 0x7dfe;
.magic : {
SHORT(0xAA55);
}
}

View File

@@ -11,9 +11,12 @@ main:
mov si, [si]
push ds
push es
pop ds
mov ax, cs
mov ds, ax
mov es, ax
cld
rep movsw
pop es
pop ds
retf 6

View File

@@ -19,8 +19,7 @@ uint16_t crc16(int data_len) {
return crc;
}
asm (
".global main \n"
asm(".global main \n"
"main: \n"
" push %bp \n"
" mov %sp, %bp \n"
@@ -31,5 +30,4 @@ asm (
" mov 6(%bp), %di \n"
" mov %ax, (%di) \n"
" pop %bp \n"
" lret $4 \n"
);
" lret $4 \n");

6
crt0.c
View File

@@ -3,8 +3,7 @@
int main();
asm (
".section .init \n"
asm(".section .init \n"
".global _start \n"
"_start: \n\t"
"mov %cs, %ax \n\t"
@@ -15,8 +14,7 @@ asm (
"mov $0x1000, %ax \n\t"
"mov %ax, %sp \n\t"
"sti \n\t"
"jmp main"
);
"jmp main");
void* memset(void* ptr, int val, size_t len) {
uint8_t* p = ptr;

20
doscom.ld Normal file
View File

@@ -0,0 +1,20 @@
SECTIONS {
. = 0x100;
.text : {
KEEP(*(.init))
*(.text*)
}
.rodata : {
*(.rodata*)
}
.bss (NOLOAD) : {
* (.bss)
}
.data : {
*(.data)
}
}

15
fat12.c
View File

@@ -8,7 +8,6 @@
#define kRootDirSizeSectors 7
#define kDataRegionStartSector (1 + kFatSizeSectors * 2 + kRootDirSizeSectors)
typedef struct {
char name[8];
char ext[3];
@@ -23,7 +22,6 @@ 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;
@@ -39,12 +37,10 @@ static int readsector(int c, int h, int s, uint8_t* addr) {
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));
if (ret == 0x80) {
continue;
} else {
return ret;
: "r"(dest), "r"(nsects), "r"(func), "r"(sect), "r"(cyl),
"r"(head), "r"(drive), "r"(seg));
if (ret != 0x80) {
break;
}
}
@@ -62,7 +58,8 @@ static int readcluster(int cluster) {
}
static void cluster2chs(int cluster, int* c, int* h, int* s) {
int logicalsector = kDataRegionStartSector + (cluster - 2) * kSectorsPerCluster;
int logicalsector =
kDataRegionStartSector + (cluster - 2) * kSectorsPerCluster;
*s = (logicalsector % kSectorsPerTrack) + 1;
*h = (logicalsector / kSectorsPerTrack) % kHeads;
*c = logicalsector / (kHeads * kSectorsPerTrack);

View File

@@ -7,15 +7,12 @@
#define kFatAddress ((void*)0x1000)
#define kRootDirAddress ((void*)0x1200)
static int putchar(int c) {
register uint8_t khar asm("al") = c;
register uint8_t func asm("ah") = 0x0e;
register uint8_t page asm("bh") = 0;
asm volatile ("int $0x10"
:: "r" (khar), "r" (func), "r" (page)
: "bp");
asm volatile("int $0x10" ::"r"(khar), "r"(func), "r"(page) : "bp");
return c;
}
@@ -27,33 +24,30 @@ static int puts(const char* msg) {
return 0;
}
__attribute__((noreturn))
static void die(const char* msg) {
__attribute__((noreturn)) static void die(const char* msg) {
puts(msg);
while (1) {
}
__builtin_unreachable();
}
__attribute__((noreturn))
static void jump(void* addr) {
__attribute__((noreturn)) static void jump(void* addr) {
asm volatile("ljmp $0,%0" ::"i"(addr));
__builtin_unreachable();
}
__attribute__((noreturn))
static void loadpolmon() {
__attribute__((noreturn)) static void loadpolmon() {
if (fat12_init(kFatAddress, kRootDirAddress)) {
die("fi");
}
if (fat12_readfile("POLMON COM", kPolmonAddress)) {
die("pnf");
while (fat12_readfile("POLIO COM", kPolmonAddress)) {
asm volatile("mov $00, %%ah \n\t"
"int $0x16 \n\t" ::
: "ax");
}
jump(kPolmonAddress);
}
int main() {
loadpolmon();
}
int main() { loadpolmon(); }

20
flat1000.ld Normal file
View File

@@ -0,0 +1,20 @@
SECTIONS {
. = 0x1000;
.text : {
KEEP(*(.init))
*(.text*)
}
.rodata : {
*(.rodata*)
}
.bss (NOLOAD) : {
* (.bss)
}
.data : {
*(.data)
}
}

80
paracli.s Normal file
View File

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

124
paracomm.c Normal file
View File

@@ -0,0 +1,124 @@
#include "paracomm.h"
#include <stdlib.h>
#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;
}

19
paracomm.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include <stdint.h>
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();

189
polio.s Normal file
View File

@@ -0,0 +1,189 @@
.arch i8086,jumps
.code16
.intel_syntax noprefix
.section .init
.global _start
_start:
jmp main
diskpointer = 0x1e*4
dbtbase = 0x0500
.section .text.dosdbt
.local dosdbt
dosdbt: # assumes es=0
push si
push di
push ds
mov si, diskpointer
lds si, [si]
mov di, dbtbase
mov cx, 0x0a # size of the dbt
cld
rep movsb # move dbt to dbtbase
pop ds
mov al, 9 # sectors per track
mov si, dbtbase
mov [si+4], al # set number of sectors
mov di, diskpointer
mov [di], si # new int1eh offset
mov [di+2], ds # and segment
pop di
pop si
ret
.section .text.int80stuff
# near copy [param 2] bytes, es:[param 0] -> es:[param 1]
copy:
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
writesector:
mov ax, 0x0301 # write command, 1 sector
jmp 0f
# params: addr, c, h, s
# return status in ah, sectors read (1 or 0) in al
readsector:
mov ax, 0x0201 # read command, 1 sector
0:
mov bx, [bp+0] # addr
mov ch, [bp+2] # c
mov dh, [bp+4] # h
mov cl, [bp+6] # s
int 0x13
ret
# params: c, h
# returns: status in ah, 0 in al?
buffer = 0xe000 # buffer needs to be big enough for a whole track
# (for some reason), so 9 * 512 bytes = 0x1200 bytes
sectors = 9
formattrack:
mov bx, buffer
xor cx, cx
0:
cmp cl, sectors
jnl 1f
mov di, cx
and di, 0x0f # max 15 sectors
shl di, 1
shl di, 1 # di = cl*4
lea di, [bx+di]
mov al, [bp+0] # track number
mov [di+0], al
mov al, [bp+2] # head number
mov [di+1], al
mov al, cl # sector number
inc al
mov [di+2], al
movb [di+3], 0x02 # 512 bytes per sector
inc cl
jmp 0b
1:
mov ah, 0x05 # format track
mov al, sectors
mov dl, 0 # first drive
mov ch, [bp+0] # track number
mov dh, [bp+2] # head number
mov cl, 1 # sector number (first sector?)
int 0x13
ret
readfile:
mov ax, [bp+0]
mov dx, [bp+2]
call fat12_readfile
ret
.section .rodata.polmon_str
polmon_str: .ascii "POLMON COM"
polmon_addr = 0x1000
.section .text.main
.global main
main:
cli
mov ax, 0x2000 # stack address
mov sp, ax # ss should already be 0
sti
call dosdbt
mov ax, 0x1a00 # first sector of first fat
mov dx, 0x1c00 # first sector of root dir
call fat12_init
mov si, 0x80*4
movw [si], offset int80h
movw [si+2], 0
mov ax, offset polmon_str
mov dx, polmon_addr
call fat12_readfile
test ax, ax
jnz 0f
jmp polmon_addr
0:
mov ax, 0x0e42
mov bh, 0
int 0x10
cli
hlt
.section .text.int80h
int80h:
push bx
push si
push di
push bp
push ds
push es
xor cx, cx
mov es, cx
mov ds, cx
shl ah
cmp ah, offset int80h_entries
jge 0f
mov al, ah
xor ah, ah
mov si, ax
mov bp, sp
lea bp, [bp + 18] # 12 for us, 6 for the interrupt
call [int80h_table+si]
jmp 1f
0:
mov ax, -1
1:
pop es
pop ds
pop bp
pop di
pop si
pop bx
iret
.section .rodata.int80h
int80h_table:
.word offset copy # 0x00
.word offset readsector # 0x01
.word offset writesector # 0x02
.word offset formattrack # 0x03
.word offset readfile # 0x04
int80h_entries = . - int80h_table

215
polmon.cc
View File

@@ -1,4 +1,5 @@
#include <cstdint>
#include <cstdio>
#ifndef WOZMON
#define WOZMON 0
@@ -8,7 +9,7 @@
#define BACKSPACE 0
#define ASCIIDUMP 0
#define CLEARSCREENCMD 0
#define FARCALL 0
#define INT80H 0
#define SHOWTITLE 0
#define BOOTSTRAP 0
#endif // WOZMON
@@ -25,8 +26,8 @@
#define CLEARSCREENCMD 1
#endif
#ifndef FARCALL
#define FARCALL 1
#ifndef INT80H
#define INT80H 1
#endif
#ifndef SHOWTITLE
@@ -39,66 +40,51 @@
namespace {
constexpr int kDumpSize = 16;
#if WOZMON
uint8_t getc() {
register uint8_t c asm ("al");
asm volatile (
"movb $0x00, %%ah\n\t"
int getchar() {
register char c asm("al");
asm volatile("movb $0x00, %%ah\n\t"
"int $0x16"
: "=r" (c)
:: "ah", "cc"
);
: "=r"(c)::"ah", "cc");
return c;
}
void putc(uint8_t c) {
asm volatile (
"push %%bp \n\t"
"movb %0, %%al \n\t"
void putchar(int c) {
asm volatile("push %%bp \n\t"
"mov %0, %%ax \n\t"
"movb $0x0e, %%ah \n\t"
"movb $0, %%bh \n\t"
"int $0x10 \n\t"
"pop %%bp \n\t"
:: "r" (c)
: "ax", "bh", "cc"
);
"pop %%bp \n\t" ::"r"(c)
: "ax", "bh", "cc");
}
void puts(const char* s) {
while (*s) {
putc(*s++);
}
}
#endif // WOZMON
constexpr int kDumpSize = 16;
// arguments on the stack in reverse order
extern "C" uint16_t Jump(uint16_t addr, int nargs);
asm (
".section .text.Jump \n"
"Jump: \n"
" push %bx \n"
extern "C" uint16_t Int80h(uint8_t fun, int nargs);
asm(".section .text.Int80h \n"
"Int80h: \n"
" push %si \n"
" push %di \n"
" push %bp \n"
" push %es \n"
" push %ax \n" // addr
" movw %sp, %bp \n"
" movw %dx, %si \n" // nargs
" add %si, %si \n"
"j0: \n"
" test %si, %si \n"
" jz j1 \n"
" lea 12(%bp,%si), %di\n"
" push %di \n"
" sub $2, %si \n"
" jmp j0 \n"
"j1: \n"
" lcall *(%bp) \n"
" add $4, %sp \n"
" mov %al, %ah \n"
" mov %sp, %bp \n"
" lea 6(%bp), %si \n" // 4 for pushed stuff, 2 for return addr
"0: \n"
" test %dx, %dx \n"
" jz 1f \n"
" push (%si) \n"
" dec %dx \n"
" add $2, %si \n"
" jmp 0b \n"
"1: \n"
" int $0x80 \n"
" mov %bp, %sp \n"
" pop %bp \n"
" pop %di \n"
" pop %si \n"
" pop %bx \n"
" ret"
// return value in ax
);
@@ -126,19 +112,15 @@ uint16_t ReadUintN(int n, const char* buf) {
return out;
}
uint8_t ReadUint8(const char* buf) {
return ReadUintN(2, buf);
}
uint8_t ReadUint8(const char* buf) { return ReadUintN(2, buf); }
uint16_t ReadUint16(const char* buf) {
return ReadUintN(4, buf);
}
uint16_t ReadUint16(const char* buf) { return ReadUintN(4, buf); }
void WriteHexNibble(uint8_t c) {
if (c > 9) {
putc('a' + c - 10);
putchar('a' + c - 10);
} else {
putc('0' + c);
putchar('0' + c);
}
}
@@ -156,11 +138,7 @@ 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)
);
asm("lodsb \n\t" : "=r"(ret) : "r"(ad), "r"(sg));
return ret;
}
@@ -168,38 +146,31 @@ 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"
);
asm("stosb \n\t" ::"r"(sg), "r"(ad), "r"(v) : "memory");
}
__attribute__((noreturn))
inline static void __basic__() {
asm volatile (
"movw $0x40, %ax \n\t"
__attribute__((noreturn)) inline static void __basic__() {
asm volatile("movw $0x40, %ax \n\t"
"mov %ax, %ds \n\t"
"int $0x18"
);
"int $0x18");
__builtin_unreachable();
}
void Dump(uint16_t addr, uint16_t seg, int count) {
for (int i = 0; i < count; i++) {
putc(' ');
putchar(' ');
uint8_t d = __get_far_u8__(addr + i, seg);
WriteUint8(d);
}
#if ASCIIDUMP
putc(' ');
putc(' ');
putchar(' ');
putchar(' ');
for (int i = 0; i < count; i++) {
uint8_t d = __get_far_u8__(addr + i, seg);
if (d > 31 && d < 127) {
putc(d);
putchar(d);
} else {
putc('.');
putchar('.');
}
}
#endif
@@ -208,28 +179,25 @@ void Dump(uint16_t addr, uint16_t seg, int count) {
void DumpHex(uint16_t addr, uint16_t seg) {
addr &= -kDumpSize;
putc('[');
putchar('[');
WriteUint16(seg);
putc(':');
putchar(':');
WriteUint16(addr);
putc(']');
putc(':');
putchar(']');
putchar(':');
Dump(addr, seg, kDumpSize);
putc('\r');
putc('\n');
putchar('\r');
putchar('\n');
}
void ClearScreen() {
asm volatile (
"movw $0x0002, %%ax \n\t"
"int $0x10"
::: "ax", "cc"
);
asm volatile("movw $0x0002, %%ax \n\t"
"int $0x10" ::
: "ax", "cc");
}
void Status(uint8_t status) {
asm volatile (
"xorb %%bh, %%bh \n\t"
asm volatile("xorb %%bh, %%bh \n\t"
"movb $0x03, %%ah \n\t"
"int $0x10 \n\t"
"mov %%dx, %%di \n\t"
@@ -237,13 +205,12 @@ void Status(uint8_t status) {
"movw $77, %%dx \n\t"
"int $0x10 \n\t"
"movb %0, %%al \n\t"
"call _ZN12_GLOBAL__N_110WriteUint8Eh \n\t"
"call %1 \n\t"
"movb $0x02, %%ah \n\t"
"movw %%di, %%dx \n\t"
"int $0x10"
:: "rm" (status)
: "ax", "bh", "dx", "cx", "di", "cc"
);
"int $0x10" ::"rm"(status),
"l"(WriteUint8)
: "ax", "bh", "dx", "cx", "di", "cc");
}
bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
@@ -255,11 +222,21 @@ bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
ptr += 5;
} else if (*ptr == '$') {
__basic__();
} else if (*ptr == 'r') {
} else if (*ptr == 'j') {
dump = false;
ptr++;
auto jump = reinterpret_cast<uint16_t (*)()>(cur_addr);
uint16_t ret = jump();
WriteUint16(ret);
putchar('\r');
putchar('\n');
#if INT80H
} else if (*ptr == 'i') {
dump = false;
#if FARCALL
int nargs = 0;
ptr++;
int fun = ReadUint8(ptr);
ptr += 2;
for (; *ptr;) {
if (*ptr == ' ') {
ptr++;
@@ -270,19 +247,13 @@ bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
nargs++;
ptr += 4;
}
uint16_t ret = Jump(cur_addr, nargs);
asm volatile (
"shl %0 \n\t"
"add %0, %%sp"
:: "r"(nargs)
);
uint16_t ret = Int80h(fun, nargs);
asm volatile("shl %0 \n\t"
"add %0, %%sp" ::"r"(nargs));
WriteUint16(ret);
putc('\r'); putc('\n');
#else // FARCALL
auto jump = reinterpret_cast<void(*)()>(cur_addr);
jump();
#endif // FARCALL
ptr++;
putchar('\r');
putchar('\n');
#endif // INT80H
#if CLEARSCREENCMD
} else if (*ptr == 'l') {
dump = false;
@@ -311,7 +282,10 @@ bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
return dump;
}
const char* prompt = "> ";
void DisplayPrompt() {
putchar('>');
putchar(' ');
}
void polmon() {
uint16_t cur_addr = 0;
@@ -324,11 +298,11 @@ void polmon() {
#if SHOWTITLE
puts("PolMon 0.2\r\n");
#endif // SHOWTITLE
puts(prompt);
DisplayPrompt();
while (1) {
uint8_t c = getc();
putc(c); // echo
uint8_t c = getchar();
putchar(c); // echo
if (c == '\r') {
*inptr = 0;
if (inbuf[0] == 0) {
@@ -336,7 +310,7 @@ void polmon() {
cur_addr += kDumpSize;
}
} else {
putc('\n');
putchar('\n');
}
if (ParseCommand(inbuf, cur_addr, cur_seg)) {
@@ -344,17 +318,17 @@ void polmon() {
};
first = false;
inptr = inbuf;
puts(prompt);
DisplayPrompt();
#if BACKSPACE
} else if (c == kBackspace || c == kOtherBackspace) {
inptr--;
if (inptr < inbuf) {
inptr = inbuf;
putc(' ');
putchar(' ');
continue;
}
putc(' ');
putc(kOtherBackspace);
putchar(' ');
putchar(kOtherBackspace);
#endif
} else {
*inptr = c;
@@ -365,13 +339,10 @@ void polmon() {
} // namespace
int main() {
polmon();
}
int main() { polmon(); }
#if BOOTSTRAP
__attribute__((section(".init"), noreturn, used))
void _start() {
__attribute__((section(".init"), noreturn, used)) void _start() {
main();
__builtin_unreachable();
}

10
readio.asm Normal file
View File

@@ -0,0 +1,10 @@
BITS 16
CPU 8086
_start:
mov bp, sp
mov si, [bp+4]
mov dx, [si]
xor ax, ax
in al, dx
retf 2

25
stdlib.c Normal file
View File

@@ -0,0 +1,25 @@
int getchar() {
register char c asm("al");
asm volatile("movb $0x00, %%ah\n\t"
"int $0x16"
: "=r"(c)::"ah", "cc");
return c;
}
int putchar(int c) {
asm volatile("push %%bp \n\t"
"mov %0, %%ax \n\t"
"movb $0x0e, %%ah \n\t"
"movb $0, %%bh \n\t"
"int $0x10 \n\t"
"pop %%bp \n\t" ::"r"(c)
: "ax", "bh", "cc");
return 0;
}
int puts(const char* s) {
while (*s) {
putchar(*s++);
}
return 0;
}

12
writeio.asm Normal file
View File

@@ -0,0 +1,12 @@
BITS 16
CPU 8086
_start:
mov bp, sp
mov si, [bp+6]
mov dx, [si]
xor ax, ax
mov si, [bp+4]
mov al, [si]
out dx, al
retf 4