diff --git a/src/devices/bus/gameboy/gb_slot.h b/src/devices/bus/gameboy/gb_slot.h index 3f330a4d959..d12fb86f6d5 100644 --- a/src/devices/bus/gameboy/gb_slot.h +++ b/src/devices/bus/gameboy/gb_slot.h @@ -1,5 +1,43 @@ // license:BSD-3-Clause // copyright-holders:Fabio Priuli, Wilbert Pol +/*************************************************************************** + +Cardridge port pinouts: + Pin Name Description + 1 VCC +5 VDC + 2 PHI CPU clock ? + 3 /WR Write + 4 /RD Read + 5 /CS SRAM select + 6 A0 Address 0 + 7 A1 Address 1 + 8 A2 Address 2 + 9 A3 Address 3 + 10 A4 Address 4 + 11 A5 Address 5 + 12 A6 Address 6 + 13 A7 Address 7 + 14 A8 Address 8 + 15 A9 Address 9 + 16 A10 Address 10 + 17 A11 Address 11 + 18 A12 Address 12 + 19 A13 Address 13 + 20 A14 Address 14 + 21 A15 Address 15 + 22 D0 Data 0 + 23 D1 Data 1 + 24 D2 Data 2 + 25 D3 Data 3 + 26 D4 Data 4 + 27 D5 Data 5 + 28 D6 Data 6 + 29 D7 Data 7 + 30 /RST Reset + 31 AUDIOIN Never used ? + 32 GND Ground + +***************************************************************************/ #ifndef MAME_BUS_GAMEBOY_GB_SLOT_H #define MAME_BUS_GAMEBOY_GB_SLOT_H diff --git a/src/devices/bus/gameboy/mbc.cpp b/src/devices/bus/gameboy/mbc.cpp index bb052f9be57..7f32f01cc30 100644 --- a/src/devices/bus/gameboy/mbc.cpp +++ b/src/devices/bus/gameboy/mbc.cpp @@ -5,6 +5,178 @@ Game Boy carts with MBC (Memory Bank Controller) + MBC1 Mapper + =========== + + The MBC1 mapper has two modes: 2MB ROM/8KB RAM or 512KB ROM/32KB RAM. + Initially, the mapper operates in 2MB ROM/8KB RAM mode. + + 0000-1FFF - Writing to this area enables (value 0x0A) or disables (not 0x0A) the + SRAM. + 2000-3FFF - Writing a value 0bXXXBBBBB into the 2000-3FFF memory area selects the + lower 5 bits of the ROM bank to select for the 4000-7FFF memory area. + If a value of 0bXXX00000 is written then this will autmatically be + changed to 0bXXX00001 by the mbc chip. Initial value 00. + 4000-5FFF - Writing a value 0bXXXXXXBB into the 4000-5FFF memory area either selects + the RAM bank to use or bits 6 and 7 for the ROM bank to use for the 4000-7FFF + memory area. This behaviour depends on the memory moddel chosen. + These address lines are fixed in mode 1 and switch depending on A14 in mode 0. + In mode 0 these will drive 0 when RB 00 is accessed (A14 low) or the value set + in 4000-5FFF when RB <> 00 is accessed (A14 high). + Switching between modes does not clear this register. Initial value 00. + 6000-7FFF - Writing a value 0bXXXXXXXB into the 6000-7FFF memory area switches the mode. + B=0 - 2MB ROM/8KB RAM mode + B=1 - 512KB ROM/32KB RAM mode + + Regular ROM aliasing rules apply. + + + MBC2 Mapper + =========== + + The MBC2 mapper includes 512x4bits of builtin RAM. + + 0000-3FFF - Writing to this area enables (value 0bXXXX1010) or disables (any + other value than 0bXXXX1010) the RAM. In order to perform this + function bit 8 of the address must be reset, so usable areas are + 0000-00FF, 0200-02FF, 0400-04FF, 0600-06FF, ..., 3E00-3EFF, + 0000-3FFF - Writing to this area selects the rom bank to appear at 4000-7FFF. + Only bits 3-0 are used to select the bank number. If a value of + 0bXXXX0000 is written then this is automatically changed into + 0bXXXX0001 by the mapper. + In order to perform the rom banking bit 8 of the address must be + set, so usable areas are 0100-01FF, 0300-03FF, 0500-05FF, 0700- + 07FF,..., 3F00-3FFF, + + Regular ROM aliasing rules apply. + + + MBC3 Mapper + =========== + + The MBC3 mapper cartridges can include a RTC chip. + + 0000-1FFF - Writing to this area enables (value 0x0A) or disables (not 0x0A) the + SRAM and RTC registers. + 2000-3FFF - Writing to this area selects the rom bank to appear at 4000-7FFF. + Bits 6-0 are used to select the bank number. If a value of + 0bX0000000 is written then this is autmatically changed into + 0bX0000001 by the mapper. + 4000-5FFF - Writing to this area selects the RAM bank or the RTC register to + read. + XXXX00bb - Select RAM bank bb. + XXXX1rrr - Select RTC register rrr. Accepted values for rrr are: + 000 - Seconds (0x00-0x3B) + 001 - Minutes (0x00-0x3B) + 010 - Hours (0x00-0x17) + 011 - Bits 7-0 of the day counter + 100 - bit 0 - Bit 8 of the day counter + bit 6 - Halt RTC timer ( 0 = timer active, 1 = halted) + bit 7 - Day counter overflow flag + 6000-7FFF - Writing 0x00 followed by 0x01 latches the RTC data. This latching + method is used for reading the RTC registers. + + Regular ROM aliasing rules apply. + + + MBC5 Mapper + =========== + + 0000-1FFF - Writing to this area enables (0x0A) or disables (not 0x0A) the SRAM area. + 2000-2FFF - Writing to this area updates bits 7-0 of the ROM bank number to + appear at 4000-7FFF. + 3000-3FFF - Writing to this area updates bit 8 of the ROM bank number to appear + at 4000-7FFF. + 4000-5FFF - Writing to this area select the RAM bank number to use. If the + cartridge includes a Rumble Pack then bit 3 is used to control + rumble motor (0 - disable motor, 1 - enable motor). + + + MBC7 Mapper (Used by Kirby's Tilt n' Tumble, Command Master) + =========== + + Status: Partial support (only ROM banking supported at the moment) + + The MBC7 mapper has 0x0200(?) bytes of RAM built in. + + 0000-1FFF - Probably enable/disable RAM + In order to use this area bit 12 of the address be set. + Values written: 00, 0A + 2000-2FFF - Writing to this area selects the ROM bank to appear at + 4000-7FFF. + In order to use this area bit 12 of the address be set. + Values written: 01, 07, 01, 1C + 3000-3FFF - Unknown + In order to use this area bit 12 of the address be set. + Values written: 00 + 4000-4FFF - Unknown + In order to use this area bit 12 of the address be set. + Values written: 00, 40, 3F + + + MMM01 mapper + ============ + + Used by: Momotarou Collection 2, Taito Pack + + Status: not supported yet. + + Momotarou Collection 2: + + MOMOTARODENGEKI2, 0x00000, blocks 0x00 - 0x1F + 0x147: 01 04 00 00 33 00 + MOMOTAROU GAIDEN, 0x80000, blocks 0x20 - 0x3F + 0x147: 06 03 00 00 18 00 + + When picking top option: + 3FFF <- 20 + 5FFF <- 40 + 7FFF <- 21 + 1FFF <- 3A + 1FFF <- 7A + + When picking bottom option: + 3FFF <- 00 + 5FFF <- 01 + 7FFF <- 01 + 1FFF <- 3A + 1FFF <- 7A + + Taito Pack (MMM01+RAM, 512KB, 64KB RAM): + 1st option (BUBBLE BOBBLE, blocks 0x10 - 0x17, MBC1+RAM, 128KB, 8KB RAM): + 2000 <- 70 01110000 => starting block, 10000 + 6000 <- 30 00110000 => 8 blocks + 4000 <- 70 01110000 => ??? + 0000 <- 40 01000000 => upper 3 bits determine lower 3 bits of starting block? + + 2nd option (ELEVATOR ACTION, blocks 0x18 - 0x1B, MBC1, 64KB, 2KB RAM): + 2000 <- 78 01111000 => starting block, 11000 + 6000 <- 38 00111000 => 4 blocks + 4000 <- 70 01110000 => ??? + 0000 <- 40 01000000 => upper 3 bits determine lower 3 bits of starting block? + + 3rd option (CHASE HQ, blocks 0x08 - 0x0F, MBC1+RAM, 128KB, 8KB RAM): + 2000 <- 68 01101000 => starting block, 01000 + 6000 <- 30 00110000 => 8 blocks + 4000 <- 70 01110000 => ??? + 0000 <- 40 01000000 => upper 3 bits determine lower 3 bits of starting block? + + 4th option (SAGAIA, blocks 0x00 - 0x07, MBC1+RAM, 128KB, 8KB RAM): + 2000 <- 60 01100000 => starting block, 00000 + 6000 <- 30 00110000 => 8 blocks + 4000 <- 70 01110000 => ??? + 0000 <- 40 01000000 => upper 3 bits determine lower 3 bits of starting block? + + Known: + The last 2 banks in a MMM01 dump are actually the starting banks for a MMM01 image. + + 0000-1FFF => bit6 set => perform mapping + + Possible mapping registers: + 1FFF - Enable RAM ??? + 3FFF - xxxbbbbb - Bit0-5 of the rom bank to select at 0x4000-0x7FFF ? + + TODO: RTC runs too fast while in-game, in MBC-3 games... find the problem! ***********************************************************************************************************/ diff --git a/src/devices/bus/gameboy/rom.cpp b/src/devices/bus/gameboy/rom.cpp index b635a828b8c..060bd7ef3a4 100644 --- a/src/devices/bus/gameboy/rom.cpp +++ b/src/devices/bus/gameboy/rom.cpp @@ -8,6 +8,83 @@ Here we emulate carts with no RAM and simple bankswitch + TAMA5 Mapper (Used by Tamagotchi 3) + ============ + + Status: partially supported. + + The TAMA5 mapper includes a special RTC chip which communicates through the + RAM area (0xA000-0xBFFF); most notably addresses 0xA000 and 0xA001 seem to + be used. In this setup 0xA001 acts like a control register and 0xA000 like + a data register. + + Accepted values by the TAMA5 control register: + 0x00 - Writing to 0xA000 will set bits 3-0 for rom bank selection. + 0x01 - Writing to 0xA000 will set bits (7-?)4 for rom bank selection. + + 0x04 - Bits 3-0 of the value to write + 0x05 - Bits 4-7 of the value to write + 0x06 - Address control hi + bit 0 - Bit 4 for the address + bit 3-1 - 000 - Write a byte to the 32 byte memory. The data to be + written must be set in registers 0x04 (lo nibble) and + 0x05 (hi nibble). + - 001 - Read a byte from the 32 byte memory. The data read + will be available in registers 0x0C (lo nibble) and + 0x0D (hi nibble). + - 010 - Unknown (occurs just after having started a game and + entered a date) (execution at address 1A19) + - 011 - Unknown (not encountered yet) + - 100 - Unknown (occurs during booting a game; appears to be + some kind of read command as it is followed by a read + of the 0x0C register) (execution at address 1B5B) + - 101 - Unknown (not encountered yet) + - 110 - Unknown (not encountered yet) + - 111 - Unknown (not encountered yet) + 0x07 - Address control lo + bit 3-0 - bits 3-0 for the address + + 0x0A - After writing this the lowest 2 bits of A000 determine whether the + TAMA5 chip is ready to accept the next command. If the lowest 2 bits + hold the value 01 then the TAMA5 chip is ready for the next command. + + 0x0C - Reading from A000 will return bits 3-0 of the data + 0x0D - Reading from A000 will return bits 7-4 of the data + + 0x04 - RTC controls? -> RTC/memory? + 0x05 - Write time/memomry? + 0x06 - RTC controls? + 0x07 - RTC controls? + + Unknown sequences: + During booting a game (1B5B: + 04 <- 00, 06 <- 08, 07 <- 01, followed by read 0C + when value read from 0C equals 00 followed by the sequence: + 04 <- 01, 06 <- 08, 07 <- 01, followed by read 0C + the value read from 0C is checked for non-zero, don't know the consequences for either + yet. + + Initialization after starting a game: + At address 1A19: + 06 <- 05, 07 <- 02, followed by read 0C, if != 0F => OK, otherwise do something. + + + Wisdom Tree mapper + ================== + + The Wisdom Tree mapper is triggered by writes in the 0x0000-0x3FFF area. The + address written to determines the bank to switch in in the 0x000-0x7FFF address + space. This mapper uses 32KB sized banks. + + + TODO: + - YongYong mapper: + - During start there are 2 writes to 5000 and 5003, it is still unknown what these do. + - Story of La Sa Ma mapper: + - This should display the Gowin logo on boot on both DMG and CGB (Not implemented yet) + - ATV Racing/Rocket Games mapper: + - How did this overlay the official Nintendo logo at BIOS check time? (Some Sachen titles use a similar trick) + ***********************************************************************************************************/ diff --git a/src/mame/nintendo/gb.cpp b/src/mame/nintendo/gb.cpp index 18d5312c031..2a092f40bc2 100644 --- a/src/mame/nintendo/gb.cpp +++ b/src/mame/nintendo/gb.cpp @@ -12,7 +12,7 @@ Wilbert Pol 2004 (Megaduck/Cougar Boy) TODO list: - - Do correct lcd stat timing + - Do correct LCD stat timing - Add Game Boy Light (Japan, 1997) - does it differ from gbpocket? - SGB should be moved to SNES driver - Emulate OAM corruption bug on 16bit inc/dec in $fe** region @@ -21,236 +21,6 @@ Mappers used in the Game Boy =========================== -MBC1 Mapper -=========== - -The MBC1 mapper has two modes: 2MB ROM/8KB RAM or 512KB ROM/32KB RAM. -Initially, the mapper operates in 2MB ROM/8KB RAM mode. - -0000-1FFF - Writing to this area enables (value 0x0A) or disables (not 0x0A) the - SRAM. -2000-3FFF - Writing a value 0bXXXBBBBB into the 2000-3FFF memory area selects the - lower 5 bits of the ROM bank to select for the 4000-7FFF memory area. - If a value of 0bXXX00000 is written then this will autmatically be - changed to 0bXXX00001 by the mbc chip. Initial value 00. -4000-5FFF - Writing a value 0bXXXXXXBB into the 4000-5FFF memory area either selects - the RAM bank to use or bits 6 and 7 for the ROM bank to use for the 4000-7FFF - memory area. This behaviour depends on the memory moddel chosen. - These address lines are fixed in mode 1 and switch depending on A14 in mode 0. - In mode 0 these will drive 0 when RB 00 is accessed (A14 low) or the value set - in 4000-5FFF when RB <> 00 is accessed (A14 high). - Switching between modes does not clear this register. Initial value 00. -6000-7FFF - Writing a value 0bXXXXXXXB into the 6000-7FFF memory area switches the mode. - B=0 - 2MB ROM/8KB RAM mode - B=1 - 512KB ROM/32KB RAM mode - -Regular ROM aliasing rules apply. - -MBC2 Mapper -=========== - -The MBC2 mapper includes 512x4bits of builtin RAM. - -0000-3FFF - Writing to this area enables (value 0bXXXX1010) or disables (any - other value than 0bXXXX1010) the RAM. In order to perform this - function bit 8 of the address must be reset, so usable areas are - 0000-00FF, 0200-02FF, 0400-04FF, 0600-06FF, ..., 3E00-3EFF, -0000-3FFF - Writing to this area selects the rom bank to appear at 4000-7FFF. - Only bits 3-0 are used to select the bank number. If a value of - 0bXXXX0000 is written then this is automatically changed into - 0bXXXX0001 by the mapper. - In order to perform the rom banking bit 8 of the address must be - set, so usable areas are 0100-01FF, 0300-03FF, 0500-05FF, 0700- - 07FF,..., 3F00-3FFF, - -Regular ROM aliasing rules apply. - -MBC3 Mapper -=========== - -The MBC3 mapper cartridges can include a RTC chip. - -0000-1FFF - Writing to this area enables (value 0x0A) or disables (not 0x0A) the - SRAM and RTC registers. -2000-3FFF - Writing to this area selects the rom bank to appear at 4000-7FFF. - Bits 6-0 are used to select the bank number. If a value of - 0bX0000000 is written then this is autmatically changed into - 0bX0000001 by the mapper. -4000-5FFF - Writing to this area selects the RAM bank or the RTC register to - read. - XXXX00bb - Select RAM bank bb. - XXXX1rrr - Select RTC register rrr. Accepted values for rrr are: - 000 - Seconds (0x00-0x3B) - 001 - Minutes (0x00-0x3B) - 010 - Hours (0x00-0x17) - 011 - Bits 7-0 of the day counter - 100 - bit 0 - Bit 8 of the day counter - bit 6 - Halt RTC timer ( 0 = timer active, 1 = halted) - bit 7 - Day counter overflow flag -6000-7FFF - Writing 0x00 followed by 0x01 latches the RTC data. This latching - method is used for reading the RTC registers. - -Regular ROM aliasing rules apply. - -MBC5 Mapper -=========== - -0000-1FFF - Writing to this area enables (0x0A) or disables (not 0x0A) the SRAM area. -2000-2FFF - Writing to this area updates bits 7-0 of the rom bank number to - appear at 4000-7FFF. -3000-3FFF - Writing to this area updates bit 8 of the rom bank number to appear - at 4000-7FFF. -4000-5FFF - Writing to this area select the RAM bank number to use. If the - cartridge includes a Rumble Pack then bit 3 is used to control - rumble motor (0 - disable motor, 1 - enable motor). - - -MBC7 Mapper (Used by Kirby's Tilt n' Tumble, Command Master) -=========== - -Status: Partial support (only ROM banking supported at the moment) - -The MBC7 mapper has 0x0200(?) bytes of RAM built in. - -0000-1FFF - Probably enable/disable RAM - In order to use this area bit 12 of the address be set. - Values written: 00, 0A -2000-2FFF - Writing to this area selects the ROM bank to appear at - 4000-7FFF. - In order to use this area bit 12 of the address be set. - Values written: 01, 07, 01, 1C -3000-3FFF - Unknown - In order to use this area bit 12 of the address be set. - Values written: 00 -4000-4FFF - Unknown - In order to use this area bit 12 of the address be set. - Values written: 00, 40, 3F - - -TAMA5 Mapper (Used by Tamagotchi 3) -============ - -Status: partially supported. - -The TAMA5 mapper includes a special RTC chip which communicates through the -RAM area (0xA000-0xBFFF); most notably addresses 0xA000 and 0xA001 seem to -be used. In this setup 0xA001 acts like a control register and 0xA000 like -a data register. - -Accepted values by the TAMA5 control register: -0x00 - Writing to 0xA000 will set bits 3-0 for rom bank selection. -0x01 - Writing to 0xA000 will set bits (7-?)4 for rom bank selection. - -0x04 - Bits 3-0 of the value to write -0x05 - Bits 4-7 of the value to write -0x06 - Address control hi - bit 0 - Bit 4 for the address - bit 3-1 - 000 - Write a byte to the 32 byte memory. The data to be - written must be set in registers 0x04 (lo nibble) and - 0x05 (hi nibble). - - 001 - Read a byte from the 32 byte memory. The data read - will be available in registers 0x0C (lo nibble) and - 0x0D (hi nibble). - - 010 - Unknown (occurs just after having started a game and - entered a date) (execution at address 1A19) - - 011 - Unknown (not encountered yet) - - 100 - Unknown (occurs during booting a game; appears to be - some kind of read command as it is followed by a read - of the 0x0C register) (execution at address 1B5B) - - 101 - Unknown (not encountered yet) - - 110 - Unknown (not encountered yet) - - 111 - Unknown (not encountered yet) -0x07 - Address control lo - bit 3-0 - bits 3-0 for the address - -0x0A - After writing this the lowest 2 bits of A000 determine whether the - TAMA5 chip is ready to accept the next command. If the lowest 2 bits - hold the value 01 then the TAMA5 chip is ready for the next command. - -0x0C - Reading from A000 will return bits 3-0 of the data -0x0D - Reading from A000 will return bits 7-4 of the data - -0x04 - RTC controls? -> RTC/memory? -0x05 - Write time/memomry? -0x06 - RTC controls? -0x07 - RTC controls? - -Unknown sequences: -During booting a game (1B5B: -04 <- 00, 06 <- 08, 07 <- 01, followed by read 0C -when value read from 0C equals 00 followed by the sequence: -04 <- 01, 06 <- 08, 07 <- 01, followed by read 0C -the value read from 0C is checked for non-zero, don't know the consequences for either -yet. - -Initialization after starting a game: -At address 1A19: -06 <- 05, 07 <- 02, followed by read 0C, if != 0F => OK, otherwise do something. - - -MMM01 mapper -============ - -Used by: Momotarou Collection 2, Taito Pack - -Status: not supported yet. - -Momotarou Collection 2: - -MOMOTARODENGEKI2, 0x00000, blocks 0x00 - 0x1F -0x147: 01 04 00 00 33 00 -MOMOTAROU GAIDEN, 0x80000, blocks 0x20 - 0x3F -0x147: 06 03 00 00 18 00 - -When picking top option: -3FFF <- 20 -5FFF <- 40 -7FFF <- 21 -1FFF <- 3A -1FFF <- 7A - -When picking bottom option: -3FFF <- 00 -5FFF <- 01 -7FFF <- 01 -1FFF <- 3A -1FFF <- 7A - -Taito Pack (MMM01+RAM, 512KB, 64KB RAM): -1st option (BUBBLE BOBBLE, blocks 0x10 - 0x17, MBC1+RAM, 128KB, 8KB RAM): - 2000 <- 70 01110000 => starting block, 10000 - 6000 <- 30 00110000 => 8 blocks - 4000 <- 70 01110000 => ??? - 0000 <- 40 01000000 => upper 3 bits determine lower 3 bits of starting block? - -2nd option (ELEVATOR ACTION, blocks 0x18 - 0x1B, MBC1, 64KB, 2KB RAM): - 2000 <- 78 01111000 => starting block, 11000 - 6000 <- 38 00111000 => 4 blocks - 4000 <- 70 01110000 => ??? - 0000 <- 40 01000000 => upper 3 bits determine lower 3 bits of starting block? - -3rd option (CHASE HQ, blocks 0x08 - 0x0F, MBC1+RAM, 128KB, 8KB RAM): - 2000 <- 68 01101000 => starting block, 01000 - 6000 <- 30 00110000 => 8 blocks - 4000 <- 70 01110000 => ??? - 0000 <- 40 01000000 => upper 3 bits determine lower 3 bits of starting block? - -4th option (SAGAIA, blocks 0x00 - 0x07, MBC1+RAM, 128KB, 8KB RAM): - 2000 <- 60 01100000 => starting block, 00000 - 6000 <- 30 00110000 => 8 blocks - 4000 <- 70 01110000 => ??? - 0000 <- 40 01000000 => upper 3 bits determine lower 3 bits of starting block? - -Known: -The last 2 banks in a MMM01 dump are actually the starting banks for a MMM01 image. - -0000-1FFF => bit6 set => perform mapping - -Possible mapping registers: -1FFF - Enable RAM ??? -3FFF - xxxbbbbb - Bit0-5 of the rom bank to select at 0x4000-0x7FFF ? - - HuC1 mapper =========== @@ -262,44 +32,883 @@ HuC3 mapper Status: not supported yet. - -Wisdom Tree mapper -================== - -The Wisdom Tree mapper is triggered by writes in the 0x0000-0x3FFF area. The -address written to determines the bank to switch in in the 0x000-0x7FFF address -space. This mapper uses 32KB sized banks. - - ***************************************************************************/ #include "emu.h" -#include "gb.h" -#include "bus/gameboy/rom.h" + +#include "bus/gameboy/gb_slot.h" #include "bus/gameboy/mbc.h" +#include "bus/gameboy/rom.h" +#include "cpu/lr35902/lr35902.h" +#include "machine/ram.h" +#include "sound/gb.h" +#include "video/gb_lcd.h" + +#include "emupal.h" #include "screen.h" #include "softlist_dev.h" #include "speaker.h" +//#define VERBOSE 1 +#include "logmacro.h" + + +namespace { + #define DMG_FRAMES_PER_SECOND 59.732155 #define SGB_FRAMES_PER_SECOND 61.17 +class gb_state : public driver_device +{ +public: + gb_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device(mconfig, type, tag), + m_cartslot(*this, "cartslot"), + m_maincpu(*this, "maincpu"), + m_apu(*this, "apu"), + m_region_maincpu(*this, "maincpu"), + m_inputs(*this, "INPUTS"), + m_bios_hack(*this, "SKIP_CHECK"), + m_ppu(*this, "ppu"), + m_palette(*this, "palette"), + m_cart_low(*this, "cartlow"), + m_cart_high(*this, "carthigh") + { } + + void gbpocket(machine_config &config); + void gameboy(machine_config &config); + +protected: + enum { + SIO_ENABLED = 0x80, + SIO_FAST_CLOCK = 0x02, + SIO_INTERNAL_CLOCK = 0x01 + }; + static constexpr u8 NO_CART = 0x00; + static constexpr u8 BIOS_ENABLED = 0x00; + static constexpr u8 CART_PRESENT = 0x01; + static constexpr u8 BIOS_DISABLED = 0x02; + + virtual void machine_start() override; + virtual void machine_reset() override; + + void gb_io_w(offs_t offset, uint8_t data); + void gb_io2_w(offs_t offset, uint8_t data); + uint8_t gb_ie_r(); + void gb_ie_w(uint8_t data); + uint8_t gb_io_r(offs_t offset); + + uint8_t gb_bios_r(offs_t offset); + + void gb_timer_callback(uint8_t data); + + void gb_init_regs(); + void gb_init(); + + uint8_t m_gb_io[0x10]{}; + + /* Timer related */ + uint16_t m_divcount = 0; + uint8_t m_shift = 0; + uint16_t m_shift_cycles = 0; + uint8_t m_triggering_irq = 0; + uint8_t m_reloading = 0; + + /* Serial I/O related */ + uint16_t m_internal_serial_clock = 0; + uint16_t m_internal_serial_frequency = 0; + uint32_t m_sio_count = 0; /* Serial I/O counter */ + + required_device m_cartslot; + + required_device m_maincpu; + required_device m_apu; + required_region_ptr m_region_maincpu; + required_ioport m_inputs; + required_ioport m_bios_hack; + required_device m_ppu; + required_device m_palette; + memory_view m_cart_low; + memory_view m_cart_high; + +private: + void gb_palette(palette_device &palette) const; + void gbp_palette(palette_device &palette) const; + + void gb_timer_increment(); + void gb_timer_check_irq(); + void gb_serial_timer_tick(); + + void save_gb_base(); + + void gameboy_map(address_map &map); +}; + + +class sgb_state : public gb_state +{ +public: + sgb_state(const machine_config &mconfig, device_type type, const char *tag) : + gb_state(mconfig, type, tag) + { } + + void supergb(machine_config &config); + void supergb2(machine_config &config); + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + +private: + void sgb_palette(palette_device &palette) const; + + void sgb_io_w(offs_t offset, uint8_t data); + + void sgb_map(address_map &map); + + int8_t m_sgb_packets = 0; + uint8_t m_sgb_bitcount = 0; + uint8_t m_sgb_bytecount = 0; + uint8_t m_sgb_start = 0; + uint8_t m_sgb_rest = 0; + uint8_t m_sgb_controller_no = 0; + uint8_t m_sgb_controller_mode = 0; + uint8_t m_sgb_data[0x100]{}; +}; + + +class gbc_state : public gb_state +{ +public: + gbc_state(const machine_config &mconfig, device_type type, const char *tag) : + gb_state(mconfig, type, tag), + m_rambank(*this, "cgb_ram"), + m_bankedram(*this, "banked_ram", 7 * 0x1000, ENDIANNESS_LITTLE) + { } + + void gbcolor(machine_config &config); + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + +private: + void gbc_io_w(offs_t offset, uint8_t data); + void gbc_io2_w(offs_t offset, uint8_t data); + uint8_t gbc_io2_r(offs_t offset); + + void gbc_map(address_map &map); + + required_memory_bank m_rambank; + memory_share_creator m_bankedram; +}; + + +class megaduck_state : public gb_state +{ +public: + megaduck_state(const machine_config &mconfig, device_type type, const char *tag) : + gb_state(mconfig, type, tag) + { } + + void megaduck(machine_config &config); + +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + +private: + uint8_t megaduck_video_r(offs_t offset); + void megaduck_video_w(offs_t offset, uint8_t data); + void megaduck_sound_w1(offs_t offset, uint8_t data); + uint8_t megaduck_sound_r1(offs_t offset); + void megaduck_sound_w2(offs_t offset, uint8_t data); + uint8_t megaduck_sound_r2(offs_t offset); + void megaduck_palette(palette_device &palette) const; + void megaduck_map(address_map &map); +}; + + + +/* RAM layout defines */ + +#define JOYPAD m_gb_io[0x00] // Joystick: 1.1.P15.P14.P13.P12.P11.P10 +#define SIODATA m_gb_io[0x01] // Serial IO data buffer +#define SIOCONT m_gb_io[0x02] // Serial IO control register +#define TIMECNT m_gb_io[0x05] // Timer counter. Gen. int. when it overflows +#define TIMEMOD m_gb_io[0x06] // New value of TimeCount after it overflows +#define TIMEFRQ m_gb_io[0x07] // Timer frequency and start/stop switch + + +//------------------------- +// handle save state +//------------------------- + +void gb_state::save_gb_base() +{ + save_item(NAME(m_gb_io)); + save_item(NAME(m_divcount)); + save_item(NAME(m_shift)); + save_item(NAME(m_shift_cycles)); + save_item(NAME(m_triggering_irq)); + save_item(NAME(m_reloading)); + save_item(NAME(m_sio_count)); + + m_cartslot->save_ram(); +} + + +void gb_state::gb_init_regs() +{ + /* Initialize the registers */ + SIODATA = 0x00; + SIOCONT = 0x7E; + + gb_io_w(0x05, 0x00); /* TIMECNT */ + gb_io_w(0x06, 0x00); /* TIMEMOD */ +} + + +void gb_state::gb_init() +{ + m_apu->sound_w(0x16, 0x00); /* Initialize sound hardware */ + + m_divcount = 8; + m_internal_serial_clock = 0; + m_internal_serial_frequency = 512 / 2; + m_triggering_irq = 0; + m_shift = 10; // slowest timer? + m_shift_cycles = 1 << m_shift; + + /* Set registers to default/startup values */ + m_gb_io[0x00] = 0xCF; + m_gb_io[0x01] = 0x00; + m_gb_io[0x02] = 0x7E; + m_gb_io[0x03] = 0xFF; + m_gb_io[0x07] = 0xF8; /* Upper bits of TIMEFRQ register are set to 1 */ +} + + +void gb_state::machine_start() +{ + save_gb_base(); +} + +void gbc_state::machine_start() +{ + gb_state::machine_start(); + + m_rambank->configure_entry(0, &m_bankedram[0]); + m_rambank->configure_entries(1, 7, &m_bankedram[0], 0x1000); +} + + +void sgb_state::machine_start() +{ + gb_state::machine_start(); + + m_sgb_packets = -1; + + save_item(NAME(m_sgb_packets)); + save_item(NAME(m_sgb_bitcount)); + save_item(NAME(m_sgb_bytecount)); + save_item(NAME(m_sgb_start)); + save_item(NAME(m_sgb_rest)); + save_item(NAME(m_sgb_controller_no)); + save_item(NAME(m_sgb_controller_mode)); + save_item(NAME(m_sgb_data)); +} + +void gb_state::machine_reset() +{ + gb_init(); + + m_cart_low.select(BIOS_ENABLED | (m_cartslot ? CART_PRESENT : NO_CART)); + m_cart_high.select(m_cartslot ? CART_PRESENT : NO_CART); +} + +void gbc_state::machine_reset() +{ + gb_state::machine_reset(); + + gb_init_regs(); + + std::fill_n(&m_bankedram[0], m_bankedram.length(), 0); +} + +void sgb_state::machine_reset() +{ + gb_state::machine_reset(); + + gb_init_regs(); +} + + +void gb_state::gb_io_w(offs_t offset, uint8_t data) +{ + static const uint8_t timer_shifts[4] = {10, 4, 6, 8}; + + switch (offset) + { + case 0x00: /* JOYP - Joypad */ + JOYPAD = 0xCF | data; + if (!(data & 0x20)) + JOYPAD &= (m_inputs->read() >> 4) | 0xF0; + if (!(data & 0x10)) + JOYPAD &= m_inputs->read() | 0xF0; + return; + case 0x01: /* SB - Serial transfer data */ + break; + case 0x02: /* SC - SIO control */ + switch (data & 0x81) + { + case 0x00: + case 0x01: + m_sio_count = 0; + break; + case 0x80: /* enabled & external clock */ + m_sio_count = 16; + break; + case 0x81: /* enabled & internal clock */ + m_sio_count = 16; + break; + } +logerror("SIOCONT write, serial clock is %04x\n", m_internal_serial_clock); + data |= 0x7E; // unused bits stay high + break; + case 0x03: + return; + case 0x04: /* DIV - Divider register */ + /* Force increment of TIMECNT register when the 'highest' bit is set */ + if ((m_divcount >> (m_shift - 1)) & 1) + { + gb_timer_increment(); + } + LOG("DIV write\n"); + m_divcount = 0; + return; + case 0x05: /* TIMA - Timer counter */ + /* Check if the counter is being reloaded in this cycle */ + if ((TIMEFRQ & 0x04) && TIMECNT == TIMEMOD && (m_divcount & (m_shift_cycles - 1)) == 4) + { + data = TIMEMOD; + } + break; + case 0x06: /* TMA - Timer module */ + /* Check if the counter is being reloaded in this cycle */ + if ((TIMEFRQ & 0x04) && TIMECNT == TIMEMOD && (m_divcount & (m_shift_cycles - 1)) == 4) + { + TIMECNT = data; + } + break; + case 0x07: /* TAC - Timer control */ + data |= 0xF8; + /* Check if timer is just disabled or the timer frequency is changing */ + if ((!(data & 0x04) && (TIMEFRQ & 0x04)) || ((data & 0x04) && (TIMEFRQ & 0x04) && (data & 0x03) != (TIMEFRQ & 0x03))) + { + /* Check if TIMECNT should be incremented */ + if ((m_divcount & (m_shift_cycles - 1)) >= (m_shift_cycles >> 1)) + { + gb_timer_increment(); + } + } + m_shift = timer_shifts[data & 0x03]; + m_shift_cycles = 1 << m_shift; + break; + case 0x0F: /* IF - Interrupt flag */ + m_ppu->update_state(); + LOG("write if\n"); + data &= 0x1F; + m_maincpu->set_if(data); + break; + } + + m_gb_io[offset] = data; +} + +void gb_state::gb_io2_w(offs_t offset, uint8_t data) +{ + if (offset == 0x10) + { + /* disable BIOS ROM */ + m_cart_low.select(BIOS_DISABLED | (m_cartslot ? CART_PRESENT : NO_CART)); + } + else + m_ppu->video_w(offset, data); +} + +#ifdef MAME_DEBUG +static const char *const sgbcmds[32] = +{ + /* 0x00 */ "PAL01 ", + /* 0x01 */ "PAL23 ", + /* 0x02 */ "PAL03 ", + /* 0x03 */ "PAL12 ", + /* 0x04 */ "ATTR_BLK", + /* 0x05 */ "ATTR_LIN", + /* 0x06 */ "ATTR_DIV", + /* 0x07 */ "ATTR_CHR", + /* 0x08 */ "SOUND ", + /* 0x09 */ "SOU_TRN ", + /* 0x0A */ "PAL_SET ", + /* 0x0B */ "PAL_TRN ", + /* 0x0C */ "ATRC_EN ", + /* 0x0D */ "TEST_EN ", + /* 0x0E */ "ICON_EN ", + /* 0x0F */ "DATA_SND", + /* 0x10 */ "DATA_TRN", + /* 0x11 */ "MLT_REG ", + /* 0x12 */ "JUMP ", + /* 0x13 */ "CHR_TRN ", + /* 0x14 */ "PCT_TRN ", + /* 0x15 */ "ATTR_TRN", + /* 0x16 */ "ATTR_SET", + /* 0x17 */ "MASK_EN ", + /* 0x18 */ "OBJ_TRN ", + /* 0x19 */ "PAL_PRI ", + /* 0x1A */ "????????", + /* 0x1B */ "????????", + /* 0x1C */ "????????", + /* 0x1D */ "????????", + /* 0x1E */ "????????", + /* 0x1F */ "????????" +}; +#endif + +void sgb_state::sgb_io_w(offs_t offset, uint8_t data) +{ + switch (offset) + { + case 0x00: + switch (data & 0x30) + { + case 0x00: /* start condition */ + if (m_sgb_start) + logerror("SGB: Start condition before end of transfer ??\n"); + m_sgb_bitcount = 0; + m_sgb_start = 1; + m_sgb_rest = 0; + JOYPAD = 0x0F & ((m_inputs->read() >> 4) | m_inputs->read() | 0xF0); + break; + case 0x10: /* data true */ + if (m_sgb_rest) + { + /* We should test for this case , but the code below won't + work with the current setup */ +#if 0 + if (m_sgb_bytecount == 16) + { + logerror("SGB: end of block is not zero!"); + m_sgb_start = 0; + } +#endif + m_sgb_data[m_sgb_bytecount] >>= 1; + m_sgb_data[m_sgb_bytecount] |= 0x80; + m_sgb_bitcount++; + if (m_sgb_bitcount == 8) + { + m_sgb_bitcount = 0; + m_sgb_bytecount++; + } + m_sgb_rest = 0; + } + JOYPAD = 0x1F & ((m_inputs->read() >> 4) | 0xF0); + break; + case 0x20: /* data false */ + if (m_sgb_rest) + { + if (m_sgb_bytecount == 16 && m_sgb_packets == -1) + { +#ifdef MAME_DEBUG + LOG("SGB: %s (%02X) pkts: %d data: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + sgbcmds[m_sgb_data[0] >> 3],m_sgb_data[0] >> 3, m_sgb_data[0] & 0x07, m_sgb_data[1], m_sgb_data[2], m_sgb_data[3], + m_sgb_data[4], m_sgb_data[5], m_sgb_data[6], m_sgb_data[7], + m_sgb_data[8], m_sgb_data[9], m_sgb_data[10], m_sgb_data[11], + m_sgb_data[12], m_sgb_data[13], m_sgb_data[14], m_sgb_data[15]); +#endif + m_sgb_packets = m_sgb_data[0] & 0x07; + m_sgb_start = 0; + } + if (m_sgb_bytecount == (m_sgb_packets << 4)) + { + switch (m_sgb_data[0] >> 3) + { + case 0x11: /* MLT_REQ - Multi controller request */ + if (m_sgb_data[1] == 0x00) + m_sgb_controller_mode = 0; + else if (m_sgb_data[1] == 0x01) + m_sgb_controller_mode = 2; + break; + default: + dynamic_cast(m_ppu.target())->sgb_io_write_pal(m_sgb_data[0] >> 3, &m_sgb_data[0]); + break; + } + m_sgb_start = 0; + m_sgb_bytecount = 0; + m_sgb_packets = -1; + } + if (m_sgb_start) + { + m_sgb_data[m_sgb_bytecount] >>= 1; + m_sgb_bitcount++; + if (m_sgb_bitcount == 8) + { + m_sgb_bitcount = 0; + m_sgb_bytecount++; + } + } + m_sgb_rest = 0; + } + JOYPAD = 0x2F & (m_inputs->read() | 0xF0); + break; + case 0x30: /* rest condition */ + if (m_sgb_start) + m_sgb_rest = 1; + if (m_sgb_controller_mode) + { + m_sgb_controller_no++; + if (m_sgb_controller_no == m_sgb_controller_mode) + m_sgb_controller_no = 0; + JOYPAD = 0x3F - m_sgb_controller_no; + } + else + JOYPAD = 0x3F; + + /* Hack to let cartridge know it's running on an SGB */ + if ((m_sgb_data[0] >> 3) == 0x1F) + JOYPAD = 0x3E; + break; + } + return; + default: + /* we didn't handle the write, so pass it to the GB handler */ + gb_io_w(offset, data); + return; + } + + m_gb_io[offset] = data; +} + +/* Interrupt Enable register */ +uint8_t gb_state::gb_ie_r() +{ + return m_maincpu->get_ie(); +} + +void gb_state::gb_ie_w(uint8_t data) +{ + m_maincpu->set_ie(data); +} + +/* IO read */ +uint8_t gb_state::gb_io_r(offs_t offset) +{ + switch(offset) + { + case 0x04: + LOG("read DIV, divcount = %04x\n", m_divcount); + return (m_divcount >> 8) & 0xFF; + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x05: + case 0x06: + case 0x07: + return m_gb_io[offset]; + case 0x0F: + /* Make sure the internal states are up to date */ + m_ppu->update_state(); + LOG("read if\n"); +logerror("IF read, serial clock is %04x\n", m_internal_serial_clock); + return 0xE0 | m_maincpu->get_if(); + default: + /* Unsupported registers return 0xFF */ + return 0xFF; + } +} + + +/* Called when 512 internal cycles are passed */ +void gb_state::gb_serial_timer_tick() +{ + if (SIOCONT & SIO_ENABLED) + { + if (m_sio_count & 1) + { + /* Shift in a received bit */ + SIODATA = (SIODATA << 1) | 0x01; + } + /* Decrement number of handled bits */ + m_sio_count--; + + LOG("%04x - gb_serial_timer_proc: SIODATA = %02x, sio_count = %u\n", m_maincpu->pc(), SIODATA, m_sio_count); + /* If all bits done, stop timer and trigger interrupt */ + if (m_sio_count == 0) + { + SIOCONT &= ~SIO_ENABLED; + m_maincpu->set_input_line(lr35902_cpu_device::SIO_INT, ASSERT_LINE); + // Make sure the state is updated during the current timeslice in case it is read. + m_maincpu->execute_set_input(lr35902_cpu_device::SIO_INT, ASSERT_LINE); + } + } +} + + +void gb_state::gb_timer_check_irq() +{ + m_reloading = 0; + if (m_triggering_irq) + { + m_triggering_irq = 0; + if (TIMECNT == 0) + { + TIMECNT = TIMEMOD; + m_maincpu->set_input_line(lr35902_cpu_device::TIM_INT, ASSERT_LINE); + // Make sure the state is updated during the current timeslice in case it is read. + m_maincpu->execute_set_input(lr35902_cpu_device::TIM_INT, ASSERT_LINE); + m_reloading = 1; + } + } +} + +void gb_state::gb_timer_increment() +{ + gb_timer_check_irq(); + + LOG("increment timer\n"); + TIMECNT += 1; + if (TIMECNT == 0) + { + m_triggering_irq = 1; + } +} + +// This gets called while the cpu is executing instructions to keep the timer state in sync +void gb_state::gb_timer_callback(uint8_t data) +{ + uint16_t old_gb_divcount = m_divcount; + uint16_t old_internal_serial_clock = m_internal_serial_clock; + m_divcount += data; + m_internal_serial_clock += data; + + if ((old_gb_divcount >> 8) != (m_divcount >> 8)) + { + //LOG("DIV became %02x\n", m_divcount >> 8); + } + gb_timer_check_irq(); + + if (TIMEFRQ & 0x04) + { + uint16_t old_count = old_gb_divcount >> m_shift; + uint16_t new_count = m_divcount >> m_shift; + if (data > m_shift_cycles) + { + gb_timer_increment(); + old_count++; + } + if (new_count != old_count) + { + gb_timer_increment(); + if (new_count << m_shift < m_divcount) + { + gb_timer_check_irq(); + } + } + } + + if (((m_internal_serial_clock ^ old_internal_serial_clock) & m_internal_serial_frequency) && (SIOCONT & SIO_INTERNAL_CLOCK)) + { + gb_serial_timer_tick(); + } +} + + +void gbc_state::gbc_io_w(offs_t offset, uint8_t data) +{ + gb_io_w(offset, data); + + // On CGB the internal serial transfer clock is selectable + if (offset == 0x02) + { + m_internal_serial_frequency = ((data & SIO_FAST_CLOCK) ? 16 : 512) / 2; + SIOCONT = (SIOCONT & ~SIO_FAST_CLOCK) | (data & SIO_FAST_CLOCK); + } +} + + +void gbc_state::gbc_io2_w(offs_t offset, uint8_t data) +{ + switch (offset) + { + case 0x0D: // KEY1 - Prepare speed switch + m_maincpu->set_speed(data); + return; + case 0x10: // BFF - BIOS disable + m_cart_low.select(BIOS_DISABLED | (m_cartslot ? CART_PRESENT : NO_CART)); + return; + case 0x16: // RP - Infrared port + break; + case 0x30: // SVBK - RAM bank select + m_rambank->set_entry(data & 0x07); + break; + default: + break; + } + m_ppu->video_w(offset, data); +} + +uint8_t gbc_state::gbc_io2_r(offs_t offset) +{ + switch (offset) + { + case 0x0D: // KEY1 + return m_maincpu->get_speed(); + case 0x16: // RP - Infrared port + break; + case 0x30: // SVBK - RAM bank select + return m_rambank->entry(); + default: + break; + } + return m_ppu->video_r(offset); +} + +/**************************************************************************** + + Megaduck routines + + ****************************************************************************/ + +void megaduck_state::machine_start() +{ + gb_state::machine_start(); +} + +void megaduck_state::machine_reset() +{ + gb_init(); + + m_cart_low.select((m_cartslot ? CART_PRESENT : NO_CART)); + m_cart_high.select(m_cartslot ? CART_PRESENT : NO_CART); +} + +/* + Map megaduck video related area on to regular Game Boy video area + + Different locations of the video registers: + Register Game Boy MegaDuck + LCDC FF40 FF10 (See different bit order below) + STAT FF41 FF11 + SCY FF42 FF12 + SCX FF43 FF13 + LY FF44 FF18 + LYC FF45 FF19 + DMA FF46 FF1A + BGP FF47 FF1B + OBP0 FF48 FF14 + OBP1 FF49 FF15 + WY FF4A FF16 + WX FF4B FF17 + Unused FF4C FF4C (?) + Unused FF4D FF4D (?) + Unused FF4E FF4E (?) + Unused FF4F FF4F (?) + + Different LCDC register + + Game Boy MegaDuck + 0 6 - BG & Window Display : 0 - Off, 1 - On + 1 0 - OBJ Display: 0 - Off, 1 - On + 2 1 - OBJ Size: 0 - 8x8, 1 - 8x16 + 3 2 - BG Tile Map Display: 0 - 9800, 1 - 9C00 + 4 4 - BG & Window Tile Data Select: 0 - 8800, 1 - 8000 + 5 5 - Window Display: 0 - Off, 1 - On + 6 3 - Window Tile Map Display Select: 0 - 9800, 1 - 9C00 + 7 7 - LCD Operation + + **************/ + +uint8_t megaduck_state::megaduck_video_r(offs_t offset) +{ + uint8_t data; + + if ((offset & 0x0C) && ((offset & 0x0C) ^ 0x0C)) + { + offset ^= 0x0C; + } + data = m_ppu->video_r(offset); + if (offset) + return data; + return bitswap<8>(data,7,0,5,4,6,3,2,1); +} + +void megaduck_state::megaduck_video_w(offs_t offset, uint8_t data) +{ + if (!offset) + { + data = bitswap<8>(data,7,3,5,4,2,1,0,6); + } + if ((offset & 0x0C) && ((offset & 0x0C) ^ 0x0C)) + { + offset ^= 0x0C; + } + m_ppu->video_w(offset, data); +} + +// Map megaduck audio offset to game boy audio offsets +// Envelope and LFSR register nibbles are reversed relative to the game boy + +static const uint8_t megaduck_sound_offsets[16] = { 0, 2, 1, 3, 4, 6, 5, 7, 8, 9, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; + +void megaduck_state::megaduck_sound_w1(offs_t offset, uint8_t data) +{ + if ((offset == 0x01) || (offset == 0x07)) + m_apu->sound_w(megaduck_sound_offsets[offset], ((data & 0x0f)<<4) | ((data & 0xf0)>>4)); + else + m_apu->sound_w(megaduck_sound_offsets[offset], data); +} + +uint8_t megaduck_state::megaduck_sound_r1(offs_t offset) +{ + uint8_t data = m_apu->sound_r(megaduck_sound_offsets[offset]); + if ((offset == 0x01) || (offset == 0x07)) + return ((data & 0x0f)<<4) | ((data & 0xf0)>>4); + else + return data; +} + +void megaduck_state::megaduck_sound_w2(offs_t offset, uint8_t data) +{ + if ((offset == 0x01) || (offset == 0x02)) + m_apu->sound_w(0x10 + megaduck_sound_offsets[offset], ((data & 0x0f)<<4) | ((data & 0xf0)>>4)); + else + m_apu->sound_w(0x10 + megaduck_sound_offsets[offset], data); +} + +uint8_t megaduck_state::megaduck_sound_r2(offs_t offset) +{ + uint8_t data = m_apu->sound_r(0x10 + megaduck_sound_offsets[offset]); + if ((offset == 0x01) || (offset == 0x02)) + return ((data & 0x0f)<<4) | ((data & 0xf0)>>4); + else + return data; +} + uint8_t gb_state::gb_bios_r(offs_t offset) { - uint8_t *ROM = m_region_maincpu->base(); if (m_bios_hack->read()) { // patch out logo and checksum checks - // (useful to run some pirate carts until we implement - // their complete functionalities + to test homebrew) + // useful to run some pirate carts until we implement their complete functionalities + to test homebrew if (offset == 0xe9 || offset == 0xea) return 0x00; if (offset == 0xfa || offset == 0xfb) return 0x00; } - return ROM[offset]; + return m_region_maincpu[offset]; } @@ -328,14 +937,14 @@ void gb_state::gameboy_map(address_map &map) map(0xffff, 0xffff).rw(FUNC(gb_state::gb_ie_r), FUNC(gb_state::gb_ie_w)); } -void gb_state::sgb_map(address_map &map) +void sgb_state::sgb_map(address_map &map) { map.unmap_value_high(); map(0x0000, 0x7fff).view(m_cart_low); m_cart_low[BIOS_ENABLED | NO_CART](0x0000, 0x7fff).noprw(); - m_cart_low[BIOS_ENABLED | NO_CART](0x0000, 0x00ff).r(FUNC(gb_state::gb_bios_r)); + m_cart_low[BIOS_ENABLED | NO_CART](0x0000, 0x00ff).r(FUNC(sgb_state::gb_bios_r)); m_cart_low[BIOS_ENABLED | CART_PRESENT](0x0000, 0x7fff).rw(m_cartslot, FUNC(gb_cart_slot_device::read_rom), FUNC(gb_cart_slot_device::write_bank)); - m_cart_low[BIOS_ENABLED | CART_PRESENT](0x0000, 0x00ff).r(FUNC(gb_state::gb_bios_r)); + m_cart_low[BIOS_ENABLED | CART_PRESENT](0x0000, 0x00ff).r(FUNC(sgb_state::gb_bios_r)); m_cart_low[BIOS_DISABLED | NO_CART](0x0000, 0x7fff).noprw(); m_cart_low[BIOS_DISABLED | CART_PRESENT](0x0000, 0x7fff).rw(m_cartslot, FUNC(gb_cart_slot_device::read_rom), FUNC(gb_cart_slot_device::write_bank)); map(0x8000, 0x9fff).rw(m_ppu, FUNC(sgb_ppu_device::vram_r), FUNC(sgb_ppu_device::vram_w)); @@ -344,24 +953,24 @@ void gb_state::sgb_map(address_map &map) m_cart_high[CART_PRESENT](0xa000, 0xbfff).rw(m_cartslot, FUNC(gb_cart_slot_device::read_ram), FUNC(gb_cart_slot_device::write_ram)); map(0xc000, 0xdfff).mirror(0x2000).ram(); map(0xfe00, 0xfeff).rw(m_ppu, FUNC(sgb_ppu_device::oam_r), FUNC(sgb_ppu_device::oam_w)); - map(0xff00, 0xff0f).rw(FUNC(gb_state::gb_io_r), FUNC(gb_state::sgb_io_w)); + map(0xff00, 0xff0f).rw(FUNC(sgb_state::gb_io_r), FUNC(sgb_state::sgb_io_w)); map(0xff10, 0xff26).rw(m_apu, FUNC(gameboy_sound_device::sound_r), FUNC(gameboy_sound_device::sound_w)); map(0xff27, 0xff2f).noprw(); map(0xff30, 0xff3f).rw(m_apu, FUNC(gameboy_sound_device::wave_r), FUNC(gameboy_sound_device::wave_w)); - map(0xff40, 0xff7f).r(m_ppu, FUNC(sgb_ppu_device::video_r)).w(FUNC(gb_state::gb_io2_w)); + map(0xff40, 0xff7f).r(m_ppu, FUNC(sgb_ppu_device::video_r)).w(FUNC(sgb_state::gb_io2_w)); map(0xff80, 0xfffe).ram(); - map(0xffff, 0xffff).rw(FUNC(gb_state::gb_ie_r), FUNC(gb_state::gb_ie_w)); + map(0xffff, 0xffff).rw(FUNC(sgb_state::gb_ie_r), FUNC(sgb_state::gb_ie_w)); } -void gb_state::gbc_map(address_map &map) +void gbc_state::gbc_map(address_map &map) { map.unmap_value_high(); map(0x0000, 0x7fff).view(m_cart_low); m_cart_low[BIOS_ENABLED | NO_CART](0x0000, 0x7fff).noprw(); - m_cart_low[BIOS_ENABLED | NO_CART](0x0000, 0x00ff).r(FUNC(gb_state::gb_bios_r)); + m_cart_low[BIOS_ENABLED | NO_CART](0x0000, 0x00ff).r(FUNC(gbc_state::gb_bios_r)); m_cart_low[BIOS_ENABLED | NO_CART](0x0200, 0x08ff).rom().region("maincpu", 0x0100); m_cart_low[BIOS_ENABLED | CART_PRESENT](0x0000, 0x7fff).rw(m_cartslot, FUNC(gb_cart_slot_device::read_rom), FUNC(gb_cart_slot_device::write_bank)); - m_cart_low[BIOS_ENABLED | CART_PRESENT](0x0000, 0x00ff).r(FUNC(gb_state::gb_bios_r)); + m_cart_low[BIOS_ENABLED | CART_PRESENT](0x0000, 0x00ff).r(FUNC(gbc_state::gb_bios_r)); m_cart_low[BIOS_ENABLED | CART_PRESENT](0x0200, 0x08ff).rom().region("maincpu", 0x0100); m_cart_low[BIOS_DISABLED | NO_CART](0x0000, 0x7fff).noprw(); m_cart_low[BIOS_DISABLED | CART_PRESENT](0x0000, 0x7fff).rw(m_cartslot, FUNC(gb_cart_slot_device::read_rom), FUNC(gb_cart_slot_device::write_bank)); @@ -370,15 +979,15 @@ void gb_state::gbc_map(address_map &map) m_cart_high[NO_CART](0xa000, 0xbfff).noprw(); m_cart_high[CART_PRESENT](0xa000, 0xbfff).rw(m_cartslot, FUNC(gb_cart_slot_device::read_ram), FUNC(gb_cart_slot_device::write_ram)); map(0xc000, 0xcfff).mirror(0x2000).ram(); - map(0xd000, 0xdfff).mirror(0x2000).bankrw("cgb_ram"); + map(0xd000, 0xdfff).mirror(0x2000).bankrw(m_rambank); map(0xfe00, 0xfeff).rw(m_ppu, FUNC(cgb_ppu_device::oam_r), FUNC(cgb_ppu_device::oam_w)); - map(0xff00, 0xff0f).rw(FUNC(gb_state::gb_io_r), FUNC(gb_state::gbc_io_w)); + map(0xff00, 0xff0f).rw(FUNC(gbc_state::gb_io_r), FUNC(gbc_state::gbc_io_w)); map(0xff10, 0xff26).rw(m_apu, FUNC(gameboy_sound_device::sound_r), FUNC(gameboy_sound_device::sound_w)); map(0xff27, 0xff2f).noprw(); map(0xff30, 0xff3f).rw(m_apu, FUNC(gameboy_sound_device::wave_r), FUNC(gameboy_sound_device::wave_w)); - map(0xff40, 0xff7f).rw(FUNC(gb_state::gbc_io2_r), FUNC(gb_state::gbc_io2_w)); + map(0xff40, 0xff7f).rw(FUNC(gbc_state::gbc_io2_r), FUNC(gbc_state::gbc_io2_w)); map(0xff80, 0xfffe).ram(); - map(0xffff, 0xffff).rw(FUNC(gb_state::gb_ie_r), FUNC(gb_state::gb_ie_w)); + map(0xffff, 0xffff).rw(FUNC(gbc_state::gb_ie_r), FUNC(gbc_state::gb_ie_w)); } void megaduck_state::megaduck_map(address_map &map) @@ -504,7 +1113,7 @@ void gb_state::gbp_palette(palette_device &palette) const palette.set_pen_color(i, palette_gb[i + 4]); } -void gb_state::sgb_palette(palette_device &palette) const +void sgb_state::sgb_palette(palette_device &palette) const { for (int i = 0; i < 32768; i++) { @@ -515,16 +1124,6 @@ void gb_state::sgb_palette(palette_device &palette) const } } -void gb_state::gbc_palette(palette_device &palette) const -{ - for (int i = 0; i < 32768; i++) - { - int const r = i & 0x1f; - int const g = (i >> 5) & 0x1f; - int const b = (i >> 10) & 0x1f; - palette.set_pen_color(i, pal5bit(r), pal5bit(g), pal5bit(b)); - } -} void megaduck_state::megaduck_palette(palette_device &palette) const { @@ -570,17 +1169,14 @@ void gb_state::gameboy(machine_config &config) SOFTWARE_LIST(config, "gbc_list").set_compatible("gbcolor"); } -void gb_state::supergb(machine_config &config) +void sgb_state::supergb(machine_config &config) { /* basic machine hardware */ LR35902(config, m_maincpu, 4295454); /* 4.295454 MHz, derived from SNES xtal */ - m_maincpu->set_addrmap(AS_PROGRAM, &gb_state::sgb_map); - m_maincpu->timer_cb().set(FUNC(gb_state::gb_timer_callback)); + m_maincpu->set_addrmap(AS_PROGRAM, &sgb_state::sgb_map); + m_maincpu->timer_cb().set(FUNC(sgb_state::gb_timer_callback)); m_maincpu->set_halt_bug(true); - MCFG_MACHINE_START_OVERRIDE(gb_state, sgb) - MCFG_MACHINE_RESET_OVERRIDE(gb_state, sgb) - /* video hardware */ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD)); screen.set_physical_aspect(4, 3); // runs on a TV, not an LCD @@ -592,7 +1188,7 @@ void gb_state::supergb(machine_config &config) screen.set_visarea(0*8, 32*8-1, 0*8, 28*8-1); GFXDECODE(config, "gfxdecode", m_palette, gfxdecode_device::empty); - PALETTE(config, m_palette, FUNC(gb_state::sgb_palette), 32768); + PALETTE(config, m_palette, palette_device::BGR_555); SGB_PPU(config, m_ppu, m_maincpu); @@ -610,15 +1206,12 @@ void gb_state::supergb(machine_config &config) SOFTWARE_LIST(config, "gbc_list").set_compatible("gbcolor"); } -void gb_state::supergb2(machine_config &config) +void sgb_state::supergb2(machine_config &config) { gameboy(config); /* basic machine hardware */ - m_maincpu->set_addrmap(AS_PROGRAM, &gb_state::sgb_map); - - MCFG_MACHINE_START_OVERRIDE(gb_state, sgb) - MCFG_MACHINE_RESET_OVERRIDE(gb_state, sgb) + m_maincpu->set_addrmap(AS_PROGRAM, &sgb_state::sgb_map); /* video hardware */ screen_device &screen(*subdevice("screen")); @@ -627,7 +1220,7 @@ void gb_state::supergb2(machine_config &config) screen.set_visarea(0*8, 32*8-1, 0*8, 28*8-1); m_palette->set_entries(32768); - m_palette->set_init(FUNC(gb_state::sgb_palette)); + m_palette->set_init(FUNC(sgb_state::sgb_palette)); SGB_PPU(config.replace(), m_ppu, m_maincpu); } @@ -642,15 +1235,12 @@ void gb_state::gbpocket(machine_config &config) MGB_PPU(config.replace(), m_ppu, m_maincpu); } -void gb_state::gbcolor(machine_config &config) +void gbc_state::gbcolor(machine_config &config) { /* basic machine hardware */ LR35902(config, m_maincpu, XTAL(4'194'304)); // todo XTAL(8'388'000) - m_maincpu->set_addrmap(AS_PROGRAM, &gb_state::gbc_map); - m_maincpu->timer_cb().set(FUNC(gb_state::gb_timer_callback)); - - MCFG_MACHINE_START_OVERRIDE(gb_state,gbc) - MCFG_MACHINE_RESET_OVERRIDE(gb_state,gbc) + m_maincpu->set_addrmap(AS_PROGRAM, &gbc_state::gbc_map); + m_maincpu->timer_cb().set(FUNC(gbc_state::gb_timer_callback)); /* video hardware */ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_LCD)); @@ -663,7 +1253,7 @@ void gb_state::gbcolor(machine_config &config) screen.set_visarea(0*8, 20*8-1, 0*8, 18*8-1); GFXDECODE(config, "gfxdecode", m_palette, gfxdecode_device::empty); - PALETTE(config, m_palette, FUNC(gb_state::gbc_palette), 32768); + PALETTE(config, m_palette, palette_device::BGR_555); CGB_PPU(config, m_ppu, m_maincpu); @@ -674,11 +1264,8 @@ void gb_state::gbcolor(machine_config &config) m_apu->add_route(0, "lspeaker", 0.50); m_apu->add_route(1, "rspeaker", 0.50); - /* internal ram */ - RAM(config, RAM_TAG).set_default_size("48K"); /* 2 pages of 8KB VRAM, 8 pages of 4KB RAM */ - /* cartslot */ - GB_CART_SLOT(config, "gbslot", gb_cart, nullptr); + GB_CART_SLOT(config, m_cartslot, gb_cart, nullptr); SOFTWARE_LIST(config, "cart_list").set_original("gbcolor"); SOFTWARE_LIST(config, "gb_list").set_compatible("gameboy"); @@ -689,7 +1276,7 @@ void megaduck_state::megaduck(machine_config &config) /* basic machine hardware */ LR35902(config, m_maincpu, XTAL(4'194'304)); /* 4.194304 MHz */ m_maincpu->set_addrmap(AS_PROGRAM, &megaduck_state::megaduck_map); - m_maincpu->timer_cb().set(FUNC(gb_state::gb_timer_callback)); + m_maincpu->timer_cb().set(FUNC(megaduck_state::gb_timer_callback)); m_maincpu->set_halt_bug(true); /* video hardware */ @@ -726,9 +1313,9 @@ void megaduck_state::megaduck(machine_config &config) ROM_START(gameboy) ROM_REGION(0x0100, "maincpu", 0) - ROM_SYSTEM_BIOS(0, "dmg", "DMG vX") + ROM_SYSTEM_BIOS(0, "vx", "DMG vX") ROMX_LOAD("dmg_boot.bin", 0x0000, 0x0100, CRC(59c8598e) SHA1(4ed31ec6b0b175bb109c0eb5fd3d193da823339f), ROM_BIOS(0)) - ROM_SYSTEM_BIOS(1, "dmg_v0", "DMG v0") + ROM_SYSTEM_BIOS(1, "v0", "DMG v0") ROMX_LOAD("dmg_v0.rom", 0x0000, 0x0100, CRC(c2f5cc97) SHA1(8bd501e31921e9601788316dbd3ce9833a97bcbc), ROM_BIOS(1)) ROM_END @@ -749,8 +1336,8 @@ ROM_END ROM_START(gbcolor) ROM_REGION(0x800, "maincpu", 0) - ROM_LOAD("gbc_boot.1", 0x0000, 0x0100, CRC(779ea374) SHA1(e4b40c9fd593a97a1618cfb2696f290cf9596a62)) /* Bootstrap code part 1 */ - ROM_LOAD("gbc_boot.2", 0x0100, 0x0700, CRC(f741807d) SHA1(f943b1e0b640cf1d371e1d8f0ada69af03ebb396)) /* Bootstrap code part 2 */ + ROM_LOAD("gbc_boot.1", 0x0000, 0x0100, CRC(779ea374) SHA1(e4b40c9fd593a97a1618cfb2696f290cf9596a62)) // Bootstrap code part 1 + ROM_LOAD("gbc_boot.2", 0x0100, 0x0700, CRC(f741807d) SHA1(f943b1e0b640cf1d371e1d8f0ada69af03ebb396)) // Bootstrap code part 2 ROM_END ROM_START(megaduck) @@ -797,16 +1384,17 @@ ROM_START(mduckspa) ROM_LOAD("81x00.bin", 0x0000, 0x10000, NO_DUMP ) ROM_END +} // anonymous namespace -/* YEAR NAME PARENT COMPAT MACHINE INPUT STATE INIT COMPANY FULLNAME */ +// YEAR NAME PARENT COMPAT MACHINE INPUT STATE INIT COMPANY FULLNAME CONS(1990, gameboy, 0, 0, gameboy, gameboy, gb_state, empty_init, "Nintendo", "Game Boy", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE) -CONS(1994, supergb, gameboy, 0, supergb, gameboy, gb_state, empty_init, "Nintendo", "Super Game Boy", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE) -CONS(1998, supergb2, gameboy, 0, supergb2, gameboy, gb_state, empty_init, "Nintendo", "Super Game Boy 2", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE) +CONS(1994, supergb, gameboy, 0, supergb, gameboy, sgb_state, empty_init, "Nintendo", "Super Game Boy", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE) +CONS(1998, supergb2, gameboy, 0, supergb2, gameboy, sgb_state, empty_init, "Nintendo", "Super Game Boy 2", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE) CONS(1996, gbpocket, gameboy, 0, gbpocket, gameboy, gb_state, empty_init, "Nintendo", "Game Boy Pocket", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE) -CONS(1998, gbcolor, 0, 0, gbcolor, gameboy, gb_state, empty_init, "Nintendo", "Game Boy Color", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE) +CONS(1998, gbcolor, 0, 0, gbcolor, gameboy, gbc_state, empty_init, "Nintendo", "Game Boy Color", MACHINE_IMPERFECT_SOUND | MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE) -// Sound is not 100% yet, it generates some sounds which could be ok. Since we're lacking a real system there's no way to verify. +// Sound is not 100% yet, it generates some sounds which could be OK. Since we're lacking a real system there's no way to verify. CONS(1993, megaduck, 0, 0, megaduck, gameboy, megaduck_state, empty_init, "Welback Holdings (Timlex International) / Creatronic / Videojet / Cougar USA", "Mega Duck / Cougar Boy", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) CONS(199?, mduckspa, 0, 0, megaduck, gameboy, megaduck_state, empty_init, "Cefa Toys", "Super Quique / Mega Duck (Spain)", MACHINE_NOT_WORKING ) // versions for other regions exist too diff --git a/src/mame/nintendo/gb.h b/src/mame/nintendo/gb.h deleted file mode 100644 index defbca70c3f..00000000000 --- a/src/mame/nintendo/gb.h +++ /dev/null @@ -1,164 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Wilbert Pol -/***************************************************************************** - * - * includes/gb.h - * - ****************************************************************************/ -#ifndef MAME_INCLUDES_GB_H -#define MAME_INCLUDES_GB_H - -#pragma once - -#include "sound/gb.h" -#include "cpu/lr35902/lr35902.h" -#include "bus/gameboy/gb_slot.h" -#include "machine/ram.h" -#include "video/gb_lcd.h" -#include "emupal.h" - - -class gb_state : public driver_device -{ -public: - gb_state(const machine_config &mconfig, device_type type, const char *tag) : - driver_device(mconfig, type, tag), - m_cartslot(*this, "gbslot"), - m_maincpu(*this, "maincpu"), - m_apu(*this, "apu"), - m_region_maincpu(*this, "maincpu"), - m_rambank(*this, "cgb_ram"), - m_inputs(*this, "INPUTS"), - m_bios_hack(*this, "SKIP_CHECK"), - m_ram(*this, RAM_TAG), - m_ppu(*this, "ppu"), - m_palette(*this, "palette"), - m_cart_low(*this, "cartlow"), - m_cart_high(*this, "carthigh") - { } - - uint8_t m_gb_io[0x10]{}; - - /* Timer related */ - uint16_t m_divcount = 0; - uint8_t m_shift = 0; - uint16_t m_shift_cycles = 0; - uint8_t m_triggering_irq = 0; - uint8_t m_reloading = 0; - - /* Serial I/O related */ - uint16_t m_internal_serial_clock = 0; - uint16_t m_internal_serial_frequency = 0; - uint32_t m_sio_count = 0; /* Serial I/O counter */ - - /* SGB variables */ - int8_t m_sgb_packets = 0; - uint8_t m_sgb_bitcount = 0; - uint8_t m_sgb_bytecount = 0; - uint8_t m_sgb_start = 0; - uint8_t m_sgb_rest = 0; - uint8_t m_sgb_controller_no = 0; - uint8_t m_sgb_controller_mode = 0; - uint8_t m_sgb_data[0x100]{}; - - /* CGB variables */ - uint8_t *m_gbc_rammap[8]{}; /* (CGB) Addresses of internal RAM banks */ - uint8_t m_gbc_rambank = 0; /* (CGB) Current CGB RAM bank */ - - void gb_io_w(offs_t offset, uint8_t data); - void gb_io2_w(offs_t offset, uint8_t data); - void sgb_io_w(offs_t offset, uint8_t data); - uint8_t gb_ie_r(); - void gb_ie_w(uint8_t data); - uint8_t gb_io_r(offs_t offset); - void gbc_io_w(offs_t offset, uint8_t data); - void gbc_io2_w(offs_t offset, uint8_t data); - uint8_t gbc_io2_r(offs_t offset); - void gb_palette(palette_device &palette) const; - DECLARE_MACHINE_START(sgb); - DECLARE_MACHINE_RESET(sgb); - void sgb_palette(palette_device &palette) const; - void gbp_palette(palette_device &palette) const; - DECLARE_MACHINE_START(gbc); - DECLARE_MACHINE_RESET(gbc); - void gbc_palette(palette_device &palette) const; - void gb_timer_callback(uint8_t data); - - uint8_t gb_bios_r(offs_t offset); - optional_device m_cartslot; - - void supergb(machine_config &config); - void supergb2(machine_config &config); - void gbcolor(machine_config &config); - void gbpocket(machine_config &config); - void gameboy(machine_config &config); - void gameboy_map(address_map &map); - void gbc_map(address_map &map); - void sgb_map(address_map &map); - -protected: - enum { - SIO_ENABLED = 0x80, - SIO_FAST_CLOCK = 0x02, - SIO_INTERNAL_CLOCK = 0x01 - }; - static constexpr u8 NO_CART = 0x00; - static constexpr u8 BIOS_ENABLED = 0x00; - static constexpr u8 CART_PRESENT = 0x01; - static constexpr u8 BIOS_DISABLED = 0x02; - - required_device m_maincpu; - required_device m_apu; - required_memory_region m_region_maincpu; - optional_memory_bank m_rambank; // cgb - required_ioport m_inputs; - required_ioport m_bios_hack; - optional_device m_ram; - required_device m_ppu; - required_device m_palette; - memory_view m_cart_low; - memory_view m_cart_high; - - void gb_timer_increment(); - void gb_timer_check_irq(); - void gb_init(); - void gb_init_regs(); - void gb_serial_timer_tick(); - - void save_gb_base(); - void save_gbc_only(); - void save_sgb_only(); - - virtual void machine_start() override; - virtual void machine_reset() override; -}; - - -class megaduck_state : public gb_state -{ -public: - megaduck_state(const machine_config &mconfig, device_type type, const char *tag) : - gb_state(mconfig, type, tag), - m_cartslot(*this, "duckslot") - { } - - void megaduck(machine_config &config); - -protected: - virtual void machine_start() override; - virtual void machine_reset() override; - -private: - uint8_t megaduck_video_r(offs_t offset); - void megaduck_video_w(offs_t offset, uint8_t data); - void megaduck_sound_w1(offs_t offset, uint8_t data); - uint8_t megaduck_sound_r1(offs_t offset); - void megaduck_sound_w2(offs_t offset, uint8_t data); - uint8_t megaduck_sound_r2(offs_t offset); - void megaduck_palette(palette_device &palette) const; - void megaduck_map(address_map &map); - - required_device m_cartslot; -}; - -#endif // MAME_INCLUDES_GB_H diff --git a/src/mame/nintendo/gb_m.cpp b/src/mame/nintendo/gb_m.cpp deleted file mode 100644 index 00cb53ab0e7..00000000000 --- a/src/mame/nintendo/gb_m.cpp +++ /dev/null @@ -1,772 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Wilbert Pol -/*************************************************************************** - - gb.c - - Machine file to handle emulation of the Nintendo Game Boy. - -Cardridge port pinouts: -Pin Name Description -1 VCC +5 VDC -2 PHI CPU clock ? -3 /WR Write -4 /RD Read -5 /CS SRAM select -6 A0 Address 0 -7 A1 Address 1 -8 A2 Address 2 -9 A3 Address 3 -10 A4 Address 4 -11 A5 Address 5 -12 A6 Address 6 -13 A7 Address 7 -14 A8 Address 8 -15 A9 Address 9 -16 A10 Address 10 -17 A11 Address 11 -18 A12 Address 12 -19 A13 Address 13 -20 A14 Address 14 -21 A15 Address 15 -22 D0 Data 0 -23 D1 Data 1 -24 D2 Data 2 -25 D3 Data 3 -26 D4 Data 4 -27 D5 Data 5 -28 D6 Data 6 -29 D7 Data 7 -30 /RST Reset -31 AUDIOIN Never used ? -32 GND Ground - - -TODO: -- YongYong mapper: - - During start there are 2 writes to 5000 and 5003, it is still unknown what these do. -- Story of La Sa Ma mapper: - - This should display the Gowin logo on boot on both DMG and CGB (Not implemented yet) -- ATV Racing/Rocket Games mapper: - - How did this overlay the official Nintendo logo at BIOS check time? (Some Sachen titles use a similar trick) - - - Changes: - - 13/2/2002 AK - MBC2 and MBC3 support and added NVRAM support. - 23/2/2002 AK - MBC5 support, and MBC2 RAM support. - 13/3/2002 AK - Tidied up the MBC code, window layer now has it's - own palette. Tidied init code. - 15/3/2002 AK - More init code tidying with a slight hack to stop - sound when the machine starts. - 19/3/2002 AK - Changed NVRAM code to the new battery_* functions. - 24/3/2002 AK - Added MBC1 mode switching, and partial MBC3 RTC support. - 28/3/2002 AK - Improved LCD status timing and interrupts. - Free memory when we shutdown instead of leaking. - 31/3/2002 AK - Handle IO memory reading so we return 0xFF for registers - that are unsupported. - 7/4/2002 AK - Free memory from battery load/save. General tidying. - 13/4/2002 AK - Ok, don't free memory when we shutdown as that causes - a crash on reset. - 28/4/2002 AK - General code tidying. - Fixed MBC3's RAM/RTC banking. - Added support for games with more than 128 ROM banks. - 12/6/2002 AK - Rewrote the way bg and sprite palettes are handled. - The window layer no longer has it's own palette. - Added Super Game Boy support. - 13/6/2002 AK - Added Game Boy Color support. - - 17/5/2004 WP - Added Megaduck/Cougar Boy support. - 13/6/2005 WP - Added support for bootstrap rom banking. - -***************************************************************************/ - -#include "emu.h" -#include "gb.h" - - -#define ENABLE_LOGGING 0 -#define LOG(x) do { if (ENABLE_LOGGING) logerror x; } while(0) - - -/* RAM layout defines */ -#define CGB_START_RAM_BANKS ( 2 * 8 * 1024 ) - - -#define JOYPAD m_gb_io[0x00] /* Joystick: 1.1.P15.P14.P13.P12.P11.P10 */ -#define SIODATA m_gb_io[0x01] /* Serial IO data buffer */ -#define SIOCONT m_gb_io[0x02] /* Serial IO control register */ -#define TIMECNT m_gb_io[0x05] /* Timer counter. Gen. int. when it overflows */ -#define TIMEMOD m_gb_io[0x06] /* New value of TimeCount after it overflows */ -#define TIMEFRQ m_gb_io[0x07] /* Timer frequency and start/stop switch */ - - -//------------------------- -// handle save state -//------------------------- - -void gb_state::save_gb_base() -{ - save_item(NAME(m_gb_io)); - save_item(NAME(m_divcount)); - save_item(NAME(m_shift)); - save_item(NAME(m_shift_cycles)); - save_item(NAME(m_triggering_irq)); - save_item(NAME(m_reloading)); - save_item(NAME(m_sio_count)); - if (m_cartslot) - m_cartslot->save_ram(); -} - -void gb_state::save_gbc_only() -{ - save_item(NAME(m_gbc_rambank)); -} - -void gb_state::save_sgb_only() -{ - save_item(NAME(m_sgb_packets)); - save_item(NAME(m_sgb_bitcount)); - save_item(NAME(m_sgb_bytecount)); - save_item(NAME(m_sgb_start)); - save_item(NAME(m_sgb_rest)); - save_item(NAME(m_sgb_controller_no)); - save_item(NAME(m_sgb_controller_mode)); - save_item(NAME(m_sgb_data)); -} - - -void gb_state::gb_init_regs() -{ - /* Initialize the registers */ - SIODATA = 0x00; - SIOCONT = 0x7E; - - gb_io_w(0x05, 0x00); /* TIMECNT */ - gb_io_w(0x06, 0x00); /* TIMEMOD */ -} - - -void gb_state::gb_init() -{ - m_apu->sound_w(0x16, 0x00); /* Initialize sound hardware */ - - m_divcount = 8; - m_internal_serial_clock = 0; - m_internal_serial_frequency = 512 / 2; - m_triggering_irq = 0; - m_shift = 10; // slowest timer? - m_shift_cycles = 1 << m_shift; - - /* Set registers to default/startup values */ - m_gb_io[0x00] = 0xCF; - m_gb_io[0x01] = 0x00; - m_gb_io[0x02] = 0x7E; - m_gb_io[0x03] = 0xFF; - m_gb_io[0x07] = 0xF8; /* Upper bits of TIMEFRQ register are set to 1 */ -} - - -void gb_state::machine_start() -{ - save_gb_base(); -} - -MACHINE_START_MEMBER(gb_state,gbc) -{ - for (int i = 0; i < 8; i++) - m_gbc_rammap[i] = m_ram->pointer() + CGB_START_RAM_BANKS + i * 0x1000; - - save_gb_base(); - save_gbc_only(); -} - - -MACHINE_START_MEMBER(gb_state,sgb) -{ - m_sgb_packets = -1; - - save_gb_base(); - save_sgb_only(); -} - -void gb_state::machine_reset() -{ - gb_init(); - - m_cart_low.select(BIOS_ENABLED | (m_cartslot ? CART_PRESENT : NO_CART)); - m_cart_high.select(m_cartslot ? CART_PRESENT : NO_CART); -} - -MACHINE_RESET_MEMBER(gb_state,gbc) -{ - gb_state::machine_reset(); - - gb_init_regs(); - - for (auto & elem : m_gbc_rammap) - memset(elem, 0, 0x1000); -} - -MACHINE_RESET_MEMBER(gb_state,sgb) -{ - gb_state::machine_reset(); - - gb_init_regs(); -} - - -void gb_state::gb_io_w(offs_t offset, uint8_t data) -{ - static const uint8_t timer_shifts[4] = {10, 4, 6, 8}; - - switch (offset) - { - case 0x00: /* JOYP - Joypad */ - JOYPAD = 0xCF | data; - if (!(data & 0x20)) - JOYPAD &= (m_inputs->read() >> 4) | 0xF0; - if (!(data & 0x10)) - JOYPAD &= m_inputs->read() | 0xF0; - return; - case 0x01: /* SB - Serial transfer data */ - break; - case 0x02: /* SC - SIO control */ - switch (data & 0x81) - { - case 0x00: - case 0x01: - m_sio_count = 0; - break; - case 0x80: /* enabled & external clock */ - m_sio_count = 16; - break; - case 0x81: /* enabled & internal clock */ - m_sio_count = 16; - break; - } -logerror("SIOCONT write, serial clock is %04x\n", m_internal_serial_clock); - data |= 0x7E; // unused bits stay high - break; - case 0x03: - return; - case 0x04: /* DIV - Divider register */ - /* Force increment of TIMECNT register when the 'highest' bit is set */ - if ((m_divcount >> (m_shift - 1)) & 1) - { - gb_timer_increment(); - } - LOG(("DIV write\n")); - m_divcount = 0; - return; - case 0x05: /* TIMA - Timer counter */ - /* Check if the counter is being reloaded in this cycle */ - if ((TIMEFRQ & 0x04) && TIMECNT == TIMEMOD && (m_divcount & (m_shift_cycles - 1)) == 4) - { - data = TIMEMOD; - } - break; - case 0x06: /* TMA - Timer module */ - /* Check if the counter is being reloaded in this cycle */ - if ((TIMEFRQ & 0x04) && TIMECNT == TIMEMOD && (m_divcount & (m_shift_cycles - 1)) == 4) - { - TIMECNT = data; - } - break; - case 0x07: /* TAC - Timer control */ - data |= 0xF8; - /* Check if timer is just disabled or the timer frequency is changing */ - if ((!(data & 0x04) && (TIMEFRQ & 0x04)) || ((data & 0x04) && (TIMEFRQ & 0x04) && (data & 0x03) != (TIMEFRQ & 0x03))) - { - /* Check if TIMECNT should be incremented */ - if ((m_divcount & (m_shift_cycles - 1)) >= (m_shift_cycles >> 1)) - { - gb_timer_increment(); - } - } - m_shift = timer_shifts[data & 0x03]; - m_shift_cycles = 1 << m_shift; - break; - case 0x0F: /* IF - Interrupt flag */ - m_ppu->update_state(); - LOG(("write if\n")); - data &= 0x1F; - m_maincpu->set_if(data); - break; - } - - m_gb_io[offset] = data; -} - -void gb_state::gb_io2_w(offs_t offset, uint8_t data) -{ - if (offset == 0x10) - { - /* disable BIOS ROM */ - m_cart_low.select(BIOS_DISABLED | (m_cartslot ? CART_PRESENT : NO_CART)); - } - else - m_ppu->video_w(offset, data); -} - -#ifdef MAME_DEBUG -static const char *const sgbcmds[32] = -{ - /* 0x00 */ "PAL01 ", - /* 0x01 */ "PAL23 ", - /* 0x02 */ "PAL03 ", - /* 0x03 */ "PAL12 ", - /* 0x04 */ "ATTR_BLK", - /* 0x05 */ "ATTR_LIN", - /* 0x06 */ "ATTR_DIV", - /* 0x07 */ "ATTR_CHR", - /* 0x08 */ "SOUND ", - /* 0x09 */ "SOU_TRN ", - /* 0x0A */ "PAL_SET ", - /* 0x0B */ "PAL_TRN ", - /* 0x0C */ "ATRC_EN ", - /* 0x0D */ "TEST_EN ", - /* 0x0E */ "ICON_EN ", - /* 0x0F */ "DATA_SND", - /* 0x10 */ "DATA_TRN", - /* 0x11 */ "MLT_REG ", - /* 0x12 */ "JUMP ", - /* 0x13 */ "CHR_TRN ", - /* 0x14 */ "PCT_TRN ", - /* 0x15 */ "ATTR_TRN", - /* 0x16 */ "ATTR_SET", - /* 0x17 */ "MASK_EN ", - /* 0x18 */ "OBJ_TRN ", - /* 0x19 */ "PAL_PRI ", - /* 0x1A */ "????????", - /* 0x1B */ "????????", - /* 0x1C */ "????????", - /* 0x1D */ "????????", - /* 0x1E */ "????????", - /* 0x1F */ "????????" -}; -#endif - -void gb_state::sgb_io_w(offs_t offset, uint8_t data) -{ - uint8_t *sgb_data = m_sgb_data; - - switch (offset) - { - case 0x00: - switch (data & 0x30) - { - case 0x00: /* start condition */ - if (m_sgb_start) - logerror("SGB: Start condition before end of transfer ??\n"); - m_sgb_bitcount = 0; - m_sgb_start = 1; - m_sgb_rest = 0; - JOYPAD = 0x0F & ((m_inputs->read() >> 4) | m_inputs->read() | 0xF0); - break; - case 0x10: /* data true */ - if (m_sgb_rest) - { - /* We should test for this case , but the code below won't - work with the current setup */ -#if 0 - if (m_sgb_bytecount == 16) - { - logerror("SGB: end of block is not zero!"); - m_sgb_start = 0; - } -#endif - sgb_data[m_sgb_bytecount] >>= 1; - sgb_data[m_sgb_bytecount] |= 0x80; - m_sgb_bitcount++; - if (m_sgb_bitcount == 8) - { - m_sgb_bitcount = 0; - m_sgb_bytecount++; - } - m_sgb_rest = 0; - } - JOYPAD = 0x1F & ((m_inputs->read() >> 4) | 0xF0); - break; - case 0x20: /* data false */ - if (m_sgb_rest) - { - if (m_sgb_bytecount == 16 && m_sgb_packets == -1) - { -#ifdef MAME_DEBUG - LOG(("SGB: %s (%02X) pkts: %d data: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", - sgbcmds[sgb_data[0] >> 3],sgb_data[0] >> 3, sgb_data[0] & 0x07, sgb_data[1], sgb_data[2], sgb_data[3], - sgb_data[4], sgb_data[5], sgb_data[6], sgb_data[7], - sgb_data[8], sgb_data[9], sgb_data[10], sgb_data[11], - sgb_data[12], sgb_data[13], sgb_data[14], sgb_data[15])); -#endif - m_sgb_packets = sgb_data[0] & 0x07; - m_sgb_start = 0; - } - if (m_sgb_bytecount == (m_sgb_packets << 4)) - { - switch (sgb_data[0] >> 3) - { - case 0x11: /* MLT_REQ - Multi controller request */ - if (sgb_data[1] == 0x00) - m_sgb_controller_mode = 0; - else if (sgb_data[1] == 0x01) - m_sgb_controller_mode = 2; - break; - default: - dynamic_cast(m_ppu.target())->sgb_io_write_pal(sgb_data[0] >> 3, &sgb_data[0]); - break; - } - m_sgb_start = 0; - m_sgb_bytecount = 0; - m_sgb_packets = -1; - } - if (m_sgb_start) - { - sgb_data[m_sgb_bytecount] >>= 1; - m_sgb_bitcount++; - if (m_sgb_bitcount == 8) - { - m_sgb_bitcount = 0; - m_sgb_bytecount++; - } - } - m_sgb_rest = 0; - } - JOYPAD = 0x2F & (m_inputs->read() | 0xF0); - break; - case 0x30: /* rest condition */ - if (m_sgb_start) - m_sgb_rest = 1; - if (m_sgb_controller_mode) - { - m_sgb_controller_no++; - if (m_sgb_controller_no == m_sgb_controller_mode) - m_sgb_controller_no = 0; - JOYPAD = 0x3F - m_sgb_controller_no; - } - else - JOYPAD = 0x3F; - - /* Hack to let cartridge know it's running on an SGB */ - if ((sgb_data[0] >> 3) == 0x1F) - JOYPAD = 0x3E; - break; - } - return; - default: - /* we didn't handle the write, so pass it to the GB handler */ - gb_io_w(offset, data); - return; - } - - m_gb_io[offset] = data; -} - -/* Interrupt Enable register */ -uint8_t gb_state::gb_ie_r() -{ - return m_maincpu->get_ie(); -} - -void gb_state::gb_ie_w(uint8_t data) -{ - m_maincpu->set_ie(data); -} - -/* IO read */ -uint8_t gb_state::gb_io_r(offs_t offset) -{ - switch(offset) - { - case 0x04: - LOG(("read DIV, divcount = %04x\n", m_divcount)); - return (m_divcount >> 8) & 0xFF; - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x05: - case 0x06: - case 0x07: - return m_gb_io[offset]; - case 0x0F: - /* Make sure the internal states are up to date */ - m_ppu->update_state(); - LOG(("read if\n")); -logerror("IF read, serial clock is %04x\n", m_internal_serial_clock); - return 0xE0 | m_maincpu->get_if(); - default: - /* Unsupported registers return 0xFF */ - return 0xFF; - } -} - - -/* Called when 512 internal cycles are passed */ -void gb_state::gb_serial_timer_tick() -{ - if (SIOCONT & SIO_ENABLED) - { - if (m_sio_count & 1) - { - /* Shift in a received bit */ - SIODATA = (SIODATA << 1) | 0x01; - } - /* Decrement number of handled bits */ - m_sio_count--; - - LOG(("%04x - gb_serial_timer_proc: SIODATA = %02x, sio_count = %u\n", m_maincpu->pc(), SIODATA, m_sio_count)); - /* If all bits done, stop timer and trigger interrupt */ - if (m_sio_count == 0) - { - SIOCONT &= ~SIO_ENABLED; - m_maincpu->set_input_line(lr35902_cpu_device::SIO_INT, ASSERT_LINE); - // Make sure the state is updated during the current timeslice in case it is read. - m_maincpu->execute_set_input(lr35902_cpu_device::SIO_INT, ASSERT_LINE); - } - } -} - - -void gb_state::gb_timer_check_irq() -{ - m_reloading = 0; - if (m_triggering_irq) - { - m_triggering_irq = 0; - if (TIMECNT == 0) - { - TIMECNT = TIMEMOD; - m_maincpu->set_input_line(lr35902_cpu_device::TIM_INT, ASSERT_LINE); - // Make sure the state is updated during the current timeslice in case it is read. - m_maincpu->execute_set_input(lr35902_cpu_device::TIM_INT, ASSERT_LINE); - m_reloading = 1; - } - } -} - -void gb_state::gb_timer_increment() -{ - gb_timer_check_irq(); - - LOG(("increment timer\n")); - TIMECNT += 1; - if (TIMECNT == 0) - { - m_triggering_irq = 1; - } -} - -// This gets called while the cpu is executing instructions to keep the timer state in sync -void gb_state::gb_timer_callback(uint8_t data) -{ - uint16_t old_gb_divcount = m_divcount; - uint16_t old_internal_serial_clock = m_internal_serial_clock; - m_divcount += data; - m_internal_serial_clock += data; - - if ( (old_gb_divcount >> 8) != (m_divcount >> 8)) { - //LOG(("DIV became %02x\n", m_divcount >> 8)); - } - gb_timer_check_irq(); - - if (TIMEFRQ & 0x04) - { - uint16_t old_count = old_gb_divcount >> m_shift; - uint16_t new_count = m_divcount >> m_shift; - if (data > m_shift_cycles) - { - gb_timer_increment(); - old_count++; - } - if (new_count != old_count) - { - gb_timer_increment(); - if (new_count << m_shift < m_divcount) - { - gb_timer_check_irq(); - } - } - } - - if (((m_internal_serial_clock ^ old_internal_serial_clock) & m_internal_serial_frequency) && (SIOCONT & SIO_INTERNAL_CLOCK)) - { - gb_serial_timer_tick(); - } -} - - -void gb_state::gbc_io_w(offs_t offset, uint8_t data) -{ - gb_io_w(offset, data); - - // On CGB the internal serial transfer clock is selectable - if (offset == 0x02) - { - m_internal_serial_frequency = ((data & SIO_FAST_CLOCK) ? 16 : 512) / 2; - SIOCONT = (SIOCONT & ~SIO_FAST_CLOCK) | (data & SIO_FAST_CLOCK); - } -} - - -void gb_state::gbc_io2_w(offs_t offset, uint8_t data) -{ - switch (offset) - { - case 0x0D: /* KEY1 - Prepare speed switch */ - m_maincpu->set_speed(data); - return; - case 0x10: /* BFF - Bios disable */ - m_cart_low.select(BIOS_DISABLED | (m_cartslot ? CART_PRESENT : NO_CART)); - return; - case 0x16: /* RP - Infrared port */ - break; - case 0x30: /* SVBK - RAM bank select */ - m_gbc_rambank = data & 0x7; - if (!m_gbc_rambank) - m_gbc_rambank = 1; - m_rambank->set_base(m_gbc_rammap[m_gbc_rambank]); - break; - default: - break; - } - m_ppu->video_w(offset, data); -} - -uint8_t gb_state::gbc_io2_r(offs_t offset) -{ - switch (offset) - { - case 0x0D: /* KEY1 */ - return m_maincpu->get_speed(); - case 0x16: /* RP - Infrared port */ - break; - case 0x30: /* SVBK - RAM bank select */ - return m_gbc_rambank; - default: - break; - } - return m_ppu->video_r(offset); -} - -/**************************************************************************** - - Megaduck routines - - ****************************************************************************/ - -void megaduck_state::machine_start() -{ - gb_state::machine_start(); -} - -void megaduck_state::machine_reset() -{ - gb_init(); - - m_cart_low.select((m_cartslot ? CART_PRESENT : NO_CART)); - m_cart_high.select(m_cartslot ? CART_PRESENT : NO_CART); -} - -/* - Map megaduck video related area on to regular Game Boy video area - - Different locations of the video registers: - Register Game Boy MegaDuck - LCDC FF40 FF10 (See different bit order below) - STAT FF41 FF11 - SCY FF42 FF12 - SCX FF43 FF13 - LY FF44 FF18 - LYC FF45 FF19 - DMA FF46 FF1A - BGP FF47 FF1B - OBP0 FF48 FF14 - OBP1 FF49 FF15 - WY FF4A FF16 - WX FF4B FF17 - Unused FF4C FF4C (?) - Unused FF4D FF4D (?) - Unused FF4E FF4E (?) - Unused FF4F FF4F (?) - - Different LCDC register - - Game Boy MegaDuck - 0 6 - BG & Window Display : 0 - Off, 1 - On - 1 0 - OBJ Display: 0 - Off, 1 - On - 2 1 - OBJ Size: 0 - 8x8, 1 - 8x16 - 3 2 - BG Tile Map Display: 0 - 9800, 1 - 9C00 - 4 4 - BG & Window Tile Data Select: 0 - 8800, 1 - 8000 - 5 5 - Window Display: 0 - Off, 1 - On - 6 3 - Window Tile Map Display Select: 0 - 9800, 1 - 9C00 - 7 7 - LCD Operation - - **************/ - -uint8_t megaduck_state::megaduck_video_r(offs_t offset) -{ - uint8_t data; - - if ((offset & 0x0C) && ((offset & 0x0C) ^ 0x0C)) - { - offset ^= 0x0C; - } - data = m_ppu->video_r(offset); - if (offset) - return data; - return bitswap<8>(data,7,0,5,4,6,3,2,1); -} - -void megaduck_state::megaduck_video_w(offs_t offset, uint8_t data) -{ - if (!offset) - { - data = bitswap<8>(data,7,3,5,4,2,1,0,6); - } - if ((offset & 0x0C) && ((offset & 0x0C) ^ 0x0C)) - { - offset ^= 0x0C; - } - m_ppu->video_w(offset, data); -} - -/* Map megaduck audio offset to game boy audio offsets */ -/* Envelope and LFSR register nibbles are reversed relative to the game boy */ - -static const uint8_t megaduck_sound_offsets[16] = { 0, 2, 1, 3, 4, 6, 5, 7, 8, 9, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; - -void megaduck_state::megaduck_sound_w1(offs_t offset, uint8_t data) -{ - if ((offset == 0x01) || (offset == 0x07)) - m_apu->sound_w(megaduck_sound_offsets[offset], ((data & 0x0f)<<4) | ((data & 0xf0)>>4)); - else - m_apu->sound_w(megaduck_sound_offsets[offset], data); -} - -uint8_t megaduck_state::megaduck_sound_r1(offs_t offset) -{ - uint8_t data = m_apu->sound_r(megaduck_sound_offsets[offset]); - if ((offset == 0x01) || (offset == 0x07)) - return ((data & 0x0f)<<4) | ((data & 0xf0)>>4); - else - return data; -} - -void megaduck_state::megaduck_sound_w2(offs_t offset, uint8_t data) -{ - if ((offset == 0x01) || (offset == 0x02)) - m_apu->sound_w(0x10 + megaduck_sound_offsets[offset], ((data & 0x0f)<<4) | ((data & 0xf0)>>4)); - else - m_apu->sound_w(0x10 + megaduck_sound_offsets[offset], data); -} - -uint8_t megaduck_state::megaduck_sound_r2(offs_t offset) -{ - uint8_t data = m_apu->sound_r(0x10 + megaduck_sound_offsets[offset]); - if ((offset == 0x01) || (offset == 0x02)) - return ((data & 0x0f)<<4) | ((data & 0xf0)>>4); - else - return data; -}