From 87ace20633ba711243e336630e2c9a8546516598 Mon Sep 17 00:00:00 2001 From: Ian C Date: Sun, 20 Aug 2006 17:39:42 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r2, which included commits to RCS files with non-trunk default branches. --- Makefile | 69 +++ emma.c | 1180 ++++++++++++++++++++++++++++++++++++++++++ emucpm.z80 | 29 ++ prelim.com | Bin 0 -> 1280 bytes z80.c | 368 ++++++++++++++ z80.h | 218 ++++++++ z80_decode.c | 1576 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ z80_private.h | 209 ++++++++ zexall.com | Bin 0 -> 8704 bytes zexdoc.com | Bin 0 -> 8704 bytes 10 files changed, 3649 insertions(+) create mode 100644 Makefile create mode 100644 emma.c create mode 100644 emucpm.z80 create mode 100644 prelim.com create mode 100644 z80.c create mode 100644 z80.h create mode 100644 z80_decode.c create mode 100644 z80_private.h create mode 100644 zexall.com create mode 100644 zexdoc.com diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2cb9ffb --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +# z80 - Z80 emulation +# +# Copyright (C) 2006 Ian Cowburn (ianc@noddybox.demon.co.uk) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# ------------------------------------------------------------------------- +# +# $Id: Makefile,v 1.1.1.1 2006-08-20 17:39:42 ianc Exp $ +# + +# This CFLAGS assumes that gcc is being used. +# Simply comment out if not, and replace as needed. +# Other CFLAGS lines *are* required. +# +CFLAGS = -Wall -Werror -pedantic -ansi -O2 -finline-functions + +TARGET = emma + +SOURCE = z80.c \ + emma.c + +OBJECTS = z80.o \ + emma.o + +all: $(TARGET) emucpm.hex + +$(TARGET): $(OBJECTS) + ld -o $(TARGET) $(OBJECTS) + +emucpm.hex: emucpm.z80 + tpasm -P Z80 -o intel emucpm.hex emucpm.z80 + +clean: + rm -f $(TARGET) $(OBJECTS) core $(TARGET).exe emucpm.hex + +depend: + makedepend -- $(CFLAGS) -- $(SOURCE) + if test -e Makefile ; then rm -f Makefile.bak ; fi + +# DO NOT DELETE THIS LINE -- make depend depends on it + +z80.o: /usr/include/stddef.h /usr/include/sys/cdefs.h +z80.o: /usr/include/sys/_null.h /usr/include/sys/_types.h +z80.o: /usr/include/machine/_types.h /usr/include/stdlib.h +z80.o: /usr/include/stdio.h /usr/include/string.h /usr/include/strings.h +z80.o: /usr/include/ctype.h /usr/include/_ctype.h /usr/include/runetype.h +z80.o: /usr/include/limits.h /usr/include/sys/limits.h +z80.o: /usr/include/machine/_limits.h /usr/include/sys/syslimits.h z80.h +emma.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h +emma.o: /usr/include/sys/_null.h /usr/include/sys/_types.h +emma.o: /usr/include/machine/_types.h /usr/include/stdio.h +emma.o: /usr/include/string.h /usr/include/strings.h /usr/include/ctype.h +emma.o: /usr/include/_ctype.h /usr/include/runetype.h /usr/include/signal.h +emma.o: /usr/include/sys/signal.h /usr/include/sys/_sigset.h +emma.o: /usr/include/machine/signal.h /usr/include/machine/trap.h +emma.o: /usr/include/stdarg.h z80.h diff --git a/emma.c b/emma.c new file mode 100644 index 0000000..f2ae2c2 --- /dev/null +++ b/emma.c @@ -0,0 +1,1180 @@ +/* + + EMMA - Z80 testbed + + Copyright (C) 2003 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + +*/ +static const char id[]="$Id$"; + +#include +#include +#include +#include +#include +#include + +#include "z80.h" +#include "expr.h" + + +#define TRUE 1 +#define FALSE 0 + +#define HI(w) (((w)&0xff00)>>8) +#define LO(w) ((w)&0xff) +#define MK(h,l) (((Z80Word)(h))<<8|(l)) + +/* ---------------------------------------- GLOBALS +*/ +static Z80 *z80; +static Z80Byte mem[0x10000]; +static sig_atomic_t stop=FALSE; +static int quit=FALSE; + +static int quiet_run=FALSE; +static int single_step=FALSE; +static int mem_trace=FALSE; + +static Z80Word bottom=0; +static Z80Word top=0xffff; + +static Z80Val int_raise=0; + +static Z80Label *label=NULL; + +static FILE *log=NULL; + + +/* ---------------------------------------- PROTOS +*/ +static void Log(const char *format, ...); +static void Run(char *p); +static Z80Word Address(const char *p); +static const char *GetLabel(Z80 *z80, Z80Word addr); + + +/* ---------------------------------------- SIGNALS +*/ +static void SigInt(int sig) +{ + stop=TRUE; + signal(SIGINT,SigInt); +} + + +/* ---------------------------------------- COMMANDS AND ASSOC UTILS +*/ +static void Log(const char *format, ...) +{ + static char buff[4096]; + + va_list va; + + va_start(va,format); + + vsprintf(buff,format,va); + + printf("%s",buff); + + if (log) + fprintf(log,"%s",buff); + + va_end(va); +} + + +static void *Malloc(size_t len) +{ + void *n; + + n=malloc(len); + + if (!n) + { + fprintf(stderr,"malloc failed\n"); + exit(EXIT_FAILURE); + } + + return n; +} + +static void *Realloc(void *p, size_t len) +{ + void *n; + + n=realloc(p,len); + + if (!n) + { + fprintf(stderr,"realloc failed\n"); + exit(EXIT_FAILURE); + } + + return n; +} + +static char *StrCopy(const char *p) +{ + return strcpy(Malloc(strlen(p)+1),p); +} + +static const int ToHex(char c) +{ + c=toupper(c); + + if (c>='0' && c<='9') + return c-'0'; + + if (c>='A' && c<='F') + return c-'A'+10; + + return 0; +} + +static const char *FlagString(Z80Byte flag) +{ + static char s[]="76543210"; + static char c[]="SZ5H3PNC"; + int f; + + for(f=0;f<8;f++) + if (flag&(1<<(7-f))) + s[f]=c[f]; + else + s[f]='-'; + + return s; +} + + +static void DisplayState(void) +{ + Z80State s; + + Z80GetState(z80,&s); + + Log("A=%2.2x F=%s ",s.AF>>8,FlagString(s.AF&0xff)); + Log("BC=%4.4x DE=%4.4x HL=%4.4x ",s.BC,s.DE,s.HL); + Log("IX=%4.4x IY=%4.4x SP=%4.4x\n",s.IX,s.IY,s.SP); + Log("I=%2.2x IM=%2.2x R=%2.2x ",s.I,s.IM,s.R); + Log("IFF1=%2.2x IFF2=%2.2x\n",s.IFF1,s.IFF2); + Log("%4.4x: ",s.PC); + Log("%s\n",Z80Disassemble(z80,&s.PC)); +} + + +static int StrEq(const char *a, const char *b) +{ + while(*a && *b && tolower(*a)==tolower(*b)) + { + a++; + b++; + } + + if (*a || *b) + return FALSE; + else + return TRUE; +} + + +static int Expand(void *client, const char *p, long *res) +{ + Z80State s; + int ok=TRUE; + + Z80GetState((Z80 *)client,&s); + + if (StrEq(p,"AF")) + *res=s.AF; + else if (StrEq(p,"BC")) + *res=s.BC; + else if (StrEq(p,"DE")) + *res=s.DE; + else if (StrEq(p,"HL")) + *res=s.HL; + else if (StrEq(p,"IX")) + *res=s.IX; + else if (StrEq(p,"IY")) + *res=s.IY; + else if (StrEq(p,"SP")) + *res=s.SP; + else if (StrEq(p,"PC")) + *res=s.PC; + else if (StrEq(p,"A")) + *res=HI(s.AF); + else if (StrEq(p,"F")) + *res=LO(s.AF); + else if (StrEq(p,"B")) + *res=HI(s.BC); + else if (StrEq(p,"C")) + *res=LO(s.BC); + else if (StrEq(p,"D")) + *res=HI(s.DE); + else if (StrEq(p,"E")) + *res=LO(s.DE); + else if (StrEq(p,"H")) + *res=HI(s.HL); + else if (StrEq(p,"L")) + *res=LO(s.HL); + else if (StrEq(p,"AF_")) + *res=s.AF_; + else if (StrEq(p,"BC_")) + *res=s.BC_; + else if (StrEq(p,"DE_")) + *res=s.DE_; + else if (StrEq(p,"HL_")) + *res=s.HL_; + else if (StrEq(p,"A_")) + *res=HI(s.AF_); + else if (StrEq(p,"F_")) + *res=LO(s.AF_); + else if (StrEq(p,"B_")) + *res=HI(s.BC_); + else if (StrEq(p,"C_")) + *res=LO(s.BC_); + else if (StrEq(p,"D_")) + *res=HI(s.DE_); + else if (StrEq(p,"E_")) + *res=LO(s.DE_); + else if (StrEq(p,"H_")) + *res=HI(s.HL_); + else if (StrEq(p,"L_")) + *res=LO(s.HL_); + else if (StrEq(p,"IM")) + *res=s.IM; + else if (StrEq(p,"R")) + *res=s.R; + else if (StrEq(p,"IFF1")) + *res=s.IFF1; + else if (StrEq(p,"IFF2")) + *res=s.IFF2; + else if (p[0]=='@') + { + Z80Word n; + + n=Address(p+1); + *res=mem[n]; + } + else if (p[0]=='#') + { + Z80Word n; + + n=Address(p+1); + *res=MK(mem[n+1],mem[n]); + } + else /* Check for labels */ + { + int f; + + ok=FALSE; + + for(f=0;label && label[f].label;f++) + { + if (StrEq(p,label[f].label)) + { + *res=label[f].address; + ok=TRUE; + break; + } + } + } + + return ok; +} + + +static Z80Word Address(const char *p) +{ + long e=0; + + if (!ExprEval(p,&e,Expand,z80)) + Log("%s\n",ExprError()); + + return (Z80Word)e; +} + + +static Z80Val Val(const char *p) +{ + long e=0; + + if (!ExprEval(p,&e,Expand,z80)) + Log("%s\n",ExprError()); + + return (Z80Val)e; +} + + +static void DoHelp(int no, const char *arg[]) +{ + Log("? - help\n"); + Log("RETURN - Repeat last command if was d, x or n\n"); + Log("!cmd - run shell cmd\n"); + Log(". file - run commands from file\n"); + Log("> [file] - Set logging file (no argument to close)\n"); + Log("l file addr - load file at address\n"); + Log("i file - load intel HEX format file\n"); + Log("d [addr [len]] - disassemble from addr for len bytes\n"); + Log("x [addr [len]] - dump from addr for len bytes\n"); + Log("D file - set disassembly labels\n"); + Log("p [addr] - Set PC to addr [pop PC from stack]\n"); + Log("n - Single step from current PC\n"); + Log("r [addr] - Run from addr [current PC]\n"); + Log("u expr [addr] - Run from addr [current PC] until expr is !0\n"); + Log("z - Show Z80 state\n"); + Log("s - Toggle silent running\n"); + Log("S - Toggle single step\n"); + Log("w bottom top - Set range of writable memory\n"); + Log("I T-state - Raise an interrupt every T-state\n"); + Log("N - Raise an NMI\n"); + Log("T - Toggle memory tracing\n"); + Log("q - quit\n"); +} + + +static void DoScript(int no, const char *arg[]) +{ + FILE *fp; + char buff[1024]; + + if (no<2) + { + Log("Missing arguments\n"); + return; + } + + if (!(fp=fopen(arg[1],"r"))) + { + Log("Failed to open %s\n",arg[1]); + return; + } + + while(fgets(buff,sizeof buff,fp)) + { + Log("EMMA> %s",buff); + + if (buff[0] && buff[0]!='\n') + Run(buff); + } + + fclose(fp); +} + + +static void DoLog(int no, const char *arg[]) +{ + if (no<2) + { + if (log) + fclose(log); + + log=NULL; + return; + } + + if (!(log=fopen(arg[1],"w"))) + { + Log("Failed to open %s\n",arg[1]); + return; + } + else + setbuf(log,NULL); +} + + +static void DoLoad(int no, const char *arg[]) +{ + FILE *fp; + Z80Word addr; + int c; + int total; + + if (no<3) + { + Log("Missing arguments\n"); + return; + } + + addr=Address(arg[2]); + + if (!(fp=fopen(arg[1],"rb"))) + { + Log("Failed to open %s\n",arg[1]); + return; + } + + total=0; + + while((c=getc(fp))!=EOF) + { + mem[addr++]=(Z80Byte)c; + total++; + } + + Log("Read 0x%4.4x bytes from %s\n",total,arg[1]); + + fclose(fp); +} + + +static void DoIntel(int no, const char *arg[]) +{ + FILE *fp; + char buff[1024]; + int done=FALSE; + + if (no<2) + { + Log("Missing arguments\n"); + return; + } + + if (!(fp=fopen(arg[1],"r"))) + { + Log("Failed to open %s\n",arg[1]); + return; + } + + while(!done) + { + if (!fgets(buff,sizeof buff,fp)) + { + Log("Missing EOF record\n"); + done=TRUE; + } + + if (!done && buff[0]!=':') + { + Log("Invalid Intel HEX file\n"); + done=TRUE; + } + + if (!done && buff[8]=='1') + { + Log("\n"); + done=TRUE; + } + + if (!done) + { + Z80Word addr; + int len; + int f; + + len=ToHex(buff[1])<<4|ToHex(buff[2]); + + addr=ToHex(buff[3])<<12|ToHex(buff[4])<<8| + ToHex(buff[5])<<4|ToHex(buff[6]); + + Log("Segment of 0x%2.2x bytes at address 0x%4.4x\n",len,addr); + + for(f=0;f0) + { + if ((count%8)==0) + Log("0x%4.4x: ",addr); + + Log(" 0x%2.2x",mem[addr]); + + if (isprint(mem[addr])) + asc[count%8]=mem[addr]; + else + asc[count%8]='.'; + + asc[(count%8)+1]=0; + + addr++; + count++; + + if ((count%8)==0) + Log(" %s\n",asc); + + len--; + } + + if (count%8) + { + while(count%8) + { + Log(" "); + count++; + } + + Log(" %s\n",asc); + } + + last=addr; +} + + +static void DoLabel(int no, const char *arg[]) +{ + FILE *fp; + char buff[1024]; + int f; + + if (no<2) + { + Log("Missing arguments\n"); + return; + } + + if (!(fp=fopen(arg[1],"r"))) + { + Log("Failed to open %s\n",arg[1]); + return; + } + + for(f=0;label && label[f].label;f++) + free((void *)label[f].label); + + free(label); + label=NULL; + f=0; + + while(fgets(buff,sizeof buff,fp)) + { + char *name=NULL; + char *addr=NULL; + + if (strlen(buff) && buff[strlen(buff)-1]=='\n') + buff[strlen(buff)-1]=0; + + name=strtok(buff," \t"); + + if (name) + addr=strtok(NULL," \t"); + + if (name && addr) + { + label=Realloc(label,sizeof(*label)*(f+2)); + + label[f].label=StrCopy(name); + label[f].address=Address(addr); + + f++; + } + } + + if (label) + label[f].label=NULL; + + fclose(fp); +} + + +static void DoStep(int no, const char *arg[]) +{ + DisplayState(); + Z80SingleStep(z80); + stop=FALSE; +} + + +static void DoSetPC(int no, const char *arg[]) +{ + Z80State s; + + Z80GetState(z80,&s); + + if (no<2) + { + s.PC=MK(mem[s.SP+1],mem[s.SP]); + s.SP+=2; + DisplayState(); + } + else + s.PC=Address(arg[1]); + + Z80SetState(z80,&s); +} + + +static void DoRun(int no, const char *arg[]) +{ + if (no>1) + { + Z80State s; + + Z80GetState(z80,&s); + s.PC=Address(arg[1]); + Z80SetState(z80,&s); + } + + while(!stop) + { + if (!quiet_run) + DisplayState(); + + Z80SingleStep(z80); + + if (single_step) + { + Log(""); + fflush(stdout); + getchar(); + } + } + + stop=FALSE; +} + + +static void DoUntil(int no, const char *arg[]) +{ + if (no<2) + { + Log("Missing arguments\n"); + return; + } + + if (no>2) + { + Z80State s; + + Z80GetState(z80,&s); + s.PC=Address(arg[2]); + Z80SetState(z80,&s); + } + + while(!stop) + { + long e; + + if (!ExprEval(arg[1],&e,Expand,z80)) + Log("%s\n",ExprError()); + + if (e) + { + Log("%s TRUE\n",arg[1]); + stop=TRUE; + } + else + { + if (!quiet_run) + DisplayState(); + + Z80SingleStep(z80); + } + } + + stop=FALSE; +} + + +static void DoShow(int no, const char *arg[]) +{ + DisplayState(); +} + + +static void DoSilent(int no, const char *arg[]) +{ + quiet_run=!quiet_run; + + if (quiet_run) + Log("Quiet running set\n"); + else + Log("Noisy running set\n"); +} + + +static void DoRange(int no, const char *arg[]) +{ + if (no<3) + { + Log("Missing arguments\n"); + return; + } + + bottom=Address(arg[1]); + top=Address(arg[2]); + + Log("Writable memory between 0x%4.4x and 0x%4.4x\n",bottom,top); +} + + +static void DoSingle(int no, const char *arg[]) +{ + single_step=!single_step; + + if (single_step) + Log("Single step running set\n"); + else + Log("Continuous running set\n"); +} + + +static void DoIntRaise(int no, const char *arg[]) +{ + if (no<1) + { + Log("Missing arguments\n"); + return; + } + + int_raise=Val(arg[1]); + + if (int_raise) + Log("Interrupt every %lu T-states\n",int_raise); + else + Log("Interrupt disabled\n"); +} + + +static void DoNMIRaise(int no, const char *arg[]) +{ + Z80NMI(z80); +} + + +static void DoMemTrace(int no, const char *arg[]) +{ + mem_trace=!mem_trace; + + if (mem_trace) + Log("Memory tracing on\n"); + else + Log("Memory tracing off\n"); +} + + +static void DoQuit(int no, const char *arg[]) +{ + quit=TRUE; +} + + +static void Run(char *cmd) +{ + static char last_cmd[1024]={0}; + static const struct + { + const char *cmd; + const int store; + void (*handler)(int no, const char *arg[]); + } cmd_table[]= + { + {"?", FALSE, DoHelp}, + {".", FALSE, DoScript}, + {">", FALSE, DoLog}, + {"l", FALSE, DoLoad}, + {"i", FALSE, DoIntel}, + {"d", TRUE, DoDis}, + {"x", TRUE, DoDump}, + {"D", FALSE, DoLabel}, + {"p", FALSE, DoSetPC}, + {"n", TRUE, DoStep}, + {"r", FALSE, DoRun}, + {"u", FALSE, DoUntil}, + {"z", FALSE, DoShow}, + {"s", FALSE, DoSilent}, + {"S", FALSE, DoSingle}, + {"w", FALSE, DoRange}, + {"I", FALSE, DoIntRaise}, + {"N", FALSE, DoNMIRaise}, + {"T", FALSE, DoMemTrace}, + {"q", FALSE, DoQuit}, + {NULL, FALSE, NULL} + }; + + char buff[1024]; + const char *arg[4]; + int use_last=FALSE; + int no; + + if (cmd[0]!='\n') + { + strcpy(buff,cmd); + } + else + { + strcpy(buff,last_cmd); + use_last=TRUE; + } + + if (buff[0]=='#') + return; + + if (buff[0]=='!') + system(buff+1); + else + { + no=0; + + arg[0]=strtok(buff," \t\n"); + + while(no<3 && arg[no]) + arg[++no]=strtok(NULL," \t\n"); + + if (arg[0]) + { + int f; + int done=FALSE; + + for(f=0;!done && cmd_table[f].cmd;f++) + if (cmd_table[f].cmd[0]==arg[0][0]) + { + if (cmd_table[f].store) + { + if (!use_last) + strcpy(last_cmd,cmd); + } + else + last_cmd[0]=0; + + done=TRUE; + cmd_table[f].handler(no,arg); + } + + if (!done) + Log("Syntax error\n"); + } + } +} + + +/* ---------------------------------------- MEMORY +*/ +static Z80Byte ReadMem(Z80 *z80, Z80Word addr) +{ + if (mem_trace) + Log("PEEK(0x%4.4x)=0x%2.2x\n",addr,mem[addr]); + + return mem[addr]; +} + + +static Z80Word ReadMemWord(Z80 *z80, Z80Word addr) +{ + Z80Word w; + + w=MK(mem[addr+1],mem[addr]); + + if (mem_trace) + Log("PEEKW(0x%4.4x)=0x%4.4x\n",addr,w); + + return w; +} + + +static Z80Byte ReadMemDis(Z80 *z80, Z80Word addr) +{ + return mem[addr]; +} + + +static void WriteMem(Z80 *z80, Z80Word addr, Z80Byte val) +{ + if (addr>=bottom && addr<=top) + { + mem[addr]=val; + + if (mem_trace) + Log("POKE(0x%4.4x)=0x%2.2x\n",addr,mem[addr]); + } + else + { + if (mem_trace) + Log("POKE(0x%4.4x)=0x%2.2x [BLOCKED]\n",addr,mem[addr]); + } +} + + +static void WriteMemWord(Z80 *z80, Z80Word addr, Z80Word val) +{ + static const char *block_str[3]={""," [ONE BYTE BLOCKED]"," [BLOCKED]"}; + Z80Byte lo,hi; + int block=0; + + lo=LO(val); + hi=HI(val); + + if (addr>=bottom && addr<=top) + mem[addr]=lo; + else + block++; + + addr++; + + if (addr>=bottom && addr<=top) + mem[addr]=hi; + else + block++; + + if (mem_trace) + Log("POKEW(0x%4.4x)=0x%4.4x%s\n",addr,val,block_str[block]); +} + + +static Z80Byte ReadPort(Z80 *z80, Z80Word addr) +{ + Z80Byte b=0xff; + Z80Word ptr; + Z80State s; + char expr[1024]; + char *p; + + Z80GetState(z80,&s); + + switch(addr&0xff) + { + case 0x80: + ptr=MK(mem[1],mem[0]); + + p=expr; + + while(mem[ptr]) + *p++=mem[ptr++]; + + *p=0; + + b=(Z80Byte)Address(expr); + + /* Log("%s -> %u\n",expr,b); */ + + break; + + case 0x81: + ptr=s.DE; + + p=expr; + + while(mem[ptr]) + *p++=mem[ptr++]; + + *p=0; + + b=(Z80Byte)Address(expr); + + break; + + default: + Log("Read from port 0x%4.4x\n",(int)addr); + break; + } + + return b; +} + + +static void WritePort(Z80 *z80, Z80Word addr, Z80Byte val) +{ + Z80State s; + + Z80GetState(z80,&s); + + switch(addr&0xff) + { + case 0x80: + putchar(val); + fflush(stdout); + break; + + case 0x81: + Log("Stop requested\n"); + stop=TRUE; + break; + + case 0x82: + while(mem[s.DE]!='$') + { + if (isspace(mem[s.DE]) || isprint(mem[s.DE])) + putchar(mem[s.DE]); + + s.DE++; + } + fflush(stdout); + break; + + case 0x83: + while(mem[s.DE]) + { + putchar(mem[s.DE]); + s.DE++; + } + fflush(stdout); + break; + + case 0x84: + DisplayState(); + break; + + default: + Log("Wrote 0x%2.2x to port 0x%4.4x\n",(int)val,(int)addr); + break; + } +} + + +static const char *GetLabel(Z80 *z80, Z80Word addr) +{ + int f; + + for(f=0;label && label[f].label;f++) + if (label[f].address==addr) + return label[f].label; + + return NULL; +} + + +static int Halt(Z80 *z80, Z80Val v) +{ + Log("Processor halted\n"); + stop=TRUE; + return TRUE; +} + + +static int IntCheck(Z80 *z80, Z80Val v) +{ + if (int_raise && v>int_raise) + { + Log("Interrupt raised\n"); + Z80Interrupt(z80,0xff); + Z80ResetCycles(z80,v-int_raise); + } + + return TRUE; +} + + +/* ---------------------------------------- MAIN +*/ +int main(int argc, char *argv[]) +{ + const char *autoarg[2]={".","auto"}; + char buff[1024]; + + z80=Z80Init(WriteMem,ReadMem,WriteMemWord,ReadMemWord, + WritePort,ReadPort,ReadMemDis); + + if (!z80) + { + printf("Failed to initialise Z80\n"); + return EXIT_FAILURE; + } + + Z80LodgeCallback(z80,eZ80_Halt,Halt); + Z80LodgeCallback(z80,eZ80_Instruction,IntCheck); + + signal(SIGINT,SigInt); + + if (argc>1) + { + if (strcmp(argv[1],"-a")!=0) + { + autoarg[1]=argv[1]; + DoScript(2,autoarg); + } + } + else + DoScript(2,autoarg); + + while(!quit) + { + Log("EMMA> "); + fflush(stdout); + + if (!fgets(buff,sizeof buff,stdin)) + { + quit=TRUE; + continue; + } + + Run(buff); + } + + return EXIT_SUCCESS; +} + + +/* END OF FILE */ + + diff --git a/emucpm.z80 b/emucpm.z80 new file mode 100644 index 0000000..befecee --- /dev/null +++ b/emucpm.z80 @@ -0,0 +1,29 @@ +; +; Quick hack to emulate some CPM bdos calls (well, enough to make it work) +; + org 0 + halt + + org 5 + jp cpm + + org $ff00 +cpm: + ld a,9 + cp c + jr z,print_string + ld a,2 + cp c + jr z,print_char + ret + +print_string: + ld bc,$0082 + out (c),a + ret + +print_char: + ld a,e + ld bc,$0080 + out (c),a + ret diff --git a/prelim.com b/prelim.com new file mode 100644 index 0000000..e5c82b5 Binary files /dev/null and b/prelim.com differ diff --git a/z80.c b/z80.c new file mode 100644 index 0000000..6745fe1 --- /dev/null +++ b/z80.c @@ -0,0 +1,368 @@ +/* + + z80 - Z80 Emulator + + Copyright (C) 2006 Ian Cowburn + + Some of the opcode routines are based on the Z80 emulation from YAZE, + Copyright (c) 1995 Frank D. Cringle. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + $Id$ + + Z80 + +*/ +#include "z80.h" +#include "z80_private.h" + +static const char ident[]="$Id$"; + + +/* ---------------------------------------- LIBRARY VARIABLES +*/ +static Z80Byte PSZtable[512]; +static Z80Byte SZtable[512]; +static Z80Byte Ptable[512]; +static Z80Byte Stable[512]; +static Z80Byte Ztable[512]; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ +static void InitTables() +{ + static int init=FALSE; + Z80Reg r; + Z80Word f; + + if (init) + return; + + init=TRUE; + + /* Initialise flag tables + */ + for(f=0;f<256;f++) + { + Z80Byte p,z,s; + int b; + + p=0; + + for(b=0;b<8;b++) + if (f&(1<raise) + { + if (cpu->nmi) + { + if (cpu->halt) + { + cpu->halt=FALSE; + CALLBACK(eZ80_Halt,0); + cpu->PC++; + } + + TSTATE(2); + cpu->IFF1=0; + cpu->nmi=FALSE; + PUSH(PC); + cpu->PC=0x66; + } + else if (cpu->IFF1) + { + if (cpu->halt) + { + cpu->halt=FALSE; + CALLBACK(eZ80_Halt,0); + cpu->PC++; + } + + TSTATE(2); + + switch(cpu->IM) + { + default: + case 0: + INC_R; + Z80_Decode(cpu,cpu->devbyte); + return; + break; + + case 1: + PUSH(PC); + cpu->PC=0x38; + break; + + case 2: + PUSH(PC); + cpu->PC=(Z80Word)cpu->I*256+cpu->devbyte; + break; + } + } + + cpu->raise=FALSE; + } +} + + +/* ---------------------------------------- INTERFACES +*/ + +Z80 *Z80Init(Z80Memory memory, + Z80MemoryControl memcontrol, + Z80WritePort write_port, + Z80ReadPort read_port) +{ + Z80 *cpu; + int f; + int r; + + InitTables(); + + if (!write_byte || !read_byte || !write_word || !read_word) + return NULL; + + cpu=malloc(sizeof *cpu); + + if (cpu) + { + cpu->memory=memory; + cpu->memctrl=memcontrol; + cpu->pread=read_port; + cpu->pwrite=write_port; + + for(f=0;fcallback[f][r]=NULL; + + Z80Reset(cpu); + } + + return cpu; +} + + +void Z80Reset(Z80 *cpu) +{ + cpu->cycle=0; + cpu->PC=0; + + cpu->A=0xff; + cpu->F=0xff; + cpu->BC=0xffff; + cpu->DE=0xffff; + cpu->HL=0xffff; + cpu->AF_=0xffff; + cpu->BC_=0xffff; + cpu->DE_=0xffff; + cpu->HL_=0xffff; + + cpu->IX=0xffff; + cpu->IY=0xffff; + + cpu->SP=0xffff; + cpu->IFF1=0; + cpu->IFF2=0; + cpu->IM=0; + cpu->I=0; + cpu->R=0; + cpu->halt=0; + + cpu->raise=FALSE; + cpu->nmi=FALSE; +} + + +void Z80ResetCycles(Z80 *cpu, Z80Val cycles) +{ + cpu->cycle=cycles; +} + + +int Z80LodgeCallback(Z80 *cpu, Z80CallbackReason reason, Z80Callback callback) +{ + int f; + + for(f=0;fcallback[reason][f]) + { + cpu->callback[reason][f]=callback; + return TRUE; + } + + return FALSE; +} + + +void Z80RemoveCallback(Z80 *cpu, Z80CallbackReason reason, Z80Callback callback) +{ + int f; + + for(f=0;fcallback[reason][f]==callback) + cpu->callback[reason][f]=NULL; +} + + +void Z80Interrupt(Z80 *cpu, Z80Byte devbyte) +{ + cpu->raise=TRUE; + cpu->devbyte=devbyte; + cpu->nmi=FALSE; +} + + +void Z80NMI(Z80 *cpu) +{ + cpu->raise=TRUE; + cpu->nmi=TRUE; +} + + +int Z80SingleStep(Z80 *cpu) +{ + cpu->last_cb=TRUE; + cpu->shift=0; + cpu->use_cb_off=FALSE; + cpu->cb_off=0; + + Z80_CheckInterrupt(cpu); + + CALLBACK(eZ80_Instruction,cpu->cycle); + + INC_R; + + Z80_Decode(cpu,cpu->memory[cpu->PC++]); + + return cpu->last_cb; +} + + +void Z80Exec(Z80 *cpu) +{ + while (Z80SingleStep(cpu)); +} + + +void Z80GetState(Z80 *cpu, Z80State *state) +{ +#define COPY(a) state->a=cpu->a + COPY(cycle); + + SET_HI(state->AF,cpu->A); + SET_LO(state->AF,cpu->F); + + COPY(BC); + COPY(DE); + COPY(HL); + + COPY(AF_); + COPY(BC_); + COPY(DE_); + COPY(HL_); + + COPY(IX); + COPY(IY); + + COPY(SP); + COPY(PC); + + COPY(IFF1); + COPY(IFF2); + COPY(IM); + COPY(I); + COPY(R); +#undef COPY +} + + +void Z80SetState(Z80 *cpu, Z80State *state) +{ +#define COPY(a) cpu->a=state->a + COPY(cycle); + + cpu->A=GET_HI(state->AF); + cpu->F=GET_LO(state->AF); + + COPY(AF); + COPY(BC); + COPY(DE); + COPY(HL); + + COPY(AF_); + COPY(BC_); + COPY(DE_); + COPY(HL_); + + COPY(IX); + COPY(IY); + + COPY(SP); + COPY(PC); + + COPY(IFF1); + COPY(IFF2); + COPY(IM); + COPY(I); + COPY(R); +#undef COPY +} + + +Z80Val Z80Cycles(Z80 *cpu) +{ + return cpu->cycle; +} + +/* END OF FILE */ diff --git a/z80.h b/z80.h new file mode 100644 index 0000000..814b32d --- /dev/null +++ b/z80.h @@ -0,0 +1,218 @@ +/* + + z80 - Z80 emulation + + Copyright (C) 2006 Ian Cowburn + + Some of the opcode routines are based on the Z80 emulation from YAZE, + Copyright (c) 1995 Frank D. Cringle. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + $Id$ + +*/ + +#ifndef Z80_H +#define Z80_H "$Id$" + +/* ---------------------------------------- TYPES +*/ + +/* The processor +*/ +struct Z80; +typedef struct Z80 Z80; + + +/* Large unsigned type +*/ +typedef unsigned long Z80Val; + + +/* 8-bit type +*/ +typedef unsigned char Z80Byte; + + +/* 8-bit signed type +*/ +typedef signed char Z80Relative; + + +/* 16-bit type +*/ +typedef unsigned short Z80Word; + +/* Memory +*/ +typedef Z80Byte Z80Memory[0x10000]; + + +/* Memory control - a TRUE indicates that the 256-byte page is writeable. +*/ +typedef int Z80MemoryControl[256]; + + +/* Interfaces needed to handle ports (IN/OUT commands) +*/ +typedef Z80Byte (*Z80ReadPort)(Z80 *cpu, Z80Word address); +typedef void (*Z80WritePort)(Z80 *cpu, Z80Word address, Z80Byte value); + + +/* Callback. Callback should return TRUE for processing to continue. +*/ +typedef int (*Z80Callback)(Z80 *cpu, Z80Val data); + + +/* Callback reasons + + eZ80_Instruction Called before the initial fetch for an instruction + (called just to once no matter how many bytes the + instruction is made up of). + + eZ80_EDHook Called when an undefined ED opcode is executed. + + eZ80_Halt Called when the HALT instruction is hit and released. + + eZ80_RETI Called when the RETI instruction is executed +*/ +typedef enum +{ + eZ80_Instruction, /* data = no cycles since reset */ + eZ80_EDHook, /* data = byte after ED opcode (only for NOP opcodes) */ + eZ80_Halt, /* data = 1 halt raised, 0 halt cleared by int */ + eZ80_RETI, /* data = ignored */ + eZ80_NO_CALLBACK /* leave at end */ +} Z80CallbackReason; + + +/* Get/settable state of the Z80 +*/ +typedef struct +{ + Z80Word PC; + Z80Word SP; + + Z80Val cycle; + + Z80Word AF; + Z80Word BC; + Z80Word DE; + Z80Word HL; + + Z80Word AF_; /* Alternate registers */ + Z80Word BC_; + Z80Word DE_; + Z80Word HL_; + + Z80Word IX; + Z80Word IY; + + Z80Byte IFF1; + Z80Byte IFF2; + Z80Byte IM; + Z80Byte I; + Z80Byte R; +} Z80State; + + +/* Flags in the F register +*/ +typedef enum +{ + eZ80_Carry =0x01, + eZ80_Neg =0x02, + eZ80_PV =0x04, + eZ80_Hidden3 =0x08, + eZ80_HalfCarry =0x10, + eZ80_Hidden5 =0x20, + eZ80_Zero =0x40, + eZ80_Sign =0x80 +} Z80FlagRegister; + + +/* ---------------------------------------- INTERFACES +*/ + + +/* Initialises the processor. +*/ +Z80 *Z80Init(Z80Memory memory, + Z80MemoryControl memcontrol, + Z80WritePort write_port, + Z80ReadPort read_port); + + +/* Resets the processor. +*/ +void Z80Reset(Z80 *cpu); + + +/* Sets the cycle count to the specified count +*/ +void Z80ResetCycles(Z80 *cpu, Z80Val cycles); + + +/* Lodge a callback to be invoked after special events. Returns FALSE + if the callback couldn't be lodged (there is a max of 10 callbacks per + reason). +*/ +int Z80LodgeCallback(Z80 *cpu, + Z80CallbackReason reason, + Z80Callback callback); + + +/* Remove a callback. Does nothing if reason was not lodged with + Z80LodgeCallback() +*/ +void Z80RemoveCallback(Z80 *cpu, + Z80CallbackReason reason, + Z80Callback callback); + + +/* Cause an interrupt before the next opcode. + devbyte is the byte generated by the device (if any). +*/ +void Z80Interrupt(Z80 *cpu, Z80Byte devbyte); + + +/* Cause an NMI +*/ +void Z80NMI(Z80 *cpu); + + +/* Execute a single instruction. Returns FALSE if any callback returned + FALSE. +*/ +int Z80SingleStep(Z80 *cpu); + + +/* Executes until a callback returns FALSE (never returns otherwise) +*/ +void Z80Exec(Z80 *cpu); + + +/* Interrogate the state of the Z80 +*/ +Z80Val Z80Cycles(Z80 *cpu); +void Z80GetState(Z80 *cpu, Z80State *state); +void Z80SetState(Z80 *cpu, const Z80State *state); + +#endif + +/* END OF FILE */ diff --git a/z80_decode.c b/z80_decode.c new file mode 100644 index 0000000..c323d1f --- /dev/null +++ b/z80_decode.c @@ -0,0 +1,1576 @@ +/* + + z80 - Z80 Emulator + + Copyright (C) 2006 Ian Cowburn + + Some of the opcode routines are based on the Z80 emulation from YAZE, + Copyright (c) 1995 Frank D. Cringle. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + $Id$ + + Z80 + +*/ +#include "z80_private.h" + +/* ---------------------------------------- ARITHMETIC OPS +*/ +#define ADD_8_BIT(REG,VAL) \ +do \ +{ \ + Z80Word w; \ + w=REG+(Z80Word)VAL; \ + cpu->F=SZtable[w]; \ + if ((REG^w^VAL)&H_Z80) cpu->F|=H_Z80; \ + if ((VAL^REG^0x80)&(REG^w)&0x80) cpu->F|=P_Z80; \ + SETHIDDEN(w); \ + REG=w; \ +} while(0) + + +#define ADC_8_BIT(REG,VAL) \ +do \ +{ \ + Z80Word w; \ + w=(REG+(Z80Word)VAL+CARRY)&0x1ff; \ + cpu->F=SZtable[w]; \ + if ((REG^w^VAL)&H_Z80) cpu->F|=H_Z80; \ + if ((VAL^REG^0x80)&(REG^w)&0x80) cpu->F|=P_Z80; \ + SETHIDDEN(w); \ + REG=w; \ +} while(0) + + +#define SUB_8_BIT(REG,VAL) \ +do \ +{ \ + Z80Word w; \ + w=(REG-(Z80Word)VAL)&0x1ff; \ + cpu->F=SZtable[w]|N_Z80; \ + if ((REG^w^VAL)&H_Z80) cpu->F|=H_Z80; \ + if ((VAL^REG^0x80)&(REG^w)&0x80) cpu->F|=P_Z80; \ + SETHIDDEN(w); \ + REG=w; \ +} while(0) + + +#define CMP_8_BIT(REG,VAL) \ +do \ +{ \ + Z80Word w; \ + w=(REG-(Z80Word)VAL)&0x1ff; \ + cpu->F=SZtable[w]|N_Z80; \ + if ((REG^w^VAL)&H_Z80) cpu->F|=H_Z80; \ + if ((VAL^REG^0x80)&(REG^w)&0x80) cpu->F|=P_Z80; \ + SETHIDDEN(VAL); \ +} while(0) + + +#define SBC_8_BIT(REG,VAL) \ +do \ +{ \ + Z80Word w; \ + w=(REG-(Z80Word)VAL-CARRY)&0x1ff; \ + cpu->F=SZtable[w]|N_Z80; \ + if ((REG^w^VAL)&H_Z80) cpu->F|=H_Z80; \ + if ((VAL^REG^0x80)&(REG^w)&0x80) cpu->F|=P_Z80; \ + SETHIDDEN(w); \ + REG=w; \ +} while(0) + + +#define ADD_16_BIT(REG,VAL) \ +do \ +{ \ + Z80Val w; \ + w=REG+(Z80Val)VAL; \ + cpu->F&=(S_Z80|Z_Z80|V_Z80); \ + if (w>0xffff) cpu->F|=C_Z80; \ + if ((REG^w^VAL)&0x1000) cpu->F|=H_Z80; \ + SETHIDDEN(w>>8); \ + REG=w; \ +} while(0) + + +#define ADC_16_BIT(REG,VAL) \ +do \ +{ \ + Z80Val w; \ + w=REG+(Z80Val)VAL+CARRY; \ + cpu->F&=(S_Z80|Z_Z80|V_Z80); \ + if (w>0xffff) cpu->F|=C_Z80; \ + if ((REG^w^VAL)&0x1000) cpu->F|=H_Z80; \ + SETHIDDEN(w>>8); \ + REG=w; \ +} while(0) + + +#define SBC_16_BIT(REG,VAL) \ +do \ +{ \ + Z80Val w; \ + w=REG-(Z80Val)VAL-CARRY; \ + cpu->F=N_Z80; \ + if (w&0x8000) cpu->F|=S_Z80; \ + if ((w&0xffff)==0) cpu->F|=Z_Z80; \ + if (w>0xffff) cpu->F|=C_Z80; \ + if ((REG^w^VAL)&0x1000) cpu->F|=H_Z80; \ + if ((VAL^REG)&(VAL^w)&0x8000) cpu->F|=P_Z80; \ + SETHIDDEN(w>>8); \ + REG=w; \ +} while(0) + + +#define INC8(REG) \ +do \ +{ \ + REG++; \ + cpu->F=CARRY|SZtable[REG]; \ + if (w&0x80) cpu->F|=P_Z80; \ + if (w&0x0f) cpu->F|=H_Z80; \ +} while(0) + + +#define DEC8(REG) \ +do \ +{ \ + cpu->F=N_Z80|CARRY; \ + if (w&0x80) cpu->F|=P_Z80; \ + if (w&0x0f) cpu->F|=H_Z80; \ + REG--; \ + cpu->F|=SZtable[REG]; \ +} while(0) + + +/* ---------------------------------------- ROTATE AND SHIFT OPS +*/ +#define RRCA \ +do \ +{ \ + cpu->F=(cpu->F&0xec)|cpu->A&C_Z80; \ + cpu->A=(cpu->A>>1)|(cpu->A<<7); \ + SETHIDDEN(cpu->A); \ +} while(0) + + +#define RRA \ +do \ +{ \ + Z80Byte c; \ + c=CARRY; \ + cpu->F=(cpu->F&0xec)|cpu->A&C_Z80; \ + cpu->A=(cpu->A>>1)|(c<<7); \ + SETHIDDEN(cpu->A); \ +} while(0) + + +#define RRC(REG) \ +do \ +{ \ + Z80Byte c; \ + c=REG&C_Z80; \ + REG=(REG>>1)|(REG<<7); \ + cpu->F=PSZtable[REG]|c; \ + SETHIDDEN(REG); \ +} while(0) + + +#define RR(REG) \ +do \ +{ \ + Z80Byte c; \ + c=REG&C_Z80; \ + REG=(REG>>1)|(CARRY<<7); \ + cpu->F=PSZtable[REG]|c; \ + SETHIDDEN(REG); \ +} while(0) + + +#define RLCA \ +do \ +{ \ + cpu->F=(cpu->F&0xec)|(cpu->A>>7); \ + cpu->A=(cpu->A<<1)|(cpu->A>>7); \ + SETHIDDEN(cpu->A); \ +} while(0) + + +#define RLA \ +do \ +{ \ + Z80Byte c; \ + c=CARRY; \ + cpu->F=(cpu->F&0xec)|(cpu->A>>7); \ + cpu->A=(cpu->A<<1)|c; \ + SETHIDDEN(cpu->A); \ +} while(0) + + +#define RLC(REG) \ +do \ +{ \ + Z80Byte c; \ + c=REG>>7; \ + REG=(REG<<1)|c; \ + cpu->F=PSZtable[REG]|c; \ + SETHIDDEN(REG); \ +} while(0) + + +#define RL(REG) \ +do \ +{ \ + Z80Byte c; \ + c=REG>>7; \ + REG=(REG<<1)|CARRY; \ + cpu->F=PSZtable[REG]|c; \ + SETHIDDEN(REG); \ +} while(0) + + +#define SRL(REG) \ +do \ +{ \ + Z80Byte c; \ + c=REG&Z80_C; \ + REG>>=1; \ + cpu->F=PSZtable[REG]|c; \ + SETHIDDEN(REG); \ +} while(0) + + +#define SRA(REG) \ +do \ +{ \ + Z80Byte c; \ + c=REG&Z80_C; \ + REG=(REG>>1)|(REG&0x80); \ + cpu->F=PSZtable[REG]|c; \ + SETHIDDEN(REG); \ +} while(0) + + +#define SLL(REG) \ +do \ +{ \ + Z80Byte c; \ + c=REG>>7; \ + REG=(REG<<1)|1; \ + cpu->F=PSZtable[REG]|c; \ + SETHIDDEN(REG); \ +} while(0) + + +#define SLA(REG) \ +do \ +{ \ + Z80Byte c; \ + c=REG>>7; \ + REG=REG<<1; \ + cpu->F=PSZtable[REG]|c; \ + SETHIDDEN(REG); \ +} while(0) + + +/* ---------------------------------------- FLOW OPS +*/ +#define CALL(COND) \ +do \ +{ \ + if (COND) \ + { \ + PUSH(cpu->PC+2); \ + cpu->PC=PEEKW(cpu->PC); \ + } \ + else \ + { \ + cpu->PC+=2; \ + } \ +} \ +while(0) + + +#define JP(COND) \ +do \ +{ \ + if (COND) \ + { \ + cpu->PC=PEEKW(cpu->PC); \ + } \ + else \ + { \ + cpu->PC+=2; \ + } \ +} \ +while(0) + + +#define JR(COND) \ +do \ +{ \ + if (COND) \ + { \ + cpu->PC+=(Z80Relative)PEEK(cpu->PC)+1; \ + } \ + else \ + { \ + cpu->PC++; \ + } \ +} \ +while(0) + + +/* ---------------------------------------- BOOLEAN OPS +*/ +#define AND(REG,VAL) \ +do \ +{ \ + REG&=VAL; \ + cpu->F=PSZtable[REG]|H_Z80; \ + SETHIDDEN(REG); \ +} while(0) + + +#define OR(REG,VAL) \ +do \ +{ \ + REG|=VAL; \ + cpu->F=PSZtable[REG]; \ + SETHIDDEN(REG); \ +} while(0) + + +#define XOR(REG,VAL) \ +do \ +{ \ + REG^=VAL; \ + cpu->F=PSZtable[REG]; \ + SETHIDDEN(REG); \ +} while(0) + + +#define BIT(REG,B) \ +do \ +{ \ + cpu->F=CARRY|H_Z80; \ + if (REG&&(1<F|=S_Z80; \ + if (B==5) cpu->F|=B5_Z80; \ + if (B==3) cpu->F|=B3_Z80; \ + } \ + else \ + { \ + cpu->F|=Z_Z80; \ + } \ +} while(0) + + +/* ---------------------------------------- GENERAL MACROS +*/ + +/* Decides whether the offset for IX/IY should be fetched, or used + from the store for a CB instruction +*/ +#define GETREL (cpu->use_cb_off ? cpu->cb_off : (Z80Relative)FETCH_BYTE) + + +/* ---------------------------------------- GENERAL UTILS +*/ + +/* This code based on the DAA opcode from YAZE, (c) Frank D. Cringle +*/ +static void DAA (Z80 *cpu) +{ + Z80Word AF; + Z80Word acu,temp,cbits; + + AF=(Z80Word)cpu->A<<8|cpu->F; + acu=cpu->A; + temp=acu&0xf; + cbits=CARRY; + + if (IS_N) /* last operation was a subtract */ + { + int hd = cbits || acu > 0x99; + if (IS_H || (temp > 9)) /* adjust low digit */ + { + if (temp > 5) + { + CLR(AF,H_Z80); + } + + acu -= 6; + acu &= 0xff; + } + if (hd) /* adjust high digit */ + acu -= 0x160; + } + else /* last operation was an add */ + { + if (IS_H || (temp > 9)) /* adjust low digit */ + { + if (temp>9) + { + SET(AF,H_Z80); + } + else + { + CLR(AF,H_Z80); + } + acu += 6; + } + if (cbits || ((acu & 0x1f0) > 0x90)) /* adjust high digit */ + acu += 0x60; + } + + cbits |= (acu >> 8) & 1; + acu &= 0xff; + AF = (acu << 8) | (acu & 0xa8) | ((acu == 0) << 6) | + (AF & 0x12) | partab[acu] | cbits; + + cpu->A=(AF>>8); + cpu->F=(AF&0xff); + SETHIDDEN(cpu->A); +} + + + +/* ---------------------------------------- HANDLERS FOR CB OPCODES +*/ +static void DecodeCB(Z80 *cpu, Z80Byte opcode) +{ + switch(opcode) + { + } + + Z80Byte bmh,bmm,bml; + + /* Split up the opcode into it's bitmasks + */ + bmh=(opcode>>6)&3; + bmm=(opcode>>3)&7; + bml=opcode&7; + + /* Instruction set 00 + */ + if (bmh==0x00) + { + CBOp_00(cpu,opcode,bmh,bmm,bml); + return; + } + + /* Instruction set 01 (BIT) + */ + if (bmh==0x01) + { + CBOp_01(cpu,opcode,bmh,bmm,bml); + return; + } + + /* Instruction set 10 (RES) and 11 (SET) + */ + CBOp_ResSet(cpu,opcode,bmh,bmm,bml); +} + + + + +/* ---------------------------------------- OPCODE DECODER +*/ +void Z80_Decode(Z80 *cpu, Z80Byte opcode) +{ + Z80Byte bmh,bmm,bml; + + /* IX/IY shifts + */ + if (opcode==0xdd || opcode==0xfd) + { + TSTATE(4); + INC_R; + + cpu->shift=opcode; + Z80_Decode(cpu,FETCH_BYTE); + return; + } + + /* CB shifts + */ + if (opcode==0xcb) + { + INC_R; + + /* Check for previous IX/IY shift. + */ + if (cpu->shift!=0) + { + cpu->use_cb_off=TRUE; + cpu->cb_off=(Z80Relative)FETCH_BYTE; + } + + DecodeCB(cpu,FETCH_BYTE); + return; + } + + /* DE shifts + */ + if (opcode==0xed) + { + INC_R; + DecodeED(cpu,FETCH_BYTE); + return; + } + + *** NEW *** + + INC_R; + + switch(opcode) + { + case 0x00: /* NOP */ + TSTATE(4); + break; + + case 0x01: /* LD BC, nnnn */ + TSTATE(10); + cpu->BC=FETCH_WORD; + break; + + case 0x02: + TSTATE(7); + break; + + case 0x03: + TSTATE(4); + break; + + case 0x04: + TSTATE(4); + break; + + case 0x05: + TSTATE(4); + break; + + case 0x06: + TSTATE(4); + break; + + case 0x07: + TSTATE(4); + break; + + case 0x08: + TSTATE(4); + break; + + case 0x09: + TSTATE(4); + break; + + case 0x0a: + TSTATE(4); + break; + + case 0x0b: + TSTATE(4); + break; + + case 0x0c: + TSTATE(4); + break; + + case 0x0d: + TSTATE(4); + break; + + case 0x0e: + TSTATE(4); + break; + + case 0x0f: + TSTATE(4); + break; + + case 0x10: + TSTATE(4); + break; + + case 0x11: + TSTATE(4); + break; + + case 0x12: + TSTATE(4); + break; + + case 0x13: + TSTATE(4); + break; + + case 0x14: + TSTATE(4); + break; + + case 0x15: + TSTATE(4); + break; + + case 0x16: + TSTATE(4); + break; + + case 0x17: + TSTATE(4); + break; + + case 0x18: + TSTATE(4); + break; + + case 0x19: + TSTATE(4); + break; + + case 0x1a: + TSTATE(4); + break; + + case 0x1b: + TSTATE(4); + break; + + case 0x1c: + TSTATE(4); + break; + + case 0x1d: + TSTATE(4); + break; + + case 0x1e: + TSTATE(4); + break; + + case 0x1f: + TSTATE(4); + break; + + case 0x20: + TSTATE(4); + break; + + case 0x21: + TSTATE(4); + break; + + case 0x22: + TSTATE(4); + break; + + case 0x23: + TSTATE(4); + break; + + case 0x24: + TSTATE(4); + break; + + case 0x25: + TSTATE(4); + break; + + case 0x26: + TSTATE(4); + break; + + case 0x27: + TSTATE(4); + break; + + case 0x28: + TSTATE(4); + break; + + case 0x29: + TSTATE(4); + break; + + case 0x2a: + TSTATE(4); + break; + + case 0x2b: + TSTATE(4); + break; + + case 0x2c: + TSTATE(4); + break; + + case 0x2d: + TSTATE(4); + break; + + case 0x2e: + TSTATE(4); + break; + + case 0x2f: + TSTATE(4); + break; + + case 0x30: + TSTATE(4); + break; + + case 0x31: + TSTATE(4); + break; + + case 0x32: + TSTATE(4); + break; + + case 0x33: + TSTATE(4); + break; + + case 0x34: + TSTATE(4); + break; + + case 0x35: + TSTATE(4); + break; + + case 0x36: + TSTATE(4); + break; + + case 0x37: + TSTATE(4); + break; + + case 0x38: + TSTATE(4); + break; + + case 0x39: + TSTATE(4); + break; + + case 0x3a: + TSTATE(4); + break; + + case 0x3b: + TSTATE(4); + break; + + case 0x3c: + TSTATE(4); + break; + + case 0x3d: + TSTATE(4); + break; + + case 0x3e: + TSTATE(4); + break; + + case 0x3f: + TSTATE(4); + break; + + case 0x40: + TSTATE(4); + break; + + case 0x41: + TSTATE(4); + break; + + case 0x42: + TSTATE(4); + break; + + case 0x43: + TSTATE(4); + break; + + case 0x44: + TSTATE(4); + break; + + case 0x45: + TSTATE(4); + break; + + case 0x46: + TSTATE(4); + break; + + case 0x47: + TSTATE(4); + break; + + case 0x48: + TSTATE(4); + break; + + case 0x49: + TSTATE(4); + break; + + case 0x4a: + TSTATE(4); + break; + + case 0x4b: + TSTATE(4); + break; + + case 0x4c: + TSTATE(4); + break; + + case 0x4d: + TSTATE(4); + break; + + case 0x4e: + TSTATE(4); + break; + + case 0x4f: + TSTATE(4); + break; + + case 0x50: + TSTATE(4); + break; + + case 0x51: + TSTATE(4); + break; + + case 0x52: + TSTATE(4); + break; + + case 0x53: + TSTATE(4); + break; + + case 0x54: + TSTATE(4); + break; + + case 0x55: + TSTATE(4); + break; + + case 0x56: + TSTATE(4); + break; + + case 0x57: + TSTATE(4); + break; + + case 0x58: + TSTATE(4); + break; + + case 0x59: + TSTATE(4); + break; + + case 0x5a: + TSTATE(4); + break; + + case 0x5b: + TSTATE(4); + break; + + case 0x5c: + TSTATE(4); + break; + + case 0x5d: + TSTATE(4); + break; + + case 0x5e: + TSTATE(4); + break; + + case 0x5f: + TSTATE(4); + break; + + case 0x60: + TSTATE(4); + break; + + case 0x61: + TSTATE(4); + break; + + case 0x62: + TSTATE(4); + break; + + case 0x63: + TSTATE(4); + break; + + case 0x64: + TSTATE(4); + break; + + case 0x65: + TSTATE(4); + break; + + case 0x66: + TSTATE(4); + break; + + case 0x67: + TSTATE(4); + break; + + case 0x68: + TSTATE(4); + break; + + case 0x69: + TSTATE(4); + break; + + case 0x6a: + TSTATE(4); + break; + + case 0x6b: + TSTATE(4); + break; + + case 0x6c: + TSTATE(4); + break; + + case 0x6d: + TSTATE(4); + break; + + case 0x6e: + TSTATE(4); + break; + + case 0x6f: + TSTATE(4); + break; + + case 0x70: + TSTATE(4); + break; + + case 0x71: + TSTATE(4); + break; + + case 0x72: + TSTATE(4); + break; + + case 0x73: + TSTATE(4); + break; + + case 0x74: + TSTATE(4); + break; + + case 0x75: + TSTATE(4); + break; + + case 0x76: + TSTATE(4); + break; + + case 0x77: + TSTATE(4); + break; + + case 0x78: + TSTATE(4); + break; + + case 0x79: + TSTATE(4); + break; + + case 0x7a: + TSTATE(4); + break; + + case 0x7b: + TSTATE(4); + break; + + case 0x7c: + TSTATE(4); + break; + + case 0x7d: + TSTATE(4); + break; + + case 0x7e: + TSTATE(4); + break; + + case 0x7f: + TSTATE(4); + break; + + case 0x80: + TSTATE(4); + break; + + case 0x81: + TSTATE(4); + break; + + case 0x82: + TSTATE(4); + break; + + case 0x83: + TSTATE(4); + break; + + case 0x84: + TSTATE(4); + break; + + case 0x85: + TSTATE(4); + break; + + case 0x86: + TSTATE(4); + break; + + case 0x87: + TSTATE(4); + break; + + case 0x88: + TSTATE(4); + break; + + case 0x89: + TSTATE(4); + break; + + case 0x8a: + TSTATE(4); + break; + + case 0x8b: + TSTATE(4); + break; + + case 0x8c: + TSTATE(4); + break; + + case 0x8d: + TSTATE(4); + break; + + case 0x8e: + TSTATE(4); + break; + + case 0x8f: + TSTATE(4); + break; + + case 0x90: + TSTATE(4); + break; + + case 0x91: + TSTATE(4); + break; + + case 0x92: + TSTATE(4); + break; + + case 0x93: + TSTATE(4); + break; + + case 0x94: + TSTATE(4); + break; + + case 0x95: + TSTATE(4); + break; + + case 0x96: + TSTATE(4); + break; + + case 0x97: + TSTATE(4); + break; + + case 0x98: + TSTATE(4); + break; + + case 0x99: + TSTATE(4); + break; + + case 0x9a: + TSTATE(4); + break; + + case 0x9b: + TSTATE(4); + break; + + case 0x9c: + TSTATE(4); + break; + + case 0x9d: + TSTATE(4); + break; + + case 0x9e: + TSTATE(4); + break; + + case 0x9f: + TSTATE(4); + break; + + case 0xa0: + TSTATE(4); + break; + + case 0xa1: + TSTATE(4); + break; + + case 0xa2: + TSTATE(4); + break; + + case 0xa3: + TSTATE(4); + break; + + case 0xa4: + TSTATE(4); + break; + + case 0xa5: + TSTATE(4); + break; + + case 0xa6: + TSTATE(4); + break; + + case 0xa7: + TSTATE(4); + break; + + case 0xa8: + TSTATE(4); + break; + + case 0xa9: + TSTATE(4); + break; + + case 0xaa: + TSTATE(4); + break; + + case 0xab: + TSTATE(4); + break; + + case 0xac: + TSTATE(4); + break; + + case 0xad: + TSTATE(4); + break; + + case 0xae: + TSTATE(4); + break; + + case 0xaf: + TSTATE(4); + break; + + case 0xb0: + TSTATE(4); + break; + + case 0xb1: + TSTATE(4); + break; + + case 0xb2: + TSTATE(4); + break; + + case 0xb3: + TSTATE(4); + break; + + case 0xb4: + TSTATE(4); + break; + + case 0xb5: + TSTATE(4); + break; + + case 0xb6: + TSTATE(4); + break; + + case 0xb7: + TSTATE(4); + break; + + case 0xb8: + TSTATE(4); + break; + + case 0xb9: + TSTATE(4); + break; + + case 0xba: + TSTATE(4); + break; + + case 0xbb: + TSTATE(4); + break; + + case 0xbc: + TSTATE(4); + break; + + case 0xbd: + TSTATE(4); + break; + + case 0xbe: + TSTATE(4); + break; + + case 0xbf: + TSTATE(4); + break; + + case 0xc0: + TSTATE(4); + break; + + case 0xc1: + TSTATE(4); + break; + + case 0xc2: + TSTATE(4); + break; + + case 0xc3: + TSTATE(4); + break; + + case 0xc4: + TSTATE(4); + break; + + case 0xc5: + TSTATE(4); + break; + + case 0xc6: + TSTATE(4); + break; + + case 0xc7: + TSTATE(4); + break; + + case 0xc8: + TSTATE(4); + break; + + case 0xc9: + TSTATE(4); + break; + + case 0xca: + TSTATE(4); + break; + + case 0xcb: + TSTATE(4); + break; + + case 0xcc: + TSTATE(4); + break; + + case 0xcd: + TSTATE(4); + break; + + case 0xce: + TSTATE(4); + break; + + case 0xcf: + TSTATE(4); + break; + + case 0xd0: + TSTATE(4); + break; + + case 0xd1: + TSTATE(4); + break; + + case 0xd2: + TSTATE(4); + break; + + case 0xd3: + TSTATE(4); + break; + + case 0xd4: + TSTATE(4); + break; + + case 0xd5: + TSTATE(4); + break; + + case 0xd6: + TSTATE(4); + break; + + case 0xd7: + TSTATE(4); + break; + + case 0xd8: + TSTATE(4); + break; + + case 0xd9: + TSTATE(4); + break; + + case 0xda: + TSTATE(4); + break; + + case 0xdb: + TSTATE(4); + break; + + case 0xdc: + TSTATE(4); + break; + + case 0xdd: + TSTATE(4); + break; + + case 0xde: + TSTATE(4); + break; + + case 0xdf: + TSTATE(4); + break; + + case 0xe0: + TSTATE(4); + break; + + case 0xe1: + TSTATE(4); + break; + + case 0xe2: + TSTATE(4); + break; + + case 0xe3: + TSTATE(4); + break; + + case 0xe4: + TSTATE(4); + break; + + case 0xe5: + TSTATE(4); + break; + + case 0xe6: + TSTATE(4); + break; + + case 0xe7: + TSTATE(4); + break; + + case 0xe8: + TSTATE(4); + break; + + case 0xe9: + TSTATE(4); + break; + + case 0xea: + TSTATE(4); + break; + + case 0xeb: + TSTATE(4); + break; + + case 0xec: + TSTATE(4); + break; + + case 0xed: + TSTATE(4); + break; + + case 0xee: + TSTATE(4); + break; + + case 0xef: + TSTATE(4); + break; + + case 0xf0: + TSTATE(4); + break; + + case 0xf1: + TSTATE(4); + break; + + case 0xf2: + TSTATE(4); + break; + + case 0xf3: + TSTATE(4); + break; + + case 0xf4: + TSTATE(4); + break; + + case 0xf5: + TSTATE(4); + break; + + case 0xf6: + TSTATE(4); + break; + + case 0xf7: + TSTATE(4); + break; + + case 0xf8: + TSTATE(4); + break; + + case 0xf9: + TSTATE(4); + break; + + case 0xfa: + TSTATE(4); + break; + + case 0xfb: + TSTATE(4); + break; + + case 0xfc: + TSTATE(4); + break; + + case 0xfd: + TSTATE(4); + break; + + case 0xfe: + TSTATE(4); + break; + + case 0xff: + TSTATE(4); + break; + + } +} + + +/* END OF FILE */ diff --git a/z80_private.h b/z80_private.h new file mode 100644 index 0000000..08dc543 --- /dev/null +++ b/z80_private.h @@ -0,0 +1,209 @@ +/* + + z80 - Z80 emulation + + Copyright (C) 2006 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------- + + $Id$ + + Private macros for Z80 + +*/ + +#ifndef Z80_PRIVATE_H +#define Z80_PRIVATE_H "$Id$" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define MAX_PER_CALLBACK 10 + + +/* ---------------------------------------- TYPES +*/ + +struct Z80 +{ + Z80Val cycle; + + Z80Word PC; + + Z80Byte A; + Z80Byte F; + Z80Word BC; + Z80Word DE; + Z80Word HL; + + Z80Word AF_; + Z80Word BC_; + Z80Word DE_; + Z80Word HL_; + + Z80Word IX; + Z80Word IY; + + Z80Word SP; + + Z80Byte IFF1; + Z80Byte IFF2; + Z80Byte IM; + Z80Byte I; + Z80Byte R; + int halt; + + int use_cb_off; + Z80Relative cb_off; + + Z80Byte shift; + + int raise; + Z80Byte devbyte; + int nmi; + + Z80Byte *memory; + int *memctrl; + + Z80ReadPort pread; + Z80WritePort pwrite; + + Z80Callback callback[eZ80_NO_CALLBACK][MAX_PER_CALLBACK]; + + int last_cb; +}; + + +/* ---------------------------------------- MACROS +*/ + +/* NOTE: A lot of these macros assume you have a variable called 'cpu' + which is a pointer to Z80 +*/ + + +/* HI/LO +*/ +#define SET_HI(reg,v) reg = (reg & 0x00ff) | ((v) << 8) +#define SET_LO(reg,v) reg = (reg & 0xff00) | ((v) & 0xff) +#define GET_HI(reg,v) ((reg & 0xff00) >> 8) +#define GET_LO(reg,v) (reg & 0xff) + +/* Invoke a callback class +*/ +#define CALLBACK(r,d) do \ + { \ + int f; \ + \ + for(f=0;fcallback[r][f]) \ + cpu->callback[r][f](cpu,d); \ + } while(0) + +/* Flag register +*/ +#define C_Z80 0x01 +#define N_Z80 0x02 +#define P_Z80 0x04 +#define V_Z80 P_Z80 +#define H_Z80 0x10 +#define Z_Z80 0x40 +#define S_Z80 0x80 + +#define B3_Z80 0x08 +#define B5_Z80 0x20 + + +#define SET(v,b) v|=b +#define CLR(v,b) v&=~(b) + +#define SETFLAG(f) SET(cpu->F,f) +#define CLRFLAG(f) CLR(cpu->F,f) + +#define PEEK(addr) (cpu->memory[addr]) +#define PEEKW(addr) (PEEK(addr) | (Z80Word)PEEK(addr+1)<<8) + +#define POKE(addr,val) do \ + { \ + if (cpu->memctrl[(addr)/256]) \ + { \ + cpu->memory[addr]=val; \ + } \ + while (0)(cpu->write(cpu,addr,val)) +#define POKEW(addr,val) do \ + { \ + POKE(addr,val); \ + POKE(addr+1,val>>8); \ + } while(0) + +#define FETCH_BYTE (cpu->memory[cpu->PC++]) +#define FETCH_WORD (cpu->PC+=2, \ + cpu->memory[cpu->PC-2]| \ + ((Z80Word)cpu->memory[cpu->PC-1]<<8)) + +#define IS_C (cpu->F&C_Z80) +#define IS_N (cpu->F&N_Z80) +#define IS_P (cpu->F&P_Z80) +#define IS_H (cpu->F&H_Z80) +#define IS_Z (cpu->F&Z_Z80) +#define IS_S (cpu->F&S_Z80) + +#define CARRY IS_C + +#define TSTATE(n) cpu->cycle+=n + +#define ADD_R(v) cpu->R=((cpu->R&0x80)|((cpu->R+(v))&0x7f)) +#define INC_R ADD_R(1) + +#define PUSH(REG) do \ + { \ + cpu->SP-=2; \ + POKEW(cpu->SP,REG); \ + } while(0) +#define POP(REG) do \ + { \ + REG=PEEKW(cpu->SP); \ + cpu->SP+=2; \ + } while(0) + +#define SETHIDDEN(res) cpu->F=(cpu->F&~(B3_Z80|B5_Z80))|\ + ((res)&(B3_Z80|B5_Z80)) + + + +/* ---------------------------------------- FLAG TABLES +*/ +extern Z80Byte PSZtable[512]; +extern Z80Byte SZtable[512]; +extern Z80Byte Ptable[512]; +extern Z80Byte Stable[512]; +extern Z80Byte Ztable[512]; + + +/* ---------------------------------------- GLOBAL GENERAL OPCODES/ROUTINES +*/ +void Z80_Decode(Z80 *cpu); + + +#endif + +/* END OF FILE */ diff --git a/zexall.com b/zexall.com new file mode 100644 index 0000000..fa0e6ff Binary files /dev/null and b/zexall.com differ diff --git a/zexdoc.com b/zexdoc.com new file mode 100644 index 0000000..5cfd531 Binary files /dev/null and b/zexdoc.com differ -- cgit v1.2.3