gba: decouple video device from main machine state using callbacks

This commit is contained in:
Giuseppe Gorgoglione 2016-08-17 14:23:25 +02:00
parent a942ce2004
commit 0f4337d6a6
4 changed files with 132 additions and 60 deletions

View File

@ -15,8 +15,6 @@
#include "gba_lcd.h"
#include "includes/gba.h" // this is a dependency from src/devices to src/mame which is very bad and should be fixed
/* LCD I/O Registers */
#define DISPCNT HWLO(0x000) /* 0x4000000 2 R/W LCD Control */
#define GRNSWAP HWHI(0x000) /* 0x4000002 2 R/W Undocumented - Green Swap */
@ -253,8 +251,13 @@ static inline UINT32 decrease_brightness(UINT32 color, int coeff_)
const device_type GBA_LCD = &device_creator<gba_lcd_device>;
gba_lcd_device::gba_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_t(mconfig, GBA_LCD, "GBA LCD", tag, owner, clock, "gba_lcd", __FILE__),
device_video_interface(mconfig, *this)
: device_t(mconfig, GBA_LCD, "GBA LCD", tag, owner, clock, "gba_lcd", __FILE__)
, device_video_interface(mconfig, *this)
, m_int_hblank_cb(*this)
, m_int_vblank_cb(*this)
, m_int_vcount_cb(*this)
, m_dma_hblank_cb(*this)
, m_dma_vblank_cb(*this)
{
}
@ -2216,12 +2219,14 @@ TIMER_CALLBACK_MEMBER(gba_lcd_device::perform_hbl)
{
draw_scanline(scanline);
machine().driver_data<gba_state>()->request_dma(gba_state::dma_start_timing::hblank);
if (!m_dma_hblank_cb.isnull())
m_dma_hblank_cb(ASSERT_LINE);
}
if ((DISPSTAT & DISPSTAT_HBL_IRQ_EN ) != 0)
{
machine().driver_data<gba_state>()->request_irq(INT_HBL);
if (!m_int_hblank_cb.isnull())
m_int_hblank_cb(ASSERT_LINE);
}
DISPSTAT_SET(DISPSTAT_HBL);
@ -2246,10 +2251,12 @@ TIMER_CALLBACK_MEMBER(gba_lcd_device::perform_scan)
{
if (DISPSTAT & DISPSTAT_VBL_IRQ_EN)
{
machine().driver_data<gba_state>()->request_irq(INT_VBL);
if (!m_int_vblank_cb.isnull())
m_int_vblank_cb(ASSERT_LINE);
}
machine().driver_data<gba_state>()->request_dma(gba_state::dma_start_timing::vblank);
if (!m_dma_vblank_cb.isnull())
m_dma_vblank_cb(ASSERT_LINE);
}
}
else
@ -2264,7 +2271,8 @@ TIMER_CALLBACK_MEMBER(gba_lcd_device::perform_scan)
if (DISPSTAT & DISPSTAT_VCNT_IRQ_EN)
{
machine().driver_data<gba_state>()->request_irq(INT_VCNT);
if (!m_int_vcount_cb.isnull())
m_int_vcount_cb(ASSERT_LINE);
}
}
@ -2294,6 +2302,13 @@ UINT32 gba_lcd_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap
void gba_lcd_device::device_start()
{
/* resolve callbacks */
m_int_hblank_cb.resolve();
m_int_vblank_cb.resolve();
m_int_vcount_cb.resolve();
m_dma_hblank_cb.resolve();
m_dma_vblank_cb.resolve();
m_pram = make_unique_clear<UINT32[]>(0x400 / 4);
m_vram = make_unique_clear<UINT32[]>(0x18000 / 4);
m_oam = make_unique_clear<UINT32[]>(0x400 / 4);

View File

@ -34,6 +34,21 @@ extern const device_type GBA_LCD;
#define MCFG_GBA_LCD_ADD(_tag) \
MCFG_DEVICE_ADD(_tag, GBA_LCD, 0)
#define MCFG_GBA_LCD_INT_HBLANK(_devcb) \
devcb = &gba_lcd_device::set_int_hblank_callback(*device, DEVCB_##_devcb);
#define MCFG_GBA_LCD_INT_VBLANK(_devcb) \
devcb = &gba_lcd_device::set_int_vblank_callback(*device, DEVCB_##_devcb);
#define MCFG_GBA_LCD_INT_VCOUNT(_devcb) \
devcb = &gba_lcd_device::set_int_vcount_callback(*device, DEVCB_##_devcb);
#define MCFG_GBA_LCD_DMA_HBLANK(_devcb) \
devcb = &gba_lcd_device::set_dma_hblank_callback(*device, DEVCB_##_devcb);
#define MCFG_GBA_LCD_DMA_VBLANK(_devcb) \
devcb = &gba_lcd_device::set_dma_vblank_callback(*device, DEVCB_##_devcb);
//**************************************************************************
// TYPE DEFINITIONS
@ -84,6 +99,31 @@ public:
TIMER_CALLBACK_MEMBER(perform_hbl);
TIMER_CALLBACK_MEMBER(perform_scan);
template<class _Object> static devcb_base &set_int_hblank_callback(device_t &device, _Object object)
{
return downcast<gba_lcd_device &>(device).m_int_hblank_cb.set_callback(object);
}
template<class _Object> static devcb_base &set_int_vblank_callback(device_t &device, _Object object)
{
return downcast<gba_lcd_device &>(device).m_int_vblank_cb.set_callback(object);
}
template<class _Object> static devcb_base &set_int_vcount_callback(device_t &device, _Object object)
{
return downcast<gba_lcd_device &>(device).m_int_vcount_cb.set_callback(object);
}
template<class _Object> static devcb_base &set_dma_hblank_callback(device_t &device, _Object object)
{
return downcast<gba_lcd_device &>(device).m_dma_hblank_cb.set_callback(object);
}
template<class _Object> static devcb_base &set_dma_vblank_callback(device_t &device, _Object object)
{
return downcast<gba_lcd_device &>(device).m_dma_vblank_cb.set_callback(object);
}
protected:
// device-level overrides
virtual void device_start() override;
@ -101,6 +141,12 @@ private:
inline int is_in_window(int x, int window);
void draw_scanline(int y);
devcb_write_line m_int_hblank_cb; /* H-Blank interrupt callback function */
devcb_write_line m_int_vblank_cb; /* V-Blank interrupt callback function */
devcb_write_line m_int_vcount_cb; /* V-Counter Match interrupt callback function */
devcb_write_line m_dma_hblank_cb; /* H-Blank DMA request callback function */
devcb_write_line m_dma_vblank_cb; /* V-Blank DMA request callback function */
std::unique_ptr<UINT32[]> m_pram;
std::unique_ptr<UINT32[]> m_vram;
std::unique_ptr<UINT32[]> m_oam;

View File

@ -99,6 +99,21 @@
#define IF_SET(val) HWHI_SET(0x200, val)
#define IF_RESET(val) HWHI_RESET(0x200, val)
#define INT_VBL 0x0001
#define INT_HBL 0x0002
#define INT_VCNT 0x0004
#define INT_TM0_OVERFLOW 0x0008
#define INT_TM1_OVERFLOW 0x0010
#define INT_TM2_OVERFLOW 0x0020
#define INT_TM3_OVERFLOW 0x0040
#define INT_SIO 0x0080
#define INT_DMA0 0x0100
#define INT_DMA1 0x0200
#define INT_DMA2 0x0400
#define INT_DMA3 0x0800
#define INT_KEYPAD 0x1000
#define INT_GAMEPAK 0x2000
#define VERBOSE_LEVEL (0)
static inline void ATTR_PRINTF(3,4) verboselog(device_t &device, int n_level, const char *s_fmt, ...)
@ -135,34 +150,6 @@ void gba_state::request_irq(UINT32 int_type)
}
}
void gba_state::request_dma(dma_start_timing start)
{
UINT16 mask = 0x0000;
switch (start)
{
case immediately:
mask = 0x0000;
break;
case vblank:
mask = 0x1000;
break;
case hblank:
mask = 0x2000;
break;
case special:
mask = 0x3000;
break;
}
for (int ch = 0; ch < 4; ch++)
{
int ctrl = DMACNT_H(ch);
if ((ctrl & 0x8000) && ((ctrl & 0x3000) == mask))
dma_exec(ch);
}
}
TIMER_CALLBACK_MEMBER(gba_state::dma_complete)
{
static const UINT32 ch_int[4] = { INT_DMA0, INT_DMA1, INT_DMA2, INT_DMA3 };
@ -1178,6 +1165,43 @@ READ32_MEMBER(gba_state::gba_10000000_r)
return data;
}
WRITE_LINE_MEMBER(gba_state::int_hblank_callback)
{
request_irq(INT_HBL);
}
WRITE_LINE_MEMBER(gba_state::int_vblank_callback)
{
request_irq(INT_VBL);
}
WRITE_LINE_MEMBER(gba_state::int_vcount_callback)
{
request_irq(INT_VCNT);
}
WRITE_LINE_MEMBER(gba_state::dma_hblank_callback)
{
for (int ch = 0; ch < 4; ch++)
{
int ctrl = DMACNT_H(ch);
if ((ctrl & 0x8000) && ((ctrl & 0x3000) == 0x2000))
dma_exec(ch);
}
}
WRITE_LINE_MEMBER(gba_state::dma_vblank_callback)
{
for (int ch = 0; ch < 4; ch++)
{
int ctrl = DMACNT_H(ch);
if ((ctrl & 0x8000) && ((ctrl & 0x3000) == 0x1000))
dma_exec(ch);
}
}
static ADDRESS_MAP_START( gba_map, AS_PROGRAM, 32, gba_state )
ADDRESS_MAP_UNMAP_HIGH // for "Fruit Mura no Doubutsu Tachi" and "Classic NES Series"
AM_RANGE(0x00000000, 0x00003fff) AM_ROM AM_READ(gba_bios_r)
@ -1377,6 +1401,11 @@ static MACHINE_CONFIG_START( gbadv, gba_state )
MCFG_CPU_PROGRAM_MAP(gba_map)
MCFG_GBA_LCD_ADD("lcd")
MCFG_GBA_LCD_INT_HBLANK(WRITELINE(gba_state, int_hblank_callback))
MCFG_GBA_LCD_INT_VBLANK(WRITELINE(gba_state, int_vblank_callback))
MCFG_GBA_LCD_INT_VCOUNT(WRITELINE(gba_state, int_vcount_callback))
MCFG_GBA_LCD_DMA_HBLANK(WRITELINE(gba_state, dma_hblank_callback))
MCFG_GBA_LCD_DMA_VBLANK(WRITELINE(gba_state, dma_vblank_callback))
MCFG_SPEAKER_STANDARD_STEREO("spkleft", "spkright")
MCFG_SOUND_ADD("custom", GAMEBOY, 0)

View File

@ -9,22 +9,7 @@
#include "sound/dac.h"
#include "video/gba_lcd.h"
#define INT_VBL 0x0001
#define INT_HBL 0x0002
#define INT_VCNT 0x0004
#define INT_TM0_OVERFLOW 0x0008
#define INT_TM1_OVERFLOW 0x0010
#define INT_TM2_OVERFLOW 0x0020
#define INT_TM3_OVERFLOW 0x0040
#define INT_SIO 0x0080
#define INT_DMA0 0x0100
#define INT_DMA1 0x0200
#define INT_DMA2 0x0400
#define INT_DMA3 0x0800
#define INT_KEYPAD 0x1000
#define INT_GAMEPAK 0x2000
/* driver state */
class gba_state : public driver_device, protected gba_registers<(0x400 - 0x060) / 4, 0x060>
{
public:
@ -52,14 +37,6 @@ public:
void request_irq(UINT32 int_type);
enum dma_start_timing {
immediately = 0,
vblank,
hblank,
special
};
void request_dma(dma_start_timing start);
void dma_exec(int ch);
void audio_tick(int ref);
@ -94,6 +71,11 @@ public:
DECLARE_READ32_MEMBER(gba_bios_r);
DECLARE_READ32_MEMBER(gba_10000000_r);
DECLARE_DRIVER_INIT(gbadv);
DECLARE_WRITE_LINE_MEMBER(int_hblank_callback);
DECLARE_WRITE_LINE_MEMBER(int_vblank_callback);
DECLARE_WRITE_LINE_MEMBER(int_vcount_callback);
DECLARE_WRITE_LINE_MEMBER(dma_hblank_callback);
DECLARE_WRITE_LINE_MEMBER(dma_vblank_callback);
virtual void machine_start() override;
virtual void machine_reset() override;
TIMER_CALLBACK_MEMBER(dma_complete);