Compare commits
59 Commits
fa1b26ef10
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b9646681e | |||
| 0e18958341 | |||
| aeee8131e7 | |||
| 8cd6652b45 | |||
| 3e471f1390 | |||
| d7e0f7b759 | |||
| aaefcc864d | |||
| a1275671b3 | |||
| 84ea4d5cae | |||
| 8818606867 | |||
| 3d90cbadf8 | |||
| 1208f1f66e | |||
| 91cf76c251 | |||
| 540f42b21b | |||
| 5e08ee648b | |||
| 798b3e8e6b | |||
| 05b733b296 | |||
| ec3fe0e07d | |||
| 0917aa1280 | |||
| d7df8501d6 | |||
| f419f72649 | |||
| 775295804e | |||
| d1bea93d07 | |||
| 2f507a2f71 | |||
| da77e08b80 | |||
| cad4f5081a | |||
| 5bb49da246 | |||
| a55c7c47e0 | |||
| cbe304cabf | |||
| 1b2237f2f7 | |||
| bf9a88805f | |||
| 188256b0ac | |||
| b1e7fde1a3 | |||
| 2ba1546dec | |||
| 3df5b28e4e | |||
| b0d57c1c96 | |||
| 806d87e0f1 | |||
| a4959782a8 | |||
| f707c394f0 | |||
| 0c0133b3c1 | |||
| 6a69215c90 | |||
| 59f49a232e | |||
| ce3ad9fa86 | |||
| 06bc908b33 | |||
| f1b2902640 | |||
| f3057e92be | |||
| e20b09f130 | |||
| 8b434f4434 | |||
| 83e267fa36 | |||
| dd5d329dff | |||
| 044e58ef12 | |||
| de41328677 | |||
| 8a06693456 | |||
| 05d5e5413f | |||
| 50e3017c5c | |||
| 7cbd423ef9 | |||
| c49d00422a | |||
| bbaefabf8a | |||
| 202bae362a |
9
.clang-format
Normal file
9
.clang-format
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
ConstructorInitializerIndentWidth: 8
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
IndentWidth: 4
|
||||||
|
InsertNewlineAtEOF: true
|
||||||
|
PointerAlignment: Left
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
*.bin
|
*.bin
|
||||||
|
*.o
|
||||||
|
*.elf
|
||||||
|
*.com
|
||||||
polos.img
|
polos.img
|
||||||
polmon.com
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ FROM deps AS build
|
|||||||
|
|
||||||
ARG TARGET
|
ARG TARGET
|
||||||
|
|
||||||
ADD . /workspace
|
ADD ./src /workspace
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
RUN make ${TARGET}
|
RUN make ${TARGET}
|
||||||
|
|
||||||
|
|||||||
75
Makefile
75
Makefile
@@ -1,86 +1,25 @@
|
|||||||
dev-image = 5150-dev
|
dev-image = 5150-dev
|
||||||
|
|
||||||
%.bin: %.asm
|
|
||||||
nasm $? -o $@
|
|
||||||
|
|
||||||
crc16.s: crc16.c
|
|
||||||
ia16-elf-gcc -S -Os -o crc16.s crc16.c
|
|
||||||
|
|
||||||
crc16.bin: crc16.s crt0.c
|
|
||||||
ia16-elf-gcc -o crc16.bin -Os -nostdlib crc16.s crt0.c
|
|
||||||
|
|
||||||
CC = ia16-elf-gcc
|
|
||||||
CXX = ia16-elf-gcc
|
|
||||||
LD = ia16-elf-gcc
|
|
||||||
CXXFLAGS = -mregparmcall -ffunction-sections -Os -flto
|
|
||||||
CFLAGS = -mregparmcall -ffunction-sections -Os -flto
|
|
||||||
LDFLAGS = -mregparmcall -Wl,--gc-sections -Os -nostdlib -flto
|
|
||||||
|
|
||||||
%.elf:
|
|
||||||
$(LD) $(LDFLAGS) $(CPPFLAGS) -o $@ $^
|
|
||||||
|
|
||||||
bootsectors = fat12boot.bin wozmon.bin
|
|
||||||
|
|
||||||
$(bootsectors):
|
|
||||||
ia16-elf-objcopy -O binary $? $@
|
|
||||||
truncate -s 510 $@
|
|
||||||
printf "\125\252" >> $@
|
|
||||||
|
|
||||||
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.com: polmon.elf
|
|
||||||
ia16-elf-objcopy -O binary $? $@
|
|
||||||
|
|
||||||
wozmon.o: polmon.cc
|
|
||||||
wozmon.o: CPPFLAGS = -DWOZMON=1
|
|
||||||
wozmon.o:
|
|
||||||
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
wozmon.elf: wozmon.o bootsect.S
|
|
||||||
wozmon.elf: LDFLAGS += -T bootsect.ld
|
|
||||||
wozmon.elf: CPPFLAGS += -DNOBPB
|
|
||||||
|
|
||||||
wozmon.bin: wozmon.elf
|
|
||||||
|
|
||||||
polos.img: fat12boot.bin polmon.com
|
|
||||||
dd if=/dev/zero of=$@ bs=512 count=720
|
|
||||||
mformat -i $@ -t 40 -h 2 -s 9
|
|
||||||
mcopy -i $@ polmon.com ::/polmon.com
|
|
||||||
dd if=fat12boot.bin of=$@ conv=notrunc
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean: ## Remove generated files
|
|
||||||
rm -rf *.bin polmon.com polos.img
|
|
||||||
|
|
||||||
.PHONY: dev-image
|
.PHONY: dev-image
|
||||||
dev-image:
|
dev-image:
|
||||||
docker build -t $(dev-image) --target dev .
|
docker build -t $(dev-image) --target dev .
|
||||||
|
|
||||||
.PHONY: dev
|
.PHONY: dev
|
||||||
dev: dev-image ## Launch a dev container
|
dev: dev-image ## Launch a dev container
|
||||||
docker run -it --rm -v $(CURDIR):/workspace $(dev-image)
|
docker run -it --rm -v $(CURDIR)/src:/workspace $(dev-image)
|
||||||
|
|
||||||
|
|
||||||
.PHONY: binaries
|
.PHONY: binaries
|
||||||
binaries: ## Build all binaries
|
binaries: ## Build all small binaries
|
||||||
|
docker build --build-arg TARGET=call.bin -o . --target=export .
|
||||||
docker build --build-arg TARGET=readfloppy.bin -o . --target=export .
|
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=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 .
|
|
||||||
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=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=format.bin -o . --target=export .
|
||||||
docker build --build-arg TARGET=polmon.com -o . --target=export .
|
docker build --build-arg TARGET=crc16.bin -o . --target=export .
|
||||||
docker build --build-arg TARGET=fat12boot.bin -o . --target=export .
|
docker build --build-arg TARGET=hello.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
|
.PHONY: floppy
|
||||||
floppy: ## Make a bootable floppy image
|
floppy: ## Make a bootable floppy image
|
||||||
|
|||||||
117
README.md
117
README.md
@@ -1,26 +1,123 @@
|
|||||||
5150 stuff
|
5150 stuff
|
||||||
==========
|
==========
|
||||||
|
|
||||||
The binary programs here use multiple conventions (more Fun!):
|
## Layout of a bootable polOS floppy disk
|
||||||
- .asm files use NASM syntax and are built with `nasm xx.asm`
|
|
||||||
- .s files use GAS syntax, they are built with `ia16-elf-gcc`
|
|
||||||
|
|
||||||
The reason is uh, because you see, I did things.
|
The floppy disk is FAT12 formatted. It must contain at least the following items:
|
||||||
|
|
||||||
The .asm came first and was copy pasta from the internet.
|
* _fat12boot.bin_: the boot sector. It loads _polio.com_ from a FAT12-formatted
|
||||||
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.
|
drive 0 to address _0x0600_ and jumps to it.
|
||||||
|
* _polio.com_: the basic OS functions. It serves all interrupt 0x80 functions
|
||||||
|
(see below) and loads _polmon.com_ to address _0x1200_, then jumps to it.
|
||||||
|
* _polmon.com_: a barebones memory utility. See below for all it can do.
|
||||||
|
|
||||||
Also the wozmon binary contains special care to make it into a boot sector (0xaa55 magic).
|
## polOS int 0x80
|
||||||
|
|
||||||
|
* function in AH
|
||||||
|
* optional argument in AL
|
||||||
|
* other arguments on the stack
|
||||||
|
* return value in AX
|
||||||
|
|
||||||
|
| AH | AL | stack arguments | return | description |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| _0x01_ | | _addr_, _c_, _h_, _s_ | error code in AH | __readsector__: reads 1 floppy sector at _c/h/s_ into _0000:addr_ |
|
||||||
|
| _0x02_ | | _addr_, _c_, _h_, _s_ | error code in AH | __writesector__: writes 1 floppy sector at _c/h/s_ from _0000:addr_ |
|
||||||
|
| 0x03 | | _c_, _h_ | error code in AH | __formattrack__: formats 1 track at c/h |
|
||||||
|
| 0x04 | | _fname_, _addr_ | non-zero if error | __readfile__: reads file with name at _ds:fname_ into _ds:addr_ |
|
||||||
|
| 0x05 | | _addr_, _size_ | bytes received, < 0 if error | __recvpara__: receive at most _size_ bytes from parallel port into _ds:addr_ |
|
||||||
|
| 0x06 | | _addr_, _size_ | bytes sent, < 0 if error | __sendpara__: send at most _size_ bytes to parallel port from _ds:addr_ |
|
||||||
|
| 0x07 | _until-idle_ | | 0 if idle | __runpara__: run a round of parallel comms if _AL=0_, keep going until idle if _AL!=0_ |
|
||||||
|
| 0x08 | | _fname_, _addr_, _size_ | error code in AH | __writefile__: writes _size_ bytes from _ds:addr_ into file with name at _ds:fname_ |
|
||||||
|
|
||||||
|
|
||||||
|
## polmon commands
|
||||||
|
|
||||||
|
Commands are a single letter followed by an optional space and arguments, with optional spaces between them.
|
||||||
|
A command can be prefixed with up to 4 hex digits, which will then change the current address.
|
||||||
|
|
||||||
|
* (no command): dumps 16 bytes at the current address. If no address is given, also increments the current address by 16.
|
||||||
|
* `w bytes:[u8]*`: writes bytes in hex to the current address.
|
||||||
|
* `k src:u16 size:u16`: copy _size_ bytes of memory from _cur-seg_:_src_ to _cur-seg:cur-addr_
|
||||||
|
* `s seg:u16`: changes the current segment
|
||||||
|
* `j`: calls a function at the current address. ABI calling conventions below.
|
||||||
|
* `l file:str`: loads a program with given name into the current segment, address _0x0100_.
|
||||||
|
* `r args:[u16]*`: runs the program in the current segment with arguments.
|
||||||
|
* `i ax:u16 args:[u16]*`: call an int 0x80 function with given AX and stack args. See section above.
|
||||||
|
|
||||||
|
The _r_, _j_ and _i_ commands display the value of the AX register after returning to polmon.
|
||||||
|
|
||||||
|
### ABI convention for (j)umping to functions
|
||||||
|
|
||||||
|
* AX, BX, CX, DX are caller-saved, all others are callee-saved
|
||||||
|
* first 3 arguments passed in AX, DX and CX, others are pushed in reverse order (former argument in lower memory)
|
||||||
|
* return value in AX
|
||||||
|
|
||||||
|
|
||||||
|
### ABI for programs
|
||||||
|
|
||||||
|
See example in _hello.com_
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Launching mushroom client
|
||||||
|
|
||||||
Anyway, just type
|
|
||||||
```
|
```
|
||||||
make binaries
|
s 200
|
||||||
|
l mushroom.com
|
||||||
|
r
|
||||||
```
|
```
|
||||||
|
|
||||||
and stop whining.
|
### FTP a file and store it to the floppy disk
|
||||||
|
|
||||||
|
```
|
||||||
|
s 200
|
||||||
|
l ftpget.com
|
||||||
|
```
|
||||||
|
|
||||||
|
* switch to segment _0x200_
|
||||||
|
* load file _ftpget.com_ to _cur-seg:0100_
|
||||||
|
|
||||||
|
Here you need to run this on the host:
|
||||||
|
```
|
||||||
|
python ftpserve.py src/hello.com
|
||||||
|
```
|
||||||
|
|
||||||
|
Then on polOS:
|
||||||
|
```
|
||||||
|
r f000 200
|
||||||
|
s 0
|
||||||
|
6000
|
||||||
|
w 48454c4c4f202020434f4d
|
||||||
|
i 0800 6000 f000 200
|
||||||
|
```
|
||||||
|
|
||||||
|
* run previously loaded `ftpget.com`: store up to _0x200_ (512) bytes to address _0000:f000_
|
||||||
|
* set _cur-seg_ to _0x0000_
|
||||||
|
* set _cur-add_ to _0x6000_
|
||||||
|
* write file name in ascii: `HELLO COM` to _0000:6000_
|
||||||
|
* call int 0x80 0x08 (writefile) with args: _fname_, _addr_, _size_
|
||||||
|
|
||||||
|
|
||||||
|
We can now test that the program got transferred correctly:
|
||||||
|
```
|
||||||
|
s 300
|
||||||
|
l hello.com
|
||||||
|
r
|
||||||
|
```
|
||||||
|
|
||||||
|
### But where is my `ls` command?
|
||||||
|
|
||||||
|
You can just inspect memory at _0000:1c00_, that's the root directory. Each entry is _0x20_ (32) bytes long and starts with 11 characters that constitute the file name.
|
||||||
|
|
||||||
## Useful stuff
|
## Useful stuff
|
||||||
|
|
||||||
* [Colab](https://colab.research.google.com/drive/1xGKYQJLKyabcSNYOiPumf9_-bbCbXke1?usp=sharing)
|
* [Colab](https://colab.research.google.com/drive/1xGKYQJLKyabcSNYOiPumf9_-bbCbXke1?usp=sharing)
|
||||||
* [Arduino pinout](https://docs.google.com/spreadsheets/d/1jgKhr-0MFtY_bFZL9xYwVsxFPt4u_5ItV8Rj7FQ7Kj4/edit)
|
* [Arduino pinout](https://docs.google.com/spreadsheets/d/1jgKhr-0MFtY_bFZL9xYwVsxFPt4u_5ItV8Rj7FQ7Kj4/edit)
|
||||||
|
* [DOS 3.1 disk images](https://dl.winworldpc.com/Abandonware%20Operating%20Systems/PC/DOS/IBM/IBM%20PC-DOS%203.1%20%285.25%29.7z)
|
||||||
|
* [DOS 3.1 manual](https://dl.winworldpc.com/IBM%20PC-DOS%203.10%20Manuals%20Feb85.7z)
|
||||||
|
* [IBM 5150 BIOS ROM](https://www.minuszerodegrees.net/bios/BIOS_IBM5150_19OCT81_5700671_U33.BIN)
|
||||||
|
* [IBM Cassette BASIC C1.00](https://www.minuszerodegrees.net/rom/bin/IBM/IBM%205150%20-%20Cassette%20BASIC%20version%20C1.00.zip)
|
||||||
|
* [IBM 5150 technical ref](https://www.minuszerodegrees.net/manuals/IBM/IBM_5150_Technical_Reference_6025005_AUG81.pdf)
|
||||||
|
* [Ruud's diagnostic ROM](https://minuszerodegrees.net/ruuds_diagnostic_rom/bin/ruuds_diagnostic_rom_v5.4_8kb.zip)
|
||||||
|
* [Supersoft diagnostic ROM](https://www.minuszerodegrees.net/supersoft_landmark/Supersoft_PCXT_8KB.bin)
|
||||||
|
|||||||
193
arduino/kbd/kbd.cc
Normal file
193
arduino/kbd/kbd.cc
Normal 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
5
arduino/kbd/kbd.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void sendAsciiChar(int c);
|
||||||
|
void setupKbd();
|
||||||
|
void checkKbdReset();
|
||||||
@@ -1,213 +1,74 @@
|
|||||||
// 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;
|
#ifndef NOKBD
|
||||||
constexpr int kDataPin = PC4;
|
#define NOKBD 1
|
||||||
|
#endif // NOKBD
|
||||||
|
|
||||||
constexpr int kBitDelayMicros = 100;
|
ParaComms pc;
|
||||||
constexpr int kCodeDelayMicros = 200;
|
|
||||||
constexpr int kKeyDelayMicros = 100;
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// put your setup code here, to run once:
|
// put your setup code here, to run once:
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
pinMode(LED_BUILTIN, OUTPUT);
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
|
||||||
pinMode(kClockPin, INPUT);
|
setupKbd();
|
||||||
pinMode(kDataPin, INPUT);
|
setupParallel();
|
||||||
|
|
||||||
Serial.println("kbd 0.1");
|
pc = ParaComms{
|
||||||
}
|
.mosi_cb =
|
||||||
|
[](uint8_t* data, uint8_t len) {
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
Serial.printf("%c", data[i]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
// 0. reset
|
Serial.println("kbd 0.1");
|
||||||
// 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() {
|
void loop() {
|
||||||
static int led_counter = 0;
|
static int led_counter = 0;
|
||||||
static int led = HIGH;
|
static int led = HIGH;
|
||||||
if (led_counter > 400000) {
|
|
||||||
led_counter = 0;
|
|
||||||
led = (led == HIGH) ? LOW : HIGH;
|
|
||||||
digitalWrite(LED_BUILTIN, led);
|
|
||||||
}
|
|
||||||
led_counter += 1;
|
|
||||||
|
|
||||||
int clockp = digitalRead(kClockPin);
|
static int mode = 0; // 0 = keyboard, 1 = parallel
|
||||||
if (state == State::kReady && clockp == LOW) {
|
|
||||||
state = State::kMaybeReset;
|
if (led_counter > 400000) {
|
||||||
lastclocklow = millis();
|
led_counter = 0;
|
||||||
} else if (state == State::kMaybeReset) {
|
led = (led == HIGH) ? LOW : HIGH;
|
||||||
if (clockp == HIGH && millis() - lastclocklow > kResetDelay) {
|
digitalWrite(LED_BUILTIN, led);
|
||||||
delay(1);
|
|
||||||
state = State::kReady;
|
|
||||||
sendCode(0xaa);
|
|
||||||
Serial.println("Reset!");
|
|
||||||
}
|
}
|
||||||
}
|
led_counter += 1;
|
||||||
|
|
||||||
if (Serial.available() > 0) {
|
#if NOKBD
|
||||||
int c = Serial.read();
|
#else // NOKBD
|
||||||
sendAsciiChar(c);
|
checkKbdReset();
|
||||||
//delayMicroseconds(kKeyDelayMicros);
|
#endif // NOKBD
|
||||||
}
|
|
||||||
|
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 NOKBD
|
||||||
|
pc.SendByte(c);
|
||||||
|
#else // NOKBD
|
||||||
|
if (c == 2) {
|
||||||
|
mode = 0;
|
||||||
|
} else if (c == 3) {
|
||||||
|
mode = 1;
|
||||||
|
} else {
|
||||||
|
if (mode == 0) {
|
||||||
|
sendAsciiChar(c);
|
||||||
|
} else {
|
||||||
|
pc.SendByte(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOKBD
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
124
arduino/kbd/paracomm.h
Normal file
124
arduino/kbd/paracomm.h
Normal 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
95
arduino/kbd/parallel.cc
Normal 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
14
arduino/kbd/parallel.h
Normal 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);
|
||||||
5
bochsrc.txt
Normal file
5
bochsrc.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
romimage: file=$BXSHARE/BIOS-bochs-legacy
|
||||||
|
boot:floppy
|
||||||
|
floppya: 360k=polos.img, status=inserted
|
||||||
|
display_library: sdl2
|
||||||
|
keyboard: keymap=$BXSHARE/keymaps/sdl2-pc-us.map
|
||||||
35
crc16.c
35
crc16.c
@@ -1,35 +0,0 @@
|
|||||||
#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 8(%bp), %si \n"
|
|
||||||
" push (%si) \n"
|
|
||||||
" call crc16 \n"
|
|
||||||
" add $0x2, %sp \n"
|
|
||||||
" mov 6(%bp), %di \n"
|
|
||||||
" mov %ax, (%di) \n"
|
|
||||||
" pop %bp \n"
|
|
||||||
" lret $4 \n"
|
|
||||||
);
|
|
||||||
52
crc16.s
52
crc16.s
@@ -1,52 +0,0 @@
|
|||||||
.arch i8086,jumps
|
|
||||||
.code16
|
|
||||||
.att_syntax prefix
|
|
||||||
#NO_APP
|
|
||||||
#APP
|
|
||||||
.global main
|
|
||||||
main:
|
|
||||||
push %bp
|
|
||||||
mov %sp, %bp
|
|
||||||
mov 8(%bp), %si
|
|
||||||
push (%si)
|
|
||||||
call crc16
|
|
||||||
add $0x2, %sp
|
|
||||||
mov 6(%bp), %di
|
|
||||||
mov %ax, (%di)
|
|
||||||
pop %bp
|
|
||||||
lret $4
|
|
||||||
|
|
||||||
#NO_APP
|
|
||||||
.text
|
|
||||||
.global crc16
|
|
||||||
.type crc16, @function
|
|
||||||
crc16:
|
|
||||||
pushw %bp
|
|
||||||
movw %sp, %bp
|
|
||||||
xorw %bx, %bx
|
|
||||||
movw $-1, %ax
|
|
||||||
.L2:
|
|
||||||
cmpw 4(%bp), %bx
|
|
||||||
jne .L5
|
|
||||||
popw %bp
|
|
||||||
ret
|
|
||||||
.L5:
|
|
||||||
movb %cs:(%bx), %dl
|
|
||||||
movb $8, %cl
|
|
||||||
shlw %cl, %dx
|
|
||||||
xorw %dx, %ax
|
|
||||||
movb %cl, %dl
|
|
||||||
.L4:
|
|
||||||
movw %ax, %cx
|
|
||||||
shlw $1, %cx
|
|
||||||
testw %ax, %ax
|
|
||||||
xchgw %ax, %cx
|
|
||||||
jge .L3
|
|
||||||
xorw $4129, %ax
|
|
||||||
.L3:
|
|
||||||
decb %dl
|
|
||||||
jne .L4
|
|
||||||
incw %bx
|
|
||||||
jmp .L2
|
|
||||||
.size crc16, .-crc16
|
|
||||||
.ident "GCC: (GNU) 6.3.0"
|
|
||||||
27
crt0.c
27
crt0.c
@@ -1,27 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
int main();
|
|
||||||
|
|
||||||
asm (
|
|
||||||
".section .init \n"
|
|
||||||
".global _start \n"
|
|
||||||
"_start: \n\t"
|
|
||||||
"mov %cs, %ax \n\t"
|
|
||||||
"mov %ax, %ds \n\t"
|
|
||||||
"mov %ax, %es \n\t"
|
|
||||||
"cli \n\t"
|
|
||||||
"mov %ax, %ss \n\t"
|
|
||||||
"mov $0x1000, %ax \n\t"
|
|
||||||
"mov %ax, %sp \n\t"
|
|
||||||
"sti \n\t"
|
|
||||||
"jmp main"
|
|
||||||
);
|
|
||||||
|
|
||||||
void* memset(void* ptr, int val, size_t len) {
|
|
||||||
uint8_t* p = ptr;
|
|
||||||
while(len--) {
|
|
||||||
*p++ = val;
|
|
||||||
}
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
138
fat12.c
138
fat12.c
@@ -1,138 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define kSectorsPerCluster 2
|
|
||||||
#define kSectorsPerTrack 9
|
|
||||||
#define kHeads 2
|
|
||||||
#define kFatSizeSectors 2
|
|
||||||
#define kRootDirSizeSectors 7
|
|
||||||
#define kDataRegionStartSector (1 + kFatSizeSectors*2 + kRootDirSizeSectors)
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char name[8];
|
|
||||||
char ext[3];
|
|
||||||
uint8_t attr;
|
|
||||||
|
|
||||||
uint8_t dontcare[14];
|
|
||||||
|
|
||||||
uint16_t cluster;
|
|
||||||
uint32_t size;
|
|
||||||
} direntry;
|
|
||||||
|
|
||||||
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;
|
|
||||||
register uint8_t func asm ("ah") = 0x02;
|
|
||||||
register uint8_t sect asm ("cl") = s;
|
|
||||||
register uint8_t cyl asm ("ch") = c;
|
|
||||||
register uint8_t head asm ("dh") = h;
|
|
||||||
register uint8_t drive asm ("dl") = 0;
|
|
||||||
register uint16_t seg asm ("es") = 0;
|
|
||||||
|
|
||||||
register uint8_t ret asm("ah");
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int readcluster(int cluster) {
|
|
||||||
int offs = cluster * 3 / 2;
|
|
||||||
if (cluster % 2) {
|
|
||||||
// high nibble is lsb + whole byte
|
|
||||||
return ((gFat[offs] & 0xf0) >> 4) + (gFat[offs+1] << 4);
|
|
||||||
} else {
|
|
||||||
return gFat[offs] + ((gFat[offs+1] & 0x0f) << 8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cluster2chs(int cluster, int* c, int* h, int* s) {
|
|
||||||
int logicalsector = kDataRegionStartSector + (cluster - 2) * kSectorsPerCluster;
|
|
||||||
*s = (logicalsector % kSectorsPerTrack) + 1;
|
|
||||||
*h = (logicalsector / kSectorsPerTrack) % kHeads;
|
|
||||||
*c = logicalsector / (kHeads * kSectorsPerTrack);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int strncmp(const char* s1, const char *s2, size_t len) {
|
|
||||||
for (int i = 0; i < len && s1[i]; i++) {
|
|
||||||
if (s1[i] != s2[i]) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// XXX: really we should return 0 only if *s1 == *s2 here too
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int loadfile(direntry* entry, void* addr) {
|
|
||||||
int cluster = entry->cluster;
|
|
||||||
for (int i = 0; i < entry->size; i+=1024) {
|
|
||||||
int c, h, s;
|
|
||||||
cluster2chs(cluster, &c, &h, &s);
|
|
||||||
if (readsector(c, h, s, addr + i)) {
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
s++;
|
|
||||||
if (s > 9) {
|
|
||||||
s = 1;
|
|
||||||
h++;
|
|
||||||
}
|
|
||||||
if (h > 1) {
|
|
||||||
h = 0;
|
|
||||||
c++;
|
|
||||||
}
|
|
||||||
if (readsector(c, h, s, addr + i + 512)) {
|
|
||||||
return -5;
|
|
||||||
};
|
|
||||||
cluster = readcluster(cluster);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fat12_init(void* fat_addr, void* rootdir_addr) {
|
|
||||||
if (readsector(0, 0, 2, fat_addr)) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
if (readsector(0, 0, 6, rootdir_addr)) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
gFat = fat_addr;
|
|
||||||
gRootdir = rootdir_addr;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fat12_readfile(const char* name, void* addr) {
|
|
||||||
direntry* file = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
direntry* entry = &gRootdir[i];
|
|
||||||
if (entry->name[0] == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!strncmp(entry->name, name, 11)) {
|
|
||||||
file = entry;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!file) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
return loadfile(file, addr);
|
|
||||||
}
|
|
||||||
59
fat12boot.c
59
fat12boot.c
@@ -1,59 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "fat12.h"
|
|
||||||
|
|
||||||
#define kPolmonAddress ((void*)0x0600)
|
|
||||||
#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");
|
|
||||||
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int puts(const char* msg) {
|
|
||||||
while (*msg) {
|
|
||||||
putchar(*msg++);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noreturn))
|
|
||||||
static void die(const char* msg) {
|
|
||||||
puts(msg);
|
|
||||||
while (1) {
|
|
||||||
}
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noreturn))
|
|
||||||
static void jump(void* addr) {
|
|
||||||
asm volatile ("ljmp $0,%0" :: "i"(addr));
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noreturn))
|
|
||||||
static void loadpolmon() {
|
|
||||||
if (fat12_init(kFatAddress, kRootDirAddress)) {
|
|
||||||
die("fi");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fat12_readfile("POLMON COM", kPolmonAddress)) {
|
|
||||||
die("pnf");
|
|
||||||
}
|
|
||||||
|
|
||||||
jump(kPolmonAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
loadpolmon();
|
|
||||||
}
|
|
||||||
79
ftpserve.py
Normal file
79
ftpserve.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import serial
|
||||||
|
|
||||||
|
|
||||||
|
kCrc16Polynomial = 0x1021
|
||||||
|
kCrc16Preset = 0xFFFF
|
||||||
|
|
||||||
|
kDefaultSerialPort = None
|
||||||
|
kDefaultBaudrate = 115200
|
||||||
|
kDefaultChunkSize = 0x20
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def crc16(data):
|
||||||
|
crc = kCrc16Preset
|
||||||
|
for c in data:
|
||||||
|
crc ^= c << 8
|
||||||
|
for j in range(8):
|
||||||
|
mix = crc & 0x8000
|
||||||
|
crc <<= 1
|
||||||
|
if mix:
|
||||||
|
crc ^= kCrc16Polynomial
|
||||||
|
crc &= 0xFFFF
|
||||||
|
return crc
|
||||||
|
|
||||||
|
|
||||||
|
def getserial(port=None, baudrate=None, **kwargs):
|
||||||
|
if port is not None:
|
||||||
|
return serial.Serial(port, baudrate)
|
||||||
|
|
||||||
|
# return first /dev/ttyACMx found
|
||||||
|
for i in range(10):
|
||||||
|
try:
|
||||||
|
return serial.Serial(f"/dev/ttyACM{i}", baudrate)
|
||||||
|
except serial.SerialException:
|
||||||
|
pass
|
||||||
|
raise RuntimeError("No serial device available.")
|
||||||
|
|
||||||
|
|
||||||
|
def ftpserve(file, device, chunksize=0x20):
|
||||||
|
with open(file, "rb") as f:
|
||||||
|
data = f.read()
|
||||||
|
device.reset_input_buffer()
|
||||||
|
logger.info(f"Serving file {file}")
|
||||||
|
for i in range(0, len(data), chunksize):
|
||||||
|
device.write(data[i : i + chunksize])
|
||||||
|
r = device.read()
|
||||||
|
if r != b"\x42":
|
||||||
|
raise RuntimeError(f"wrong response {r}")
|
||||||
|
logger.info(f"ack for chunk @{i}")
|
||||||
|
logger.info(f"Done serving file {file}")
|
||||||
|
crc = crc16(data)
|
||||||
|
logger.info(f"File length: {len(data):04x}, crc16: {crc:04x}")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description="Serve a file to ftpget.")
|
||||||
|
parser.add_argument("file", help="file to serve")
|
||||||
|
parser.add_argument("--port", help="path to the serial port")
|
||||||
|
parser.add_argument("--baudrate", default=kDefaultBaudrate)
|
||||||
|
parser.add_argument("--chunksize", default=kDefaultChunkSize)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
args = parse_args()
|
||||||
|
device = getserial(**vars(args))
|
||||||
|
ftpserve(args.file, device, args.chunksize)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
67
mrbridge.py
Normal file
67
mrbridge.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import argparse
|
||||||
|
import serial
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import websocket
|
||||||
|
|
||||||
|
kDefaultSerialPort = None
|
||||||
|
kDefaultBaudrate = 115200
|
||||||
|
|
||||||
|
|
||||||
|
def getserial(port=None, baudrate=None, **kwargs):
|
||||||
|
if port is not None:
|
||||||
|
return serial.Serial(port, baudrate)
|
||||||
|
|
||||||
|
# return first /dev/ttyACMx found
|
||||||
|
for i in range(10):
|
||||||
|
try:
|
||||||
|
return serial.Serial(f"/dev/ttyACM{i}", baudrate)
|
||||||
|
except serial.SerialException:
|
||||||
|
pass
|
||||||
|
raise RuntimeError("No serial device available.")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description="Bridge a serial port to a websocket")
|
||||||
|
parser.add_argument(
|
||||||
|
"--port", help="path to the serial port", default=kDefaultSerialPort
|
||||||
|
)
|
||||||
|
parser.add_argument("--baudrate", default=kDefaultBaudrate)
|
||||||
|
parser.add_argument("ws", help="URL of the websocket")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def device_thread(device, ws):
|
||||||
|
while True:
|
||||||
|
line = device.readline()
|
||||||
|
ws.send(line)
|
||||||
|
|
||||||
|
|
||||||
|
def slowwrite(device, data):
|
||||||
|
for b in data:
|
||||||
|
device.write([b])
|
||||||
|
time.sleep(0.005)
|
||||||
|
|
||||||
|
|
||||||
|
def ws_thread(device, ws):
|
||||||
|
while True:
|
||||||
|
data = ws.recv()
|
||||||
|
slowwrite(device, data.replace(b"\n", b"\r\n"))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
device = getserial(**vars(args))
|
||||||
|
ws = websocket.create_connection(args.ws)
|
||||||
|
|
||||||
|
wst = threading.Thread(target=ws_thread, args=(device, ws), daemon=True)
|
||||||
|
wst.start()
|
||||||
|
|
||||||
|
device_thread(device, ws)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
386
polmon.cc
386
polmon.cc
@@ -1,386 +0,0 @@
|
|||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#ifndef WOZMON
|
|
||||||
#define WOZMON 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if WOZMON
|
|
||||||
#define BACKSPACE 0
|
|
||||||
#define ASCIIDUMP 0
|
|
||||||
#define CLEARSCREENCMD 0
|
|
||||||
#define FARCALL 0
|
|
||||||
#define SHOWTITLE 0
|
|
||||||
#define BOOTSTRAP 0
|
|
||||||
#endif // WOZMON
|
|
||||||
|
|
||||||
#ifndef BACKSPACE
|
|
||||||
#define BACKSPACE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ASCIIDUMP
|
|
||||||
#define ASCIIDUMP 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CLEARSCREENCMD
|
|
||||||
#define CLEARSCREENCMD 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FARCALL
|
|
||||||
#define FARCALL 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SHOWTITLE
|
|
||||||
#define SHOWTITLE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef BOOTSTRAP
|
|
||||||
#define BOOTSTRAP 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr int kDumpSize = 16;
|
|
||||||
|
|
||||||
uint8_t getc() {
|
|
||||||
register uint8_t c asm ("al");
|
|
||||||
asm volatile (
|
|
||||||
"movb $0x00, %%ah\n\t"
|
|
||||||
"int $0x16"
|
|
||||||
: "=r" (c)
|
|
||||||
:: "ah", "cc"
|
|
||||||
);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void putc(uint8_t c) {
|
|
||||||
asm volatile (
|
|
||||||
"push %%bp \n\t"
|
|
||||||
"movb %0, %%al \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"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void puts(const char* s) {
|
|
||||||
while (*s) {
|
|
||||||
putc(*s++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// arguments on the stack in reverse order
|
|
||||||
extern "C" uint16_t Jump(uint16_t addr, int nargs);
|
|
||||||
asm (
|
|
||||||
".section .text \n"
|
|
||||||
"Jump: \n"
|
|
||||||
" push %bx \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"
|
|
||||||
" pop %bp \n"
|
|
||||||
" pop %di \n"
|
|
||||||
" pop %si \n"
|
|
||||||
" pop %bx \n"
|
|
||||||
" ret"
|
|
||||||
// return value in ax
|
|
||||||
);
|
|
||||||
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ReadUintN(int n, const char* buf) {
|
|
||||||
uint16_t out = 0;
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
if (buf[i] == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out <<= 4;
|
|
||||||
out += ReadHexNibble(buf[i]);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t ReadUint8(const char* buf) {
|
|
||||||
return ReadUintN(2, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ReadUint16(const char* buf) {
|
|
||||||
return ReadUintN(4, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteHexNibble(uint8_t c) {
|
|
||||||
if (c > 9) {
|
|
||||||
putc('a' + c - 10);
|
|
||||||
} else {
|
|
||||||
putc('0' + c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteUint8(uint8_t a) {
|
|
||||||
WriteHexNibble(a >> 4);
|
|
||||||
WriteHexNibble(a & 0xf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteUint16(uint16_t a) {
|
|
||||||
WriteUint8(a >> 8);
|
|
||||||
WriteUint8(a & 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __set_es__(uint16_t es) {
|
|
||||||
asm volatile (
|
|
||||||
"mov %0, %%es"
|
|
||||||
:: "r" (es)
|
|
||||||
: "es"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t __get_es_u8__(uint16_t addr) {
|
|
||||||
register uint16_t ad asm ("di") = addr;
|
|
||||||
uint8_t ret;
|
|
||||||
asm volatile (
|
|
||||||
"movb %%es:(%1), %0"
|
|
||||||
: "=r" (ret)
|
|
||||||
: "r" (ad)
|
|
||||||
);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void __set_es_u8__(uint16_t addr, uint8_t val) {
|
|
||||||
register uint16_t ad asm ("di") = addr;
|
|
||||||
asm volatile (
|
|
||||||
"movb %0, %%es:(%1)"
|
|
||||||
:: "ri" (val), "rmi" (ad)
|
|
||||||
: "memory"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noreturn))
|
|
||||||
inline static void __basic__() {
|
|
||||||
asm volatile (
|
|
||||||
"movw $0x40, %ax \n\t"
|
|
||||||
"mov %ax, %ds \n\t"
|
|
||||||
"int $0x18"
|
|
||||||
);
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dump(uint16_t addr, int count) {
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
putc(' ');
|
|
||||||
uint8_t d = __get_es_u8__(addr + i);
|
|
||||||
WriteUint8(d);
|
|
||||||
}
|
|
||||||
#if ASCIIDUMP
|
|
||||||
putc(' ');
|
|
||||||
putc(' ');
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
uint8_t d = __get_es_u8__(addr + i);
|
|
||||||
if (d > 31 && d < 127) {
|
|
||||||
putc(d);
|
|
||||||
} else {
|
|
||||||
putc('.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void DumpHex(uint16_t addr, uint16_t seg) {
|
|
||||||
addr &= -kDumpSize;
|
|
||||||
|
|
||||||
putc('[');
|
|
||||||
WriteUint16(seg);
|
|
||||||
putc(':');
|
|
||||||
__set_es__(seg);
|
|
||||||
WriteUint16(addr);
|
|
||||||
putc(']');
|
|
||||||
putc(':');
|
|
||||||
Dump(addr, kDumpSize);
|
|
||||||
putc('\r');
|
|
||||||
putc('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClearScreen() {
|
|
||||||
asm volatile (
|
|
||||||
"movw $0x0002, %%ax \n\t"
|
|
||||||
"int $0x10"
|
|
||||||
::: "ax", "cc"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Status(uint8_t status) {
|
|
||||||
asm volatile (
|
|
||||||
"xorb %%bh, %%bh \n\t"
|
|
||||||
"movb $0x03, %%ah \n\t"
|
|
||||||
"int $0x10 \n\t"
|
|
||||||
"mov %%dx, %%di \n\t"
|
|
||||||
"movb $0x02, %%ah \n\t"
|
|
||||||
"movw $77, %%dx \n\t"
|
|
||||||
"int $0x10 \n\t"
|
|
||||||
"movb %0, %%al \n\t"
|
|
||||||
"call _ZN12_GLOBAL__N_110WriteUint8Eh \n\t"
|
|
||||||
"movb $0x02, %%ah \n\t"
|
|
||||||
"movw %%di, %%dx \n\t"
|
|
||||||
"int $0x10"
|
|
||||||
:: "rm" (status)
|
|
||||||
: "ax", "bh", "dx", "cx", "di", "cc"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
|
|
||||||
bool dump = true;
|
|
||||||
for (const char* ptr = buf; *ptr;) {
|
|
||||||
if (*ptr == 's') {
|
|
||||||
cur_addr = 0;
|
|
||||||
cur_seg = ReadUint16(ptr+1);
|
|
||||||
ptr += 5;
|
|
||||||
} else if (*ptr == '$') {
|
|
||||||
__basic__();
|
|
||||||
} else if (*ptr == 'r') {
|
|
||||||
dump = false;
|
|
||||||
#if FARCALL
|
|
||||||
int nargs = 0;
|
|
||||||
ptr++;
|
|
||||||
for (; *ptr;) {
|
|
||||||
if (*ptr == ' ') {
|
|
||||||
ptr++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
uint16_t d = ReadUint16(ptr);
|
|
||||||
asm volatile ("push %0" :: "r" (d));
|
|
||||||
nargs++;
|
|
||||||
ptr += 4;
|
|
||||||
}
|
|
||||||
__set_es__(cur_seg);
|
|
||||||
uint16_t ret = Jump(cur_addr, 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++;
|
|
||||||
#if CLEARSCREENCMD
|
|
||||||
} else if (*ptr == 'l') {
|
|
||||||
dump = false;
|
|
||||||
ClearScreen();
|
|
||||||
ptr++;
|
|
||||||
#endif
|
|
||||||
} else if (*ptr == 'w') {
|
|
||||||
dump = false;
|
|
||||||
uint16_t addr = cur_addr;
|
|
||||||
ptr += 1;
|
|
||||||
for (; *ptr;) {
|
|
||||||
if (*ptr == ' ') {
|
|
||||||
ptr++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
uint8_t d = ReadUint8(ptr);
|
|
||||||
__set_es__(cur_seg);
|
|
||||||
__set_es_u8__(addr, d);
|
|
||||||
addr++;
|
|
||||||
ptr += 2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cur_addr = ReadUint16(ptr);
|
|
||||||
ptr += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dump;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* prompt = "> ";
|
|
||||||
|
|
||||||
void polmon() {
|
|
||||||
uint16_t cur_addr = 0;
|
|
||||||
uint16_t cur_seg = 0;
|
|
||||||
char inbuf[64];
|
|
||||||
bool first = true;
|
|
||||||
char* inptr = inbuf;
|
|
||||||
|
|
||||||
ClearScreen();
|
|
||||||
#if SHOWTITLE
|
|
||||||
puts("PolMon 0.2\r\n");
|
|
||||||
#endif // SHOWTITLE
|
|
||||||
puts(prompt);
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
uint8_t c = getc();
|
|
||||||
putc(c); // echo
|
|
||||||
if (c == '\r') {
|
|
||||||
*inptr = 0;
|
|
||||||
if (inbuf[0] == 0) {
|
|
||||||
if (!first) {
|
|
||||||
cur_addr += kDumpSize;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
putc('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ParseCommand(inbuf, cur_addr, cur_seg)) {
|
|
||||||
DumpHex(cur_addr, cur_seg);
|
|
||||||
};
|
|
||||||
first = false;
|
|
||||||
inptr = inbuf;
|
|
||||||
puts(prompt);
|
|
||||||
#if BACKSPACE
|
|
||||||
} else if (c == kBackspace || c == kOtherBackspace) {
|
|
||||||
inptr--;
|
|
||||||
if (inptr < inbuf) {
|
|
||||||
inptr = inbuf;
|
|
||||||
putc(' ');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
putc(' ');
|
|
||||||
putc(kOtherBackspace);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
*inptr = c;
|
|
||||||
inptr++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
polmon();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BOOTSTRAP
|
|
||||||
__attribute__((section(".init"), noreturn, used))
|
|
||||||
void _start() {
|
|
||||||
main();
|
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
|
||||||
#endif // BOOTSTRAP
|
|
||||||
55
src/Makefile
Normal file
55
src/Makefile
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
CC = ia16-elf-gcc
|
||||||
|
CXX = ia16-elf-gcc
|
||||||
|
LD = ia16-elf-gcc
|
||||||
|
OBJCOPY = ia16-elf-objcopy
|
||||||
|
CXXFLAGS = -mregparmcall -ffunction-sections -Os -flto
|
||||||
|
CFLAGS = -mregparmcall -ffunction-sections -Os -flto
|
||||||
|
LDFLAGS = -mregparmcall -Wl,--gc-sections -Os -nostdlib -flto
|
||||||
|
|
||||||
|
%.bin: %.asm
|
||||||
|
nasm $< -o $@
|
||||||
|
|
||||||
|
%.elf:
|
||||||
|
$(LD) $(LDFLAGS) $(CPPFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
%.com: %.elf
|
||||||
|
$(OBJCOPY) -O binary $< $@
|
||||||
|
|
||||||
|
fat12boot.bin:
|
||||||
|
$(OBJCOPY) -O binary $< $@
|
||||||
|
|
||||||
|
fat12boot.elf: fat12boot.o fat12.o bootsect.S stdlib.o
|
||||||
|
fat12boot.elf: LDFLAGS += -T bootsect.ld
|
||||||
|
|
||||||
|
fat12boot.bin: fat12boot.elf
|
||||||
|
|
||||||
|
stdlib.o: CFLAGS := $(filter-out -flto, $(CFLAGS))
|
||||||
|
|
||||||
|
polmon.elf: LDFLAGS += -T init.ld
|
||||||
|
polmon.elf: polmon.o stdlib.o
|
||||||
|
|
||||||
|
polio.elf: LDFLAGS += -T flat0600.ld -mprotected-mode
|
||||||
|
polio.elf: polio-main.o polio.s fat12.o paracomm.o stdlib.o
|
||||||
|
|
||||||
|
paracli.elf: LDFLAGS += -T doscom.ld
|
||||||
|
paracli.elf: paracli.s paracomm.o
|
||||||
|
|
||||||
|
dos-programs = mushroom.com hello.com ftpget.com crc16.com
|
||||||
|
$(dos-programs:.com=.elf): LDFLAGS += -T doscom.ld
|
||||||
|
|
||||||
|
mushroom.elf: mushroom.s
|
||||||
|
hello.elf: hello.o stdlib.o crt0.s
|
||||||
|
ftpget.elf: ftpget.o stdlib.o crt0.s
|
||||||
|
crc16.elf: crc16.o stdlib.o crt0.s
|
||||||
|
|
||||||
|
polos.img: fat12boot.bin polmon.com polio.com $(dos-programs)
|
||||||
|
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 $@ $(dos-programs) ::/
|
||||||
|
dd if=fat12boot.bin of=$@ conv=notrunc
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean: ## Remove generated files
|
||||||
|
rm -rf *.bin *.elf *.o *.com polos.img
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
.global _start
|
.global _start
|
||||||
_start:
|
_start:
|
||||||
#ifndef NOBPB
|
#ifndef NOBPB
|
||||||
jmp l0
|
jmp 0f
|
||||||
nop
|
nop
|
||||||
|
|
||||||
.ascii "IBM 3.1"
|
.ascii "IBM 3.1"
|
||||||
@@ -18,7 +18,7 @@ bpb_localsectors: .word 720
|
|||||||
bpb_mediadescr: .byte 0xfd
|
bpb_mediadescr: .byte 0xfd
|
||||||
bpb_sectsperfat: .word 2
|
bpb_sectsperfat: .word 2
|
||||||
|
|
||||||
l0:
|
0:
|
||||||
#endif // NOBPB
|
#endif // NOBPB
|
||||||
cli
|
cli
|
||||||
xor %ax, %ax
|
xor %ax, %ax
|
||||||
25
src/bootsect.ld
Normal file
25
src/bootsect.ld
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
SECTIONS {
|
||||||
|
. = 0x7c00;
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
KEEP(*(.init))
|
||||||
|
*(.text*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
*(.rodata*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss (NOLOAD) : {
|
||||||
|
* (.bss)
|
||||||
|
}
|
||||||
|
|
||||||
|
.data (NOLOAD) : {
|
||||||
|
*(.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
. = 0x7dfe;
|
||||||
|
.magic : {
|
||||||
|
SHORT(0xAA55);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,15 +19,15 @@ _start:
|
|||||||
mov bx, 0x6
|
mov bx, 0x6
|
||||||
xor cx, cx
|
xor cx, cx
|
||||||
a1:
|
a1:
|
||||||
cmp cx, [es:si+4]
|
cmp cx, [si+4]
|
||||||
jge a2
|
jge a2
|
||||||
lea ax, [es:si+bx]
|
lea ax, [si+bx]
|
||||||
push ax
|
push ax
|
||||||
add bl, 2
|
add bl, 2
|
||||||
inc cl
|
inc cl
|
||||||
jmp a1
|
jmp a1
|
||||||
a2:
|
a2:
|
||||||
call far [es:si]
|
call far [si]
|
||||||
pop bp
|
pop bp
|
||||||
pop di
|
pop di
|
||||||
pop si
|
pop si
|
||||||
@@ -11,9 +11,12 @@ main:
|
|||||||
mov si, [si]
|
mov si, [si]
|
||||||
push ds
|
push ds
|
||||||
push es
|
push es
|
||||||
pop ds
|
mov ax, cs
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
cld
|
cld
|
||||||
rep movsw
|
rep movsw
|
||||||
|
pop es
|
||||||
pop ds
|
pop ds
|
||||||
|
|
||||||
retf 6
|
retf 6
|
||||||
45
src/crc16.asm
Normal file
45
src/crc16.asm
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
CPU 8086
|
||||||
|
|
||||||
|
_start:
|
||||||
|
push bp
|
||||||
|
mov bp, sp
|
||||||
|
mov si, [bp+8]
|
||||||
|
push word [si]
|
||||||
|
mov si, [bp+10]
|
||||||
|
push word [si]
|
||||||
|
call crc16
|
||||||
|
mov di, [bp+6]
|
||||||
|
mov [di], ax
|
||||||
|
mov sp, bp
|
||||||
|
pop bp
|
||||||
|
retf 6
|
||||||
|
|
||||||
|
crc16:
|
||||||
|
push bp
|
||||||
|
mov bp, sp
|
||||||
|
mov bx, [bp+4]
|
||||||
|
add [bp+6], bx
|
||||||
|
mov ax, -1
|
||||||
|
.L2:
|
||||||
|
cmp bx, [bp+6]
|
||||||
|
jne .L5
|
||||||
|
pop bp
|
||||||
|
ret
|
||||||
|
.L5:
|
||||||
|
mov dl, [bx]
|
||||||
|
mov cl, 0x8
|
||||||
|
shl dx, cl
|
||||||
|
xor ax, dx
|
||||||
|
mov dl, cl
|
||||||
|
.L4:
|
||||||
|
mov cx, ax
|
||||||
|
shl cx, 1
|
||||||
|
test ax, ax
|
||||||
|
xchg cx, ax
|
||||||
|
jge .L3
|
||||||
|
xor ax, 4129
|
||||||
|
.L3:
|
||||||
|
dec dl
|
||||||
|
jne .L4
|
||||||
|
inc bx
|
||||||
|
jmp .L2
|
||||||
31
src/crc16.c
Normal file
31
src/crc16.c
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "polos.h"
|
||||||
|
|
||||||
|
#define kDefaultSegment 0
|
||||||
|
|
||||||
|
uint16_t crc16(uint16_t data, const uint16_t seg, int data_len) {
|
||||||
|
uint16_t crc = 0xFFFF;
|
||||||
|
for (unsigned int i = 0; i < data_len; ++i) {
|
||||||
|
uint16_t dbyte = __get_far_u8__(data + i, seg);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, int argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uint16_t seg = kDefaultSegment;
|
||||||
|
if (argc > 2) {
|
||||||
|
seg = argv[2];
|
||||||
|
}
|
||||||
|
return crc16(argv[0], seg, argv[1]);
|
||||||
|
}
|
||||||
8
src/crt0.s
Normal file
8
src/crt0.s
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.arch i8086,jumps
|
||||||
|
.code16
|
||||||
|
.intel_syntax noprefix
|
||||||
|
|
||||||
|
.section .init
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
jmp main
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
SECTIONS {
|
SECTIONS {
|
||||||
. = 0x7c00;
|
. = 0x100;
|
||||||
|
|
||||||
.text : {
|
.text : {
|
||||||
KEEP(*(.init))
|
KEEP(*(.init))
|
||||||
@@ -14,7 +14,7 @@ SECTIONS {
|
|||||||
* (.bss)
|
* (.bss)
|
||||||
}
|
}
|
||||||
|
|
||||||
.data : {
|
.data (NOLOAD) : {
|
||||||
*(.data)
|
*(.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
239
src/fat12.c
Normal file
239
src/fat12.c
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "polos.h"
|
||||||
|
|
||||||
|
#define kSectorsPerCluster 2
|
||||||
|
#define kSectorsPerTrack 9
|
||||||
|
#define kHeads 2
|
||||||
|
#define kTracks 40
|
||||||
|
#define kFatSizeSectors 2
|
||||||
|
#define kRootDirSizeSectors 7
|
||||||
|
#define kBytesPerCluster 1024
|
||||||
|
#define kDataRegionStartSector (1 + kFatSizeSectors * 2 + kRootDirSizeSectors)
|
||||||
|
#define kNumClusters ((kSectorsPerTrack * kHeads * kTracks - kDataRegionStartSector) / kSectorsPerCluster + 2)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char name[8];
|
||||||
|
char ext[3];
|
||||||
|
uint8_t attr;
|
||||||
|
|
||||||
|
uint8_t dontcare[14];
|
||||||
|
|
||||||
|
uint16_t cluster;
|
||||||
|
uint32_t size;
|
||||||
|
} direntry;
|
||||||
|
|
||||||
|
static uint8_t* gFat;
|
||||||
|
static direntry* gRootdir;
|
||||||
|
|
||||||
|
static uint8_t rwsector(uint8_t w, int c, int h, int s, void* addr) {
|
||||||
|
uint16_t ret;
|
||||||
|
uint16_t cmd = w ? 0x0301 : 0x0201;
|
||||||
|
// retry for a total of up to 3 attempts if drive not ready
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
ret = __readwritesector__(cmd, addr, c, h, s) >> 8;
|
||||||
|
if (ret != 0x80) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t readsector(int c, int h, int s, void* addr) {
|
||||||
|
return rwsector(0, c, h, s, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t writesector(int c, int h, int s, void* addr) {
|
||||||
|
return rwsector(1, c, h, s, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int readcluster(int cluster) {
|
||||||
|
int offs = cluster * 3 / 2;
|
||||||
|
if (cluster % 2) {
|
||||||
|
// high nibble is lsb + whole byte
|
||||||
|
return ((gFat[offs] & 0xf0) >> 4) + (gFat[offs + 1] << 4);
|
||||||
|
} else {
|
||||||
|
return gFat[offs] + ((gFat[offs + 1] & 0x0f) << 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int writecluster(int cluster, int value) {
|
||||||
|
int offs = cluster * 3 / 2;
|
||||||
|
if (cluster % 2) {
|
||||||
|
gFat[offs] = (gFat[offs] & 0x0f) + ((value & 0x0f) << 4);
|
||||||
|
gFat[offs+1] = value >> 4;
|
||||||
|
// high nibble is lsb + whole byte
|
||||||
|
} else {
|
||||||
|
gFat[offs] = value & 0xff;
|
||||||
|
gFat[offs+1] = (gFat[offs+1] & 0xf0) + (value >> 8);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int firstfreecluster() {
|
||||||
|
for (int i = 2; i < kNumClusters; i++) {
|
||||||
|
if (readcluster(i) == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cluster2chs(int cluster, int* c, int* h, int* s) {
|
||||||
|
int logicalsector =
|
||||||
|
kDataRegionStartSector + (cluster - 2) * kSectorsPerCluster;
|
||||||
|
*s = (logicalsector % kSectorsPerTrack) + 1;
|
||||||
|
*h = (logicalsector / kSectorsPerTrack) % kHeads;
|
||||||
|
*c = logicalsector / (kHeads * kSectorsPerTrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int loadfile(direntry* entry, void* addr) {
|
||||||
|
int cluster = entry->cluster;
|
||||||
|
for (int i = 0; i < entry->size; i += kBytesPerCluster) {
|
||||||
|
int c, h, s;
|
||||||
|
cluster2chs(cluster, &c, &h, &s);
|
||||||
|
if (readsector(c, h, s, addr + i)) {
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
if (s > 9) {
|
||||||
|
s = 1;
|
||||||
|
h++;
|
||||||
|
}
|
||||||
|
// if (h > 1) { // this will never happen
|
||||||
|
// h = 0;
|
||||||
|
// c++;
|
||||||
|
// }
|
||||||
|
if (readsector(c, h, s, addr + i + 512)) {
|
||||||
|
return -5;
|
||||||
|
};
|
||||||
|
cluster = readcluster(cluster);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static direntry* findfile(const char* name) {
|
||||||
|
direntry* file = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
direntry* entry = &gRootdir[i];
|
||||||
|
if (!strncmp(entry->name, name, 11)) {
|
||||||
|
file = entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
static direntry* newfile(const char* name) {
|
||||||
|
direntry* file = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
direntry* entry = &gRootdir[i];
|
||||||
|
if (entry->name[0] == 0) {
|
||||||
|
file = entry;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!file) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(file->name, name, 11);
|
||||||
|
file->size = 0;
|
||||||
|
file->cluster = 0;
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int allocatecluster(int previous) {
|
||||||
|
int cluster = firstfreecluster();
|
||||||
|
writecluster(cluster, 0xfff);
|
||||||
|
if (previous != 0) {
|
||||||
|
writecluster(previous, cluster);
|
||||||
|
}
|
||||||
|
return cluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeclusterchain(int cluster) {
|
||||||
|
if (cluster < 2 || cluster >= 0xf00) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
freeclusterchain(readcluster(cluster));
|
||||||
|
writecluster(cluster, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int savefile(direntry* file, void* addr, int size) {
|
||||||
|
freeclusterchain(file->cluster);
|
||||||
|
file->cluster = 0;
|
||||||
|
int cluster = 0;
|
||||||
|
for (int i = 0; i < size; i += kBytesPerCluster) {
|
||||||
|
cluster = allocatecluster(cluster);
|
||||||
|
if (file->cluster == 0) {
|
||||||
|
file->cluster = cluster;
|
||||||
|
}
|
||||||
|
int c, h, s;
|
||||||
|
cluster2chs(cluster, &c, &h, &s);
|
||||||
|
if (writesector(c, h, s, addr + i)) {
|
||||||
|
return -7;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
if (s > 9) {
|
||||||
|
s = 1;
|
||||||
|
h++;
|
||||||
|
}
|
||||||
|
// if (h > 1) { // no cluster will cross a cylinder boundary
|
||||||
|
// h = 0;
|
||||||
|
// c++;
|
||||||
|
// }
|
||||||
|
if (writesector(c, h, s, addr + i + 512)) {
|
||||||
|
return -7;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
file->size = size;
|
||||||
|
if (writesector(0, 0, 2, gFat)) {
|
||||||
|
return -8;
|
||||||
|
}
|
||||||
|
if (writesector(0, 0, 6, gRootdir)) {
|
||||||
|
return -9;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat12_init(void* fat_addr, void* rootdir_addr) {
|
||||||
|
if (readsector(0, 0, 2, fat_addr)) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
if (readsector(0, 0, 6, rootdir_addr)) {
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
gFat = fat_addr;
|
||||||
|
gRootdir = rootdir_addr;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat12_readfile(const char* name, void* addr) {
|
||||||
|
direntry* file = findfile(name);
|
||||||
|
if (!file) {
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadfile(file, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fat12_writefile(const char* name, void* addr, int size) {
|
||||||
|
direntry* file = findfile(name);
|
||||||
|
if (!file) {
|
||||||
|
file = newfile(name);
|
||||||
|
}
|
||||||
|
if (!file) {
|
||||||
|
// no moar space
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
return savefile(file, addr, size);
|
||||||
|
}
|
||||||
@@ -11,5 +11,5 @@ int fat12_init(void* fat_addr, void* rootdir_addr);
|
|||||||
* Returns:
|
* Returns:
|
||||||
* -4 if file is not found
|
* -4 if file is not found
|
||||||
* -5 if there is a disk error
|
* -5 if there is a disk error
|
||||||
*/
|
*/
|
||||||
int fat12_readfile(const char* name, void* addr);
|
int fat12_readfile(const char* name, void* addr);
|
||||||
53
src/fat12boot.c
Normal file
53
src/fat12boot.c
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "fat12.h"
|
||||||
|
|
||||||
|
#define kPolmonAddress ((void*)0x0600)
|
||||||
|
#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");
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int puts(const char* msg) {
|
||||||
|
while (*msg) {
|
||||||
|
putchar(*msg++);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noreturn)) static void die(const char* msg) {
|
||||||
|
puts(msg);
|
||||||
|
while (1) {
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noreturn)) static void jump(void* addr) {
|
||||||
|
asm volatile("ljmp $0,%0" ::"i"(addr));
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noreturn)) static void loadpolmon() {
|
||||||
|
if (fat12_init(kFatAddress, kRootDirAddress)) {
|
||||||
|
die("fi");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fat12_readfile("POLIO COM", kPolmonAddress)) {
|
||||||
|
asm volatile("mov $00, %%ah \n\t"
|
||||||
|
"int $0x16 \n\t" ::
|
||||||
|
: "ax");
|
||||||
|
}
|
||||||
|
|
||||||
|
jump(kPolmonAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() { loadpolmon(); }
|
||||||
@@ -14,7 +14,7 @@ SECTIONS {
|
|||||||
* (.bss)
|
* (.bss)
|
||||||
}
|
}
|
||||||
|
|
||||||
.data : {
|
.data (NOLOAD) : {
|
||||||
*(.data)
|
*(.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
48
src/ftp.asm
Normal file
48
src/ftp.asm
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
CPU 8086
|
||||||
|
|
||||||
|
chunksize equ 0x20
|
||||||
|
devicedelay equ 0x08
|
||||||
|
|
||||||
|
_start:
|
||||||
|
push bp
|
||||||
|
mov bp, sp
|
||||||
|
sub sp, 8
|
||||||
|
mov [bp-2], word chunksize ; recv size
|
||||||
|
mov [bp-4], word 0xf000 ; recv addr
|
||||||
|
mov [bp-6], word 0x0001 ; send size
|
||||||
|
mov [bp-8], word 0x7000 ; send addr
|
||||||
|
mov bx, [bp-8]
|
||||||
|
mov [bx], byte 0x42
|
||||||
|
l0:
|
||||||
|
cmp word [bp-4], 0xf600
|
||||||
|
jb l1
|
||||||
|
l2:
|
||||||
|
mov sp, bp
|
||||||
|
pop bp
|
||||||
|
ret
|
||||||
|
l1:
|
||||||
|
mov ah, 0x07
|
||||||
|
int 0x80
|
||||||
|
test al, al
|
||||||
|
jnz l1
|
||||||
|
push word [bp-2]
|
||||||
|
push word [bp-4]
|
||||||
|
mov ah, 0x05
|
||||||
|
int 0x80
|
||||||
|
test al, al
|
||||||
|
jz l2
|
||||||
|
mov ah, 0
|
||||||
|
add [bp-4], ax
|
||||||
|
push word [bp-6]
|
||||||
|
push word [bp-8]
|
||||||
|
mov ah, 0x06
|
||||||
|
int 0x80
|
||||||
|
add sp, 8
|
||||||
|
mov cx, devicedelay
|
||||||
|
l3:
|
||||||
|
mov ah, 0x07
|
||||||
|
push cx
|
||||||
|
int 0x80
|
||||||
|
pop cx
|
||||||
|
loop l3 ; give the device some time to answer
|
||||||
|
jmp l0
|
||||||
52
src/ftpget.c
Normal file
52
src/ftpget.c
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "polos.h"
|
||||||
|
|
||||||
|
#define kDefaultChunkSize 0x20
|
||||||
|
|
||||||
|
static int recv(uint8_t* buf, uint8_t size) {
|
||||||
|
uint16_t ds = 0;
|
||||||
|
asm volatile ("push %%ds \n\t"
|
||||||
|
"mov %0, %%ds \n\t"
|
||||||
|
"pop %0 \n" : "+r"(ds));
|
||||||
|
int len = read(kLpt1, buf, size);
|
||||||
|
asm volatile ("mov %0, %%ds" :: "r"(ds));
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, uint16_t argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* dest = (uint8_t*)argv[0];
|
||||||
|
uint16_t size = argv[1];
|
||||||
|
uint8_t chunksize = kDefaultChunkSize;
|
||||||
|
if (argc > 2) {
|
||||||
|
chunksize = argv[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ack = 0x42;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < size;) {
|
||||||
|
// delay?
|
||||||
|
|
||||||
|
int len;
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
int r = runcomms(kUntilIdle);
|
||||||
|
len = recv(dest + i, chunksize);
|
||||||
|
if (len > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (len <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
write(kLpt1, &ack, 1);
|
||||||
|
i += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
12
src/hello.c
Normal file
12
src/hello.c
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(int argc, int argv[]) {
|
||||||
|
puts("Hello, world!\r\n");
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
return argv[0] + argv[1];
|
||||||
|
} else if (argc > 0) {
|
||||||
|
return argv[0];
|
||||||
|
}
|
||||||
|
return 0x42;
|
||||||
|
}
|
||||||
20
src/init.ld
Normal file
20
src/init.ld
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
SECTIONS {
|
||||||
|
. = 0x1200;
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
KEEP(*(.init))
|
||||||
|
*(.text*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
*(.rodata*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss (NOLOAD) : {
|
||||||
|
* (.bss)
|
||||||
|
}
|
||||||
|
|
||||||
|
.data (NOLOAD) : {
|
||||||
|
*(.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
261
src/mushroom.s
Normal file
261
src/mushroom.s
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
.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 .bss
|
||||||
|
kBufSize = 77
|
||||||
|
kDividerRow = 23
|
||||||
|
kRecvBufSize = 128
|
||||||
|
|
||||||
|
curpos: .word 0
|
||||||
|
inputsize: .byte 0
|
||||||
|
escaped: .byte 0
|
||||||
|
inputbuf: .zero kBufSize
|
||||||
|
recvbuf: .zero kRecvBufSize
|
||||||
|
|
||||||
|
# XXX: funky call
|
||||||
|
# inputs:
|
||||||
|
# si: string offset
|
||||||
|
# cl: string length
|
||||||
|
# clobbers: ax, bh, dx
|
||||||
|
.section .text.printn
|
||||||
|
printn:
|
||||||
|
push bx
|
||||||
|
push bp
|
||||||
|
mov bh, 0
|
||||||
|
1:
|
||||||
|
test cl, cl
|
||||||
|
jz 0f
|
||||||
|
mov al, [si]
|
||||||
|
cmpb escaped, 0
|
||||||
|
je 3f
|
||||||
|
cmp al, 'm'
|
||||||
|
jne 5f
|
||||||
|
movb escaped, 0
|
||||||
|
jmp 5f
|
||||||
|
3:
|
||||||
|
cmp al, 0x1b # ESC
|
||||||
|
jne 4f
|
||||||
|
movb escaped, 1
|
||||||
|
jmp 5f
|
||||||
|
4:
|
||||||
|
mov ah, 0x0e # write tty
|
||||||
|
int 0x10
|
||||||
|
push cx
|
||||||
|
mov ah, 0x03 # get cursor
|
||||||
|
int 0x10
|
||||||
|
cmp dh, kDividerRow
|
||||||
|
jb 2f
|
||||||
|
push dx
|
||||||
|
mov ah, 0x08 # read color
|
||||||
|
int 0x10
|
||||||
|
mov bh, ah
|
||||||
|
xor cx, cx
|
||||||
|
mov dx, 0x164f
|
||||||
|
mov ax, 0x0601 # scroll up, 1 line
|
||||||
|
int 0x10
|
||||||
|
pop dx
|
||||||
|
dec dh
|
||||||
|
mov bh, 0
|
||||||
|
mov ah, 0x02 # set cursor
|
||||||
|
int 0x10
|
||||||
|
2:
|
||||||
|
pop cx
|
||||||
|
5:
|
||||||
|
inc si
|
||||||
|
dec cl
|
||||||
|
jmp 1b
|
||||||
|
0:
|
||||||
|
pop bp
|
||||||
|
pop bx
|
||||||
|
ret
|
||||||
|
|
||||||
|
# XXX: funky call
|
||||||
|
# inputs:
|
||||||
|
# si: string offset
|
||||||
|
# cl: string length
|
||||||
|
# clobbers: ax, bh, dx
|
||||||
|
.section .text.addtext
|
||||||
|
addtext:
|
||||||
|
push di
|
||||||
|
push cx
|
||||||
|
mov bh, 0
|
||||||
|
mov ah, 0x03 # get cursor pos
|
||||||
|
int 0x10
|
||||||
|
pop cx
|
||||||
|
push dx
|
||||||
|
mov di, offset curpos
|
||||||
|
mov dx, [di]
|
||||||
|
mov ah, 0x02 # set cursor
|
||||||
|
int 0x10
|
||||||
|
call printn
|
||||||
|
mov ah, 0x03 # get cursor pos
|
||||||
|
int 0x10
|
||||||
|
mov [di], dx
|
||||||
|
pop dx
|
||||||
|
mov ah, 0x02 # set cursor
|
||||||
|
int 0x10
|
||||||
|
pop di
|
||||||
|
ret
|
||||||
|
|
||||||
|
.section .text.sendbuffer
|
||||||
|
sendbuffer:
|
||||||
|
push bp
|
||||||
|
mov bp, sp
|
||||||
|
sub sp, 2
|
||||||
|
mov al, inputsize
|
||||||
|
mov [bp-2], al
|
||||||
|
movb inputsize, 0
|
||||||
|
mov si, offset .l0
|
||||||
|
mov cl, 2
|
||||||
|
call addtext
|
||||||
|
mov si, offset inputbuf
|
||||||
|
mov cl, [bp-2]
|
||||||
|
call addtext
|
||||||
|
mov si, offset .l1
|
||||||
|
mov cl, 2
|
||||||
|
call addtext
|
||||||
|
mov si, offset inputbuf
|
||||||
|
mov cl, [bp-2]
|
||||||
|
call parasend
|
||||||
|
# XXX: should check cx in case not everything was sent
|
||||||
|
mov al, ' '
|
||||||
|
mov dx, 0x1802 # [24; 2]
|
||||||
|
call drawchar
|
||||||
|
mov ah, 0x02
|
||||||
|
int 0x10 # mov cursor back to start of line
|
||||||
|
add sp, 2
|
||||||
|
pop bp
|
||||||
|
ret
|
||||||
|
|
||||||
|
# draw until end of line
|
||||||
|
# al: char
|
||||||
|
# dh: row
|
||||||
|
# dl: start col
|
||||||
|
.section .text.drawchar
|
||||||
|
drawchar:
|
||||||
|
push ax
|
||||||
|
mov bh, 0
|
||||||
|
mov ah, 0x02 # set cursor
|
||||||
|
int 0x10
|
||||||
|
mov cx, 80
|
||||||
|
sub cl, dl
|
||||||
|
pop ax
|
||||||
|
mov ah, 0x0a
|
||||||
|
int 0x10
|
||||||
|
ret
|
||||||
|
|
||||||
|
.section .text.parasend
|
||||||
|
parasend:
|
||||||
|
mov ah, 0x06 # send parallel
|
||||||
|
push cx
|
||||||
|
push si
|
||||||
|
int 0x80
|
||||||
|
mov cx, 1
|
||||||
|
push cx
|
||||||
|
mov cx, offset .l1 + 1
|
||||||
|
push cx
|
||||||
|
mov ah, 0x06 # send parallel
|
||||||
|
int 0x80
|
||||||
|
add sp, 8
|
||||||
|
ret
|
||||||
|
|
||||||
|
prompt:
|
||||||
|
mov bh, 0
|
||||||
|
mov dx, 0x1800
|
||||||
|
mov ah, 0x02
|
||||||
|
int 0x10
|
||||||
|
mov ah, 0x0e
|
||||||
|
mov al, '>'
|
||||||
|
int 0x10
|
||||||
|
mov ah, 0x0e
|
||||||
|
mov al, ' '
|
||||||
|
int 0x10
|
||||||
|
ret
|
||||||
|
|
||||||
|
.section .rodata
|
||||||
|
.l0: .ascii "> "
|
||||||
|
.l1: .byte 0x0d, 0x0a
|
||||||
|
.l2: .ascii "MUSHRoom 8086 client 0.1\r\n"
|
||||||
|
l2len = . - offset .l2
|
||||||
|
|
||||||
|
.section .text.main
|
||||||
|
.global main
|
||||||
|
main:
|
||||||
|
movb inputsize, 0 # inputsize = 0
|
||||||
|
movw curpos, 0 # mov buffer cursor to [0;0]
|
||||||
|
movb escaped, 0
|
||||||
|
mov ax, 0x0002
|
||||||
|
int 0x10
|
||||||
|
mov si, offset .l2
|
||||||
|
mov cl, l2len
|
||||||
|
call addtext
|
||||||
|
mov dx, 0x1700 # [23; 0]
|
||||||
|
mov al, 0xc4 # horizontal box line
|
||||||
|
call drawchar
|
||||||
|
call prompt
|
||||||
|
0:
|
||||||
|
mov ah, 0x01
|
||||||
|
int 0x16 # check buffer
|
||||||
|
jz 1f # if empty, do a round of comms
|
||||||
|
mov ah, 0x00
|
||||||
|
int 0x16 # read 1 key
|
||||||
|
cmp al, 0x08 # backspace
|
||||||
|
je 5f
|
||||||
|
cmp al, 0x0d # CR
|
||||||
|
je 4f
|
||||||
|
mov di, offset inputbuf
|
||||||
|
mov si, offset inputsize
|
||||||
|
mov bh, 0
|
||||||
|
mov bl, [si]
|
||||||
|
cmp bl, kBufSize # stop accepting input at end of screen
|
||||||
|
jge 1f # do a round of comms
|
||||||
|
mov [di+bx], al # store character in buffer
|
||||||
|
incb [si] # increment buffer size
|
||||||
|
mov ah, 0x0e
|
||||||
|
mov bh, 0
|
||||||
|
int 0x10 # local echo
|
||||||
|
jmp 0b
|
||||||
|
5:
|
||||||
|
mov bl, inputsize
|
||||||
|
test bl, bl
|
||||||
|
jz 0b
|
||||||
|
decb inputsize
|
||||||
|
mov bh, 0
|
||||||
|
mov ah, 0x0e
|
||||||
|
int 0x10 # backspace
|
||||||
|
mov ax, 0x0e20
|
||||||
|
int 0x10
|
||||||
|
mov ax, 0x0e08
|
||||||
|
int 0x10
|
||||||
|
jmp 0b
|
||||||
|
4:
|
||||||
|
call sendbuffer # send input buffer
|
||||||
|
1:
|
||||||
|
mov ax, 0x0700 # do parallel comms
|
||||||
|
int 0x80
|
||||||
|
mov ax, kRecvBufSize
|
||||||
|
push ax
|
||||||
|
mov ax, offset recvbuf
|
||||||
|
push ax
|
||||||
|
mov ah, 0x05 # recv
|
||||||
|
int 0x80
|
||||||
|
test al, al
|
||||||
|
mov cl, al
|
||||||
|
pop si
|
||||||
|
pop bx
|
||||||
|
jz 0b
|
||||||
|
call addtext
|
||||||
|
jmp 0b
|
||||||
80
src/paracli.s
Normal file
80
src/paracli.s
Normal 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
|
||||||
125
src/paracomm.c
Normal file
125
src/paracomm.c
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#include "paracomm.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.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;
|
||||||
|
recv_cb miso_recv_cb;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
miso_recv_cb(miso_recvbuf, miso_size);
|
||||||
|
miso_state = 0;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void paracomm_init(recv_cb miso_cb) {
|
||||||
|
mosi_size = 0;
|
||||||
|
mosi_workbuf_idx = 0;
|
||||||
|
mosi_workbuf_size = 0;
|
||||||
|
mosi_state = 0;
|
||||||
|
|
||||||
|
miso_state = 0;
|
||||||
|
miso_recv_cb = miso_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t paracomm_busy() { return mosi_state != 0 || miso_state != 0; }
|
||||||
22
src/paracomm.h
Normal file
22
src/paracomm.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef void (*recv_cb)(const uint8_t* buf, uint8_t size);
|
||||||
|
|
||||||
|
/** Must call first **/
|
||||||
|
void paracomm_init(recv_cb 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();
|
||||||
|
|
||||||
|
/** Returns non-zero if busy */
|
||||||
|
uint8_t paracomm_busy();
|
||||||
112
src/polio-main.c
Normal file
112
src/polio-main.c
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "fat12.h"
|
||||||
|
#include "paracomm.h"
|
||||||
|
|
||||||
|
#define kFatAddr ((void*)0x1a00)
|
||||||
|
#define kRootDirAddr ((void*)0x1c00)
|
||||||
|
#define kPolmonAddr ((void*)0x1200)
|
||||||
|
#define kInt80Vector ((uint16_t*)(0x80 * 4))
|
||||||
|
#define kParallelDataPort 0x3bc
|
||||||
|
#define kParallelStatusPort 0x3bd
|
||||||
|
#define kParallelControlPort 0x3be
|
||||||
|
#define kParaDelay 100
|
||||||
|
#define kRecvBufferSize 256
|
||||||
|
|
||||||
|
void dosdbt();
|
||||||
|
void int80h();
|
||||||
|
|
||||||
|
static uint8_t parabuf[kRecvBufferSize];
|
||||||
|
static uint8_t parasize;
|
||||||
|
|
||||||
|
static void paracb(const uint8_t* buf, uint8_t size) {
|
||||||
|
if (parasize + size > sizeof(parabuf)) {
|
||||||
|
size = sizeof(parabuf) - parasize;
|
||||||
|
}
|
||||||
|
memcpy(parabuf + parasize, buf, size);
|
||||||
|
parasize += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void die() {
|
||||||
|
while (1) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __outb__(uint16_t port, uint8_t data) {
|
||||||
|
register uint16_t p asm("dx") = port;
|
||||||
|
register uint8_t d asm("al") = data;
|
||||||
|
asm volatile("outb %0, %1" ::"r"(d), "r"(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t __inb__(uint16_t port) {
|
||||||
|
register uint16_t p asm("dx") = port;
|
||||||
|
register uint8_t d asm("al");
|
||||||
|
asm volatile("inb %1, %0" : "=r"(d) : "r"(p));
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __delay__(int n) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
asm volatile("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t xferpara(uint8_t until_idle) {
|
||||||
|
do {
|
||||||
|
uint8_t mosib = paracomm_nextbyte();
|
||||||
|
__outb__(kParallelDataPort, mosib);
|
||||||
|
__outb__(kParallelControlPort, 1);
|
||||||
|
__delay__(kParaDelay);
|
||||||
|
__outb__(kParallelControlPort, 0);
|
||||||
|
uint8_t mison = __inb__(kParallelStatusPort) >> 4;
|
||||||
|
paracomm_feed(mison);
|
||||||
|
} while (until_idle && paracomm_busy());
|
||||||
|
|
||||||
|
return paracomm_busy();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t parasend(uint8_t* addr, uint8_t size) {
|
||||||
|
uint8_t i;
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
if (paracomm_send(addr[i])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t pararecv(uint8_t* buf, uint8_t size) {
|
||||||
|
if (parasize == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (size > parasize) {
|
||||||
|
size = parasize;
|
||||||
|
}
|
||||||
|
memcpy(buf, parabuf, size);
|
||||||
|
if (size < parasize) {
|
||||||
|
memcpy(parabuf, parabuf + size, parasize - size);
|
||||||
|
}
|
||||||
|
parasize -= size;
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
dosdbt();
|
||||||
|
parasize = 0;
|
||||||
|
paracomm_init(paracb);
|
||||||
|
|
||||||
|
fat12_init(kFatAddr, kRootDirAddr);
|
||||||
|
|
||||||
|
uint16_t* int80v = kInt80Vector;
|
||||||
|
int80v[0] = (uint16_t)int80h;
|
||||||
|
int80v[1] = 0;
|
||||||
|
|
||||||
|
if (fat12_readfile("POLMON COM", kPolmonAddr)) {
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
asm volatile("jmp *%0" ::"r"(kPolmonAddr));
|
||||||
|
}
|
||||||
191
src/polio.s
Normal file
191
src/polio.s
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
.arch i8086,jumps
|
||||||
|
.code16
|
||||||
|
.intel_syntax noprefix
|
||||||
|
|
||||||
|
.section .init
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
cli
|
||||||
|
mov ax, 0x2000 # stack address, shared with polmon
|
||||||
|
mov sp, ax # ss should already be 0
|
||||||
|
sti
|
||||||
|
jmp main
|
||||||
|
|
||||||
|
|
||||||
|
diskpointer = 0x1e*4
|
||||||
|
dbtbase = 0x0500
|
||||||
|
|
||||||
|
.section .text.dosdbt
|
||||||
|
.global 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
|
||||||
|
|
||||||
|
## 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
|
||||||
|
mov dl, 0 # drive
|
||||||
|
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
|
||||||
|
|
||||||
|
# params: ds:fname, ds:dest
|
||||||
|
readfile:
|
||||||
|
mov si, [bp-14] # ds
|
||||||
|
mov cl, 4
|
||||||
|
shl si, cl
|
||||||
|
mov ax, [bp+0]
|
||||||
|
add ax, si
|
||||||
|
mov dx, [bp+2]
|
||||||
|
add dx, si
|
||||||
|
jmp fat12_readfile
|
||||||
|
|
||||||
|
# params: ds:fname, ds:src, size
|
||||||
|
writefile:
|
||||||
|
mov si, [bp-14] # ds
|
||||||
|
mov cl, 4
|
||||||
|
shl si, cl
|
||||||
|
mov ax, [bp+0]
|
||||||
|
add ax, si
|
||||||
|
mov dx, [bp+2]
|
||||||
|
add dx, si
|
||||||
|
mov cx, [bp+4]
|
||||||
|
jmp fat12_writefile
|
||||||
|
|
||||||
|
# params: ds:addr, size
|
||||||
|
sendpara:
|
||||||
|
mov si, [bp-14] # ds
|
||||||
|
mov cl, 4
|
||||||
|
shl si, cl
|
||||||
|
mov ax, [bp+0]
|
||||||
|
add ax, si
|
||||||
|
mov dx, [bp+2]
|
||||||
|
jmp parasend
|
||||||
|
|
||||||
|
# params: ds:addr, size
|
||||||
|
recvpara:
|
||||||
|
mov si, [bp-14] # ds
|
||||||
|
mov cl, 4
|
||||||
|
shl si, cl
|
||||||
|
mov ax, [bp+0]
|
||||||
|
add ax, si
|
||||||
|
mov dx, [bp+2]
|
||||||
|
jmp pararecv
|
||||||
|
|
||||||
|
dummy:
|
||||||
|
mov ax, -1
|
||||||
|
ret
|
||||||
|
|
||||||
|
.section .text.int80h
|
||||||
|
.global int80h
|
||||||
|
int80h:
|
||||||
|
push si
|
||||||
|
push di
|
||||||
|
push bp
|
||||||
|
push ds
|
||||||
|
push es
|
||||||
|
mov bp, sp
|
||||||
|
xor cx, cx
|
||||||
|
mov es, cx
|
||||||
|
mov ds, cx
|
||||||
|
xchg ah, cl
|
||||||
|
shl cl
|
||||||
|
cmp cl, offset int80h_entries
|
||||||
|
jge 0f
|
||||||
|
mov si, cx
|
||||||
|
add bp, 16 # 10 for us, 6 for the interrupt
|
||||||
|
call cs:[int80h_table+si]
|
||||||
|
jmp 1f
|
||||||
|
0:
|
||||||
|
mov ax, -1
|
||||||
|
1:
|
||||||
|
pop es
|
||||||
|
pop ds
|
||||||
|
pop bp
|
||||||
|
pop di
|
||||||
|
pop si
|
||||||
|
iret
|
||||||
|
|
||||||
|
.section .rodata.int80h
|
||||||
|
int80h_table:
|
||||||
|
.word offset dummy # 0x00
|
||||||
|
.word offset readsector # 0x01
|
||||||
|
.word offset writesector # 0x02
|
||||||
|
.word offset formattrack # 0x03
|
||||||
|
.word offset readfile # 0x04
|
||||||
|
.word offset recvpara # 0x05
|
||||||
|
.word offset sendpara # 0x06
|
||||||
|
.word offset xferpara # 0x07
|
||||||
|
.word offset writefile # 0x08
|
||||||
|
|
||||||
|
int80h_entries = . - int80h_table
|
||||||
417
src/polmon.cc
Normal file
417
src/polmon.cc
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "polos.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr int kDumpSize = 16;
|
||||||
|
|
||||||
|
extern "C" uint16_t Int80h(uint16_t fun, uint16_t nargs, uint16_t args[]);
|
||||||
|
asm(".section .text.Int80h \n"
|
||||||
|
"Int80h: \n"
|
||||||
|
" push %si \n"
|
||||||
|
" push %bp \n"
|
||||||
|
" mov %sp, %bp \n"
|
||||||
|
" mov %cx, %si \n"
|
||||||
|
" shl $1, %dx \n"
|
||||||
|
" add %dx, %si \n"
|
||||||
|
"0: \n"
|
||||||
|
" cmp %cx, %si \n"
|
||||||
|
" je 1f \n"
|
||||||
|
" push -2(%si) \n"
|
||||||
|
" sub $2, %si \n"
|
||||||
|
" jmp 0b \n"
|
||||||
|
"1: \n"
|
||||||
|
" int $0x80 \n"
|
||||||
|
" mov %bp, %sp \n"
|
||||||
|
" pop %bp \n"
|
||||||
|
" pop %si \n"
|
||||||
|
" ret"
|
||||||
|
// return value in ax
|
||||||
|
);
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ReadUintN(int n, const char*& buf) {
|
||||||
|
uint16_t out = 0;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (*buf < '0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out <<= 4;
|
||||||
|
out += ReadHexNibble(*buf);
|
||||||
|
buf += 1;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ReadUint8(const char*& buf) { return ReadUintN(2, buf); }
|
||||||
|
|
||||||
|
uint16_t ReadUint16(const char*& buf) { return ReadUintN(4, buf); }
|
||||||
|
|
||||||
|
void WriteHexNibble(uint8_t c) {
|
||||||
|
if (c > 9) {
|
||||||
|
putchar('a' + c - 10);
|
||||||
|
} else {
|
||||||
|
putchar('0' + c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteUint8(uint8_t a) {
|
||||||
|
WriteHexNibble(a >> 4);
|
||||||
|
WriteHexNibble(a & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteUint16(uint16_t a) {
|
||||||
|
WriteUint8(a >> 8);
|
||||||
|
WriteUint8(a & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteUint16ln(uint16_t a) {
|
||||||
|
WriteUint16(a);
|
||||||
|
puts("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((noreturn)) inline static void __basic__() {
|
||||||
|
asm volatile("movw $0x40, %ax \n\t"
|
||||||
|
"mov %ax, %ds \n\t"
|
||||||
|
"int $0x18");
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dump(uint16_t addr, uint16_t seg, int count) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
putchar(' ');
|
||||||
|
uint8_t d = __get_far_u8__(addr + i, seg);
|
||||||
|
WriteUint8(d);
|
||||||
|
}
|
||||||
|
putchar(' ');
|
||||||
|
putchar(' ');
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
uint8_t d = __get_far_u8__(addr + i, seg);
|
||||||
|
if (d > 31 && d < 127) {
|
||||||
|
putchar(d);
|
||||||
|
} else {
|
||||||
|
putchar('.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpHex(uint16_t addr, uint16_t seg) {
|
||||||
|
addr &= -kDumpSize;
|
||||||
|
|
||||||
|
putchar('[');
|
||||||
|
WriteUint16(seg);
|
||||||
|
putchar(':');
|
||||||
|
WriteUint16(addr);
|
||||||
|
putchar(']');
|
||||||
|
putchar(':');
|
||||||
|
Dump(addr, seg, kDumpSize);
|
||||||
|
puts("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearScreen() {
|
||||||
|
asm volatile("movw $0x0002, %%ax \n\t"
|
||||||
|
"int $0x10" ::
|
||||||
|
: "ax", "cc");
|
||||||
|
}
|
||||||
|
|
||||||
|
char toupper(char c) {
|
||||||
|
if (c >= 'a' && c <= 'z') {
|
||||||
|
return c - ('a' - 'A');
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tofatname(const char* name, uint8_t len, char* fatname) {
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i < 11; i++) {
|
||||||
|
if (j == len || name[j] == '.') {
|
||||||
|
fatname[i] = ' ';
|
||||||
|
} else {
|
||||||
|
fatname[i] = toupper(name[j]);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
if (i == 7 && name[j] == '.') {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t __readfile__(const char* name, uint16_t addr) {
|
||||||
|
register uint16_t ret asm("ax");
|
||||||
|
asm volatile("push %2 \n\t"
|
||||||
|
"push %1 \n\t"
|
||||||
|
"mov $04, %%ah \n\t"
|
||||||
|
"int $0x80 \n\t"
|
||||||
|
"pop %%cx \n\t"
|
||||||
|
"pop %%cx \n\t"
|
||||||
|
: "=r"(ret)
|
||||||
|
: "r"(name), "r"(addr)
|
||||||
|
: "bx", "cx", "dx", "memory", "cc");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t LoadFile(const char* name, uint8_t len, uint16_t seg) {
|
||||||
|
constexpr uint16_t kAddress = 0x100;
|
||||||
|
// trampoline:
|
||||||
|
// call 0x100
|
||||||
|
// retf
|
||||||
|
// XXX: if you change kAddress, the trampoline needs to be updated
|
||||||
|
constexpr uint8_t kTrampoline[] = {0xe8, 0xfd, 0x00, 0xcb};
|
||||||
|
|
||||||
|
uint16_t headeraddr = (seg << 4);
|
||||||
|
uint16_t flataddr = headeraddr + kAddress;
|
||||||
|
|
||||||
|
char fatname[11];
|
||||||
|
tofatname(name, len, fatname);
|
||||||
|
|
||||||
|
uint16_t r = __readfile__(fatname, flataddr);
|
||||||
|
if (r) {
|
||||||
|
memcpy(fatname + 8, "COM", 3);
|
||||||
|
r = __readfile__(fatname, flataddr);
|
||||||
|
}
|
||||||
|
if (r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* ptr = reinterpret_cast<uint8_t*>(headeraddr);
|
||||||
|
memcpy(ptr, kTrampoline, sizeof(kTrampoline));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Exec(uint16_t seg, uint8_t argc, uint16_t* argv) {
|
||||||
|
constexpr uint16_t kAddress = 0x100;
|
||||||
|
constexpr uint16_t kArgvAddress = 0x0004;
|
||||||
|
|
||||||
|
uint16_t headeraddr = (seg << 4);
|
||||||
|
auto* argp = reinterpret_cast<uint16_t*>(headeraddr + kArgvAddress);
|
||||||
|
memcpy(argp, argv, argc * 2);
|
||||||
|
|
||||||
|
register uint16_t ac asm("ax") = argc;
|
||||||
|
register uint16_t av asm("dx") = kArgvAddress;
|
||||||
|
asm volatile("mov %2, %%cx \n\t"
|
||||||
|
"mov %%cx, %%ds \n\t"
|
||||||
|
"mov %%cx, %%es \n\t"
|
||||||
|
"cli \n\t"
|
||||||
|
"mov %%cx, %%ss \n\t"
|
||||||
|
"mov %3, %%cx \n\t"
|
||||||
|
"xchg %%cx, %%sp \n\t"
|
||||||
|
"sti \n\t"
|
||||||
|
"push %%cx \n\t"
|
||||||
|
"push %2 \n\t"
|
||||||
|
"xor %%cx, %%cx \n\t"
|
||||||
|
"push %%cx \n\t"
|
||||||
|
"mov %%sp, %%bx \n\t"
|
||||||
|
"lcall *(%%bx) \n\t"
|
||||||
|
"pop %%cx \n\t"
|
||||||
|
"pop %%dx \n\t"
|
||||||
|
"mov %%cx, %%ds \n\t"
|
||||||
|
"mov %%cx, %%es \n\t"
|
||||||
|
"cli \n\t"
|
||||||
|
"pop %%sp \n\t"
|
||||||
|
"mov %%cx, %%ss \n\t"
|
||||||
|
"sti"
|
||||||
|
: "+r"(ac), "+r"(av)
|
||||||
|
: "r"(seg), "i"(kAddress)
|
||||||
|
: "bx", "cx", "memory", "cc");
|
||||||
|
|
||||||
|
return ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint16_t kCtrlBreakVector = (0x1b * 4);
|
||||||
|
|
||||||
|
extern "C" void cbreak();
|
||||||
|
asm(" .section .text.cbreak \n"
|
||||||
|
"cbreak: \n"
|
||||||
|
" xor %ax, %ax \n"
|
||||||
|
" mov %ax, %ds \n"
|
||||||
|
" cli \n"
|
||||||
|
" mov %ax, %ss \n"
|
||||||
|
" mov $0x2000, %sp \n"
|
||||||
|
" sti \n"
|
||||||
|
" pushf \n"
|
||||||
|
" push %ax \n"
|
||||||
|
" mov $0x1000, %ax \n"
|
||||||
|
" push %ax \n"
|
||||||
|
" mov $0x20, %al \n"
|
||||||
|
" out %al, $0x20 \n"
|
||||||
|
" iret \n");
|
||||||
|
|
||||||
|
void InstallCtrlBreak() {
|
||||||
|
auto vec = reinterpret_cast<void (**)()>(kCtrlBreakVector);
|
||||||
|
vec[0] = cbreak;
|
||||||
|
vec[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// j
|
||||||
|
// w [u8]*
|
||||||
|
// i [u16] [u16]*
|
||||||
|
// r str [u16]*
|
||||||
|
// k u16 u16 u16
|
||||||
|
|
||||||
|
int ReadArgs(const char*& ptr, uint16_t* args, int size) {
|
||||||
|
int nargs = 0;
|
||||||
|
while (*ptr && nargs < size) {
|
||||||
|
if (*ptr == ' ') {
|
||||||
|
ptr += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
args[nargs] = ReadUint16(ptr);
|
||||||
|
nargs += 1;
|
||||||
|
}
|
||||||
|
return nargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ReadString(const char*& ptr, const char*& str) {
|
||||||
|
while (*ptr == ' ') {
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
str = ptr;
|
||||||
|
int l;
|
||||||
|
for (l = 0; *ptr && *ptr != ' '; l++, ptr++) {
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) {
|
||||||
|
bool dump = true;
|
||||||
|
for (const char* ptr = buf; *ptr;) {
|
||||||
|
if (*ptr == 's') {
|
||||||
|
ptr += 1;
|
||||||
|
cur_addr = 0;
|
||||||
|
ReadArgs(ptr, &cur_seg, 1);
|
||||||
|
} else if (*ptr == '$') {
|
||||||
|
__basic__();
|
||||||
|
} else if (*ptr == 'j') {
|
||||||
|
dump = false;
|
||||||
|
ptr++;
|
||||||
|
auto jump = reinterpret_cast<uint16_t (*)()>(cur_addr);
|
||||||
|
uint16_t ret = jump();
|
||||||
|
WriteUint16ln(ret);
|
||||||
|
} else if (*ptr == 'i') {
|
||||||
|
dump = false;
|
||||||
|
ptr++;
|
||||||
|
uint16_t args[8]; // ought to be enough for everybody
|
||||||
|
int nargs = ReadArgs(ptr, args, 8);
|
||||||
|
uint16_t fun = args[0];
|
||||||
|
uint16_t ret = Int80h(fun, nargs - 1, args + 1);
|
||||||
|
WriteUint16ln(ret);
|
||||||
|
} else if (*ptr == 'l') {
|
||||||
|
dump = false;
|
||||||
|
ptr++;
|
||||||
|
const char* fname;
|
||||||
|
int l = ReadString(ptr, fname);
|
||||||
|
uint16_t ret = LoadFile(fname, l, cur_seg);
|
||||||
|
if (ret) {
|
||||||
|
WriteUint16ln(ret);
|
||||||
|
}
|
||||||
|
} else if (*ptr == 'r') {
|
||||||
|
dump = false;
|
||||||
|
ptr++;
|
||||||
|
uint16_t args[8]; // ought to be enough for everybody
|
||||||
|
int nargs = ReadArgs(ptr, args, 8);
|
||||||
|
uint16_t ret = Exec(cur_seg, nargs, args);
|
||||||
|
WriteUint16ln(ret);
|
||||||
|
} else if (*ptr == 'w') {
|
||||||
|
dump = false;
|
||||||
|
uint16_t addr = cur_addr;
|
||||||
|
ptr += 1;
|
||||||
|
for (; *ptr;) {
|
||||||
|
if (*ptr == ' ') {
|
||||||
|
ptr++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint8_t d = ReadUint8(ptr);
|
||||||
|
__set_far_u8__(addr, cur_seg, d);
|
||||||
|
addr++;
|
||||||
|
}
|
||||||
|
} else if (*ptr == 'k') {
|
||||||
|
dump = false;
|
||||||
|
ptr++;
|
||||||
|
uint16_t args[2];
|
||||||
|
// TODO: implement copy on non-zero segments
|
||||||
|
if (cur_seg == 0 && ReadArgs(ptr, args, 2) == 2) {
|
||||||
|
auto* src = reinterpret_cast<uint8_t*>(args[0]);
|
||||||
|
uint16_t size = args[1];
|
||||||
|
auto* dest = reinterpret_cast<uint8_t*>(cur_addr);
|
||||||
|
memcpy(dest, src, size);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cur_addr = ReadUint16(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dump;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayPrompt() { puts("> "); }
|
||||||
|
|
||||||
|
[[noreturn]]
|
||||||
|
void polmon() {
|
||||||
|
uint16_t cur_addr = 0;
|
||||||
|
uint16_t cur_seg = 0;
|
||||||
|
char inbuf[64];
|
||||||
|
bool first = true;
|
||||||
|
char* inptr = inbuf;
|
||||||
|
|
||||||
|
ClearScreen();
|
||||||
|
puts("PolMon for Workgroups 3.1\r\n");
|
||||||
|
DisplayPrompt();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
uint8_t c = getchar();
|
||||||
|
putchar(c); // echo
|
||||||
|
if (c == '\r') {
|
||||||
|
*inptr = 0;
|
||||||
|
if (inbuf[0] == 0) {
|
||||||
|
if (!first) {
|
||||||
|
cur_addr += kDumpSize;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ParseCommand(inbuf, cur_addr, cur_seg)) {
|
||||||
|
DumpHex(cur_addr, cur_seg);
|
||||||
|
};
|
||||||
|
first = false;
|
||||||
|
inptr = inbuf;
|
||||||
|
DisplayPrompt();
|
||||||
|
} else if (c == kBackspace || c == kOtherBackspace) {
|
||||||
|
inptr--;
|
||||||
|
if (inptr < inbuf) {
|
||||||
|
inptr = inbuf;
|
||||||
|
putchar(' ');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
putchar(' ');
|
||||||
|
putchar(kOtherBackspace);
|
||||||
|
} else {
|
||||||
|
*inptr = c;
|
||||||
|
inptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
InstallCtrlBreak();
|
||||||
|
polmon();
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((section(".init"), used)) void _start() { main(); }
|
||||||
59
src/polos.h
Normal file
59
src/polos.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define kLpt1 0x10
|
||||||
|
#define kUntilIdle 1
|
||||||
|
#define kOnce 0
|
||||||
|
|
||||||
|
int runcomms(char until_idle);
|
||||||
|
|
||||||
|
int putchar(int);
|
||||||
|
int puts(const char*);
|
||||||
|
|
||||||
|
inline static uint8_t __get_far_u8__(uint16_t addr, uint16_t seg) {
|
||||||
|
register uint16_t ad asm("di") = addr;
|
||||||
|
register uint8_t ret asm("al");
|
||||||
|
asm("mov %2, %%es \t\n"
|
||||||
|
"movb %%es:(%%di), %%al \t\n"
|
||||||
|
: "=r"(ret)
|
||||||
|
: "r"(ad), "r"(seg)
|
||||||
|
: "es");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void __set_far_u8__(uint16_t addr, uint16_t seg, uint8_t val) {
|
||||||
|
register uint16_t ad asm("di") = addr;
|
||||||
|
register uint8_t v asm("al") = val;
|
||||||
|
asm("mov %0, %%es \t\n"
|
||||||
|
"stosb \n\t" ::"r"(seg), "r"(ad), "r"(v) : "es", "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static uint16_t __readwritesector__(uint16_t cmd, uint8_t* addr, uint8_t c, uint8_t h, uint8_t s) {
|
||||||
|
register uint8_t* dest asm("bx") = addr;
|
||||||
|
register uint8_t sect asm("cl") = s;
|
||||||
|
register uint8_t cyl asm("ch") = c;
|
||||||
|
register uint8_t head asm("dh") = h;
|
||||||
|
register uint8_t drive asm("dl") = 0;
|
||||||
|
register uint16_t ret asm("ax");
|
||||||
|
|
||||||
|
asm volatile("xor %%ax, %%ax \n\t"
|
||||||
|
"mov %%ax, %%es \n\t"
|
||||||
|
"mov %1, %%ax \n\t"
|
||||||
|
"int $0x13"
|
||||||
|
: "=r"(ret)
|
||||||
|
: "ir"(cmd), "r"(dest), "r"(sect), "r"(cyl),
|
||||||
|
"r"(head), "r"(drive)
|
||||||
|
);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static uint16_t __readsector__(uint8_t* addr, uint8_t c, uint8_t h, uint8_t s) {
|
||||||
|
return __readwritesector__(0x0201, addr, c, h, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static uint16_t __writesector__(uint8_t* addr, uint8_t c, uint8_t h, uint8_t s) {
|
||||||
|
return __readwritesector__(0x0301, addr, c, h, s);
|
||||||
|
}
|
||||||
|
|
||||||
10
src/readio.asm
Normal file
10
src/readio.asm
Normal 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
|
||||||
10
src/runcomm.asm
Normal file
10
src/runcomm.asm
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
CPU 8086
|
||||||
|
|
||||||
|
mov cx, 0x100
|
||||||
|
a:
|
||||||
|
mov ah, 0x07
|
||||||
|
int 0x80
|
||||||
|
test al, al
|
||||||
|
loopnz a
|
||||||
|
b:
|
||||||
|
ret
|
||||||
100
src/stdlib.c
Normal file
100
src/stdlib.c
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "polos.h"
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memcpy(void* dest, const void* src, size_t n) {
|
||||||
|
uint8_t* d = dest;
|
||||||
|
const uint8_t* s = src;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
d[i] = s[i];
|
||||||
|
}
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memset(void* ptr, int val, size_t len) {
|
||||||
|
uint8_t* p = ptr;
|
||||||
|
while (len--) {
|
||||||
|
*p++ = val;
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strncmp(const char* s1, const char* s2, size_t len) {
|
||||||
|
for (int i = 0; i < len && s1[i]; i++) {
|
||||||
|
if (s1[i] != s2[i]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// XXX: really we should return 0 only if *s1 == *s2 here too
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int int80h_2(uint8_t fun, uint16_t arg1, uint16_t arg2) {
|
||||||
|
register uint16_t a1 asm("dx") = arg1;
|
||||||
|
register uint16_t a2 asm("cx") = arg2;
|
||||||
|
register uint8_t ah asm("ah") = fun;
|
||||||
|
register uint16_t ret asm("ax");
|
||||||
|
asm volatile("push %3 \n\t"
|
||||||
|
"push %2 \n\t"
|
||||||
|
"int $0x80 \n\t"
|
||||||
|
"pop %%cx \n\t"
|
||||||
|
"pop %%cx \n\t"
|
||||||
|
: "=r"(ret)
|
||||||
|
: "r"(ah), "r"(a1), "r"(a2)
|
||||||
|
: "bx", "cc", "memory");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int runcomms(char until_idle) {
|
||||||
|
register int ret asm("ax");
|
||||||
|
register char ui asm("al") = until_idle;
|
||||||
|
asm volatile("mov $0x07, %%ah \n\t"
|
||||||
|
"int $0x80 \n\t"
|
||||||
|
: "=r"(ret)
|
||||||
|
: "r"(ui)
|
||||||
|
: "bx", "cx", "dx", "cc", "memory");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read(int fd, void* buf, size_t size) {
|
||||||
|
if (fd != kLpt1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return int80h_2(0x05, (uint16_t)buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int write(int fd, void* buf, size_t size) {
|
||||||
|
if (fd != kLpt1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return int80h_2(0x06, (uint16_t)buf, size);
|
||||||
|
}
|
||||||
13
src/trampoline.asm
Normal file
13
src/trampoline.asm
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
CPU 8086
|
||||||
|
|
||||||
|
src equ 0xf000
|
||||||
|
dest equ 0x0600
|
||||||
|
size equ 0x0600
|
||||||
|
|
||||||
|
_start:
|
||||||
|
mov cx, size
|
||||||
|
mov si, src
|
||||||
|
mov di, dest
|
||||||
|
rep movsb
|
||||||
|
mov ax, dest
|
||||||
|
jmp ax
|
||||||
12
src/writeio.asm
Normal file
12
src/writeio.asm
Normal 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
|
||||||
Reference in New Issue
Block a user