350 lines
8.2 KiB
C++
350 lines
8.2 KiB
C++
#include <cstdint>
|
|
#include <cstdio>
|
|
|
|
#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<uint16_t (*)()>(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
|