#define MEM_TRACE 0
#define IO_TRACE 0
+#define BDOS 0xffbb // ret
+#define JMP_BOOT 0xffbc // -3: Cold start routine
+#define JMP_WBOOT 0xffbf // 0: Warm boot - reload command processor
+#define JMP_CONST 0xffc2 // 3: Console status
+#define JMP_CONIN 0xffc5 // 6: Console input
+#define JMP_CONOUT 0xffc8 // 9: Console output
+#define JMP_LIST 0xffcb // 12: Printer output
+#define JMP_PUNCH 0xffce // 15: Paper tape punch output
+#define JMP_READER 0xffd1 // 18: Paper tape reader input
+#define JMP_HOME 0xffd4 // 21: Move disc head to track 0
+#define JMP_SELDSK 0xffd7 // 24: Select disc drive
+#define JMP_SETTRK 0xffda // 27: Set track number
+#define JMP_SETSEC 0xffdd // 30: Set sector number
+#define JMP_SETDMA 0xffe0 // 33: Set DMA address
+#define JMP_READ 0xffe3 // 36: Read a sector
+#define JMP_WRITE 0xffe6 // 39: Write a sector
+#define JMP_LISTST 0xffe9 // 42: Status of list device
+#define JMP_SECTRAN 0xffec // 45: Sector translation for skewing
+#define BIOS_BOOT 0xffef // ret
+#define BIOS_WBOOT 0xfff0 // ret
+#define BIOS_CONST 0xfff1 // ret
+#define BIOS_CONIN 0xfff2 // ret
+#define BIOS_CONOUT 0xfff3 // ret
+#define BIOS_LIST 0xfff4 // ret
+#define BIOS_PUNCH 0xfff5 // ret
+#define BIOS_READER 0xfff6 // ret
+#define BIOS_HOME 0xfff7 // ret
+#define BIOS_SELDSK 0xfff8 // ret
+#define BIOS_SETTRK 0xfff9 // ret
+#define BIOS_SETSEC 0xfffa // ret
+#define BIOS_SETDMA 0xfffb // ret
+#define BIOS_READ 0xfffc // ret
+#define BIOS_WRITE 0xfffd // ret
+#define BIOS_LISTST 0xfffe // ret
+#define BIOS_SECTRAN 0xffff // ret
+
#define MEM_SIZE 0x10000
uint8_t mem[MEM_SIZE];
return entry_point;
}
-int bdos(int c, int de) {
+int bdos(int a, int c, int de, uint16_t *hl) {
switch (c) {
case 2:
+ // Character output
de &= 0xff;
- if (de != '\r')
+ switch (de) {
+ case '\n':
+ break;
+ case '\r':
+ de = '\n';
+ default:
putchar(de);
+ break;
+ }
break;
- case 9:
- while (mem[de] != '$') {
- if (mem[de] != '\r')
- putchar(mem[de]);
- de = (de + 1) & 0xffff;
+ case 6:
+ // Direct console I/O
+ de &= 0xff;
+ if (de == 0xff) {
+ int data = getchar();
+ switch (data) {
+ case '\n':
+ data = '\r';
+ goto avail;
+ case 0x7f:
+ data = '\b';
+ //goto avail;
+ default:
+ avail:
+ a = data;
+ break;
+ case EOF:
+ a = 0;
+ break;
+ }
+ break;
+ }
+ switch (de) {
+ case '\n':
+ break;
+ case '\r':
+ de = '\n';
+ default:
+ putchar(de);
+ break;
}
break;
+ case 9:
+ // Display string
+ for (int data; (data = mem[de]) != '$'; de = (de + 1) & 0xffff)
+ switch (data) {
+ case '\n':
+ break;
+ case '\r':
+ data = '\n';
+ default:
+ putchar(data);
+ break;
+ }
+ break;
+ case 0xc:
+ // Get CP/M version
+ *hl = 0x22;
+ break;
+ case 0x29: // DRV_ROVEC - Return bitmap of read-only drives
+ *hl = 0;
+ break;
default:
fprintf(stderr, "unimplemented BDOS call 0x%02x\n", c);
exit(EXIT_FAILURE);
}
- return de;
+ return a;
+}
+
+int bios(int pc, int a, int c) {
+ switch (pc) {
+ case BIOS_WBOOT:
+ exit(EXIT_SUCCESS);
+ case BIOS_CONST:
+ a = 0; // defeat BASIC Ctrl-C checking
+ break;
+ case BIOS_CONIN:
+ {
+ int data = getchar();
+ switch (data) {
+ case '\n':
+ data = '\r';
+ goto avail;
+ case 0x7f:
+ data = '\b';
+ //goto avail;
+ default:
+ avail:
+ a = data;
+ break;
+ case EOF:
+ a = 0;
+ break;
+ }
+ }
+ break;
+ case BIOS_CONOUT:
+ switch (c) {
+ case '\n':
+ break;
+ case '\r':
+ c = '\n';
+ default:
+ putchar(c);
+ break;
+ }
+ break;
+ default:
+ fprintf(stderr, "unimplemented BIOS call 0x%04x\n", pc);
+ exit(EXIT_FAILURE);
+ }
+ return a;
}
int read_byte(void *context, int addr) {
}
int entry_point = load_ihx(argv[1]);
- mem[5] = 0xc9; // ret, for bdos emulation
- mem[6] = 0xff; // memory top, lo byte
- mem[7] = 0xff; // memory top, hi byte
+ mem[0] = 0xc3; // jmp
+ mem[1] = (uint8_t)JMP_WBOOT;
+ mem[2] = (uint8_t)(JMP_WBOOT >> 8);
+
+ mem[5] = 0xc3; // jmp
+ mem[6] = (uint8_t)BDOS;
+ mem[7] = (uint8_t)(BDOS >> 8);
+
+ mem[BDOS] = 0xc9; // ret
+
+ mem[JMP_BOOT] = 0xc3; // jmp
+ mem[JMP_BOOT + 1] = (uint8_t)BIOS_BOOT;
+ mem[JMP_BOOT + 2] = (uint8_t)(BIOS_BOOT >> 8);
+
+ mem[JMP_WBOOT] = 0xc3; // jmp
+ mem[JMP_WBOOT + 1] = (uint8_t)BIOS_WBOOT;
+ mem[JMP_WBOOT + 2] = (uint8_t)(BIOS_WBOOT >> 8);
+
+ mem[JMP_CONST] = 0xc3; // jmp
+ mem[JMP_CONST + 1] = (uint8_t)BIOS_CONST;
+ mem[JMP_CONST + 2] = (uint8_t)(BIOS_CONST >> 8);
+
+ mem[JMP_CONIN] = 0xc3; // jmp
+ mem[JMP_CONIN + 1] = (uint8_t)BIOS_CONIN;
+ mem[JMP_CONIN + 2] = (uint8_t)(BIOS_CONIN >> 8);
+
+ mem[JMP_CONOUT] = 0xc3; // jmp
+ mem[JMP_CONOUT + 1] = (uint8_t)BIOS_CONOUT;
+ mem[JMP_CONOUT + 2] = (uint8_t)(BIOS_CONOUT >> 8);
+
+ mem[JMP_LIST] = 0xc3; // jmp
+ mem[JMP_LIST + 1] = (uint8_t)BIOS_LIST;
+ mem[JMP_LIST + 2] = (uint8_t)(BIOS_LIST >> 8);
+
+ mem[JMP_PUNCH] = 0xc3; // jmp
+ mem[JMP_PUNCH + 1] = (uint8_t)BIOS_PUNCH;
+ mem[JMP_PUNCH + 2] = (uint8_t)(BIOS_PUNCH >> 8);
+
+ mem[JMP_READER] = 0xc3; // jmp
+ mem[JMP_READER + 1] = (uint8_t)BIOS_READER;
+ mem[JMP_READER + 2] = (uint8_t)(BIOS_READER >> 8);
+
+ mem[JMP_HOME] = 0xc3; // jmp
+ mem[JMP_HOME + 1] = (uint8_t)BIOS_HOME;
+ mem[JMP_HOME + 2] = (uint8_t)(BIOS_HOME >> 8);
+
+ mem[JMP_SELDSK] = 0xc3; // jmp
+ mem[JMP_SELDSK + 1] = (uint8_t)BIOS_SELDSK;
+ mem[JMP_SELDSK + 2] = (uint8_t)(BIOS_SELDSK >> 8);
+
+ mem[JMP_SETTRK] = 0xc3; // jmp
+ mem[JMP_SETTRK + 1] = (uint8_t)BIOS_SETTRK;
+ mem[JMP_SETTRK + 2] = (uint8_t)(BIOS_SETTRK >> 8);
+
+ mem[JMP_SETSEC] = 0xc3; // jmp
+ mem[JMP_SETSEC + 1] = (uint8_t)BIOS_SETSEC;
+ mem[JMP_SETSEC + 2] = (uint8_t)(BIOS_SETSEC >> 8);
+
+ mem[JMP_SETDMA] = 0xc3; // jmp
+ mem[JMP_SETDMA + 1] = (uint8_t)BIOS_SETDMA;
+ mem[JMP_SETDMA + 2] = (uint8_t)(BIOS_SETDMA >> 8);
+
+ mem[JMP_READ] = 0xc3; // jmp
+ mem[JMP_READ + 1] = (uint8_t)BIOS_READ;
+ mem[JMP_READ + 2] = (uint8_t)(BIOS_READ >> 8);
+
+ mem[JMP_WRITE] = 0xc3; // jmp
+ mem[JMP_WRITE + 1] = (uint8_t)BIOS_WRITE;
+ mem[JMP_WRITE + 2] = (uint8_t)(BIOS_WRITE >> 8);
+
+ mem[JMP_LISTST] = 0xc3; // jmp
+ mem[JMP_LISTST + 1] = (uint8_t)BIOS_LISTST;
+ mem[JMP_LISTST + 2] = (uint8_t)(BIOS_LISTST >> 8);
+
+ mem[JMP_SECTRAN] = 0xc3; // jmp
+ mem[JMP_SECTRAN + 1] = (uint8_t)BIOS_SECTRAN;
+ mem[JMP_SECTRAN + 2] = (uint8_t)(BIOS_SECTRAN >> 8);
+
+ mem[BIOS_BOOT] = 0xc9; // ret
+ mem[BIOS_WBOOT] = 0xc9; // ret
+ mem[BIOS_CONST] = 0xc9; // ret
+ mem[BIOS_CONIN] = 0xc9; // ret
+ mem[BIOS_CONOUT] = 0xc9; // ret
+ mem[BIOS_LIST] = 0xc9; // ret
+ mem[BIOS_PUNCH] = 0xc9; // ret
+ mem[BIOS_READER] = 0xc9; // ret
+ mem[BIOS_HOME] = 0xc9; // ret
+ mem[BIOS_SELDSK] = 0xc9; // ret
+ mem[BIOS_SETTRK] = 0xc9; // ret
+ mem[BIOS_SETSEC] = 0xc9; // ret
+ mem[BIOS_SETDMA] = 0xc9; // ret
+ mem[BIOS_READ] = 0xc9; // ret
+ mem[BIOS_WRITE] = 0xc9; // ret
+ mem[BIOS_LISTST] = 0xc9; // ret
+ mem[BIOS_SECTRAN] = 0xc9; // ret
#if ALT_BACKEND
z80 cpu;
cpu.port_in = in;
cpu.port_out = out;
cpu.pc = entry_point;
+ cpu.sp = BDOS - 2; // assume word at BDOS - 2 is initialized to 0
while (true) {
#if REG_TRACE
);
#endif
- if (cpu.pc == 0) {
- putchar('\n');
- break;
- }
- if (cpu.pc == 5) {
+ if (cpu.pc == BDOS) {
int de = cpu.e | (cpu.d << 8);
- de = bdos(cpu.c, de);
- cpu.e = de & 0xff;
- cpu.d = de >> 8;
+ uint16_t hl = cpu.l | (cpu.h << 8);
+ cpu.a = bdos(cpu.a, cpu.c, de, &hl);
+ cpu.l = (uint8_t)hl;
+ cpu.h = (uint8_t)(hl >> 8);
}
+ else if (cpu.pc >= BIOS_BOOT)
+ cpu.a = bios(cpu.pc, cpu.a, cpu.c);
+
z80_step(&cpu, 1);
}
#else
);
cpu_z80_reset(&cpu);
cpu.regs.word.pc = entry_point;
+ cpu.regs.word.sp = BDOS - 2; // assume word at BDOS - 2 is initialized to 0
while (true) {
#if REG_TRACE
);
#endif
- if (cpu.regs.word.pc == 0) {
- putchar('\n');
- break;
- }
- if (cpu.regs.word.pc == 5) {
- cpu.regs.word.de = bdos(
+ if (cpu.regs.word.pc == BDOS)
+ cpu.regs.byte.a = bdos(
+ cpu.regs.byte.a,
cpu.regs.byte.c,
- cpu.regs.word.de
+ cpu.regs.word.de,
+ &cpu.regs.word.hl
);
- }
+ else if (cpu.regs.word.pc >= BIOS_BOOT)
+ cpu.regs.byte.a = bios(
+ cpu.regs.word.pc,
+ cpu.regs.byte.a,
+ cpu.regs.byte.c
+ );
+
cpu_z80_execute(&cpu);
}
#endif