Files
5150/src/polmon.cc

410 lines
11 KiB
C++

#include <cstdint>
#include <cstdio>
#include <cstring>
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");
}
uint8_t __get_far_u8__(uint16_t addr, uint16_t seg) {
register uint16_t ad asm("si") = addr;
register uint16_t sg asm("ds") = seg;
register uint8_t ret asm("al");
asm("lodsb \n\t" : "=r"(ret) : "r"(ad), "r"(sg));
return ret;
}
void __set_far_u8__(uint16_t addr, uint16_t seg, uint8_t val) {
register uint16_t ad asm("di") = addr;
register uint16_t sg asm("es") = seg;
register uint8_t v asm("al") = val;
asm("stosb \n\t" ::"r"(sg), "r"(ad), "r"(v) : "memory");
}
__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");
}
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 %1 \n\t"
"movb $0x02, %%ah \n\t"
"movw %%di, %%dx \n\t"
"int $0x10" ::"rm"(status),
"l"(WriteUint8)
: "ax", "bh", "dx", "cx", "di", "cc");
}
char toupper(char c) {
if (c >= 'a' && c <= 'z') {
return c - ('a' - 'A');
}
return c;
}
void tofatname(const char* name, char* fatname) {
for (int i = 0; i < 11; i++) {
if (*name == 0 || *name == '.') {
fatname[i] = ' ';
} else {
fatname[i] = toupper(*name++);
}
if (i == 7 && *name == '.') {
name++;
}
}
}
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 LaunchFile(const char* name, uint8_t argc, uint16_t* argv) {
constexpr uint16_t kSegment = 0x200;
constexpr uint16_t kAddress = 0x100;
constexpr uint16_t kHeaderAddress = (kSegment << 4);
constexpr uint16_t kFlatAddress = (kSegment << 4) + kAddress;
constexpr uint16_t kArgvAddress = 0x0004;
// trampoline:
// call 0x100
// retf
// XXX: if you change kAddress, the trampoline needs to be updated
constexpr uint8_t kTrampoline[] = { 0xe8, 0xfd, 0x00, 0xcb };
char fatname[11];
tofatname(name, fatname);
uint16_t r = __readfile__(fatname, kFlatAddress);
if (r) {
return r;
}
auto* ptr = reinterpret_cast<uint8_t*>(kHeaderAddress);
memcpy(ptr, kTrampoline, sizeof(kTrampoline));
auto* argp = reinterpret_cast<uint16_t*>(kHeaderAddress + 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"
"lcall %2,$0 \n\t"
"xor %%cx, %%cx \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)
: "i"(kSegment), "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, %ds \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;
}
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;
ptr += 1;
cur_seg = ReadUint16(ptr);
} 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;
uint16_t args[8]; // ought to be enough for everybody
uint8_t nargs = 0;
ptr++;
int fun = ReadUint16(ptr);
for (; *ptr && nargs < 8;) {
if (*ptr == ' ') {
ptr++;
continue;
}
args[nargs++] = ReadUint16(ptr);
}
uint16_t ret = Int80h(fun, nargs, args);
WriteUint16ln(ret);
} else if (*ptr == 'l') {
dump = false;
ClearScreen();
ptr++;
} else if (*ptr == 'r') {
dump = false;
uint16_t args[8]; // ought to be enough for everybody
uint8_t nargs = 0;
ptr++;
while (*ptr == ' ') {
ptr++;
}
const char* fname = ptr;
for (; *ptr && *ptr != ' '; ptr++) {
}
for (; *ptr && nargs < 8;) {
if (*ptr == ' ') {
ptr++;
continue;
}
args[nargs++] = ReadUint16(ptr);
}
uint16_t ret = LaunchFile(fname, 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 {
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();
}