gb.cpp: Some cpu, sound, and video updates (nw)

This commit is contained in:
Wilbert Pol 2016-09-30 22:06:39 +02:00
parent 8566a1fd10
commit c3e0ead534
16 changed files with 3863 additions and 2105 deletions

View File

@ -25823,7 +25823,7 @@ more investigation needed...
<feature name="led2" value="LED2" /> <!-- green, connected to CN2? -->
<feature name="cart_model" value="SHVC-042" />
<feature name="cart_back_label" value="SHVC-SGB2-JPN" />
<feature name="slot" value="lorom_sgb" />
<feature name="slot" value="lorom_sgb2" />
<dataarea name="rom" size="524288">
<rom name="sys-sgb2-10.u4" size="524288" crc="cb176e45" sha1="e5b2922ca137051059e4269b236d07a22c07bc84" offset="0x000000" />
</dataarea>

View File

@ -19,15 +19,17 @@
// sns_rom_sgb_device - constructor
//-------------------------------------------------
const device_type SNS_LOROM_SUPERGB = &device_creator<sns_rom_sgb_device>;
const device_type SNS_LOROM_SUPERGB = &device_creator<sns_rom_sgb1_device>;
const device_type SNS_LOROM_SUPERGB2 = &device_creator<sns_rom_sgb2_device>;
sns_rom_sgb_device::sns_rom_sgb_device(const machine_config& mconfig, const char* tag, device_t* owner, UINT32 clock) :
sns_rom_device(mconfig, SNS_LOROM_SUPERGB, "SNES Super Game Boy Cart", tag, owner, clock, "sns_rom_sgb", __FILE__),
m_gb_cpu(*this, "sgb_cpu"),
m_gb_snd(*this, "sgb_snd"),
m_gb_lcd(*this, "sgb_lcd"),
sns_rom_sgb_device::sns_rom_sgb_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
: sns_rom_device(mconfig, type, name, tag, owner, clock, shortname, source),
m_sgb_cpu(*this, "sgb_cpu"),
m_sgb_apu(*this, "sgb_apu"),
m_sgb_ppu(*this, "sgb_ppu"),
m_cartslot(*this, "gb_slot"),
m_region_bios(*this, "sgb_cpu"),
m_sgb_ly(0),
m_sgb_row(0),
m_vram(0),
@ -39,7 +41,20 @@ sns_rom_sgb_device::sns_rom_sgb_device(const machine_config& mconfig, const char
m_vram_offs(0),
m_mlt_req(0),
m_lcd_row(0),
m_packetsize(0)
m_packetsize(0),
m_bios_disabled(false)
{
}
sns_rom_sgb1_device::sns_rom_sgb1_device(const machine_config& mconfig, const char* tag, device_t* owner, UINT32 clock) :
sns_rom_sgb_device(mconfig, SNS_LOROM_SUPERGB, "SNES Super Game Boy Cart", tag, owner, clock, "sns_rom_sgb", __FILE__)
{
}
sns_rom_sgb2_device::sns_rom_sgb2_device(const machine_config& mconfig, const char* tag, device_t* owner, UINT32 clock) :
sns_rom_sgb_device(mconfig, SNS_LOROM_SUPERGB2, "SNES Super Game Boy 2 Cart", tag, owner, clock, "sns_rom_sgb2", __FILE__)
{
}
@ -62,6 +77,10 @@ void sns_rom_sgb_device::device_reset()
READ8_MEMBER(sns_rom_sgb_device::gb_cart_r)
{
if (offset < 0x100 && !m_bios_disabled)
{
return m_region_bios->base()[offset];
}
return m_cartslot->read_rom(space, offset);
}
@ -101,12 +120,12 @@ WRITE8_MEMBER(sns_rom_sgb_device::gb_io_w)
READ8_MEMBER(sns_rom_sgb_device::gb_ie_r)
{
return m_gb_cpu->get_ie();
return m_sgb_cpu->get_ie();
}
WRITE8_MEMBER(sns_rom_sgb_device::gb_ie_w)
{
m_gb_cpu->set_ie(data & 0x1f);
m_sgb_cpu->set_ie(data);
}
@ -114,16 +133,16 @@ WRITE8_MEMBER(sns_rom_sgb_device::gb_ie_w)
static ADDRESS_MAP_START(supergb_map, AS_PROGRAM, 8, sns_rom_sgb_device )
ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(0x0000, 0x7fff) AM_READWRITE(gb_cart_r, gb_bank_w)
AM_RANGE(0x8000, 0x9fff) AM_DEVREADWRITE("sgb_lcd", sgb_lcd_device, vram_r, vram_w) /* 8k VRAM */
AM_RANGE(0x8000, 0x9fff) AM_DEVREADWRITE("sgb_ppu", sgb_ppu_device, vram_r, vram_w) /* 8k VRAM */
AM_RANGE(0xa000, 0xbfff) AM_READWRITE(gb_ram_r, gb_ram_w ) /* 8k switched RAM bank (cartridge) */
AM_RANGE(0xc000, 0xdfff) AM_RAM /* 8k low RAM */
AM_RANGE(0xe000, 0xfdff) AM_READWRITE(gb_echo_r, gb_echo_w) /* echo RAM */
AM_RANGE(0xc000, 0xdfff) AM_RAM /* 8k low RAM */
AM_RANGE(0xe000, 0xfdff) AM_READWRITE(gb_echo_r, gb_echo_w)
AM_RANGE(0xfe00, 0xfeff) AM_DEVREADWRITE("sgb_ppu", sgb_ppu_device, oam_r, oam_w) /* OAM RAM */
AM_RANGE(0xff00, 0xff0f) AM_READWRITE(gb_io_r, gb_io_w) /* I/O */
AM_RANGE(0xff10, 0xff26) AM_DEVREADWRITE("sgb_snd", gameboy_sound_device, sound_r, sound_w) /* sound registers */
AM_RANGE(0xfe00, 0xfeff) AM_DEVREADWRITE("sgb_lcd", sgb_lcd_device, oam_r, oam_w) /* OAM RAM */
AM_RANGE(0xff10, 0xff26) AM_DEVREADWRITE("sgb_apu", gameboy_sound_device, sound_r, sound_w) /* sound registers */
AM_RANGE(0xff27, 0xff2f) AM_NOP /* unused */
AM_RANGE(0xff30, 0xff3f) AM_DEVREADWRITE("sgb_snd", gameboy_sound_device, wave_r, wave_w) /* Wave RAM */
AM_RANGE(0xff40, 0xff7f) AM_DEVREADWRITE("sgb_lcd", sgb_lcd_device, video_r, video_w) /* also disable bios?? */ /* Video controller & BIOS flip-flop */
AM_RANGE(0xff30, 0xff3f) AM_DEVREADWRITE("sgb_apu", gameboy_sound_device, wave_r, wave_w) /* Wave RAM */
AM_RANGE(0xff40, 0xff7f) AM_DEVREADWRITE("sgb_ppu", sgb_ppu_device, video_r, video_w) /* also disable bios?? */ /* Video controller & BIOS flip-flop */
AM_RANGE(0xff80, 0xfffe) AM_RAM /* High RAM */
AM_RANGE(0xffff, 0xffff) AM_READWRITE(gb_ie_r, gb_ie_w) /* Interrupt enable register */
ADDRESS_MAP_END
@ -140,26 +159,71 @@ static SLOT_INTERFACE_START(supergb_cart)
SLOT_INTERFACE_INTERNAL("rom_mbc1", GB_ROM_MBC1)
SLOT_INTERFACE_END
static MACHINE_CONFIG_FRAGMENT( supergb )
MCFG_CPU_ADD("sgb_cpu", LR35902, 4295454) /* 4.295454 MHz */
MCFG_CPU_PROGRAM_MAP(supergb_map)
MCFG_LR35902_TIMER_CB(WRITE8(sns_rom_sgb_device, gb_timer_callback))
MCFG_LR35902_HALT_BUG
MCFG_GB_LCD_SGB_ADD("sgb_lcd")
MCFG_SGB_PPU_ADD("sgb_ppu", "sgb_cpu")
MCFG_SOUND_ADD("sgb_snd", GAMEBOY, 0)
MCFG_SOUND_ADD("sgb_apu", DMG_APU, 4295454)
MCFG_GB_CARTRIDGE_ADD("gb_slot", supergb_cart, nullptr)
MACHINE_CONFIG_END
machine_config_constructor sns_rom_sgb_device::device_mconfig_additions() const
machine_config_constructor sns_rom_sgb1_device::device_mconfig_additions() const
{
return MACHINE_CONFIG_NAME( supergb );
}
ROM_START( supergb )
ROM_REGION(0x100, "sgb_cpu", 0)
ROM_LOAD("sgb_boot.bin", 0x0000, 0x0100, CRC(ec8a83b9) SHA1(aa2f50a77dfb4823da96ba99309085a3c6278515))
ROM_END
const tiny_rom_entry *sns_rom_sgb1_device::device_rom_region() const
{
return ROM_NAME( supergb );
}
static MACHINE_CONFIG_FRAGMENT( supergb2 )
MCFG_CPU_ADD("sgb_cpu", LR35902, XTAL_4_194304Mhz) /* 4.194MHz derived from clock on sgb2 pcb */
MCFG_CPU_PROGRAM_MAP(supergb_map)
MCFG_LR35902_TIMER_CB(WRITE8(sns_rom_sgb_device, gb_timer_callback))
MCFG_LR35902_HALT_BUG
MCFG_SGB_PPU_ADD("sgb_ppu", "sgb_cpu")
MCFG_SOUND_ADD("sgb_apu", DMG_APU, XTAL_4_194304Mhz)
MCFG_GB_CARTRIDGE_ADD("gb_slot", supergb_cart, nullptr)
MACHINE_CONFIG_END
machine_config_constructor sns_rom_sgb2_device::device_mconfig_additions() const
{
return MACHINE_CONFIG_NAME( supergb2 );
}
ROM_START( supergb2 )
ROM_REGION(0x100, "sgb_cpu", 0)
ROM_LOAD("sgb2_boot.bin", 0x0000, 0x0100, CRC(53d0dd63) SHA1(93407ea10d2f30ab96a314d8eca44fe160aea734))
ROM_END
const tiny_rom_entry *sns_rom_sgb2_device::device_rom_region() const
{
return ROM_NAME( supergb2 );
}
/*-------------------------------------------------
mapper specific handlers
-------------------------------------------------*/

View File

@ -20,12 +20,11 @@ class sns_rom_sgb_device : public sns_rom_device
{
public:
// construction/destruction
sns_rom_sgb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
sns_rom_sgb_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
virtual machine_config_constructor device_mconfig_additions() const override;
// reading and writing
virtual DECLARE_READ8_MEMBER(read_l) override;
@ -45,10 +44,12 @@ public:
virtual DECLARE_WRITE8_MEMBER(gb_ie_w);
virtual DECLARE_WRITE8_MEMBER(gb_timer_callback);
required_device<lr35902_cpu_device> m_gb_cpu;
required_device<gameboy_sound_device> m_gb_snd;
required_device<sgb_lcd_device> m_gb_lcd;
protected:
required_device<lr35902_cpu_device> m_sgb_cpu;
required_device<gameboy_sound_device> m_sgb_apu;
required_device<sgb_ppu_device> m_sgb_ppu;
required_device<gb_cart_slot_device> m_cartslot;
required_memory_region m_region_bios;
void lcd_render(UINT32 *source);
@ -69,10 +70,36 @@ public:
// input bits
int m_packetsize;
UINT8 m_packet_data[64][16];
bool m_bios_disabled;
};
class sns_rom_sgb1_device : public sns_rom_sgb_device
{
public:
// construction/destruction
sns_rom_sgb1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
// device-level overrides
virtual machine_config_constructor device_mconfig_additions() const override;
virtual const tiny_rom_entry *device_rom_region() const override;
};
class sns_rom_sgb2_device : public sns_rom_sgb_device
{
public:
// construction/destruction
sns_rom_sgb2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
// device-level overrides
virtual machine_config_constructor device_mconfig_additions() const override;
virtual const tiny_rom_entry *device_rom_region() const override;
};
// device type definition
extern const device_type SNS_LOROM_SUPERGB;
extern const device_type SNS_LOROM_SUPERGB2;
#endif

View File

@ -19,6 +19,7 @@ SLOT_INTERFACE_START(snes_cart)
SLOT_INTERFACE_INTERNAL("lorom_sdd1", SNS_LOROM_SDD1)
SLOT_INTERFACE_INTERNAL("lorom_sfx", SNS_LOROM_SUPERFX)
SLOT_INTERFACE_INTERNAL("lorom_sgb", SNS_LOROM_SUPERGB) // SuperGB base cart - unsupported
SLOT_INTERFACE_INTERNAL("lorom_sgb2", SNS_LOROM_SUPERGB2) // SuperGB2 base cart - unsupported
SLOT_INTERFACE_INTERNAL("lorom_st010", SNS_LOROM_SETA10)
SLOT_INTERFACE_INTERNAL("lorom_st011", SNS_LOROM_SETA11)
SLOT_INTERFACE_INTERNAL("lorom_st018", SNS_LOROM) // Cart + ST018 - unsupported

View File

@ -80,6 +80,7 @@ lr35902_cpu_device::lr35902_cpu_device(const machine_config &mconfig, const char
, m_IF(0)
, m_enable(0)
, m_has_halt_bug(false)
, m_entering_halt(false)
, m_timer_func(*this)
, m_incdec16_func(*this)
{
@ -156,7 +157,7 @@ void lr35902_cpu_device::device_start()
save_item(NAME(m_gb_speed));
save_item(NAME(m_gb_speed_change_pending));
save_item(NAME(m_enable));
save_item(NAME(m_handle_halt_bug));
save_item(NAME(m_entering_halt));
// Register state for debugger
state_add( LR35902_PC, "PC", m_PC ).callimport().callexport().formatstr("%04X");
@ -218,10 +219,10 @@ void lr35902_cpu_device::device_reset()
m_IF = 0;
m_execution_state = 0;
m_handle_halt_bug = false;
m_handle_ei_delay = false;
m_gb_speed_change_pending = 0;
m_gb_speed = 1;
m_entering_halt = false;
}
@ -254,6 +255,7 @@ void lr35902_cpu_device::check_interrupts()
logerror("LR35902 Interrupt IRQ $%02X\n", irq);
*/
bool was_halted = (m_enable & HALTED);
for( ; irqline < 5; irqline++ )
{
if( irq & (1<<irqline) )
@ -262,17 +264,42 @@ void lr35902_cpu_device::check_interrupts()
{
m_enable &= ~HALTED;
m_PC++;
// In general there seems to be a 4 cycle delay to leave the halt state; except when the
// trigger is caused by the VBlank interrupt (on DMG/MGB/SGB?/SGB2?).
//
// On CGB/AGB/AGS this delay to leave the halt seems to always be 4 cycles.
//
if ( m_has_halt_bug ) {
if ( ! ( m_enable & IME ) ) {
/* Old cpu core (dmg/mgb/sgb) */
m_handle_halt_bug = true;
m_PC--;
}
// TODO: Properly detect when the delay should be skipped. Cases seen so far:
// - Vblank irq
// - STAT mode 1 irq (triggered at same time as vblank)
// - STAT mode 2 irq (8 cycles?, breaks gambatte halt/m2irq_ly tests when always applied but fix other gambatte halt/m2irq and halt/m2int cases)
// No delay:
// - LY=LYC irq
// STAT and not vblank just triggered (this on dmg/mgb/sgb only)? or Timer IRQ
//
// This is a bit hacky, more testing is needed to determine exact
// hardware behavior.
if ((irqline == 1 && !(m_IF & 0x01)) || irqline == 2)
{
// Cycles needed for leaving the halt state
cycles_passed(4);
if (irqline == 2)
{
cycles_passed(2);
}
}
} else {
/* New cpu core (cgb/agb/ags) */
/* Adjust for internal syncing with video core */
/* This feature needs more investigation */
if ( irqline < 2 ) {
cycles_passed( 4 );
// Leaving halt state seems to take 4 cycles.
cycles_passed(4);
if (!(m_enable & IME) && !m_entering_halt)
{
cycles_passed(4);
}
}
}
@ -284,6 +311,9 @@ void lr35902_cpu_device::check_interrupts()
mem_write_word( m_SP, m_PC );
m_PC = 0x40 + irqline * 8;
/*logerror("LR35902 Interrupt PC $%04X\n", m_PC );*/
if (was_halted) {
m_op = mem_read_byte( m_PC );
}
return;
}
}
@ -299,32 +329,50 @@ void lr35902_cpu_device::execute_run()
{
do
{
if ( m_execution_state ) {
UINT8 x;
/* Execute instruction */
switch( m_op ) {
#include "opc_main.hxx"
default:
// actually this should lock up the cpu!
logerror("LR35902: Illegal opcode $%02X @ %04X\n", m_op, m_PC);
break;
if (m_dma_cycles_to_burn > 0)
{
if (m_dma_cycles_to_burn < 4)
{
cycles_passed(m_dma_cycles_to_burn);
m_dma_cycles_to_burn = 0;
}
} else {
/* Fetch and count cycles */
check_interrupts();
debugger_instruction_hook(this, m_PC);
if ( m_enable & HALTED ) {
cycles_passed( 4 );
m_execution_state = 1;
} else {
m_op = mem_read_byte( m_PC++ );
if ( m_handle_halt_bug ) {
m_PC--;
m_handle_halt_bug = false;
}
else
{
cycles_passed(4);
m_dma_cycles_to_burn -= 4;
}
}
m_execution_state ^= 1;
else
{
if ( m_execution_state ) {
UINT8 x;
/* Execute instruction */
switch( m_op ) {
#include "opc_main.hxx"
default:
// actually this should lock up the cpu!
logerror("LR35902: Illegal opcode $%02X @ %04X\n", m_op, m_PC);
break;
}
} else {
/* Fetch and count cycles */
bool was_halted = (m_enable & HALTED);
check_interrupts();
debugger_instruction_hook(this, m_PC);
if ( m_enable & HALTED ) {
cycles_passed(m_has_halt_bug ? 2 : 4);
m_execution_state = 1;
m_entering_halt = false;
} else {
if (was_halted) {
m_PC++;
} else {
m_op = mem_read_byte( m_PC++ );
}
}
}
m_execution_state ^= 1;
}
} while (m_icount > 0);
}

View File

@ -45,13 +45,30 @@ public:
static void set_halt_bug(device_t &device) { downcast<lr35902_cpu_device &>(device).m_has_halt_bug = true; }
UINT8 get_speed();
void set_speed( UINT8 speed_request );
void set_speed(UINT8 speed_request);
UINT8 get_ie() { return m_IE; }
void set_ie( UINT8 data ) { m_IE = data; }
inline UINT8 get_ie() { return m_IE; }
inline void set_ie(UINT8 data) { m_IE = data; }
UINT8 get_if() { return m_IF; }
void set_if( UINT8 data ) { m_IF = data; }
inline UINT8 get_if() { return m_IF; }
inline void set_if(UINT8 data) { m_IF = data; }
inline void dma_cycles_to_burn(UINT16 cycles_to_burn) { m_dma_cycles_to_burn += cycles_to_burn; }
// Needed for some gameboy operation which needs to read the results
// of setting an input during the currently running timeslice.
// Can become protected again once this core becomes cycle accurate.
virtual void execute_set_input(int inputnum, int state) override;
enum
{
/* Interrupts */
VBL_INT = 0, /* V-Blank */
LCD_INT = 1, /* LCD Status */
TIM_INT = 2, /* Timer */
SIO_INT = 3, /* Serial I/O */
EXT_INT = 4 /* Joypad */
};
protected:
@ -64,7 +81,6 @@ protected:
virtual UINT32 execute_max_cycles() const override { return 16; }
virtual UINT32 execute_input_lines() const override { return 5; }
virtual void execute_run() override;
virtual void execute_set_input(int inputnum, int state) override;
// device_memory_interface overrides
virtual const address_space_config *memory_space_config(address_spacenum spacenum = AS_0) const override { return (spacenum == AS_PROGRAM) ? &m_program_config : nullptr; }
@ -115,8 +131,9 @@ protected:
int m_gb_speed;
int m_gb_speed_change_pending;
int m_enable;
bool m_handle_halt_bug;
bool m_has_halt_bug;
UINT32 m_dma_cycles_to_burn;
bool m_entering_halt;
/* Callbacks */
devcb_write8 m_timer_func;

View File

@ -229,8 +229,21 @@ case 0x0F: /* RRCA */
}
break;
case 0x10: /* STOP */
if ( m_gb_speed_change_pending ) {
m_gb_speed = ( m_gb_speed == 1 ) ? 2 : 1;
if ( m_gb_speed_change_pending )
{
if (m_gb_speed == 1)
{
// Quite a lot of time for a simple input clock change...
// And still not all speedchange related tests are passing.
UINT32 cycles = ( 2 * 45 + 1 ) * 65536 + 8;
do {
cycles_passed(128);
cycles -= 128;
} while (cycles > 128);
cycles_passed(cycles);
}
m_gb_speed = ( m_gb_speed == 1 ) ? 2 : 1;
}
m_gb_speed_change_pending = 0;
break;
@ -717,6 +730,9 @@ case 0x75: /* LD (HL),L */
break;
case 0x76: /* HALT */
m_enable |= HALTED;
m_entering_halt = true;
// Prefetch the next instruction
m_op = mem_read_byte( m_PC );
m_PC--;
break;
case 0x77: /* LD (HL),A */
@ -1060,16 +1076,17 @@ case 0xC4: /* CALL NZ,n16 */
if ( ! (m_F & FLAG_Z) )
{
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = addr;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = addr;
}
}
break;
case 0xC5: /* PUSH BC */
PUSH( m_B, m_C );
// Internal delay
cycles_passed( 4 );
PUSH( m_B, m_C );
break;
case 0xC6: /* ADD A,n8 */
@ -1077,10 +1094,10 @@ case 0xC6: /* ADD A,n8 */
ADD_A_X (x)
break;
case 0xC7: /* RST 0 */
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = 0;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = 0;
break;
case 0xC8: /* RET Z */
cycles_passed( 4 );
@ -1122,10 +1139,10 @@ case 0xCC: /* CALL Z,n16 */
if (m_F & FLAG_Z)
{
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = addr;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = addr;
}
}
break;
@ -1134,10 +1151,11 @@ case 0xCD: /* CALL n16 */
UINT16 addr = mem_read_word( m_PC );
m_PC += 2;
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = addr;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = addr;
}
break;
case 0xCE: /* ADC A,n8 */
@ -1146,10 +1164,10 @@ case 0xCE: /* ADC A,n8 */
ADC_A_X (x)
break;
case 0xCF: /* RST 8 */
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = 8;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = 8;
break;
case 0xD0: /* RET NC */
cycles_passed( 4 );
@ -1182,16 +1200,17 @@ case 0xD4: /* CALL NC,n16 */
if ( ! (m_F & FLAG_C) )
{
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = addr;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = addr;
}
}
break;
case 0xD5: /* PUSH DE */
PUSH( m_D, m_E );
// Internal delay
cycles_passed( 4 );
PUSH( m_D, m_E );
break;
case 0xD6: /* SUB A,n8 */
@ -1199,10 +1218,10 @@ case 0xD6: /* SUB A,n8 */
SUB_A_X (x)
break;
case 0xD7: /* RST $10 */
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = 0x10;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = 0x10;
break;
case 0xD8: /* RET C */
cycles_passed( 4 );
@ -1238,10 +1257,10 @@ case 0xDC: /* CALL C,n16 */
if (m_F & FLAG_C)
{
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = addr;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = addr;
}
}
break;
@ -1251,10 +1270,10 @@ case 0xDE: /* SBC A,n8 */
SBC_A_X (x)
break;
case 0xDF: /* RST $18 */
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = 0x18;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = 0x18;
break;
case 0xE0: /* LD ($FF00+n8),A */
{
@ -1270,8 +1289,9 @@ case 0xE2: /* LD ($FF00+C),A */
mem_write_byte( 0xFF00 + m_C, m_A );
break;
case 0xE5: /* PUSH HL */
PUSH( m_H, m_L );
// Internal delay
cycles_passed( 4 );
PUSH( m_H, m_L );
break;
case 0xE6: /* AND A,n8 */
@ -1279,10 +1299,10 @@ case 0xE6: /* AND A,n8 */
AND_A_X (x)
break;
case 0xE7: /* RST $20 */
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = 0x20;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = 0x20;
break;
case 0xE8: /* ADD SP,n8 */
/*
@ -1329,10 +1349,10 @@ case 0xEE: /* XOR A,n8 */
XOR_A_X (x)
break;
case 0xEF: /* RST $28 */
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = 0x28;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = 0x28;
break;
case 0xF0: /* LD A,($FF00+n8) */
{
@ -1353,9 +1373,10 @@ case 0xF3: /* DI */
m_enable &= ~IME;
break;
case 0xF5: /* PUSH AF */
// Internal delay
cycles_passed( 4 );
m_F &= 0xF0;
PUSH( m_A, m_F );
cycles_passed( 4 );
break;
case 0xF6: /* OR A,n8 */
@ -1363,10 +1384,10 @@ case 0xF6: /* OR A,n8 */
OR_A_X (x)
break;
case 0xF7: /* RST $30 */
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = 0x30;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = 0x30;
break;
case 0xF8: /* LD HL,SP+n8 */
/*
@ -1422,8 +1443,8 @@ case 0xFE: /* CP A,n8 */
CP_A_X (x)
break;
case 0xFF: /* RST $38 */
m_SP -= 2;
mem_write_word( m_SP, m_PC );
m_PC = 0x38;
// Internal delay
cycles_passed( 4 );
PUSH( m_PC >> 8, m_PC & 0xff );
m_PC = 0x38;
break;

File diff suppressed because it is too large Load Diff

View File

@ -1,105 +1,199 @@
// license:BSD-3-Clause
// copyright-holders:Anthony Kruize
// copyright-holders:Anthony Kruize, Wilbert Pol
// thanks-to:Shay Green
#ifndef __GBSOUND_H__
#define __GBSOUND_H__
#define MAX_FREQUENCIES 2048
struct SOUND
{
/* Common */
UINT8 on;
UINT8 channel;
INT32 length;
INT32 pos;
UINT32 period;
INT32 count;
INT8 mode;
/* Mode 1, 2, 3 */
INT8 duty;
/* Mode 1, 2, 4 */
INT32 env_value;
INT8 env_direction;
INT32 env_length;
INT32 env_count;
INT8 signal;
/* Mode 1 */
UINT32 frequency;
INT32 swp_shift;
INT32 swp_direction;
INT32 swp_time;
INT32 swp_count;
/* Mode 3 */
INT8 level;
UINT8 offset;
UINT32 dutycount;
/* Mode 4 */
INT32 ply_step;
INT16 ply_value;
};
struct SOUNDC
{
UINT8 on;
UINT8 vol_left;
UINT8 vol_right;
UINT8 mode1_left;
UINT8 mode1_right;
UINT8 mode2_left;
UINT8 mode2_right;
UINT8 mode3_left;
UINT8 mode3_right;
UINT8 mode4_left;
UINT8 mode4_right;
};
class gameboy_sound_device : public device_t,
public device_sound_interface
public device_sound_interface
{
public:
gameboy_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
gameboy_sound_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
DECLARE_READ8_MEMBER(sound_r);
DECLARE_READ8_MEMBER(wave_r);
DECLARE_WRITE8_MEMBER(sound_w);
DECLARE_WRITE8_MEMBER(wave_w);
virtual DECLARE_READ8_MEMBER(wave_r) = 0;
virtual DECLARE_WRITE8_MEMBER(sound_w) = 0;
virtual DECLARE_WRITE8_MEMBER(wave_w) = 0;
protected:
// device-level overrides
virtual void device_config_complete() override;
virtual void device_start() override;
virtual void device_reset() override;
// sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
private:
void sound_w_internal(int offset, UINT8 data);
protected:
enum
{
NR10 = 0x00,
NR11 = 0x01,
NR12 = 0x02,
NR13 = 0x03,
NR14 = 0x04,
// 0x05
NR21 = 0x06,
NR22 = 0x07,
NR23 = 0x08,
NR24 = 0x09,
NR30 = 0x0A,
NR31 = 0x0B,
NR32 = 0x0C,
NR33 = 0x0D,
NR34 = 0x0E,
// 0x0F
NR41 = 0x10,
NR42 = 0x11,
NR43 = 0x12,
NR44 = 0x13,
NR50 = 0x14,
NR51 = 0x15,
NR52 = 0x16,
// 0x17 - 0x1F
AUD3W0 = 0x20,
AUD3W1 = 0x21,
AUD3W2 = 0x22,
AUD3W3 = 0x23,
AUD3W4 = 0x24,
AUD3W5 = 0x25,
AUD3W6 = 0x26,
AUD3W7 = 0x27,
AUD3W8 = 0x28,
AUD3W9 = 0x29,
AUD3WA = 0x2A,
AUD3WB = 0x2B,
AUD3WC = 0x2C,
AUD3WD = 0x2D,
AUD3WE = 0x2E,
AUD3WF = 0x2F
};
static const unsigned int FRAME_CYCLES = 8192;
static const int wave_duty_table[4][8];
sound_stream *m_channel;
int m_rate;
INT32 m_env_length_table[8];
INT32 m_swp_time_table[8];
UINT32 m_period_table[MAX_FREQUENCIES];
UINT32 m_period_mode3_table[MAX_FREQUENCIES];
UINT32 m_period_mode4_table[8][16];
UINT32 m_length_table[64];
UINT32 m_length_mode3_table[256];
struct SOUND
{
/* Common */
UINT8 reg[5];
bool on;
UINT8 channel;
UINT8 length;
UINT8 length_mask;
bool length_counting;
bool length_enabled;
/* Mode 1, 2, 3 */
UINT64 cycles_left;
INT8 duty;
/* Mode 1, 2, 4 */
bool envelope_enabled;
INT8 envelope_value;
INT8 envelope_direction;
UINT8 envelope_time;
UINT8 envelope_count;
INT8 signal;
/* Mode 1 */
UINT16 frequency;
UINT16 frequency_counter;
bool sweep_enabled;
bool sweep_neg_mode_used;
UINT8 sweep_shift;
INT32 sweep_direction;
UINT8 sweep_time;
UINT8 sweep_count;
/* Mode 3 */
UINT8 level;
UINT8 offset;
UINT32 duty_count;
INT8 current_sample;
bool sample_reading;
/* Mode 4 */
bool noise_short;
UINT16 noise_lfsr;
};
struct SOUND m_snd_1;
struct SOUND m_snd_2;
struct SOUND m_snd_3;
struct SOUND m_snd_4;
struct SOUNDC m_snd_control;
struct
{
UINT8 on;
UINT8 vol_left;
UINT8 vol_right;
UINT8 mode1_left;
UINT8 mode1_right;
UINT8 mode2_left;
UINT8 mode2_right;
UINT8 mode3_left;
UINT8 mode3_right;
UINT8 mode4_left;
UINT8 mode4_right;
UINT64 cycles;
bool wave_ram_locked;
} m_snd_control;
UINT8 m_snd_regs[0x30];
attotime m_last_updated;
emu_timer *m_timer;
virtual void apu_power_off() = 0;
void sound_w_internal(int offset, UINT8 data);
void update_square_channel(struct SOUND &snd, UINT64 cycles);
virtual void update_wave_channel(struct SOUND &snd, UINT64 cycles) = 0;
void update_noise_channel(struct SOUND &snd, UINT64 cycles);
INT32 calculate_next_sweep(struct SOUND &snd);
void apply_next_sweep(struct SOUND &snd);
void tick_length(struct SOUND &snd);
void tick_sweep(struct SOUND &snd);
void tick_envelope(struct SOUND &snd);
void update_state();
bool dac_enabled(struct SOUND &snd);
virtual void corrupt_wave_ram() { };
UINT64 noise_period_cycles();
TIMER_CALLBACK_MEMBER(timer_callback);
};
extern const device_type GAMEBOY;
class dmg_apu_device : public gameboy_sound_device
{
public:
dmg_apu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual DECLARE_READ8_MEMBER(wave_r) override;
virtual DECLARE_WRITE8_MEMBER(wave_w) override;
virtual DECLARE_WRITE8_MEMBER(sound_w) override;
protected:
virtual void apu_power_off() override;
virtual void corrupt_wave_ram() override;
virtual void update_wave_channel(struct SOUND &snd, UINT64 cycles) override;
};
class cgb04_apu_device : public gameboy_sound_device
{
public:
cgb04_apu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual DECLARE_READ8_MEMBER(wave_r) override;
virtual DECLARE_WRITE8_MEMBER(wave_w) override;
virtual DECLARE_WRITE8_MEMBER(sound_w) override;
protected:
virtual void device_reset() override;
virtual void apu_power_off() override;
virtual void update_wave_channel(struct SOUND &snd, UINT64 cycles) override;
};
extern const device_type DMG_APU;
//extern const device_type CGB02_APU;
extern const device_type CGB04_APU;
//extern const device_type CGB05_APU;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
#define __GB_LCD_H__
#include "emu.h"
#include "cpu/lr35902/lr35902.h"
struct layer_struct {
@ -26,13 +27,14 @@ struct layer_struct {
};
class gb_lcd_device : public device_t,
class dmg_ppu_device : public device_t,
public device_video_interface
{
public:
gb_lcd_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
gb_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
dmg_ppu_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source, UINT32 vram_size);
dmg_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
static void static_set_lr35902_tag(device_t &device, const char *tag) { downcast<dmg_ppu_device &>(device).m_lr35902.set_tag(tag); }
UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
@ -46,21 +48,42 @@ public:
// FIXME: remove it when proper sgb support is added
void set_sgb_hack(bool val) { m_sgb_border_hack = val ? 1 : 0; }
virtual void update_state();
protected:
inline void plot_pixel(bitmap_ind16 &bitmap, int x, int y, UINT32 color);
enum {
// bits in LCD Control register
ENABLED = 0x80,
WINDOW_ENABLED = 0x20,
LARGE_SPRITES = 0x04,
SPRITES_ENABLED = 0x02,
BACKGROUND_ENABLED = 0x01,
// bits in LCD Status register
LY_LYC_INT_ENABLED = 0x40,
MODE_2_INT_ENABLED = 0x20,
MODE_1_INT_ENABLED = 0x10,
MODE_0_INT_ENABLED = 0x08,
LY_LYC_FLAG = 0x04
};
inline void plot_pixel(int x, int y, UINT16 color);
void select_sprites();
void calculate_window_cycles();
virtual void update_sprites();
virtual void update_scanline();
virtual void update_scanline(UINT32 cycles_to_go);
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
void common_start();
void common_reset();
// pointer to the main system
cpu_device *m_maincpu;
required_device<lr35902_cpu_device> m_lr35902;
address_space *m_program_space;
// state variables
bitmap_ind16 m_bitmap;
@ -87,11 +110,56 @@ protected:
UINT16 m_cgb_bpal[32]; /* CGB current background palette table */
UINT16 m_cgb_spal[32]; /* CGB current sprite palette table */
UINT8 m_gb_bpal[4]; /* Background palette */
UINT8 m_gb_spal0[4]; /* Sprite 0 palette */
UINT8 m_gb_spal1[4]; /* Sprite 1 palette */
UINT16 m_gb_bpal[4]; /* Background palette */
UINT16 m_gb_spal0[4]; /* Sprite 0 palette */
UINT16 m_gb_spal1[4]; /* Sprite 1 palette */
/* WIP Things used to render current line */
struct {
// Background/window data
UINT8 tile_cycle;
UINT8 tile_count;
UINT8 y;
UINT16 pattern_address;
UINT8 pattern;
UINT16 tile_address;
UINT8 plane0;
UINT8 plane1;
UINT16 shift_register;
// Sprite data
struct {
bool enabled;
UINT8 x;
UINT8 y;
UINT8 pattern;
UINT8 flags;
UINT8 tile_plane_0;
UINT8 tile_plane_1;
} sprite[10];
UINT8 sprite_delay_cycles;
// other internal data
bool starting; // Inital fetches when (re)starting the rendering engine.
UINT8 sequence_counter;
bool drawing;
bool start_drawing;
UINT8 scrollx_delay;
UINT8 scrollx_to_apply;
UINT8 pixels_drawn;
UINT16 window_compare_position;
bool window_active;
UINT8 scrollx;
// To keep track of when changes to WNDPOSY/WNDPOSX should kick in
UINT8 window_start_y[16];
UINT8 window_start_x[16];
int window_start_y_index;
// To keep track of when changes to LCDCONT should kick in for window
UINT8 window_enable[16];
int window_enable_index;
bool window_should_trigger;
} m_line;
bool m_frame_window_active;
/* Things used to render current line */
int m_current_line; /* Current line */
int m_cmp_line; /* Compare line */
int m_sprCount; /* Number of sprites on current line */
@ -101,25 +169,38 @@ protected:
int m_end_x; /* Pixel to end drawing (exclusive) */
int m_mode; /* Keep track of internal STAT mode */
int m_state; /* Current state of the video state machine */
int m_lcd_irq_line;
int m_triggering_line_irq;
int m_line_irq;
int m_triggering_mode_irq;
int m_mode_irq;
int m_delayed_line_irq;
int m_sprite_cycles;
int m_window_cycles;
int m_scrollx_adjust;
int m_oam_locked;
int m_oam_locked_reading;
int m_vram_locked;
int m_pal_locked;
int m_hdma_enabled;
int m_hdma_possible;
int m_hdma_cycles_to_start;
UINT16 m_hdma_length;
struct layer_struct m_layer[2];
emu_timer *m_lcd_timer;
int m_oam_dma_start_cycles;
int m_oam_dma_cycles_left;
UINT16 m_oam_dma_source_address;
int m_gbc_mode;
UINT8 m_window_x;
UINT8 m_window_y;
UINT8 m_old_curline;
// Interrupt related
bool m_stat_mode0_int;
bool m_stat_mode1_int;
bool m_stat_mode2_int;
bool m_stat_lyc_int;
bool m_stat_lyc_int_prev;
bool m_stat_write_int;
bool m_stat_int;
std::unique_ptr<UINT8[]> m_vram; // Pointer to VRAM
std::unique_ptr<UINT8[]> m_oam; // Pointer to OAM memory
bool m_oam_dma_processing;
UINT8 m_gb_tile_no_mod;
UINT32 m_gb_chrgen_offs; // GB Character generator
UINT32 m_gb_bgdtab_offs; // GB Background character table
@ -129,18 +210,32 @@ protected:
UINT32 m_gbc_wndtab_offs; // CGB Window character table
int m_vram_bank;
TIMER_CALLBACK_MEMBER(video_init_vbl);
virtual TIMER_CALLBACK_MEMBER(lcd_timer_proc);
attotime m_last_updated;
UINT64 m_cycles_left;
int m_next_state;
bool m_updating_state;
bool m_enable_experimental_engine;
virtual void videoptr_restore();
virtual bool stat_write(UINT8 new_data);
void increment_scanline();
void lcd_switch_on();
void lcd_switch_on(UINT8 new_data);
void update_oam_dma_state(UINT64 cycles);
void check_stat_irq();
void clear_line_state();
void update_line_state(UINT64 cycles);
void check_start_of_window();
private:
UINT32 m_oam_size;
UINT32 m_vram_size;
};
class mgb_lcd_device : public gb_lcd_device
class mgb_ppu_device : public dmg_ppu_device
{
public:
mgb_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
mgb_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
protected:
@ -149,10 +244,10 @@ protected:
};
class sgb_lcd_device : public gb_lcd_device
class sgb_ppu_device : public dmg_ppu_device
{
public:
sgb_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
sgb_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
void sgb_io_write_pal(int offs, UINT8 *data);
@ -163,15 +258,15 @@ protected:
virtual void device_reset() override;
virtual void update_sprites() override;
virtual void update_scanline() override;
virtual void update_scanline(UINT32 cycles_to_go) override;
void refresh_border();
};
class cgb_lcd_device : public gb_lcd_device
class cgb_ppu_device : public dmg_ppu_device
{
public:
cgb_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
cgb_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual DECLARE_READ8_MEMBER(video_r) override;
virtual DECLARE_WRITE8_MEMBER(video_w) override;
@ -183,31 +278,38 @@ protected:
virtual void device_reset() override;
virtual void update_sprites() override;
virtual void update_scanline() override;
virtual void update_scanline(UINT32 cycles_to_go) override;
virtual TIMER_CALLBACK_MEMBER(lcd_timer_proc) override;
virtual void update_state() override;
virtual void videoptr_restore() override;
virtual bool stat_write(UINT8 new_data) override;
void update_hdma_state(UINT64 cycles);
void hdma_trans(UINT16 length);
void hdma_trans_execute();
};
extern const device_type GB_LCD_DMG;
extern const device_type GB_LCD_MGB;
extern const device_type GB_LCD_SGB;
extern const device_type GB_LCD_CGB;
extern const device_type DMG_PPU;
extern const device_type MGB_PPU;
extern const device_type SGB_PPU;
extern const device_type CGB_PPU;
#define MCFG_GB_LCD_DMG_ADD(_tag ) \
MCFG_DEVICE_ADD( _tag, GB_LCD_DMG, 0 )
#define MCFG_DMG_PPU_ADD(_tag, _cpu_tag ) \
MCFG_DEVICE_ADD( _tag, DMG_PPU, 0 ) \
dmg_ppu_device::static_set_lr35902_tag(*device, "^" _cpu_tag);
#define MCFG_GB_LCD_MGB_ADD(_tag ) \
MCFG_DEVICE_ADD( _tag, GB_LCD_MGB, 0 )
#define MCFG_MGB_PPU_ADD(_tag, _cpu_tag ) \
MCFG_DEVICE_ADD( _tag, MGB_PPU, 0 ) \
dmg_ppu_device::static_set_lr35902_tag(*device, "^" _cpu_tag);
#define MCFG_GB_LCD_SGB_ADD(_tag ) \
MCFG_DEVICE_ADD( _tag, GB_LCD_SGB, 0 )
#define MCFG_SGB_PPU_ADD(_tag, _cpu_tag ) \
MCFG_DEVICE_ADD( _tag, SGB_PPU, 0 ) \
dmg_ppu_device::static_set_lr35902_tag(*device, "^" _cpu_tag);
#define MCFG_GB_LCD_CGB_ADD(_tag ) \
MCFG_DEVICE_ADD( _tag, GB_LCD_CGB, 0 )
#define MCFG_CGB_PPU_ADD(_tag, _cpu_tag ) \
MCFG_DEVICE_ADD( _tag, CGB_PPU, 0 ) \
dmg_ppu_device::static_set_lr35902_tag(*device, "^" _cpu_tag);
#endif /* GB_LCD_H_ */

View File

@ -18,134 +18,6 @@
- Emulate OAM corruption bug on 16bit inc/dec in $fe** region
Timers
======
There seems to be some kind of selectable internal clock divider which is used to drive
the timer increments. This causes the first timer cycle to now always be a full cycle.
For instance in 1024 clock cycle mode, the first timer cycle could easily only take 400
clock cycles. The next timer cycle will take the full 1024 clock cycles though.
Writes to the DIV register seem to cause this internal clock divider/register to be
reset in such a way that the next stimulus cause a timer increment (in any mode).
Interrupts
==========
Taking an interrupt seems to take around 20 clock cycles.
Stat timing
===========
This timing table is accurate within 4 cycles:
| stat = 2 | stat = 3 | stat = 0 |
No sprites | 80 | 172 | 204 |
1 sprite | 80 | 182 | 194 |
2 sprites | 80 | 192 | 184 |
3 sprites | 80 | 202 | 174 |
4 sprites | 80 | 212 | 164 |
5 sprites | 80 | 222 | 154 |
6 sprites | 80 | 232 | 144 |
7 sprites | 80 | 242 | 134 |
8 sprites | 80 | 252 | 124 |
9 sprites | 80 | 262 | 114 |
10 sprites | 80 | 272 | 104 |
In other words, each sprite on a line makes stat 3 last 10 cycles longer.
For lines 1 - 143 when stat changes to 2 the line counter is incremented.
Line 153 is little odd timing wise. The line counter stays 153 for ~4 clock cycles
and is then rolls over to 0.
When the line counter is changed it gets checked against the lyc register.
Here is a detailed run of the STAT and LY register together with LYC set to 3 on a
dmg and mgb. The time between each sample is 4 clock cycles:
STAT:
22222222 22233333 33333333 33333333 33333333 33333333 33333300 00000000 00000000 00000000
00000000 00000000 00000000 06666666 66666666 66666777 77777777 77777777 77777777 77777777
77777777 44444444 44444444 44444444 44444444 44444444 44444444 44022222 22222222
LY:
33333333 33333333 33333333 33333333 33333333 33333333 33333333 33333333 33333333 33333333
33333333 33333333 33333333 44444444 44444444 44444444 44444444 44444444 44444444 44444444
44444444 44444444 44444444 44444444 44444444 44444444 44444444 44555555 55555555
^ ^
As you can see, it seems as though the LY register is incremented slightly before the STAT
register is changed, resulting in a short period where STAT goes 0 before going to 2. This
bug/feature has been fixed in the CGB and AGB.
Around lines 152-153-0 the picture becomes as follows:
STAT:
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
11111111 11111111 11111111 15555555 55555555 55555555 55555555 55555555 55555555 55555555
55555555 55555555 55555555 55555555 55555555 55555555 55555555 55111111 11111111 11111111
11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
11111111 11110222 22222222 22222222 23333333 33333333 33333333 33333333 33333333
LY:
77777777 77777777 77777777 77777777 77777777 77777777 77777777 77777777 77777777 77777777
77777777 77777777 77777777 88888888 88888888 88888888 88888888 88888888 88888888 88888888
88888888 88888888 88888888 88888888 88888888 88888888 88888888 88900000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
The full STAT/LY value state machine.
=====================================
The timing information below is with sprites disabled.
For STAT we only show the lower 3 bits and for LY only the lower 5 bits of the full
register. Each digit stands for 4 clock cycles (the smallest measurable unit on a
dmg or mgb). When the video hardware is switched on the LY register is set 0 and
the STAT mode is 0. The values for STAT and LY will change as follows:
STAT 000000000000000000003333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000
LY 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 line #0
^LY=LYC bit can get set here LY=LYC bit is reset here^
STAT 222222222222222222223333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000
LY 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112 line #1
:
:
STAT 222222222222222222223333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000
LY FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0 line #143
STAT 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
LY 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 line #144
:
:
STAT 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
LY 888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889 line #152
STAT 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110
LY 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 line #153
^
LY=LYC interrupt for 153 can get triggered here
STAT 222222222222222222223333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000
LY 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 line #0
STAT 222222222222222222223333333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000
LY 111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112 line #1
:
etc
Mappers used in the Game Boy
===========================
@ -429,6 +301,11 @@ space. This mapper uses 32KB sized banks.
#include "bus/gameboy/mbc.h"
#include "softlist.h"
#define DMG_FRAMES_PER_SECOND 59.732155
#define SGB_FRAMES_PER_SECOND 61.17
READ8_MEMBER(gb_state::gb_cart_r)
{
if (m_bios_disable && m_cartslot)
@ -545,75 +422,75 @@ WRITE8_MEMBER(megaduck_state::bank2_w)
}
static ADDRESS_MAP_START(gameboy_map, AS_PROGRAM, 8, gb_state )
static ADDRESS_MAP_START(gameboy_map, AS_PROGRAM, 8, gb_state)
ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(0x0000, 0x7fff) AM_READWRITE(gb_cart_r, gb_bank_w)
AM_RANGE(0x8000, 0x9fff) AM_DEVREADWRITE("lcd", gb_lcd_device, vram_r, vram_w) /* 8k VRAM */
AM_RANGE(0xa000, 0xbfff) AM_READWRITE(gb_ram_r, gb_ram_w) /* 8k switched RAM bank (cartridge) */
AM_RANGE(0xc000, 0xdfff) AM_RAM /* 8k low RAM */
AM_RANGE(0xe000, 0xfdff) AM_READWRITE(gb_echo_r, gb_echo_w) /* echo RAM */
AM_RANGE(0xfe00, 0xfeff) AM_DEVREADWRITE("lcd", gb_lcd_device, oam_r, oam_w) /* OAM RAM */
AM_RANGE(0xff00, 0xff0f) AM_READWRITE(gb_io_r, gb_io_w) /* I/O */
AM_RANGE(0xff10, 0xff26) AM_DEVREADWRITE("custom", gameboy_sound_device, sound_r, sound_w) /* sound registers */
AM_RANGE(0xff27, 0xff2f) AM_NOP /* unused */
AM_RANGE(0xff30, 0xff3f) AM_DEVREADWRITE("custom", gameboy_sound_device, wave_r, wave_w) /* Wave ram */
AM_RANGE(0xff40, 0xff7f) AM_DEVREAD("lcd", gb_lcd_device, video_r) AM_WRITE(gb_io2_w) /* Video controller & BIOS flip-flop */
AM_RANGE(0xff80, 0xfffe) AM_RAM /* High RAM */
AM_RANGE(0xffff, 0xffff) AM_READWRITE(gb_ie_r, gb_ie_w) /* Interrupt enable register */
AM_RANGE(0x8000, 0x9fff) AM_DEVREADWRITE("ppu", dmg_ppu_device, vram_r, vram_w) /* 8k VRAM */
AM_RANGE(0xa000, 0xbfff) AM_READWRITE(gb_ram_r, gb_ram_w) /* 8k switched RAM bank (cartridge) */
AM_RANGE(0xc000, 0xdfff) AM_RAM /* 8k low RAM */
AM_RANGE(0xe000, 0xfdff) AM_READWRITE(gb_echo_r, gb_echo_w)
AM_RANGE(0xfe00, 0xfeff) AM_DEVREADWRITE("ppu", dmg_ppu_device, oam_r, oam_w) /* OAM RAM */
AM_RANGE(0xff00, 0xff0f) AM_READWRITE(gb_io_r, gb_io_w) /* I/O */
AM_RANGE(0xff10, 0xff26) AM_DEVREADWRITE("apu", gameboy_sound_device, sound_r, sound_w) /* sound registers */
AM_RANGE(0xff27, 0xff2f) AM_NOP /* unused */
AM_RANGE(0xff30, 0xff3f) AM_DEVREADWRITE("apu", gameboy_sound_device, wave_r, wave_w) /* Wave ram */
AM_RANGE(0xff40, 0xff7f) AM_DEVREAD("ppu", dmg_ppu_device, video_r) AM_WRITE(gb_io2_w) /* Video controller & BIOS flip-flop */
AM_RANGE(0xff80, 0xfffe) AM_RAM /* High RAM */
AM_RANGE(0xffff, 0xffff) AM_READWRITE(gb_ie_r, gb_ie_w) /* Interrupt enable register */
ADDRESS_MAP_END
static ADDRESS_MAP_START(sgb_map, AS_PROGRAM, 8, gb_state )
static ADDRESS_MAP_START(sgb_map, AS_PROGRAM, 8, gb_state)
ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(0x0000, 0x7fff) AM_READWRITE(gb_cart_r, gb_bank_w)
AM_RANGE(0x8000, 0x9fff) AM_DEVREADWRITE("lcd", sgb_lcd_device, vram_r, vram_w) /* 8k VRAM */
AM_RANGE(0xa000, 0xbfff) AM_READWRITE(gb_ram_r, gb_ram_w) /* 8k switched RAM bank (cartridge) */
AM_RANGE(0xc000, 0xdfff) AM_RAM /* 8k low RAM */
AM_RANGE(0xe000, 0xfdff) AM_READWRITE(gb_echo_r, gb_echo_w) /* echo RAM */
AM_RANGE(0xfe00, 0xfeff) AM_DEVREADWRITE("lcd", sgb_lcd_device, oam_r, oam_w) /* OAM RAM */
AM_RANGE(0xff00, 0xff0f) AM_READWRITE(gb_io_r, sgb_io_w) /* I/O */
AM_RANGE(0xff10, 0xff26) AM_DEVREADWRITE("custom", gameboy_sound_device, sound_r, sound_w) /* sound registers */
AM_RANGE(0xff27, 0xff2f) AM_NOP /* unused */
AM_RANGE(0xff30, 0xff3f) AM_DEVREADWRITE("custom", gameboy_sound_device, wave_r, wave_w) /* Wave RAM */
AM_RANGE(0xff40, 0xff7f) AM_DEVREAD("lcd", sgb_lcd_device, video_r) AM_WRITE(gb_io2_w) /* Video controller & BIOS flip-flop */
AM_RANGE(0xff80, 0xfffe) AM_RAM /* High RAM */
AM_RANGE(0xffff, 0xffff) AM_READWRITE(gb_ie_r, gb_ie_w) /* Interrupt enable register */
AM_RANGE(0x8000, 0x9fff) AM_DEVREADWRITE("ppu", sgb_ppu_device, vram_r, vram_w) /* 8k VRAM */
AM_RANGE(0xa000, 0xbfff) AM_READWRITE(gb_ram_r, gb_ram_w) /* 8k switched RAM bank (cartridge) */
AM_RANGE(0xc000, 0xdfff) AM_RAM /* 8k low RAM */
AM_RANGE(0xe000, 0xfdff) AM_READWRITE(gb_echo_r, gb_echo_w)
AM_RANGE(0xfe00, 0xfeff) AM_DEVREADWRITE("ppu", sgb_ppu_device, oam_r, oam_w) /* OAM RAM */
AM_RANGE(0xff00, 0xff0f) AM_READWRITE(gb_io_r, sgb_io_w) /* I/O */
AM_RANGE(0xff10, 0xff26) AM_DEVREADWRITE("apu", gameboy_sound_device, sound_r, sound_w) /* sound registers */
AM_RANGE(0xff27, 0xff2f) AM_NOP /* unused */
AM_RANGE(0xff30, 0xff3f) AM_DEVREADWRITE("apu", gameboy_sound_device, wave_r, wave_w) /* Wave RAM */
AM_RANGE(0xff40, 0xff7f) AM_DEVREAD("ppu", sgb_ppu_device, video_r) AM_WRITE(gb_io2_w) /* Video controller & BIOS flip-flop */
AM_RANGE(0xff80, 0xfffe) AM_RAM /* High RAM */
AM_RANGE(0xffff, 0xffff) AM_READWRITE(gb_ie_r, gb_ie_w) /* Interrupt enable register */
ADDRESS_MAP_END
static ADDRESS_MAP_START(gbc_map, AS_PROGRAM, 8, gb_state )
static ADDRESS_MAP_START(gbc_map, AS_PROGRAM, 8, gb_state)
ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(0x0000, 0x7fff) AM_READWRITE(gbc_cart_r, gb_bank_w)
AM_RANGE(0x8000, 0x9fff) AM_DEVREADWRITE("lcd", cgb_lcd_device, vram_r, vram_w) /* 8k VRAM */
AM_RANGE(0xa000, 0xbfff) AM_READWRITE(gb_ram_r, gb_ram_w) /* 8k switched RAM bank (cartridge) */
AM_RANGE(0xc000, 0xcfff) AM_RAM /* 4k fixed RAM bank */
AM_RANGE(0xd000, 0xdfff) AM_RAMBANK("cgb_ram") /* 4k switched RAM bank */
AM_RANGE(0xe000, 0xfdff) AM_READWRITE(gb_echo_r, gb_echo_w) /* echo RAM */
AM_RANGE(0xfe00, 0xfeff) AM_DEVREADWRITE("lcd", cgb_lcd_device, oam_r, oam_w) /* OAM RAM */
AM_RANGE(0xff00, 0xff0f) AM_READWRITE(gb_io_r, gb_io_w) /* I/O */
AM_RANGE(0xff10, 0xff26) AM_DEVREADWRITE("custom", gameboy_sound_device, sound_r, sound_w) /* sound controller */
AM_RANGE(0xff27, 0xff2f) AM_NOP /* unused */
AM_RANGE(0xff30, 0xff3f) AM_DEVREADWRITE("custom", gameboy_sound_device, wave_r, wave_w) /* Wave RAM */
AM_RANGE(0xff40, 0xff7f) AM_READWRITE(gbc_io2_r, gbc_io2_w) /* Other I/O and video controller */
AM_RANGE(0xff80, 0xfffe) AM_RAM /* high RAM */
AM_RANGE(0xffff, 0xffff) AM_READWRITE(gb_ie_r, gb_ie_w) /* Interrupt enable register */
AM_RANGE(0x8000, 0x9fff) AM_DEVREADWRITE("ppu", cgb_ppu_device, vram_r, vram_w) /* 8k banked VRAM */
AM_RANGE(0xa000, 0xbfff) AM_READWRITE(gb_ram_r, gb_ram_w) /* 8k switched RAM bank (cartridge) */
AM_RANGE(0xc000, 0xcfff) AM_RAM /* 4k fixed RAM bank */
AM_RANGE(0xd000, 0xdfff) AM_RAMBANK("cgb_ram") /* 4k switched RAM bank */
AM_RANGE(0xe000, 0xfdff) AM_READWRITE(gb_echo_r, gb_echo_w)
AM_RANGE(0xfe00, 0xfeff) AM_DEVREADWRITE("ppu", cgb_ppu_device, oam_r, oam_w) /* OAM RAM */
AM_RANGE(0xff00, 0xff0f) AM_READWRITE(gb_io_r, gbc_io_w) /* I/O */
AM_RANGE(0xff10, 0xff26) AM_DEVREADWRITE("apu", gameboy_sound_device, sound_r, sound_w) /* sound controller */
AM_RANGE(0xff27, 0xff2f) AM_NOP /* unused */
AM_RANGE(0xff30, 0xff3f) AM_DEVREADWRITE("apu", gameboy_sound_device, wave_r, wave_w) /* Wave RAM */
AM_RANGE(0xff40, 0xff7f) AM_READWRITE(gbc_io2_r, gbc_io2_w) /* Other I/O and video controller */
AM_RANGE(0xff80, 0xfffe) AM_RAM /* high RAM */
AM_RANGE(0xffff, 0xffff) AM_READWRITE(gb_ie_r, gb_ie_w) /* Interrupt enable register */
ADDRESS_MAP_END
static ADDRESS_MAP_START(megaduck_map, AS_PROGRAM, 8, megaduck_state )
static ADDRESS_MAP_START(megaduck_map, AS_PROGRAM, 8, megaduck_state)
ADDRESS_MAP_UNMAP_HIGH
AM_RANGE(0x0000, 0x7fff) AM_READWRITE(cart_r, bank1_w)
AM_RANGE(0x8000, 0x9fff) AM_DEVREADWRITE("lcd", gb_lcd_device, vram_r, vram_w) /* 8k VRAM */
AM_RANGE(0xa000, 0xafff) AM_NOP /* unused? */
AM_RANGE(0x8000, 0x9fff) AM_DEVREADWRITE("ppu", dmg_ppu_device, vram_r, vram_w) /* 8k VRAM */
AM_RANGE(0xa000, 0xafff) AM_NOP /* unused? */
AM_RANGE(0xb000, 0xb000) AM_WRITE(bank2_w)
AM_RANGE(0xb001, 0xbfff) AM_NOP /* unused? */
AM_RANGE(0xc000, 0xfe9f) AM_RAM /* 8k low RAM, echo RAM */
AM_RANGE(0xfe00, 0xfeff) AM_DEVREADWRITE("lcd", gb_lcd_device, oam_r, oam_w) /* OAM RAM */
AM_RANGE(0xff00, 0xff0f) AM_READWRITE(gb_io_r, gb_io_w) /* I/O */
AM_RANGE(0xff10, 0xff1f) AM_READWRITE(megaduck_video_r, megaduck_video_w) /* video controller */
AM_RANGE(0xff20, 0xff2f) AM_READWRITE(megaduck_sound_r1, megaduck_sound_w1) /* sound controller pt1 */
AM_RANGE(0xff30, 0xff3f) AM_DEVREADWRITE("custom", gameboy_sound_device, wave_r, wave_w) /* wave ram */
AM_RANGE(0xff40, 0xff46) AM_READWRITE(megaduck_sound_r2, megaduck_sound_w2) /* sound controller pt2 */
AM_RANGE(0xff47, 0xff7f) AM_NOP /* unused */
AM_RANGE(0xff80, 0xfffe) AM_RAM /* high RAM */
AM_RANGE(0xffff, 0xffff) AM_READWRITE(gb_ie_r, gb_ie_w) /* interrupt enable register */
AM_RANGE(0xb001, 0xbfff) AM_NOP /* unused? */
AM_RANGE(0xc000, 0xfe9f) AM_RAM /* 8k/16k? RAM */
AM_RANGE(0xfe00, 0xfeff) AM_DEVREADWRITE("ppu", dmg_ppu_device, oam_r, oam_w) /* OAM RAM */
AM_RANGE(0xff00, 0xff0f) AM_READWRITE(gb_io_r, gb_io_w) /* I/O */
AM_RANGE(0xff10, 0xff1f) AM_READWRITE(megaduck_video_r, megaduck_video_w) /* video controller */
AM_RANGE(0xff20, 0xff2f) AM_READWRITE(megaduck_sound_r1, megaduck_sound_w1) /* sound controller pt1 */
AM_RANGE(0xff30, 0xff3f) AM_DEVREADWRITE("apu", gameboy_sound_device, wave_r, wave_w) /* wave ram */
AM_RANGE(0xff40, 0xff46) AM_READWRITE(megaduck_sound_r2, megaduck_sound_w2) /* sound controller pt2 */
AM_RANGE(0xff47, 0xff7f) AM_NOP /* unused */
AM_RANGE(0xff80, 0xfffe) AM_RAM /* high RAM */
AM_RANGE(0xffff, 0xffff) AM_READWRITE(gb_ie_r, gb_ie_w) /* interrupt enable register */
ADDRESS_MAP_END
static GFXDECODE_START( gb )
@ -758,7 +635,7 @@ static MACHINE_CONFIG_START( gameboy, gb_state )
MCFG_SCREEN_ADD("screen", LCD)
MCFG_SCREEN_REFRESH_RATE(DMG_FRAMES_PER_SECOND)
MCFG_SCREEN_VBLANK_TIME(0)
MCFG_SCREEN_UPDATE_DEVICE("lcd", gb_lcd_device, screen_update)
MCFG_SCREEN_UPDATE_DEVICE("ppu", dmg_ppu_device, screen_update)
MCFG_SCREEN_PALETTE("palette")
MCFG_DEFAULT_LAYOUT(layout_lcd)
@ -770,11 +647,11 @@ static MACHINE_CONFIG_START( gameboy, gb_state )
MCFG_PALETTE_ADD("palette", 4)
MCFG_PALETTE_INIT_OWNER(gb_state,gb)
MCFG_GB_LCD_DMG_ADD("lcd")
MCFG_DMG_PPU_ADD("ppu", "maincpu")
/* sound hardware */
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
MCFG_SOUND_ADD("custom", GAMEBOY, 0)
MCFG_SOUND_ADD("apu", DMG_APU, XTAL_4_194304Mhz)
MCFG_SOUND_ROUTE(0, "lspeaker", 0.50)
MCFG_SOUND_ROUTE(1, "rspeaker", 0.50)
@ -785,13 +662,11 @@ static MACHINE_CONFIG_START( gameboy, gb_state )
MCFG_SOFTWARE_LIST_COMPATIBLE_ADD("gbc_list","gbcolor")
MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( supergb, gameboy )
static MACHINE_CONFIG_START( supergb, gb_state )
/* basic machine hardware */
MCFG_CPU_REPLACE("maincpu", LR35902, 4295454) /* 4.295454 MHz, derived from SNES xtal */
MCFG_CPU_ADD("maincpu", LR35902, 4295454) /* 4.295454 MHz, derived from SNES xtal */
MCFG_CPU_PROGRAM_MAP(sgb_map)
MCFG_CPU_MODIFY("maincpu")
MCFG_LR35902_TIMER_CB( WRITE8(gb_state, gb_timer_callback ) )
MCFG_LR35902_HALT_BUG
@ -799,29 +674,41 @@ static MACHINE_CONFIG_DERIVED( supergb, gameboy )
MCFG_MACHINE_RESET_OVERRIDE(gb_state, sgb)
/* video hardware */
MCFG_DEFAULT_LAYOUT(layout_horizont) /* runs on a TV, not an LCD */
MCFG_SCREEN_ADD("screen", LCD)
MCFG_SCREEN_REFRESH_RATE(SGB_FRAMES_PER_SECOND)
MCFG_SCREEN_VBLANK_TIME(0)
MCFG_SCREEN_UPDATE_DEVICE("ppu", dmg_ppu_device, screen_update)
MCFG_SCREEN_PALETTE("palette")
MCFG_SCREEN_MODIFY("screen")
MCFG_DEFAULT_LAYOUT(layout_horizont) /* runs on a TV, not an LCD */
MCFG_SCREEN_SIZE(32*8, 28*8)
MCFG_SCREEN_VISIBLE_AREA(0*8, 32*8-1, 0*8, 28*8-1)
MCFG_PALETTE_MODIFY("palette")
MCFG_PALETTE_ENTRIES(32768)
MCFG_GFXDECODE_ADD("gfxdecode", "palette", gb)
MCFG_PALETTE_ADD("palette", 32768)
MCFG_PALETTE_INIT_OWNER(gb_state,sgb)
MCFG_DEVICE_REMOVE("lcd")
MCFG_GB_LCD_SGB_ADD("lcd")
MCFG_SGB_PPU_ADD("ppu", "maincpu")
/* sound hardware */
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
MCFG_SOUND_ADD("apu", DMG_APU, 4295454)
MCFG_SOUND_ROUTE(0, "lspeaker", 0.50)
MCFG_SOUND_ROUTE(1, "rspeaker", 0.50)
/* cartslot */
MCFG_GB_CARTRIDGE_ADD("gbslot", gb_cart, nullptr)
MCFG_SOFTWARE_LIST_ADD("cart_list","gameboy")
MCFG_SOFTWARE_LIST_COMPATIBLE_ADD("gbc_list","gbcolor")
MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( supergb2, gameboy )
/* basic machine hardware */
MCFG_CPU_MODIFY("maincpu")
MCFG_CPU_PROGRAM_MAP(sgb_map)
MCFG_CPU_MODIFY("maincpu")
MCFG_LR35902_TIMER_CB( WRITE8(gb_state, gb_timer_callback ) )
MCFG_LR35902_HALT_BUG
MCFG_MACHINE_START_OVERRIDE(gb_state, sgb)
MCFG_MACHINE_RESET_OVERRIDE(gb_state, sgb)
@ -836,18 +723,19 @@ static MACHINE_CONFIG_DERIVED( supergb2, gameboy )
MCFG_PALETTE_ENTRIES(32768)
MCFG_PALETTE_INIT_OWNER(gb_state,sgb)
MCFG_DEVICE_REMOVE("lcd")
MCFG_GB_LCD_SGB_ADD("lcd")
MCFG_DEVICE_REMOVE("ppu")
MCFG_SGB_PPU_ADD("ppu", "maincpu")
MACHINE_CONFIG_END
static MACHINE_CONFIG_DERIVED( gbpocket, gameboy )
/* video hardware */
MCFG_PALETTE_MODIFY("palette")
MCFG_PALETTE_INIT_OWNER(gb_state,gbp)
MCFG_DEVICE_REMOVE("lcd")
MCFG_GB_LCD_MGB_ADD("lcd")
MCFG_DEVICE_REMOVE("ppu")
MCFG_MGB_PPU_ADD("ppu", "maincpu")
MACHINE_CONFIG_END
static MACHINE_CONFIG_START( gbcolor, gb_state )
@ -864,7 +752,7 @@ static MACHINE_CONFIG_START( gbcolor, gb_state )
MCFG_SCREEN_ADD("screen", LCD)
MCFG_SCREEN_REFRESH_RATE(DMG_FRAMES_PER_SECOND)
MCFG_SCREEN_VBLANK_TIME(0)
MCFG_SCREEN_UPDATE_DEVICE("lcd", gb_lcd_device, screen_update)
MCFG_SCREEN_UPDATE_DEVICE("ppu", dmg_ppu_device, screen_update)
MCFG_SCREEN_PALETTE("palette")
MCFG_DEFAULT_LAYOUT(layout_lcd)
@ -877,11 +765,11 @@ static MACHINE_CONFIG_START( gbcolor, gb_state )
MCFG_PALETTE_ADD("palette", 32768)
MCFG_PALETTE_INIT_OWNER(gb_state,gbc)
MCFG_GB_LCD_CGB_ADD("lcd")
MCFG_CGB_PPU_ADD("ppu", "maincpu")
/* sound hardware */
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
MCFG_SOUND_ADD("custom", GAMEBOY, 0)
MCFG_SOUND_ADD("apu", CGB04_APU, XTAL_4_194304Mhz)
MCFG_SOUND_ROUTE(0, "lspeaker", 0.50)
MCFG_SOUND_ROUTE(1, "rspeaker", 0.50)
@ -899,7 +787,7 @@ MACHINE_CONFIG_END
static MACHINE_CONFIG_START( megaduck, megaduck_state )
/* basic machine hardware */
MCFG_CPU_ADD("maincpu", LR35902, 4194304) /* 4.194304 MHz */
MCFG_CPU_ADD("maincpu", LR35902, XTAL_4_194304Mhz) /* 4.194304 MHz */
MCFG_CPU_PROGRAM_MAP(megaduck_map)
MCFG_LR35902_TIMER_CB( WRITE8(gb_state, gb_timer_callback ) )
MCFG_LR35902_HALT_BUG
@ -913,7 +801,7 @@ static MACHINE_CONFIG_START( megaduck, megaduck_state )
MCFG_MACHINE_START_OVERRIDE(megaduck_state, megaduck)
MCFG_MACHINE_RESET_OVERRIDE(megaduck_state, megaduck)
MCFG_SCREEN_UPDATE_DEVICE("lcd", gb_lcd_device, screen_update)
MCFG_SCREEN_UPDATE_DEVICE("ppu", dmg_ppu_device, screen_update)
MCFG_SCREEN_SIZE(20*8, 18*8)
MCFG_SCREEN_VISIBLE_AREA(0*8, 20*8-1, 0*8, 18*8-1)
@ -923,11 +811,11 @@ static MACHINE_CONFIG_START( megaduck, megaduck_state )
MCFG_PALETTE_ADD("palette", 4)
MCFG_PALETTE_INIT_OWNER(megaduck_state,megaduck)
MCFG_GB_LCD_DMG_ADD("lcd")
MCFG_DMG_PPU_ADD("ppu", "maincpu")
/* sound hardware */
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
MCFG_SOUND_ADD("custom", GAMEBOY, 0)
MCFG_SOUND_ADD("apu", DMG_APU, XTAL_4_194304Mhz)
MCFG_SOUND_ROUTE(0, "lspeaker", 0.50)
MCFG_SOUND_ROUTE(1, "rspeaker", 0.50)

View File

@ -1408,7 +1408,7 @@ static MACHINE_CONFIG_START( gbadv, gba_state )
MCFG_GBA_LCD_DMA_VBLANK(WRITELINE(gba_state, dma_vblank_callback))
MCFG_SPEAKER_STANDARD_STEREO("spkleft", "spkright")
MCFG_SOUND_ADD("custom", GAMEBOY, 0)
MCFG_SOUND_ADD("custom", CGB04_APU, XTAL_16_777216MHz/4)
MCFG_SOUND_ROUTE(0, "spkleft", 0.50)
MCFG_SOUND_ROUTE(1, "spkright", 0.50)
MCFG_SOUND_ADD("direct_a_left", DAC, 0) // GBA direct sound A left

View File

@ -1328,7 +1328,7 @@ static MACHINE_CONFIG_START( vgmplay, vgmplay_state )
MCFG_SOUND_ROUTE(0, "lspeaker", 1)
MCFG_SOUND_ROUTE(1, "rspeaker", 1)
MCFG_SOUND_ADD("dmg", GAMEBOY, 0)
MCFG_SOUND_ADD("dmg", DMG_APU, XTAL_4_194304Mhz)
MCFG_SOUND_ROUTE(0, "lspeaker", 1)
MCFG_SOUND_ROUTE(1, "rspeaker", 1)

View File

@ -15,33 +15,6 @@
#include "machine/ram.h"
#include "video/gb_lcd.h"
/* Interrupts */
#define VBL_INT 0 /* V-Blank */
#define LCD_INT 1 /* LCD Status */
#define TIM_INT 2 /* Timer */
#define SIO_INT 3 /* Serial I/O */
#define EXT_INT 4 /* Joypad */
#ifdef TIMER
#undef TIMER
#endif
/* Cartridge types */
#define CART_RAM 0x01 /* Cartridge has RAM */
#define BATTERY 0x02 /* Cartridge has a battery to save RAM */
#define TIMER 0x04 /* Cartridge has a real-time-clock (MBC3 only) */
#define RUMBLE 0x08 /* Cartridge has a rumble motor (MBC5 only) */
#define SRAM 0x10 /* Cartridge has SRAM */
#define UNKNOWN 0x80 /* Cartridge is of an unknown type */
#define DMG_FRAMES_PER_SECOND 59.732155
#define SGB_FRAMES_PER_SECOND 61.17
#define MAX_ROMBANK 512
#define MAX_RAMBANK 256
class gb_state : public driver_device
{
@ -50,13 +23,13 @@ public:
: driver_device(mconfig, type, tag),
m_cartslot(*this, "gbslot"),
m_maincpu(*this, "maincpu"),
m_custom(*this, "custom"),
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_lcd(*this, "lcd") { }
m_ppu(*this, "ppu") { }
//gb_state driver_data;
UINT8 m_gb_io[0x10];
@ -69,8 +42,9 @@ public:
UINT8 m_reloading;
/* Serial I/O related */
UINT16 m_internal_serial_clock;
UINT16 m_internal_serial_frequency;
UINT32 m_sio_count; /* Serial I/O counter */
emu_timer *m_gb_serial_timer;
/* SGB variables */
INT8 m_sgb_packets;
@ -86,7 +60,7 @@ public:
UINT8 *m_gbc_rammap[8]; /* (CGB) Addresses of internal RAM banks */
UINT8 m_gbc_rambank; /* (CGB) Current CGB RAM bank */
int m_bios_disable;
bool m_bios_disable;
DECLARE_WRITE8_MEMBER(gb_io_w);
DECLARE_WRITE8_MEMBER(gb_io2_w);
@ -94,6 +68,7 @@ public:
DECLARE_READ8_MEMBER(gb_ie_r);
DECLARE_WRITE8_MEMBER(gb_ie_w);
DECLARE_READ8_MEMBER(gb_io_r);
DECLARE_WRITE8_MEMBER(gbc_io_w);
DECLARE_WRITE8_MEMBER(gbc_io2_w);
DECLARE_READ8_MEMBER(gbc_io2_r);
DECLARE_PALETTE_INIT(gb);
@ -104,7 +79,6 @@ public:
DECLARE_MACHINE_START(gbc);
DECLARE_MACHINE_RESET(gbc);
DECLARE_PALETTE_INIT(gbc);
TIMER_CALLBACK_MEMBER(gb_serial_timer_proc);
DECLARE_WRITE8_MEMBER(gb_timer_callback);
DECLARE_READ8_MEMBER(gb_cart_r);
@ -118,18 +92,19 @@ public:
protected:
required_device<lr35902_cpu_device> m_maincpu;
required_device<gameboy_sound_device> m_custom;
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<gb_lcd_device> m_lcd;
required_device<dmg_ppu_device> m_ppu;
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();

View File

@ -80,36 +80,27 @@ TODO:
13/6/2005 WP - Added support for bootstrap rom banking.
***************************************************************************/
#define __MACHINE_GB_C
#include "emu.h"
#include "includes/gb.h"
#define ENABLE_LOGGING 0
#define LOG(x) do { if (ENABLE_LOGGING) logerror x; } while(0)
/* RAM layout defines */
#define CGB_START_VRAM_BANKS 0x0000
#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 DIVREG m_gb_io[0x04] /* Divider 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 */
/*
Prototypes
*/
#ifdef MAME_DEBUG
/* #define V_GENERAL*/ /* Display general debug information */
/* #define V_BANK*/ /* Display bank switching debug information */
#endif
//-------------------------
// handle save state
//-------------------------
@ -160,29 +151,31 @@ void gb_state::gb_init_regs()
void gb_state::gb_init()
{
address_space &space = m_maincpu->space(AS_PROGRAM);
m_custom->sound_w(space, 0x16, 0x00); /* Initialize sound hardware */
m_apu->sound_w(space, 0x16, 0x00); /* Initialize sound hardware */
m_divcount = 0;
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()
{
/* Allocate the serial timer, and disable it */
m_gb_serial_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gb_state::gb_serial_timer_proc),this));
m_gb_serial_timer->enable( 0 );
save_gb_base();
}
MACHINE_START_MEMBER(gb_state,gbc)
{
/* Allocate the serial timer, and disable it */
m_gb_serial_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gb_state::gb_serial_timer_proc),this));
m_gb_serial_timer->enable( 0 );
for (int i = 0; i < 8; i++)
m_gbc_rammap[i] = m_ram->pointer() + CGB_START_RAM_BANKS + i * 0x1000;
@ -195,15 +188,11 @@ MACHINE_START_MEMBER(gb_state,sgb)
{
m_sgb_packets = -1;
/* Allocate the serial timer, and disable it */
m_gb_serial_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gb_state::gb_serial_timer_proc),this));
m_gb_serial_timer->enable( 0 );
save_gb_base();
save_sgb_only();
if (m_cartslot && m_cartslot->get_sgb_hack()) {
dynamic_cast<sgb_lcd_device*>(m_lcd.target())->set_sgb_hack(TRUE);
dynamic_cast<sgb_ppu_device*>(m_ppu.target())->set_sgb_hack(TRUE);
}
}
@ -212,9 +201,7 @@ void gb_state::machine_reset()
gb_init();
/* Enable BIOS rom */
m_bios_disable = 0;
m_divcount = 0x0004;
m_bios_disable = false;
}
MACHINE_RESET_MEMBER(gb_state,gbc)
@ -224,7 +211,7 @@ MACHINE_RESET_MEMBER(gb_state,gbc)
gb_init_regs();
/* Enable BIOS rom */
m_bios_disable = 0;
m_bios_disable = false;
for (auto & elem : m_gbc_rammap)
memset(elem, 0, 0x1000);
@ -237,9 +224,7 @@ MACHINE_RESET_MEMBER(gb_state,sgb)
gb_init_regs();
/* Enable BIOS rom */
m_bios_disable = 0;
m_divcount = 0x0004;
m_bios_disable = false;
}
@ -259,37 +244,42 @@ WRITE8_MEMBER(gb_state::gb_io_w)
case 0x01: /* SB - Serial transfer data */
break;
case 0x02: /* SC - SIO control */
switch( data & 0x81 )
switch (data & 0x81)
{
case 0x00:
case 0x01:
case 0x80: /* enabled & external clock */
m_sio_count = 0;
case 0x80: /* enabled & external clock */
m_sio_count = 16;
break;
case 0x81: /* enabled & internal clock */
SIODATA = 0xFF;
m_sio_count = 8;
m_gb_serial_timer->adjust(m_maincpu->cycles_to_attotime(512), 0, m_maincpu->cycles_to_attotime(512));
m_gb_serial_timer->enable( 1 );
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 */
if ( m_divcount >= 16 )
/* 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 ( m_reloading && ( m_divcount & ( m_shift_cycles - 1 ) ) == 4 )
if ((TIMEFRQ & 0x04) && TIMECNT == TIMEMOD && (m_divcount & (m_shift_cycles - 1)) == 4)
{
data = TIMECNT;
data = TIMEMOD;
}
break;
case 0x06: /* TMA - Timer module */
/* Check if the counter is being reloaded in this cycle */
if ( m_reloading && ( m_divcount & ( m_shift_cycles - 1 ) ) == 4 )
if ((TIMEFRQ & 0x04) && TIMECNT == TIMEMOD && (m_divcount & (m_shift_cycles - 1)) == 4)
{
TIMECNT = data;
}
@ -297,10 +287,10 @@ WRITE8_MEMBER(gb_state::gb_io_w)
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 ) ) )
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 ) )
if ((m_divcount & (m_shift_cycles - 1)) >= (m_shift_cycles >> 1))
{
gb_timer_increment();
}
@ -309,8 +299,10 @@ WRITE8_MEMBER(gb_state::gb_io_w)
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 );
m_maincpu->set_if(data);
break;
}
@ -322,11 +314,10 @@ WRITE8_MEMBER(gb_state::gb_io2_w)
if (offset == 0x10)
{
/* disable BIOS ROM */
m_bios_disable = 1;
//printf("here again?\n");
m_bios_disable = true;
}
else
m_lcd->video_w(space, offset, data);
m_ppu->video_w(space, offset, data);
}
#ifdef MAME_DEBUG
@ -371,7 +362,7 @@ WRITE8_MEMBER(gb_state::sgb_io_w)
{
UINT8 *sgb_data = m_sgb_data;
switch( offset )
switch (offset)
{
case 0x00:
switch (data & 0x30)
@ -411,15 +402,13 @@ WRITE8_MEMBER(gb_state::sgb_io_w)
case 0x20: /* data false */
if (m_sgb_rest)
{
if( m_sgb_bytecount == 16 && m_sgb_packets == -1 )
if (m_sgb_bytecount == 16 && m_sgb_packets == -1)
{
#ifdef MAME_DEBUG
logerror("SGB: %s (%02X) pkts: %d data: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
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
sgb_data[12], sgb_data[13], sgb_data[14], sgb_data[15]));
m_sgb_packets = sgb_data[0] & 0x07;
m_sgb_start = 0;
}
@ -434,14 +423,14 @@ WRITE8_MEMBER(gb_state::sgb_io_w)
m_sgb_controller_mode = 2;
break;
default:
dynamic_cast<sgb_lcd_device*>(m_lcd.target())->sgb_io_write_pal(sgb_data[0] >> 3, &sgb_data[0]);
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 )
if (m_sgb_start)
{
sgb_data[m_sgb_bytecount] >>= 1;
m_sgb_bitcount++;
@ -469,14 +458,14 @@ WRITE8_MEMBER(gb_state::sgb_io_w)
JOYPAD = 0x3F;
/* Hack to let cartridge know it's running on an SGB */
if ( (sgb_data[0] >> 3) == 0x1F )
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( space, offset, data );
gb_io_w(space, offset, data);
return;
}
@ -491,7 +480,7 @@ READ8_MEMBER(gb_state::gb_ie_r)
WRITE8_MEMBER(gb_state::gb_ie_w)
{
m_maincpu->set_ie( data & 0x1F );
m_maincpu->set_ie(data);
}
/* IO read */
@ -500,7 +489,8 @@ READ8_MEMBER(gb_state::gb_io_r)
switch(offset)
{
case 0x04:
return ( m_divcount >> 8 ) & 0xFF;
LOG(("read DIV, divcount = %04x\n", m_divcount));
return (m_divcount >> 8) & 0xFF;
case 0x00:
case 0x01:
case 0x02:
@ -511,39 +501,55 @@ READ8_MEMBER(gb_state::gb_io_r)
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:
/* It seems unsupported registers return 0xFF */
/* Unsupported registers return 0xFF */
return 0xFF;
}
}
TIMER_CALLBACK_MEMBER(gb_state::gb_serial_timer_proc)
/* Called when 512 internal cycles are passed */
void gb_state::gb_serial_timer_tick()
{
/* Shift in a received bit */
SIODATA = (SIODATA << 1) | 0x01;
/* Decrement number of handled bits */
m_sio_count--;
/* If all bits done, stop timer and trigger interrupt */
if ( ! m_sio_count )
if (SIOCONT & 0x80)
{
SIOCONT &= 0x7F;
m_gb_serial_timer->enable( 0 );
m_maincpu->set_input_line(SIO_INT, ASSERT_LINE);
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 &= 0x7F;
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 )
if (m_triggering_irq)
{
m_triggering_irq = 0;
if ( TIMECNT == 0 )
if (TIMECNT == 0)
{
TIMECNT = TIMEMOD;
m_maincpu->set_input_line(TIM_INT, ASSERT_LINE);
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;
}
}
@ -553,49 +559,75 @@ void gb_state::gb_timer_increment()
{
gb_timer_check_irq();
LOG(("increment timer\n"));
TIMECNT += 1;
if ( TIMECNT == 0 )
if (TIMECNT == 0)
{
m_triggering_irq = 1;
}
}
WRITE8_MEMBER( gb_state::gb_timer_callback )
// This gets called while the cpu is executing instructions to keep the timer state in sync
WRITE8_MEMBER(gb_state::gb_timer_callback)
{
UINT16 old_gb_divcount = m_divcount;
UINT16 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 )
if (TIMEFRQ & 0x04)
{
UINT16 old_count = old_gb_divcount >> m_shift;
UINT16 new_count = m_divcount >> m_shift;
if ( data > m_shift_cycles )
if (data > m_shift_cycles)
{
gb_timer_increment();
old_count++;
}
if ( new_count != old_count )
if (new_count != old_count)
{
gb_timer_increment();
if (new_count << m_shift < m_divcount)
{
gb_timer_check_irq();
}
}
if ( new_count << m_shift < m_divcount )
{
gb_timer_check_irq();
}
}
if ((m_internal_serial_clock ^ old_internal_serial_clock) & m_internal_serial_frequency)
{
gb_serial_timer_tick();
}
}
WRITE8_MEMBER(gb_state::gbc_io_w)
{
gb_io_w(space, offset, data);
// On CGB the internal serial transfer clock is selectable
if (offset == 0x02)
{
m_internal_serial_frequency = ((data & 0x02) ? 16 : 512) / 2;
SIOCONT = (SIOCONT & ~0x02) | (data & 0x02);
}
}
WRITE8_MEMBER(gb_state::gbc_io2_w)
{
switch( offset )
switch (offset)
{
case 0x0D: /* KEY1 - Prepare speed switch */
m_maincpu->set_speed(data);
return;
case 0x10: /* BFF - Bios disable */
m_bios_disable = 1;
m_bios_disable = true;
return;
case 0x16: /* RP - Infrared port */
break;
@ -608,12 +640,12 @@ WRITE8_MEMBER(gb_state::gbc_io2_w)
default:
break;
}
m_lcd->video_w(space, offset, data);
m_ppu->video_w(space, offset, data);
}
READ8_MEMBER(gb_state::gbc_io2_r)
{
switch( offset )
switch (offset)
{
case 0x0D: /* KEY1 */
return m_maincpu->get_speed();
@ -624,7 +656,7 @@ READ8_MEMBER(gb_state::gbc_io2_r)
default:
break;
}
return m_lcd->video_r(space, offset);
return m_ppu->video_r(space, offset);
}
/****************************************************************************
@ -635,10 +667,6 @@ READ8_MEMBER(gb_state::gbc_io2_r)
MACHINE_START_MEMBER(megaduck_state,megaduck)
{
/* Allocate the serial timer, and disable it */
m_gb_serial_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gb_state::gb_serial_timer_proc),this));
m_gb_serial_timer->enable( 0 );
save_gb_base();
}
@ -647,7 +675,7 @@ MACHINE_RESET_MEMBER(megaduck_state,megaduck)
/* We may have to add some more stuff here, if not then it can be merged back into gb */
gb_init();
m_bios_disable = 1;
m_bios_disable = true;
}
/*
@ -694,8 +722,8 @@ READ8_MEMBER(megaduck_state::megaduck_video_r)
{
offset ^= 0x0C;
}
data = m_lcd->video_r(space, offset);
if ( offset )
data = m_ppu->video_r(space, offset);
if (offset)
return data;
return BITSWAP8(data,7,0,5,4,6,3,2,1);
}
@ -710,7 +738,7 @@ WRITE8_MEMBER(megaduck_state::megaduck_video_w)
{
offset ^= 0x0C;
}
m_lcd->video_w(space, offset, data);
m_ppu->video_w(space, offset, data);
}
/* Map megaduck audio offset to game boy audio offsets */
@ -721,14 +749,14 @@ static const UINT8 megaduck_sound_offsets[16] = { 0, 2, 1, 3, 4, 6, 5, 7, 8, 9,
WRITE8_MEMBER(megaduck_state::megaduck_sound_w1)
{
if ((offset == 0x01) || (offset == 0x07))
m_custom->sound_w(space, megaduck_sound_offsets[offset], ((data & 0x0f)<<4) | ((data & 0xf0)>>4));
m_apu->sound_w(space, megaduck_sound_offsets[offset], ((data & 0x0f)<<4) | ((data & 0xf0)>>4));
else
m_custom->sound_w(space, megaduck_sound_offsets[offset], data);
m_apu->sound_w(space, megaduck_sound_offsets[offset], data);
}
READ8_MEMBER(megaduck_state::megaduck_sound_r1)
{
UINT8 data = m_custom->sound_r(space, megaduck_sound_offsets[offset]);
UINT8 data = m_apu->sound_r(space, megaduck_sound_offsets[offset]);
if ((offset == 0x01) || (offset == 0x07))
return ((data & 0x0f)<<4) | ((data & 0xf0)>>4);
else
@ -738,14 +766,14 @@ READ8_MEMBER(megaduck_state::megaduck_sound_r1)
WRITE8_MEMBER(megaduck_state::megaduck_sound_w2)
{
if ((offset == 0x01) || (offset == 0x02))
m_custom->sound_w(space, 0x10 + megaduck_sound_offsets[offset], ((data & 0x0f)<<4) | ((data & 0xf0)>>4));
m_apu->sound_w(space, 0x10 + megaduck_sound_offsets[offset], ((data & 0x0f)<<4) | ((data & 0xf0)>>4));
else
m_custom->sound_w(space, 0x10 + megaduck_sound_offsets[offset], data);
m_apu->sound_w(space, 0x10 + megaduck_sound_offsets[offset], data);
}
READ8_MEMBER(megaduck_state::megaduck_sound_r2)
{
UINT8 data = m_custom->sound_r(space, 0x10 + megaduck_sound_offsets[offset]);
UINT8 data = m_apu->sound_r(space, 0x10 + megaduck_sound_offsets[offset]);
if ((offset == 0x01) || (offset == 0x02))
return ((data & 0x0f)<<4) | ((data & 0xf0)>>4);
else