#include #include #include 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(kHeaderAddress); memcpy(ptr, kTrampoline, sizeof(kTrampoline)); auto* argp = reinterpret_cast(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(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(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(); }