GameBoy\Core::memoryWrite PHP Method

memoryWrite() public method

Memory Writing:
public memoryWrite ( $address, $data )
    public function memoryWrite($address, $data)
    {
        if ($address < 0x8000) {
            if ($this->cMBC1) {
                if ($address < 0x2000) {
                    //MBC RAM Bank Enable/Disable:
                    $this->MBCRAMBanksEnabled = ($data & 0xf) == 0xa;
                    //If lower nibble is 0x0A, then enable, otherwise disable.
                } elseif ($address < 0x4000) {
                    // MBC1WriteROMBank
                    //MBC1 ROM bank switching:
                    $this->ROMBank1offs = $this->ROMBank1offs & 0x60 | $data & 0x1f;
                    $this->setCurrentMBC1ROMBank();
                } elseif ($address < 0x6000) {
                    //MBC1WriteRAMBank
                    //MBC1 RAM bank switching
                    if ($this->MBC1Mode) {
                        //4/32 Mode
                        $this->currMBCRAMBank = $data & 0x3;
                        $this->currMBCRAMBankPosition = ($this->currMBCRAMBank << 13) - 0xa000;
                    } else {
                        //16/8 Mode
                        $this->ROMBank1offs = ($data & 0x3) << 5 | $this->ROMBank1offs & 0x1f;
                        $this->setCurrentMBC1ROMBank();
                    }
                } else {
                    //MBC1WriteType
                    //MBC1 mode setting:
                    $this->MBC1Mode = ($data & 0x1) == 0x1;
                }
            } elseif ($this->cMBC2) {
                if ($address < 0x1000) {
                    //MBC RAM Bank Enable/Disable:
                    $this->MBCRAMBanksEnabled = ($data & 0xf) == 0xa;
                    //If lower nibble is 0x0A, then enable, otherwise disable.
                } elseif ($address >= 0x2100 && $address < 0x2200) {
                    //MBC2WriteROMBank
                    //MBC2 ROM bank switching:
                    $this->ROMBank1offs = $data & 0xf;
                    $this->setCurrentMBC2AND3ROMBank();
                } else {
                    //We might have encountered illegal RAM writing or such, so just do nothing...
                }
            } elseif ($this->cMBC3) {
                if ($address < 0x2000) {
                    //MBC RAM Bank Enable/Disable:
                    $this->MBCRAMBanksEnabled = ($data & 0xf) == 0xa;
                    //If lower nibble is 0x0A, then enable, otherwise disable.
                } elseif ($address < 0x4000) {
                    //MBC3 ROM bank switching:
                    $this->ROMBank1offs = $data & 0x7f;
                    $this->setCurrentMBC2AND3ROMBank();
                } elseif ($address < 0x6000) {
                    //MBC3WriteRAMBank
                    $this->currMBCRAMBank = $data;
                    if ($data < 4) {
                        //MBC3 RAM bank switching
                        $this->currMBCRAMBankPosition = ($this->currMBCRAMBank << 13) - 0xa000;
                    }
                } else {
                    //MBC3WriteRTCLatch
                    if ($data == 0) {
                        $this->RTCisLatched = false;
                    } elseif (!$this->RTCisLatched) {
                        //Copy over the current RTC time for reading.
                        $this->RTCisLatched = true;
                        $this->latchedSeconds = floor($this->RTCSeconds);
                        $this->latchedMinutes = $this->RTCMinutes;
                        $this->latchedHours = $this->RTCHours;
                        $this->latchedLDays = $this->RTCDays & 0xff;
                        $this->latchedHDays = $this->RTCDays >> 8;
                    }
                }
            } elseif ($this->cMBC5 || $this->cRUMBLE) {
                if ($address < 0x2000) {
                    //MBC RAM Bank Enable/Disable:
                    $this->MBCRAMBanksEnabled = ($data & 0xf) == 0xa;
                    //If lower nibble is 0x0A, then enable, otherwise disable.
                } elseif ($address < 0x3000) {
                    //MBC5WriteROMBankLow
                    //MBC5 ROM bank switching:
                    $this->ROMBank1offs = $this->ROMBank1offs & 0x100 | $data;
                    $this->setCurrentMBC5ROMBank();
                } elseif ($address < 0x4000) {
                    //MBC5WriteROMBankHigh
                    //MBC5 ROM bank switching (by least significant bit):
                    $this->ROMBank1offs = ($data & 0x1) << 8 | $this->ROMBank1offs & 0xff;
                    $this->setCurrentMBC5ROMBank();
                } elseif ($address < 0x6000) {
                    if ($this->cRUMBLE) {
                        //MBC5 RAM bank switching
                        //Like MBC5, but bit 3 of the lower nibble is used for rumbling and bit 2 is ignored.
                        $this->currMBCRAMBank = $data & 0x3;
                        $this->currMBCRAMBankPosition = ($this->currMBCRAMBank << 13) - 0xa000;
                    } else {
                        //MBC5 RAM bank switching
                        $this->currMBCRAMBank = $data & 0xf;
                        $this->currMBCRAMBankPosition = ($this->currMBCRAMBank << 13) - 0xa000;
                    }
                } else {
                    //We might have encountered illegal RAM writing or such, so just do nothing...
                }
            } elseif ($this->cHuC3) {
                if ($address < 0x2000) {
                    //MBC RAM Bank Enable/Disable:
                    $this->MBCRAMBanksEnabled = ($data & 0xf) == 0xa;
                    //If lower nibble is 0x0A, then enable, otherwise disable.
                } elseif ($address < 0x4000) {
                    //MBC3 ROM bank switching:
                    $this->ROMBank1offs = $data & 0x7f;
                    $this->setCurrentMBC2AND3ROMBank();
                } elseif ($address < 0x6000) {
                    //HuC3WriteRAMBank
                    //HuC3 RAM bank switching
                    $this->currMBCRAMBank = $data & 0x3;
                    $this->currMBCRAMBankPosition = ($this->currMBCRAMBank << 13) - 0xa000;
                } else {
                    //We might have encountered illegal RAM writing or such, so just do nothing...
                }
            } else {
                //We might have encountered illegal RAM writing or such, so just do nothing...
            }
        } elseif ($address < 0xa000) {
            // VRAMWrite
            //VRAM cannot be written to during mode 3
            if ($this->lcdController->modeSTAT < 3) {
                // Bkg Tile data area
                if ($address < 0x9800) {
                    $tileIndex = ($address - 0x8000 >> 4) + 384 * $this->currVRAMBank;
                    if ($this->tileReadState[$tileIndex] == 1) {
                        $r = count($this->tileData) - $this->tileCount + $tileIndex;
                        do {
                            $this->tileData[$r] = null;
                            $r -= $this->tileCount;
                        } while ($r >= 0);
                        $this->tileReadState[$tileIndex] = 0;
                    }
                }
                if ($this->currVRAMBank == 0) {
                    $this->memory[$address] = $data;
                } else {
                    $this->VRAM[$address - 0x8000] = $data;
                }
            }
        } elseif ($address < 0xc000) {
            if ($this->numRAMBanks == 1 / 16 && $address < 0xa200 || $this->numRAMBanks >= 1) {
                if (!$this->cMBC3) {
                    //memoryWriteMBCRAM
                    if ($this->MBCRAMBanksEnabled || Settings::$overrideMBC) {
                        $this->MBCRam[$address + $this->currMBCRAMBankPosition] = $data;
                    }
                } else {
                    //MBC3 RTC + RAM:
                    //memoryWriteMBC3RAM
                    if ($this->MBCRAMBanksEnabled || Settings::$overrideMBC) {
                        switch ($this->currMBCRAMBank) {
                            case 0x0:
                            case 0x1:
                            case 0x2:
                            case 0x3:
                                $this->MBCRam[$address + $this->currMBCRAMBankPosition] = $data;
                                break;
                            case 0x8:
                                if ($data < 60) {
                                    $this->RTCSeconds = $data;
                                } else {
                                    echo '(Bank #' . $this->currMBCRAMBank . ') RTC write out of range: ' . $data . PHP_EOL;
                                }
                                break;
                            case 0x9:
                                if ($data < 60) {
                                    $this->RTCMinutes = $data;
                                } else {
                                    echo '(Bank #' . $this->currMBCRAMBank . ') RTC write out of range: ' . $data . PHP_EOL;
                                }
                                break;
                            case 0xa:
                                if ($data < 24) {
                                    $this->RTCHours = $data;
                                } else {
                                    echo '(Bank #' . $this->currMBCRAMBank . ') RTC write out of range: ' . $data . PHP_EOL;
                                }
                                break;
                            case 0xb:
                                $this->RTCDays = $data & 0xff | $this->RTCDays & 0x100;
                                break;
                            case 0xc:
                                $this->RTCDayOverFlow = ($data & 0x80) == 0x80;
                                $this->RTCHalt = ($data & 0x40) == 0x40;
                                $this->RTCDays = ($data & 0x1) << 8 | $this->RTCDays & 0xff;
                                break;
                            default:
                                echo 'Invalid MBC3 bank address selected: ' . $this->currMBCRAMBank . PHP_EOL;
                        }
                    }
                }
            } else {
                //We might have encountered illegal RAM writing or such, so just do nothing...
            }
        } elseif ($address < 0xe000) {
            if ($this->cGBC && $address >= 0xd000) {
                //memoryWriteGBCRAM
                $this->GBCMemory[$address + $this->gbcRamBankPosition] = $data;
            } else {
                //memoryWriteNormal
                $this->memory[$address] = $data;
            }
        } elseif ($address < 0xfe00) {
            if ($this->cGBC && $address >= 0xf000) {
                //memoryWriteECHOGBCRAM
                $this->GBCMemory[$address + $this->gbcRamBankPositionECHO] = $data;
            } else {
                //memoryWriteECHONormal
                $this->memory[$address - 0x2000] = $data;
            }
        } elseif ($address <= 0xfea0) {
            //memoryWriteOAMRAM
            //OAM RAM cannot be written to in mode 2 & 3
            if ($this->lcdController->modeSTAT < 2) {
                $this->memory[$address] = $data;
            }
        } elseif ($address < 0xff00) {
            //Only GBC has access to this RAM.
            if ($this->cGBC) {
                //memoryWriteNormal
                $this->memory[$address] = $data;
            } else {
                //We might have encountered illegal RAM writing or such, so just do nothing...
            }
            //I/O Registers (GB + GBC):
        } elseif ($address == 0xff00) {
            $this->memory[0xff00] = $data & 0x30 | (($data & 0x20) == 0 ? $this->JoyPad >> 4 : 0xf) & (($data & 0x10) == 0 ? $this->JoyPad & 0xf : 0xf);
        } elseif ($address == 0xff02) {
            if (($data & 0x1) == 0x1) {
                //Internal clock:
                $this->memory[0xff02] = $data & 0x7f;
                $this->memory[0xff0f] |= 0x8;
                //Get this time delayed...
            } else {
                //External clock:
                $this->memory[0xff02] = $data;
                //No connected serial device, so don't trigger interrupt...
            }
        } elseif ($address == 0xff04) {
            $this->memory[0xff04] = 0;
        } elseif ($address == 0xff07) {
            $this->memory[0xff07] = $data & 0x7;
            $this->TIMAEnabled = ($data & 0x4) == 0x4;
            $this->TACClocker = pow(4, ($data & 0x3) != 0 ? $data & 0x3 : 4);
            //TODO: Find a way to not make a conditional in here...
        } elseif ($address == 0xff40) {
            if ($this->cGBC) {
                $temp_var = ($data & 0x80) == 0x80;
                if ($temp_var != $this->lcdController->LCDisOn) {
                    //When the display mode changes...
                    $this->lcdController->LCDisOn = $temp_var;
                    $this->memory[0xff41] &= 0xf8;
                    $this->lcdController->STATTracker = $this->lcdController->modeSTAT = $this->LCDTicks = $this->lcdController->actualScanLine = $this->memory[0xff44] = 0;
                    if ($this->lcdController->LCDisOn) {
                        $this->lcdController->matchLYC();
                        //Get the compare of the first scan line.
                    } else {
                        $this->displayShowOff();
                    }
                    $this->memory[0xff0f] &= 0xfd;
                }
                $this->gfxWindowY = ($data & 0x40) == 0x40;
                $this->gfxWindowDisplay = ($data & 0x20) == 0x20;
                $this->gfxBackgroundX = ($data & 0x10) == 0x10;
                $this->gfxBackgroundY = ($data & 0x8) == 0x8;
                $this->gfxSpriteDouble = ($data & 0x4) == 0x4;
                $this->gfxSpriteShow = ($data & 0x2) == 0x2;
                $this->spritePriorityEnabled = ($data & 0x1) == 0x1;
                $this->memory[0xff40] = $data;
            } else {
                $temp_var = ($data & 0x80) == 0x80;
                if ($temp_var != $this->lcdController->LCDisOn) {
                    //When the display mode changes...
                    $this->lcdController->LCDisOn = $temp_var;
                    $this->memory[0xff41] &= 0xf8;
                    $this->lcdController->STATTracker = $this->lcdController->modeSTAT = $this->LCDTicks = $this->lcdController->actualScanLine = $this->memory[0xff44] = 0;
                    if ($this->lcdController->LCDisOn) {
                        $this->lcdController->matchLYC();
                        //Get the compare of the first scan line.
                    } else {
                        $this->displayShowOff();
                    }
                    $this->memory[0xff0f] &= 0xfd;
                }
                $this->gfxWindowY = ($data & 0x40) == 0x40;
                $this->gfxWindowDisplay = ($data & 0x20) == 0x20;
                $this->gfxBackgroundX = ($data & 0x10) == 0x10;
                $this->gfxBackgroundY = ($data & 0x8) == 0x8;
                $this->gfxSpriteDouble = ($data & 0x4) == 0x4;
                $this->gfxSpriteShow = ($data & 0x2) == 0x2;
                if (($data & 0x1) == 0) {
                    // this emulates the gbc-in-gb-mode, not the original gb-mode
                    $this->bgEnabled = false;
                    $this->gfxWindowDisplay = false;
                } else {
                    $this->bgEnabled = true;
                }
                $this->memory[0xff40] = $data;
            }
        } elseif ($address == 0xff41) {
            if ($this->cGBC) {
                $this->lcdController->LYCMatchTriggerSTAT = ($data & 0x40) == 0x40;
                $this->lcdController->mode2TriggerSTAT = ($data & 0x20) == 0x20;
                $this->lcdController->mode1TriggerSTAT = ($data & 0x10) == 0x10;
                $this->lcdController->mode0TriggerSTAT = ($data & 0x8) == 0x8;
                $this->memory[0xff41] = $data & 0xf8;
            } else {
                $this->lcdController->LYCMatchTriggerSTAT = ($data & 0x40) == 0x40;
                $this->lcdController->mode2TriggerSTAT = ($data & 0x20) == 0x20;
                $this->lcdController->mode1TriggerSTAT = ($data & 0x10) == 0x10;
                $this->lcdController->mode0TriggerSTAT = ($data & 0x8) == 0x8;
                $this->memory[0xff41] = $data & 0xf8;
                if ($this->lcdController->LCDisOn && $this->lcdController->modeSTAT < 2) {
                    $this->memory[0xff0f] |= 0x2;
                }
            }
        } elseif ($address == 0xff45) {
            $this->memory[0xff45] = $data;
            if ($this->lcdController->LCDisOn) {
                $this->lcdController->matchLYC();
                //Get the compare of the first scan line.
            }
        } elseif ($address == 0xff46) {
            $this->memory[0xff46] = $data;
            //DMG cannot DMA from the ROM banks.
            if ($this->cGBC || $data > 0x7f) {
                $data <<= 8;
                $address = 0xfe00;
                while ($address < 0xfea0) {
                    $this->memory[$address++] = $this->memoryRead($data++);
                }
            }
        } elseif ($address == 0xff47) {
            $this->decodePalette(0, $data);
            if ($this->memory[0xff47] != $data) {
                $this->memory[0xff47] = $data;
                $this->invalidateAll(0);
            }
        } elseif ($address == 0xff48) {
            $this->decodePalette(4, $data);
            if ($this->memory[0xff48] != $data) {
                $this->memory[0xff48] = $data;
                $this->invalidateAll(1);
            }
        } elseif ($address == 0xff49) {
            $this->decodePalette(8, $data);
            if ($this->memory[0xff49] != $data) {
                $this->memory[0xff49] = $data;
                $this->invalidateAll(2);
            }
        } elseif ($address == 0xff4d) {
            if ($this->cGBC) {
                $this->memory[0xff4d] = ($data & 0x7f) + ($this->memory[0xff4d] & 0x80);
            } else {
                $this->memory[0xff4d] = $data;
            }
        } elseif ($address == 0xff4f) {
            if ($this->cGBC) {
                $this->currVRAMBank = $data & 0x1;
                //Only writable by GBC.
            }
        } elseif ($address == 0xff50) {
            if ($this->inBootstrap) {
                echo 'Boot ROM reads blocked: Bootstrap process has ended.' . PHP_EOL;
                $this->inBootstrap = false;
                $this->disableBootROM();
                //Fill in the boot ROM ranges with ROM  bank 0 ROM ranges
                $this->memory[0xff50] = $data;
                //Bits are sustained in memory?
            }
        } elseif ($address == 0xff51) {
            if ($this->cGBC) {
                if (!$this->hdmaRunning) {
                    $this->memory[0xff51] = $data;
                }
            }
        } elseif ($address == 0xff52) {
            if ($this->cGBC) {
                if (!$this->hdmaRunning) {
                    $this->memory[0xff52] = $data & 0xf0;
                }
            }
        } elseif ($address == 0xff53) {
            if ($this->cGBC) {
                if (!$this->hdmaRunning) {
                    $this->memory[0xff53] = $data & 0x1f;
                }
            }
        } elseif ($address == 0xff54) {
            if ($this->cGBC) {
                if (!$this->hdmaRunning) {
                    $this->memory[0xff54] = $data & 0xf0;
                }
            }
        } elseif ($address == 0xff55) {
            if ($this->cGBC) {
                if (!$this->hdmaRunning) {
                    if (($data & 0x80) == 0) {
                        //DMA
                        $this->CPUTicks += 1 + 8 * (($data & 0x7f) + 1) * $this->multiplier;
                        $dmaSrc = ($this->memory[0xff51] << 8) + $this->memory[0xff52];
                        $dmaDst = 0x8000 + ($this->memory[0xff53] << 8) + $this->memory[0xff54];
                        $endAmount = ($data & 0x7f) * 0x10 + 0x10;
                        for ($loopAmount = 0; $loopAmount < $endAmount; ++$loopAmount) {
                            $this->memoryWrite($dmaDst++, $this->memoryRead($dmaSrc++));
                        }
                        $this->memory[0xff51] = ($dmaSrc & 0xff00) >> 8;
                        $this->memory[0xff52] = $dmaSrc & 0xf0;
                        $this->memory[0xff53] = ($dmaDst & 0x1f00) >> 8;
                        $this->memory[0xff54] = $dmaDst & 0xf0;
                        $this->memory[0xff55] = 0xff;
                        //Transfer completed.
                    } else {
                        //H-Blank DMA
                        if ($data > 0x80) {
                            $this->hdmaRunning = true;
                            $this->memory[0xff55] = $data & 0x7f;
                        } else {
                            $this->memory[0xff55] = 0xff;
                        }
                    }
                } elseif (($data & 0x80) == 0) {
                    //Stop H-Blank DMA
                    $this->hdmaRunning = false;
                    $this->memory[0xff55] |= 0x80;
                }
            } else {
                $this->memory[0xff55] = $data;
            }
        } elseif ($address == 0xff68) {
            if ($this->cGBC) {
                $this->memory[0xff69] = 0xff & $this->gbcRawPalette[$data & 0x3f];
                $this->memory[0xff68] = $data;
            } else {
                $this->memory[0xff68] = $data;
            }
        } elseif ($address == 0xff69) {
            if ($this->cGBC) {
                $this->setGBCPalette($this->memory[0xff68] & 0x3f, $data);
                // high bit = autoincrement
                if ($this->usbtsb($this->memory[0xff68]) < 0) {
                    $next = $this->usbtsb($this->memory[0xff68]) + 1 & 0x3f;
                    $this->memory[0xff68] = $next | 0x80;
                    $this->memory[0xff69] = 0xff & $this->gbcRawPalette[$next];
                } else {
                    $this->memory[0xff69] = $data;
                }
            } else {
                $this->memory[0xff69] = $data;
            }
        } elseif ($address == 0xff6a) {
            if ($this->cGBC) {
                $this->memory[0xff6b] = 0xff & $this->gbcRawPalette[$data & 0x3f | 0x40];
                $this->memory[0xff6a] = $data;
            } else {
                $this->memory[0xff6a] = $data;
            }
        } elseif ($address == 0xff6b) {
            if ($this->cGBC) {
                $this->setGBCPalette(($this->memory[0xff6a] & 0x3f) + 0x40, $data);
                // high bit = autoincrement
                if ($this->usbtsb($this->memory[0xff6a]) < 0) {
                    $next = $this->memory[0xff6a] + 1 & 0x3f;
                    $this->memory[0xff6a] = $next | 0x80;
                    $this->memory[0xff6b] = 0xff & $this->gbcRawPalette[$next | 0x40];
                } else {
                    $this->memory[0xff6b] = $data;
                }
            } else {
                $this->memory[0xff6b] = $data;
            }
        } elseif ($address == 0xff6c) {
            if ($this->inBootstrap) {
                if ($this->inBootstrap) {
                    $this->cGBC = $data == 0x80;
                    echo 'Booted to GBC Mode: ' . $this->cGBC . PHP_EOL;
                }
                $this->memory[0xff6c] = $data;
            }
        } elseif ($address == 0xff70) {
            if ($this->cGBC) {
                $addressCheck = $this->memory[0xff51] << 8 | $this->memory[0xff52];
                //Cannot change the RAM bank while WRAM is the source of a running HDMA.
                if (!$this->hdmaRunning || $addressCheck < 0xd000 || $addressCheck >= 0xe000) {
                    $this->gbcRamBank = max($data & 0x7, 1);
                    //Bank range is from 1-7
                    $this->gbcRamBankPosition = ($this->gbcRamBank - 1) * 0x1000 - 0xd000;
                    $this->gbcRamBankPositionECHO = ($this->gbcRamBank - 1) * 0x1000 - 0xf000;
                }
                $this->memory[0xff70] = $data | 0x40;
                //Bit 6 cannot be written to.
            } else {
                $this->memory[0xff70] = $data;
            }
        } else {
            //Start the I/O initialization by filling in the slots as normal memory:
            //memoryWriteNormal
            $this->memory[$address] = $data;
        }
    }

Usage Example

 /**
  * Opcode #0xFF.
  *
  * RST 0x38
  *
  * @param Core $core
  */
 public static function opcode255(Core $core)
 {
     $core->stackPointer = $core->unswtuw($core->stackPointer - 1);
     $core->memoryWrite($core->stackPointer, $core->programCounter >> 8);
     $core->stackPointer = $core->unswtuw($core->stackPointer - 1);
     $core->memoryWrite($core->stackPointer, $core->programCounter & 0xff);
     $core->programCounter = 0x38;
 }