Z-80 emulator in C with CP/M BIOS

Nick Sayer mrapple at quack.sac.ca.us
Fri Nov 9 04:17:26 AEST 1990


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 at 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 at 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.



More information about the Alt.sources mailing list