home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume15
/
upm
/
part02
< prev
next >
Wrap
Text File
|
1990-12-17
|
46KB
|
1,939 lines
Newsgroups: comp.sources.misc
X-UNIX-From: mrapple@quack.sac.ca.us
organization: The Duck Pond, Stockton, CA
subject: v15i102: CP/M emulator, part 2/2
from: mrapple@quack.sac.ca.us (Nick Sayer)
Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
Posting-number: Volume 15, Issue 102
Submitted-by: mrapple@quack.sac.ca.us (Nick Sayer)
Archive-name: upm/part02
#!/bin/sh
# this is upm.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.