site save of codeslinger.co.uk - gameboy emulator programming in c++ - opcodes

Upload: ryan

Post on 07-Apr-2018

227 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    1/12

    ExecuteNextOpcode:So far this tutorial has focussed o n how the internals of the gameboy fit together and how to emulate

    each. I often mentioned how knowing ho w long each ins truction takes to execute on the original hardware

    we can synchronize the graphics and timers. This information was acquired in the main update loo p from

    the function called ExecuteNextOpcode, however I never gave an implem entation to this function, m ainly

    because it was to do with emulating the Z80 like process or, and I was leaving this to las t. As I have

    discuss ed all other aspects of the gameboy I can now give the definition of this function.

    int Emulator::ExecuteNextOpcode( )

    {

    int res = 0

    BYTE opcode = ReadMemory(m_ProgramCounter) ;

    m_ProgramCounter++ ;

    res = ExecuteOpcode(opcode) ;

    return res ;

    }

    Well thats s hort and sweet, however now we have to im plement the ExecuteOpcode function which is a

    GameboyEmulation:

    Getting Started

    The Hardware

    Memory Control and

    Ma p

    ROM and RAM

    Banking

    The Timers

    Interupts

    LCD

    DMA Transfer

    Graphic Emulation

    Joypad Emulation

    Opcode Examples

    codeslinger.co.uk

    H o m e

    B a s i c s

    Z u k o

    M e g a D r i v e / G e n e s i s

    M a s t e r S y s t e m

    Gameboy C h i p 8

    B l o g

    codeslinger.co.uk

    Gameboy - Example Opcodes.

    PDFmyURL.com

    http://codeslinger.co.uk/index.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/dma.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/dma.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/dma.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/dma.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/dma.htmlhttp://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://codeslinger.co.uk/pages/projects/gameboy/finished.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/opcodes.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/joypad.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/graphics.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/dma.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/lcd.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/interupts.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/timers.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/banking.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/memory.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/hardware.htmlhttp://codeslinger.co.uk/pages/projects/gameboy/beginning.htmlhttp://codeslinger.co.uk/pages/blog/index.htmlhttp://codeslinger.co.uk/pages/projects/chip8.htmlhttp://codeslinger.co.uk/pages/projects/gameboy.htmlhttp://codeslinger.co.uk/pages/projects/mastersystem.htmlhttp://codeslinger.co.uk/pages/projects/megadrive.htmlhttp://codeslinger.co.uk/pages/projects/zuko.htmlhttp://codeslinger.co.uk/pages/basics.htmlhttp://codeslinger.co.uk/index.phphttp://codeslinger.co.uk/index.html
  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    2/12

  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    3/12

    assert(false); return 0 ;// unhandled opcode

    }

    The opcode 0 xCB is the extended opcode m eaning that whenever we in encounter the 0xCB o pcode wehave to decode the next immediate byte in memo ry and treat that as the opcode. The opco des 0 xAB isdifferent to 0 xCB 0xAB. This m eans we can have more o pcodes then the standard 0x0- 0xFF.ExecuteExtendedOpcode function is identical layout to ExecuteOpcode except it deals with the extendedopcodes like so:

    int Emulator::ExecuteExtendedOpcode( )

    {

    BYTE opcode = ReadMemory(m_ProgramCounter) ;

    m_ProgramCounter++;

    switch(opcode)

    {

    case 0xB :

    CPU_RRC(m_RegisterDE.lo) ;

    return 8;

    case 0x73 :

    CPU_TEST_BIT( m_RegisterDE.lo, 6 ) ;

    return 8;

    default:

    assert(false); return 0; // unhandled extended opcode

    }

    Thats all there is to em ulating the Z80 except of course actually implementing all the functions that arecalled from the switch statement :-). Remember that all the CPU instructions yo u have to im plement can

    be found in the Gameboy PDF im hos ting.

    8-Bit Loads:

    The 8-Bit Loads put one byte of immediate data from memo ry into one o f the 8 registers. So if the

    function CPU_8 IT_LOAD takes a reference of one o f the registers as an argument like opco de 0x0 6 then

    it can be used for all 8 registers. Opcode 0x0 6 lo ads o ne byte of immediate data into register B.

    Implementation:

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://codeslinger.co.uk/pages/projects/gameboy/files/GB.pdf
  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    4/12

    void Emulator::CPU_8BIT_LOAD( BYTE& reg )

    {

    BYTE n = ReadMemory(m_ProgramCounter) ;

    m_ProgramCounter++ ;

    reg = n ;

    }

    This function can be used fo r all 8 registers, not just B.

    8-Bit ADD:

    This function adds one byte to one o f the registes and sets flags. Like CPU_8BIT_LOAD if we pass a

    reference as a parameter then it can be used for all 8 registes no t just register A as in the example

    (opcode 0 x80 ). This function takes a total of 4 arguments, these are:

    Argument 1: The register reference where the ADD result will be placed

    Argument 2: The byte to add to the register if it is no t immediate data

    Argument 3: Wheter what we want to add to the register is im mediate data

    Argument 4: Wheter we also want to add the carry flag

    There are two type of z 80 8- bit add instructions . The first adds a byte to the register and the second o neadds a byte plus the carry flag to the register (hence the fourth argument). This instruction is called ADC.

    This is the conditions the flags get set under:

    FLAG_Z: If after the addition the result is 0

    FLAG_N: Set to 0

    FLAG_C: If the result will be greater than max byte (255)

    FLAG_H: If there will be a carry from the lo wer nibble to the upper nibble

    FLAG_C needs to check the result before the addition, o therwise how wil l you kno w if the result wasgreater than 255 if when it does go greater than 255 it wraps round back to 0 ?

    This is the implementation o f 8-Bit ADD

    void Emulator::CPU_8BIT_ADD(BYTE& reg, BYTE toAdd, int cycles, bool

    useImmediate, bool addCarry)

    {

    BYTE before = reg ;

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    5/12

    BYTE adding = 0 ;

    // are we adding immediate data or the second param?

    if (useImmediate)

    {

    BYTE n = ReadMemory(m_ProgramCounter) ;

    m_ProgramCounter++ ;

    adding = n ;

    }else

    {

    adding = toAdd ;

    }

    // are we also adding the carry flag?

    if (addCarry)

    {

    if (TestBit(m_RegisterAF.lo, FLAG_C))

    adding++ ;

    }

    reg+=adding ;

    // set the flags

    m_RegisterAF.lo = 0 ;

    if (reg == 0)

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_Z) ;

    WORD htest = (before & 0xF) ;

    htest += (adding & 0xF) ;

    if (htest > 0xF)

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_H) ;

    if ((before + adding) > 0xFF)

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_C) ;

    }

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    6/12

    8-Bit Sub:

    This is pretty much identical to 8- Bit Add except its for s ubtractions. It still can use im mediate data or the

    second argument and it can also m inus the carry flag aswell. This is how the flags are affected:

    FLAG_Z: If after the subtraction the result is 0

    FLAG_N: Set to 1

    FLAG_C: If the result will be less than 0

    FLAG_H: If there will be a carry from the lo wer upper nibble to the lower nibble

    It is implem ented like so :

    void Emulator::CPU_8BIT_SUB(BYTE& reg, BYTE subtracting, int cycles, bool

    useImmediate, bool subCarry)

    {

    BYTE before = reg ;

    BYTE toSubtract = 0 ;

    if (useImmediate)

    {

    BYTE n = ReadMemory(m_ProgramCounter) ;

    m_ProgramCounter++ ;

    toSubtract = n ;

    }

    else

    {

    toSubtract = subtracting ;

    }

    if (subCarry)

    {

    if (TestBit(m_RegisterAF.lo, FLAG_C))

    toSubtract++ ;

    }

    reg -= toSubtract ;

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    7/12

    m_RegisterAF.lo = 0 ;

    if (reg == 0)

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_Z) ;

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_N) ;

    // set if no borrow

    if (before < toSubtract)m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_C) ;

    SIGNED_WORD htest = (before & 0xF) ;

    htest -= (toSubtract & 0xF) ;

    if (htest < 0)

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_H) ;

    }

    8-Bit XOR:

    All the logical o perators AND, OR and XOR work the same as ADD and SUB, except it doesnt care about

    the carry flag (so there is no fo urth argument). The flags are set like s o fo r XOR:

    FLAG_Z: If the result is 0

    FLAG_N: Set to 0

    FLAG_C: Set to 0

    FLAG_H: Set to 0

    Nice and easy. This is the implementation:

    void Emulator::CPU_8BIT_XOR(BYTE& reg, BYTE toXOr, int cycles, bool

    useImmediate)

    {

    BYTE myxor = 0 ;

    if (useImmediate)

    {

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    8/12

    BYTE n = ReadMemory(m_ProgramCounter) ;

    m_ProgramCounter++ ;

    myxor = n ;

    }

    else

    {

    myxor = toXOr ;

    }

    reg ^= myxor ;

    m_RegisterAF.lo = 0 ;

    if (reg == 0)

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_Z) ;

    }

    Jumps:

    There are a fair few jump instructions but they all do the same thing. Jumps to a s pecific address, ho wever

    mo st of the jump instruction o nly jumps if a certain condition is true. Some jump instructions jumps to the

    address po inted to by the next two bytes o f the immediate data. Where the other jump instructions jum ps

    to the current program counter + the next immediate data (called Jum p Immediate). One jump instructions

    jumps to the address po inted to by register HL. This is the implementation o f the JUMP_IMMEDIATE

    instruction:

    void Emulator::CPU_JUMP_IMMEDIATE(bool useCondition, int flag, bool condition)

    {

    SIGNED_BYTE n = (SIGNED_BYTE)ReadMemory(m_ProgramCounter) ;

    if (!useCondition)

    {

    m_ProgramCounter += n;

    }

    else if (TestBit(m_RegisterAF.lo, flag) == condition)

    {

    m_ProgramCounter += n ;

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    9/12

    }

    m_ProgramCounter++ ;

    }

    As you can see if we are using a jum p condition (i.e. jump if the Z flag is set) then we jump to address ofm_ProgramCounter+ReadMemory(m_ProgramCounter).

    Calls:

    The call instruction is the sam e as the Jump i nstruction which jumps to the address o f the next two

    immediate data. However the difference is that a call is a subro utine and will eventually return to its current

    address, where as a jump do esn't. So in o rder to kno w where to return to we have to push the current

    program counter on to the stack.

    void Emulator::CPU_CALL(bool useCondition, int flag, bool condition)

    {

    WORD nn = ReadWord( ) ;

    m_ProgramCounter += 2;

    if (!useCondition)

    {

    PushWordOntoStack(m_ProgramCounter) ;

    m_ProgramCounter = nn ;

    return ;

    }

    if (TestBit(m_RegisterAF.lo, flag)==condition)

    {

    PushWordOntoStack(m_ProgramCounter) ;m_ProgramCounter = nn ;

    }

    }

    Remember this is the same as the o ther Jump instruction (not jump imm ediate) except that the jumpinstruction do esnt push anything onto the stack

    Returns:PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    10/12

    Now we've handled calls to subro utines we neeed to return from them. The way you return is yo u pop a

    word from the stack and assign it to the program co unter (remember you pushed the program co unter

    onto the stack with the call function). Once again we can put a conditio n on the return aswell.

    void Emulator::CPU_RETURN(bool useCondition, int flag, bool condition)

    {

    if (!useCondition)

    {

    m_ProgramCounter = PopWordOffStack( ) ;

    return ;

    }

    if (TestBit(m_RegisterAF.lo, flag) == condition)

    {

    m_ProgramCounter = PopWordOffStack( ) ;

    }

    }

    Extended: Rotate Right Through Carry:

    This ins truction ro tates a regiser A right and sets the carry flag with the least significant bit before the

    rotation. A right rotation means everything gets s hifted right one and the mo st si gnificant bit gets s et to

    what the least significant bit was before the ro tation. All the flags get set to 0 except the carry flag which as

    previously m entioned gets set to the least significant bit of reg a before the rotation

    void Emulator::CPU_RRC(BYTE& reg)

    {

    bool isLSBSet = TestBit(reg, 0) ;

    m_RegisterAF.lo = 0 ;

    reg >>= 1;

    if (isLSBSet)

    {

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_C) ;

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    11/12

    reg = BitSet(reg,7) ;

    }

    if (reg == 0)

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_Z) ;

    }

    Extended: TestBit:

    This tests the bit of a byte and sets the approprite flags. The flags are set like so :

    FLAG Z: Set if the bit is 0

    FLAG_N: Set to 0

    FLAG_C: Unchanged

    FLAG_H: Set to 1

    This is the implemenation:

    void Emulator::CPU_TEST_BIT(BYTE reg, int bit, int cycles)

    {

    if (TestBit(reg, bit))

    m_RegisterAF.lo = BitReset(m_RegisterAF.lo, FLAG_Z) ;

    else

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_Z) ;

    m_RegisterAF.lo = BitReset(m_RegisterAF.lo, FLAG_N) ;

    m_RegisterAF.lo = BitSet(m_RegisterAF.lo, FLAG_H) ;

    }

    The important thing to know with the TEST_BIT instruction (and with the SET_BIT and RESET_BITinstructions ). Is that the documentation states that extended opco de 0x40 tests bit 0 o f register B. Butdoes nt give the opco des for when bit 1 is needed to be tested or bit 2. Luckily all you have to do is add0x8 to get hex value of the other opco des. So to test bit 1 of register B it is 0 x48. To test bit 2 o f register Bit is 0x50 . This also works for the other registers, for example the documentation says that opcode 0 x41tests bit 0 o f register C. From this we can also deduce that 0x49 tests bit 1 of register C etc. You need toemulate all these ins truction to test all 7 bits of all 8 regis ters. The same goes for emulating the SET_BITand RESET_BIT functions.

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01
  • 8/6/2019 Site save of CodeSlinger.co.uk - GameBoy Emulator Programming in C++ - Opcodes

    12/12

    Copyright 2008 codeslinger.co.uk

    PDFmyURL.com

    http://pdfmyurl.com/?otsrc=watermark&otclc=0.01http://pdfmyurl.com/?otsrc=watermark&otclc=0.01