changeset 820:cf6149b7c6e5

Implement a tiny bit of CPM BDOS and add a corresponding Z80 core driver so that simple CPM programs like ZEXDOC/ZEXALL can be run against my Z80 core
author Michael Pavone <pavone@retrodev.com>
date Wed, 29 Jul 2015 00:05:21 -0700
parents ab017fb09e77
children 21a69dfb6ee7
files Makefile blastcpm.c fake_cpm.sz8
diffstat 3 files changed, 220 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Wed Jul 29 00:03:36 2015 -0700
+++ b/Makefile	Wed Jul 29 00:05:21 2015 -0700
@@ -164,6 +164,9 @@
 vgmplay$(EXE) : vgmplay.o render_sdl.o $(CONFIGOBJS) $(AUDIOOBJS)
 	$(CC) -o $@ $^ $(LDFLAGS)
 	$(FIXUP) ./$@
+	
+blastcpm : blastcpm.o util.o $(Z80OBJS) $(TRANSOBJS)
+	$(CC) -o $@ $^
 
 test : test.o vdp.o
 	$(CC) -o test test.o vdp.o
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/blastcpm.c	Wed Jul 29 00:05:21 2015 -0700
@@ -0,0 +1,112 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/select.h>
+
+#include "z80_to_x86.h"
+#include "util.h"
+
+uint8_t ram[64 * 1024];
+
+#define START_OFF 0x100
+#define OS_START 0xE400
+#define OS_RESET 0xE403
+int headless = 1;
+
+void z80_next_int_pulse(z80_context * context)
+{
+	context->int_pulse_start = context->int_pulse_end = CYCLE_NEVER;
+}
+
+void render_errorbox(char *title, char *message)
+{
+}
+
+void render_infobox(char *title, char *message)
+{
+}
+
+void *console_write(uint32_t address, void *context, uint8_t value)
+{
+	putchar(value);
+	return context;
+}
+
+uint8_t console_read(uint32_t address, void *context)
+{
+	return getchar();
+}
+
+void *console_flush_write(uint32_t address, void *context, uint8_t value)
+{
+	fflush(stdout);
+	return context;
+}
+
+uint8_t console_status_read(uint32_t address, void *context)
+{
+	fd_set read_fds;
+	FD_ZERO(&read_fds);
+	struct timeval timeout;
+	timeout.tv_sec = 0;
+	timeout.tv_usec = 0;
+	FD_SET(fileno(stdin), &read_fds);
+	return select(fileno(stdin)+1, &read_fds, NULL, NULL, &timeout) > 0; 
+}
+
+void *exit_write(uint32_t address, void *context, uint8_t value)
+{
+	exit(0);
+	return context;
+}
+
+const memmap_chunk z80_map[] = {
+	{ 0x0000, 0x10000,  0xFFFF, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram, NULL, NULL, NULL, NULL},
+};
+
+const memmap_chunk io_map[] = {
+	{ 0x0, 0x1, 0xFFFF, 0, 0, NULL, NULL, NULL, console_read, console_write},
+	{ 0x1, 0x2, 0xFFFF, 0, 0, NULL, NULL, NULL, console_status_read, console_flush_write},
+	{ 0x2, 0x3, 0xFFFF, 0, 0, NULL, NULL, NULL, NULL, exit_write},
+};
+
+int main(int argc, char **argv)
+{
+	FILE *f = fopen("fake_cpm.bin", "rb");
+	long fsize = file_size(f);
+	if (fsize > sizeof(ram) - OS_START) {
+		fsize = sizeof(ram) - OS_START;
+	}
+	if (fread(ram + OS_START, 1, fsize, f) != fsize) {
+		fprintf(stderr, "Error reading from fake_cpm.bin\n");
+		exit(1);
+	}
+	f = fopen(argv[1], "rb");
+	fsize = file_size(f);
+	if (fsize > OS_START - START_OFF) {
+		fsize = OS_START - START_OFF;
+	}
+	if (fread(ram + START_OFF, 1, fsize, f) != fsize) {
+		fprintf(stderr, "Error reading from file %s\n", argv[1]);
+		exit(1);
+	}
+	fclose(f);
+	ram[0] = 0xC3;
+	ram[1] = OS_RESET & 0xFF;
+	ram[2] = OS_RESET >> 8;
+	ram[5] = 0xC3;
+	ram[6] = OS_START & 0xFF;
+	ram[7] = OS_START >> 8;
+	
+	z80_options opts;
+	z80_context context;
+	init_z80_opts(&opts, z80_map, 1, io_map, 3, 1);
+	init_z80_context(&context, &opts);
+	for(;;)
+	{
+		z80_run(&context, 1000000);
+		context.current_cycle = 0;
+	}
+	return 0;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fake_cpm.sz8	Wed Jul 29 00:05:21 2015 -0700
@@ -0,0 +1,105 @@
+CONSOLE_PORT equ 0
+STATUS_PORT  equ 1
+EXIT_PORT    equ 2
+	org $E400
+	jp handle_call
+	ld a, (should_exit)
+	dec a
+	jr z, do_exit
+	ld a, 1
+	ld (should_exit), a
+	jp $100
+do_exit:
+no_impl
+	out (EXIT_PORT), a
+should_exit:
+	dc.b 0
+	
+console_in:
+	in a, (CONSOLE_PORT)
+	ld l, a
+	ret
+console_out:
+	ld a, e
+	out (CONSOLE_PORT), a
+	ret
+get_iobyte:
+	ld a, (3)
+	ld l, a
+	ret
+set_iobyte:
+	ld a, e
+	ld (3), a
+	ret
+write_string:
+	ld c, '$'
+	jp .start
+.continue
+	out (CONSOLE_PORT), a
+	inc de
+.start
+	ld a, (de)
+	cp c
+	jr nz, .continue
+	;flush output
+	out (STATUS_PORT),a 
+	ret
+read_string:
+	ld a, (de)
+	ld c, a
+	ld b, $A ;newline
+	inc c
+	inc de
+	push de
+	inc de
+	jp .start
+.continue
+	in a, (CONSOLE_PORT)
+	cp b
+	jr z, .end
+	ld (de), a
+	inc de
+.start
+	dec c
+	jr nz, .continue
+	;todo: consume excess characters
+.end
+	pop hl
+	ex de, hl
+	sbc hl, de
+	ld a, l
+	ld (de), a
+	ret
+
+console_status:
+	in a, (STATUS_PORT)
+	ld l, a
+	ret
+
+handle_call:
+	ld a, c
+	or a
+	jr z, do_exit
+	dec a
+	jr z, console_in
+	dec a
+	jr z, console_out
+	dec a
+	jr z, no_impl ;aux reader input
+	dec a
+	jr z, no_impl ;aux punch output
+	dec a
+	jr z, no_impl ;printer output
+	dec a
+	jr z, no_impl ;direct console IO
+	dec a
+	jr z, get_iobyte
+	dec a
+	jr z, set_iobyte
+	dec a
+	jr z, write_string
+	dec a
+	jr z, read_string
+	dec a
+	jr z, console_status
+	jp no_impl
\ No newline at end of file