--- /dev/null
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if ALT_BACKEND
+#include "virtualxt/lib/vxt/cpu.h"
+#else
+#include "cpu_8086.h"
+#endif
+
+#define REG_TRACE 1
+#define MEM_TRACE 1
+#define IO_TRACE 1
+
+#define MEM_SIZE 0x100000
+uint8_t mem[MEM_SIZE];
+
+// read and write are separated for I/O
+#define IO_SIZE 0x100
+uint8_t io_read[IO_SIZE];
+uint8_t io_write[IO_SIZE];
+
+int load_ihx(char *name) {
+ FILE *fp = fopen(name, "r");
+ if (fp == NULL) {
+ perror(name);
+ exit(EXIT_FAILURE);
+ }
+
+ int base = 0, entry_point = 0;
+ bool had_eof = false;
+ char line[0x100];
+ while (fgets(line, 0x100, fp)) {
+ for (char *p = line; *p; ++p)
+ if (*p == '\n') {
+ *p = 0;
+ break;
+ }
+
+ if (had_eof) {
+ fprintf(stderr, "garbage after EOF record: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+
+ if (line[0] != ':') {
+ fprintf(stderr, "require colon: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t buf[0x7f];
+ int len;
+ for (len = 0; len < 0x7f; ++len) {
+ char *p = line + 1 + len * 2;
+ if (*p == 0)
+ break;
+ if (*p == '\n') {
+ *p = 0;
+ break;
+ }
+ uint8_t c = p[2];
+ p[2] = 0;
+
+ char *q;
+ buf[len] = (uint8_t)strtol(p, &q, 16);
+ p[2] = c;
+ if (q != p + 2) {
+ fprintf(stderr, "not hex byte: %s\n", p);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (len == 0) {
+ fprintf(stderr, "empty line: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+
+ uint8_t checksum = 0;
+ for (int i = 0; i < len; ++i)
+ checksum += buf[i];
+ if (checksum) {
+ checksum -= buf[len - 1];
+ fprintf(
+ stderr,
+ "checksum %02x, should be %02x\n",
+ checksum,
+ buf[len - 1]
+ );
+ exit(EXIT_FAILURE);
+ }
+
+ len -= 5;
+ if (len != buf[0]) {
+ fprintf(stderr, "incorrect length: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+
+ int addr = (buf[1] << 8) | buf[2], end_addr;
+ switch (buf[3]) {
+ case 0:
+ addr += base;
+ end_addr = addr + len;
+ if (end_addr < addr || end_addr > MEM_SIZE) {
+ fprintf(stderr, "invalid load range: [0x%x, 0x%x)\n", addr, end_addr);
+ exit(EXIT_FAILURE);
+ }
+ memcpy(mem + addr, buf + 4, len);
+ break;
+ case 1:
+ had_eof = true;
+ break;
+ case 3:
+ if (len < 4) {
+ fprintf(stderr, "invalid start address record: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+ entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ break;
+ case 4:
+ if (len < 2) {
+ fprintf(stderr, "invalid extended linear address record: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+ base = (buf[4] << 24) | (buf[5] << 16);
+ break;
+#if 0
+ case 5:
+ if (len < 4) {
+ fprintf(stderr, "invalid start linear address record: %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+ entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ break;
+#endif
+ default:
+ fprintf(stderr, "unknown record type: 0x%x\n", buf[3]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (!had_eof) {
+ fprintf(stderr, "no EOF record\n");
+ exit(EXIT_FAILURE);
+ }
+
+ fclose(fp);
+ return entry_point;
+}
+
+int msdos(int ax, int dx, int ds, bool *zf) {
+ switch (ax >> 8) {
+ case 2:
+ // Character output
+ dx &= 0xff;
+ switch (dx) {
+ case '\n':
+ break;
+ case '\r':
+ dx = '\n';
+ default:
+ putchar(dx);
+ break;
+ }
+ break;
+ case 6:
+ // Direct console I/O
+ dx &= 0xff;
+ if (dx == 0xff) {
+ int data = getchar();
+ switch (data) {
+ case '\n':
+ data = '\r';
+ goto avail;
+ case 0x7f:
+ data = '\b';
+ //goto avail;
+ default:
+ avail:
+ ax = data | 0x600;
+ *zf = true;
+ break;
+ case EOF:
+ *zf = false;
+ break;
+ }
+ break;
+ }
+ switch (dx) {
+ case '\n':
+ break;
+ case '\r':
+ dx = '\n';
+ default:
+ putchar(dx);
+ break;
+ }
+ break;
+ case 9:
+ // Display string
+ for (
+ int data;
+ (data = mem[dx + (ds << 4)]) != '$';
+ dx = (dx + 1) & 0xffff
+ )
+ switch (data) {
+ case '\n':
+ break;
+ case '\r':
+ data = '\n';
+ default:
+ putchar(data);
+ break;
+ }
+ break;
+ case 0x26:
+ // Create PSP -- ignore
+ break;
+ case 0x30:
+ // Get DOS version
+ ax = 0x211;
+ break;
+ case 0x33:
+ // Get or set Ctrl-Break -- ignore
+ break;
+ case 0x25:
+ // Set interrupt vector -- ignore
+ break;
+ case 0x35:
+ // Get interrupt vector -- ignore
+ break;
+ default:
+ fprintf(stderr, "unimplemented MSDOS call 0x%02x\n", ax >> 8);
+ exit(EXIT_FAILURE);
+ }
+ return ax;
+}
+
+int read_byte(void *context, int addr) {
+#if MEM_TRACE
+ int data = mem[addr];
+ fprintf(stderr, "addr=%05x rd=%02x\n", addr, data);
+ return data;
+#else
+ return mem[addr];
+#endif
+}
+
+void write_byte(void *context, int addr, int data) {
+#if MEM_TRACE
+ fprintf(stderr, "addr=%05x wr=%02x\n", addr, data);
+#endif
+ mem[addr] = data;
+}
+
+int in_byte(void *context, int addr) {
+ addr &= 0xff;
+#if IO_TRACE
+ int data = io_read[addr];
+ printf("io=%02x rd=%02x\n", addr, data);
+ return data;
+#else
+ return io_read[addr];
+#endif
+}
+
+void out_byte(void *context, int addr, int data) {
+ addr &= 0xff;
+#if IO_TRACE
+ printf("io=%02x wr=%02x\n", addr, data);
+#endif
+ io_write[addr] = data;
+}
+
+#if ALT_BACKEND
+#if defined(VXT_LIBC) && !defined(VXT_NO_LOGGING)
+#include <stdarg.h>
+static int libc_print(const char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int ret = vprintf(fmt, args);
+ va_end(args);
+ return ret;
+}
+int (*logger)(const char*, ...) = &libc_print;
+#else
+static int no_print(const char *fmt, ...) {
+ UNUSED(fmt);
+ return -1;
+}
+int (*logger)(const char*, ...) = &no_print;
+#endif
+
+static void no_breakpoint(void) {}
+void (*breakpoint)(void) = &no_breakpoint;
+
+vxt_byte vxt_system_read_byte(CONSTP(vxt_system) s, vxt_pointer addr) {
+ addr &= 0xFFFFF;
+ return read_byte(NULL, addr);
+}
+
+void vxt_system_write_byte(CONSTP(vxt_system) s, vxt_pointer addr, vxt_byte data) {
+ addr &= 0xFFFFF;
+ write_byte(NULL, addr, data);
+}
+
+vxt_word vxt_system_read_word(CONSTP(vxt_system) s, vxt_pointer addr) {
+ int data = vxt_system_read_byte(s, addr);
+ return WORD(vxt_system_read_byte(s, addr + 1), data);
+}
+
+void vxt_system_write_word(CONSTP(vxt_system) s, vxt_pointer addr, vxt_word data) {
+ vxt_system_write_byte(s, addr, LBYTE(data));
+ vxt_system_write_byte(s, addr + 1, HBYTE(data));
+}
+
+vxt_byte system_in(CONSTP(vxt_system) s, vxt_word port) {
+ return in_byte(NULL, port);
+}
+
+void system_out(CONSTP(vxt_system) s, vxt_word port, vxt_byte data) {
+ out_byte(NULL, port, data);
+}
+#endif
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ printf("usage: %s image.ihx\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ int entry_point = load_ihx(argv[1]);
+
+ // int 0x20 vector
+ mem[0x80] = 0;
+ mem[0x81] = 0;
+ mem[0x82] = 0x40;
+ mem[0x83] = 0;
+ mem[0x400] = 0xcf; // iret, for msdos emulation
+
+ // int 0x21 vector
+ mem[0x84] = 1;
+ mem[0x85] = 0;
+ mem[0x86] = 0x40;
+ mem[0x87] = 0;
+ mem[0x401] = 0xcf; // iret, for msdos emulation
+
+ // psp
+ int code_segment = (entry_point >> 16) & 0xffff;
+ mem[5 + (code_segment << 4)] = 0xc3; // ret, for bdos emulation
+ mem[6 + (code_segment << 4)] = 0xfe; // memory top, lo byte
+ mem[7 + (code_segment << 4)] = 0xff; // memory top, hi byte
+ mem[0x80 + (code_segment << 4)] = 0; // command line length
+ mem[0x81 + (code_segment << 4)] = '\r'; // command line sentinel
+
+#if ALT_BACKEND
+ struct cpu cpu;
+ memset(&cpu, 0, sizeof(struct cpu));
+ cpu_reset(&cpu);
+ //cpu.read_byte = rb;
+ //cpu.write_byte = wb;
+ //cpu.port_in = in;
+ //cpu.port_out = out;
+ cpu.regs.ip = entry_point & 0xffff;
+ cpu.regs.cs = code_segment;
+ cpu.regs.sp = 0xfffe;
+ cpu.regs.ss = code_segment;
+
+ while (true) {
+#if REG_TRACE
+ fprintf(
+ stderr,
+ "ip=%04x ax=%04x cx=%04x dx=%04x bx=%04x sp=%04x bp=%04x si=%04x di=%04x cs=%04x ds=%04x es=%04x ss=%04x flags=%04x cf=%d pf=%d af=%d zf=%d sf=%d tf=%d if=%d df=%d of=%d\n",
+ cpu.regs.ip,
+ cpu.regs.ax,
+ cpu.regs.cx,
+ cpu.regs.dx,
+ cpu.regs.bx,
+ cpu.regs.sp,
+ cpu.regs.bp,
+ cpu.regs.si,
+ cpu.regs.di,
+ cpu.regs.cs,
+ cpu.regs.ds,
+ cpu.regs.es,
+ cpu.regs.ss,
+ cpu.regs.flags,
+ cpu.regs.flags & 1,
+ (cpu.regs.flags >> 2) & 1,
+ (cpu.regs.flags >> 4) & 1,
+ (cpu.regs.flags >> 6) & 1,
+ (cpu.regs.flags >> 7) & 1,
+ (cpu.regs.flags >> 8) & 1,
+ (cpu.regs.flags >> 9) & 1,
+ (cpu.regs.flags >> 10) & 1,
+ (cpu.regs.flags >> 11) & 1
+ );
+#endif
+
+ if (
+ (cpu.regs.ip == 0 && cpu.regs.cs == code_segment) ||
+ (cpu.regs.ip == 0 && cpu.regs.cs == 0x40)
+ ) {
+ putchar('\n');
+ break;
+ }
+ if (cpu.regs.ip == 5 && cpu.regs.cs == code_segment) {
+ bool zf = (cpu.regs.flags >> 6) & 1;
+ cpu.regs.ax = msdos(cpu.regs.cl << 8, cpu.regs.dx, cpu.regs.ds, &zf);
+ cpu.regs.flags = (zf << 6) | (cpu.regs.flags & ~0x40);
+ }
+ else if (cpu.regs.ip == 1 && cpu.regs.cs == 0x40) {
+ bool zf = (cpu.regs.flags >> 6) & 1;
+ cpu.regs.ax = msdos(cpu.regs.ax, cpu.regs.dx, cpu.regs.ds, &zf);
+ cpu.regs.flags = (zf << 6) | (cpu.regs.flags & ~0x40);
+ }
+ cpu_step(&cpu);
+ }
+#else
+ struct cpu_8086 cpu;
+ cpu_8086_init(
+ &cpu,
+ read_byte,
+ NULL,
+ write_byte,
+ NULL,
+ in_byte,
+ NULL,
+ out_byte,
+ NULL
+ );
+ cpu_8086_reset(&cpu);
+ cpu.regs.word.pc = entry_point;
+
+ while (true) {
+#if REG_TRACE
+ fprintf(
+ stderr,
+ "pc=%04x af=%04x bc=%04x de=%04x hl=%04x sp=%04x ix=%04x iy=%04x cf=%d nf=%d pvf=%d hf=%d zf=%d sf=%d\n",
+ cpu.regs.word.pc,
+ cpu.regs.word.af,
+ cpu.regs.word.bc,
+ cpu.regs.word.de,
+ cpu.regs.word.hl,
+ cpu.regs.word.sp,
+ cpu.regs.word.ix,
+ cpu.regs.word.iy,
+ cpu.regs.bit.cf,
+ cpu.regs.bit.nf,
+ cpu.regs.bit.pvf,
+ cpu.regs.bit.hf,
+ cpu.regs.bit.zf,
+ cpu.regs.bit.sf
+ );
+#endif
+
+ if (cpu.regs.word.pc == 0) {
+ putchar('\n');
+ break;
+ }
+ if (cpu.regs.word.pc == 5) {
+ cpu.regs.word.de = bdos(
+ cpu.regs.byte.c,
+ cpu.regs.word.de
+ );
+ }
+ cpu_8086_execute(&cpu);
+ }
+#endif
+
+ return 0;
+}
--- /dev/null
+10 PRINT "HELLO"
+20 REM P9=X9^Y9 === GOSUB 60030
+30 X9=.3:Y9=.7:GOSUB 60030:PRINT .3^.7,P9
+40 REM L9=LOG(X9) === GOSUB 60090
+50 X9=.4:GOSUB 60090:PRINT LOG(.4),L9
+60 REM E9=EXP(X9) === GOSUB 60160
+70 X9=.5:GOSUB 60160:PRINT EXP(.5),E9
+80 REM C9=COS(X9) === GOSUB 60240
+90 X9=.6:GOSUB 60240:PRINT COS(.6),C9
+100 REM T9=TAN(X9) === GOSUB 60280
+110 X9=.7:GOSUB 60280:PRINT TAN(.7),T9
+120 REM A9=ATN(X9) === GOSUB 60310
+130 X9=.8:GOSUB 60310:PRINT ATN(.8),A9
+140 END
+60000 REM EXPONENTIATION: P9=X9^Y9
+60010 REM NEED: EXP, LOG
+60020 REM VARIABLES USED: A9,B9,C9,E9,L9,P9,X9,Y9
+60030 P9=1 : E9=0 : IF Y9=0 THEN RETURN
+60040 IF X9<0 THEN IF INT(Y9)=Y9 THEN P9=1-2*Y9+4*INT(Y9/2) : X9=-X9
+60050 IF X9<>0 THEN GOSUB 60090 : X9=Y9*L9 : GOSUB 60160
+60060 P9=P9*E9 : RETURN
+60070 REM NATURAL LOGARITHM: L9=LOG(X9)
+60080 REM VARIABLES USED: A9,B9,C9,E9,L9,X9
+60090 E9=0 : IF X9<=0 THEN PRINT "LOG FC ERROR"; : STOP
+60095 A9=1 : B9=2 : C9=.5 : REM THIS WILL SPEED UP THE FOLLOWING
+60100 IF X9>=A9 THEN X9=C9*X9 : E9=E9+A9 : GOTO 60100
+60110 IF X9<C9 THEN X9=B9*X9 : E9=E9-A9 : GOTO 60110
+60120 X9=(X9-.707107)/(X9+.707107) : L9=X9*X9
+60130 L9=(((.598979*L9+.961471)*L9+2.88539)*X9+E9-.5)*.693147
+60135 RETURN
+60140 REM EXPONENTIAL: E9=EXP(X9)
+60150 REM VARIABLES USED: A9,E9,L9,X9
+60160 L9=INT(1.4427*X9)+1 : IF L9<1E7 THEN 60180
+60170 IF X9>0 THEN PRINT "EXP OV ERROR"; : STOP
+60175 E9=0 : RETURN
+60180 E9=.693147*L9-X9 : A9=1.32988E-3-1.41316E-4*E9
+60190 A9=((A9*E9-8.30136E-3)*E9+4.16574E-2)*E9
+60195 E9=(((A9-.166665)*E9+.5)*E9-1)*E9+1 : A9=2
+60197 IF L9<=0 THEN A9=.5 : L9=-L9 : IF L9=0 THEN RETURN
+60200 FOR X9=1 TO L9 : E9=A9*E9 : NEXT X9 : RETURN
+60210 REM COSINE: C9=COS(X9)
+60220 REM N.B. SIN MUST BE RETAINED AT LOAD-TIME
+60230 REM VARIABLES USED: C9,X9
+60240 C9=SIN(X9+1.5708) : RETURN
+60250 REM TANGENT: T9=TAN(X9)
+60260 REM NEEDS COS. (SIN NUST BE RETAINED AT LOAD-TIME)
+60270 REM VARIABLES USED: C9,T9,X9
+60280 GOSUB 60240 : T9=SIN(X9)/C9 : RETURN
+60290 REM ARCTANGENT: A9=ATN(X9)
+60300 REM VARIABLES USED: A9,B9,C9,T9,X9
+60310 T9=SGN(X9): X9=ABS(X9): C9=0 : IF X9>1 THEN C9=1 : X9=1/X9
+60320 A9=X9*X9 : B9=((2.86623E-3*A9-1.61657E-2)*A9+4.29096E-2)*A9
+60330 B9=((((B9-7.5289E-2)*A9+.106563)*A9-.142089)*A9+.199936)*A9
+60340 A9=((B9-.333332)*A9+1)*X9 : IF C9=1 THEN A9=1.5708-A9
+60350 A9=T9*A9 : RETURN
+RUN
+PRINT "DONE"
+SYSTEM