home *** CD-ROM | disk | FTP | other *** search
- /*
-
- z80.c - Z-80 microprocessor emulator.
-
- Copyright MCMXC - Nick Sayer - All rights reserved.
-
- See COPYRIGHT file for details.
-
- v0.0 - 04/08/90 - epoch
- v0.0A0 - 04/13/90 - alpha-test.
- v0.0A1 - 08/04/90 - alpha-test 2.
- v0.0A2 - 09/04/90 - alpha-test 3.
-
- global data types:
-
- WORD = unsigned short - i.e. an address or register pair.
- BYTE = unsigned char - i.e. a memory location or register.
-
- global data:
-
- BYTE z80_mem[65536];
- WORD AF,BC,DE,HL,SP,PC,IX,IY,IR,AF2,BC2,DE2,HL2,INT_FLAGS;
-
- global routines:
-
- z80_run();
-
- Start running at addr. PRESUMES PC AND OTHER REGISTERS SET PROPERLY!!!
- Returns if Z-80 executes a HALT. Returns with PC set to address of HALT
- instruction.
-
- z80_instr();
-
- Execute a single instruction.
-
- wrport(addr,data);
- BYTE addr,data;
-
- BYTE rdport(addr);
- BYTE addr;
-
- These routines are called by the Z-80 when it wants to read or write
- to the port-space.
-
- char INT,NMI,RESET;
-
- Each of these starts at 0. If some event makes any of these true, the
- event each represents will take place. Think of them as the coresponding
- wires that go into the CPU, except that in the real CPU these wires are
- inverse logic.
-
- BYTE int_read();
-
- This routine called on an interrupt. It should return the proper
- interrupt acknowledgement cycle data.
-
- KNOWN "FEATURES":
-
- This actually simulates a MOSTEK MK 3880. Whether or not this
- device differs from the Zilog product, I don't know. But I
- doubt it.
-
- If you single-step using z80_instr(), memory refresh, interrupt
- checking, and similar housekeeping will NOT take place.
-
- If the processor is in interrupt mode 0, the int_read()
- value MUST be an RST instruction.
-
- Undefined opcode sequences WILL have truely bizarre and twisted
- results. Count on it. Especially undefined DD/FD operations.
-
- "Interrupting devices" at this time can't tell when an interrupt
- service routine is finished. Normally they monitor the bus
- waiting for a RETI instruction. There's no way to do that with
- this code yet.
-
- */
-
- #include "z80.h"
-
- BYTE real_z80_mem[65536];
- char STOP_FLAG; /* hack to stop us on HALT */
- char INT=0,NMI=0,RESET=0;
- WORD AF,BC,DE,HL,PC,SP,IX,IY,IR,AF2,BC2,DE2,HL2,INT_FLAGS;
- int TRAPval = -1, lastPC = -1;
-
- z80_run()
- {
- STOP_FLAG=0;
- do
- {
-
- /* do an instruction */
-
- if (PC == TRAPval) {
- printf("\n\rTrapping at 0x%04x (last=0x%04x)\n\r",PC,lastPC);
- debugit();
- }
- lastPC = PC;
- z80_instr();
-
- /* If we did an EI instruction before last, set both IFFs */
-
- if (INT_FLAGS&IFTMP)
- {
- INT_FLAGS-=0x10;
- if (!(INT_FLAGS&IFTMP))
- INT_FLAGS|=IFF1|IFF2;
- }
-
- /* If an interrupt is pending and they're enabled, do it */
-
- if (INT && INT_FLAGS&IFF1)
- {
- register WORD operand;
-
- INT_FLAGS&=~(IFF1|IFF2);
- push(PC);
- switch (INT_FLAGS&IM_STAT)
- {
- case 0:PC=int_read()&0x38; /* DANGEROUSLY assumes an RST op... */
- break;
- case 1:PC=0x38; int_read(); /* we have to fetch, then spike it */
- break;
- case 2:operand=(IR&0xff80)|(int_read()&0xfe);
- PC=z80_mem(operand)|(z80_mem(operand+1)<<8);
- break;
- }
- INT=0;
- }
-
- /* If an NMI is pending, do it */
-
- if (NMI)
- {
- INT_FLAGS&=~IFF1;
- push(PC);
- PC=0x66;
- NMI=0;
- }
-
- /* if a RESET is pending, that has absolute priority */
-
- if (RESET)
- {
- INT=0; NMI=0; RESET=0; INT_FLAGS=0; PC=0;
- }
-
- /* Now do a "refresh" cycle (really just increment low 7 bits of IR) */
-
- IR=(IR&0xff00)|((IR+1)&0x7f);
-