#include #include #ifndef WOZMON #define WOZMON 0 #endif #if WOZMON #define BACKSPACE 0 #define ASCIIDUMP 0 #define CLEARSCREENCMD 0 #define INT80H 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 INT80H #define INT80H 1 #endif #ifndef SHOWTITLE #define SHOWTITLE 1 #endif #ifndef BOOTSTRAP #define BOOTSTRAP 1 #endif namespace { #if WOZMON int getchar() { register char c asm("al"); asm volatile("movb $0x00, %%ah\n\t" "int $0x16" : "=r"(c)::"ah", "cc"); return c; } void putchar(int c) { asm volatile("push %%bp \n\t" "mov %0, %%ax \n\t" "movb $0x0e, %%ah \n\t" "movb $0, %%bh \n\t" "int $0x10 \n\t" "pop %%bp \n\t" ::"r"(c) : "ax", "bh", "cc"); } #endif // WOZMON constexpr int kDumpSize = 16; // arguments on the stack in reverse order extern "C" uint16_t Int80h(uint8_t fun, int nargs); asm(".section .text.Int80h \n" "Int80h: \n" " push %si \n" " push %bp \n" " mov %al, %ah \n" " mov %sp, %bp \n" " lea 6(%bp), %si \n" // 4 for pushed stuff, 2 for return addr "0: \n" " test %dx, %dx \n" " jz 1f \n" " push (%si) \n" " dec %dx \n" " add $2, %si \n" " jmp 0b \n" "1: \n" " int $0x80 \n" " mov %bp, %sp \n" " pop %bp \n" " pop %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[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) { 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); } 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); } #if ASCIIDUMP 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('.'); } } #endif } void DumpHex(uint16_t addr, uint16_t seg) { addr &= -kDumpSize; putchar('['); WriteUint16(seg); putchar(':'); WriteUint16(addr); putchar(']'); putchar(':'); Dump(addr, seg, kDumpSize); putchar('\r'); putchar('\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"); } 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 == 'j') { dump = false; ptr++; auto jump = reinterpret_cast(cur_addr); uint16_t ret = jump(); WriteUint16(ret); putchar('\r'); putchar('\n'); #if INT80H } else if (*ptr == 'i') { dump = false; int nargs = 0; ptr++; int fun = ReadUint8(ptr); ptr += 2; for (; *ptr;) { if (*ptr == ' ') { ptr++; continue; } uint16_t d = ReadUint16(ptr); asm volatile("push %0" ::"r"(d)); nargs++; ptr += 4; } uint16_t ret = Int80h(fun, nargs); asm volatile("shl %0 \n\t" "add %0, %%sp" ::"r"(nargs)); WriteUint16(ret); putchar('\r'); putchar('\n'); #endif // INT80H #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_far_u8__(addr, cur_seg, d); addr++; ptr += 2; } } else { cur_addr = ReadUint16(ptr); ptr += 4; } } return dump; } void DisplayPrompt() { putchar('>'); putchar(' '); } 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 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(); #if BACKSPACE } else if (c == kBackspace || c == kOtherBackspace) { inptr--; if (inptr < inbuf) { inptr = inbuf; putchar(' '); continue; } putchar(' '); putchar(kOtherBackspace); #endif } else { *inptr = c; inptr++; } } } } // namespace int main() { polmon(); } #if BOOTSTRAP __attribute__((section(".init"), noreturn, used)) void _start() { main(); __builtin_unreachable(); } #endif // BOOTSTRAP