nintendo/gb.cpp: A bit of cleanup.

* Combined driver source files.
* Split up state classes and got rid of legacy start/reset callback
  overrides.
* Use configured banking for GBC RAM (fixes some save state issues, but
  there could be more lurking).
* Moved notes about cartridge hardware to more appropriate places.
This commit is contained in:
Vas Crabb 2022-08-21 01:53:06 +10:00
parent 761788fbd9
commit ae31e06854
6 changed files with 1179 additions and 1240 deletions

View File

@ -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

View File

@ -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!
***********************************************************************************************************/

View File

@ -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)
***********************************************************************************************************/

File diff suppressed because it is too large Load Diff

View File

@ -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<gb_cart_slot_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<lr35902_cpu_device> m_maincpu;
required_device<gameboy_sound_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<ram_device> m_ram;
required_device<dmg_ppu_device> m_ppu;
required_device<palette_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<megaduck_cart_slot_device> m_cartslot;
};
#endif // MAME_INCLUDES_GB_H

View File

@ -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<sgb_ppu_device*>(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;
}