/* Z80 emulator Copyright (c) 1996-2003,2004 Alessandro Scotti http://www.ascotti.org/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "z80.h" /* Constructor */ Z80::Z80( Z80Environment & env ) : env_( env ) { reset(); } /* Resets the CPU */ void Z80::reset() { PC = 0; // Program counter is zero I = 0; // Interrupt register cleared R = 0; // Memory refresh register cleared iflags_ = 0; // IFF1 and IFF2 cleared, IM0 enabled cycles_ = 0; // Could that be 2 (according to some Zilog docs)? // There is no official documentation for the following! B = B1 = 0; C = C1 = 0; D = D1 = 0; E = E1 = 0; H = H1 = 0; L = L1 = 0; A = A1 = 0; F = F1 = 0; IX = 0; IY = 0; SP = 0xF000; } /* Executes one instruction */ void Z80::step() { // Update memory refresh register R = (R+1) & 0x7F; if( iflags_ & Halted ) { // CPU is halted, do a NOP instruction cycles_ += OpInfo_[0].cycles; // NOP } else { // Get the opcode to execute unsigned op = fetchByte(); // Update the cycles counter with the number of cycles for this opcode cycles_ += OpInfo_[ op ].cycles; // Execute the opcode handler (this->*(OpInfo_[ op ].handler))(); // Update registers PC &= 0xFFFF; // Clip program counter SP &= 0xFFFF; // Clip stack pointer } } /* Runs the CPU for the specified number of cycles. Note: the memory refresh register is not updated! */ unsigned Z80::run( unsigned runCycles ) { unsigned target_cycles = cycles_ + runCycles; // Execute instructions until the specified number of // cycles has elapsed while( cycles_ < target_cycles ) { // Update memory refresh register R = (R+1) & 0x7F; if( iflags_ & Halted ) { // CPU is halted, do NOPs for the rest of cycles // (this may be off by a few cycles) cycles_ = target_cycles; } else { // Get the opcode to execute unsigned op = fetchByte(); // Update the cycles counter with the number of cycles for this opcode cycles_ += OpInfo_[ op ].cycles; // Execute the opcode handler (this->*(OpInfo_[ op ].handler))(); } } // Update registers PC &= 0xFFFF; // Clip program counter SP &= 0xFFFF; // Clip stack pointer // Return the number of extra cycles executed return cycles_ - target_cycles; } /* Interrupt */ bool Z80::interrupt( unsigned char data ) { bool result = false; // Execute interrupt only if interrupts are enabled if( iflags_ & IFF1 ) { // Disable maskable interrupts and restart the CPU if halted iflags_ &= ~(IFF1 | IFF2 | Halted); switch( getInterruptMode() ) { case 0: (this->*(OpInfo_[ data ].handler))(); cycles_ += 11; break; case 1: callSub( 0x38 ); cycles_ += 11; break; case 2: callSub( readWord( ((unsigned)I) << 8 | (data & 0xFE) ) ); cycles_ += 19; break; } result = true; } return result; } /* Non-maskable interrupt */ void Z80::nmi() { // Disable maskable interrupts but preserve IFF2 (that is a copy of IFF1), // also restart the CPU if halted iflags_ &= ~(IFF1 | Halted); callSub( 0x66 ); cycles_ += 11; }