Merge from default.
This commit is contained in:
		
						commit
						b47d59aa4a
					
				
					 15 changed files with 2880 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -19,6 +19,7 @@ vars.plats = {
 | 
			
		|||
	"em22",
 | 
			
		||||
}
 | 
			
		||||
vars.plats_with_tests = {
 | 
			
		||||
	"cpm",
 | 
			
		||||
	"linux68k",
 | 
			
		||||
	"linux386",
 | 
			
		||||
	"linuxppc",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										116
									
								
								plat/cpm/emu/bdos.s
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								plat/cpm/emu/bdos.s
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,116 @@
 | 
			
		|||
#
 | 
			
		||||
.sect .text
 | 
			
		||||
.sect .rom
 | 
			
		||||
.sect .data
 | 
			
		||||
.sect .bss
 | 
			
		||||
 | 
			
		||||
.sect .text
 | 
			
		||||
 | 
			
		||||
bdos: ! BDOS entry point
 | 
			
		||||
	out 0xff
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
COLDSTART: ! system startup entry point --- this needs to be four bytes after FBASE.
 | 
			
		||||
	jmp boot	    ! 0: Cold start routine
 | 
			
		||||
bios:
 | 
			
		||||
	jmp wboot	! 1: Warm boot - reload command processor
 | 
			
		||||
	jmp const	! 2: Console status
 | 
			
		||||
	jmp conin	! 3: Console input
 | 
			
		||||
	jmp conout	! 4: Console output
 | 
			
		||||
	jmp list	    ! 5: Printer output
 | 
			
		||||
	jmp punch	! 6: Paper tape punch output
 | 
			
		||||
    jmp reader	! 7: Paper tape reader input
 | 
			
		||||
	jmp home	    ! 8: Move disc head to track 0
 | 
			
		||||
	jmp seldsk	! 9: Select disc drive
 | 
			
		||||
	jmp settrk	!10: Set track number
 | 
			
		||||
	jmp setsec	!11: Set sector number
 | 
			
		||||
	jmp setdma	!12: Set DMA address
 | 
			
		||||
	jmp read     !13: Read a sector
 | 
			
		||||
	jmp write	!14: Write a sector
 | 
			
		||||
 | 
			
		||||
boot:
 | 
			
		||||
	xra a
 | 
			
		||||
	sta 3 ! iobyte
 | 
			
		||||
	sta 4 ! drive
 | 
			
		||||
	! falls through
 | 
			
		||||
wboot:
 | 
			
		||||
	mvi a, 0xc3 ! jmp
 | 
			
		||||
	sta 0
 | 
			
		||||
	sta 5
 | 
			
		||||
 | 
			
		||||
	lxi h, bios
 | 
			
		||||
	shld 1
 | 
			
		||||
 | 
			
		||||
	lxi h, bdos
 | 
			
		||||
	shld 6
 | 
			
		||||
 | 
			
		||||
	lda 4 ! get the current drive/user
 | 
			
		||||
	mov c, a
 | 
			
		||||
	out 1
 | 
			
		||||
 | 
			
		||||
const:
 | 
			
		||||
	out 2
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
conin:
 | 
			
		||||
	out 3
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
conout:
 | 
			
		||||
	out 4
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
list:
 | 
			
		||||
	out 5
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
punch:
 | 
			
		||||
	out 6
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
reader:
 | 
			
		||||
	out 7
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
home:
 | 
			
		||||
	out 8
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
seldsk:
 | 
			
		||||
	out 9
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
settrk:
 | 
			
		||||
	out 10
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
setsec:
 | 
			
		||||
	out 11
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
setdma:
 | 
			
		||||
	out 12
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
read:
 | 
			
		||||
	out 13
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
write:
 | 
			
		||||
	out 14
 | 
			
		||||
	ora a
 | 
			
		||||
	ret
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										451
									
								
								plat/cpm/emu/biosbdos.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										451
									
								
								plat/cpm/emu/biosbdos.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,451 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <glob.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <poll.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include "intel_8080_emulator.h"
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
 | 
			
		||||
#define FBASE 0xff00
 | 
			
		||||
#define COLDSTART (FBASE + 4) /* see bdos.asm */
 | 
			
		||||
#define CBASE (FBASE - (7*1024))
 | 
			
		||||
 | 
			
		||||
static uint16_t dma;
 | 
			
		||||
static int exitcode = 0;
 | 
			
		||||
 | 
			
		||||
struct fcb
 | 
			
		||||
{
 | 
			
		||||
	cpm_filename_t filename; /* includes drive */
 | 
			
		||||
	uint8_t extent;
 | 
			
		||||
	uint8_t s1;
 | 
			
		||||
	uint8_t s2;
 | 
			
		||||
	uint8_t recordcount;
 | 
			
		||||
	uint8_t d[16];
 | 
			
		||||
	uint8_t currentrecord;
 | 
			
		||||
	uint8_t r[3];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bios_getchar(void);
 | 
			
		||||
 | 
			
		||||
static uint16_t get_de(void)
 | 
			
		||||
{
 | 
			
		||||
	return i8080_read_reg16(DE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t get_c(void)
 | 
			
		||||
{
 | 
			
		||||
	return i8080_read_reg8(C);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t get_d(void)
 | 
			
		||||
{
 | 
			
		||||
	return i8080_read_reg8(D);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t get_e(void)
 | 
			
		||||
{
 | 
			
		||||
	return i8080_read_reg8(E);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t get_a(void)
 | 
			
		||||
{
 | 
			
		||||
	return i8080_read_reg8(A);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_a(uint8_t a)
 | 
			
		||||
{
 | 
			
		||||
	i8080_write_reg8(A, a);
 | 
			
		||||
}
 | 
			
		||||
	
 | 
			
		||||
static void set_result(uint16_t result)
 | 
			
		||||
{
 | 
			
		||||
	i8080_write_reg16(HL, result);
 | 
			
		||||
 | 
			
		||||
	i8080_write_reg8(A, result);
 | 
			
		||||
	uint8_t f = i8080_read_reg8(FLAGS);
 | 
			
		||||
	f &= ~(1<<6) & ~(1<<7);
 | 
			
		||||
	if (!result)
 | 
			
		||||
		f |= 1<<6;
 | 
			
		||||
	if (result & 0x80)
 | 
			
		||||
		f |= 1<<7;
 | 
			
		||||
	i8080_write_reg8(FLAGS, f);
 | 
			
		||||
 | 
			
		||||
	i8080_write_reg8(B, result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bios_coldboot(void)
 | 
			
		||||
{
 | 
			
		||||
	memcpy(&ram[FBASE], bdos_data, bdos_len);
 | 
			
		||||
	i8080_write_reg16(PC, COLDSTART);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bios_warmboot(void)
 | 
			
		||||
{
 | 
			
		||||
	dma = 0x0080;
 | 
			
		||||
 | 
			
		||||
	if (!user_command_line[0])
 | 
			
		||||
		fatal("running the CCP isn't supported");
 | 
			
		||||
 | 
			
		||||
	static bool terminate_next_time = false;
 | 
			
		||||
	if (terminate_next_time)
 | 
			
		||||
		exit(exitcode);
 | 
			
		||||
	terminate_next_time = true;
 | 
			
		||||
 | 
			
		||||
	i8080_write_reg16(PC, 0x0100);
 | 
			
		||||
 | 
			
		||||
	/* Push the magic exit code onto the stack. */
 | 
			
		||||
	i8080_write_reg16(SP, FBASE-4);
 | 
			
		||||
	ram[FBASE-4] = (FBASE-2) & 0xFF;
 | 
			
		||||
	ram[FBASE-3] = (FBASE-2) >> 8;
 | 
			
		||||
	ram[FBASE-2] = 0xD3; // out (??), a
 | 
			
		||||
	ram[FBASE-1] = 0xFE; // exit emulator
 | 
			
		||||
 | 
			
		||||
	int fd = open(user_command_line[0], O_RDONLY);
 | 
			
		||||
	if (fd == -1)
 | 
			
		||||
			fatal("couldn't open program: %s", strerror(errno));
 | 
			
		||||
	read(fd, &ram[0x0100], 0xFE00);
 | 
			
		||||
	close(fd);
 | 
			
		||||
 | 
			
		||||
	int offset = 1;
 | 
			
		||||
	for (int word = 1; user_command_line[word]; word++)
 | 
			
		||||
	{
 | 
			
		||||
		if (word > 1)
 | 
			
		||||
		{
 | 
			
		||||
			ram[0x0080 + offset] = ' ';
 | 
			
		||||
			offset++;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const char* pin = user_command_line[word];
 | 
			
		||||
		while (*pin)
 | 
			
		||||
		{
 | 
			
		||||
			if (offset > 125)
 | 
			
		||||
				fatal("user command line too long");
 | 
			
		||||
			ram[0x0080 + offset] = toupper(*pin++);
 | 
			
		||||
			offset++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ram[0x0080] = offset;
 | 
			
		||||
	ram[0x0080+offset] = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bios_const(void)
 | 
			
		||||
{
 | 
			
		||||
	struct pollfd pollfd = { 0, POLLIN, 0 };
 | 
			
		||||
	poll(&pollfd, 1, 0);
 | 
			
		||||
	set_a((pollfd.revents & POLLIN) ? 0xff : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bios_getchar(void)
 | 
			
		||||
{
 | 
			
		||||
	char c = 0;
 | 
			
		||||
	(void) read(0, &c, 1);
 | 
			
		||||
	if (c == '\n')
 | 
			
		||||
		c = '\r';
 | 
			
		||||
	set_a(c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bios_putchar(void)
 | 
			
		||||
{
 | 
			
		||||
	char c = get_c();
 | 
			
		||||
	(void) write(1, &c, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bios_entry(uint8_t bios_call)
 | 
			
		||||
{
 | 
			
		||||
	switch (bios_call)
 | 
			
		||||
	{
 | 
			
		||||
		case 0: bios_coldboot();  return;
 | 
			
		||||
		case 1: bios_warmboot();  return;
 | 
			
		||||
		case 2: bios_const();     return; // const
 | 
			
		||||
		case 3: bios_getchar();   return; // conin
 | 
			
		||||
		case 4: bios_putchar();   return; // conout
 | 
			
		||||
 | 
			
		||||
		case 0xFE: exit(0); // magic emulator exit
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	showregs();
 | 
			
		||||
	fatal("unimplemented bios entry %d", bios_call);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_getchar(void)
 | 
			
		||||
{
 | 
			
		||||
	bios_getchar();
 | 
			
		||||
	set_result(get_a());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_putchar(void)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t c = get_e();
 | 
			
		||||
	(void) write(1, &c, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_consoleio(void)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t c = get_e();
 | 
			
		||||
	if (c == 0xff)
 | 
			
		||||
	{
 | 
			
		||||
		bios_const();
 | 
			
		||||
		if (get_a() == 0xff)
 | 
			
		||||
			bios_getchar();
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		bdos_putchar();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_printstring(void)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t de = get_de();
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t c = ram[de++];
 | 
			
		||||
		if (c == '$')
 | 
			
		||||
			break;
 | 
			
		||||
		(void) write(1, &c, 1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_consolestatus(void)
 | 
			
		||||
{
 | 
			
		||||
	bios_const();
 | 
			
		||||
	set_result(get_a());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdos_readline(void)
 | 
			
		||||
{
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
 | 
			
		||||
	uint16_t de = i8080_read_reg16(DE);
 | 
			
		||||
	uint8_t maxcount = ram[de+0];
 | 
			
		||||
	int count = read(0, &ram[de+2], maxcount);
 | 
			
		||||
	if ((count > 0) && (ram[de+2+count-1] == '\n'))
 | 
			
		||||
		count--;
 | 
			
		||||
	ram[de+1] = count;
 | 
			
		||||
	set_result(count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct fcb* fcb_at(uint16_t address)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* fcb = (struct fcb*) &ram[address];
 | 
			
		||||
 | 
			
		||||
	/* Autoselect the current drive. */
 | 
			
		||||
	if (fcb->filename.drive == 0)
 | 
			
		||||
		fcb->filename.drive = ram[4] + 1;
 | 
			
		||||
 | 
			
		||||
	return fcb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct fcb* find_fcb(void)
 | 
			
		||||
{
 | 
			
		||||
	return fcb_at(i8080_read_reg16(DE));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_current_record(struct fcb* fcb)
 | 
			
		||||
{
 | 
			
		||||
	return (fcb->extent * 128) + fcb->currentrecord;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_current_record(struct fcb* fcb, int record, int total)
 | 
			
		||||
{
 | 
			
		||||
	int extents = total / 128;
 | 
			
		||||
	fcb->extent = record / 128;
 | 
			
		||||
	if (fcb->extent < extents)
 | 
			
		||||
		fcb->recordcount = 128;
 | 
			
		||||
	else
 | 
			
		||||
		fcb->recordcount = total % 128;
 | 
			
		||||
	fcb->currentrecord = record % 128;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_resetdisk(void)
 | 
			
		||||
{
 | 
			
		||||
	dma = 0x0080;
 | 
			
		||||
	ram[4] = 0; /* select drive A */
 | 
			
		||||
	set_result(0xff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_selectdisk(void)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t e = get_e();
 | 
			
		||||
	ram[4] = e;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_getdisk(void)
 | 
			
		||||
{
 | 
			
		||||
	set_result(ram[4]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_openfile(void)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* fcb = find_fcb();
 | 
			
		||||
	struct file* f = file_open(&fcb->filename);
 | 
			
		||||
	if (f)
 | 
			
		||||
	{
 | 
			
		||||
		set_current_record(fcb, 0, file_getrecordcount(f));
 | 
			
		||||
		set_result(0);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		set_result(0xff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_makefile(void)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* fcb = find_fcb();
 | 
			
		||||
	struct file* f = file_create(&fcb->filename);
 | 
			
		||||
	if (f)
 | 
			
		||||
	{
 | 
			
		||||
		set_current_record(fcb, 0, 0);
 | 
			
		||||
		set_result(0);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		set_result(0xff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_closefile(void)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* fcb = find_fcb();
 | 
			
		||||
	struct file* f = file_open(&fcb->filename);
 | 
			
		||||
	if (file_getrecordcount(f) < 128)
 | 
			
		||||
		file_setrecordcount(f, fcb->recordcount);
 | 
			
		||||
	int result = file_close(&fcb->filename);
 | 
			
		||||
	set_result(result ? 0xff : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_renamefile(void)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* srcfcb = fcb_at(i8080_read_reg16(DE));
 | 
			
		||||
	struct fcb* destfcb = fcb_at(i8080_read_reg16(DE)+16);
 | 
			
		||||
	int result = file_rename(&srcfcb->filename, &destfcb->filename);
 | 
			
		||||
	set_result(result ? 0xff : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_findnext(void)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* fcb = (struct fcb*) &ram[dma];
 | 
			
		||||
	memset(fcb, 0, sizeof(struct fcb));
 | 
			
		||||
	int i = file_findnext(&fcb->filename);
 | 
			
		||||
	set_result(i ? 0xff : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_findfirst(void)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* fcb = find_fcb();
 | 
			
		||||
	int i = file_findfirst(&fcb->filename);
 | 
			
		||||
	if (i == 0)
 | 
			
		||||
		bdos_findnext();
 | 
			
		||||
	else
 | 
			
		||||
		set_result(i ? 0xff : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_deletefile(void)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* fcb = find_fcb();
 | 
			
		||||
	int i = file_delete(&fcb->filename);
 | 
			
		||||
	set_result(i ? 0xff : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef int readwrite_cb(struct file* f, uint8_t* ptr, uint16_t record);
 | 
			
		||||
 | 
			
		||||
static void bdos_readwritesequential(readwrite_cb* readwrite)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* fcb = find_fcb();
 | 
			
		||||
 | 
			
		||||
	struct file* f = file_open(&fcb->filename);
 | 
			
		||||
	int here = get_current_record(fcb);
 | 
			
		||||
	int i = readwrite(f, &ram[dma], here);
 | 
			
		||||
	set_current_record(fcb, here+1, file_getrecordcount(f));
 | 
			
		||||
	if (i == -1)
 | 
			
		||||
		set_result(0xff);
 | 
			
		||||
	else if (i == 0)
 | 
			
		||||
		set_result(1);
 | 
			
		||||
	else
 | 
			
		||||
		set_result(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_readwriterandom(readwrite_cb* readwrite)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* fcb = find_fcb();
 | 
			
		||||
 | 
			
		||||
	uint16_t record = fcb->r[0] + (fcb->r[1]<<8);
 | 
			
		||||
	struct file* f = file_open(&fcb->filename);
 | 
			
		||||
	int i = readwrite(f, &ram[dma], record);
 | 
			
		||||
	set_current_record(fcb, record, file_getrecordcount(f));
 | 
			
		||||
	if (i == -1)
 | 
			
		||||
		set_result(0xff);
 | 
			
		||||
	else if (i == 0)
 | 
			
		||||
		set_result(1);
 | 
			
		||||
	else
 | 
			
		||||
		set_result(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_filelength(void)
 | 
			
		||||
{
 | 
			
		||||
	struct fcb* fcb = find_fcb();
 | 
			
		||||
	struct file* f = file_open(&fcb->filename);
 | 
			
		||||
 | 
			
		||||
	int length = file_getrecordcount(f);
 | 
			
		||||
	fcb->r[0] = length;
 | 
			
		||||
	fcb->r[1] = length>>8;
 | 
			
		||||
	fcb->r[2] = length>>16;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_getsetuser(void)
 | 
			
		||||
{
 | 
			
		||||
	if (get_e() == 0xff)
 | 
			
		||||
		set_result(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdos_entry(uint8_t bdos_call)
 | 
			
		||||
{
 | 
			
		||||
	switch (bdos_call)
 | 
			
		||||
	{
 | 
			
		||||
		case  1: bdos_getchar();     return;
 | 
			
		||||
		case  2: bdos_putchar();     return;
 | 
			
		||||
		case  6: bdos_consoleio();   return;
 | 
			
		||||
		case  9: bdos_printstring(); return;
 | 
			
		||||
		case 10: bdos_readline();    return;
 | 
			
		||||
		case 11: bdos_consolestatus(); return;
 | 
			
		||||
		case 12: set_result(0x0022); return; // get CP/M version
 | 
			
		||||
		case 13: bdos_resetdisk();   return; // reset disk system
 | 
			
		||||
		case 14: bdos_selectdisk();  return; // select disk
 | 
			
		||||
		case 15: bdos_openfile();    return;
 | 
			
		||||
		case 16: bdos_closefile();   return;
 | 
			
		||||
		case 17: bdos_findfirst();   return;
 | 
			
		||||
		case 18: bdos_findnext();    return;
 | 
			
		||||
		case 19: bdos_deletefile();  return;
 | 
			
		||||
		case 20: bdos_readwritesequential(file_read);  return;
 | 
			
		||||
		case 21: bdos_readwritesequential(file_write); return;
 | 
			
		||||
		case 22: bdos_makefile();    return;
 | 
			
		||||
		case 23: bdos_renamefile();  return;
 | 
			
		||||
		case 24: set_result(0xffff); return; // get login vector
 | 
			
		||||
		case 25: bdos_getdisk();     return; // get current disk
 | 
			
		||||
		case 26: dma = get_de();     return; // set DMA
 | 
			
		||||
		case 27: set_result(0);      return; // get allocation vector
 | 
			
		||||
		case 29: set_result(0x0000); return; // get read-only vector
 | 
			
		||||
		case 31: set_result(0);      return; // get disk parameter block
 | 
			
		||||
		case 32: bdos_getsetuser();  return;
 | 
			
		||||
		case 33: bdos_readwriterandom(file_read);  return;
 | 
			
		||||
		case 34: bdos_readwriterandom(file_write); return;
 | 
			
		||||
		case 35: bdos_filelength(); return;
 | 
			
		||||
		case 40: bdos_readwriterandom(file_write); return;
 | 
			
		||||
		case 45:                     return; // set hardware error action
 | 
			
		||||
		case 108: exitcode = get_d(); return; // set exit code
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	showregs();
 | 
			
		||||
	fatal("unimplemented bdos entry %d", bdos_call);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void biosbdos_entry(int syscall)
 | 
			
		||||
{
 | 
			
		||||
	if (syscall == 0xff)
 | 
			
		||||
		bdos_entry(i8080_read_reg16(BC));
 | 
			
		||||
	else
 | 
			
		||||
		bios_entry(syscall);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								plat/cpm/emu/build.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								plat/cpm/emu/build.lua
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
ackfile {
 | 
			
		||||
	name = "bdos_o",
 | 
			
		||||
	srcs = { "./*.s" },
 | 
			
		||||
	vars = { plat = "cpm" },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
normalrule {
 | 
			
		||||
	name = "bdos_out",
 | 
			
		||||
	ins = {
 | 
			
		||||
		"util/led+led",
 | 
			
		||||
		"+bdos_o",
 | 
			
		||||
	},
 | 
			
		||||
	outleaves = { "bdos.out" },
 | 
			
		||||
	commands = { "%{ins[1]} -b0:0xff00 %{ins[2]} -o %{outs[1]}" }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
normalrule {
 | 
			
		||||
	name = "bdos_img",
 | 
			
		||||
	ins = {
 | 
			
		||||
		"util/amisc+aslod",
 | 
			
		||||
		"+bdos_out",
 | 
			
		||||
	},
 | 
			
		||||
	outleaves = { "bdos.img" },
 | 
			
		||||
	commands = { "%{ins[1]} %{ins[2]} %{outs[1]}" }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
normalrule {
 | 
			
		||||
	name = "bdos_c",
 | 
			
		||||
	ins = {
 | 
			
		||||
		"util/cmisc+objectify",
 | 
			
		||||
		"+bdos_img",
 | 
			
		||||
	},
 | 
			
		||||
	outleaves = { "bdos.c" },
 | 
			
		||||
	commands = { "%{ins[1]} bdos < %{ins[2]} > %{outs[1]}" }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cprogram {
 | 
			
		||||
	name = "emu",
 | 
			
		||||
	srcs = {
 | 
			
		||||
		"+bdos_c",
 | 
			
		||||
		"./*.c"
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										366
									
								
								plat/cpm/emu/dis8080.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								plat/cpm/emu/dis8080.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,366 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "intel_8080_emulator.h"
 | 
			
		||||
#include "dis8080.h"
 | 
			
		||||
 | 
			
		||||
enum
 | 
			
		||||
{
 | 
			
		||||
    NOTHING,
 | 
			
		||||
    CONST8,
 | 
			
		||||
    CONST16,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct insn
 | 
			
		||||
{
 | 
			
		||||
    const char* name;
 | 
			
		||||
    int operand;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct insn insns[0x100] =
 | 
			
		||||
{
 | 
			
		||||
    /* 00-07 */
 | 
			
		||||
    { "nop",            NOTHING },
 | 
			
		||||
    { "lxi b, 0x%04x",  CONST16 },
 | 
			
		||||
    { "stax b",         NOTHING },
 | 
			
		||||
    { "inx b",          NOTHING },
 | 
			
		||||
    { "inr b",          NOTHING },
 | 
			
		||||
    { "dcr b",          NOTHING },
 | 
			
		||||
    { "mvi b, 0x%02x",  CONST8 },
 | 
			
		||||
    { "rlc",            NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 08-0f */
 | 
			
		||||
    { "undef",          NOTHING },
 | 
			
		||||
    { "dad b",          NOTHING },
 | 
			
		||||
    { "ldax b",         NOTHING },
 | 
			
		||||
    { "dcx b",          NOTHING },
 | 
			
		||||
    { "inr c",          NOTHING },
 | 
			
		||||
    { "dcr c",          NOTHING },
 | 
			
		||||
    { "mvi c, 0x%02x",  CONST8 },
 | 
			
		||||
    { "rrc",            NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 10-17 */
 | 
			
		||||
    { "nop",            NOTHING },
 | 
			
		||||
    { "lxi d, 0x%04x",  CONST16 },
 | 
			
		||||
    { "stax d",         NOTHING },
 | 
			
		||||
    { "inx d",          NOTHING },
 | 
			
		||||
    { "inr d",          NOTHING },
 | 
			
		||||
    { "dcr d",          NOTHING },
 | 
			
		||||
    { "mvi d, 0x%02x",  CONST8 },
 | 
			
		||||
    { "ral",            NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 18-1f */
 | 
			
		||||
    { "undef",          NOTHING },
 | 
			
		||||
    { "dad d",          NOTHING },
 | 
			
		||||
    { "ldax d",         NOTHING },
 | 
			
		||||
    { "dcx d",          NOTHING },
 | 
			
		||||
    { "inr e",          NOTHING },
 | 
			
		||||
    { "dcr e",          NOTHING },
 | 
			
		||||
    { "mvi e, 0x%02x",  CONST8 },
 | 
			
		||||
    { "rar",            NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 20-27 */
 | 
			
		||||
    { "nop",            NOTHING },
 | 
			
		||||
    { "lxi h, 0x%04x",  CONST16 },
 | 
			
		||||
    { "shld 0x%04x",    CONST16 },
 | 
			
		||||
    { "inx h",          NOTHING },
 | 
			
		||||
    { "inr h",          NOTHING },
 | 
			
		||||
    { "dcr h",          NOTHING },
 | 
			
		||||
    { "mvi h, 0x%02x",  CONST8 },
 | 
			
		||||
    { "daa",            NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 28-2f */
 | 
			
		||||
    { "undef",          NOTHING },
 | 
			
		||||
    { "dad h",          NOTHING },
 | 
			
		||||
    { "lhld 0x%04x",    CONST16 },
 | 
			
		||||
    { "dcx h",          NOTHING },
 | 
			
		||||
    { "inr l",          NOTHING },
 | 
			
		||||
    { "dcr l",          NOTHING },
 | 
			
		||||
    { "mvi l, 0x%02x",  CONST8 },
 | 
			
		||||
    { "cma",            NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 30-37 */
 | 
			
		||||
    { "nop",            NOTHING },
 | 
			
		||||
    { "lxi sp, 0x%04x", CONST16 },
 | 
			
		||||
    { "sta 0x%04x",     CONST16 },
 | 
			
		||||
    { "inx sp",         NOTHING },
 | 
			
		||||
    { "inr m",          NOTHING },
 | 
			
		||||
    { "dcr m",          NOTHING },
 | 
			
		||||
    { "mvi m, 0x%02x",  CONST8 },
 | 
			
		||||
    { "stc",            NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 38-3f */
 | 
			
		||||
    { "undef",         NOTHING },
 | 
			
		||||
    { "dad sp",        NOTHING },
 | 
			
		||||
    { "lda 0x%04x",    CONST16 },
 | 
			
		||||
    { "dcx sp",        NOTHING },
 | 
			
		||||
    { "inr a",         NOTHING },
 | 
			
		||||
    { "dcr a",         NOTHING },
 | 
			
		||||
    { "mvi a, 0x%02x", CONST8 },
 | 
			
		||||
    { "cmc",           NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 40-47 */
 | 
			
		||||
    { "mov b, b",      NOTHING },
 | 
			
		||||
    { "mov b, c",      NOTHING },
 | 
			
		||||
    { "mov b, d",      NOTHING },
 | 
			
		||||
    { "mov b, e",      NOTHING },
 | 
			
		||||
    { "mov b, h",      NOTHING },
 | 
			
		||||
    { "mov b, l",      NOTHING },
 | 
			
		||||
    { "mov b, m",      NOTHING },
 | 
			
		||||
    { "mov b, a",      NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 47-4f */
 | 
			
		||||
    { "mov c, b",      NOTHING },
 | 
			
		||||
    { "mov c, c",      NOTHING },
 | 
			
		||||
    { "mov c, d",      NOTHING },
 | 
			
		||||
    { "mov c, e",      NOTHING },
 | 
			
		||||
    { "mov c, h",      NOTHING },
 | 
			
		||||
    { "mov c, l",      NOTHING },
 | 
			
		||||
    { "mov c, m",      NOTHING },
 | 
			
		||||
    { "mov c, a",      NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 50-57 */
 | 
			
		||||
    { "mov d, b",      NOTHING },
 | 
			
		||||
    { "mov d, c",      NOTHING },
 | 
			
		||||
    { "mov d, d",      NOTHING },
 | 
			
		||||
    { "mov d, e",      NOTHING },
 | 
			
		||||
    { "mov d, h",      NOTHING },
 | 
			
		||||
    { "mov d, l",      NOTHING },
 | 
			
		||||
    { "mov d, m",      NOTHING },
 | 
			
		||||
    { "mov d, a",      NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 57-5f */
 | 
			
		||||
    { "mov e, b",      NOTHING },
 | 
			
		||||
    { "mov e, c",      NOTHING },
 | 
			
		||||
    { "mov e, d",      NOTHING },
 | 
			
		||||
    { "mov e, e",      NOTHING },
 | 
			
		||||
    { "mov e, h",      NOTHING },
 | 
			
		||||
    { "mov e, l",      NOTHING },
 | 
			
		||||
    { "mov e, m",      NOTHING },
 | 
			
		||||
    { "mov e, a",      NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 60-67 */
 | 
			
		||||
    { "mov h, b",      NOTHING },
 | 
			
		||||
    { "mov h, c",      NOTHING },
 | 
			
		||||
    { "mov h, d",      NOTHING },
 | 
			
		||||
    { "mov h, e",      NOTHING },
 | 
			
		||||
    { "mov h, h",      NOTHING },
 | 
			
		||||
    { "mov h, l",      NOTHING },
 | 
			
		||||
    { "mov h, m",      NOTHING },
 | 
			
		||||
    { "mov h, a",      NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 67-6f */
 | 
			
		||||
    { "mov l, b",      NOTHING },
 | 
			
		||||
    { "mov l, c",      NOTHING },
 | 
			
		||||
    { "mov l, d",      NOTHING },
 | 
			
		||||
    { "mov l, e",      NOTHING },
 | 
			
		||||
    { "mov l, h",      NOTHING },
 | 
			
		||||
    { "mov l, l",      NOTHING },
 | 
			
		||||
    { "mov l, m",      NOTHING },
 | 
			
		||||
    { "mov l, a",      NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 70-77 */
 | 
			
		||||
    { "mov m, b",      NOTHING },
 | 
			
		||||
    { "mov m, c",      NOTHING },
 | 
			
		||||
    { "mov m, d",      NOTHING },
 | 
			
		||||
    { "mov m, e",      NOTHING },
 | 
			
		||||
    { "mov m, h",      NOTHING },
 | 
			
		||||
    { "mov m, l",      NOTHING },
 | 
			
		||||
    { "mov m, m",      NOTHING },
 | 
			
		||||
    { "hlt",           NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 77-7f */
 | 
			
		||||
    { "mov a, b",      NOTHING },
 | 
			
		||||
    { "mov a, c",      NOTHING },
 | 
			
		||||
    { "mov a, d",      NOTHING },
 | 
			
		||||
    { "mov a, e",      NOTHING },
 | 
			
		||||
    { "mov a, h",      NOTHING },
 | 
			
		||||
    { "mov a, l",      NOTHING },
 | 
			
		||||
    { "mov a, m",      NOTHING },
 | 
			
		||||
    { "mov a, a",      NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 80-87 */
 | 
			
		||||
    { "add b",         NOTHING },
 | 
			
		||||
    { "add c",         NOTHING },
 | 
			
		||||
    { "add d",         NOTHING },
 | 
			
		||||
    { "add e",         NOTHING },
 | 
			
		||||
    { "add h",         NOTHING },
 | 
			
		||||
    { "add l",         NOTHING },
 | 
			
		||||
    { "add m",         NOTHING },
 | 
			
		||||
    { "add a",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 88-8f */
 | 
			
		||||
    { "adc b",         NOTHING },
 | 
			
		||||
    { "adc c",         NOTHING },
 | 
			
		||||
    { "adc d",         NOTHING },
 | 
			
		||||
    { "adc e",         NOTHING },
 | 
			
		||||
    { "adc h",         NOTHING },
 | 
			
		||||
    { "adc l",         NOTHING },
 | 
			
		||||
    { "adc m",         NOTHING },
 | 
			
		||||
    { "adc a",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 90-97 */
 | 
			
		||||
    { "sub b",         NOTHING },
 | 
			
		||||
    { "sub c",         NOTHING },
 | 
			
		||||
    { "sub d",         NOTHING },
 | 
			
		||||
    { "sub e",         NOTHING },
 | 
			
		||||
    { "sub h",         NOTHING },
 | 
			
		||||
    { "sub l",         NOTHING },
 | 
			
		||||
    { "sub m",         NOTHING },
 | 
			
		||||
    { "sub a",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* 98-9f */
 | 
			
		||||
    { "sbb b",         NOTHING },
 | 
			
		||||
    { "sbb c",         NOTHING },
 | 
			
		||||
    { "sbb d",         NOTHING },
 | 
			
		||||
    { "sbb e",         NOTHING },
 | 
			
		||||
    { "sbb h",         NOTHING },
 | 
			
		||||
    { "sbb l",         NOTHING },
 | 
			
		||||
    { "sbb m",         NOTHING },
 | 
			
		||||
    { "sbb a",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* a0-a7 */
 | 
			
		||||
    { "ana b",         NOTHING },
 | 
			
		||||
    { "ana c",         NOTHING },
 | 
			
		||||
    { "ana d",         NOTHING },
 | 
			
		||||
    { "ana e",         NOTHING },
 | 
			
		||||
    { "ana h",         NOTHING },
 | 
			
		||||
    { "ana l",         NOTHING },
 | 
			
		||||
    { "ana m",         NOTHING },
 | 
			
		||||
    { "ana a",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* a8-af */
 | 
			
		||||
    { "xra b",         NOTHING },
 | 
			
		||||
    { "xra c",         NOTHING },
 | 
			
		||||
    { "xra d",         NOTHING },
 | 
			
		||||
    { "xra e",         NOTHING },
 | 
			
		||||
    { "xra h",         NOTHING },
 | 
			
		||||
    { "xra l",         NOTHING },
 | 
			
		||||
    { "xra m",         NOTHING },
 | 
			
		||||
    { "xra a",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* b0-b7 */
 | 
			
		||||
    { "ora b",         NOTHING },
 | 
			
		||||
    { "ora c",         NOTHING },
 | 
			
		||||
    { "ora d",         NOTHING },
 | 
			
		||||
    { "ora e",         NOTHING },
 | 
			
		||||
    { "ora h",         NOTHING },
 | 
			
		||||
    { "ora l",         NOTHING },
 | 
			
		||||
    { "ora m",         NOTHING },
 | 
			
		||||
    { "ora a",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* b8-bf */
 | 
			
		||||
    { "cmp b",         NOTHING },
 | 
			
		||||
    { "cmp c",         NOTHING },
 | 
			
		||||
    { "cmp d",         NOTHING },
 | 
			
		||||
    { "cmp e",         NOTHING },
 | 
			
		||||
    { "cmp h",         NOTHING },
 | 
			
		||||
    { "cmp l",         NOTHING },
 | 
			
		||||
    { "cmp m",         NOTHING },
 | 
			
		||||
    { "cmp a",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* c0-c7 */
 | 
			
		||||
    { "rnz",           NOTHING },
 | 
			
		||||
    { "pop b",         NOTHING },
 | 
			
		||||
    { "jnz 0x%04x",    CONST16 },
 | 
			
		||||
    { "jmp 0x%04x",    CONST16 },
 | 
			
		||||
    { "cnz 0x%04x",    CONST16 },
 | 
			
		||||
    { "push b",        NOTHING },
 | 
			
		||||
    { "adi 0x%02x",    CONST8 },
 | 
			
		||||
    { "rst 0",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* c8-cf */
 | 
			
		||||
    { "rz",            NOTHING },
 | 
			
		||||
    { "ret",           NOTHING },
 | 
			
		||||
    { "jz 0x%04x",     CONST16 },
 | 
			
		||||
    { "*jmp 0x%04x",   CONST16 },
 | 
			
		||||
    { "cz 0x%04x",     CONST16 },
 | 
			
		||||
    { "call 0x%04x",   CONST16 },
 | 
			
		||||
    { "aci 0x%02x",    CONST8 },
 | 
			
		||||
    { "rst 1",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* d0-d7 */
 | 
			
		||||
    { "rnc",           NOTHING },
 | 
			
		||||
    { "pop d",         NOTHING },
 | 
			
		||||
    { "jnc 0x%04x",    CONST16 },
 | 
			
		||||
    { "out 0x%02x",    CONST8 },
 | 
			
		||||
    { "cnc 0x%04x",    CONST16 },
 | 
			
		||||
    { "push d",        NOTHING },
 | 
			
		||||
    { "sui 0x%02x",    CONST8 },
 | 
			
		||||
    { "rst 2",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* d8-df */
 | 
			
		||||
    { "rc",            NOTHING },
 | 
			
		||||
    { "*ret",          NOTHING },
 | 
			
		||||
    { "jc 0x%04x",     CONST16 },
 | 
			
		||||
    { "in 0x%02x",     CONST8 },
 | 
			
		||||
    { "cc 0x%04x",     CONST16 },
 | 
			
		||||
    { "*call 0x%04x",  CONST16 },
 | 
			
		||||
    { "sbi 0x%02x",    CONST8 },
 | 
			
		||||
    { "rst 3",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* e0-e7 */
 | 
			
		||||
    { "rpo",           NOTHING },
 | 
			
		||||
    { "pop h",         NOTHING },
 | 
			
		||||
    { "jpo 0x%04x",    CONST16 },
 | 
			
		||||
    { "xthl",          NOTHING },
 | 
			
		||||
    { "cpo 0x%04x",    CONST16 },
 | 
			
		||||
    { "push h",        NOTHING },
 | 
			
		||||
    { "ani 0x%02x",    CONST8 },
 | 
			
		||||
    { "rst 4",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* e8-ef */
 | 
			
		||||
    { "rpe",           NOTHING },
 | 
			
		||||
    { "pchl",          NOTHING },
 | 
			
		||||
    { "jpe 0x%04x",    CONST16 },
 | 
			
		||||
    { "xchg",          NOTHING },
 | 
			
		||||
    { "cpe 0x%04x",    CONST16 },
 | 
			
		||||
    { "*call 0x%04x",  CONST16 },
 | 
			
		||||
    { "xri 0x%02x",    CONST8 },
 | 
			
		||||
    { "rst 5",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* f0-f7 */
 | 
			
		||||
    { "rp",            NOTHING },
 | 
			
		||||
    { "pop psw",       NOTHING },
 | 
			
		||||
    { "jp 0x%04x",     CONST16 },
 | 
			
		||||
    { "di",            NOTHING },
 | 
			
		||||
    { "cp 0x%04x",     CONST16 },
 | 
			
		||||
    { "push psw",      NOTHING },
 | 
			
		||||
    { "ori 0x%02x",    CONST8 },
 | 
			
		||||
    { "rst 6",         NOTHING },
 | 
			
		||||
 | 
			
		||||
    /* f8-ff */
 | 
			
		||||
    { "rm",            NOTHING },
 | 
			
		||||
    { "sphl",          NOTHING },
 | 
			
		||||
    { "jm 0x%04x",     CONST16 },
 | 
			
		||||
    { "ei",            NOTHING },
 | 
			
		||||
    { "cm 0x%04x",     CONST16 },
 | 
			
		||||
    { "*call 0x%04x",  CONST16 },
 | 
			
		||||
    { "cpi 0x%02x",    CONST8 },
 | 
			
		||||
    { "rst 7",         NOTHING },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint16_t i8080_disassemble(char* buffer, size_t bufsiz, uint16_t pc)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t opcode = i8080_read(pc++);
 | 
			
		||||
    struct insn* insn = &insns[opcode];
 | 
			
		||||
    uint16_t value = 0;
 | 
			
		||||
    switch (insn->operand)
 | 
			
		||||
    {
 | 
			
		||||
        case NOTHING:
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case CONST8:
 | 
			
		||||
            value = i8080_read(pc++);
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case CONST16:
 | 
			
		||||
            value = i8080_read(pc++);
 | 
			
		||||
            value |= i8080_read(pc++) << 8;
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    snprintf(buffer, bufsiz, insn->name, value);
 | 
			
		||||
    return pc;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								plat/cpm/emu/dis8080.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								plat/cpm/emu/dis8080.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
#ifndef DIS8080_H
 | 
			
		||||
#define DIS8080_H
 | 
			
		||||
 | 
			
		||||
extern uint16_t i8080_disassemble(char* buffer, size_t bufsiz, uint16_t pc);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										378
									
								
								plat/cpm/emu/emulator.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								plat/cpm/emu/emulator.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,378 @@
 | 
			
		|||
#define _POSIX_C_SOURCE 199309
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "intel_8080_emulator.h"
 | 
			
		||||
#include "dis8080.h"
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
 | 
			
		||||
uint8_t ram[0x10000];
 | 
			
		||||
 | 
			
		||||
struct watchpoint
 | 
			
		||||
{
 | 
			
		||||
	uint16_t address;
 | 
			
		||||
	uint8_t value;
 | 
			
		||||
	bool enabled;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static uint16_t breakpoints[16];
 | 
			
		||||
static struct watchpoint watchpoints[16];
 | 
			
		||||
static bool tracing = false;
 | 
			
		||||
static bool singlestepping = true;
 | 
			
		||||
static bool bdosbreak = false;
 | 
			
		||||
 | 
			
		||||
uint8_t i8080_read(uint16_t addr)
 | 
			
		||||
{
 | 
			
		||||
	return ram[addr];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void i8080_write(uint16_t addr, uint8_t value)
 | 
			
		||||
{
 | 
			
		||||
	ram[addr] = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t i8080_inport(uint8_t addr)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void i8080_outport(uint8_t addr, uint8_t value)
 | 
			
		||||
{
 | 
			
		||||
	biosbdos_entry(addr & 0xff);
 | 
			
		||||
	if (bdosbreak)
 | 
			
		||||
		singlestepping = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void showregs(void)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t af = i8080_read_reg16(AF);
 | 
			
		||||
	printf("%c%c.%c.%c%c%c sp=%04x af=%04x bc=%04x de=%04x hl=%04x\n",
 | 
			
		||||
		(af & 0x80) ? 'S' : 's',
 | 
			
		||||
		(af & 0x40) ? 'Z' : 'z',
 | 
			
		||||
		(af & 0x10) ? 'H' : 'h',
 | 
			
		||||
		(af & 0x04) ? 'P' : 'p',
 | 
			
		||||
		(af & 0x02) ? 'N' : 'n',
 | 
			
		||||
		(af & 0x01) ? 'C' : 'c',
 | 
			
		||||
		i8080_read_reg16(SP),
 | 
			
		||||
		af,
 | 
			
		||||
		i8080_read_reg16(BC),
 | 
			
		||||
		i8080_read_reg16(DE),
 | 
			
		||||
		i8080_read_reg16(HL));
 | 
			
		||||
 | 
			
		||||
	char buffer[80];
 | 
			
		||||
	int tstates;
 | 
			
		||||
	uint16_t pc = i8080_read_reg16(PC);
 | 
			
		||||
	i8080_disassemble(buffer, sizeof(buffer), pc);
 | 
			
		||||
	printf("%04x : %s\n", pc, buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_register(void)
 | 
			
		||||
{
 | 
			
		||||
	char* w1 = strtok(NULL, " ");
 | 
			
		||||
	char* w2 = strtok(NULL, " ");
 | 
			
		||||
 | 
			
		||||
	if (w1 && w2)
 | 
			
		||||
	{
 | 
			
		||||
		Z80_REG_T reg = -1;
 | 
			
		||||
		if (strcmp(w1, "sp") == 0)
 | 
			
		||||
			reg = SP;
 | 
			
		||||
		else if (strcmp(w1, "pc") == 0)
 | 
			
		||||
			reg = PC;
 | 
			
		||||
		else if (strcmp(w1, "af") == 0)
 | 
			
		||||
			reg = AF;
 | 
			
		||||
		else if (strcmp(w1, "bc") == 0)
 | 
			
		||||
			reg = BC;
 | 
			
		||||
		else if (strcmp(w1, "de") == 0)
 | 
			
		||||
			reg = DE;
 | 
			
		||||
		else if (strcmp(w1, "hl") == 0)
 | 
			
		||||
			reg = HL;
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			printf("Bad register\n");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		i8080_write_reg16(reg, strtoul(w2, NULL, 16));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	showregs();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_break(void)
 | 
			
		||||
{
 | 
			
		||||
	char* w1 = strtok(NULL, " ");
 | 
			
		||||
	if (w1)
 | 
			
		||||
	{
 | 
			
		||||
		uint16_t breakpc = strtoul(w1, NULL, 16);
 | 
			
		||||
		for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
 | 
			
		||||
		{
 | 
			
		||||
			if (breakpoints[i] == 0xffff)
 | 
			
		||||
			{
 | 
			
		||||
				breakpoints[i] = breakpc;
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		printf("Too many breakpoints\n");
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
 | 
			
		||||
		{
 | 
			
		||||
			if (breakpoints[i] != 0xffff)
 | 
			
		||||
				printf("%04x\n", breakpoints[i]);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_watch(void)
 | 
			
		||||
{
 | 
			
		||||
	char* w1 = strtok(NULL, " ");
 | 
			
		||||
	if (w1)
 | 
			
		||||
	{
 | 
			
		||||
		uint16_t watchaddr = strtoul(w1, NULL, 16);
 | 
			
		||||
		for (int i=0; i<sizeof(watchpoints)/sizeof(*watchpoints); i++)
 | 
			
		||||
		{
 | 
			
		||||
			struct watchpoint* w = &watchpoints[i];
 | 
			
		||||
			if (!w->enabled)
 | 
			
		||||
			{
 | 
			
		||||
				w->address = watchaddr;
 | 
			
		||||
				w->enabled = true;
 | 
			
		||||
				w->value = ram[watchaddr];
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		printf("Too many breakpoints\n");
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		for (int i=0; i<sizeof(watchpoints)/sizeof(*watchpoints); i++)
 | 
			
		||||
		{
 | 
			
		||||
			struct watchpoint* w = &watchpoints[i];
 | 
			
		||||
			if (w->enabled)
 | 
			
		||||
				printf("%04x (current value: %02x)\n", w->address, w->value);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_delete_breakpoint(void)
 | 
			
		||||
{
 | 
			
		||||
	char* w1 = strtok(NULL, " ");
 | 
			
		||||
	if (w1)
 | 
			
		||||
	{
 | 
			
		||||
		uint16_t breakpc = strtoul(w1, NULL, 16);
 | 
			
		||||
		for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
 | 
			
		||||
		{
 | 
			
		||||
			if (breakpoints[i] == breakpc)
 | 
			
		||||
			{
 | 
			
		||||
				breakpoints[i] = 0xffff;
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		printf("No such breakpoint\n");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_delete_watchpoint(void)
 | 
			
		||||
{
 | 
			
		||||
	char* w1 = strtok(NULL, " ");
 | 
			
		||||
	if (w1)
 | 
			
		||||
	{
 | 
			
		||||
		uint16_t address = strtoul(w1, NULL, 16);
 | 
			
		||||
		for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
 | 
			
		||||
		{
 | 
			
		||||
			struct watchpoint* w = &watchpoints[i];
 | 
			
		||||
			if (w->enabled && (w->address == address))
 | 
			
		||||
			{
 | 
			
		||||
				w->enabled = false;
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		printf("No such watchpoint\n");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_memory(void)
 | 
			
		||||
{
 | 
			
		||||
	char* w1 = strtok(NULL, " ");
 | 
			
		||||
	char* w2 = strtok(NULL, " ");
 | 
			
		||||
 | 
			
		||||
	if (!w2)
 | 
			
		||||
		w2 = "100";
 | 
			
		||||
 | 
			
		||||
	if (w1 && w2)
 | 
			
		||||
	{
 | 
			
		||||
		uint16_t startaddr = strtoul(w1, NULL, 16);
 | 
			
		||||
		uint16_t endaddr = startaddr + strtoul(w2, NULL, 16);
 | 
			
		||||
		uint16_t startrounded = startaddr & ~0xf;
 | 
			
		||||
		uint16_t endrounded = (endaddr + 0xf) & ~0xf;
 | 
			
		||||
 | 
			
		||||
		uint16_t p = startrounded;
 | 
			
		||||
 | 
			
		||||
		while (p < endrounded)
 | 
			
		||||
		{
 | 
			
		||||
			printf("%04x : ", p);
 | 
			
		||||
			for (int i = 0; i < 16; i++)
 | 
			
		||||
			{
 | 
			
		||||
				uint16_t pp = p + i;
 | 
			
		||||
				if ((pp >= startaddr) && (pp < endaddr))
 | 
			
		||||
					printf("%02x ", ram[pp]);
 | 
			
		||||
				else
 | 
			
		||||
					printf("   ");
 | 
			
		||||
			}
 | 
			
		||||
			printf(": ");
 | 
			
		||||
			for (int i = 0; i < 16; i++)
 | 
			
		||||
			{
 | 
			
		||||
				uint16_t pp = p + i;
 | 
			
		||||
				if ((pp >= startaddr) && (pp < endaddr))
 | 
			
		||||
				{
 | 
			
		||||
					uint8_t c = ram[pp];
 | 
			
		||||
					if ((c < 32) || (c > 127))
 | 
			
		||||
						c = '.';
 | 
			
		||||
					putchar(c);
 | 
			
		||||
				}
 | 
			
		||||
				else
 | 
			
		||||
					putchar(' ');
 | 
			
		||||
			}
 | 
			
		||||
			p += 16;
 | 
			
		||||
			putchar('\n');
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_bdos(void)
 | 
			
		||||
{
 | 
			
		||||
	char* w1 = strtok(NULL, " ");
 | 
			
		||||
	if (w1)
 | 
			
		||||
		bdosbreak = !!strtoul(w1, NULL, 16);
 | 
			
		||||
	else
 | 
			
		||||
		printf("break on bdos entry: %s\n", bdosbreak ? "on" : "off");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_tracing(void)
 | 
			
		||||
{
 | 
			
		||||
	char* w1 = strtok(NULL, " ");
 | 
			
		||||
	if (w1)
 | 
			
		||||
		tracing = !!strtoul(w1, NULL, 16);
 | 
			
		||||
	else
 | 
			
		||||
		printf("tracing: %s\n", tracing ? "on" : "off");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cmd_help(void)
 | 
			
		||||
{
 | 
			
		||||
	printf("Sleazy debugger\n"
 | 
			
		||||
	       "  r               show registers\n"
 | 
			
		||||
		   "  r <reg> <value> set register\n"
 | 
			
		||||
		   "  b               show breakpoints\n"
 | 
			
		||||
		   "  b <addr>        set breakpoint\n"
 | 
			
		||||
		   "  db <addr>       delete breakpoint\n"
 | 
			
		||||
		   "  w <addr>        set watchpoint\n"
 | 
			
		||||
		   "  dw <addr>       delete watchpoint\n"
 | 
			
		||||
		   "  m <addr> <len>  show memory\n"
 | 
			
		||||
		   "  s               single step\n"
 | 
			
		||||
		   "  g               continue\n"
 | 
			
		||||
		   "  bdos 0|1        enable break on bdos entry\n"
 | 
			
		||||
		   "  trace 0|1       enable tracing\n"
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void debug(void)
 | 
			
		||||
{
 | 
			
		||||
	bool go = false;
 | 
			
		||||
	showregs();
 | 
			
		||||
	while (!go)
 | 
			
		||||
	{
 | 
			
		||||
		char cmdline[80];
 | 
			
		||||
		printf("debug> ");
 | 
			
		||||
		fflush(stdout);
 | 
			
		||||
		if (!fgets(cmdline, sizeof(cmdline), stdin))
 | 
			
		||||
			exit(0);
 | 
			
		||||
 | 
			
		||||
		char* token = strtok(cmdline, " \n\r\t");
 | 
			
		||||
		if (token != NULL)
 | 
			
		||||
		{
 | 
			
		||||
			if (strcmp(token, "?") == 0)
 | 
			
		||||
				cmd_help();
 | 
			
		||||
			else if (strcmp(token, "r") == 0)
 | 
			
		||||
				cmd_register();
 | 
			
		||||
			else if (strcmp(token, "b") == 0)
 | 
			
		||||
				cmd_break();
 | 
			
		||||
			else if (strcmp(token, "w") == 0)
 | 
			
		||||
				cmd_watch();
 | 
			
		||||
			else if (strcmp(token, "db") == 0)
 | 
			
		||||
				cmd_delete_breakpoint();
 | 
			
		||||
			else if (strcmp(token, "dw") == 0)
 | 
			
		||||
				cmd_delete_watchpoint();
 | 
			
		||||
			else if (strcmp(token, "m") == 0)
 | 
			
		||||
				cmd_memory();
 | 
			
		||||
			else if (strcmp(token, "s") == 0)
 | 
			
		||||
			{
 | 
			
		||||
				singlestepping = true;
 | 
			
		||||
				go = true;
 | 
			
		||||
			}
 | 
			
		||||
			else if (strcmp(token, "g") == 0)
 | 
			
		||||
			{
 | 
			
		||||
				singlestepping = false;
 | 
			
		||||
				go = true;
 | 
			
		||||
			}
 | 
			
		||||
			else if (strcmp(token, "bdos") == 0)
 | 
			
		||||
				cmd_bdos();
 | 
			
		||||
			else if (strcmp(token, "tracing") == 0)
 | 
			
		||||
				cmd_tracing();
 | 
			
		||||
			else
 | 
			
		||||
				printf("Bad command\n");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sigusr1_cb(int number)
 | 
			
		||||
{
 | 
			
		||||
	singlestepping = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void emulator_init(void)
 | 
			
		||||
{
 | 
			
		||||
	for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
 | 
			
		||||
		breakpoints[i] = 0xffff;
 | 
			
		||||
 | 
			
		||||
	singlestepping = flag_enter_debugger;
 | 
			
		||||
 | 
			
		||||
	struct sigaction action = {
 | 
			
		||||
		.sa_handler = sigusr1_cb
 | 
			
		||||
	};
 | 
			
		||||
	sigaction(SIGUSR1, &action, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void emulator_run(void)
 | 
			
		||||
{
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
		uint16_t pc = i8080_read_reg16(PC);
 | 
			
		||||
		if (!singlestepping)
 | 
			
		||||
		{
 | 
			
		||||
			for (int i=0; i<sizeof(breakpoints)/sizeof(*breakpoints); i++)
 | 
			
		||||
				if (pc == breakpoints[i])
 | 
			
		||||
					singlestepping = true;
 | 
			
		||||
		}
 | 
			
		||||
		for (int i=0; i<sizeof(watchpoints)/sizeof(*watchpoints); i++)
 | 
			
		||||
		{
 | 
			
		||||
			struct watchpoint* w = &watchpoints[i];
 | 
			
		||||
			if (w->enabled && (ram[w->address] != w->value))
 | 
			
		||||
			{
 | 
			
		||||
				printf("\nWatchpoint hit: %04x has changed from %02x to %02x\n",
 | 
			
		||||
					w->address, w->value, ram[w->address]);
 | 
			
		||||
				w->value = ram[w->address];
 | 
			
		||||
				singlestepping = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (singlestepping)
 | 
			
		||||
			debug();
 | 
			
		||||
		else if (tracing)
 | 
			
		||||
			showregs();
 | 
			
		||||
 | 
			
		||||
		i8080_exec(1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										440
									
								
								plat/cpm/emu/fileio.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										440
									
								
								plat/cpm/emu/fileio.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,440 @@
 | 
			
		|||
#define _XOPEN_SOURCE 500
 | 
			
		||||
#define _POSIX_C_SOURCE 200809
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
 | 
			
		||||
#define logf(args...) while (0)
 | 
			
		||||
//#define logf(args...) printf(args)
 | 
			
		||||
 | 
			
		||||
struct file
 | 
			
		||||
{
 | 
			
		||||
	struct file* prev;
 | 
			
		||||
	struct file* next;
 | 
			
		||||
	cpm_filename_t filename;
 | 
			
		||||
	int fd;
 | 
			
		||||
	int flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define NUM_FILES 16
 | 
			
		||||
static struct file files[NUM_FILES];
 | 
			
		||||
static struct file* firstfile;
 | 
			
		||||
 | 
			
		||||
#define NUM_DRIVES 16
 | 
			
		||||
static int drives[NUM_DRIVES];
 | 
			
		||||
 | 
			
		||||
static cpm_filename_t currentpattern;
 | 
			
		||||
static int currentsearchdrivefd;
 | 
			
		||||
static DIR* currentdir;
 | 
			
		||||
 | 
			
		||||
void files_init(void)
 | 
			
		||||
{
 | 
			
		||||
	for (int i=0; i<NUM_DRIVES; i++)
 | 
			
		||||
		drives[i] = -1;
 | 
			
		||||
	file_set_drive(0, ".");
 | 
			
		||||
 | 
			
		||||
	for (int i=0; i<NUM_FILES; i++)
 | 
			
		||||
	{
 | 
			
		||||
		struct file* f = &files[i];
 | 
			
		||||
		if (i == 0)
 | 
			
		||||
			f->prev = NULL;
 | 
			
		||||
		else
 | 
			
		||||
			f->prev = &files[i-1];
 | 
			
		||||
 | 
			
		||||
		if (i == (NUM_FILES-1))
 | 
			
		||||
			f->next = NULL;
 | 
			
		||||
		else
 | 
			
		||||
			f->next = &files[i+1];
 | 
			
		||||
 | 
			
		||||
		memset(&f->filename.bytes, ' ', 11);
 | 
			
		||||
		f->filename.drive = 0;
 | 
			
		||||
		f->fd = -1;
 | 
			
		||||
		f->flags = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	firstfile = &files[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void file_set_drive(int drive, const char* path)
 | 
			
		||||
{
 | 
			
		||||
	if ((drive < 0) || (drive >= NUM_DRIVES))
 | 
			
		||||
		fatal("bad drive letter");
 | 
			
		||||
		
 | 
			
		||||
	if (drives[drive] != -1)
 | 
			
		||||
		close(drives[drive]);
 | 
			
		||||
	drives[drive] = open(path, O_RDONLY);
 | 
			
		||||
	if (drives[drive] == -1)
 | 
			
		||||
		fatal("could not open '%s': %s", path, strerror(errno));
 | 
			
		||||
 | 
			
		||||
	struct stat st;
 | 
			
		||||
	fstat(drives[drive], &st);
 | 
			
		||||
	if (!S_ISDIR(st.st_mode))
 | 
			
		||||
		fatal("could not open '%s': not a directory", path);
 | 
			
		||||
	logf("[drive %c now pointing at %s (fd %d)]\n", drive+'A', path, drives[drive]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bump(struct file* f)
 | 
			
		||||
{
 | 
			
		||||
	// logf("[bumping file %d to front]\n", f-files);
 | 
			
		||||
 | 
			
		||||
	if (f != firstfile)
 | 
			
		||||
	{
 | 
			
		||||
		/* Remove from list. */
 | 
			
		||||
		if (f->prev)
 | 
			
		||||
			f->prev->next = f->next;
 | 
			
		||||
		if (f->next)
 | 
			
		||||
			f->next->prev = f->prev;
 | 
			
		||||
 | 
			
		||||
		/* Reinsert at head of list. */
 | 
			
		||||
		firstfile->prev = f;
 | 
			
		||||
		f->prev = NULL;
 | 
			
		||||
		f->next = firstfile;
 | 
			
		||||
		firstfile = f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// logf("[first file is %d]\n", firstfile-files);
 | 
			
		||||
	// for (int i=0; i<NUM_FILES; i++)
 | 
			
		||||
	// {
 | 
			
		||||
	// 	f = &files[i];
 | 
			
		||||
	// 	logf("[file %02d: %c:%.11s, fd=%d, prev=%d next=%d]\n",
 | 
			
		||||
	// 		i, 'A'-1+f->filename.drive, f->filename.bytes, f->fd,
 | 
			
		||||
	// 		f->prev ? (f->prev - files) : -1,
 | 
			
		||||
	// 		f->next ? (f->next - files) : -1);
 | 
			
		||||
	// }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void cpm_filename_to_unix(cpm_filename_t* cpmfilename, char* unixfilename)
 | 
			
		||||
{
 | 
			
		||||
	char* pin = cpmfilename->bytes;
 | 
			
		||||
	char* pout = unixfilename;
 | 
			
		||||
 | 
			
		||||
	for (int i=0; i<8; i++)
 | 
			
		||||
	{
 | 
			
		||||
		char c = *pin++;
 | 
			
		||||
		if (c != ' ')
 | 
			
		||||
			*pout++ = tolower(c);
 | 
			
		||||
	}
 | 
			
		||||
	*pout++ = '.';
 | 
			
		||||
	for (int i=0; i<3; i++)
 | 
			
		||||
	{
 | 
			
		||||
		char c = *pin++;
 | 
			
		||||
		if (c != ' ')
 | 
			
		||||
			*pout++ = tolower(c);
 | 
			
		||||
	}
 | 
			
		||||
	if (pout[-1] == '.')
 | 
			
		||||
		pout--;
 | 
			
		||||
	*pout = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool unix_filename_to_cpm(const char* unixfilename, cpm_filename_t* cpmfilename)
 | 
			
		||||
{
 | 
			
		||||
	const char* pin = unixfilename;
 | 
			
		||||
 | 
			
		||||
	memset(cpmfilename, ' ', sizeof(cpm_filename_t));
 | 
			
		||||
	char* pout = &cpmfilename->bytes[0];
 | 
			
		||||
	int count = 0;
 | 
			
		||||
	int maxcount = 8;
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
		char c = *pin++;
 | 
			
		||||
		if ((c == '.') && (maxcount == 8))
 | 
			
		||||
		{
 | 
			
		||||
			maxcount = 3;
 | 
			
		||||
			count = 0;
 | 
			
		||||
			pout = &cpmfilename->bytes[8];
 | 
			
		||||
		}
 | 
			
		||||
		else if (c == '\0')
 | 
			
		||||
			break;
 | 
			
		||||
		else if (count == maxcount)
 | 
			
		||||
			return false;
 | 
			
		||||
		else if (isupper(c))
 | 
			
		||||
			return false;
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			*pout++ = toupper(c);
 | 
			
		||||
			count++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool match_filenames(cpm_filename_t* pattern, cpm_filename_t* filename)
 | 
			
		||||
{
 | 
			
		||||
	if (pattern->drive != filename->drive)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	for (int i=0; i<sizeof(pattern->bytes); i++)
 | 
			
		||||
	{
 | 
			
		||||
		char p = pattern->bytes[i];
 | 
			
		||||
		if (p == '?')
 | 
			
		||||
			continue;
 | 
			
		||||
		if (p != filename->bytes[i])
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_drive_fd(cpm_filename_t* filename)
 | 
			
		||||
{
 | 
			
		||||
	int drive = filename->drive - 1;
 | 
			
		||||
	if ((drive < 0) || (drive >= NUM_DRIVES))
 | 
			
		||||
	{
 | 
			
		||||
		logf("[reference to bad drive %c]\n", drive + 'A');
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	int drivefd = drives[drive];
 | 
			
		||||
	if (drivefd == -1)
 | 
			
		||||
	{
 | 
			
		||||
		logf("[reference to undefined drive %c]\n", drive + 'A');
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	logf("[selecting drive %c on fd %d]\n", drive + 'A', drivefd);
 | 
			
		||||
	return drivefd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void reopen(struct file* f, int flags)
 | 
			
		||||
{
 | 
			
		||||
	if ((f->fd == -1) || ((f->flags == O_RDONLY) && (flags == O_RDWR)))
 | 
			
		||||
	{
 | 
			
		||||
		char unixfilename[13];
 | 
			
		||||
		cpm_filename_to_unix(&f->filename, unixfilename);
 | 
			
		||||
 | 
			
		||||
		if (f->fd != -1)
 | 
			
		||||
		{
 | 
			
		||||
			logf("[reopening actual file '%s' on %d with different flags]\n", unixfilename, f->fd);
 | 
			
		||||
			close(f->fd);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int drivefd = get_drive_fd(&f->filename);
 | 
			
		||||
		if (drivefd == -1)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		f->flags = flags & O_ACCMODE;
 | 
			
		||||
		errno = 0;
 | 
			
		||||
		f->fd = openat(drivefd, unixfilename, flags, 0666);
 | 
			
		||||
		logf("[opened actual file '%s' to fd %d: %s]\n", unixfilename, f->fd, strerror(errno));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct file* find_file(cpm_filename_t* filename)
 | 
			
		||||
{
 | 
			
		||||
	struct file* f = firstfile;
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
		if (memcmp(filename, &f->filename, sizeof(cpm_filename_t)) == 0)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (f->next)
 | 
			
		||||
			f = f->next;
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			logf("[allocating file %d for '%.11s']\n", f-files, filename->bytes);
 | 
			
		||||
			bump(f);
 | 
			
		||||
			if (f->fd != -1)
 | 
			
		||||
			{
 | 
			
		||||
				logf("[closing old file %d for '%.11s']\n", f-files, f->filename.bytes);
 | 
			
		||||
				close(f->fd);
 | 
			
		||||
			}
 | 
			
		||||
			f->fd = -1;
 | 
			
		||||
			f->filename = *filename;
 | 
			
		||||
			f->flags = 0;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct file* file_open(cpm_filename_t* filename)
 | 
			
		||||
{
 | 
			
		||||
	struct file* f = find_file(filename);
 | 
			
		||||
	reopen(f, O_RDONLY);
 | 
			
		||||
	if (f->fd == -1)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct file* file_create(cpm_filename_t* filename)
 | 
			
		||||
{
 | 
			
		||||
	struct file* f = find_file(filename);
 | 
			
		||||
	logf("[creating file %d for '%.11s']\n", f-files, f->filename.bytes);
 | 
			
		||||
	reopen(f, O_RDWR | O_CREAT);
 | 
			
		||||
	if (f->fd == -1)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int file_close(cpm_filename_t* filename)
 | 
			
		||||
{
 | 
			
		||||
	struct file* f = find_file(filename);
 | 
			
		||||
 | 
			
		||||
	logf("[explicitly closing file %d for '%.11s']\n", f-files, f->filename.bytes);
 | 
			
		||||
	if (f->fd != -1)
 | 
			
		||||
	{
 | 
			
		||||
		logf("[closing file descriptor %d]\n", f->fd);
 | 
			
		||||
		close(f->fd);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memset(&f->filename.bytes, ' ', 11);
 | 
			
		||||
	f->fd = -1;
 | 
			
		||||
	f->flags = 0;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int file_read(struct file* f, uint8_t* data, uint16_t record)
 | 
			
		||||
{
 | 
			
		||||
	reopen(f, O_RDONLY);
 | 
			
		||||
	
 | 
			
		||||
	logf("[read record %04x from file %d for '%.11s']\n", record, f-files, f->filename.bytes);
 | 
			
		||||
	bump(f);
 | 
			
		||||
	memset(data, '\0', 128);
 | 
			
		||||
	return pread(f->fd, data, 128, record*128);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int file_write(struct file* f, uint8_t* data, uint16_t record)
 | 
			
		||||
{
 | 
			
		||||
	reopen(f, O_RDWR);
 | 
			
		||||
 | 
			
		||||
	logf("[write record %04x from file %d for '%.11s']\n", record, f-files, f->filename.bytes);
 | 
			
		||||
	bump(f);
 | 
			
		||||
	return pwrite(f->fd, data, 128, record*128);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int file_getrecordcount(struct file* f)
 | 
			
		||||
{
 | 
			
		||||
	reopen(f, O_RDONLY);
 | 
			
		||||
	
 | 
			
		||||
	struct stat st;
 | 
			
		||||
	fstat(f->fd, &st);
 | 
			
		||||
	return (st.st_size + 127) >> 7;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void file_setrecordcount(struct file* f, int count)
 | 
			
		||||
{
 | 
			
		||||
	reopen(f, O_RDONLY);
 | 
			
		||||
	
 | 
			
		||||
	if (count != file_getrecordcount(f))
 | 
			
		||||
	{
 | 
			
		||||
		logf("[truncating file %d to %d records]\n", f-files, count);
 | 
			
		||||
		reopen(f, O_RDWR);
 | 
			
		||||
		ftruncate(f->fd, count*128);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int file_findfirst(cpm_filename_t* pattern)
 | 
			
		||||
{
 | 
			
		||||
	if (currentdir)
 | 
			
		||||
	{
 | 
			
		||||
		closedir(currentdir);
 | 
			
		||||
		currentdir = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	currentpattern = *pattern;
 | 
			
		||||
	logf("[reset search; current find pattern is '%.11s']\n", currentpattern.bytes);
 | 
			
		||||
	currentsearchdrivefd = get_drive_fd(pattern);
 | 
			
		||||
	if (currentsearchdrivefd == -1)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	currentdir = fdopendir(dup(currentsearchdrivefd));
 | 
			
		||||
	if (currentdir)
 | 
			
		||||
	{
 | 
			
		||||
		rewinddir(currentdir);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int file_findnext(cpm_filename_t* result)
 | 
			
		||||
{
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
		if (!currentdir)
 | 
			
		||||
			return -1;
 | 
			
		||||
 | 
			
		||||
		struct dirent* de = readdir(currentdir);
 | 
			
		||||
		if (!de)
 | 
			
		||||
		{
 | 
			
		||||
			closedir(currentdir);
 | 
			
		||||
			currentdir = NULL;
 | 
			
		||||
			logf("[finished search]\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		struct stat st;
 | 
			
		||||
		if ((fstatat(currentsearchdrivefd, de->d_name, &st, 0) == 0)
 | 
			
		||||
			&& S_ISREG(st.st_mode)
 | 
			
		||||
			&& unix_filename_to_cpm(de->d_name, result))
 | 
			
		||||
		{
 | 
			
		||||
			result->drive = currentpattern.drive;
 | 
			
		||||
			logf("[compare '%.11s' with pattern '%.11s']\n", result->bytes, currentpattern.bytes);
 | 
			
		||||
			if (match_filenames(¤tpattern, result))
 | 
			
		||||
			{
 | 
			
		||||
				logf("[positive match]\n");
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int file_delete(cpm_filename_t* pattern)
 | 
			
		||||
{
 | 
			
		||||
	logf("[attempting to delete pattern '%.11s' on drive %c]\n", pattern->bytes, '@'+pattern->drive);
 | 
			
		||||
	int drivefd = get_drive_fd(pattern);
 | 
			
		||||
	DIR* dir = fdopendir(dup(drivefd));
 | 
			
		||||
	if (!dir)
 | 
			
		||||
		return -1;
 | 
			
		||||
	rewinddir(dir);
 | 
			
		||||
 | 
			
		||||
	int result = -1;
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
		struct dirent* de = readdir(dir);
 | 
			
		||||
		if (!de)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		struct stat st;
 | 
			
		||||
		cpm_filename_t candidate;
 | 
			
		||||
 | 
			
		||||
		if ((fstatat(drivefd, de->d_name, &st, 0) == 0)
 | 
			
		||||
			&& S_ISREG(st.st_mode)
 | 
			
		||||
			&& unix_filename_to_cpm(de->d_name, &candidate))
 | 
			
		||||
		{
 | 
			
		||||
			candidate.drive = pattern->drive;
 | 
			
		||||
			logf("[compare '%.11s' with pattern '%.11s']\n", candidate.bytes, pattern->bytes);
 | 
			
		||||
			if (match_filenames(pattern, &candidate))
 | 
			
		||||
			{
 | 
			
		||||
				logf("[positive match, deleting]\n");
 | 
			
		||||
				unlinkat(drivefd, de->d_name, 0);
 | 
			
		||||
				result = 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	closedir(dir);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int file_rename(cpm_filename_t* src, cpm_filename_t* dest)
 | 
			
		||||
{
 | 
			
		||||
	logf("[renaming %.11s to %.11s on drive %c]\n",
 | 
			
		||||
		src->bytes, dest->bytes, '@'+src->drive);
 | 
			
		||||
 | 
			
		||||
	char srcunixfilename[13];
 | 
			
		||||
	cpm_filename_to_unix(src, srcunixfilename);
 | 
			
		||||
 | 
			
		||||
	char destunixfilename[13];
 | 
			
		||||
	cpm_filename_to_unix(dest, destunixfilename);
 | 
			
		||||
 | 
			
		||||
	int drivefd = get_drive_fd(src);
 | 
			
		||||
	return renameat(drivefd, srcunixfilename, drivefd, destunixfilename);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								plat/cpm/emu/globals.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								plat/cpm/emu/globals.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
#ifndef GLOBALS_H
 | 
			
		||||
#define GLOBALS_H
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <z80ex/z80ex.h>
 | 
			
		||||
 | 
			
		||||
extern Z80EX_CONTEXT* z80;
 | 
			
		||||
extern uint8_t ram[0x10000];
 | 
			
		||||
 | 
			
		||||
extern void emulator_init(void);
 | 
			
		||||
extern void emulator_run(void);
 | 
			
		||||
extern void showregs(void);
 | 
			
		||||
 | 
			
		||||
extern const uint8_t ccp_data[];
 | 
			
		||||
extern const int ccp_len;
 | 
			
		||||
 | 
			
		||||
extern const uint8_t bdos_data[];
 | 
			
		||||
extern const int bdos_len;
 | 
			
		||||
 | 
			
		||||
extern void bios_coldboot(void);
 | 
			
		||||
 | 
			
		||||
extern void biosbdos_entry(int syscall);
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	uint8_t drive;
 | 
			
		||||
	char bytes[11];
 | 
			
		||||
}
 | 
			
		||||
cpm_filename_t;
 | 
			
		||||
 | 
			
		||||
extern void files_init(void);
 | 
			
		||||
extern void file_set_drive(int drive, const char* path);
 | 
			
		||||
extern struct file* file_open(cpm_filename_t* filename);
 | 
			
		||||
extern struct file* file_create(cpm_filename_t* filename);
 | 
			
		||||
extern int file_close(cpm_filename_t* filename);
 | 
			
		||||
extern int file_read(struct file* file, uint8_t* data, uint16_t record);
 | 
			
		||||
extern int file_write(struct file* file, uint8_t* data, uint16_t record);
 | 
			
		||||
extern int file_getrecordcount(struct file* f);
 | 
			
		||||
extern void file_setrecordcount(struct file* f, int count);
 | 
			
		||||
extern int file_findfirst(cpm_filename_t* pattern);
 | 
			
		||||
extern int file_findnext(cpm_filename_t* result);
 | 
			
		||||
extern int file_delete(cpm_filename_t* pattern);
 | 
			
		||||
extern int file_rename(cpm_filename_t* src, cpm_filename_t* dest);
 | 
			
		||||
 | 
			
		||||
extern void fatal(const char* message, ...);
 | 
			
		||||
 | 
			
		||||
extern bool flag_enter_debugger;
 | 
			
		||||
extern char* const* user_command_line;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										874
									
								
								plat/cpm/emu/intel_8080_emulator.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										874
									
								
								plat/cpm/emu/intel_8080_emulator.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,874 @@
 | 
			
		|||
/*
 | 
			
		||||
  Intel 8080 emulator in C
 | 
			
		||||
  Written by Mike Chambers, April 2018
 | 
			
		||||
 | 
			
		||||
  Use this code for whatever you want. I don't care. It's officially public domain.
 | 
			
		||||
  Credit would be appreciated.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include "intel_8080_emulator.h"
 | 
			
		||||
 | 
			
		||||
#define ALLOW_UNDEFINED
 | 
			
		||||
 | 
			
		||||
#define reg16_PSW (((uint16_t)reg8[A] << 8) | (uint16_t)reg8[FLAGS])
 | 
			
		||||
#define reg16_BC (((uint16_t)reg8[B] << 8) | (uint16_t)reg8[C])
 | 
			
		||||
#define reg16_DE (((uint16_t)reg8[D] << 8) | (uint16_t)reg8[E])
 | 
			
		||||
#define reg16_HL (((uint16_t)reg8[H] << 8) | (uint16_t)reg8[L])
 | 
			
		||||
 | 
			
		||||
uint8_t reg8[9], INTE = 0;
 | 
			
		||||
uint16_t reg_SP, reg_PC;
 | 
			
		||||
 | 
			
		||||
#define set_S() reg8[FLAGS] |= 0x80
 | 
			
		||||
#define set_Z() reg8[FLAGS] |= 0x40
 | 
			
		||||
#define set_AC() reg8[FLAGS] |= 0x10
 | 
			
		||||
#define set_P() reg8[FLAGS] |= 0x04
 | 
			
		||||
#define set_C() reg8[FLAGS] |= 0x01
 | 
			
		||||
#define clear_S() reg8[FLAGS] &= 0x7F
 | 
			
		||||
#define clear_Z() reg8[FLAGS] &= 0xBF
 | 
			
		||||
#define clear_AC() reg8[FLAGS] &= 0xEF
 | 
			
		||||
#define clear_P() reg8[FLAGS] &= 0xFB
 | 
			
		||||
#define clear_C() reg8[FLAGS] &= 0xFE
 | 
			
		||||
#define test_S() (reg8[FLAGS] & 0x80)
 | 
			
		||||
#define test_Z() (reg8[FLAGS] & 0x40)
 | 
			
		||||
#define test_AC() (reg8[FLAGS] & 0x10)
 | 
			
		||||
#define test_P() (reg8[FLAGS] & 0x04)
 | 
			
		||||
#define test_C() (reg8[FLAGS] & 0x01)
 | 
			
		||||
 | 
			
		||||
static const uint8_t parity[0x100] = {
 | 
			
		||||
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
 | 
			
		||||
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
 | 
			
		||||
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
 | 
			
		||||
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
 | 
			
		||||
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
 | 
			
		||||
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
 | 
			
		||||
	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
 | 
			
		||||
	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint16_t read_RP(uint8_t rp) {
 | 
			
		||||
	switch (rp) {
 | 
			
		||||
		case 0x00:
 | 
			
		||||
			return reg16_BC;
 | 
			
		||||
		case 0x01:
 | 
			
		||||
			return reg16_DE;
 | 
			
		||||
		case 0x02:
 | 
			
		||||
			return reg16_HL;
 | 
			
		||||
		case 0x03:
 | 
			
		||||
			return reg_SP;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t read_RP_PUSHPOP(uint8_t rp) {
 | 
			
		||||
	switch (rp) {
 | 
			
		||||
		case 0x00:
 | 
			
		||||
			return reg16_BC;
 | 
			
		||||
		case 0x01:
 | 
			
		||||
			return reg16_DE;
 | 
			
		||||
		case 0x02:
 | 
			
		||||
			return reg16_HL;
 | 
			
		||||
		case 0x03:
 | 
			
		||||
			return (reg16_PSW | 0x02) & 0xFFD7;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void write_RP(uint8_t rp, uint8_t lb, uint8_t hb) {
 | 
			
		||||
	switch (rp) {
 | 
			
		||||
		case 0x00:
 | 
			
		||||
			reg8[C] = lb;
 | 
			
		||||
			reg8[B] = hb;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x01:
 | 
			
		||||
			reg8[E] = lb;
 | 
			
		||||
			reg8[D] = hb;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x02:
 | 
			
		||||
			reg8[L] = lb;
 | 
			
		||||
			reg8[H] = hb;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x03:
 | 
			
		||||
			reg_SP = (uint16_t)lb | ((uint16_t)hb << 8);
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void write16_RP(uint8_t rp, uint16_t value) {
 | 
			
		||||
	switch (rp) {
 | 
			
		||||
		case 0x00:
 | 
			
		||||
			reg8[C] = value & 0x00FF;
 | 
			
		||||
			reg8[B] = value >> 8;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x01:
 | 
			
		||||
			reg8[E] = value & 0x00FF;
 | 
			
		||||
			reg8[D] = value >> 8;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x02:
 | 
			
		||||
			reg8[L] = value & 0x00FF;
 | 
			
		||||
			reg8[H] = value >> 8;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x03:
 | 
			
		||||
			reg_SP = value;
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void write16_RP_PUSHPOP(uint8_t rp, uint16_t value) {
 | 
			
		||||
	switch (rp) {
 | 
			
		||||
		case 0x00:
 | 
			
		||||
			reg8[C] = value & 0x00FF;
 | 
			
		||||
			reg8[B] = value >> 8;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x01:
 | 
			
		||||
			reg8[E] = value & 0x00FF;
 | 
			
		||||
			reg8[D] = value >> 8;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x02:
 | 
			
		||||
			reg8[L] = value & 0x00FF;
 | 
			
		||||
			reg8[H] = value >> 8;
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x03:
 | 
			
		||||
			reg8[FLAGS] = ((value & 0x00FF) | 0x02) & 0xD7;
 | 
			
		||||
			reg8[A] = value >> 8;
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void calc_SZP(uint8_t value) {
 | 
			
		||||
	if (value == 0) set_Z(); else clear_Z();
 | 
			
		||||
	if (value & 0x80) set_S(); else clear_S();
 | 
			
		||||
	if (parity[value]) set_P(); else clear_P();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void calc_AC(uint8_t val1, uint8_t val2) {
 | 
			
		||||
	if (((val1 & 0x0F) + (val2 & 0x0F)) > 0x0F) {
 | 
			
		||||
		set_AC();
 | 
			
		||||
	} else {
 | 
			
		||||
		clear_AC();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void calc_AC_carry(uint8_t val1, uint8_t val2) {
 | 
			
		||||
	if (((val1 & 0x0F) + (val2 & 0x0F)) >= 0x0F) {
 | 
			
		||||
		set_AC();
 | 
			
		||||
	} else {
 | 
			
		||||
		clear_AC();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void calc_subAC(int8_t val1, uint8_t val2) {
 | 
			
		||||
	if ((val2 & 0x0F) <= (val1 & 0x0F)) {
 | 
			
		||||
		set_AC();
 | 
			
		||||
	} else {
 | 
			
		||||
		clear_AC();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void calc_subAC_borrow(int8_t val1, uint8_t val2) {
 | 
			
		||||
	if ((val2 & 0x0F) < (val1 & 0x0F)) {
 | 
			
		||||
		set_AC();
 | 
			
		||||
	} else {
 | 
			
		||||
		clear_AC();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t test_cond(uint8_t code) {
 | 
			
		||||
	switch (code) {
 | 
			
		||||
		case 0: //Z not set
 | 
			
		||||
			if (!test_Z()) return 1; else return 0;
 | 
			
		||||
		case 1: //Z set
 | 
			
		||||
			if (test_Z()) return 1; else return 0;
 | 
			
		||||
		case 2: //C not set
 | 
			
		||||
			if (!test_C()) return 1; else return 0;
 | 
			
		||||
		case 3: //C set
 | 
			
		||||
			if (test_C()) return 1; else return 0;
 | 
			
		||||
		case 4: //P not set
 | 
			
		||||
			if (!test_P()) return 1; else return 0;
 | 
			
		||||
		case 5: //P set
 | 
			
		||||
			if (test_P()) return 1; else return 0;
 | 
			
		||||
		case 6: //S not set
 | 
			
		||||
			if (!test_S()) return 1; else return 0;
 | 
			
		||||
		case 7: //S set
 | 
			
		||||
			if (test_S()) return 1; else return 0;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void i8080_push(uint16_t value) {
 | 
			
		||||
	i8080_write(--reg_SP, value >> 8);
 | 
			
		||||
	i8080_write(--reg_SP, (uint8_t)value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t i8080_pop() {
 | 
			
		||||
	uint16_t temp;
 | 
			
		||||
	temp = i8080_read(reg_SP++);
 | 
			
		||||
	temp |= (uint16_t)i8080_read(reg_SP++) << 8;
 | 
			
		||||
	return temp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void i8080_interrupt(uint8_t n) {
 | 
			
		||||
	if (!INTE) return;
 | 
			
		||||
	i8080_push(reg_PC);
 | 
			
		||||
	reg_PC = (uint16_t)n << 3;
 | 
			
		||||
	INTE = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void i8080_jump(uint16_t addr) {
 | 
			
		||||
	reg_PC = addr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void i8080_reset() {
 | 
			
		||||
	reg_PC = reg_SP = 0x0000;
 | 
			
		||||
	//reg8[FLAGS] = 0x02;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void i8080_write_reg8(reg_t reg, uint8_t value) {
 | 
			
		||||
	if (reg == M) {
 | 
			
		||||
		i8080_write(reg16_HL, value);
 | 
			
		||||
	} else {
 | 
			
		||||
		reg8[reg] = value;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t i8080_read_reg8(reg_t reg) {
 | 
			
		||||
	if (reg == M) {
 | 
			
		||||
		return i8080_read(reg16_HL);
 | 
			
		||||
	} else {
 | 
			
		||||
		return reg8[reg];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t i8080_read_reg16(reg_t reg) {
 | 
			
		||||
	switch (reg) {
 | 
			
		||||
		case AF: return reg16_PSW;
 | 
			
		||||
		case BC: return reg16_BC;
 | 
			
		||||
		case DE: return reg16_DE;
 | 
			
		||||
		case HL: return reg16_HL;
 | 
			
		||||
		case SP: return reg_SP;
 | 
			
		||||
		case PC: return reg_PC;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void i8080_write_reg16(reg_t reg, uint16_t value) {
 | 
			
		||||
	switch (reg) {
 | 
			
		||||
		case AF: reg8[A] = value>>8; reg8[FLAGS] = value; break;
 | 
			
		||||
		case BC: reg8[B] = value>>8; reg8[C] = value; break;
 | 
			
		||||
		case DE: reg8[D] = value>>8; reg8[E] = value; break;
 | 
			
		||||
		case HL: reg8[H] = value>>8; reg8[L] = value; break;
 | 
			
		||||
		case SP: reg_SP = value; break;
 | 
			
		||||
		case PC: reg_PC = value; break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int i8080_exec(int cycles) {
 | 
			
		||||
	uint8_t opcode, temp8, reg, reg2;
 | 
			
		||||
	uint16_t temp16;
 | 
			
		||||
	uint32_t temp32;
 | 
			
		||||
 | 
			
		||||
	while (cycles > 0) {
 | 
			
		||||
		opcode = i8080_read(reg_PC++);
 | 
			
		||||
 | 
			
		||||
		switch (opcode) {
 | 
			
		||||
			case 0x3A: //LDA a - load A from memory
 | 
			
		||||
				temp16 = (uint16_t)i8080_read(reg_PC) | ((uint16_t)i8080_read(reg_PC+1)<<8);
 | 
			
		||||
				reg8[A] = i8080_read(temp16);
 | 
			
		||||
				reg_PC += 2;
 | 
			
		||||
				cycles -= 13;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x32: //STA a - store A to memory
 | 
			
		||||
				temp16 = (uint16_t)i8080_read(reg_PC) | ((uint16_t)i8080_read(reg_PC+1)<<8);
 | 
			
		||||
				i8080_write(temp16, reg8[A]);
 | 
			
		||||
				reg_PC += 2;
 | 
			
		||||
				cycles -= 13;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x2A: //LHLD a - load H:L from memory
 | 
			
		||||
				temp16 = (uint16_t)i8080_read(reg_PC) | ((uint16_t)i8080_read(reg_PC+1)<<8);
 | 
			
		||||
				reg8[L] = i8080_read(temp16++);
 | 
			
		||||
				reg8[H] = i8080_read(temp16);
 | 
			
		||||
				reg_PC += 2;
 | 
			
		||||
				cycles -= 16;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x22: //SHLD a - store H:L to memory
 | 
			
		||||
				temp16 = (uint16_t)i8080_read(reg_PC) | ((uint16_t)i8080_read(reg_PC+1)<<8);
 | 
			
		||||
				i8080_write(temp16++, reg8[L]);
 | 
			
		||||
				i8080_write(temp16, reg8[H]);
 | 
			
		||||
				reg_PC += 2;
 | 
			
		||||
				cycles -= 16;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xEB: //XCHG - exchange DE and HL content
 | 
			
		||||
				temp8 = reg8[D];
 | 
			
		||||
				reg8[D] = reg8[H];
 | 
			
		||||
				reg8[H] = temp8;
 | 
			
		||||
				temp8 = reg8[E];
 | 
			
		||||
				reg8[E] = reg8[L];
 | 
			
		||||
				reg8[L] = temp8;
 | 
			
		||||
				cycles -= 5;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xC6: //ADI # - add immediate to A
 | 
			
		||||
				temp8 = i8080_read(reg_PC++);
 | 
			
		||||
				temp16 = (uint16_t)reg8[A] + (uint16_t)temp8;
 | 
			
		||||
				if (temp16 & 0xFF00) set_C(); else clear_C();
 | 
			
		||||
				calc_AC(reg8[A], temp8);
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				reg8[A] = (uint8_t)temp16;
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xCE: //ACI # - add immediate to A with carry
 | 
			
		||||
				temp8 = i8080_read(reg_PC++);
 | 
			
		||||
				temp16 = (uint16_t)reg8[A] + (uint16_t)temp8 + (uint16_t)test_C();
 | 
			
		||||
				if (test_C()) calc_AC_carry(reg8[A], temp8); else calc_AC(reg8[A], temp8);
 | 
			
		||||
				if (temp16 & 0xFF00) set_C(); else clear_C();
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				reg8[A] = (uint8_t)temp16;
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xD6: //SUI # - subtract immediate from A
 | 
			
		||||
				temp8 = i8080_read(reg_PC++);
 | 
			
		||||
				temp16 = (uint16_t)reg8[A] - (uint16_t)temp8;
 | 
			
		||||
				if (((temp16 & 0x00FF) >= reg8[A]) && temp8) set_C(); else clear_C();
 | 
			
		||||
				calc_subAC(reg8[A], temp8);
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				reg8[A] = (uint8_t)temp16;
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x27: //DAA - decimal adjust accumulator
 | 
			
		||||
				temp16 = reg8[A];
 | 
			
		||||
				if (((temp16 & 0x0F) > 0x09) || test_AC()) {
 | 
			
		||||
					if (((temp16 & 0x0F) + 0x06) & 0xF0) set_AC(); else clear_AC();
 | 
			
		||||
					temp16 += 0x06;
 | 
			
		||||
					if (temp16 & 0xFF00) set_C(); //can also cause carry to be set during addition to the low nibble
 | 
			
		||||
				}
 | 
			
		||||
				if (((temp16 & 0xF0) > 0x90) || test_C()) {
 | 
			
		||||
					temp16 += 0x60;
 | 
			
		||||
					if (temp16 & 0xFF00) set_C(); //doesn't clear it if this clause is false
 | 
			
		||||
				}
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				reg8[A] = (uint8_t)temp16;
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xE6: //ANI # - AND immediate with A
 | 
			
		||||
				temp8 = i8080_read(reg_PC++);
 | 
			
		||||
				if ((reg8[A] | temp8) & 0x08) set_AC(); else clear_AC();
 | 
			
		||||
				reg8[A] &= temp8;
 | 
			
		||||
				clear_C();
 | 
			
		||||
				calc_SZP(reg8[A]);
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xF6: //ORI # - OR immediate with A
 | 
			
		||||
				reg8[A] |= i8080_read(reg_PC++);
 | 
			
		||||
				clear_AC();
 | 
			
		||||
				clear_C();
 | 
			
		||||
				calc_SZP(reg8[A]);
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xEE: //XRI # - XOR immediate with A
 | 
			
		||||
				reg8[A] ^= i8080_read(reg_PC++);
 | 
			
		||||
				clear_AC();
 | 
			
		||||
				clear_C();
 | 
			
		||||
				calc_SZP(reg8[A]);
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xDE: //SBI # - subtract immediate from A with borrow
 | 
			
		||||
				temp8 = i8080_read(reg_PC++);
 | 
			
		||||
				temp16 = (uint16_t)reg8[A] - (uint16_t)temp8 - (uint16_t)test_C();
 | 
			
		||||
				if (test_C()) calc_subAC_borrow(reg8[A], temp8); else calc_subAC(reg8[A], temp8);
 | 
			
		||||
				if (((temp16 & 0x00FF) >= reg8[A]) && (temp8 | test_C())) set_C(); else clear_C();
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				reg8[A] = (uint8_t)temp16;
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xFE: //CPI # - compare immediate with A
 | 
			
		||||
				temp8 = i8080_read(reg_PC++);
 | 
			
		||||
				temp16 = (uint16_t)reg8[A] - (uint16_t)temp8;
 | 
			
		||||
				if (((temp16 & 0x00FF) >= reg8[A]) && temp8) set_C(); else clear_C();
 | 
			
		||||
				calc_subAC(reg8[A], temp8);
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x07: //RLC - rotate A left
 | 
			
		||||
				if (reg8[A] & 0x80) set_C(); else clear_C();
 | 
			
		||||
				reg8[A] = (reg8[A] >> 7) | (reg8[A] << 1);
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x0F: //RRC - rotate A right
 | 
			
		||||
				if (reg8[A] & 0x01) set_C(); else clear_C();
 | 
			
		||||
				reg8[A] = (reg8[A] << 7) | (reg8[A] >> 1);
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x17: //RAL - rotate A left through carry
 | 
			
		||||
				temp8 = test_C();
 | 
			
		||||
				if (reg8[A] & 0x80) set_C(); else clear_C();
 | 
			
		||||
				reg8[A] = (reg8[A] << 1) | temp8;
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x1F: //RAR - rotate A right through carry
 | 
			
		||||
				temp8 = test_C();
 | 
			
		||||
				if (reg8[A] & 0x01) set_C(); else clear_C();
 | 
			
		||||
				reg8[A] = (reg8[A] >> 1) | (temp8 << 7);
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x2F: //CMA - compliment A
 | 
			
		||||
				reg8[A] = ~reg8[A];
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x3F: //CMC - compliment carry flag
 | 
			
		||||
				reg8[FLAGS] ^= 1;
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x37: //STC - set carry flag
 | 
			
		||||
				set_C();
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xC7: //RST n - restart (call n*8)
 | 
			
		||||
			case 0xD7:
 | 
			
		||||
			case 0xE7:
 | 
			
		||||
			case 0xF7:
 | 
			
		||||
			case 0xCF:
 | 
			
		||||
			case 0xDF:
 | 
			
		||||
			case 0xEF:
 | 
			
		||||
			case 0xFF:
 | 
			
		||||
				i8080_push(reg_PC);
 | 
			
		||||
				reg_PC = (uint16_t)((opcode >> 3) & 7) << 3;
 | 
			
		||||
				cycles -= 11;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xE9: //PCHL - jump to address in H:L
 | 
			
		||||
				reg_PC = reg16_HL;
 | 
			
		||||
				cycles -= 5;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xE3: //XTHL - swap H:L with top word on stack
 | 
			
		||||
				temp16 = i8080_pop();
 | 
			
		||||
				i8080_push(reg16_HL);
 | 
			
		||||
				write16_RP(2, temp16);
 | 
			
		||||
				cycles -= 18;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xF9: //SPHL - set SP to content of HL
 | 
			
		||||
				reg_SP = reg16_HL;
 | 
			
		||||
				cycles -= 5;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xDB: //IN p - read input port into A
 | 
			
		||||
				reg8[A] = i8080_inport(i8080_read(reg_PC++));
 | 
			
		||||
				cycles -= 10;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xD3: //OUT p - write A to output port
 | 
			
		||||
				i8080_outport(i8080_read(reg_PC++), reg8[A]);
 | 
			
		||||
				cycles -= 10;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xFB: //EI - enable interrupts
 | 
			
		||||
				INTE = 1;
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xF3: //DI - disbale interrupts
 | 
			
		||||
				INTE = 0;
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x76: //HLT - halt processor
 | 
			
		||||
				reg_PC--;
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x00: //NOP - no operation
 | 
			
		||||
#ifdef ALLOW_UNDEFINED
 | 
			
		||||
			case 0x10:
 | 
			
		||||
			case 0x20:
 | 
			
		||||
			case 0x30:
 | 
			
		||||
			case 0x08:
 | 
			
		||||
			case 0x18:
 | 
			
		||||
			case 0x28:
 | 
			
		||||
			case 0x38:
 | 
			
		||||
#endif
 | 
			
		||||
				cycles -= 4;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x40: case 0x50: case 0x60: case 0x70: //MOV D,S - move register to register
 | 
			
		||||
			case 0x41: case 0x51: case 0x61: case 0x71:
 | 
			
		||||
			case 0x42: case 0x52: case 0x62: case 0x72:
 | 
			
		||||
			case 0x43: case 0x53: case 0x63: case 0x73:
 | 
			
		||||
			case 0x44: case 0x54: case 0x64: case 0x74:
 | 
			
		||||
			case 0x45: case 0x55: case 0x65: case 0x75:
 | 
			
		||||
			case 0x46: case 0x56: case 0x66:
 | 
			
		||||
			case 0x47: case 0x57: case 0x67: case 0x77:
 | 
			
		||||
			case 0x48: case 0x58: case 0x68: case 0x78:
 | 
			
		||||
			case 0x49: case 0x59: case 0x69: case 0x79:
 | 
			
		||||
			case 0x4A: case 0x5A: case 0x6A: case 0x7A:
 | 
			
		||||
			case 0x4B: case 0x5B: case 0x6B: case 0x7B:
 | 
			
		||||
			case 0x4C: case 0x5C: case 0x6C: case 0x7C:
 | 
			
		||||
			case 0x4D: case 0x5D: case 0x6D: case 0x7D:
 | 
			
		||||
			case 0x4E: case 0x5E: case 0x6E: case 0x7E:
 | 
			
		||||
			case 0x4F: case 0x5F: case 0x6F: case 0x7F:
 | 
			
		||||
				reg = (opcode >> 3) & 7;
 | 
			
		||||
				reg2 = opcode & 7;
 | 
			
		||||
				i8080_write_reg8(reg, i8080_read_reg8(reg2));
 | 
			
		||||
				if ((reg == M) || (reg2 == M)) {
 | 
			
		||||
					cycles -= 7;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 5;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x06: //MVI D,# - move immediate to register
 | 
			
		||||
			case 0x16:
 | 
			
		||||
			case 0x26:
 | 
			
		||||
			case 0x36:
 | 
			
		||||
			case 0x0E:
 | 
			
		||||
			case 0x1E:
 | 
			
		||||
			case 0x2E:
 | 
			
		||||
			case 0x3E:
 | 
			
		||||
				reg = (opcode >> 3) & 7;
 | 
			
		||||
				i8080_write_reg8(reg, i8080_read(reg_PC++));
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 10;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 7;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x01: //LXI RP,# - load register pair immediate
 | 
			
		||||
			case 0x11:
 | 
			
		||||
			case 0x21:
 | 
			
		||||
			case 0x31:
 | 
			
		||||
				reg = (opcode >> 4) & 3;
 | 
			
		||||
				write_RP(reg, i8080_read(reg_PC), i8080_read(reg_PC + 1));
 | 
			
		||||
				reg_PC += 2;
 | 
			
		||||
				cycles -= 10;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x0A: //LDAX BC - load A indirect through BC
 | 
			
		||||
				reg8[A] = i8080_read(reg16_BC);
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x1A: //LDAX DE - load A indirect through DE
 | 
			
		||||
				reg8[A] = i8080_read(reg16_DE);
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x02: //STAX BC - store A indirect through BC
 | 
			
		||||
				i8080_write(reg16_BC, reg8[A]);
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x12: //STAX DE - store A indirect through DE
 | 
			
		||||
				i8080_write(reg16_DE, reg8[A]);
 | 
			
		||||
				cycles -= 7;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x04: //INR D - increment register
 | 
			
		||||
			case 0x14:
 | 
			
		||||
			case 0x24:
 | 
			
		||||
			case 0x34:
 | 
			
		||||
			case 0x0C:
 | 
			
		||||
			case 0x1C:
 | 
			
		||||
			case 0x2C:
 | 
			
		||||
			case 0x3C:
 | 
			
		||||
				reg = (opcode >> 3) & 7;
 | 
			
		||||
				temp8 = i8080_read_reg8(reg); //reg8[reg];
 | 
			
		||||
				calc_AC(temp8, 1);
 | 
			
		||||
				calc_SZP(temp8 + 1);
 | 
			
		||||
				i8080_write_reg8(reg, temp8 + 1); //reg8[reg]++;
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 10;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 5;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x05: //DCR D - decrement register
 | 
			
		||||
			case 0x15:
 | 
			
		||||
			case 0x25:
 | 
			
		||||
			case 0x35:
 | 
			
		||||
			case 0x0D:
 | 
			
		||||
			case 0x1D:
 | 
			
		||||
			case 0x2D:
 | 
			
		||||
			case 0x3D:
 | 
			
		||||
				reg = (opcode >> 3) & 7;
 | 
			
		||||
				temp8 = i8080_read_reg8(reg); //reg8[reg];
 | 
			
		||||
				calc_subAC(temp8, 1);
 | 
			
		||||
				calc_SZP(temp8 - 1);
 | 
			
		||||
				i8080_write_reg8(reg, temp8 - 1); //reg8[reg]--;
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 10;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 5;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x03: //INX RP - increment register pair
 | 
			
		||||
			case 0x13:
 | 
			
		||||
			case 0x23:
 | 
			
		||||
			case 0x33:
 | 
			
		||||
				reg = (opcode >> 4) & 3;
 | 
			
		||||
				write16_RP(reg, read_RP(reg) + 1);
 | 
			
		||||
				cycles -= 5;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x0B: //DCX RP - decrement register pair
 | 
			
		||||
			case 0x1B:
 | 
			
		||||
			case 0x2B:
 | 
			
		||||
			case 0x3B:
 | 
			
		||||
				reg = (opcode >> 4) & 3;
 | 
			
		||||
				write16_RP(reg, read_RP(reg) - 1);
 | 
			
		||||
				cycles -= 5;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x09: //DAD RP - add register pair to HL
 | 
			
		||||
			case 0x19:
 | 
			
		||||
			case 0x29:
 | 
			
		||||
			case 0x39:
 | 
			
		||||
				reg = (opcode >> 4) & 3;
 | 
			
		||||
				temp32 = (uint32_t)reg16_HL + (uint32_t)read_RP(reg);
 | 
			
		||||
				write16_RP(2, (uint16_t)temp32);
 | 
			
		||||
				if (temp32 & 0xFFFF0000) set_C(); else clear_C();
 | 
			
		||||
				cycles -= 10;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x80: //ADD S - add register or memory to A
 | 
			
		||||
			case 0x81:
 | 
			
		||||
			case 0x82:
 | 
			
		||||
			case 0x83:
 | 
			
		||||
			case 0x84:
 | 
			
		||||
			case 0x85:
 | 
			
		||||
			case 0x86:
 | 
			
		||||
			case 0x87:
 | 
			
		||||
				reg = opcode & 7;
 | 
			
		||||
				temp8 = i8080_read_reg8(reg);
 | 
			
		||||
				temp16 = (uint16_t)reg8[A] + (uint16_t)temp8;
 | 
			
		||||
				if (temp16 & 0xFF00) set_C(); else clear_C();
 | 
			
		||||
				calc_AC(reg8[A], temp8);
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				reg8[A] = (uint8_t)temp16;
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 7;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 4;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x88: //ADC S - add register or memory to A with carry
 | 
			
		||||
			case 0x89:
 | 
			
		||||
			case 0x8A:
 | 
			
		||||
			case 0x8B:
 | 
			
		||||
			case 0x8C:
 | 
			
		||||
			case 0x8D:
 | 
			
		||||
			case 0x8E:
 | 
			
		||||
			case 0x8F:
 | 
			
		||||
				reg = opcode & 7;
 | 
			
		||||
				temp8 = i8080_read_reg8(reg);
 | 
			
		||||
				temp16 = (uint16_t)reg8[A] + (uint16_t)temp8 + (uint16_t)test_C();
 | 
			
		||||
				if (test_C()) calc_AC_carry(reg8[A], temp8); else calc_AC(reg8[A], temp8);
 | 
			
		||||
				if (temp16 & 0xFF00) set_C(); else clear_C();
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				reg8[A] = (uint8_t)temp16;
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 7;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 4;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x90: //SUB S - subtract register or memory from A
 | 
			
		||||
			case 0x91:
 | 
			
		||||
			case 0x92:
 | 
			
		||||
			case 0x93:
 | 
			
		||||
			case 0x94:
 | 
			
		||||
			case 0x95:
 | 
			
		||||
			case 0x96:
 | 
			
		||||
			case 0x97:
 | 
			
		||||
				reg = opcode & 7;
 | 
			
		||||
				temp8 = i8080_read_reg8(reg);
 | 
			
		||||
				temp16 = (uint16_t)reg8[A] - (uint16_t)temp8;
 | 
			
		||||
				if (((temp16 & 0x00FF) >= reg8[A]) && temp8) set_C(); else clear_C();
 | 
			
		||||
				calc_subAC(reg8[A], temp8);
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				reg8[A] = (uint8_t)temp16;
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 7;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 4;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0x98: //SBB S - subtract register or memory from A with borrow
 | 
			
		||||
			case 0x99:
 | 
			
		||||
			case 0x9A:
 | 
			
		||||
			case 0x9B:
 | 
			
		||||
			case 0x9C:
 | 
			
		||||
			case 0x9D:
 | 
			
		||||
			case 0x9E:
 | 
			
		||||
			case 0x9F:
 | 
			
		||||
				reg = opcode & 7;
 | 
			
		||||
				temp8 = i8080_read_reg8(reg);
 | 
			
		||||
				temp16 = (uint16_t)reg8[A] - (uint16_t)temp8 - (uint16_t)test_C();
 | 
			
		||||
				if (test_C()) calc_subAC_borrow(reg8[A], temp8); else calc_subAC(reg8[A], temp8);
 | 
			
		||||
				if (((temp16 & 0x00FF) >= reg8[A]) && (temp8 | test_C())) set_C(); else clear_C();
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				reg8[A] = (uint8_t)temp16;
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 7;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 4;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xA0: //ANA S - AND register with A
 | 
			
		||||
			case 0xA1:
 | 
			
		||||
			case 0xA2:
 | 
			
		||||
			case 0xA3:
 | 
			
		||||
			case 0xA4:
 | 
			
		||||
			case 0xA5:
 | 
			
		||||
			case 0xA6:
 | 
			
		||||
			case 0xA7:
 | 
			
		||||
				reg = opcode & 7;
 | 
			
		||||
				temp8 = i8080_read_reg8(reg);
 | 
			
		||||
				if ((reg8[A] | temp8) & 0x08) set_AC(); else clear_AC();
 | 
			
		||||
				reg8[A] &= temp8;
 | 
			
		||||
				clear_C();
 | 
			
		||||
				calc_SZP(reg8[A]);
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 7;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 4;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xB0: //ORA S - OR register with A
 | 
			
		||||
			case 0xB1:
 | 
			
		||||
			case 0xB2:
 | 
			
		||||
			case 0xB3:
 | 
			
		||||
			case 0xB4:
 | 
			
		||||
			case 0xB5:
 | 
			
		||||
			case 0xB6:
 | 
			
		||||
			case 0xB7:
 | 
			
		||||
				reg = opcode & 7;
 | 
			
		||||
				reg8[A] |= i8080_read_reg8(reg);
 | 
			
		||||
				clear_AC();
 | 
			
		||||
				clear_C();
 | 
			
		||||
				calc_SZP(reg8[A]);
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 7;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 4;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xA8: //XRA S - XOR register with A
 | 
			
		||||
			case 0xA9:
 | 
			
		||||
			case 0xAA:
 | 
			
		||||
			case 0xAB:
 | 
			
		||||
			case 0xAC:
 | 
			
		||||
			case 0xAD:
 | 
			
		||||
			case 0xAE:
 | 
			
		||||
			case 0xAF:
 | 
			
		||||
				reg = opcode & 7;
 | 
			
		||||
				reg8[A] ^= i8080_read_reg8(reg);
 | 
			
		||||
				clear_AC();
 | 
			
		||||
				clear_C();
 | 
			
		||||
				calc_SZP(reg8[A]);
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 7;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 4;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xB8: //CMP S - compare register with A
 | 
			
		||||
			case 0xB9:
 | 
			
		||||
			case 0xBA:
 | 
			
		||||
			case 0xBB:
 | 
			
		||||
			case 0xBC:
 | 
			
		||||
			case 0xBD:
 | 
			
		||||
			case 0xBE:
 | 
			
		||||
			case 0xBF:
 | 
			
		||||
				reg = opcode & 7;
 | 
			
		||||
				temp8 = i8080_read_reg8(reg);
 | 
			
		||||
				temp16 = (uint16_t)reg8[A] - (uint16_t)temp8;
 | 
			
		||||
				if (((temp16 & 0x00FF) >= reg8[A]) && temp8) set_C(); else clear_C();
 | 
			
		||||
				calc_subAC(reg8[A], temp8);
 | 
			
		||||
				calc_SZP((uint8_t)temp16);
 | 
			
		||||
				if (reg == M) {
 | 
			
		||||
					cycles -= 7;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 4;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xC3: //JMP a - unconditional jump
 | 
			
		||||
#ifdef ALLOW_UNDEFINED
 | 
			
		||||
			case 0xCB:
 | 
			
		||||
#endif
 | 
			
		||||
				temp16 = (uint16_t)i8080_read(reg_PC) | (((uint16_t)i8080_read(reg_PC + 1)) << 8);
 | 
			
		||||
				reg_PC = temp16;
 | 
			
		||||
				cycles -= 10;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xC2: //Jccc - conditional jumps
 | 
			
		||||
			case 0xCA:
 | 
			
		||||
			case 0xD2:
 | 
			
		||||
			case 0xDA:
 | 
			
		||||
			case 0xE2:
 | 
			
		||||
			case 0xEA:
 | 
			
		||||
			case 0xF2:
 | 
			
		||||
			case 0xFA:
 | 
			
		||||
				temp16 = (uint16_t)i8080_read(reg_PC) | (((uint16_t)i8080_read(reg_PC + 1)) << 8);
 | 
			
		||||
				if (test_cond((opcode >> 3) & 7)) reg_PC = temp16; else reg_PC += 2;
 | 
			
		||||
				cycles -= 10;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xCD: //CALL a - unconditional call
 | 
			
		||||
#ifdef ALLOW_UNDEFINED
 | 
			
		||||
			case 0xDD:
 | 
			
		||||
			case 0xED:
 | 
			
		||||
			case 0xFD:
 | 
			
		||||
#endif
 | 
			
		||||
				temp16 = (uint16_t)i8080_read(reg_PC) | (((uint16_t)i8080_read(reg_PC + 1)) << 8);
 | 
			
		||||
				i8080_push(reg_PC + 2);
 | 
			
		||||
				reg_PC = temp16;
 | 
			
		||||
				cycles -= 17;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xC4: //Cccc - conditional calls
 | 
			
		||||
			case 0xCC:
 | 
			
		||||
			case 0xD4:
 | 
			
		||||
			case 0xDC:
 | 
			
		||||
			case 0xE4:
 | 
			
		||||
			case 0xEC:
 | 
			
		||||
			case 0xF4:
 | 
			
		||||
			case 0xFC:
 | 
			
		||||
				temp16 = (uint16_t)i8080_read(reg_PC) | (((uint16_t)i8080_read(reg_PC + 1)) << 8);
 | 
			
		||||
				if (test_cond((opcode >> 3) & 7)) {
 | 
			
		||||
					i8080_push(reg_PC + 2);
 | 
			
		||||
					reg_PC = temp16;
 | 
			
		||||
					cycles -= 17;
 | 
			
		||||
				} else {
 | 
			
		||||
					reg_PC += 2;
 | 
			
		||||
					cycles -= 11;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xC9: //RET - unconditional return
 | 
			
		||||
#ifdef ALLOW_UNDEFINED
 | 
			
		||||
			case 0xD9:
 | 
			
		||||
#endif
 | 
			
		||||
				reg_PC = i8080_pop();
 | 
			
		||||
				cycles -= 10;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xC0: //Rccc - conditional returns
 | 
			
		||||
			case 0xC8:
 | 
			
		||||
			case 0xD0:
 | 
			
		||||
			case 0xD8:
 | 
			
		||||
			case 0xE0:
 | 
			
		||||
			case 0xE8:
 | 
			
		||||
			case 0xF0:
 | 
			
		||||
			case 0xF8:
 | 
			
		||||
				if (test_cond((opcode >> 3) & 7)) {
 | 
			
		||||
					reg_PC = i8080_pop();
 | 
			
		||||
					cycles -= 11;
 | 
			
		||||
				} else {
 | 
			
		||||
					cycles -= 5;
 | 
			
		||||
				}
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xC5: //PUSH RP - push register pair on the stack
 | 
			
		||||
			case 0xD5:
 | 
			
		||||
			case 0xE5:
 | 
			
		||||
			case 0xF5:
 | 
			
		||||
				reg = (opcode >> 4) & 3;
 | 
			
		||||
				i8080_push(read_RP_PUSHPOP(reg));
 | 
			
		||||
				cycles -= 11;
 | 
			
		||||
				break;
 | 
			
		||||
			case 0xC1: //POP RP - pop register pair from the stack
 | 
			
		||||
			case 0xD1:
 | 
			
		||||
			case 0xE1:
 | 
			
		||||
			case 0xF1:
 | 
			
		||||
				reg = (opcode >> 4) & 3;
 | 
			
		||||
				write16_RP_PUSHPOP(reg, i8080_pop());
 | 
			
		||||
				cycles -= 10;
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
#ifndef ALLOW_UNDEFINED
 | 
			
		||||
			default:
 | 
			
		||||
				printf("UNRECOGNIZED INSTRUCTION @ %04Xh: %02X\n", reg_PC - 1, opcode);
 | 
			
		||||
				exit(0);
 | 
			
		||||
#endif
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cycles;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								plat/cpm/emu/intel_8080_emulator.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								plat/cpm/emu/intel_8080_emulator.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
#ifndef INTEL_I8080_EMULATOR_H
 | 
			
		||||
#define INTEL_I8080_EMULATOR_H
 | 
			
		||||
 | 
			
		||||
typedef enum
 | 
			
		||||
{
 | 
			
		||||
	B=0, C, D, E, H, L, M, A, FLAGS,
 | 
			
		||||
	AF, BC, DE, HL, SP, PC
 | 
			
		||||
}
 | 
			
		||||
reg_t;
 | 
			
		||||
 | 
			
		||||
extern uint8_t i8080_read(uint16_t addr);
 | 
			
		||||
extern void i8080_write(uint16_t addr, uint8_t value);
 | 
			
		||||
extern uint8_t i8080_inport(uint8_t port);
 | 
			
		||||
extern void i8080_outport(uint8_t port, uint8_t value);
 | 
			
		||||
 | 
			
		||||
extern uint8_t i8080_read_reg8(reg_t reg);
 | 
			
		||||
extern void i8080_write_reg8(reg_t reg, uint8_t value);
 | 
			
		||||
 | 
			
		||||
extern uint16_t i8080_read_reg16(reg_t reg);
 | 
			
		||||
extern void i8080_write_reg16(reg_t reg, uint16_t value);
 | 
			
		||||
 | 
			
		||||
extern int i8080_exec(int cycles);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										83
									
								
								plat/cpm/emu/main.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								plat/cpm/emu/main.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,83 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include "globals.h"
 | 
			
		||||
 | 
			
		||||
bool flag_enter_debugger = false;
 | 
			
		||||
char* const* user_command_line = NULL;
 | 
			
		||||
 | 
			
		||||
void fatal(const char* message, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	va_start(ap, message);
 | 
			
		||||
	fprintf(stderr, "fatal: ");
 | 
			
		||||
	vfprintf(stderr, message, ap);
 | 
			
		||||
	fprintf(stderr, "\n");
 | 
			
		||||
	exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void syntax(void)
 | 
			
		||||
{
 | 
			
		||||
	printf("cpm [<flags>] [command] [args]:\n");
 | 
			
		||||
	printf("  -h             this help\n");
 | 
			
		||||
	printf("  -d             enter debugger on startup\n");
 | 
			
		||||
	printf("  -p DRIVE=PATH  map a drive to a path (by default, A=.)\n");
 | 
			
		||||
	printf("If command is specified, a Unix file of that name will be loaded and\n");
 | 
			
		||||
	printf("injected directly into memory (it's not loaded through the CCP).\n");
 | 
			
		||||
	printf("Arguments may also be provided, but note that any FCBs aren't set up,\n");
 | 
			
		||||
	printf("so traditional Unix utilities probably won't work.\n");
 | 
			
		||||
	exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void parse_options(int argc, char* const* argv)
 | 
			
		||||
{
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
		switch (getopt(argc, argv, "hdp:"))
 | 
			
		||||
		{
 | 
			
		||||
			case -1:
 | 
			
		||||
				goto end_of_flags;
 | 
			
		||||
 | 
			
		||||
			case 'd':
 | 
			
		||||
				flag_enter_debugger = true;
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			case 'p':
 | 
			
		||||
			{
 | 
			
		||||
				if (!optarg[0] || (optarg[1] != '='))
 | 
			
		||||
					fatal("invalid syntax in drive assignment");
 | 
			
		||||
 | 
			
		||||
				uint8_t drive = toupper(optarg[0]) - 'A';
 | 
			
		||||
				const char* path = &optarg[2];
 | 
			
		||||
				file_set_drive(drive, path);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				syntax();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
end_of_flags:
 | 
			
		||||
	user_command_line = &argv[optind];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* const* argv)
 | 
			
		||||
{
 | 
			
		||||
	files_init();
 | 
			
		||||
	parse_options(argc, argv);
 | 
			
		||||
 | 
			
		||||
	emulator_init();
 | 
			
		||||
	bios_coldboot();
 | 
			
		||||
	
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
		emulator_run();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								plat/cpm/tests/build.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								plat/cpm/tests/build.lua
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
include("tests/plat/build.lua")
 | 
			
		||||
 | 
			
		||||
plat_testsuite {
 | 
			
		||||
    name = "tests",
 | 
			
		||||
    plat = "cpm",
 | 
			
		||||
    method = "plat/cpm/emu+emu"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -29,3 +29,8 @@ cprogram {
 | 
			
		|||
	srcs = { "./ed.c" }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cprogram {
 | 
			
		||||
	name = "objectify",
 | 
			
		||||
	srcs = { "./objectify.c" }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										33
									
								
								util/cmisc/objectify.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								util/cmisc/objectify.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
int main(int argc, const char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	size_t count = 0;
 | 
			
		||||
 | 
			
		||||
	if (argc != 2)
 | 
			
		||||
	{
 | 
			
		||||
		fprintf(stderr, "syntax: objectify <symbol>\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printf("#include <stdint.h>\n");
 | 
			
		||||
	printf("#include <unistd.h>\n");
 | 
			
		||||
	printf("const uint8_t %s_data[] = {", argv[1]);
 | 
			
		||||
	for (;;)
 | 
			
		||||
	{
 | 
			
		||||
		int c = getchar();
 | 
			
		||||
		if (c == -1)
 | 
			
		||||
			break;
 | 
			
		||||
		if ((count & 15) == 0)
 | 
			
		||||
			putchar('\n');
 | 
			
		||||
		printf("0x%02x, ", c);
 | 
			
		||||
		count++;
 | 
			
		||||
	}
 | 
			
		||||
	printf("\n};\n");
 | 
			
		||||
	printf("const size_t %s_len = %d;\n", argv[1], count);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue