[alt.sources] Z-80 emulator in C with CP/M BIOS

mrapple@quack.sac.ca.us (Nick Sayer) (11/09/90)

This was posted to comp.sources.misc, but aparently that group
is down for the count.

This emulator is quite slow, but is good for debugging. Have fun.
Pass it around all you like, but don't charge money for it. If
you have bug fixes, mail 'em. Specifically, someone ought to
put #ifdef's around all the debug hooks in z80.c & z80_cbed.c.

The CP/M BIOS is written in C, and the BIOS jump table is made
of HALT and RET instructions. A HALT instructs the interpreter
to stop and return to the caller. The caller in this case is the
BIOS interpreter. The BIOS then examines the program counter
lower byte to see which jump table entry is being called.
It then performs the required action, increments the PC, and
begins interpreting again -- at the RET instruction.

The standard CP/M BDOS and CCP are used to talk to this BIOS.
Unfortunately BDOS and CCP are still copyrighted by Digital Research,
and it is the author's opinion that he does not have permission, nor
could he get permission, to distribute CP/M. So you have to get
that on your own. The good news is that there are probably public
domain copycat BDOS and CCP programs (ZCPR comes to mind).

This program is so pitifully slow, however, it may all be a moot
point unless you have a Cray (and if you do, why would you be running
CP/M?).


#!/bin/sh
# This is a shell archive (shar 3.24)
# made 11/08/1990 17:07 UTC by mrapple@quack
# Source directory /files/users/mrapple/upm
#
# existing files WILL be overwritten
#
# This is part 1 of a multipart archive                                    
# do not concatenate these parts, unpack them in order with /bin/sh        
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#    853 -rw-r--r-- Makefile
#    717 -rw-r--r-- README
#   1625 -rw-r--r-- README.upm
#   5553 -rw-r--r-- bios.c
#   8597 -rw-r--r-- debug.c
#    462 -rw-r--r-- io_ask.c
#    255 -rw-r--r-- io_mem.c
#    373 -rw-r--r-- lh.c
#   1262 -rw-r--r-- loadhex.c
#   1062 -rw-r--r-- makedrive.c
#   9809 -rw-r--r-- udbg.c
#   6850 -rw-r--r-- upm.c
#   1744 -rw-r--r-- upm.h
#  27243 -rw-r--r-- z80.c
#   1672 -rw-r--r-- z80.h
#  16492 -rw-r--r-- z80_cbed.c
#
if touch 2>&1 | fgrep '[-amc]' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
if test -r shar3_seq_.tmp; then
	echo "Must unpack archives in sequence!"
	next=`cat shar3_seq_.tmp`; echo "Please unpack part $next next"
	exit 1
fi
# ============= Makefile ==============
echo "x - extracting Makefile (Text)"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
Xz80.o: z80.h
Xz80_cbed.o: z80.h
X
XZOBJS = z80.o z80_cbed.o
XIFILES = z80.h
X
XCFLAGS = -O
X
Xall: upm debug dumbrun makedrive dbg
X
Xdebug: $(ZOBJS) io_ask.o debug.o loadhex.o
X	$(CC) $(CFLAGS) $(ZOBJS) io_ask.o debug.o loadhex.o -o debug
X
Xdbg: $(ZOBJS) io_ask.o dbg.o loadhex.o
X	$(CC) $(CFLAGS) $(ZOBJS) io_ask.o dbg.o loadhex.o -o dbg
X
Xdumbrun: $(ZOBJS) io_ask.o dumbrun.o
X	$(CC) $(CFLAGS) $(ZOBJS) io_ask.o dumbrun.o -o dumbrun
X
Xbios.o: z80.h
Xdebug.o: z80.h
Xio_ask.o: z80.h
Xio_mem.o: z80.h
Xloadhex.o: z80.h
Xlh.o: z80.h
Xudbg.o: z80.h
X
Xupm: $(ZOBJS) io_mem.o upm.o loadhex.o bios.o udbg.o
X	$(CC) $(CFLAGS) $(ZOBJS) io_mem.o upm.o loadhex.o bios.o udbg.o -o upm
X
Xmakedrive: makedrive.c
X	$(CC) $(CFLAGS) makedrive.c -o makedrive
X
Xinitdisk: initdisk.c
X	$(CC) $(CFLAGS) initdisk.c -o initdisk
X
X$(ZOBJS): z80.h
X
Xclean:
X	rm -f upm debug dumbrun makedrive initdisk *.o
SHAR_EOF
$TOUCH -am 0928182190 Makefile &&
chmod 0644 Makefile ||
echo "restore of Makefile failed"
set `wc -c Makefile`;Wc_c=$1
if test "$Wc_c" != "853"; then
	echo original size 853, current size $Wc_c
fi
# ============= README ==============
echo "x - extracting README (Text)"
sed 's/^X//' << 'SHAR_EOF' > README &&
XSince emulating a CPU is a fairly memory intensive thing to do,
Xit's unlikely that the Z-80 emulator code will require any reworking
Xto fit your needs. There is an option you need to set, though. Two
Xfiles are included to handle I/O instructions. io_mem.c implements
Xa 256 byte RAM on the I/O space. io_ask.c prompts the console
Xfor input and prints output writes.
X
XYou also have a couple choices for main():
X
Xdebug.c is a debugger. This debugger is intended primarily to debug
Xthe interpreter itself rather than debug z-80 code.
X
Xupm.c implements CP/M on top of the emulator. See README.upm
X
XMany thanks to Mark W. Eichin for his help in debugging some
Xof my truely nasty code during Alpha-testing. Truely a saint.
X
SHAR_EOF
$TOUCH -am 1031111990 README &&
chmod 0644 README ||
echo "restore of README failed"
set `wc -c README`;Wc_c=$1
if test "$Wc_c" != "717"; then
	echo original size 717, current size $Wc_c
fi
# ============= README.upm ==============
echo "x - extracting README.upm (Text)"
sed 's/^X//' << 'SHAR_EOF' > README.upm &&
XThe BIOS is implemented like this:
X
Xxx00: 76 C9 00 76 C9 00 76 C9 00......
X
XFor the non-z80-literate, that is a series of HALT and RETurn from
Xsubroutine statements. HALT instructions return control to the calling
X(c program) routine that started the Z-80 running in the first place.
XBy dividing the low 8 bits of the PC by 3, we can find out what BIOS
Xroutine the caller wanted, do it in C, then return.
X
Xupm takes various paramaters either on the command line or in
X~/.upmrc. If specified, command line paramaters take precedence over
X.upmrc options. Options are in the form of dev:file, where dev is
XA-O, for disk devices, or TY, LP, PT, U1 or U2, for alternate physical
Xdevices. The files specified are attached to the coresponding disks or
Xphysical devices. The above identifiers corespond to these CP/M physical
Xdevices:
X
XTY	TTY:
XLP	LPT:
XPT	PTP: PTR:
XU1	UC1: UL1: UP1: UR1:
XU2	UP2: UR2:
X
XThe CRT: device is permanently assigned to stdin/stdout. stdin/stdout
Xare set to RAW mode, to make all keys work. The last BIOS jump table
Xentry is non-standard, and causes the CP/M system to halt and control
Xreturn to unix. The CP/M program EXIT.COM will do this.
X
XAs usual, the BAT: device is a combination of CRT: and LPT:
X
XThe default I/O byte assigns as follows:
X
XCON:=CRT:
XRDR:=PTP:
XPUN:=PTR:
XLST:=LPT:
X
XA typical command line might say:
X
X% upm a:cpm.adrive lp:printer_file pt:copy_file
X
XThe device identifiers may be in upper-case, but the files, of course,
Xwill be literal-cased.
X
XAll device files except LP: will be fopen()ed "r+". LP: will be
Xfopen()ed "w".
X
XAny device not specifically assigned will act like /dev/null.
SHAR_EOF
$TOUCH -am 1031112690 README.upm &&
chmod 0644 README.upm ||
echo "restore of README.upm failed"
set `wc -c README.upm`;Wc_c=$1
if test "$Wc_c" != "1625"; then
	echo original size 1625, current size $Wc_c
fi
# ============= bios.c ==============
echo "x - extracting bios.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > bios.c &&
X/*
X
Xbios.c - CP/M BIOS in C
X
X(C) MCMXM - Nick Sayer - All rights reserved.
X
XSee COPYRIGHT file for more details.
X
X*/
X
X#include <stdio.h>
X#include "z80.h"
X#include "upm.h"
X
X#include <sys/ioctl.h>
X
X#define IO_BYTE 3
X
XWORD trk,sec,dma,dsk;
X
Xchar cold_flag=0;
X
X/*
X
XOur Z-80 BIOS is just HALT, RET, NOP, HALT, RET, NOP..... We find out
Xwhich BIOS jump it is by dividing the LSB of PC by 3.
X
XTHIS PRESUMES BIOS BEGINS ON AN EVEN PAGE BOUNDARY. This is a pretty
Xgood assumption.
X
XBIOS Memory map:
X
X0000 -     jump table
X0080 -     DIRBUF
X0100 -     disk buffers - this set up in upm.c
X
XIt is up to upm.c to set up the disk buffers as necessary for
Xdifferent size devices, and set up the pointers in diskbufs[].
X
X*/
X
Xchar bios()
X{
X  register char bios_call;
X
X  bios_call=(PC&0xff)/3;
X  PC++; /* Skip past the HALT */
X
Xif(debugflag) printf("\tBios! - %d from %x\n\r",bios_call,PC);
X
X  switch(bios_call)
X  {
X    case 0:cold_boot(); /* DON'T break... we WANT to fall into warm boot! */
X    case 1:warm_boot();
X    break;
X    case 2:stat_con();
X    break;
X    case 3:read_con();
X    break;
X    case 4:write_con();
X    break;
X    case 5:write_lst();
X    break;
X    case 6:write_pun();
X    break;
X    case 7:read_rdr();
X    break;
X    case 8:home_dsk();
X    break;
X    case 9:sel_dsk();
X    break;
X    case 10:set_trk();
X    break;
X    case 11:set_sec();
X    break;
X    case 12:set_dma();
X    break;
X    case 13:read_dsk();
X    break;
X    case 14:write_dsk();
X    break;
X    case 15:stat_lst();
X    break;
X    case 16:sec_trans();
X    break;
X    case 30:shell_escape();
X    break;
X    case 31:quit(); return 1;
X    default:
X	printf("unhandled bios call %d\n\r",bios_call);
X	debugit();
X  }
X
X  return 0;
X}
X
Xquit()
X{
X}
X
Xshell_escape()
X{
X    debugit();
X}
X
Xcold_boot()
X{
X
X  real_z80_mem[IO_BYTE]=0x95; /* CRT, PUN, RDR, LPT */
X  real_z80_mem[0]=0xC3;
X  real_z80_mem[1]=0x03;
X  real_z80_mem[2]=(ccp_start>>8)+8+0xe; /* First page of BIOS */
X  real_z80_mem[5]=0xC3;
X  real_z80_mem[6]=0x06;
X  real_z80_mem[7]=(ccp_start>>8)+8; /* First page of BDOS */
X  printf("\n\r\n\rCP/M Ver 2.2\n\rCopyright Digital Research, Inc.\n\r");
X  printf("UP/M Version 1.0B\n\rCopyright Nick Sayer\n\r\n\r");
X  cold_flag++;
X
X}
X
Xwarm_boot()
X{
X  register WORD i;
X
X  for(i=0;i<SIZE_CCP_IMAGE;i++)
X    real_z80_mem[i+ccp_start]=ccp_image[i];
X
X  if (cold_flag)
X  {
X    cold_flag=0;
X    PC=ccp_start;
X  }
X  else
X    PC=ccp_start+3;
X
X  BC=0;
X  debugit();
X}
X
Xstat_con()
X{
X  FILE *which;
X  int ret;
X
X  switch (real_z80_mem[IO_BYTE]&0x3)
X  {
X    case 0:which=devices[F_TY]; break;
X    case 1:which=stdin; break;
X    case 2:which=stdin; break;
X    case 3:which=devices[F_U1]; break;
X  }
X
X  ioctl(fileno(which),FIONREAD,&ret);
X  AF=(AF&0xff)|(ret?0xff00:0);
X/* */
X/*  if(!ret) AF |= FLAG_Z; else AF &= ~FLAG_Z; */
X  AF |= FLAG_Z;
X
X}
X
Xread_con()
X{
X  FILE *which;
X
X  switch (real_z80_mem[IO_BYTE]&0x3)
X  {
X    case 0:which=devices[F_TY]; break;
X    case 1:which=stdin; break;
X    case 2:which=stdin; break;
X    case 3:which=devices[F_U1]; break;
X  }
X  if (which==NULL)
X  {
X    AF&=0xff;
X    return;
X  }
X  AF=(AF&0xff)|(getc(which)<<8);
X}
X
Xwrite_con()
X{
X  FILE *which;
X
X  switch(real_z80_mem[IO_BYTE]&0x3)
X  {
X    case 0:which=devices[F_TY]; break;
X    case 1:which=stdout; break;
X    case 2:write_lst(); which=stdout; break;
X    case 3:which=devices[F_U1]; break;
X  }
X  if (which!=NULL)
X    putc(BC&0xff,which);
X}
X
Xstat_lst()
X{
X  FILE *which;
X
X/*
X  switch((real_z80_mem[IO_BYTE]>>6)&0x3)
X  {
X    case 0:which=devices[F_TY]; break;
X    case 1:which=stdout; break;
X    case 2:which=devices[F_LP]; break;
X    case 3:which=devices[F_U1]; break;
X  }
X
XUnder unix, writing is always fine.
X
X*/
X  AF|=0xff00;
X}
X
Xwrite_lst()
X{
X  FILE *which;
X
X  switch((real_z80_mem[IO_BYTE]>>6)&0x3)
X  {
X    case 0:which=devices[F_TY]; break;
X    case 1:which=stdout; break;
X    case 2:which=devices[F_LP]; break;
X    case 3:which=devices[F_U1]; break;
X  }
X  if (which!=NULL)
X    putc(BC&0xff,which);
X}
X
Xwrite_pun()
X{
X  FILE *which;
X
X  switch((real_z80_mem[IO_BYTE]>>4)&0x3)
X  {
X    case 0:which=devices[F_TY]; break;
X    case 1:which=devices[F_PT]; break;
X    case 2:which=devices[F_U1]; break;
X    case 3:which=devices[F_U2]; break;
X  }
X  if (which!=NULL)
X    putc(BC&0xff,which);
X}
X
Xread_rdr()
X{
X  FILE *which;
X
X  switch((real_z80_mem[IO_BYTE]>>2)&0x3)
X  {
X    case 0:which=devices[F_TY]; break;
X    case 1:which=devices[F_PT]; break;
X    case 2:which=devices[F_U1]; break;
X    case 3:which=devices[F_U2]; break;
X  }
X  if (which==NULL)
X  {
X    AF&=0xff;
X    return;
X  }
X  AF=(AF&0xff)|(getc(which)<<8);
X}
X
Xhome_dsk()
X{
X  bioslog("home disk\n\r");
X  trk = 0;
X}
X
Xsec_trans()
X{
X  bioslog("set trans to 0x%04x\n\r",BC);
X  HL=BC;
X}
X
Xsel_dsk()
X{
X  if (disks[BC&0xf]==NULL)
X  {
X    HL=0;
X    bioslog("Invalid disk select 0x%04x\n\r",BC);
X  }
X  else
X  {
X    HL=diskbufs[BC&0xf];
X    dsk=BC&0xf;
X    bioslog("Valid disk select [%x] 0x%4x\n\r",BC&0xf,HL);
X  }
X}
X
Xset_trk()
X{
X  bioslog("set track to 0x%02x\n\r",BC&0xff);
X  trk=BC&0xff;
X}
X
Xset_sec()
X{
X  bioslog("set sec to 0x%02x\n\r",BC&0xff);
X  sec=BC&0xff;
X}
X
Xset_dma()
X{
X  bioslog("set dma to 0x%04x\n\r",BC);
X  dma=BC;
X}
X
Xread_dsk()
X{
X  bioslog("Reading: track %d, sec %d, offset %d, dma 0x%4x\n\r",
X	 trk, sec, 128*((trk*0x40)+sec), dma);
X  fseek(disks[dsk],128*((trk*0x40)+sec),0);
X  AF=(AF&0xff)|((fread(real_z80_mem+dma,128,1,disks[dsk])!=1)<<8);
X}
X
Xwrite_dsk()
X{
X  bioslog("Writing: track %d, sec %d, offset %d, dma 0x%4x\n\r",
X	 trk, sec, 128*((trk*0x40)+sec), dma);
X  fseek(disks[dsk],128*((trk*0x40)+sec),0);
X  AF=(AF&0xff)|((fwrite(real_z80_mem+dma,128,1,disks[dsk])!=1)<<8);
X}
SHAR_EOF
$TOUCH -am 0928182090 bios.c &&
chmod 0644 bios.c ||
echo "restore of bios.c failed"
set `wc -c bios.c`;Wc_c=$1
if test "$Wc_c" != "5553"; then
	echo original size 5553, current size $Wc_c
fi
# ============= debug.c ==============
echo "x - extracting debug.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > debug.c &&
X/*
X
Xdebug.c - debugger for z-80 emulator.
X
X*/
X
X#include <stdio.h>
X#include <setjmp.h>
X#include <signal.h>
X
X#include "z80.h"
Xextern char *sys_errlist[];
Xextern int errno;
X
Xjmp_buf lj;
X
Xvoid int_handler()
X{
X  longjmp(lj);
X}
X
Xlong z80_memlines[65536];
Xlong z80_memseeks[65536];
XFILE *z80_file;
X
X
X
Xmain()
X{
X  int i;
X
X  setjmp(lj);
X  signal(SIGINT,int_handler);
X
X  z80_file = 0;
X  while(1)
X  {
X    char ibuf[128], *istat;
X    
X    char cmd_char;
X
X    do {
X      printf("\n>");
X      bzero(ibuf,127);
X      istat = fgets(ibuf, 127, stdin);
X    } while (istat && strlen(ibuf)<2);
X    if(!istat) break;
X    
X    cmd_char = ibuf[0];
X    switch(cmd_char)
X    {
X    case 'd':dump(ibuf);
X    break;
X    case 's':z80_instr(ibuf);
X    break;
X    case 'g':z80_run(ibuf);
X    break;
X    case 'c':pr_reg(ibuf);
X    break;
X    case 'l':gethex(ibuf);
X    break;
X    case 'b':getbin(ibuf);
X    break;
X    case 'm':movemem(ibuf);
X    break;
X    case 'w':writehex(ibuf);
X    break;
X    case 'y':getlines(ibuf);
X    break;
X    case 'r':set_reg(ibuf);
X    break;
X    case 'q':exit(0);
X    break;
X    case 'e':edit(ibuf);
X    break;
X    case '$':user_cmd(ibuf);
X    break;
X    default:help(ibuf);
X    break;
X    }
X  }
X
X}
X
X/*
X
Xon-line help
X
X*/
X
Xhelp(ibuf) char* ibuf;
X{
X  printf("\nb file           - load a binary image\n");
X  printf("c                - display register values\n");
X  printf("d start [len]    - display memory\n");
X  printf("e start          - edit memory\n");
X  printf("g                - start Z-80 running\n");
X  printf("l file           - load hex file\n");
X  printf("m start end dest - move a chunk of memory\n");
X  printf("q                - quit\n");
X  printf("r reg val        - change register/flag value\n");
X  printf("s                - single step\n");
X  printf("w start end file - write hex file\n");
X  printf("y file           - read lines file\n");
X  printf("$                - execute user command\n");
X}
X
X/*
X
Xdump
X
X*/
X
Xdump(ibuf) char* ibuf;
X{
X  int start,end=0;
X
X  if(2!=sscanf(ibuf,"%*s %x %x",&start,&end)) {
X    printf("usage: dump start end\n");
X    return;
X  }
X  pr_mem(start,end);
X}
X
X/*
X
Xedit
X
X*/
X
Xedit(ibuf) char* ibuf;
X{
X  int start,byte;
X  if(2!=sscanf(ibuf,"%*s %x %x",&start,&byte)) {
X    printf("usage: edit address value\n");
X    return;
X  }
X  start&=0xffff;
X  byte&=0xff;
X  real_z80_mem[start]=byte;
X}
X
X/*
X
Xset registers
X
X*/
X
Xset_reg(ibuf) char* ibuf;
X{
X  char reg[80];
X  int i;
X  if(2!=sscanf(ibuf,"%*s %s %x",reg, &i)) {
X    printf("usage: set register value\n");
X    return;
X  }
X  i&=0xffff;
X  if (!strcmp(reg,"pc"))
X    PC=i;
X  else if (!strcmp(reg,"sp"))
X    SP=i;
X  else if (!strcmp(reg,"af"))
X    AF=i;
X  else if (!strcmp(reg,"bc"))
X    BC=i;
X  else if (!strcmp(reg,"de"))
X    DE=i;
X  else if (!strcmp(reg,"hl"))
X    HL=i;
X  else if (!strcmp(reg,"af'"))
X    AF2=i;
X  else if (!strcmp(reg,"bc'"))
X    BC2=i;
X  else if (!strcmp(reg,"de'"))
X    DE2=i;
X  else if (!strcmp(reg,"hl'"))
X    HL2=i;
X  else if (!strcmp(reg,"ix"))
X    IX=i;
X  else if (!strcmp(reg,"iy"))
X    IY=i;
X  else if (!strcmp(reg,"i"))
X    IR=(IR&0xff)|(i<<8);
X  else if (!strcmp(reg,"r"))
X    IR=(IR&0xff00)|i;
X  else {
X    printf("register should be one of: pc sp af bc de hl af' bc' de' hl' ix iy i r\n");
X  }
X}
X
X/*
X
Xdump out memory for the user. A is the starting address. L is the amount
Xof dumping he wants. if L is 0, a default value is supplied.
X
X*/
X
Xpr_mem(a,l)
XWORD a,l;
X{
X  WORD i;
X  int counter=0;
X
X  if (!l)
X    l=0x100;
X  for(i=0;i<l;i++)
X  {
X    if (!(counter%16))
X      printf("%04X- ",(a+i)&0xffff);
X
X    printf("%02X ",real_z80_mem[(a+i)&0xffff]);
X    counter++;
X
X    if (!(counter%16))
X    {
X      char c,j;
X      for (j=15;j>=0;j--)
X      {
X	c=real_z80_mem[(a+i-j)&0xffff]&0x7f;
X        putchar( ((c>0x20) && (c<0x7f))?c:'.' );
X      }
X      printf("\n");
X    }
X  }
X  if (counter%16)
X  {
X    int j;
X    char c;
X    for(j=counter%16;j>0;j--)
X    {
X      c=real_z80_mem[(a+i-j)&0xffff]&0x7f;
X      putchar( ((c>0x20) && (c<0x7f))?c:'.' );
X    }
X    printf("\n");
X  }
X}
X
Xshow_debug_line(addr) WORD addr;
X{
X  char ibuf[1024];
X  int ilow = addr, ihi = addr;
X  if(z80_file) {
X    while(ilow>0 && !z80_memlines[ilow]) ilow--;
X    while(ihi<65536 && !z80_memlines[ilow]) ihi++;
X    printf("(range %d %d)\n",ilow,ihi);
X    fseek(z80_file,z80_memseeks[ilow],0);
X    fgets(ibuf,1023,z80_file);
X    printf("%d: %s",z80_memlines[ilow],ibuf); /* \n included in ibuf... */
X  }
X}
X
Xpr_reg(ibuf) char* ibuf;
X{
X  static char *flag_chars="CNVxHxZS";
X  int i;
X
X  printf("\nA =%02XH BC =%04XH DE =%04XH HL =%04XH SP=%04XH IX=%04XH\n"
X      ,AF>>8,BC,DE,HL,SP,IX);
X  printf("A'=%02XH BC'=%04XH DE'=%04XH HL'=%04XH PC=%04XH IY=%04XH\n"
X      ,AF2>>8,BC2,DE2,HL2,PC,IY);
X
X  printf("\nI=%02XH R=%02XH  F=",IR>>8,IR%0xff);
X  for(i=7;i>=0;i--)
X    putchar( (AF&(1<<i))?flag_chars[i]:'-' );
X  printf(" F'=");
X  for(i=7;i>=0;i--)
X    putchar( (AF2&(1<<i))?flag_chars[i]:'-' );
X  printf("  IFF1=%c  IFF2=%c"
X      ,(INT_FLAGS&IFF1)?'1':'-',(INT_FLAGS&IFF2)?'1':'-');
X
X  printf("\n(PC)=");
X  for(i=PC; i<PC+16; i++) {
X    printf("%02X ",real_z80_mem[i]);
X  }
X  printf("\n(HL)=");
X  for(i=HL; i<HL+16; i++) {
X    printf("%02X ",real_z80_mem[i]);
X  }
X  printf("\n");
X
X  show_debug_line(PC);
X}
X
Xgetlines(ibuf) char* ibuf;
X{
X  char fname[80];
X  char lbuf[1024], *lstat;
X
X  if(z80_file) {
X    int i;
X    fclose(z80_file);
X    for(i = 0; i<65536; i++) {
X      z80_memlines[i] = 0;
X      z80_memseeks[i] = 0;
X    }
X  }
X  sscanf(ibuf,"%*s %s",fname);
X
X  z80_file=fopen(fname,"r");
X  if (z80_file==NULL)
X  {
X    printf("%s: %s",fname,sys_errlist[errno]);
X    return;
X  }
X  /* long z80_memlines[65536]; */
X  do {
X    int addr, line, told;
X    told = ftell(z80_file);
X    lstat = fgets(lbuf, 1023, z80_file);
X    if(!lstat) break;
X    sscanf(lbuf,"%d: %x",&line,&addr);
X    z80_memlines[addr] = line;
X    z80_memseeks[addr] = told;
X  } while(lstat);
X
X  /* fclose(file); */
X}
X
X
Xgethex(ibuf) char* ibuf;
X{
X  char fname[80];
X  FILE *file;
X
X  sscanf(ibuf,"%*s %s",fname);
X
X  file=fopen(fname,"r");
X  if (file==NULL)
X  {
X    printf("%s: %s",fname,sys_errlist[errno]);
X    return;
X  }
X  loadhex(file);
X  fclose(file);
X}
X
Xgetbin(ibuf) char* ibuf;
X{
X  char fname[80];
X  FILE *file;
X  WORD count=0;
X
X  sscanf(ibuf,"%*s %s",fname);
X
X  file=fopen(fname,"r");
X  if (file==NULL)
X  {
X    printf("%s: %s",fname,sys_errlist[errno]);
X    return;
X  }
X  while (!feof(file))
X    real_z80_mem[count++]=getc(file);
X  fclose(file);
X}
X
Xextern BYTE csum();
X/*
X#define HEXCHAR(a) ( ((a)>=10) ? ((a)+'a'-10) : ((a)+'0') )
X*/
Xchar HEXCHAR(a)
Xchar a;
X{
X  return ( ((a)>=10) ? ((a)+'a'-10) : ((a)+'0') );
X}
Xwritehex(ibuf) char* ibuf;
X{
X  char fname[80],c[80];
X  FILE *file;
X  WORD start,end,i,j;
X  BYTE tmp;
X  char counter=0;
X
X  if(3!=sscanf(ibuf,"%*s %hx %hx %s",&start,&end,fname)) {
X    printf("usage: write start end filename\n");
X    return;
X  }
X  end++;
X
X  file=fopen(fname,"a");
X  if (file==NULL)
X  {
X    printf("%s: %s",fname,sys_errlist[errno]);
X    return;
X  }
X  for(i=start;i<=end-32;i+=32)
X  {
X    strcpy(c,":20");
X    c[3]=HEXCHAR(i>>12);
X    c[4]=HEXCHAR((i>>8)&15);
X    c[5]=HEXCHAR((i>>4)&15);
X    c[6]=HEXCHAR(i&15);
X    c[7]='0';
X    c[8]='0';
X    for(j=0;j<32;j++)
X    {
X      c[ 9+2*j]=HEXCHAR(real_z80_mem[i+j]>>4);
X      c[10+2*j]=HEXCHAR(real_z80_mem[i+j]&15);
X    }
X    c[73]=0;
X    tmp=256-csum(c+1);
X    c[73]=HEXCHAR(tmp>>4);
X    c[74]=HEXCHAR(tmp&15);
X    c[75]=0;
X    fprintf(file,"%s\n",c);
X  }
X  if (i<end)
X  {
X    c[0]=':';
X    c[1]=HEXCHAR((end-i)>>4);
X    c[2]=HEXCHAR((end-i)&15);
X    c[3]=HEXCHAR(i>>12);
X    c[4]=HEXCHAR((i>>8)&15);
X    c[5]=HEXCHAR((i>>4)&15);
X    c[6]=HEXCHAR(i&15);
X    c[7]='0';
X    c[8]='0';
X    for (j=0;j<end-i;j++)
X    {
X      c[ 9+2*j]=HEXCHAR(real_z80_mem[i+j]>>4);
X      c[10+2*j]=HEXCHAR(real_z80_mem[i+j]&15);
X    }
X    c[ 9+2*(end-i)]=0;
X    tmp=256-csum(c+1);
X    c[ 9+2*(end-i)]=HEXCHAR(tmp>>4);
X    c[10+2*(end-i)]=HEXCHAR(tmp&15);
X    c[11+2*(end-i)]=0;
X    fprintf(file,"%s\n",c);
X  }
X  fprintf(file,":0000000000\n");
X  fclose(file);
X}
X
Xmovemem(ibuf) char* ibuf;
X{
X  WORD start,end,new,i;
X
X  if(3!=sscanf(ibuf,"%*s %hx %hx %hx",&start,&end,&new)) {
X    printf("usage: move old_start old_end new_start\n");
X    return;
X  }
X
X  for(i=start;i<=end;i++)
X    real_z80_mem[new+(i-start)]=real_z80_mem[i];
X}
X
Xuser_cmd(ibuf) char* ibuf; /* for us, a relocator */
X{
X  WORD start,end,bitmap,offset,i;
X
X  if(4!=sscanf(ibuf,"%*s %hx %hx %hx %hx",&start,&end,&bitmap,&offset)) {
X    printf("usage: user_cmd start end bitmap offset\n");
X    return;
X  }
X  offset&=0xff;
X
X  for (i=start;i<=end;i++)
X    if ( real_z80_mem[bitmap+((i-start)/8)] & (1<<((i-start)%8)) )
X      real_z80_mem[i]+=offset;
X}
SHAR_EOF
$TOUCH -am 0928182090 debug.c &&
chmod 0644 debug.c ||
echo "restore of debug.c failed"
set `wc -c debug.c`;Wc_c=$1
if test "$Wc_c" != "8597"; then
	echo original size 8597, current size $Wc_c
fi
# ============= io_ask.c ==============
echo "x - extracting io_ask.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > io_ask.c &&
X/*
X
Xio_ask.c - interactive I/O for Z-80 emulator.
X
X*/
X
X#include "z80.h"
X#include <stdio.h>
X
XBYTE rdport(addr)
XBYTE addr;
X{
X  short data;
X
X  printf("Z-80 reading from port %02XH:",addr);
X  scanf("%hx",&data);
X  return (BYTE) data;
X}
X
Xwrport(addr,data)
XBYTE addr,data;
X{
X  printf("Z-80 writes %02XH to port %02XH\n",data,addr);
X}
X
XBYTE int_read()
X{
X  short data;
X
X  printf("Z-80 reading for interrupt acknowledge:");
X  scanf("%hx",&data);
X  return (BYTE) data;
X}
X
SHAR_EOF
$TOUCH -am 0928182090 io_ask.c &&
chmod 0644 io_ask.c ||
echo "restore of io_ask.c failed"
set `wc -c io_ask.c`;Wc_c=$1
if test "$Wc_c" != "462"; then
	echo original size 462, current size $Wc_c
fi
# ============= io_mem.c ==============
echo "x - extracting io_mem.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > io_mem.c &&
X/*
X
Xio_mem.c: Implement a 256 byte RAM on the Z-80 I/O space.
X
X*/
X
X#include "z80.h"
X
XBYTE z80_io[256];
X
XBYTE rdport(addr)
XBYTE addr;
X{
X  return z80_io[addr];
X}
X
Xwrport(addr,data)
XBYTE addr,data;
X{
X  z80_io[addr]=data;
X}
X
XBYTE int_read()
X{
X  return 255;
X}
SHAR_EOF
$TOUCH -am 0928182090 io_mem.c &&
chmod 0644 io_mem.c ||
echo "restore of io_mem.c failed"
set `wc -c io_mem.c`;Wc_c=$1
if test "$Wc_c" != "255"; then
	echo original size 255, current size $Wc_c
fi
# ============= lh.c ==============
echo "x - extracting lh.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > lh.c &&
X/*
X
XLoad .HEX format file into Z-80 memory.
X
X*/
X
X/* #include <stdio.h> */
X#define NULL 0
X#include <strings.h> 
X#include <ctype.h> 
X#include "z80.h"
X
XBYTE hexval(),hex_byte(),csum();
X
XBYTE hexval(c)
Xchar c;
X{
X  char *l;
X  static char digits[]="0123456789ABCDEF";
X
X  if (islower(c))
X    c=toupper(c);
X  l=index(digits,c);
X  if (l==NULL)
X    return 255;
X  return l-digits;
X}
X
SHAR_EOF
$TOUCH -am 0928182090 lh.c &&
chmod 0644 lh.c ||
echo "restore of lh.c failed"
set `wc -c lh.c`;Wc_c=$1
if test "$Wc_c" != "373"; then
	echo original size 373, current size $Wc_c
fi
# ============= loadhex.c ==============
echo "x - extracting loadhex.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > loadhex.c &&
X/*
X
XLoad .HEX format file into Z-80 memory.
X
X*/
X
X#include <stdio.h>
X#include <strings.h>
X#include <ctype.h>
X#include "z80.h"
X
XBYTE hexval(),hex_byte(),csum();
X
Xint getline(f,l)
XFILE *f;
Xchar *l;
X{
X  int c;
X
X  while((c=getc(f))!=EOF && c!='\n')
X    *l++=c;
X  if (c==EOF)
X    return 1;
X  *l=0;
X  return 0;
X}
X
Xloadhex(f)
XFILE *f;
X{
X  char line[80],*ptr;
X  BYTE count,parse,i,cs;
X  WORD addr;
X
X  while(!getline(f,line))
X  {
X    ptr=line;
X    if (index(ptr,':')==NULL)
X      continue;
X    ptr=index(ptr,':')+1;
X    if (cs=csum(ptr))
X    {
X      printf("Checksum error: %s\n",ptr);
X      continue;
X    }
X    count=hex_byte(ptr);
X    ptr+= 2;
X    addr=(hex_byte(ptr)<<8)|hex_byte(ptr+2);
X    ptr+= 4;
X    parse=hex_byte(ptr);
X    ptr+= 2;
X
X    /* check parse byte if you want... */
X
X    for(i=0;i<count;i++,ptr+= 2)
X    {
X      real_z80_mem[addr+i]=hex_byte(ptr);
X    }
X  }
X  
X}
X
XBYTE hex_byte(c)
Xchar *c;
X{
X  return ((hexval(*c)<<4)|hexval(*(c+1)));
X}
X
XBYTE hexval(c)
Xchar c;
X{
X  char *l;
X  static char digits[]="0123456789ABCDEF";
X
X  if (islower(c))
X    c=toupper(c);
X  l=index(digits,c);
X  if (l==NULL)
X    return 255;
X  return l-digits;
X}
X
XBYTE csum(l)
Xchar *l;
X{
X  BYTE csum=0;
X
X  while(strlen(l))
X  {
X    csum+=(hex_byte(l)&0xff);
X    l+=2;
X  }
X
X  return csum;
X}
SHAR_EOF
$TOUCH -am 0928182090 loadhex.c &&
chmod 0644 loadhex.c ||
echo "restore of loadhex.c failed"
set `wc -c loadhex.c`;Wc_c=$1
if test "$Wc_c" != "1262"; then
	echo original size 1262, current size $Wc_c
fi
# ============= makedrive.c ==============
echo "x - extracting makedrive.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > makedrive.c &&
X/*
X
Xmakedrive.c - creates a "drive device" file for CP/M - really a file
Xwith a size a multiple of 2K and more than 128K, with the first 16K
Xfilled with $E5, and a hole for the rest up to the EOF. This creates
Xa file that takes up 16K on disk, but has a huge EOF. Under CP/M, the
XEOF of a "drive device" file defines its size, which (of course)
Xdoesn't change.
X
X*/
X
X#include <stdio.h>
X
Xextern int errno;
Xextern char *sys_errlist[];
X
Xusage(name)
Xchar *name;
X{
X  printf("Usage: %s [size] [filename]\n\n",name);
X  printf("size is in K-bytes and must be an even number 128 or greater.\n\n");
X  exit(1);
X}
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X  int size,i;
X  FILE *f;
X
X  if (argc!=3)
X    usage(*argv);
X
X  size=atoi(argv[1]);
X
X  if (size<128 || size%2)
X    usage(*argv);
X
X  f=fopen(argv[2],"w");
X  if (f==NULL)
X  {
X    printf("%s: %s - %s\n",*argv,argv[2],sys_errlist[errno]);
X    exit(1);
X  }
X
X  for (i=0;i<16384*4;i++) /* write out a blank directory */
X    putc(0xE5,f);
X
X  fseek(f,(long)(1024*size-1),0); /* make a big hole */
X  putc(0xE5,f);
X
X  fclose(f);
X
X}
SHAR_EOF
$TOUCH -am 0928182090 makedrive.c &&
chmod 0644 makedrive.c ||
echo "restore of makedrive.c failed"
set `wc -c makedrive.c`;Wc_c=$1
if test "$Wc_c" != "1062"; then
	echo original size 1062, current size $Wc_c
fi
# ============= udbg.c ==============
echo "x - extracting udbg.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > udbg.c &&
X/*
X
Xdebug.c - debugger for z-80 emulator.
X
X*/
X
X#include <stdio.h>
X#include <setjmp.h>
X#include <signal.h>
X
X#include "z80.h"
Xextern char *sys_errlist[];
Xextern int errno;
X
Xjmp_buf lj;
X
Xvoid int_handler()
X{
X  longjmp(lj);
X}
X
Xlong z80_memlines[65536];
Xlong z80_memseeks[65536];
XFILE *z80_file;
X
X
X#include <sgtty.h>
Xextern struct sgttyb tty_sgtty_data;
X
X
Xdebug_write(x,y)
X     WORD x;
X     BYTE y;
X{
X  if(x==TWRTval) {		/* bdos storage of user # == 0xedfe*/
X    printf("\n\r0x%04x:%02x\n\r",x,y);
X    debugit();
X  }
X  real_z80_mem[x]=y;
X}
X
X
Xdebugit()
X{
X  int i;
X
X  gtty(fileno(stdin),&tty_sgtty_data);
X  tty_sgtty_data.sg_flags&=~RAW;
X  tty_sgtty_data.sg_flags|=ECHO;
X  stty(fileno(stdin),&tty_sgtty_data);
X
X  z80_file = 0;
X  while(1)
X  {
X    char ibuf[128], *istat;
X    
X    char cmd_char;
X
X    do {
X      printf("\n>");
X      bzero(ibuf,127);
X      istat = fgets(ibuf, 127, stdin);
X    } while (istat && strlen(ibuf)<2);
X    if(!istat) break;
X    
X    cmd_char = ibuf[0];
X    switch(cmd_char)
X    {
X    case 'd':dump(ibuf);
X    break;
X    case 'G':/* z80_run(ibuf) */
X  gtty(fileno(stdin),&tty_sgtty_data);
X  tty_sgtty_data.sg_flags|=RAW;
X  tty_sgtty_data.sg_flags&=~ECHO;
X  stty(fileno(stdin),&tty_sgtty_data);
X      return 1;
X    case 'g':/* z80_run(ibuf) */
X  gtty(fileno(stdin),&tty_sgtty_data);
X  tty_sgtty_data.sg_flags|=RAW;
X  tty_sgtty_data.sg_flags&=~ECHO;
X  stty(fileno(stdin),&tty_sgtty_data);
X      return 0;
X    break;
X    case 's':z80_instr(ibuf);
X      /* break; */ /* really fall through */
X    case 'c':pr_reg(ibuf);
X    break;
X    case 'l':gethex(ibuf);
X    break;
X    case 'b':getbin(ibuf);
X    break;
X    case 'm':movemem(ibuf);
X    break;
X    case 'w':writehex(ibuf);
X    break;
X    case 'y':getlines(ibuf);
X    break;
X    case 'r':set_reg(ibuf);
X    break;
X    case 'q':
X  gtty(fileno(stdin),&tty_sgtty_data);
X  tty_sgtty_data.sg_flags|=RAW;
X  tty_sgtty_data.sg_flags&=~ECHO;
X  stty(fileno(stdin),&tty_sgtty_data);
X      exit(0);
X    break;
X    case 'e':edit(ibuf);
X    break;
X    case '$':user_cmd(ibuf);
X    break;
X    default:help(ibuf);
X    break;
X    }
X  }
X
X}
X
X/*
X
Xon-line help
X
X*/
X
Xhelp(ibuf) char* ibuf;
X{
X  printf("\nb file           - load a binary image\n");
X  printf("c                - display register values\n");
X  printf("d start [len]    - display memory\n");
X  printf("e start          - edit memory\n");
X  printf("g                - start Z-80 running\n");
X  printf("l file           - load hex file\n");
X  printf("m start end dest - move a chunk of memory\n");
X  printf("q                - quit\n");
X  printf("r reg val        - change register/flag value\n");
X  printf("s                - single step\n");
X  printf("w start end file - write hex file\n");
X  printf("y file           - read lines file\n");
X  printf("$                - execute user command\n");
X}
X
X/*
X
Xdump
X
X*/
X
Xdump(ibuf) char* ibuf;
X{
X  int start,end=0;
X
X  if(2!=sscanf(ibuf,"%*s %x %x",&start,&end)) {
X    printf("usage: dump start end\n");
X    return;
X  }
X  pr_mem(start,end);
X}
X
X/*
X
Xedit
X
X*/
X
Xedit(ibuf) char* ibuf;
X{
X  int start,byte;
X  if(2!=sscanf(ibuf,"%*s %x %x",&start,&byte)) {
X    printf("usage: edit address value\n");
X    return;
X  }
X  start&=0xffff;
X  byte&=0xff;
X  real_z80_mem[start]=byte;
X}
X
X/*
X
Xset registers
X
X*/
X
Xset_reg(ibuf) char* ibuf;
X{
X  char reg[80];
X  int i;
X  if(2!=sscanf(ibuf,"%*s %s %x",reg, &i)) {
X    printf("usage: set register value\n");
X    return;
X  }
X  i&=0xffff;
X  if (!strcmp(reg,"pc"))
X    PC=i;
X  else if (!strcmp(reg,"sp"))
X    SP=i;
X  else if (!strcmp(reg,"af"))
X    AF=i;
X  else if (!strcmp(reg,"bc"))
X    BC=i;
X  else if (!strcmp(reg,"de"))
X    DE=i;
X  else if (!strcmp(reg,"hl"))
X    HL=i;
X  else if (!strcmp(reg,"af'"))
X    AF2=i;
X  else if (!strcmp(reg,"bc'"))
X    BC2=i;
X  else if (!strcmp(reg,"de'"))
X    DE2=i;
X  else if (!strcmp(reg,"hl'"))
X    HL2=i;
X  else if (!strcmp(reg,"ix"))
X    IX=i;
X  else if (!strcmp(reg,"iy"))
X    IY=i;
X  else if (!strcmp(reg,"i"))
X    IR=(IR&0xff)|(i<<8);
X  else if (!strcmp(reg,"r"))
X    IR=(IR&0xff00)|i;
X  else {
X    printf("register should be one of: pc sp af bc de hl af' bc' de' hl' ix iy i r\n");
X  }
X}
X
X/*
X
Xdump out memory for the user. A is the starting address. L is the amount
Xof dumping he wants. if L is 0, a default value is supplied.
X
X*/
X
Xpr_mem(a,l)
XWORD a,l;
X{
X  WORD i;
X  int counter=0;
X
X  if (!l)
X    l=0x100;
X  for(i=0;i<l;i++)
X  {
X    if (!(counter%16))
X      printf("%04X- ",(a+i)&0xffff);
X
X    printf("%02X ",real_z80_mem[(a+i)&0xffff]);
X    counter++;
X
X    if (!(counter%16))
X    {
X      char c; int j;
X      for (j=15;j>=0;j--)
X      {
X	c=real_z80_mem[(a+i-j)&0xffff]&0x7f;
X        putchar( ((c>0x20) && (c<0x7f))?c:'.' );
X      }
X      printf("\n");
X    }
X  }
X  if (counter%16)
X  {
X    int j;
X    char c;
X    for(j=counter%16;j>0;j--)
X    {
X      c=real_z80_mem[(a+i-j)&0xffff]&0x7f;
X      putchar( ((c>0x20) && (c<0x7f))?c:'.' );
X    }
X    printf("\n");
X  }
X}
X
Xshow_debug_line(addr) WORD addr;
X{
X  char ibuf[1024];
X  int ilow = addr, ihi = addr;
X  if(z80_file) {
X    while(ilow>0 && !z80_memlines[ilow]) ilow--;
X    while(ihi<65536 && !z80_memlines[ilow]) ihi++;
X    printf("(range %d %d)\n",ilow,ihi);
X    fseek(z80_file,z80_memseeks[ilow],0);
X    fgets(ibuf,1023,z80_file);
X    printf("%d: %s",z80_memlines[ilow],ibuf); /* \n included in ibuf... */
X  }
X}
X
Xpr_reg(ibuf) char* ibuf;
X{
X  static char *flag_chars="CNVxHxZS";
X  int i;
X
X  printf("\nA =%02XH BC =%04XH DE =%04XH HL =%04XH SP=%04XH IX=%04XH\n"
X      ,AF>>8,BC,DE,HL,SP,IX);
X  printf("A'=%02XH BC'=%04XH DE'=%04XH HL'=%04XH PC=%04XH IY=%04XH\n"
X      ,AF2>>8,BC2,DE2,HL2,PC,IY);
X
X  printf("\nI=%02XH R=%02XH  F=",IR>>8,IR%0xff);
X  for(i=7;i>=0;i--)
X    putchar( (AF&(1<<i))?flag_chars[i]:'-' );
X  printf(" F'=");
X  for(i=7;i>=0;i--)
X    putchar( (AF2&(1<<i))?flag_chars[i]:'-' );
X  printf("  IFF1=%c  IFF2=%c"
X      ,(INT_FLAGS&IFF1)?'1':'-',(INT_FLAGS&IFF2)?'1':'-');
X
X  printf("\n(PC)=");
X  for(i=PC; i<PC+16; i++) {
X    printf("%02X ",real_z80_mem[i]);
X  }
X  printf("\n(HL)=");
X  for(i=HL; i<HL+16; i++) {
X    printf("%02X ",real_z80_mem[i]);
X  }
X  printf("\n(SP)=");
X  for(i=SP; i<SP+16; i++) {
X    printf("%02X ",real_z80_mem[i]);
X  }
X  printf("\n");
X
X  show_debug_line(PC);
X}
X
Xgetlines(ibuf) char* ibuf;
X{
X  char fname[80];
X  char lbuf[1024], *lstat;
X
X  if(z80_file) {
X    int i;
X    fclose(z80_file);
X    for(i = 0; i<65536; i++) {
X      z80_memlines[i] = 0;
X      z80_memseeks[i] = 0;
X    }
X  }
X  sscanf(ibuf,"%*s %s",fname);
X
X  z80_file=fopen(fname,"r");
X  if (z80_file==NULL)
X  {
X    printf("%s: %s",fname,sys_errlist[errno]);
X    return;
X  }
X  /* long z80_memlines[65536]; */
X  do {
X    int addr, line, told;
X    told = ftell(z80_file);
X    lstat = fgets(lbuf, 1023, z80_file);
X    if(!lstat) break;
X    sscanf(lbuf,"%d: %x",&line,&addr);
X    z80_memlines[addr] = line;
X    z80_memseeks[addr] = told;
X  } while(lstat);
X
X  /* fclose(file); */
X}
X
X
Xgethex(ibuf) char* ibuf;
X{
X  char fname[80];
X  FILE *file;
X
X  sscanf(ibuf,"%*s %s",fname);
X
X  file=fopen(fname,"r");
X  if (file==NULL)
X  {
X    printf("%s: %s",fname,sys_errlist[errno]);
X    return;
X  }
X  loadhex(file);
X  fclose(file);
X}
X
Xgetbin(ibuf) char* ibuf;
X{
X  char fname[80];
X  FILE *file;
X  WORD icount=0,count=0;
X
X  if(2!=sscanf(ibuf,"%*s %hx %s",&icount,fname)) {
X    printf("usage: getbin offset filename\n");
X    return;
X  };
X
X  file=fopen(fname,"r");
X  if (file==NULL)
X  {
X    printf("%s: %s",fname,sys_errlist[errno]);
X    return;
X  }
X  count=icount;
X  printf("loading %s into %04x\n",fname,count);
X  while (!feof(file))
X    real_z80_mem[count++]=getc(file);
X  fclose(file);
X  printf("loaded %d bytes (save %d foo.com)\n",count-icount,((count-icount)/256)+1);
X}
X
Xextern BYTE csum();
X/*
X#define HEXCHAR(a) ( ((a)>=10) ? ((a)+'a'-10) : ((a)+'0') )
X*/
Xchar HEXCHAR(a)
Xchar a;
X{
X  return ( ((a)>=10) ? ((a)+'a'-10) : ((a)+'0') );
X}
Xwritehex(ibuf) char* ibuf;
X{
X  char fname[80],c[80];
X  FILE *file;
X  WORD start,end,i,j;
X  BYTE tmp;
X  char counter=0;
X
X  if(3!=sscanf(ibuf,"%*s %hx %hx %s",&start,&end,fname)) {
X    printf("usage: write start end filename\n");
X    return;
X  }
X  end++;
X
X  file=fopen(fname,"a");
X  if (file==NULL)
X  {
X    printf("%s: %s",fname,sys_errlist[errno]);
X    return;
X  }
X  for(i=start;i<=end-32;i+=32)
X  {
X    strcpy(c,":20");
X    c[3]=HEXCHAR(i>>12);
X    c[4]=HEXCHAR((i>>8)&15);
X    c[5]=HEXCHAR((i>>4)&15);
X    c[6]=HEXCHAR(i&15);
X    c[7]='0';
X    c[8]='0';
X    for(j=0;j<32;j++)
X    {
X      c[ 9+2*j]=HEXCHAR(real_z80_mem[i+j]>>4);
X      c[10+2*j]=HEXCHAR(real_z80_mem[i+j]&15);
X    }
X    c[73]=0;
X    tmp=256-csum(c+1);
X    c[73]=HEXCHAR(tmp>>4);
X    c[74]=HEXCHAR(tmp&15);
X    c[75]=0;
X    fprintf(file,"%s\n",c);
X  }
X  if (i<end)
X  {
X    c[1]=HEXCHAR((end-i)>>4);
X    c[2]=HEXCHAR((end-i)&15);
X    c[3]=HEXCHAR(i>>12);
X    c[4]=HEXCHAR((i>>8)&15);
X    c[5]=HEXCHAR((i>>4)&15);
X    c[6]=HEXCHAR(i&15);
X    c[7]='0';
X    c[8]='0';
X    for (j=0;j<end-i;j++)
X    {
X      c[ 9+2*j]=HEXCHAR(real_z80_mem[i+j]>>4);
X      c[10+2*j]=HEXCHAR(real_z80_mem[i+j]&15);
X    }
X    c[ 9+2*(end-i)]=0;
X    tmp=256-csum(c+1);
X    c[ 9+2*(end-i)]=HEXCHAR(tmp>>4);
X    c[10+2*(end-i)]=HEXCHAR(tmp&15);
X    c[11+2*(end-i)]=0;
X    fprintf(file,"%s\n",c);
X  }
X  fprintf(file,":0000000000\n");
X  fclose(file);
X}
X
Xmovemem(ibuf) char* ibuf;
X{
X  WORD start,end,new,i;
X
X  if(3!=sscanf(ibuf,"%*s %hx %hx %hx",&start,&end,&new)) {
X    printf("usage: move old_start old_end new_start\n");
X    return;
X  }
X
X  for(i=start;i<=end;i++)
X    real_z80_mem[new+(i-start)]=real_z80_mem[i];
X}
X
Xuser_cmd(ibuf) char* ibuf; /* for us, a relocator */
X{
X  WORD start,end,bitmap,offset,i;
X
X  if(4!=sscanf(ibuf,"%*s %hx %hx %hx %hx",&start,&end,&bitmap,&offset)) {
X    printf("usage: user_cmd start end bitmap offset\n");
X    return;
X  }
X  offset&=0xff;
X
X  for (i=start;i<=end;i++)
X    if ( real_z80_mem[bitmap+((i-start)/8)] & (1<<((i-start)%8)) )
X      real_z80_mem[i]+=offset;
X}
SHAR_EOF
$TOUCH -am 0928182090 udbg.c &&
chmod 0644 udbg.c ||
echo "restore of udbg.c failed"
set `wc -c udbg.c`;Wc_c=$1
if test "$Wc_c" != "9809"; then
	echo original size 9809, current size $Wc_c
fi
# ============= upm.c ==============
echo "x - extracting upm.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > upm.c &&
X/*
X
Xup/m - unix CP/M.
X
X(C) MCMXC - Nick Sayer - All rights reserved.
X
XSee COPYRIGHT file for more details.
X
X
XConfiguration section.
X
X
XCPM_FILE - default file with CP/M HEX images.
X
XAddresses 0-2K are CCP, 2K-5.5K - BDOS. 5.5K-16K - BIOS. Address 65535
Xwill have the length (in pages) of CP/M.
X
XAddresses 16K-? - relocation bitmap. Each bit in the bitmap represents
Xa BYTE in 0-16K. If the bit is 1, the high byte of the first address of
Xthe destination address for the CCP should be added to the byte in 0-16K
Ximage. That image is then moved to the proper location. The PC is set to
X32K and the Z-80 starts running. The high byte of the first byte of the
XCCP destination address is gotten from address 65535. This value is
Xthen adjusted if CP/M is to be relocated lower than the default
Xtop of memory. The CP/M image to load should have a resident run address
Xbefore relocation of 0.
X
X*/
X#define CPM_FILE "CPM"
X
X#include <strings.h>
X#include <ctype.h>
X#include <sgtty.h>
X#include <signal.h>
X
X#include "upm.h"
X
XWORD topmem=256;       /* These may be changed by arguments */
Xchar *cpm_file=CPM_FILE;
X
Xstruct sgttyb tty_sgtty_data;
X
Xstatic BYTE dph[32]={
X
X/* Disk Paramater Header (diskbufs[]) */
X
X0x00,0x00,	/* TRANSTABLE unused */
X0x00,0x00,	/* unused */
X0x00,0x00,
X0x00,0x00,
X0x80,0xff,	/* DIRBUF - patch with topmem-1 later */
X0x10,0xff,	/* DPB - patch with OUR page no. */
X0x00,0x00,	/* CHKVEC - not used */
X0x20,0xff,	/* ALLOCVEC - patch with OUR page no. */
X
X/* Disk Paramater Block */
X
X0x40,0x00,	/* Sectors per track */
X0x04,		/* Block Shift */
X0x0f,		/* Block Mask */
X0x00,		/* Extent Mask */
X0x00,0x10,	/* Blocks on device - patch if variable size implemented */
X0xff,0x01,	/* Directory entries -1 */
X0xff,0x00,	/* Allocation masks for directory */
X0x00,0x00,	/* Check vector size - patch if checkvecs implemented */
X0x00,0x00,	/* offset to first user-track */
X0x00		/* spare */
X};
X
X/* These are externs in upm.h, but they have to be declared somewhere. */
X
XFILE *disks[16],*devices[5];
XWORD diskbufs[16];
XBYTE ccp_image[SIZE_CCP_IMAGE];
XWORD ccp_start;
X
X/*
X
Xrelocade(address);
XBYTE address;
X
XRelocate CP/M image at 0000-4000 using bitmap at 4000-4800.
XCopy image to new location, and copy first 5.5K to ccp_image[];
XStop if we get to the top of RAM.
X
X*/
X
Xrelocate(add,len)
XBYTE add;
X{
X  WORD i;
X
X  for (i=0;i!=(256*len);i++)
X  {
X    if ( real_z80_mem[16384+(i>>3)]&(1<<(i&7)) )
X      real_z80_mem[i]=real_z80_mem[i]+add;
X    real_z80_mem[i+(add<<8)]=real_z80_mem[i];
X    if (i<SIZE_CCP_IMAGE)
X      ccp_image[i]=real_z80_mem[i];
X  }
X}
Xint debugflag = 0;
Xint dlogflag = 0;
X
Xdebugit();
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X  FILE *cpm;
X  char line[80];
X  int i;
X
X  signal(SIGINT,debugit);
X
X  for(i=0;i<16;i++)
X  {
X    disks[i]=NULL;
X    devices[i%5]=NULL; /* hack: only do 0-4 */
X  }
X
X/* OPEN ~/.upmrc, send each line to process_args(line); */
X
X  argc--; argv++;
X  for(;argc--;argv++)
X    process_args(*argv);
X
X  if (disks[0]==NULL)
X  {
X    printf("A: must be assigned.\n");
X    exit(0);
X  }
X
X/*
X
XFor each non-null pointer in disks[], lower topmem, and save the pointer
Xto diskbufs[]. This assigns space for the allocation vector, and the
XDPH/DPB. Then copy in a "standard" DPH/DPB into the bottom. We'll
Xpatch it later.
X
XWe really should allow disks to be sized at runtime, but for now
Xthey're fixed at 8MB, so we lower topmem 3 for each one. 2 pages
Xfor the alloc table, another page (actually 32 bytes)
Xfor the miscelany.
X
X*/
X
X  for(i=0;i!=16;i++)
X  {
X    char j;
X
X    if (disks[i]==NULL)
X      continue;
X    topmem-=3;
X    diskbufs[i]=topmem<<8;
X    for(j=0;j!=32;j++)
X      real_z80_mem[diskbufs[i]+j]=dph[j];
X    real_z80_mem[diskbufs[i]+0x0b]=real_z80_mem[diskbufs[i]+0x0f]=topmem;
X  }
X
X/*
X
XNow for each non-null disk[] readjust the dirbuf pointer.
XWe couldn't do it before because we didn't have a final
Xlocation for dirbuf. topmem-1 is the high-byte of the
Xfinal location for dirbuf. the low byte is 0x80.
XThis page of memory is shared with the BIOS "jump" table.
X
X*/
X
X  for(i=0;i!=16;i++)
X  {
X    if (disks[i]==NULL)
X      continue;
X    real_z80_mem[diskbufs[i]+9]=topmem-1;
X  }
X
X  cpm=fopen(cpm_file,"r");
X  if (cpm==NULL)
X  {
X    printf("Can't open CP/M binaries: %s\n",sys_errlist[errno]);
X    exit(1);
X  }
X  loadhex(cpm);
X
X  ccp_start=(topmem-real_z80_mem[65535])<<8;
X  relocate(topmem-real_z80_mem[65535],real_z80_mem[65535]);
X
X  PC=0x8000;
X
X/*
X
XNow set up the terminal. Just toggling RAW should be enough.
X
X*/
X
X  gtty(fileno(stdin),&tty_sgtty_data);
X  tty_sgtty_data.sg_flags|=RAW;
X  tty_sgtty_data.sg_flags&=~ECHO;
X  stty(fileno(stdin),&tty_sgtty_data);
X
X  do
X  { z80_run();
X    if(debugflag>1 && debugit()) PC++;
X    else if(bios()) break;
X  } while(1); /*  }  while(!bios()); */
X
X  gtty(fileno(stdin),&tty_sgtty_data);
X  tty_sgtty_data.sg_flags&=~RAW;
X  tty_sgtty_data.sg_flags|=ECHO;
X  stty(fileno(stdin),&tty_sgtty_data);
X}
X
X/*
X
XThe arguments can include A:file-O:file, {TY, LP, PT, U1, U2}:file,
Xmem:0-128.
X
XDisk files are fopen()ed "r+", and are assigned to disks[].
XDevice files are fopen()ed "r+" and are assigned to devices[], except
Xfor LP:, which is fopen()ed "w".
X
XThe value after mem: lowers the top of memory by that many pages (256
Xbytes) to save space for things like BYE, etc.
X
XThe arguments MUST be stripped of white-spaces.
X
X*/
X
Xprocess_args(arg)
Xchar *arg;
X{
X  char i,left[80],*right;
X
X    if (index(arg,':')==NULL)
X    {
X      printf("Missing ':' in argument.\n");
X      return;
X    }
X
X    strcpy(left,arg);
X    right=index(left,':')+1;
X    *index(left,':')='\0';
X    for(i=0;i!=strlen(left);i++)
X      if (islower(left[i]))
X        left[i]=toupper(left[i]);
X
X    if (strlen(left)==1 && (*left<='O') && (*left>='A'))
X    {
X      disks[*left-'A']=fopen(right,"r+");
X      return;
X    }
X    if (!strcmp(left,"MEM"))
X    {
X      topmem-=atoi(right);
X      return;
X    }
X    if (!strcmp(left,"TY"))
X    {
X      devices[F_TY]=fopen(right,"r+");
X      return;
X    }
X    if (!strcmp(left,"PT"))
X    {
X      devices[F_PT]=fopen(right,"r+");
X      return;
X    }
X    if (!strcmp(left,"LP"))
X    {
X      devices[F_LP]=fopen(right,"w");
X      return;
X    }
X    if (!strcmp(left,"U1"))
X    {
X      devices[F_U1]=fopen(right,"r+");
X      return;
X    }
X    if (!strcmp(left,"U2"))
X    {
X      devices[F_U2]=fopen(right,"r+");
X      return;
X    }
X    if (!strcmp(left,"DEBUG"))
X    {
X      debugflag=atoi(right);
X      return;
X    }
X    if (!strcmp(left,"DLOG"))
X    {
X      dlogflag=atoi(right);
X      return;
X    }
X    if (!strcmp(left,"BIOS"))
X    {
X      biosflag=atoi(right);
X      return;
X    }
X    if (!strcmp(left,"TRAP"))
X    {
X      sscanf(right,"%4x",&TRAPval);
X      return;
X    }
X    if (!strcmp(left,"TWRT"))
X    {
X      sscanf(right,"%4x",&TWRTval);
X      return;
X    }
X}
X
Xcoredump()
X{
X  FILE *qb;
X  int i;
X
X  qb=fopen("mem","w");
X  for(i=0;i!=65536;i++)
X    putc(real_z80_mem[i],qb);
X  fclose(qb);
X}
SHAR_EOF
$TOUCH -am 0928214490 upm.c &&
chmod 0644 upm.c ||
echo "restore of upm.c failed"
set `wc -c upm.c`;Wc_c=$1
if test "$Wc_c" != "6850"; then
	echo original size 6850, current size $Wc_c
fi
# ============= upm.h ==============
echo "x - extracting upm.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > upm.h &&
X/*
X
Xupm.h - common data for upm.
X
X(C) MCMXC - Nick Sayer - All rights reserved.
X
X*/
X
X#include <stdio.h>
X#include "z80.h"
Xextern char *sys_errlist[];
Xextern int errno;
X
Xextern FILE *disks[16],*devices[5]; /* These are FILEs for CP/M devices */
X
Xextern WORD diskbufs[16]; /* These are pointers to disk paramater
X                             tables. See sel_dsk() */
X
X/* Which devices[] go to which physical devices? */
X#define F_TTY 0
X#define F_LPT 1
X#define F_PTP 2
X#define F_PTR 2
X#define F_UL1 3
X#define F_UC1 3
X#define F_UP1 3
X#define F_UR1 3
X#define F_UR2 4
X#define F_UP2 4
X
X/* Which command line args go to which physical devices? */
X#define F_TY 0
X#define F_LP 1
X#define F_PT 2
X#define F_U1 3
X#define F_U2 4
X
X/*
X
XWhen CP/M is run on a real system, the warm boot procedure reads
Xin the CCP image from disk. Of course, we don't have a real disk,
Xso the startup procedure for the emulator places the relocated
Ximage of the ccp into the following structure. Actually, the
Ximage size is 5.5K, so it incorporates BDOS as well.
X
X*/
X
X#define SIZE_CCP_IMAGE 5632
Xextern BYTE ccp_image[SIZE_CCP_IMAGE]; /* Save CCP for warm boot */
Xextern WORD ccp_start; /* ... and where to put it */
X
X/*
X
Xbios() implements the CP/M bios in C. The BIOS function to execute is
Xdetermined by ((PC&0xff)/3). This assumes the BIOS starts on an even
Xpage boundary, which in most cases is a requirement anyway. This allows
Xall sorts of silly things to happen if programs crunch up in strange
Xways, but that happens a lot with CP/M anyway. This routine takes no
Xformal arguments, and has all sorts of side effects on the Z-80
Xregister pairs and the Z-80 memory space. The return value is non-zero
Xif the EXIT BIOS entry (HALT at xx5D) is called.
X
X*/
X
Xextern char bios(); 
SHAR_EOF
$TOUCH -am 0928182090 upm.h &&
chmod 0644 upm.h ||
echo "restore of upm.h failed"
set `wc -c upm.h`;Wc_c=$1
if test "$Wc_c" != "1744"; then
	echo original size 1744, current size $Wc_c
fi
# ============= z80.c ==============
echo "x - extracting z80.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > z80.c &&
X/*
X
Xz80.c - Z-80 microprocessor emulator.
X
XCopyright MCMXC - Nick Sayer - All rights reserved.
X
XSee COPYRIGHT file for details.
X
Xv0.0   - 04/08/90 - epoch
Xv0.0A0 - 04/13/90 - alpha-test.
Xv0.0A1 - 08/04/90 - alpha-test 2.
Xv0.0A2 - 09/04/90 - alpha-test 3.
X
Xglobal data types:
X
XWORD = unsigned short - i.e. an address or register pair.
XBYTE = unsigned char  - i.e. a memory location or register.
X
Xglobal data:
X
XBYTE z80_mem[65536];
XWORD AF,BC,DE,HL,SP,PC,IX,IY,IR,AF2,BC2,DE2,HL2,INT_FLAGS;
X
Xglobal routines:
X
Xz80_run();
X
X	Start running at addr. PRESUMES PC AND OTHER REGISTERS SET PROPERLY!!!
X	Returns if Z-80 executes a HALT. Returns with PC set to address of HALT
X	instruction.
X
Xz80_instr();
X
X	Execute a single instruction.
X
Xwrport(addr,data);
XBYTE addr,data;
X
XBYTE rdport(addr);
XBYTE addr;
X
X	These routines are called by the Z-80 when it wants to read or write
X	to the port-space.
X
Xchar INT,NMI,RESET;
X
X	Each of these starts at 0. If some event makes any of these true, the
X	event each represents will take place. Think of them as the coresponding
X	wires that go into the CPU, except that in the real CPU these wires are
X	inverse logic.
X
XBYTE int_read();
X
X	This routine called on an interrupt. It should return the proper
X	interrupt acknowledgement cycle data.
X
XKNOWN "FEATURES":
X
X	This actually simulates a MOSTEK MK 3880. Whether or not this
X	device differs from the Zilog product, I don't know. But I
X	doubt it.
X
X	If you single-step using z80_instr(), memory refresh, interrupt
X	checking, and similar housekeeping will NOT take place.
X
X	If the processor is in interrupt mode 0, the int_read()
X        value MUST be an RST instruction.
X
X	Undefined opcode sequences WILL have truely bizarre and twisted
X	results. Count on it. Especially undefined DD/FD operations.
X
X	"Interrupting devices" at this time can't tell when an interrupt
X	service routine is finished. Normally they monitor the bus
X	waiting for a RETI instruction. There's no way to do that with
X	this code yet.
X
X*/
X
X#include "z80.h"
X
XBYTE real_z80_mem[65536];
Xchar STOP_FLAG; /* hack to stop us on HALT */
Xchar INT=0,NMI=0,RESET=0;
XWORD AF,BC,DE,HL,PC,SP,IX,IY,IR,AF2,BC2,DE2,HL2,INT_FLAGS;
Xint TRAPval = -1, lastPC = -1;
X
Xz80_run()
X{
X  STOP_FLAG=0;
X  do
X  {
X
X/* do an instruction */
X
X    if (PC == TRAPval) {
X      printf("\n\rTrapping at 0x%04x (last=0x%04x)\n\r",PC,lastPC);
X      debugit();
X    }
X    lastPC = PC;
X    z80_instr();
X
X/* If we did an EI instruction before last, set both IFFs */
X
X  if (INT_FLAGS&IFTMP)
X  {
X    INT_FLAGS-=0x10;
X    if (!(INT_FLAGS&IFTMP))
X      INT_FLAGS|=IFF1|IFF2;
X  }
X
X/* If an interrupt is pending and they're enabled, do it */
X
X  if (INT && INT_FLAGS&IFF1)
X  {
X    register WORD operand;
X
X    INT_FLAGS&=~(IFF1|IFF2);
X    push(PC);
X    switch (INT_FLAGS&IM_STAT)
X    {
X      case 0:PC=int_read()&0x38; /* DANGEROUSLY assumes an RST op... */
X      break;
X      case 1:PC=0x38; int_read(); /* we have to fetch, then spike it */
X      break;
X      case 2:operand=(IR&0xff80)|(int_read()&0xfe);
X	     PC=z80_mem(operand)|(z80_mem(operand+1)<<8);
X      break;
X    }
X    INT=0;
X  }
X
X/* If an NMI is pending, do it */
X
X  if (NMI)
X  {
X    INT_FLAGS&=~IFF1;
X    push(PC);
X    PC=0x66;
X    NMI=0;
X  }
X
X/* if a RESET is pending, that has absolute priority */
X
X  if (RESET)
X  {
X    INT=0; NMI=0; RESET=0; INT_FLAGS=0; PC=0;
X  }
X
X/* Now do a "refresh" cycle (really just increment low 7 bits of IR) */
X
X  IR=(IR&0xff00)|((IR+1)&0x7f);
SHAR_EOF
echo "End of  part 1"
echo "File z80.c is continued in part 2"
echo "2" > shar3_seq_.tmp
exit 0
-- 
Nick Sayer              | Disclaimer: "Don't try this at home, | RIP: Mel Blanc
mrapple@quack.sac.ca.us | kids. This should only be done by    |   1908-1989
N6QQQ  [44.2.1.17]      | trained, professional idiots."       |  May he never
209-952-5347 (Telebit)  |                     --Plucky Duck    |  be silenced.

mrapple@quack.sac.ca.us (Nick Sayer) (11/09/90)

#!/bin/sh
# this is foo.02 (part 2 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file z80.c continued
#
if touch 2>&1 | fgrep '[-amc]' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
if test ! -r shar3_seq_.tmp; then
	echo "Please unpack part 1 first!"
	exit 1
fi
(read Scheck
 if test "$Scheck" != 2; then
	echo "Please unpack part $Scheck next!"
	exit 1
 else
	exit 0
 fi
) < shar3_seq_.tmp || exit 1
echo "x - Continuing file z80.c"
sed 's/^X//' << 'SHAR_EOF' >> z80.c &&
X
X/* If last instruction was a HALT, we're out of here - NOT what a real
X   Z-80 does, of course. */
X 
X  }
X  while (!STOP_FLAG);
X}
X
Xz80_instr()
X{
X  BYTE opcode,op_p1,op_p2,op_p3,op_p13;
X
X  opcode=z80_mem(PC++);
X  op_p1=opcode&0x7;                /* Piece 1 - bottom 3 bits */
X  op_p2=(opcode&0x38)>>3;          /* Piece 2 - middle 3 bits */
X  op_p3=(opcode&0xC0)>>6;          /* Piece 3 - top 2 bits */
X  op_p13=(opcode&0xC7);            /* Piece 1 and 3 OR'ed */
X
X/*
X
XNow we perform what amounts to just a bunch of ifs...
X
XSections noted as Z-80 only are not present on the 8080. To make
Xan 8080 interpreter, comment these sections out. This is not
Xstrictly necessary, however, since an 8080 program (e.g. CP/M)
Xwill run just fine on a Z-80.
X
XFirst take care of the multi-byte opcodes. These are Z-80 only.
X
X*/
X
X  if (opcode==0xCB)
X  {
X    dlog("CB:");
X    cb_ops(HL); /* see notes in z80_cbed.c */
X    return;
X  }
X  if (opcode==0xFD || opcode==0xDD)
X  {
X    dlog(opcode==0xFD?"FD:":"DD:");
X    ixy_ops(opcode==0xFD); /* pass 1 if IY */
X    return;
X  }
X  if (opcode==0xED)
X  {
X    dlog("ED:");
X    ed_ops();
X    return;
X  }
X  
X/*
X
XNOP & HALT
X
X*/
X
X  if (opcode==0x00) /* NOP */
X    {
X      dlog("NOP\n");
X      return;
X    }
X
X  if (opcode==0x76) /* HALT */
X  {
X    STOP_FLAG++;
X    PC--;
X    dlog("HALT\n");
X    return;
X  }
X
X/*
X
XInterrupt control: EI, DI
X
X*/
X
X  if (opcode==0xFB) /* EI */
X  {
X    INT_FLAGS|=0x20; /* counter decremented after this & next instr */
X    dlog("EI\n");
X    return;
X  }
X
X  if (opcode==0xF3) /* DI */
X  {
X    INT_FLAGS&=~(IFF1|IFF2);
X    dlog("DI\n");
X    return;
X  }
X/*
X
XExchange group: EX, EXX
X
X*/
X
X  if (opcode==0x08) /* EX AF,AF' [Z-80 only] */
X  {
X    register WORD temp;
X    temp=AF; AF=AF2; AF2=temp;
X    dlog("EX AF,AF'\n");
X    return;
X  }
X  if (opcode==0xEB) /* EX DE,HL */
X  {
X    register WORD temp;
X    temp=DE; DE=HL; HL=temp;
X    dlog("EX DE,HL\n");
X    return;
X  }
X  if (opcode==0xE3) /* EX (SP),HL */
X  {
X    register WORD temp;
X    temp=z80_mem(SP)|(z80_mem(SP+1)<<8);
X    wr_z80_mem(SP,HL&0xff); wr_z80_mem(SP+1,HL>>8);
X    HL=temp;
X    dlog("EX (SP),HL\n");
X    return;
X  }
X  if (opcode==0xD9) /* EXX [Z-80 only] */
X  {
X    register WORD temp;
X    temp=BC; BC=BC2; BC2=temp;
X    temp=DE; DE=DE2; DE2=temp;
X    temp=HL; HL=HL2; HL2=temp;
X    dlog("EXX\n");
X    return;
X  }
X
X/*
X
XExecution control: JP, JR, CALL, RET, RST
X
X*/
X
X  if (opcode==0xC3) /* JP uncond */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X    PC=operand;
X    dlog("JP 0x%04x\n",PC);
X    return;
X  }
X
X  if (op_p13==0xC2) /* JP conditional */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X    switch(op_p2)
X    {
X      case 0: if (~AF&FLAG_Z)
X                PC=operand;
X      break;
X      case 1: if (AF&FLAG_Z)
X                PC=operand;
X      break;
X      case 2: if (~AF&FLAG_C)
X                PC=operand;
X      break;
X      case 3: if (AF&FLAG_C)
X                PC=operand;
X      break;
X      case 4: if (~AF&FLAG_PV)
X                PC=operand;
X      break;
X      case 5: if (AF&FLAG_PV)
X                PC=operand;
X      break;
X      case 6: if (~AF&FLAG_S)
X                PC=operand;
X      break;
X      case 7: if (AF&FLAG_S)
X                PC=operand;
X    }   
X    dlog("JP %c%c, 0x%04x\n",
X	 "NZ ZNC CPOPE P M"[(op_p2<<1)],
X	 "NZ ZNC CPOPE P M"[(op_p2<<1)+1],
X	 operand);
X    return;
X  }
X
X  if (opcode==0xE9) /* JP (HL) */
X  {
X    PC=HL;  /* The old 8080 called this instruction "PCHL" */
X    dlog("JP (HL)\n");
X    return;
X  }
X
X  if (opcode==0x10) /* DJNZ [Z-80 only] */
X  {
X    register BYTE operand;
X
X    operand=z80_mem(PC++);
X    BC-= 0x100;  /* B-- */
X    if ( BC>>8 )
X      DO_JR;
X    dlog("DJNZ 0x%04x\n",PC);
X    return;
X  }
X
X  if (opcode==0x18) /* JR [Z-80 only] */
X  {
X    register BYTE operand;
X
X    operand=z80_mem(PC++);
X    DO_JR;
X    dlog("JR 0x%04x\n",PC);
X    return;
X  }
X
X  if ((opcode&0xE7)==0x20) /* JR cond [Z-80 only] */
X  {
X    register BYTE operand;
X
X    operand=z80_mem(PC++);
X
X/* if the flag in question is false and the 4th bit is false */
X
X#define XOR(a,b) ( (a!=0) != (b!=0) ) /* logical XOR, not standard C */
X
X    if ( XOR ( (AF&((opcode&0x10)?FLAG_C:FLAG_Z)) , !(opcode&0x08) ) )
X      DO_JR;
X    dlog("JR %d,0x%04x\n",opcode,PC);
X    return;
X  }
X
X  if (opcode==0xCD) /* CALL */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X    push(PC);
X    PC=operand;
X    dlog("CALL 0x%04x\n",PC);
X    return;
X  }
X
X  if (op_p13==0xC4) /* CALL cond */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X
X    switch(op_p2)
X    {
X      case 0:if (~AF&FLAG_Z)
X	     {
X	       push(PC);
X	       PC=operand;
X             }
X      break;
X      case 1:if (AF&FLAG_Z)
X	     {
X	       push(PC);
X	       PC=operand;
X             }
X      break;
X      case 2:if (~AF&FLAG_C)
X	     {
X	       push(PC);
X	       PC=operand;
X	     }
X      break;
X      case 3:if (AF&FLAG_C)
X	     {
X	       push(PC);
X	       PC=operand;
X             }
X      break;
X      case 4:if (~AF&FLAG_PV)
X	     {
X	       push(PC);
X	       PC=operand;
X	     }
X      break;
X      case 5:if (AF&FLAG_PV)
X	     {
X	       push(PC);
X	       PC=operand;
X	     }
X      break;
X      case 6:if (~AF&FLAG_S)
X	     {
X	       push(PC);
X	       PC=operand;
X	     }
X      break;
X      case 7:if (AF&FLAG_S)
X	     {
X	       push(PC);
X	       PC=operand;
X	     }
X      break;
X    }
X    dlog("CALL %c%c, 0x%04x\n","NZ ZNC CPOPE P M"[(op_p2<<1)],
X	 "NZ ZNC CPOPE P M"[(op_p2<<1)+1],
X	 PC);	
X    return;
X  }
X
X  if (opcode==0xC9) /* RET */
X  {
X    PC=pop();
X    dlog("RET\n");
X    return;
X  }
X
X  if (op_p13==0xC0) /* RET cond */
X  {
X    switch(op_p2)
X    {
X      case 0:if (~AF&FLAG_Z)
X	       PC=pop();
X      break;
X      case 1:if (AF&FLAG_Z)
X	       PC=pop();
X      break;
X      case 2:if (~AF&FLAG_C)
X	       PC=pop();
X      break;
X      case 3:if (AF&FLAG_C)
X	       PC=pop();
X      break;
X      case 4:if (~AF&FLAG_PV)
X	       PC=pop();
X      break;
X      case 5:if (AF&FLAG_PV)
X	       PC=pop();
X      break;
X      case 6:if (~AF&FLAG_S)
X	       PC=pop();
X      break;
X      case 7:if (AF&FLAG_S)
X	       PC=pop();
X      break;
X    }
X    dlog("RET %c%c\n","NZ ZNC CPOPE P M"[op_p2<<1],
X	 "NZ ZNC CPOPE P M"[(op_p2<<1)+1]);
X    return;
X  }
X
X  if (op_p13==0xC7) /* RST ?? */
X  {
X    push(PC);
X    PC=op_p2<<3;
X    dlog("RST 0x%02x\n",PC);
X    return;
X  }
X
X/*
X
XLD
X
X*/
X
X  if (opcode==0x3A) /* LD A,(imm) */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X    AF=(AF&0x00ff)|z80_mem(operand)<<8;
X    dlog("LD A,(0x%04x)\n",operand);
X    return;
X  }
X  if (opcode==0x32) /* LD (imm),A */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X    wr_z80_mem(operand,AF>>8);
X    dlog("LD (0x%04x),A\n",operand);
X    return;
X  }
X  if ((opcode&0xEF)==0x0A) /* LD A,(rp) */
X  {
X    AF=(AF&0xff)|z80_mem((opcode&0x10)?DE:BC)<<8;
X    dlog("LD A,(%s)\n",(opcode&0x10)?"DE":"BC");
X    return;
X  }
X  if ((opcode&0xEF)==0x02) /* LD (rp),A */
X  {
X    wr_z80_mem((opcode&0x10)?DE:BC,AF>>8);
X    dlog("LD (%s),A\n",(opcode&0x10)?"DE":"BC");
X    return;
X  }
X  if (op_p3==0x1) /* LD reg,reg [and (HL) too] */
X  {
X    register BYTE value;
X    switch(op_p1)
X    {
X      case 0:value=BC>>8;       break;
X      case 1:value=BC&0xff;     break;
X      case 2:value=DE>>8;       break;
X      case 3:value=DE&0xff;     break;
X      case 4:value=HL>>8;       break;
X      case 5:value=HL&0xff;     break;
X      case 6:value=z80_mem(HL); break;
X      case 7:value=AF>>8;       break;
X    }
X    switch(op_p2)
X    {
X      case 0:BC=(BC&0xff)|(value<<8);  break;
X      case 1:BC=(BC&0xff00)|value;     break;
X      case 2:DE=(DE&0xff)|(value<<8);  break;
X      case 3:DE=(DE&0xff00)|value;     break;
X      case 4:HL=(HL&0xff)|(value<<8);  break;
X      case 5:HL=(HL&0xff00)|value;     break;
X      case 6:wr_z80_mem(HL,value);     break;
X      case 7:AF=(AF&0xff)|(value<<8);  break;
X    }
X    dlog("LD %c,%c\n","BCDEHLMA"[op_p2],"BCDEHLMA"[op_p1]);
X    return;
X  }
X  if (opcode==0x2A) /* LD HL,(imm) */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X    HL=z80_mem(operand)|(z80_mem(operand+1)<<8);
X    dlog("LD HL,(0x%04x)\n",operand);
X    return;
X  }
X  if (opcode==0x22) /* LD (imm),HL */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X    wr_z80_mem(operand,HL&0xff);
X    wr_z80_mem(operand+1,HL>>8);
X    dlog("LD (0x%04x),HL\n",operand);
X    return;
X  }
X  if (op_p13==0x06) /* LD reg,imm and LD (HL),imm */
X  {
X    register BYTE operand;
X
X    operand=z80_mem(PC++);
X
X    switch (op_p2)
X    {
X      case 0:BC=(BC&0xff)|(operand<<8);
X      break;
X      case 1:BC=(BC&0xff00)|operand;
X      break;
X      case 2:DE=(DE&0xff)|(operand<<8);
X      break;
X      case 3:DE=(DE&0xFF00)|operand;
X      break;
X      case 4:HL=(HL&0xff)|(operand<<8);
X      break;
X      case 5:HL=(HL&0xff00)|operand;
X      break;
X      case 6:wr_z80_mem(HL,operand);
X      break;
X      case 7:AF=(AF&0xff)|(operand<<8);
X      break;
X    }
X    dlog("LD %c,0x%x\n","BCDEHLMA"[op_p2],operand);
X    return;
X  }
X  if ((opcode&0xCF)==0x01) /* LD rp,imm */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X
X    switch ((opcode&0x30)>>4)
X    {
X      case 0:BC=operand;
X	dlog("LD BC,");
X      break;
X      case 1:DE=operand;
X	dlog("LD DE,");
X      break;
X      case 2:HL=operand;
X	dlog("LD HL,");
X      break;
X      case 3:SP=operand;
X	dlog("LD SP,");
X      break;
X    }
X    dlog("0x%04x\n",operand);
X    return;
X  }
X  if (opcode==0xF9) /* LD SP,HL */
X  {
X    SP=HL;
X    dlog("LD SP,HL\n");
X    return;
X  }
X
X/*
X
XPUSH & POP
X
X*/
X
X  if ((opcode&0xCF)==0xC5) /* PUSH rp */
X  {
X    switch ((opcode>>4)&3)
X    {
X      case 0:push(BC); break;
X      case 1:push(DE); break;
X      case 2:push(HL); break;
X      case 3:push(AF); break;
X      break;
X    }
X    dlog("PUSH %c%c\n","BDHA"[(opcode>>4)&3],"CELF"[(opcode>>4)&3]);
X    return;
X  }
X
X  if ((opcode&0xCF)==0xC1) /* POP rp */
X  {
X    switch ((opcode>>4)&3)
X    {
X      case 0:BC=pop(); break;
X      case 1:DE=pop(); break;
X      case 2:HL=pop(); break;
X      case 3:AF=pop(); break;
X      break;
X    }
X    dlog("POP %c%c\n","BDHA"[(opcode>>4)&3],"CELF"[(opcode>>4)&3]);
X    return;
X  }
X
X/*
X
XMATH & LOGICAL instructions
X
X*/
X
X  if (op_p13==0x03) /* 16 bit INC/DEC */
X  {
X    switch(op_p2>>1)
X    {
X      case 0:BC+=(op_p2&0x1)?-1:1;
X      break;
X      case 1:DE+=(op_p2&0x1)?-1:1;
X      break;
X      case 2:HL+=(op_p2&0x1)?-1:1;
X      break;
X      case 3:SP+=(op_p2&0x1)?-1:1;
X      break;
X    }
X    dlog("%s %c%c\n",(op_p2&1)?"DEC":"INC","BDHS"[op_p2>>1],"CELP"[op_p2>>1]);
X    return;
X  }
X
X  if ((op_p13&0xFE)==0x04) /* 8 bit INC/DEC */
X  {
X    char temp;
X    BYTE dir; WORD t;
X
X    temp=AF&FLAG_C; /* save carry flag */
X
X    dir=(op_p1&0x1)?~0:1; /* which direction? */
X
X    switch(op_p2)
X    {
X      case 0:BC=(BC&0xff)|(alu_add(BC>>8,dir)<<8);
X      break;
X      case 1:BC=(BC&0xff00)|alu_add(BC&0xff,dir);
X      break;
X      case 2:DE=(DE&0xff)|(alu_add(DE>>8,dir)<<8);
X      break;
X      case 3:DE=(DE&0xff00)|alu_add(DE&0xff,dir);
X      break;
X      case 4:HL=(HL&0xff)|(alu_add(HL>>8,dir)<<8);
X      break;
X      case 5:HL=(HL&0xff00)|alu_add(HL&0xff,dir);
X      break;
X      case 6:wr_z80_mem(HL,alu_add(z80_mem(HL),dir));
X      break;
X      case 7:t=alu_add(AF>>8,dir); AF&=0xff; AF|=t<<8;
X      break;
X    }
X    AF=(AF&~FLAG_N)|((opcode&1)?FLAG_N:0);
X    AF=(AF&~FLAG_C)|temp; /* restore carry flag */
X    dlog("%s %c\n",(dir==1)?"INC":"DEC","BCDEHLMA"[op_p2]);
X    return;
X  }
X
X  if (op_p3==0x2) /* 8 bit ADD, ADC, SUB, SBC, AND, OR, XOR, CMP */
X  {
X    WORD t;
X    switch(op_p2)
X    {
X      case 0: /* ADD */
X	switch(op_p1)
X	{
X	  case 0:t=alu_add(AF>>8,BC>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 1:t=alu_add(AF>>8,BC&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 2:t=alu_add(AF>>8,DE>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X          case 3:t=alu_add(AF>>8,DE&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 4:t=alu_add(AF>>8,HL>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 5:t=alu_add(AF>>8,HL&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 6:t=alu_add(AF>>8,z80_mem(HL))<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 7:t=alu_add(AF>>8,AF>>8)<<8; /* Sigh */ AF&=0xff; AF|=t;
X	  break;
X	}
X	dlog("ADD %c\n","BCDEHLMA"[op_p1]);
X      break;
X      case 1: /* ADC */
X	switch(op_p1)
X	{
X	  case 0:t=alu_adc(AF>>8,BC>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 1:t=alu_adc(AF>>8,BC&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 2:t=alu_adc(AF>>8,DE>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X          case 3:t=alu_adc(AF>>8,DE&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 4:t=alu_adc(AF>>8,HL>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 5:t=alu_adc(AF>>8,HL&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 6:t=alu_adc(AF>>8,z80_mem(HL))<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 7:t=alu_adc(AF>>8,AF>>8)<<8; /* Sigh */ AF&=0xff; AF|=t;
X	  break;
X	}
X	dlog("ADC %c\n","BCDEHLMA"[op_p1]);
X      break;
X      case 2: /* SUB */
X	switch(op_p1)
X	{
X	  case 0:t=alu_sub(AF>>8,BC>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 1:t=alu_sub(AF>>8,BC&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 2:t=alu_sub(AF>>8,DE>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X          case 3:t=alu_sub(AF>>8,DE&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 4:t=alu_sub(AF>>8,HL>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 5:t=alu_sub(AF>>8,HL&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 6:t=alu_sub(AF>>8,z80_mem(HL))<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 7:t=alu_sub(AF>>8,AF>>8)<<8; /* Sigh */ AF&=0xff; AF|=t;
X	  break;
X	}
X	dlog("SUB %c\n","BCDEHLMA"[op_p1]);
X      break;
X      case 3: /* SBC */
X	switch(op_p1)
X	{
X	  case 0:t=alu_sbc(AF>>8,BC>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 1:t=alu_sbc(AF>>8,BC&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 2:t=alu_sbc(AF>>8,DE>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X          case 3:t=alu_sbc(AF>>8,DE&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 4:t=alu_sbc(AF>>8,HL>>8)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 5:t=alu_sbc(AF>>8,HL&0xff)<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 6:t=alu_sbc(AF>>8,z80_mem(HL))<<8; AF&=0xff; AF|=t;
X	  break;
X	  case 7:t=alu_sbc(AF>>8,AF>>8)<<8; /* Sigh */ AF&=0xff; AF|=t;
X	  break;
X	}
X	dlog("SBC %c\n","BCDEHLMA"[op_p1]);
X      break;
X      case 4: /* AND */
X	switch (op_p1)
X	{
X	  case 0:AF=(AF&0xff)|( ((AF>>8)&(BC>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 1:AF=(AF&0xff)|( ((AF>>8)&(BC&0xff)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 2:AF=(AF&0xff)|( ((AF>>8)&(DE>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 3:AF=(AF&0xff)|( ((AF>>8)&(DE&0xff)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 4:AF=(AF&0xff)|( ((AF>>8)&(HL>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 5:AF=(AF&0xff)|( ((AF>>8)&(HL&0xff)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 6:AF=(AF&0xff)|( ((AF>>8)&(z80_mem(HL))) <<8 );
X		 log_flags(AF>>8);
X	  break;
X	  case 7:AF=(AF&0xff)|( ((AF>>8)&(AF>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	}
X	AF&=~FLAG_C;
X	dlog("AND %c\n","BCDEHLMA"[op_p1]);
X      break;
X      case 5: /* XOR */
X	switch (op_p1)
X	{
X	  case 0:AF=(AF&0xff)|( ((AF>>8)^(BC>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 1:AF=(AF&0xff)|( ((AF>>8)^(BC&0xff)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 2:AF=(AF&0xff)|( ((AF>>8)^(DE>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 3:AF=(AF&0xff)|( ((AF>>8)^(DE&0xff)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 4:AF=(AF&0xff)|( ((AF>>8)^(HL>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 5:AF=(AF&0xff)|( ((AF>>8)^(HL&0xff)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 6:AF=(AF&0xff)|( ((AF>>8)^(z80_mem(HL))) <<8 );
X		 log_flags(AF>>8);
X	  break;
X	  case 7:AF=(AF&0xff)|( ((AF>>8)^(AF>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	}
X	dlog("XOR %c\n","BCDEHLMA"[op_p1]);
X	AF&=~FLAG_C;
X      break;
X      case 6: /* OR */
X	switch (op_p1)
X	{
X	  case 0:AF=(AF&0xff)|( ((AF>>8)|(BC>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 1:AF=(AF&0xff)|( ((AF>>8)|(BC&0xff)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 2:AF=(AF&0xff)|( ((AF>>8)|(DE>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 3:AF=(AF&0xff)|( ((AF>>8)|(DE&0xff)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 4:AF=(AF&0xff)|( ((AF>>8)|(HL>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 5:AF=(AF&0xff)|( ((AF>>8)|(HL&0xff)) <<8 ); log_flags(AF>>8);
X	  break;
X	  case 6:AF=(AF&0xff)|( ((AF>>8)|(z80_mem(HL))) <<8 );
X		 log_flags(AF>>8);
X	  break;
X	  case 7:AF=(AF&0xff)|( ((AF>>8)|(AF>>8)) <<8 ); log_flags(AF>>8);
X	  break;
X	}
X	dlog("OR %c\n","BCDEHLMA"[op_p1]);
X	AF&=~FLAG_C;
X      break;
X      case 7: /* CMP */
X	switch(op_p1)
X	{
X	  case 0:alu_sub(AF>>8,BC>>8);
X	  break;
X	  case 1:alu_sub(AF>>8,BC&0xff);
X	  break;
X	  case 2:alu_sub(AF>>8,DE>>8);
X	  break;
X	  case 3:alu_sub(AF>>8,DE&0xff);
X	  break;
X	  case 4:alu_sub(AF>>8,HL>>8);
X	  break;
X	  case 5:alu_sub(AF>>8,HL&0xff);
X	  break;
X	  case 6:alu_sub(AF>>8,z80_mem(HL));
X	  break;
X	  case 7:alu_sub(AF>>8,AF>>8); /* Sigh... What a waste */
X	  break;
X	}
X	dlog("CMP %c\n","BCDEHLMA"[op_p1]);
X    }
X    return;
X  }
X  if (op_p13==0xC6) /* 8 bit math with immediate operands. */
X  {
X    register BYTE operand;
X    WORD t;
X    
X    operand=z80_mem(PC++);
X
X    switch(op_p2)
X    {
X      case 0:t=alu_add(AF>>8,operand)<<8; AF&=0xff; AF|=t;
X	dlog("ADD 0x%02x\n",operand);
X      break;
X      case 1:t=alu_adc(AF>>8,operand)<<8; AF&=0xff; AF|=t;
X	dlog("ADC 0x%02x\n",operand);
X      break;
X      case 2:t=alu_sub(AF>>8,operand)<<8; AF&=0xff; AF|=t;
X	dlog("SUB 0x%02x\n",operand);
X      break;
X      case 3:t=alu_sbc(AF>>8,operand)<<8; AF&=0xff; AF|=t;
X	dlog("SBC 0x%02x\n",operand);
X      break;
X      case 4:AF=(AF&0xff)|( ((AF>>8)&operand) <<8 ); log_flags(AF>>8);
X	     AF&=~FLAG_C;
X	dlog("AND 0x%02x\n",operand);
X      break;
X      case 5:AF=(AF&0xff)|( ((AF>>8)|operand) <<8 ); log_flags(AF>>8);
X	     AF&=~FLAG_C;
X	dlog("OR 0x%02x\n",operand);
X      break;
X      case 6:AF=(AF&0xff)|( ((AF>>8)^operand) <<8 ); log_flags(AF>>8);
X	     AF&=~FLAG_C;
X	dlog("XOR 0x%02x\n",operand);
X      break;
X      case 7:alu_sub(AF>>8,operand);
X	dlog("CMP 0x%02x\n",operand);
X      break;
X    }
X
X    return;
X  }
X
X  if ((opcode&0xCF)==0x09) /* ADD HL,rp */
X  {
X    register int temp;
X    
X    switch ((opcode&0x30)>>4)
X    {
X      case 0:temp=HL+BC; break;
X      case 1:temp=HL+DE; break;
X      case 2:temp=HL+HL; break;
X      case 3:temp=HL+SP; break;
X    }
X    HL=(WORD) temp;
X    AF=(AF&~FLAG_C)|((temp>0xffff)?FLAG_C:0);
X    AF&=~FLAG_N;
X    dlog("ADD HL,%c%c\n","BDHS"[op_p2>>1],"CELP"[op_p2>>1]);
X    return;
X
X  }
X
X  if (opcode==0x27) /* DAA */
X  {
X    WORD t;
X    if ( (((AF>>8)&0xf)>9) || (AF&FLAG_H) )
X      if (AF&FLAG_N)
X	{ t=alu_sbc(AF>>8,6)<<8;  AF&=0xff; AF|=t; }
X      else
X	{ t=alu_add(AF>>8,6)<<8;  AF&=0xff; AF|=t; }
X    if ( (AF&FLAG_C) || (((AF>>8)&0xf0)>0x90) )
X      if (AF&FLAG_N)
X	{ t=alu_sbc(AF>>8,0x60)<<8;  AF&=0xff; AF|=t; }
X      else
X	{ t=alu_add(AF>>8,0x60)<<8;  AF&=0xff; AF|=t; }
X
X    dlog("DAA\n");
X    return;
X  }
X
X/*
X
XROTATES
X
X*/
X
X  if ((opcode&0xE7)==0x07)
X  {
X    switch ((opcode&0x18)>>3)
X    {
X      case 0:if (AF&0x8000) /* RLCA */
X	       {
X		 AF=(AF&0xff)|((AF&0x7f00)<<1)|0x100|FLAG_C;
X	       }
X	       else
X	       {
X		 AF=(AF&0xff&~FLAG_C)|((AF&0x7f00)<<1);
X	       }
X	dlog("RLCA\n");
X      break;
X      case 1:if (AF&0x100) /* RRCA */
X	       {
X		 AF=(AF&0xff)|((AF&0xfe00)>>1)|0x8000|FLAG_C;
X	       }
X	       else
X	       {
X		 AF=(AF&0xff&~FLAG_C)|((AF&0xfe00)>>1);
X	       }
X	dlog("RRCA\n");
X      break;
X      case 2:if (AF&FLAG_C) /* RLA */
X	     {
X	       AF=(AF&~FLAG_C)|((AF&0x8000)?FLAG_C:0);
X	       AF=(AF&0xff)|((AF&0x7f00)<<1)|0x100;
X	     }
X	     else
X	     {
X	       AF=(AF&~FLAG_C)|((AF&0x8000)?FLAG_C:0);
X	       AF=(AF&0xff)|((AF&0x7f00)<<1);
X	     }
X	dlog("RLA\n");
X      break;
X      case 3:if (AF&FLAG_C) /* RRA */
X	     {
X	       /*  clr carry  then set it if bit 0 set */
X	       AF=(AF&~FLAG_C)|((AF&0x100)?FLAG_C:0);
X	       /* set A to A>>1, then add in known carry on top */
X	       AF=(AF&0xff)|((AF&0xfe00)>>1)|0x8000;
X	     }
X	     else
X	     {
X	       AF=(AF&~FLAG_C)|((AF&0x100)?FLAG_C:0);
X	       AF=(AF&0xff)|((AF&0xfe00)>>1);
X	     }
X	dlog("RRA\n");
X      break;
X    }
X    AF&=~(FLAG_H|FLAG_N);	/* according to Z80 prod spec, p. 13 */
X    return;
X  }
X
X/*
X
XI/O
X
X*/
X
X  if (opcode==0xD3) /* OUT (port),A */
X  {
X    wrport(z80_mem(PC++),AF>>8);
X    dlog("OUT (0x%02x),A\n",z80_mem(PC-1));
X    return;
X  }
X
X  if (opcode==0xDB) /* IN A,(port) */
X  {
X    AF=(AF&0xff)|rdport(z80_mem(PC++))<<8;
X    dlog("IN A,(0x%02x)\n",z80_mem(PC-1));
X    return;
X  }
X
X/*
X
Xmisc - xCF, CPL
X
X*/
X
X  if (opcode==0x3f) /* CCF */
X  {
X    AF^=FLAG_C;
X    AF&=~(FLAG_N);
X    dlog("CCF\n");
X    return;
X  }
X
X  if (opcode==0x37) /* SCF */
X  {
X    AF|=FLAG_C;
X    AF&=~(FLAG_N|FLAG_H);
X    dlog("SCF\n");
X    return;
X  }
X
X  if (opcode==0x2f) /* CPL */
X  {
X    AF^=0xff00;
X    AF|=FLAG_H|FLAG_N;
X    dlog("CPL\n");
X    return;
X  }
X
X/*
X
XWE SHOULD NEVER APPEAR HERE!!!!
X
XThere is no value of the first opcode byte of a Z-80 instruction that is
Xinvalid. If we get here, the above code is severely hosed.
X
X*/
X
X  printf("OH NO!!! PARSE ERROR - FIRST OPCODE BYTE!!!!!\n");
X  printf("PC = %4x  (PC) = %2x\n\n",PC-1,opcode);
X  exit(99);
X
X}
X
X/*
X
Xadditional routines:
X
Xalu_adc takes two bytes and adds them, creating the proper effect
Xon the flags register. For an add without carry, c=0. S, Z, C and H are
Xset as defined. P/V is set to indicate overflow.
X
XFor 16 bit add, lo=add(lo(a),lo(b)); hi=add(hi(a),hi(b)+((AF&FLAG_C)!=0));
XTHIS PRESUMES THAT != OPERATIONS ON YOUR COMPILER RETURN 0 OR 1!!!
X
X*/
X
XBYTE alu_adc(a,b)		/* X+Y+c... */
XWORD a,b;
X{
X    BYTE answer;
X    answer = alu_add(a,b+((AF&FLAG_C)!=0));
X    return answer;
X}
X
XBYTE alu_add(a,b)		/* essentially, X+Y, set FLAG_C as needed */
XWORD a,b;
X{
X
X  register BYTE answer=(a+b)&0xff;
X  register WORD flags=AF&0xff;
X  flags&= ~FLAG_N; /* Addition op */
X  flags=(flags&~FLAG_Z)|((answer==0)?FLAG_Z:0);
X  flags=(flags&~FLAG_S)|((answer&0x80)?FLAG_S:0);
X
X/*  For the H flag, we chop to 1 nibble each and check LSB of hi nibble
X    for carry. Yuck! */
X  flags=(flags&~FLAG_H)|( (((a&0xf)+(b&0xf))&0x10) ?FLAG_H:0);
X
X/* Bring the operands out to WORD size, add and see if the result's too big. */
X  flags=(flags&~FLAG_C)|(  ( (((WORD)a)+((WORD)b)) >0xff )  ?FLAG_C:0);
X
X/* The next one is complicated. It reads, "if the high bits of the operands
X   are the same, and the high bit of the first operand is different from
X   the high bit of the answer, overflow has occurred." Bleahack!!! */
X
X  flags=(flags&~FLAG_PV) |(( ((a>0x7f)==(b>0x7f)) && ((a>0x7f)!=(answer>0x7f)))
X			   ?FLAG_PV:0);
X  AF &= 0xff00; AF |= flags;
X  return answer;
X}
X
XBYTE alu_sbc(a,b)
XBYTE a,b;
X{
X  BYTE answer;
X  AF ^= FLAG_C;
X  answer=alu_adc(a,(~b)&0xff);
X  AF ^= FLAG_C;
X  AF^=FLAG_H; /* same for half-carry */
X  AF|=FLAG_N; /* set for subtract op */
X  return answer;
X}
X
XBYTE alu_sub(a,b)
XBYTE a,b;
X{
X  BYTE answer;
X/*  AF ^= FLAG_C; */
X  answer=alu_add(a,(0xff&~b)+1);
X  AF ^= FLAG_C;
X  AF^=FLAG_H; /* same for half-carry */
X  AF|=FLAG_N; /* set for subtract op */
X  return answer;
X}
X
X/*
X
Xlog_flags - set flags after a logical operation. N & H =0, P/V set to
Xindicate parity, S with sign, and Z if zero as expected.
X
X*/
X
Xlog_flags(a)
XBYTE a;
X{
X  char count=0,i;
X
X  AF&=~(FLAG_N|FLAG_H);
X  AF=(AF&~FLAG_S)|((a&0x80)?FLAG_S:0);
X  AF=(AF&~FLAG_Z)|((a==0)?FLAG_Z:0);
X
X/*  for(i=0;i<8;i++)
X    {
X      if (a&1)
X	count++;
X      a=a>>1;
X    }
X  AF=(AF&~FLAG_PV)|((count%2)?0:FLAG_PV);
X*/
X  i = a;
X  i = (i & 0xf0) ^ ( (i&0x0f) << 4);
X  i = (i & 0xc0) ^ ( (i&0x30) << 2);
X  i = (i & 0x80) ^ ( (i&0x40) << 1);
X  AF=(AF&~FLAG_PV)|((0x80==i)?0:FLAG_PV);
X}
X
X/*
X
XStack routines
X
Xpush & pop do the obvious.
X
X*/
X
Xpush(a)
XWORD a;
X{
X  wr_z80_mem(--SP,a>>8);
X  wr_z80_mem(--SP,a&0xff);
X}
X
XWORD pop()
X{
X  register WORD temp;
X  temp=z80_mem(SP++);
X  temp|=z80_mem(SP++)<<8;
X  return temp;
X}
X
SHAR_EOF
echo "File z80.c is complete" &&
$TOUCH -am 0928182190 z80.c &&
chmod 0644 z80.c ||
echo "restore of z80.c failed"
set `wc -c z80.c`;Wc_c=$1
if test "$Wc_c" != "27243"; then
	echo original size 27243, current size $Wc_c
fi
# ============= z80.h ==============
echo "x - extracting z80.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > z80.h &&
X/*
X
Xz80.h - defines for the z80 emulator.
X
XCopyright MCMXC - Nick Sayer - All rights reserved.
X
XSee COPYRIGHT file for more info.
X
X
XThere's only one configuration option. Some compilers have trouble
Xwith signed chars, and that's how we do JR, so choose one of these
Xtwo definitions to be used in the JR instructions
X
X*/
X
X    
X/* #define DO_JR PC+= (operand>127)?(-(256-operand)):(operand); */
X#define DO_JR PC+= (char) operand /* */
X/* #define DO_JR PC+= (operand>127)?256-(operand&0x7f):operand /* */
X
X/*
X
XGlobal types, data, and routines:
X
X*/
X
X#define BYTE unsigned char
X#define WORD unsigned short
X
Xextern BYTE real_z80_mem[65536];
X#define z80_mem(x) real_z80_mem[x]
X#define wr_z80_mem(x,y) debug_write(x,y)
Xextern WORD AF,BC,DE,HL,IX,IY,AF2,BC2,DE2,HL2,IR,PC,SP,INT_FLAGS;
Xextern z80_instr(),z80_run(),wrport();
Xextern BYTE rdport(),int_read();
Xextern char INT,NMI,RESET;
X
X/*
X
Xflag bits - these represent positions within the AF register.
X
X*/
X
X#define FLAG_C	0x01
X#define FLAG_N	0x02
X#define FLAG_PV	0x04
X#define FLAG_H	0x10
X#define FLAG_Z	0x40
X#define FLAG_S	0x80
X
X/*
X
XThe INT_FLAGS register doesn't really exist, we use it to store the IM status
Xand the two IFF flags.
X
XIFTMP is a counter to allow EI to take effect after the NEXT
Xinstruction.
X
X*/
X
X#define IM_STAT	0x03
X#define IFF1	0x04
X#define IFF2	0x08
X#define IFTMP	0x30
X
X/*
X
XThese routines are internal. We include it here just to keep
Xz80_cbed.c happy. Don't use 'em!!
X
X*/
X
XWORD pop();
XBYTE alu_adc(),alu_sbc(),alu_sub(),alu_add();
X
Xint dlogflag;
Xint debugflag;
Xint biosflag;
Xint TRAPval;
Xint TWRTval;
X
X#define dlog if(dlogflag) printf
X#define bioslog if(biosflag) printf
X/* static void dlog() { return; }  */
SHAR_EOF
$TOUCH -am 0928182190 z80.h &&
chmod 0644 z80.h ||
echo "restore of z80.h failed"
set `wc -c z80.h`;Wc_c=$1
if test "$Wc_c" != "1672"; then
	echo original size 1672, current size $Wc_c
fi
# ============= z80_cbed.c ==============
echo "x - extracting z80_cbed.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > z80_cbed.c &&
X/*
X
Xz80_cbed.c - Z-80 microprocessor emulator, part 2.
X
XCopyright MCMXC - Nick Sayer - All rights reserved.
X
XSee COPYRIGHT file for details.
X
Xv0.0   - 04/08/90 - epoch
Xv0.0A0 - 04/13/90 - alpha-test.
Xv0.0A1 - 08/03/90 - alpha-test 2.
Xv0.0A2 - 09/04/90 - alpha-test 3.
X
XCB, ED, and DD/FD ops:
X
Xcb_ops(mem);
XWORD mem;
X
Xed_ops();
X
Xixy_ops(iy_reg);
Xchar iy_reg;
X
Xcb_ops called if first opcode byte is CB. PC points to byte after the
XCB. CB is passed the location of the "M" register. See comments before
Xcb_ops() for details.
X
Xed_ops called if first opcode byte is ED.
X
Xixy_ops similarly called if first opcode byte is DD or FD. iy_reg true for FD.
X
X*/
X
X#include "z80.h"
X
Xed_ops()
X{
X  BYTE op2,op2_p1,op2_p2,op2_p3,op2_p13;
X
X  op2=z80_mem(PC++);
X  op2_p1=op2&0x7;
X  op2_p2=(op2&0x38)>>3;
X  op2_p3=(op2&0xC0)>>6;
X  op2_p13=op2&0xC7;
X
X/* Now another horrendous if statement string... */
X
X  if (op2_p13==0x42) /* SBC/ADC HL,rp */
X  {
X    register WORD temp,temp2,sub_flag=0;
X
X    switch(op2_p2)
X    {
X      case 0:temp=BC; sub_flag++; break;
X      case 1:temp=BC;             break;
X      case 2:temp=DE; sub_flag++; break;
X      case 3:temp=DE;		  break;
X      case 4:temp=HL; sub_flag++; break;
X      case 5:temp=HL;		  break;
X      case 6:temp=SP; sub_flag++; break;
X      case 7:temp=SP;		  break;
X    }
X    if (sub_flag)
X    {
X      temp2=alu_sbc(HL&0xff,temp&0xff);
X      AF^=FLAG_C; /* proper chaining! */
X      HL=temp2|(alu_sbc(HL>>8,(temp>>8))<<8);
X    }
X    else
X    {
X      temp2=alu_adc(HL&0xff,temp&0xff);
X      HL=temp2|(alu_adc(HL>>8,temp>>8)<<8);
X    }
X    return;
X  }
X
X  if (op2_p13==0x43) /* LD (imm),rp & rp,(imm) */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X    switch(op2_p2)
X    {
X      case 0:wr_z80_mem(operand,BC&0xff); wr_z80_mem(operand+1,BC>>8); break;
X      case 1:BC=z80_mem(operand)|(z80_mem(operand+1)<<8); break;
X      case 2:wr_z80_mem(operand,DE&0xff); wr_z80_mem(operand+1,DE>>8); break;
X      case 3:DE=z80_mem(operand)|(z80_mem(operand+1)<<8); break;
X      case 4:wr_z80_mem(operand,HL&0xff); wr_z80_mem(operand+1,HL>>8); break;
X      case 5:HL=z80_mem(operand)|(z80_mem(operand+1)<<8); break;
X      case 6:wr_z80_mem(operand,SP&0xff); wr_z80_mem(operand+1,SP>>8); break;
X      case 7:SP=z80_mem(operand)|(z80_mem(operand+1)<<8); break;
X    }
X    return;
X  }
X
X  if ((op2&0xF7)==0x67) /* RLD or RRD */
X  {
X    register BYTE temp;
X
X    if (op2&0x8)
X    {
X      temp=z80_mem(HL)>>4;
X      wr_z80_mem(HL,(z80_mem(HL)<<4)|((AF&0x0f00)>>8));
X      AF=(AF&0xf0ff)|(temp<<8);
X    }
X    else
X    {
X      temp=z80_mem(HL)&0xf;
X      wr_z80_mem(HL,(z80_mem(HL)>>4)|((AF&0x0f00)>>4));
X      AF=(AF&0xf0ff)|(temp<<8);
X    }
X    log_flags(AF>>8);
X
X    return;
X  }
X
X  if (op2_p13==0x40) /* IN reg,(C) */
X  {
X    switch (op2_p2)
X    {
X      case 0:BC=(HL&0xff)|(rdport(BC&0xff)<<8); break;
X      case 1:BC=(BC&0xff00)|rdport(BC&0xff);    break;
X      case 2:DE=(DE&0xff)|(rdport(BC&0xff)<<8); break;
X      case 3:DE=(DE&0xff00)|rdport(DE&0xff);    break;
X      case 4:HL=(HL&0xff)|(rdport(BC&0xff)<<8); break;
X      case 5:HL=(HL&0xff00)|rdport(BC&0xff);    break;
X      case 6:wr_z80_mem(HL,rdport(BC&0xff));   break;/* is this REALLY true? */
X      case 7:AF=(AF&0xFF)|(rdport(BC&0xff)<<8); break;
X    }
X    return;
X  }
X
X  if (op2_p13==0x41) /* OUT (C),reg */
X  {
X    switch (op2_p2)
X    {
X      case 0:wrport(BC&0xff,BC>>8);       break;
X      case 1:wrport(BC&0xff,BC&0xff);     break;
X      case 2:wrport(BC&0xff,DE>>8);       break;
X      case 3:wrport(BC&0xff,DE&0xff);     break;
X      case 4:wrport(BC&0xff,HL>>8);       break;
X      case 5:wrport(BC&0xff,HL&0xff);     break;
X      case 6:wrport(BC&0xff,z80_mem(HL)); break; /* Is this REALLY true? */
X      case 7:wrport(BC&0xff,AF>>8);       break;
X    }
X    return;
X  }
X
X  if (op2==0x44) /* NEG */
X  {
X    WORD t;
X    t=alu_sub(0,AF>>8)<<8; AF&=0xff; AF|=t;
X    return;
X  }
X
X  if (op2==0x45) /* RETN */
X  {
X	 /* restore IFF1 from IFF2 */
X    INT_FLAGS=(INT_FLAGS&~IFF1)|((INT_FLAGS&IFF2)?IFF1:0);
X    PC=pop();
X    return;
X  }
X
X  if (op2==0x4D) /* RETI */
X  {
X    PC=pop();
X    return;
X  }
X
X  if ((op2&0xE7)==0x46) /* IM ? */
X  {
X    INT_FLAGS&=~IM_STAT;
X    switch ((op2&0x18)>>3)
X    {
X      case 0: /* IM 0 - we're done! */
X      break;
X      case 2:INT_FLAGS|=1; /* IM 1 - perverse, isn't it? */
X      break;
X      case 3:INT_FLAGS|=2; /* IM 2 */
X    }
X    return;
X  }
X
X  if ((op2&0xE7)==0x47) /* moves involving I & R */
X  {
X    switch((op2&0x10)>>4) /* which way? */
X    {
X      case 1:switch ((op2&0x8)>>3) /* which reg? */
X	     {
X	       case 0:AF=(AF&0xff)|(IR&0xff00); break;  /* A,I */
X	       case 1:AF=(AF&0xff)|(IR<<8);     break;  /* A,R */
X	     }
X	     AF=(AF&~(FLAG_H|FLAG_N));
X	     AF=(AF&~FLAG_PV)|((INT_FLAGS&IFF1)?FLAG_PV:0);
X	     AF=(AF&~FLAG_S)|((AF&0x8000)?FLAG_S:0);
X	     AF=(AF&~FLAG_Z)|(((AF>>8)==0)?FLAG_Z:0);
X      break;
X      case 0:switch ((op2&0x8)>>3)
X             {
X	       case 0:IR=(IR&0xff)|(AF&0xff00); break; /* I,A */
X	       case 1:IR=(IR&0xff00)|(AF>>8);   break; /* R,A */
X	     }
X      break;
X    }
X    return;
X  }
X
X/*
X
X{LD,CP,OT,IN}[ID][R] instructions (e.g. LDIR, OTD, etc)
X
X*/
X
X  if ((op2&0xE4)==0xA0)
X  {
X    register BYTE dir;
X    register BYTE repeat;
X
X    dir=op2&0x8;
X    repeat=op2&0x10;
X    switch (op2&3)
X    {
X      case 0:wr_z80_mem(DE,z80_mem(HL));
X	     if (dir)
X	       DE--,HL--;
X	     else
X	       DE++,HL++;
X             BC--;
X	     AF=AF&~(FLAG_N|FLAG_H);
X	     AF=(AF&FLAG_PV)|((BC==0)?FLAG_PV:0);
X	     if (repeat && BC)
X	       PC-= 2;
X      break;
X      case 1:alu_sbc(AF>>8,z80_mem(HL));
X	     if (dir)
X	       HL--;
X             else
X	       HL++;
X             BC--;
X	     AF=(AF&FLAG_PV)|((BC==0)?FLAG_PV:0);
X	     if (repeat && BC && !(AF&FLAG_Z))
X	       PC-= 2;
X      break;
X      case 2:wr_z80_mem(HL,rdport(BC&0xff));
X	     if (dir)
X	       HL--;
X             else
X	       HL++;
X             BC-=0x100;
X	     AF=FLAG_N|(AF&FLAG_Z)|(((BC>>8)==0)?FLAG_Z:0);
X	     if (repeat && (BC>>8))
X	       PC-= 2;
X      break;
X      case 3:wrport(BC&0xff,z80_mem(HL));
X	     if (dir)
X	       HL--;
X	     else
X	       HL++;
X             BC-= 0x100;
X	     AF=FLAG_N|(AF&FLAG_Z)|(((BC>>8)==0)?FLAG_Z:0);
X	     if (repeat && (BC>>8))
X	       PC-= 2;
X      break;
X    }
X    return;
X  }
X
X/* I don't know if there are any unparsable ED ops or not. If
X   we get here, crash'n'burn */
X
X   printf("OH NO!!!!! PARSE ERROR - 2nd opcode - ED ops\n");
X   printf("PC = %4x  (PC) = %2x\n\n",PC-1,op2);
X   exit(99);
X
X}
X
X/*
X
XCB operations can also be activated with index registers, e.g.
XBIT 0,(IY+5). The opcodes for this are FD CB 05 46. In machine-type
Xlanguage, this says "use index register IY+5 instead of HL for
Xthe operation "CB 46". The operation "CB 46" says BIT 6,(HL). This
Xis in fact how all the index operations work. The opcode after
XFD/ED relates in some way to (HL). Its just as easy to rewrite
Xthe small portion of the opcode table relating to index registers
Xin this file, but for CB ops, it's just easier to pass the
Xaddress to the CB parser it should use if (HL) is referred to.
XIf we are parsing a straight CB op, this routine is called
Xwith the contents of HL. If it's an FD/ED op, we call it with
XIX/IY + d. Calling this number "mem" is a throwback to the 8080.
X(HL) was called "M" the meta-register "memory." LD A,(HL) was written
X"MOV A,M". That sort of thing can be seen all over in the opcode
Xtable - especially the math and load ops. register "M" was number 6 in
Xthe numbering scheme (BCDEHLMA).
X
XThe problem with doing this is that if we're passed a CB op from the
XFD/ED parser, and that op doesn't refer to mem, it will act as if the
Xindex register is not involved. But such an op would have unpredictable
Xresults on a real Z-80 anyway. fnord.
X
X*/
X
Xcb_ops(mem)
XWORD mem;
X{
X  register BYTE op2,op_typ,op_reg;
X
X  op2=z80_mem(PC++);
X  op_typ=(op2&0x38)>>3;
X  op_reg=op2&0x7;
X
X/* And off we go again... */
X
X  switch((op2&0xC0)>>6)
X  {
X    case 1: /* BIT */
X      switch(op_reg)
X      {
X	case 0:AF=(AF&~FLAG_Z)|((BC&(1<<(op_typ+8)))?0:FLAG_Z);       break;
X	case 1:AF=(AF&~FLAG_Z)|((BC&(1<<op_typ))?0:FLAG_Z);           break;
X	case 2:AF=(AF&~FLAG_Z)|((DE&(1<<(op_typ+8)))?0:FLAG_Z);       break;
X	case 3:AF=(AF&~FLAG_Z)|((DE&(1<<op_typ))?0:FLAG_Z);           break;
X	case 4:AF=(AF&~FLAG_Z)|((HL&(1<<(op_typ+8)))?0:FLAG_Z);       break;
X	case 5:AF=(AF&~FLAG_Z)|((HL&(1<<op_typ))?0:FLAG_Z);           break;
X	case 6:AF=(AF&~FLAG_Z)|((z80_mem(mem)&(1<<op_typ))?0:FLAG_Z); break;
X	case 7:AF=(AF&~FLAG_Z)|((AF&(1<<(op_typ+8)))?0:FLAG_Z);       break;
X      }
X      AF|=FLAG_H;
X      AF&=~FLAG_N;
X    break;
X    case 2: /* RES */
X      switch(op_reg)
X      {
X	case 0:BC&=~(1<<(op_typ+8));        break;
X	case 1:BC&=~(1<<op_typ);            break;
X	case 2:DE&=~(1<<(op_typ+8));        break;
X	case 3:DE&=~(1<<op_typ);            break;
X	case 4:HL&=~(1<<(op_typ+8));        break;
X	case 5:HL&=~(1<<op_typ);            break;
X	case 6:wr_z80_mem(mem,z80_mem(mem)&~(1<<op_typ));  break;
X	case 7:AF&=~(1<<(op_typ+8));        break;
X      }
X    break;
X    case 3: /* SET */
X      switch(op_reg)
X      {
X	case 0:BC|=1<<(op_typ+8);       break;
X	case 1:BC|=1<<op_typ;           break;
X	case 2:DE|=1<<(op_typ+8);       break;
X	case 3:DE|=1<<op_typ;           break;
X	case 4:HL|=1<<(op_typ+8);       break;
X	case 5:HL|=1<<op_typ;           break;
X	case 6:wr_z80_mem(mem,z80_mem(mem)|1<<op_typ); break;
X	case 7:AF|=1<<(op_typ+8);       break;
X      }
X    break;
X    case 0: /* Additional rotate/shift section */
X    {
X      register BYTE temp,temp2;
X
X      switch(op_reg) /* get it out */
X      {
X	case 0:temp=((BC&0xff00)>>8); break;
X	case 1:temp=BC&0xff;          break;
X	case 2:temp=((DE&0xff00)>>8); break;
X	case 3:temp=DE&0xff;          break;
X	case 4:temp=((HL&0xff00)>>8); break;
X	case 5:temp=HL&0xff;          break;
X	case 6:temp=z80_mem(mem);     break;
X	case 7:temp=((AF&0xff00)>>8); break;
X      }
X      switch(op_typ)
X      {
X	case 0: /* RLC */
X	       temp=(temp<<1)|((temp&0x80)?1:0);
X	       log_flags(temp);
X	       AF=(AF&~FLAG_C)|((temp&1)?FLAG_C:0);
X        break;
X        case 1: /* RRC */
X	       temp=(temp>>1)|((temp&1)?0x80:0);
X	       log_flags(temp);
X	       AF=(AF&~FLAG_C)|((temp&0x80)?FLAG_C:0);
X	break;
X        case 2: /* RL */
X	       temp2=temp&0x80;
X	       temp=(temp<<1)|((AF&FLAG_C)?1:0);
X	       log_flags(temp);
X	       AF=(AF&~FLAG_C)|(temp2?FLAG_C:0);
X        break;
X        case 3: /* RR */
X	       temp2=temp&0x1;
X	       temp=(temp>>1)|((AF&FLAG_C)?0x80:0);
X	       log_flags(temp);
X	       AF=(AF&~FLAG_C)|(temp2?FLAG_C:0);
X        break;
X        case 4: /* SLA */
X	       AF=(AF&~FLAG_C)|((temp&0x80)?FLAG_C:0);
X	       temp=temp<<1;
X	       log_flags(temp);
X        break;
X        case 5: /* SRA */
X	       AF=(AF&~FLAG_C)|((temp&1)?FLAG_C:0);
X	       temp=(temp>>1)|((temp&0x80)?0x80:0);
X	       log_flags(temp);
X        break;
X        case 6: /* NOT USED - NOP */
X        break;
X	case 7: /* SRL */
X	       AF=(AF&~FLAG_C)|((temp&1)?FLAG_C:0);
X	       temp=temp>>1;
X	       log_flags(temp);
X        break;
X      }
X      switch(op_reg) /* put it back */
X      {
X	case 0:BC=(BC&0xff)|(temp<<8); break;
X	case 1:BC=(BC&0xff00)|temp;    break;
X	case 2:DE=(DE&0xff)|(temp<<8); break;
X	case 3:DE=(DE&0xff00)|temp;    break;
X	case 4:HL=(HL&0xff)|(temp<<8); break;
X	case 5:HL=(HL&0xff00)|temp;    break;
X	case 6:wr_z80_mem(mem,temp);   break;
X	case 7:AF=(AF&0xff)|(temp<<8); break;
X      }
X    }
X    break;
X  }
X
X/* This parser is a little different. It's normal to get here. Don't
X   crash'n'burn */
X
X  return;
X}
X
X/* this'll help \/  */
X
X#define INDEX_VALUE ((IY_FLAG)?IY+d:IX+d)
X
Xixy_ops(IY_FLAG)
Xchar IY_FLAG; /* true for IY */
X{
X  register BYTE op2;
X  register BYTE d;
X
X  op2=z80_mem(PC++);
X  d=z80_mem(PC++);    /* DANGER WILL ROBINSON!!! Better fix this
X			 for PUSH, INC et all */
X
X/* And away they go!... */
X
X  if (op2==0xCB) /* CB ops */
X  {
X    cb_ops(INDEX_VALUE);
X    return;
X  }
X
X  if ((op2&0xC0)==0x40) /* LD (I?+d),reg or reg,(I?+d) */
X  {
X    register BYTE value;
X    switch((op2&0x38)>>3)
X    {
X      case 0:value=BC>>8;       break;
X      case 1:value=BC&0xff;     break;
X      case 2:value=DE>>8;       break;
X      case 3:value=DE&0xff;     break;
X      case 4:value=HL>>8;       break;
X      case 5:value=HL&0xff;     break;
X      case 6:value=z80_mem(INDEX_VALUE); break;
X      case 7:value=AF>>8;       break;
X    }
X    switch(op2&0x7)
X    {
X      case 0:BC=(BC&0xff)|(value<<8);  break;
X      case 1:BC=(BC&0xff00)|value;     break;
X      case 2:DE=(DE&0xff)|(value<<8);  break;
X      case 3:DE=(DE&0xff00)|value;     break;
X      case 4:HL=(HL&0xff)|(value<<8);  break;
X      case 5:HL=(HL&0xff00)|value;     break;
X      case 6:wr_z80_mem(INDEX_VALUE,value); break;
X      case 7:AF=(AF&0xff)|(value<<8);  break;
X    }
X
X    return;
X  }
X
X  if (op2==0x36) /* LD (I?+d),imm */
X  {
X    wr_z80_mem(INDEX_VALUE,z80_mem(PC++));
X    return;
X  }
X
X  if ((op2&0xFE)==0x34) /* INC/DEC (I?+d) */
X  {
X    switch (op2&1)
X    {
X      case 0:wr_z80_mem(INDEX_VALUE,alu_add(z80_mem(INDEX_VALUE),1));
X      break;
X      case 1:wr_z80_mem(INDEX_VALUE,alu_sub(z80_mem(INDEX_VALUE),1));
X      break;
X    }
X    return;
X  }
X
X  if ((op2&0xC7)==0x86) /* MATH OPS (I?+d) */
X  {
X    WORD t;
X    switch((op2&0x38)>>3)
X    {
X      case 0:t=alu_add(AF>>8,z80_mem(INDEX_VALUE))<<8; AF&=0xff; AF|=t; /* ADD */
X      break;
X      case 1:t=alu_adc(AF>>8,z80_mem(INDEX_VALUE))<<8; AF&=0xff; AF|=t; 	     /* ADC */
X      break;
X      case 2:t=alu_sub(AF>>8,z80_mem(INDEX_VALUE))<<8; AF&=0xff; AF|=t; /* SUB */
X      break;
X      case 3:t=alu_sbc(AF>>8,z80_mem(INDEX_VALUE))<<8; AF&=0xff; AF|=t; 	     /* SBC */
X      break;
X      case 4:AF=(AF&0xff)|(((AF>>8) & z80_mem(INDEX_VALUE))<<8);
X	     log_flags(AF>>8);
X	     /* AND */
X      break;
X      case 5:AF=(AF&0xff)|(((AF>>8) ^ z80_mem(INDEX_VALUE))<<8);
X	     log_flags(AF>>8);
X	     /* XOR */
X      break;
X      case 6:AF=(AF&0xff)|(((AF>>8) | z80_mem(INDEX_VALUE))<<8);
X	     log_flags(AF>>8);
X	     /* OR */
X      break;
X      case 7:alu_sub(AF>>8,z80_mem(INDEX_VALUE)); /* CMP */
X      break;
X    }
X
X    return;
X  }
X
X/* From here on are ops that don't involve d. For these, we
X   must decrement the PC to make up for the extra increment we did
X   to fetch d in the first place. d must be the opcode for the
X   next instruction. */
X
X  PC--;
X  d=0;  /* this makes our macro work. What a yucky hack! */
X
X  if (op2==0x21) /* LD I?,imm */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X    if (IY_FLAG)
X      IY=operand;
X    else
X      IX=operand;
X
X    return;
X  }
X  if (op2==0x22) /* LD (imm),I? */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X
X    wr_z80_mem(operand,INDEX_VALUE&0xff);
X    wr_z80_mem(operand+1,INDEX_VALUE>>8);
X
X    return;
X  }
X  if (op2==0x2A) /* LD I?,(imm) */
X  {
X    register WORD operand;
X
X    operand=z80_mem(PC++);
X    operand|=z80_mem(PC++)<<8;
X
X    if (IY_FLAG)
X      IY=z80_mem(operand)|(z80_mem(operand+1)<<8);
X    else
X      IX=z80_mem(operand)|(z80_mem(operand+1)<<8);
X
X    return;
X  }
X
X  if ((op2&0xCF)==0x09) /* ADD I?,rp */
X  {
X    register int temp;
X
X    switch ((op2&0x30)>>4)
X    {
X      case 0:temp=INDEX_VALUE+BC; break;
X      case 1:temp=INDEX_VALUE+DE; break;
X      case 2:temp=INDEX_VALUE+HL; break;
X      case 3:temp=INDEX_VALUE+SP; break;
X    }
X    if (IY_FLAG)
X      IY=(WORD) temp;
X    else
X      IX=(WORD) temp;
X    AF|=((temp>0xffff)?FLAG_C:0);
X    AF&=~FLAG_N;
X    return;
X  }
X
X  if (op2==0x23) /* INC I? */
X  {
X    if (IY_FLAG)
X      IY++;
X    else
X      IX++;
X
X    return;
X  }
X  if (op2==0x2B) /* DEC I? */
X  {
X    if (IY_FLAG)
X      IY--;
X    else
X      IX--;
X
X    return;
X  }
X  if (op2==0xE1) /* POP I? */
X  {
X    if (IY_FLAG)
X      IY=pop();
X    else
X      IX=pop();
X
X    return;
X  }
X  if (op2==0xE5) /* PUSH I? */
X  {
X    push(INDEX_VALUE);
X    return;
X  }
X  if (op2==0xE9) /* JP (I?) */
X  {
X    PC=INDEX_VALUE;
X    return;
X  }
X  if (op2==0xE3) /* EX (SP),I? */
X  {
X    register WORD temp;
X
X    temp=z80_mem(SP)|(z80_mem(SP+1)<<8);
X    wr_z80_mem(SP,INDEX_VALUE&0xff);
X    wr_z80_mem(SP+1,INDEX_VALUE>>8);
X    if (IY_FLAG)
X      IY=temp;
X    else
X      IX=temp;
X
X    return;
X  }
X  if (op2==0xF9) /* LD SP,I? */
X  {
X    SP=INDEX_VALUE;
X    return;
X  }
X
X/* There are undefined ED/FD ops. What do we do about it? Good
X   question. My theory says crash-n-burn. */
X
X  printf("OH NO!!!!! PARSE ERROR 2nd operand, Index ops\n");
X  printf("PC = %4x  (PC) = %2x\n\n",PC-1,op2);
X
X  exit(99);
X}
SHAR_EOF
$TOUCH -am 0928182190 z80_cbed.c &&
chmod 0644 z80_cbed.c ||
echo "restore of z80_cbed.c failed"
set `wc -c z80_cbed.c`;Wc_c=$1
if test "$Wc_c" != "16492"; then
	echo original size 16492, current size $Wc_c
fi
rm -f shar3_seq_.tmp
echo "You have unpacked the last part"
exit 0
-- 
Nick Sayer              | Disclaimer: "Don't try this at home, | RIP: Mel Blanc
mrapple@quack.sac.ca.us | kids. This should only be done by    |   1908-1989
N6QQQ  [44.2.1.17]      | trained, professional idiots."       |  May he never
209-952-5347 (Telebit)  |                     --Plucky Duck    |  be silenced.