summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/gb_lcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/video/gb_lcd.c')
-rw-r--r--src/mame/video/gb_lcd.c2852
1 files changed, 2852 insertions, 0 deletions
diff --git a/src/mame/video/gb_lcd.c b/src/mame/video/gb_lcd.c
new file mode 100644
index 00000000000..b46aece5a15
--- /dev/null
+++ b/src/mame/video/gb_lcd.c
@@ -0,0 +1,2852 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/***************************************************************************
+
+ gb_lcd.c
+
+ Video file to handle emulation of the Nintendo Game Boy.
+
+ Original code Carsten Sorensen 1998
+ Mess modifications, bug fixes and speedups Hans de Goede 1998
+ Bug fixes, SGB and GBC code Anthony Kruize 2002
+ Improvements to match real hardware Wilbert Pol 2006-2008
+
+ Timing is not accurate enough:
+ - Mode 3 takes 172 cycles (measuered with logic analyzer by costis)
+
+***************************************************************************/
+
+#include "emu.h"
+//#include "cpu/lr35902/lr35902.h"
+#include "video/gb_lcd.h"
+
+/* Interrupts (copied from includes/gb.h)... */
+#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 */
+
+
+
+#define LCDCONT m_vid_regs[0x00] /* LCD control register */
+#define LCDSTAT m_vid_regs[0x01] /* LCD status register */
+#define SCROLLY m_vid_regs[0x02] /* Starting Y position of the background */
+#define SCROLLX m_vid_regs[0x03] /* Starting X position of the background */
+#define CURLINE m_vid_regs[0x04] /* Current screen line being scanned */
+#define CMPLINE m_vid_regs[0x05] /* Gen. int. when scan reaches this line */
+#define BGRDPAL m_vid_regs[0x07] /* Background palette */
+#define SPR0PAL m_vid_regs[0x08] /* Sprite palette #0 */
+#define SPR1PAL m_vid_regs[0x09] /* Sprite palette #1 */
+#define WNDPOSY m_vid_regs[0x0A] /* Window Y position */
+#define WNDPOSX m_vid_regs[0x0B] /* Window X position */
+#define KEY1 m_vid_regs[0x0D] /* Prepare speed switch */
+#define HDMA1 m_vid_regs[0x11] /* HDMA source high byte */
+#define HDMA2 m_vid_regs[0x12] /* HDMA source low byte */
+#define HDMA3 m_vid_regs[0x13] /* HDMA destination high byte */
+#define HDMA4 m_vid_regs[0x14] /* HDMA destination low byte */
+#define HDMA5 m_vid_regs[0x15] /* HDMA length/mode/start */
+#define GBCBCPS m_vid_regs[0x28] /* Backgound palette spec */
+#define GBCBCPD m_vid_regs[0x29] /* Backgound palette data */
+#define GBCOCPS m_vid_regs[0x2A] /* Object palette spec */
+#define GBCOCPD m_vid_regs[0x2B] /* Object palette data */
+
+/* -- Super Game Boy specific -- */
+#define SGB_BORDER_PAL_OFFSET 64 /* Border colours stored from pal 4-7 */
+#define SGB_XOFFSET 48 /* GB screen starts at column 48 */
+#define SGB_YOFFSET 40 /* GB screen starts at row 40 */
+
+
+enum {
+ UNLOCKED=0,
+ LOCKED
+};
+
+
+enum {
+ GB_LCD_STATE_LYXX_M3=1,
+ GB_LCD_STATE_LYXX_PRE_M0,
+ GB_LCD_STATE_LYXX_M0,
+ GB_LCD_STATE_LYXX_M0_SCX3,
+ GB_LCD_STATE_LYXX_M0_GBC_PAL,
+ GB_LCD_STATE_LYXX_M0_PRE_INC,
+ GB_LCD_STATE_LYXX_M0_INC,
+ GB_LCD_STATE_LY00_M2,
+ GB_LCD_STATE_LYXX_M2,
+ GB_LCD_STATE_LY9X_M1,
+ GB_LCD_STATE_LY9X_M1_INC,
+ GB_LCD_STATE_LY00_M1,
+ GB_LCD_STATE_LY00_M1_1,
+ GB_LCD_STATE_LY00_M1_2,
+ GB_LCD_STATE_LY00_M0
+};
+
+
+/* OAM contents on power up.
+
+ The OAM area seems contain some kind of unit fingerprint. On each boot
+ the data is almost always the same. Some random bits are flipped between
+ different boots. It is currently unknown how much these fingerprints
+ differ between different units.
+
+ OAM fingerprints taken from Wilbert Pol's own unit.
+ */
+
+static const UINT8 dmg_oam_fingerprint[0x100] = {
+ 0xD8, 0xE6, 0xB3, 0x89, 0xEC, 0xDE, 0x11, 0x62, 0x0B, 0x7E, 0x48, 0x9E, 0xB9, 0x6E, 0x26, 0xC9,
+ 0x36, 0xF4, 0x7D, 0xE4, 0xD9, 0xCE, 0xFA, 0x5E, 0xA3, 0x77, 0x60, 0xFC, 0x1C, 0x64, 0x8B, 0xAC,
+ 0xB6, 0x74, 0x3F, 0x9A, 0x0E, 0xFE, 0xEA, 0xA9, 0x40, 0x3A, 0x7A, 0xB6, 0xF2, 0xED, 0xA8, 0x3E,
+ 0xAF, 0x2C, 0xD2, 0xF2, 0x01, 0xE0, 0x5B, 0x3A, 0x53, 0x6A, 0x1C, 0x6C, 0x20, 0xD9, 0x22, 0xB4,
+ 0x8C, 0x38, 0x71, 0x69, 0x3E, 0x93, 0xA3, 0x22, 0xCE, 0x76, 0x24, 0xE7, 0x1A, 0x14, 0x6B, 0xB1,
+ 0xF9, 0x3D, 0xBF, 0x3D, 0x74, 0x64, 0xCB, 0xF5, 0xDC, 0x9A, 0x53, 0xC6, 0x0E, 0x78, 0x34, 0xCB,
+ 0x42, 0xB3, 0xFF, 0x07, 0x73, 0xAE, 0x6C, 0xA2, 0x6F, 0x6A, 0xA4, 0x66, 0x0A, 0x8C, 0x40, 0xB3,
+ 0x9A, 0x3D, 0x39, 0x78, 0xAB, 0x29, 0xE7, 0xC5, 0x7A, 0xDD, 0x51, 0x95, 0x2B, 0xE4, 0x1B, 0xF6,
+ 0x31, 0x16, 0x34, 0xFE, 0x11, 0xF2, 0x5E, 0x11, 0xF3, 0x95, 0x66, 0xB9, 0x37, 0xC2, 0xAD, 0x6D,
+ 0x1D, 0xA7, 0x79, 0x06, 0xD7, 0xE5, 0x8F, 0xFA, 0x9C, 0x02, 0x0C, 0x31, 0x8B, 0x17, 0x2E, 0x31,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const UINT8 mgb_oam_fingerprint[0x100] = {
+ 0xB9, 0xE9, 0x0D, 0x69, 0xBB, 0x7F, 0x00, 0x80, 0xE9, 0x7B, 0x79, 0xA2, 0xFD, 0xCF, 0xD8, 0x0A,
+ 0x87, 0xEF, 0x44, 0x11, 0xFE, 0x37, 0x10, 0x21, 0xFA, 0xFF, 0x00, 0x17, 0xF6, 0x4F, 0x83, 0x03,
+ 0x3A, 0xF4, 0x00, 0x24, 0xBB, 0xAE, 0x05, 0x01, 0xFF, 0xF7, 0x12, 0x48, 0xA7, 0x5E, 0xF6, 0x28,
+ 0x5B, 0xFF, 0x2E, 0x10, 0xFF, 0xB9, 0x50, 0xC8, 0xAF, 0x77, 0x2C, 0x1A, 0x62, 0xD7, 0x81, 0xC2,
+ 0xFD, 0x5F, 0xA0, 0x94, 0xAF, 0xFF, 0x51, 0x20, 0x36, 0x76, 0x50, 0x0A, 0xFD, 0xF6, 0x20, 0x00,
+ 0xFE, 0xF7, 0xA0, 0x68, 0xFF, 0xFC, 0x29, 0x51, 0xA3, 0xFA, 0x06, 0xC4, 0x94, 0xFF, 0x39, 0x0A,
+ 0xFF, 0x6C, 0x20, 0x20, 0xF1, 0xAD, 0x0C, 0x81, 0x56, 0xFB, 0x03, 0x82, 0xFF, 0xFF, 0x08, 0x58,
+ 0x96, 0x7E, 0x01, 0x4D, 0xFF, 0xE4, 0x82, 0xE3, 0x3D, 0xBB, 0x54, 0x00, 0x3D, 0xF3, 0x04, 0x21,
+ 0xB7, 0x39, 0xCC, 0x10, 0xF9, 0x5B, 0x80, 0x50, 0x3F, 0x6A, 0x1C, 0x21, 0x1F, 0xFA, 0xA8, 0x52,
+ 0x5F, 0xB3, 0x44, 0xA1, 0x96, 0x1E, 0x00, 0x27, 0x63, 0x77, 0x30, 0x54, 0x37, 0x6F, 0x60, 0x22,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const UINT8 cgb_oam_fingerprint[0x100] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x74, 0xFF, 0x09, 0x00, 0x9D, 0x61, 0xA8, 0x28, 0x36, 0x1E, 0x58, 0xAA, 0x75, 0x74, 0xA1, 0x42,
+ 0x05, 0x96, 0x40, 0x09, 0x41, 0x02, 0x60, 0x00, 0x1F, 0x11, 0x22, 0xBC, 0x31, 0x52, 0x22, 0x54,
+ 0x22, 0xA9, 0xC4, 0x00, 0x1D, 0xAD, 0x80, 0x0C, 0x5D, 0xFA, 0x51, 0x92, 0x93, 0x98, 0xA4, 0x04,
+ 0x22, 0xA9, 0xC4, 0x00, 0x1D, 0xAD, 0x80, 0x0C, 0x5D, 0xFA, 0x51, 0x92, 0x93, 0x98, 0xA4, 0x04,
+ 0x22, 0xA9, 0xC4, 0x00, 0x1D, 0xAD, 0x80, 0x0C, 0x5D, 0xFA, 0x51, 0x92, 0x93, 0x98, 0xA4, 0x04,
+ 0x22, 0xA9, 0xC4, 0x00, 0x1D, 0xAD, 0x80, 0x0C, 0x5D, 0xFA, 0x51, 0x92, 0x93, 0x98, 0xA4, 0x04
+};
+
+/*
+ For an AGS in CGB mode this data is: */
+#if 0
+static const UINT8 abs_oam_fingerprint[0x100] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+ 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+ 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
+ 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
+ 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+#endif
+
+
+const device_type GB_LCD_DMG = &device_creator<gb_lcd_device>;
+const device_type GB_LCD_MGB = &device_creator<mgb_lcd_device>;
+const device_type GB_LCD_SGB = &device_creator<sgb_lcd_device>;
+const device_type GB_LCD_CGB = &device_creator<cgb_lcd_device>;
+
+
+
+gb_lcd_device::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)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_video_interface(mconfig, *this),
+ m_sgb_border_hack(0)
+{
+}
+
+gb_lcd_device::gb_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, GB_LCD_DMG, "DMG LCD", tag, owner, clock, "dmg_lcd", __FILE__),
+ device_video_interface(mconfig, *this),
+ m_sgb_border_hack(0)
+{
+}
+
+mgb_lcd_device::mgb_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : gb_lcd_device(mconfig, GB_LCD_MGB, "MGB LCD", tag, owner, clock, "mgb_lcd", __FILE__)
+{
+}
+
+sgb_lcd_device::sgb_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : gb_lcd_device(mconfig, GB_LCD_SGB, "SGB LCD", tag, owner, clock, "sgb_lcd", __FILE__)
+{
+}
+
+cgb_lcd_device::cgb_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : gb_lcd_device(mconfig, GB_LCD_CGB, "CGB LCD", tag, owner, clock, "cgb_lcd", __FILE__)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void gb_lcd_device::common_start()
+{
+ m_screen->register_screen_bitmap(m_bitmap);
+ save_item(NAME(m_bitmap));
+ m_oam = auto_alloc_array_clear(machine(), UINT8, 0x100);
+
+ machine().save().register_postload(save_prepost_delegate(FUNC(gb_lcd_device::videoptr_restore), this));
+
+ m_maincpu = machine().device<cpu_device>("maincpu");
+
+ save_pointer(NAME(m_oam), 0x100);
+ save_item(NAME(m_window_lines_drawn));
+ save_item(NAME(m_vid_regs));
+ save_item(NAME(m_bg_zbuf));
+
+ save_item(NAME(m_cgb_bpal));
+ save_item(NAME(m_cgb_spal));
+
+ save_item(NAME(m_gb_bpal));
+ save_item(NAME(m_gb_spal0));
+ save_item(NAME(m_gb_spal1));
+
+ save_item(NAME(m_current_line));
+ save_item(NAME(m_cmp_line));
+ save_item(NAME(m_sprCount));
+ save_item(NAME(m_sprite));
+ save_item(NAME(m_previous_line));
+ save_item(NAME(m_start_x));
+ save_item(NAME(m_end_x));
+ save_item(NAME(m_mode));
+ save_item(NAME(m_state));
+ save_item(NAME(m_lcd_irq_line));
+ save_item(NAME(m_triggering_line_irq));
+ save_item(NAME(m_line_irq));
+ save_item(NAME(m_triggering_mode_irq));
+ save_item(NAME(m_mode_irq));
+ save_item(NAME(m_delayed_line_irq));
+ save_item(NAME(m_sprite_cycles));
+ save_item(NAME(m_scrollx_adjust));
+ save_item(NAME(m_oam_locked));
+ save_item(NAME(m_vram_locked));
+ save_item(NAME(m_pal_locked));
+ save_item(NAME(m_hdma_enabled));
+ save_item(NAME(m_hdma_possible));
+ save_item(NAME(m_gbc_mode));
+ save_item(NAME(m_gb_tile_no_mod));
+ save_item(NAME(m_vram_bank));
+
+ save_item(NAME(m_gb_chrgen_offs));
+ save_item(NAME(m_gb_bgdtab_offs));
+ save_item(NAME(m_gb_wndtab_offs));
+ save_item(NAME(m_gbc_chrgen_offs));
+ save_item(NAME(m_gbc_bgdtab_offs));
+ save_item(NAME(m_gbc_wndtab_offs));
+
+ save_item(NAME(m_layer[0].enabled));
+ save_item(NAME(m_layer[0].xindex));
+ save_item(NAME(m_layer[0].xshift));
+ save_item(NAME(m_layer[0].xstart));
+ save_item(NAME(m_layer[0].xend));
+ save_item(NAME(m_layer[0].bgline));
+ save_item(NAME(m_layer[1].enabled));
+ save_item(NAME(m_layer[1].xindex));
+ save_item(NAME(m_layer[1].xshift));
+ save_item(NAME(m_layer[1].xstart));
+ save_item(NAME(m_layer[1].xend));
+ save_item(NAME(m_layer[1].bgline));
+}
+
+
+void gb_lcd_device::videoptr_restore()
+{
+ m_layer[0].bg_map = m_vram + m_gb_bgdtab_offs;
+ m_layer[0].bg_tiles = m_vram + m_gb_chrgen_offs;
+ m_layer[1].bg_map = m_vram + m_gb_wndtab_offs;
+ m_layer[1].bg_tiles = m_vram + m_gb_chrgen_offs;
+}
+
+void cgb_lcd_device::videoptr_restore()
+{
+ m_layer[0].bg_map = m_vram + m_gb_bgdtab_offs;
+ m_layer[0].gbc_map = m_vram + m_gbc_bgdtab_offs;
+ m_layer[1].bg_map = m_vram + m_gb_wndtab_offs;
+ m_layer[1].gbc_map = m_vram + m_gbc_wndtab_offs;
+}
+
+
+void gb_lcd_device::device_start()
+{
+ common_start();
+ m_lcd_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gb_lcd_device::lcd_timer_proc),this));
+
+ m_vram = auto_alloc_array_clear(machine(), UINT8, 0x2000);
+ save_pointer(NAME(m_vram), 0x2000);
+
+ memcpy(m_oam, dmg_oam_fingerprint, 0x100);
+}
+
+void mgb_lcd_device::device_start()
+{
+ common_start();
+ m_lcd_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mgb_lcd_device::lcd_timer_proc),this));
+
+ m_vram = auto_alloc_array_clear(machine(), UINT8, 0x2000);
+ save_pointer(NAME(m_vram), 0x2000);
+
+ memcpy(m_oam, mgb_oam_fingerprint, 0x100);
+}
+
+void sgb_lcd_device::device_start()
+{
+ common_start();
+ m_lcd_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sgb_lcd_device::lcd_timer_proc),this));
+
+ m_vram = auto_alloc_array_clear(machine(), UINT8, 0x2000);
+ save_pointer(NAME(m_vram), 0x2000);
+
+ m_sgb_tile_data = auto_alloc_array_clear(machine(), UINT8, 0x2000);
+ save_pointer(NAME(m_sgb_tile_data), 0x2000);
+
+ memset(m_sgb_tile_map, 0, sizeof(m_sgb_tile_map));
+
+ /* Some default colours for non-SGB games */
+ m_sgb_pal[0] = 32767;
+ m_sgb_pal[1] = 21140;
+ m_sgb_pal[2] = 10570;
+ m_sgb_pal[3] = 0;
+ /* The rest of the colortable can be black */
+ for (int i = 4; i < 8 * 16; i++)
+ m_sgb_pal[i] = 0;
+
+ save_item(NAME(m_sgb_atf_data));
+ save_item(NAME(m_sgb_atf));
+ save_item(NAME(m_sgb_pal_data));
+ save_item(NAME(m_sgb_pal_map));
+ save_item(NAME(m_sgb_pal));
+ save_item(NAME(m_sgb_tile_map));
+ save_item(NAME(m_sgb_window_mask));
+}
+
+void cgb_lcd_device::device_start()
+{
+ common_start();
+ m_lcd_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(cgb_lcd_device::lcd_timer_proc),this));
+
+ m_vram = auto_alloc_array_clear(machine(), UINT8, 0x4000);
+ save_pointer(NAME(m_vram), 0x4000);
+
+ memcpy(m_oam, cgb_oam_fingerprint, 0x100);
+
+ /* Background is initialised as white */
+ for (int i = 0; i < 32; i++)
+ m_cgb_bpal[i] = 32767;
+ /* Sprites are supposed to be uninitialized, but we'll make them black */
+ for (int i = 0; i < 32; i++)
+ m_cgb_spal[i] = 0;
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void gb_lcd_device::common_reset()
+{
+ m_window_lines_drawn = 0;
+
+ m_current_line = 0;
+ m_cmp_line = 0;
+ m_sprCount = 0;
+ m_previous_line = 0;
+ m_start_x = 0;
+ m_end_x = 0;
+ m_mode = 0;
+ m_state = 0;
+ m_lcd_irq_line = 0;
+ m_triggering_line_irq = 0;
+ m_line_irq = 0;
+ m_triggering_mode_irq = 0;
+ m_mode_irq = 0;
+ m_delayed_line_irq = 0;
+ m_sprite_cycles = 0;
+ m_scrollx_adjust = 0;
+ m_oam_locked = 0;
+ m_vram_locked = 0;
+ m_pal_locked = 0;
+ m_gbc_mode = 0;
+ m_gb_tile_no_mod = 0;
+ m_vram_bank = 0;
+
+ m_gb_chrgen_offs = 0;
+ m_gb_bgdtab_offs = 0x1c00;
+ m_gb_wndtab_offs = 0x1c00;
+
+ memset(&m_vid_regs, 0, sizeof(m_vid_regs));
+ memset(&m_bg_zbuf, 0, sizeof(m_bg_zbuf));
+ memset(&m_cgb_bpal, 0, sizeof(m_cgb_bpal));
+ memset(&m_cgb_spal, 0, sizeof(m_cgb_spal));
+ memset(&m_sprite, 0, sizeof(m_sprite));
+ memset(&m_layer[0], 0, sizeof(m_layer[0]));
+ memset(&m_layer[1], 0, sizeof(m_layer[1]));
+
+ // specific reg initialization
+ m_vid_regs[0x06] = 0xff;
+
+ for (int i = 0x0c; i < _NR_GB_VID_REGS; i++)
+ m_vid_regs[i] = 0xff;
+
+ LCDSTAT = 0x80;
+ LCDCONT = 0x00; /* Video hardware is turned off at boot time */
+ m_current_line = CURLINE = CMPLINE = 0x00;
+ SCROLLX = SCROLLY = 0x00;
+ SPR0PAL = SPR1PAL = 0xFF;
+ WNDPOSX = WNDPOSY = 0x00;
+
+ // Initialize palette arrays
+ for (int i = 0; i < 4; i++)
+ m_gb_bpal[i] = m_gb_spal0[i] = m_gb_spal1[i] = i;
+
+}
+
+
+void gb_lcd_device::device_reset()
+{
+ common_reset();
+
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(456));
+}
+
+void sgb_lcd_device::device_reset()
+{
+ common_reset();
+
+ memset(m_sgb_tile_data, 0, 0x2000);
+
+ m_sgb_window_mask = 0;
+
+ memset(m_sgb_pal_map, 0, sizeof(m_sgb_pal_map));
+ memset(m_sgb_atf_data, 0, sizeof(m_sgb_atf_data));
+}
+
+void cgb_lcd_device::device_reset()
+{
+ common_reset();
+
+ m_gbc_chrgen_offs = 0x2000;
+ m_gbc_bgdtab_offs = 0x3c00;
+ m_gbc_wndtab_offs = 0x3c00;
+
+ /* HDMA disabled */
+ m_hdma_enabled = 0;
+ m_hdma_possible = 0;
+
+ m_gbc_mode = 1;
+}
+
+
+
+inline void gb_lcd_device::plot_pixel(bitmap_ind16 &bitmap, int x, int y, UINT32 color)
+{
+ bitmap.pix16(y, x) = (UINT16)color;
+}
+
+/*
+ Select which sprites should be drawn for the current scanline and return the
+ number of sprites selected.
+ */
+void gb_lcd_device::select_sprites()
+{
+ int /*yindex,*/ line, height;
+ UINT8 *oam = m_oam + 39 * 4;
+
+ m_sprCount = 0;
+
+ /* If video hardware is enabled and sprites are enabled */
+ if ((LCDCONT & 0x80) && (LCDCONT & 0x02))
+ {
+ /* Check for stretched sprites */
+ if (LCDCONT & 0x04)
+ height = 16;
+ else
+ height = 8;
+
+ //yindex = m_current_line;
+ line = m_current_line + 16;
+
+ for (int i = 39; i >= 0; i--)
+ {
+ if (line >= oam[0] && line < (oam[0] + height) && oam[1] && oam[1] < 168)
+ {
+ /* We limit the sprite count to max 10 here;
+ proper games should not exceed this... */
+ if (m_sprCount < 10)
+ {
+ m_sprite[m_sprCount] = i;
+ m_sprCount++;
+ }
+ }
+ oam -= 4;
+ }
+ }
+}
+
+void gb_lcd_device::update_sprites()
+{
+ bitmap_ind16 &bitmap = m_bitmap;
+ UINT8 height, tilemask, line, *oam, *vram;
+ int yindex;
+
+ if (LCDCONT & 0x04)
+ {
+ height = 16;
+ tilemask = 0xFE;
+ }
+ else
+ {
+ height = 8;
+ tilemask = 0xFF;
+ }
+
+ yindex = m_current_line;
+ line = m_current_line + 16;
+
+ oam = m_oam + 39 * 4;
+ vram = m_vram;
+ for (int i = 39; i >= 0; i--)
+ {
+ /* if sprite is on current line && x-coordinate && x-coordinate is < 168 */
+ if (line >= oam[0] && line < (oam[0] + height) && oam[1] && oam[1] < 168)
+ {
+ UINT16 data;
+ UINT8 bit, *spal;
+ int xindex, adr;
+
+ spal = (oam[3] & 0x10) ? m_gb_spal1 : m_gb_spal0;
+ xindex = oam[1] - 8;
+ if (oam[3] & 0x40) /* flip y ? */
+ {
+ adr = (oam[2] & tilemask) * 16 + (height - 1 - line + oam[0]) * 2;
+ }
+ else
+ {
+ adr = (oam[2] & tilemask) * 16 + (line - oam[0]) * 2;
+ }
+ data = (vram[adr + 1] << 8) | vram[adr];
+
+ switch (oam[3] & 0xA0)
+ {
+ case 0xA0: /* priority is set (behind bgnd & wnd, flip x) */
+ for (bit = 0; bit < 8; bit++, xindex++)
+ {
+ register int colour = ((data & 0x0100) ? 2 : 0) | ((data & 0x0001) ? 1 : 0);
+ if (colour && !m_bg_zbuf[xindex] && xindex >= 0 && xindex < 160)
+ plot_pixel(bitmap, xindex, yindex, spal[colour]);
+ data >>= 1;
+ }
+ break;
+ case 0x20: /* priority is not set (overlaps bgnd & wnd, flip x) */
+ for (bit = 0; bit < 8; bit++, xindex++)
+ {
+ register int colour = ((data & 0x0100) ? 2 : 0) | ((data & 0x0001) ? 1 : 0);
+ if (colour && xindex >= 0 && xindex < 160)
+ plot_pixel(bitmap, xindex, yindex, spal[colour]);
+ data >>= 1;
+ }
+ break;
+ case 0x80: /* priority is set (behind bgnd & wnd, don't flip x) */
+ for (bit = 0; bit < 8 && xindex < 160; bit++, xindex++)
+ {
+ register int colour = ((data & 0x8000) ? 2 : 0) | ((data & 0x0080) ? 1 : 0);
+ if (colour && !m_bg_zbuf[xindex] && xindex >= 0 && xindex < 160)
+ plot_pixel(bitmap, xindex, yindex, spal[colour]);
+ data <<= 1;
+ }
+ break;
+ case 0x00: /* priority is not set (overlaps bgnd & wnd, don't flip x) */
+ for (bit = 0; bit < 8 && xindex < 160; bit++, xindex++)
+ {
+ register int colour = ((data & 0x8000) ? 2 : 0) | ((data & 0x0080) ? 1 : 0);
+ if (colour && xindex >= 0 && xindex < 160)
+ plot_pixel(bitmap, xindex, yindex, spal[colour]);
+ data <<= 1;
+ }
+ break;
+ }
+ }
+ oam -= 4;
+ }
+}
+
+void gb_lcd_device::update_scanline()
+{
+ bitmap_ind16 &bitmap = m_bitmap;
+
+ g_profiler.start(PROFILER_VIDEO);
+
+ /* Make sure we're in mode 3 */
+ if ((LCDSTAT & 0x03) == 0x03)
+ {
+ /* Calculate number of pixels to render based on time still left on the timer */
+ UINT32 cycles_to_go = m_maincpu->attotime_to_cycles(m_lcd_timer->remaining());
+ int l = 0;
+
+ if (m_start_x < 0)
+ {
+ /* Window is enabled if the hardware says so AND the current scanline is
+ * within the window AND the window X coordinate is <=166 */
+ m_layer[1].enabled = ((LCDCONT & 0x20) && (m_current_line >= WNDPOSY) && (WNDPOSX <= 166)) ? 1 : 0;
+
+ /* BG is enabled if the hardware says so AND (window_off OR (window_on
+ * AND window's X position is >=7)) */
+ m_layer[0].enabled = ((LCDCONT & 0x01) && ((!m_layer[1].enabled) || (m_layer[1].enabled && (WNDPOSX >= 7)))) ? 1 : 0;
+
+ if (m_layer[0].enabled)
+ {
+ m_layer[0].bgline = (SCROLLY + m_current_line) & 0xFF;
+ m_layer[0].bg_map = m_vram + m_gb_bgdtab_offs;
+ m_layer[0].bg_tiles = m_vram + m_gb_chrgen_offs;
+ m_layer[0].xindex = SCROLLX >> 3;
+ m_layer[0].xshift = SCROLLX & 7;
+ m_layer[0].xstart = 0;
+ m_layer[0].xend = 160;
+ }
+
+ if (m_layer[1].enabled)
+ {
+ int xpos = WNDPOSX - 7; /* Window is offset by 7 pixels */
+ if (xpos < 0)
+ xpos = 0;
+
+ m_layer[1].bgline = m_window_lines_drawn;
+ m_layer[1].bg_map = m_vram + m_gb_wndtab_offs;
+ m_layer[1].bg_tiles = m_vram + m_gb_chrgen_offs;
+ m_layer[1].xindex = 0;
+ m_layer[1].xshift = 0;
+ m_layer[1].xstart = xpos;
+ m_layer[1].xend = 160;
+ m_layer[0].xend = xpos;
+ }
+ m_start_x = 0;
+ }
+
+ if (cycles_to_go < 160)
+ {
+ m_end_x = MIN(160 - cycles_to_go, 160);
+ /* Draw empty pixels when the background is disabled */
+ if (!(LCDCONT & 0x01))
+ {
+ rectangle r(m_start_x, m_end_x - 1, m_current_line, m_current_line);
+ bitmap.fill(m_gb_bpal[0], r);
+ }
+ while (l < 2)
+ {
+ UINT8 xindex, *map, *tiles;
+ UINT16 data;
+ int i, tile_index;
+
+ if (!m_layer[l].enabled)
+ {
+ l++;
+ continue;
+ }
+ map = m_layer[l].bg_map + ((m_layer[l].bgline << 2) & 0x3E0);
+ tiles = m_layer[l].bg_tiles + ((m_layer[l].bgline & 7) << 1);
+ xindex = m_start_x;
+ if (xindex < m_layer[l].xstart)
+ xindex = m_layer[l].xstart;
+ i = m_end_x;
+ if (i > m_layer[l].xend)
+ i = m_layer[l].xend;
+ i = i - xindex;
+
+ tile_index = (map[m_layer[l].xindex] ^ m_gb_tile_no_mod) * 16;
+ data = tiles[tile_index] | (tiles[tile_index+1] << 8);
+ data <<= m_layer[l].xshift;
+
+ while (i > 0)
+ {
+ while ((m_layer[l].xshift < 8) && i)
+ {
+ register int colour = ((data & 0x8000) ? 2 : 0) | ((data & 0x0080) ? 1 : 0);
+ plot_pixel(bitmap, xindex, m_current_line, m_gb_bpal[colour]);
+ m_bg_zbuf[xindex] = colour;
+ xindex++;
+ data <<= 1;
+ m_layer[l].xshift++;
+ i--;
+ }
+ if (m_layer[l].xshift == 8)
+ {
+ /* Take possible changes to SCROLLY into account */
+ if (l == 0)
+ {
+ m_layer[0].bgline = (SCROLLY + m_current_line) & 0xFF;
+ map = m_layer[l].bg_map + ((m_layer[l].bgline << 2) & 0x3E0);
+ tiles = m_layer[l].bg_tiles + ((m_layer[l].bgline & 7) << 1);
+ }
+
+ m_layer[l].xindex = (m_layer[l].xindex + 1) & 31;
+ m_layer[l].xshift = 0;
+ tile_index = (map[m_layer[l].xindex] ^ m_gb_tile_no_mod) * 16;
+ data = tiles[tile_index] | (tiles[tile_index + 1] << 8);
+ }
+ }
+ l++;
+ }
+ if (m_end_x == 160 && LCDCONT & 0x02)
+ {
+ update_sprites();
+ }
+ m_start_x = m_end_x;
+ }
+ }
+ else
+ {
+ if (!(LCDCONT & 0x80))
+ {
+ /* Draw an empty line when LCD is disabled */
+ if (m_previous_line != m_current_line)
+ {
+ if (m_current_line < 144)
+ {
+ const rectangle &r = m_screen->visible_area();
+ rectangle r1(r.min_x, r.max_x, m_current_line, m_current_line);
+ bitmap.fill(0, r1);
+ }
+ m_previous_line = m_current_line;
+ }
+ }
+ }
+
+ g_profiler.stop();
+}
+
+/* --- Super Game Boy Specific --- */
+
+void sgb_lcd_device::update_sprites()
+{
+ bitmap_ind16 &bitmap = m_bitmap;
+ UINT8 height, tilemask, line, *oam, *vram, pal;
+ INT16 yindex;
+
+ if (LCDCONT & 0x04)
+ {
+ height = 16;
+ tilemask = 0xFE;
+ }
+ else
+ {
+ height = 8;
+ tilemask = 0xFF;
+ }
+
+ /* Offset to center of screen */
+ yindex = m_current_line + SGB_YOFFSET;
+ line = m_current_line + 16;
+
+ oam = m_oam + 39 * 4;
+ vram = m_vram;
+ for (int i = 39; i >= 0; i--)
+ {
+ /* if sprite is on current line && x-coordinate && x-coordinate is < 168 */
+ if (line >= oam[0] && line < (oam[0] + height) && oam[1] && oam[1] < 168)
+ {
+ UINT16 data;
+ UINT8 bit, *spal;
+ INT16 xindex;
+ int adr;
+
+ spal = (oam[3] & 0x10) ? m_gb_spal1 : m_gb_spal0;
+ xindex = oam[1] - 8;
+ if (oam[3] & 0x40) /* flip y ? */
+ {
+ adr = (oam[2] & tilemask) * 16 + (height - 1 - line + oam[0]) * 2;
+ }
+ else
+ {
+ adr = (oam[2] & tilemask) * 16 + (line - oam[0]) * 2;
+ }
+ data = (vram[adr + 1] << 8) | vram[adr];
+
+ /* Find the palette to use */
+ // If sprite started before the start of the line we may need to pick a different pal_map entry?
+ pal = m_sgb_pal_map[(xindex < 0) ? 0 : (xindex >> 3)][((yindex - SGB_YOFFSET) >> 3)] << 2;
+
+ /* Offset to center of screen */
+ xindex += SGB_XOFFSET;
+
+ switch (oam[3] & 0xA0)
+ {
+ case 0xA0: /* priority is set (behind bgnd & wnd, flip x) */
+ for (bit = 0; bit < 8; bit++, xindex++)
+ {
+ register int colour = ((data & 0x0100) ? 2 : 0) | ((data & 0x0001) ? 1 : 0);
+ if ((xindex >= SGB_XOFFSET && xindex < SGB_XOFFSET + 160) && colour && !m_bg_zbuf[xindex - SGB_XOFFSET])
+ plot_pixel(bitmap, xindex, yindex, m_sgb_pal[pal + spal[colour]]);
+ data >>= 1;
+ }
+ break;
+ case 0x20: /* priority is not set (overlaps bgnd & wnd, flip x) */
+ for (bit = 0; bit < 8; bit++, xindex++)
+ {
+ register int colour = ((data & 0x0100) ? 2 : 0) | ((data & 0x0001) ? 1 : 0);
+ if ((xindex >= SGB_XOFFSET && xindex < SGB_XOFFSET + 160) && colour)
+ plot_pixel(bitmap, xindex, yindex, m_sgb_pal[pal + spal[colour]]);
+ data >>= 1;
+ }
+ break;
+ case 0x80: /* priority is set (behind bgnd & wnd, don't flip x) */
+ for (bit = 0; bit < 8; bit++, xindex++)
+ {
+ register int colour = ((data & 0x8000) ? 2 : 0) | ((data & 0x0080) ? 1 : 0);
+ if ((xindex >= SGB_XOFFSET && xindex < SGB_XOFFSET + 160) && colour && !m_bg_zbuf[xindex - SGB_XOFFSET])
+ plot_pixel(bitmap, xindex, yindex, m_sgb_pal[pal + spal[colour]]);
+ data <<= 1;
+ }
+ break;
+ case 0x00: /* priority is not set (overlaps bgnd & wnd, don't flip x) */
+ for (bit = 0; bit < 8; bit++, xindex++)
+ {
+ register int colour = ((data & 0x8000) ? 2 : 0) | ((data & 0x0080) ? 1 : 0);
+ if ((xindex >= SGB_XOFFSET && xindex < SGB_XOFFSET + 160) && colour)
+ plot_pixel(bitmap, xindex, yindex, m_sgb_pal[pal + spal[colour]]);
+ data <<= 1;
+ }
+ break;
+ }
+ }
+ oam -= 4;
+ }
+}
+
+
+void sgb_lcd_device::refresh_border()
+{
+ UINT16 data, data2;
+ UINT8 *tiles, *tiles2;
+
+ for (UINT16 yidx = 0; yidx < 224; yidx++)
+ {
+ UINT8 *map = m_sgb_tile_map + ((yidx >> 3) * 64);
+ UINT16 xindex = 0;
+
+ for (UINT16 xidx = 0; xidx < 64; xidx += 2)
+ {
+ if (map[xidx + 1] & 0x80) /* Vertical flip */
+ tiles = m_sgb_tile_data + ((7 - (yidx % 8)) << 1);
+ else /* No vertical flip */
+ tiles = m_sgb_tile_data + ((yidx % 8) << 1);
+ tiles2 = tiles + 16;
+
+ UINT8 pal = (map[xidx + 1] & 0x1C) >> 2;
+ if (pal == 0)
+ pal = 1;
+ pal <<= 4;
+
+ if (m_sgb_border_hack)
+ { /* A few games do weird stuff */
+ UINT8 tileno = map[xidx];
+ if (tileno >= 128) tileno = ((64 + tileno) % 128) + 128;
+ else tileno = (64 + tileno) % 128;
+ data = tiles[tileno * 32] | (tiles[(tileno * 32) + 1] << 8);
+ data2 = tiles2[tileno * 32] | (tiles2[(tileno * 32) + 1] << 8);
+ }
+ else
+ {
+ data = tiles[map[xidx] * 32] | (tiles[(map[xidx] * 32) + 1] << 8);
+ data2 = tiles2[map[xidx] * 32] | (tiles2[(map[xidx] * 32) + 1] << 8);
+ }
+
+ for (int i = 0; i < 8; i++)
+ {
+ register UINT8 colour;
+ if ((map[xidx + 1] & 0x40)) /* Horizontal flip */
+ {
+ colour = ((data & 0x0001) ? 1 : 0) | ((data & 0x0100) ? 2 : 0) |
+ ((data2 & 0x0001) ? 4 : 0) | ((data2 & 0x0100) ? 8 : 0);
+ data >>= 1;
+ data2 >>= 1;
+ }
+ else /* No horizontal flip */
+ {
+ colour = ((data & 0x0080) ? 1 : 0) | ((data & 0x8000) ? 2 : 0) |
+ ((data2 & 0x0080) ? 4 : 0) | ((data2 & 0x8000) ? 8 : 0);
+ data <<= 1;
+ data2 <<= 1;
+ }
+ /* A slight hack below so we don't draw over the GB screen.
+ * Drawing there is allowed, but due to the way we draw the
+ * scanline, it can obscure the screen even when it shouldn't.
+ */
+ if (!((yidx >= SGB_YOFFSET && yidx < SGB_YOFFSET + 144) &&
+ (xindex >= SGB_XOFFSET && xindex < SGB_XOFFSET + 160)))
+ {
+ plot_pixel(m_bitmap, xindex, yidx, m_sgb_pal[pal + colour]);
+ }
+ xindex++;
+ }
+ }
+ }
+}
+
+void sgb_lcd_device::update_scanline()
+{
+ bitmap_ind16 &bitmap = m_bitmap;
+
+ g_profiler.start(PROFILER_VIDEO);
+
+ if ((LCDSTAT & 0x03) == 0x03)
+ {
+ /* Calcuate number of pixels to render based on time still left on the timer */
+ UINT32 cycles_to_go = m_maincpu->attotime_to_cycles(m_lcd_timer->remaining());
+ int l = 0;
+
+ if (m_start_x < 0)
+ {
+ /* Window is enabled if the hardware says so AND the current scanline is
+ * within the window AND the window X coordinate is <=166 */
+ m_layer[1].enabled = ((LCDCONT & 0x20) && m_current_line >= WNDPOSY && WNDPOSX <= 166) ? 1 : 0;
+
+ /* BG is enabled if the hardware says so AND (window_off OR (window_on
+ * AND window's X position is >=7 )) */
+ m_layer[0].enabled = ((LCDCONT & 0x01) && ((!m_layer[1].enabled) || (m_layer[1].enabled && WNDPOSX >= 7))) ? 1 : 0;
+
+ if (m_layer[0].enabled)
+ {
+ m_layer[0].bgline = (SCROLLY + m_current_line) & 0xFF;
+ m_layer[0].bg_map = m_vram + m_gb_bgdtab_offs;
+ m_layer[0].bg_tiles = m_vram + m_gb_chrgen_offs;
+ m_layer[0].xindex = SCROLLX >> 3;
+ m_layer[0].xshift = SCROLLX & 7;
+ m_layer[0].xstart = 0;
+ m_layer[0].xend = 160;
+ }
+
+ if (m_layer[1].enabled)
+ {
+ int xpos;
+
+ /* Window X position is offset by 7 so we'll need to adjust */
+ xpos = WNDPOSX - 7;
+ if (xpos < 0)
+ xpos = 0;
+
+ m_layer[1].bgline = m_window_lines_drawn;
+ m_layer[1].bg_map = m_vram + m_gb_wndtab_offs;
+ m_layer[1].bg_tiles = m_vram + m_gb_chrgen_offs;
+ m_layer[1].xindex = 0;
+ m_layer[1].xshift = 0;
+ m_layer[1].xstart = xpos;
+ m_layer[1].xend = 160;
+ m_layer[0].xend = xpos;
+ }
+ m_start_x = 0;
+ }
+
+ if (cycles_to_go == 0)
+ {
+ /* Does this belong here? or should it be moved to the else block */
+ /* Handle SGB mask */
+ switch (m_sgb_window_mask)
+ {
+ case 1: /* Freeze screen */
+ return;
+ case 2: /* Blank screen (black) */
+ {
+ rectangle r(SGB_XOFFSET, SGB_XOFFSET + 160-1, SGB_YOFFSET, SGB_YOFFSET + 144 - 1);
+ bitmap.fill(0, r);
+ }
+ return;
+ case 3: /* Blank screen (white - or should it be color 0?) */
+ {
+ rectangle r(SGB_XOFFSET, SGB_XOFFSET + 160 - 1, SGB_YOFFSET, SGB_YOFFSET + 144 - 1);
+ bitmap.fill(32767, r);
+ }
+ return;
+ }
+
+ /* Draw the "border" if we're on the first line */
+ if (m_current_line == 0)
+ {
+ refresh_border();
+ }
+ }
+ if (cycles_to_go < 160)
+ {
+ m_end_x = MIN(160 - cycles_to_go,160);
+
+ /* if background or screen disabled clear line */
+ if (!(LCDCONT & 0x01))
+ {
+ rectangle r(SGB_XOFFSET, SGB_XOFFSET + 160 - 1, m_current_line + SGB_YOFFSET, m_current_line + SGB_YOFFSET);
+ bitmap.fill(0, r);
+ }
+ while (l < 2)
+ {
+ UINT8 xindex, sgb_palette, *map, *tiles;
+ UINT16 data;
+ int i, tile_index;
+
+ if (!m_layer[l].enabled)
+ {
+ l++;
+ continue;
+ }
+ map = m_layer[l].bg_map + ((m_layer[l].bgline << 2) & 0x3E0);
+ tiles = m_layer[l].bg_tiles + ((m_layer[l].bgline & 7) << 1);
+ xindex = m_start_x;
+ if (xindex < m_layer[l].xstart)
+ xindex = m_layer[l].xstart;
+ i = m_end_x;
+ if (i > m_layer[l].xend)
+ i = m_layer[l].xend;
+ i = i - xindex;
+
+ tile_index = (map[m_layer[l].xindex] ^ m_gb_tile_no_mod) * 16;
+ data = tiles[tile_index] | (tiles[tile_index + 1] << 8);
+ data <<= m_layer[l].xshift;
+
+ while (i > 0)
+ {
+ /* Figure out which palette we're using */
+ assert(((m_end_x - i) >> 3) >= 0 && ((m_end_x - i) >> 3) < ARRAY_LENGTH(m_sgb_pal_map));
+ sgb_palette = m_sgb_pal_map[(m_end_x - i) >> 3][m_current_line >> 3] << 2;
+
+ while ((m_layer[l].xshift < 8) && i)
+ {
+ register int colour = ((data & 0x8000) ? 2 : 0) | ((data & 0x0080) ? 1 : 0);
+ plot_pixel(bitmap, xindex + SGB_XOFFSET, m_current_line + SGB_YOFFSET, m_sgb_pal[sgb_palette + m_gb_bpal[colour]]);
+ m_bg_zbuf[xindex] = colour;
+ xindex++;
+ data <<= 1;
+ m_layer[l].xshift++;
+ i--;
+ }
+ if (m_layer[l].xshift == 8)
+ {
+ /* Take possible changes to SCROLLY into account */
+ if (l == 0)
+ {
+ m_layer[0].bgline = (SCROLLY + m_current_line) & 0xFF;
+ map = m_layer[l].bg_map + ((m_layer[l].bgline << 2) & 0x3E0);
+ tiles = m_layer[l].bg_tiles + ((m_layer[l].bgline & 7) << 1);
+ }
+
+ m_layer[l].xindex = (m_layer[l].xindex + 1) & 31;
+ m_layer[l].xshift = 0;
+ tile_index = (map[m_layer[l].xindex] ^ m_gb_tile_no_mod) * 16;
+ data = tiles[tile_index] | (tiles[tile_index + 1] << 8);
+ }
+ }
+ l++;
+ }
+ if ((m_end_x == 160) && (LCDCONT & 0x02))
+ {
+ update_sprites();
+ }
+ m_start_x = m_end_x;
+ }
+ }
+ else
+ {
+ if (!(LCDCONT * 0x80))
+ {
+ /* if screen disabled clear line */
+ if (m_previous_line != m_current_line)
+ {
+ /* Also refresh border here??? */
+ if (m_current_line < 144)
+ {
+ rectangle r(SGB_XOFFSET, SGB_XOFFSET + 160 - 1, m_current_line + SGB_YOFFSET, m_current_line + SGB_YOFFSET);
+ bitmap.fill(0, r);
+ }
+ m_previous_line = m_current_line;
+ }
+ }
+ }
+
+ g_profiler.stop();
+}
+
+/* --- Game Boy Color Specific --- */
+
+void cgb_lcd_device::update_sprites()
+{
+ bitmap_ind16 &bitmap = m_bitmap;
+ UINT8 height, tilemask, line, *oam;
+ int xindex, yindex;
+
+ if (LCDCONT & 0x04)
+ {
+ height = 16;
+ tilemask = 0xFE;
+ }
+ else
+ {
+ height = 8;
+ tilemask = 0xFF;
+ }
+
+ yindex = m_current_line;
+ line = m_current_line + 16;
+
+ oam = m_oam + 39 * 4;
+ for (int i = 39; i >= 0; i--)
+ {
+ /* if sprite is on current line && x-coordinate && x-coordinate is < 168 */
+ if (line >= oam[0] && line < (oam[0] + height) && oam[1] && oam[1] < 168)
+ {
+ UINT16 data;
+ UINT8 bit, pal;
+
+ /* Handle mono mode for GB games */
+ if (!m_gbc_mode)
+ pal = (oam[3] & 0x10) ? 4 : 0;
+ else
+ pal = ((oam[3] & 0x7) * 4);
+
+ xindex = oam[1] - 8;
+ if (oam[3] & 0x40) /* flip y ? */
+ {
+ data = *((UINT16 *) &m_vram[((oam[3] & 0x8)<<10) + (oam[2] & tilemask) * 16 + (height - 1 - line + oam[0]) * 2]);
+ }
+ else
+ {
+ data = *((UINT16 *) &m_vram[((oam[3] & 0x8)<<10) + (oam[2] & tilemask) * 16 + (line - oam[0]) * 2]);
+ }
+
+ data = LITTLE_ENDIANIZE_INT16(data);
+
+ switch (oam[3] & 0xA0)
+ {
+ case 0xA0: /* priority is set (behind bgnd & wnd, flip x) */
+ for (bit = 0; bit < 8; bit++, xindex++)
+ {
+ register int colour = ((data & 0x0100) ? 2 : 0) | ((data & 0x0001) ? 1 : 0);
+ if (colour && !m_bg_zbuf[xindex] && xindex >= 0 && xindex < 160)
+ {
+ if (!m_gbc_mode)
+ colour = pal ? m_gb_spal1[colour] : m_gb_spal0[colour];
+ plot_pixel(bitmap, xindex, yindex, m_cgb_spal[pal + colour]);
+ }
+ data >>= 1;
+ }
+ break;
+ case 0x20: /* priority is not set (overlaps bgnd & wnd, flip x) */
+ for (bit = 0; bit < 8; bit++, xindex++)
+ {
+ register int colour = ((data & 0x0100) ? 2 : 0) | ((data & 0x0001) ? 1 : 0);
+ if ((m_bg_zbuf[xindex] & 0x80) && (m_bg_zbuf[xindex] & 0x7f) && (LCDCONT & 0x1))
+ colour = 0;
+ if (colour && xindex >= 0 && xindex < 160)
+ {
+ if (!m_gbc_mode)
+ colour = pal ? m_gb_spal1[colour] : m_gb_spal0[colour];
+ plot_pixel(bitmap, xindex, yindex, m_cgb_spal[pal + colour]);
+ }
+ data >>= 1;
+ }
+ break;
+ case 0x80: /* priority is set (behind bgnd & wnd, don't flip x) */
+ for (bit = 0; bit < 8; bit++, xindex++)
+ {
+ register int colour = ((data & 0x8000) ? 2 : 0) | ((data & 0x0080) ? 1 : 0);
+ if (colour && !m_bg_zbuf[xindex] && xindex >= 0 && xindex < 160)
+ {
+ if (!m_gbc_mode)
+ colour = pal ? m_gb_spal1[colour] : m_gb_spal0[colour];
+ plot_pixel(bitmap, xindex, yindex, m_cgb_spal[pal + colour]);
+ }
+ data <<= 1;
+ }
+ break;
+ case 0x00: /* priority is not set (overlaps bgnd & wnd, don't flip x) */
+ for (bit = 0; bit < 8; bit++, xindex++)
+ {
+ if (xindex >= 0 && xindex < 160)
+ {
+ register int colour = ((data & 0x8000) ? 2 : 0) | ((data & 0x0080) ? 1 : 0);
+ if ((m_bg_zbuf[xindex] & 0x80) && (m_bg_zbuf[xindex] & 0x7f) && (LCDCONT & 0x1))
+ colour = 0;
+ if (colour)
+ {
+ if (!m_gbc_mode)
+ colour = pal ? m_gb_spal1[colour] : m_gb_spal0[colour];
+ plot_pixel(bitmap, xindex, yindex, m_cgb_spal[pal + colour]);
+ }
+ }
+ data <<= 1;
+ }
+ break;
+ }
+ }
+ oam -= 4;
+ }
+}
+
+void cgb_lcd_device::update_scanline()
+{
+ bitmap_ind16 &bitmap = m_bitmap;
+
+ g_profiler.start(PROFILER_VIDEO);
+
+ if ((LCDSTAT & 0x03) == 0x03)
+ {
+ /* Calcuate number of pixels to render based on time still left on the timer */
+ UINT32 cycles_to_go = m_maincpu->attotime_to_cycles(m_lcd_timer->remaining());
+ int l = 0;
+
+ if (m_start_x < 0)
+ {
+ /* Window is enabled if the hardware says so AND the current scanline is
+ * within the window AND the window X coordinate is <=166 */
+ m_layer[1].enabled = ((LCDCONT & 0x20) && (m_current_line >= WNDPOSY) && (WNDPOSX <= 166)) ? 1 : 0;
+
+ /* BG is enabled if the hardware says so AND (window_off OR (window_on
+ * AND window's X position is >=7 )) */
+ m_layer[0].enabled = ((LCDCONT & 0x01) && ((!m_layer[1].enabled) || (m_layer[1].enabled && (WNDPOSX >= 7)))) ? 1 : 0;
+
+ if (m_layer[0].enabled)
+ {
+ m_layer[0].bgline = (SCROLLY + m_current_line) & 0xFF;
+ m_layer[0].bg_map = m_vram + m_gb_bgdtab_offs;
+ m_layer[0].gbc_map = m_vram + m_gbc_bgdtab_offs;
+ m_layer[0].xindex = SCROLLX >> 3;
+ m_layer[0].xshift = SCROLLX & 7;
+ m_layer[0].xstart = 0;
+ m_layer[0].xend = 160;
+ }
+
+ if (m_layer[1].enabled)
+ {
+ int xpos;
+
+ /* Window X position is offset by 7 so we'll need to adust */
+ xpos = WNDPOSX - 7;
+ if (xpos < 0)
+ xpos = 0;
+
+ m_layer[1].bgline = m_window_lines_drawn;
+ m_layer[1].bg_map = m_vram + m_gb_wndtab_offs;
+ m_layer[1].gbc_map = m_vram + m_gbc_wndtab_offs;
+ m_layer[1].xindex = 0;
+ m_layer[1].xshift = 0;
+ m_layer[1].xstart = xpos;
+ m_layer[1].xend = 160;
+ m_layer[0].xend = xpos;
+ }
+ m_start_x = 0;
+ }
+
+ if (cycles_to_go < 160)
+ {
+ m_end_x = MIN(160 - cycles_to_go, 160);
+ /* Draw empty line when the background is disabled */
+ if (!(LCDCONT & 0x01))
+ {
+ rectangle r(m_start_x, m_end_x - 1, m_current_line, m_current_line);
+ bitmap.fill((!m_gbc_mode) ? 0 : 32767, r);
+ }
+ while (l < 2)
+ {
+ UINT8 xindex, *map, *tiles, *gbcmap;
+ UINT16 data;
+ int i, tile_index;
+
+ if (!m_layer[l].enabled)
+ {
+ l++;
+ continue;
+ }
+ map = m_layer[l].bg_map + ((m_layer[l].bgline << 2) & 0x3E0);
+ gbcmap = m_layer[l].gbc_map + ((m_layer[l].bgline << 2) & 0x3E0);
+ tiles = (gbcmap[m_layer[l].xindex] & 0x08) ? (m_vram + m_gbc_chrgen_offs) : (m_vram + m_gb_chrgen_offs);
+
+ /* Check for vertical flip */
+ if (gbcmap[m_layer[l].xindex] & 0x40)
+ {
+ tiles += ((7 - (m_layer[l].bgline & 0x07)) << 1);
+ }
+ else
+ {
+ tiles += ((m_layer[l].bgline & 0x07) << 1);
+ }
+ xindex = m_start_x;
+ if (xindex < m_layer[l].xstart)
+ xindex = m_layer[l].xstart;
+ i = m_end_x;
+ if (i > m_layer[l].xend)
+ i = m_layer[l].xend;
+ i = i - xindex;
+
+ tile_index = (map[m_layer[l].xindex] ^ m_gb_tile_no_mod) * 16;
+ data = tiles[tile_index] | (tiles[tile_index + 1] << 8);
+ /* Check for horinzontal flip */
+ if (gbcmap[m_layer[l].xindex] & 0x20)
+ {
+ data >>= m_layer[l].xshift;
+ }
+ else
+ {
+ data <<= m_layer[l].xshift;
+ }
+
+ while (i > 0)
+ {
+ while ((m_layer[l].xshift < 8) && i)
+ {
+ int colour;
+ /* Check for horinzontal flip */
+ if (gbcmap[m_layer[l].xindex] & 0x20)
+ {
+ colour = ((data & 0x0100) ? 2 : 0) | ((data & 0x0001) ? 1 : 0);
+ data >>= 1;
+ }
+ else
+ {
+ colour = ((data & 0x8000) ? 2 : 0) | ((data & 0x0080) ? 1 : 0);
+ data <<= 1;
+ }
+ plot_pixel(bitmap, xindex, m_current_line, m_cgb_bpal[(!m_gbc_mode) ? m_gb_bpal[colour] : (((gbcmap[m_layer[l].xindex] & 0x07) * 4) + colour)]);
+ m_bg_zbuf[xindex] = colour + (gbcmap[m_layer[l].xindex] & 0x80);
+ xindex++;
+ m_layer[l].xshift++;
+ i--;
+ }
+ if (m_layer[l].xshift == 8)
+ {
+ /* Take possible changes to SCROLLY into account */
+ if (l == 0)
+ {
+ m_layer[0].bgline = (SCROLLY + m_current_line) & 0xFF;
+ map = m_layer[l].bg_map + ((m_layer[l].bgline << 2) & 0x3E0);
+ gbcmap = m_layer[l].gbc_map + ((m_layer[l].bgline << 2) & 0x3E0);
+ }
+
+ m_layer[l].xindex = (m_layer[l].xindex + 1) & 31;
+ m_layer[l].xshift = 0;
+ tiles = (gbcmap[m_layer[l].xindex] & 0x08) ? (m_vram + m_gbc_chrgen_offs) : (m_vram + m_gb_chrgen_offs);
+
+ /* Check for vertical flip */
+ if (gbcmap[m_layer[l].xindex] & 0x40)
+ {
+ tiles += ((7 - (m_layer[l].bgline & 0x07)) << 1);
+ }
+ else
+ {
+ tiles += ((m_layer[l].bgline & 0x07) << 1);
+ }
+ tile_index = (map[m_layer[l].xindex] ^ m_gb_tile_no_mod) * 16;
+ data = tiles[tile_index] | (tiles[tile_index + 1] << 8);
+ }
+ }
+ l++;
+ }
+ if (m_end_x == 160 && (LCDCONT & 0x02))
+ {
+ update_sprites();
+ }
+ m_start_x = m_end_x;
+ }
+ }
+ else
+ {
+ if (!(LCDCONT & 0x80))
+ {
+ /* Draw an empty line when LCD is disabled */
+ if (m_previous_line != m_current_line)
+ {
+ if (m_current_line < 144)
+ {
+ const rectangle &r1 = m_screen->visible_area();
+ rectangle r(r1.min_x, r1.max_x, m_current_line, m_current_line);
+ bitmap.fill((!m_gbc_mode) ? 0 : 32767 , r);
+ }
+ m_previous_line = m_current_line;
+ }
+ }
+ }
+
+ g_profiler.stop();
+}
+
+
+TIMER_CALLBACK_MEMBER(gb_lcd_device::video_init_vbl)
+{
+ m_maincpu->set_input_line(VBL_INT, ASSERT_LINE);
+}
+
+UINT32 gb_lcd_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+ return 0;
+}
+
+
+void gb_lcd_device::increment_scanline()
+{
+ m_current_line = (m_current_line + 1) % 154;
+ if (LCDCONT & 0x80)
+ {
+ CURLINE = m_current_line;
+ }
+ if (m_current_line == 0)
+ {
+ m_window_lines_drawn = 0;
+ }
+}
+
+TIMER_CALLBACK_MEMBER(gb_lcd_device::lcd_timer_proc)
+{
+ static const int gb_sprite_cycles[] = { 0, 8, 20, 32, 44, 52, 64, 76, 88, 96, 108 };
+
+ m_state = param;
+
+ if (LCDCONT & 0x80)
+ {
+ switch (m_state)
+ {
+ case GB_LCD_STATE_LYXX_PRE_M0: /* Just before switching to mode 0 */
+ m_mode = 0;
+ if (LCDSTAT & 0x08)
+ {
+ if (!m_mode_irq)
+ {
+ if (!m_line_irq && !m_delayed_line_irq)
+ {
+ m_mode_irq = 1;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ else
+ {
+ m_mode_irq = 0;
+ }
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LYXX_M0);
+ break;
+ case GB_LCD_STATE_LYXX_M0: /* Switch to mode 0 */
+ /* update current scanline */
+ update_scanline();
+ /* Increment the number of window lines drawn if enabled */
+ if (m_layer[1].enabled)
+ {
+ m_window_lines_drawn++;
+ }
+ m_previous_line = m_current_line;
+ /* Set Mode 0 lcdstate */
+ m_mode = 0;
+ LCDSTAT &= 0xFC;
+ m_oam_locked = UNLOCKED;
+ m_vram_locked = UNLOCKED;
+ /*
+ There seems to a kind of feature in the Game Boy hardware when the lowest bits of the
+ SCROLLX register equals 3 or 7, then the delayed M0 irq is triggered 4 cycles later
+ than usual.
+ The SGB probably has the same bug.
+ */
+ if ((SCROLLX & 0x03) == 0x03)
+ {
+ m_scrollx_adjust += 4;
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LYXX_M0_SCX3);
+ break;
+ }
+ case GB_LCD_STATE_LYXX_M0_SCX3:
+ /* Generate lcd interrupt if requested */
+ if (!m_mode_irq && (LCDSTAT & 0x08) &&
+ ((!m_line_irq && m_delayed_line_irq) || !(LCDSTAT & 0x40)))
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(196 - m_scrollx_adjust - m_sprite_cycles), GB_LCD_STATE_LYXX_M0_PRE_INC);
+ break;
+ case GB_LCD_STATE_LYXX_M0_PRE_INC: /* Just before incrementing the line counter go to mode 2 internally */
+ if (CURLINE < 143)
+ {
+ m_mode = 2;
+ m_triggering_mode_irq = (LCDSTAT & 0x20) ? 1 : 0;
+ if (m_triggering_mode_irq)
+ {
+ if (!m_mode_irq)
+ {
+ if (!m_line_irq && !m_delayed_line_irq)
+ {
+ m_mode_irq = 1;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ else
+ {
+ m_mode_irq = 0;
+ }
+ }
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LYXX_M0_INC);
+ break;
+ case GB_LCD_STATE_LYXX_M0_INC: /* Increment LY, stay in M0 for 4 more cycles */
+ increment_scanline();
+ m_delayed_line_irq = m_line_irq;
+ m_triggering_line_irq = ((CMPLINE == CURLINE) && (LCDSTAT & 0x40)) ? 1 : 0;
+ m_line_irq = 0;
+ if (!m_mode_irq && !m_delayed_line_irq && m_triggering_line_irq && !m_triggering_mode_irq)
+ {
+ m_line_irq = m_triggering_line_irq;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ /* Reset LY==LYC STAT bit */
+ LCDSTAT &= 0xFB;
+ /* Check if we're going into VBlank next */
+ if (CURLINE == 144)
+ {
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY9X_M1);
+ }
+ else
+ {
+ /* Internally switch to mode 2 */
+ m_mode = 2;
+ /* Generate lcd interrupt if requested */
+ if (!m_mode_irq && m_triggering_mode_irq &&
+ ((!m_triggering_line_irq && !m_delayed_line_irq) || !(LCDSTAT & 0x40)))
+ {
+ m_mode_irq = 1;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LYXX_M2);
+ }
+ break;
+ case GB_LCD_STATE_LY00_M2: /* Switch to mode 2 on line #0 */
+ /* Set Mode 2 lcdstate */
+ m_mode = 2;
+ LCDSTAT = (LCDSTAT & 0xFC) | 0x02;
+ m_oam_locked = LOCKED;
+ /* Generate lcd interrupt if requested */
+ if ((LCDSTAT & 0x20) && !m_line_irq)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ /* Check for regular compensation of x-scroll register */
+ m_scrollx_adjust = (SCROLLX & 0x04) ? 4 : 0;
+ /* Mode 2 lasts approximately 80 clock cycles */
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(80), GB_LCD_STATE_LYXX_M3);
+ break;
+ case GB_LCD_STATE_LYXX_M2: /* Switch to mode 2 */
+ /* Update STAT register to the correct state */
+ LCDSTAT = (LCDSTAT & 0xFC) | 0x02;
+ m_oam_locked = LOCKED;
+ /* Generate lcd interrupt if requested */
+ if ((m_delayed_line_irq && m_triggering_line_irq && !(LCDSTAT & 0x20)) ||
+ (!m_mode_irq && !m_line_irq && !m_delayed_line_irq && m_triggering_mode_irq))
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ m_line_irq = m_triggering_line_irq;
+ m_triggering_mode_irq = 0;
+ /* Check if LY==LYC STAT bit should be set */
+ if (CURLINE == CMPLINE)
+ {
+ LCDSTAT |= 0x04;
+ }
+ /* Check for regular compensation of x-scroll register */
+ m_scrollx_adjust = (SCROLLX & 0x04) ? 4 : 0;
+ /* Mode 2 last for approximately 80 clock cycles */
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(80), GB_LCD_STATE_LYXX_M3);
+ break;
+ case GB_LCD_STATE_LYXX_M3: /* Switch to mode 3 */
+ select_sprites();
+ m_sprite_cycles = gb_sprite_cycles[m_sprCount];
+ /* Set Mode 3 lcdstate */
+ m_mode = 3;
+ LCDSTAT = (LCDSTAT & 0xFC) | 0x03;
+ m_vram_locked = LOCKED;
+ /* Check for compensations of x-scroll register */
+ /* Mode 3 lasts for approximately 172+cycles needed to handle sprites clock cycles */
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(168 + m_scrollx_adjust + m_sprite_cycles), GB_LCD_STATE_LYXX_PRE_M0);
+ m_start_x = -1;
+ break;
+ case GB_LCD_STATE_LY9X_M1: /* Switch to or stay in mode 1 */
+ if (CURLINE == 144)
+ {
+ /* Trigger VBlank interrupt */
+ m_maincpu->set_input_line(VBL_INT, ASSERT_LINE);
+ /* Set VBlank lcdstate */
+ m_mode = 1;
+ LCDSTAT = (LCDSTAT & 0xFC) | 0x01;
+ /* Trigger LCD interrupt if requested */
+ if (LCDSTAT & 0x10)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ /* Check if LY==LYC STAT bit should be set */
+ if (CURLINE == CMPLINE)
+ {
+ LCDSTAT |= 0x04;
+ }
+ if (m_delayed_line_irq && m_triggering_line_irq)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(452), GB_LCD_STATE_LY9X_M1_INC);
+ break;
+ case GB_LCD_STATE_LY9X_M1_INC: /* Increment scanline counter */
+ increment_scanline();
+ m_delayed_line_irq = m_line_irq;
+ m_triggering_line_irq = ((CMPLINE == CURLINE) && (LCDSTAT & 0x40)) ? 1 : 0;
+ m_line_irq = 0;
+ if (!m_delayed_line_irq && m_triggering_line_irq)
+ {
+ m_line_irq = m_triggering_line_irq;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ /* Reset LY==LYC STAT bit */
+ LCDSTAT &= 0xFB;
+ if (m_current_line == 153)
+ {
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY00_M1);
+ }
+ else
+ {
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY9X_M1);
+ }
+ break;
+ case GB_LCD_STATE_LY00_M1: /* we stay in VBlank but current line counter should already be incremented */
+ /* Check LY=LYC for line #153 */
+ if (m_delayed_line_irq)
+ {
+ if (m_triggering_line_irq)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ m_delayed_line_irq = m_delayed_line_irq | m_line_irq;
+ if (CURLINE == CMPLINE)
+ {
+ LCDSTAT |= 0x04;
+ }
+ increment_scanline();
+ m_triggering_line_irq = ((CMPLINE == CURLINE) && (LCDSTAT & 0x40)) ? 1 : 0;
+ m_line_irq = 0;
+ LCDSTAT &= 0xFB;
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4/*8*/), GB_LCD_STATE_LY00_M1_1);
+ break;
+ case GB_LCD_STATE_LY00_M1_1:
+ if (!m_delayed_line_irq && m_triggering_line_irq)
+ {
+ m_line_irq = m_triggering_line_irq;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY00_M1_2);
+ break;
+ case GB_LCD_STATE_LY00_M1_2: /* Rest of line #0 during VBlank */
+ if (m_delayed_line_irq && m_triggering_line_irq)
+ {
+ m_line_irq = m_triggering_line_irq;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ if (CURLINE == CMPLINE)
+ {
+ LCDSTAT |= 0x04;
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(444), GB_LCD_STATE_LY00_M0);
+ break;
+ case GB_LCD_STATE_LY00_M0: /* The STAT register seems to go to 0 for about 4 cycles */
+ /* Set Mode 0 lcdstat */
+ m_mode = 0;
+ LCDSTAT = (LCDSTAT & 0xFC);
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY00_M2);
+ break;
+ }
+ }
+ else
+ {
+ increment_scanline();
+ if (m_current_line < 144)
+ {
+ update_scanline();
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(456));
+ }
+}
+
+
+// CGB specific code
+
+void cgb_lcd_device::hdma_trans(UINT16 length)
+{
+ UINT16 src, dst;
+ address_space &space = m_maincpu->space(AS_PROGRAM);
+
+ src = ((UINT16)HDMA1 << 8) | (HDMA2 & 0xF0);
+ dst = ((UINT16)(HDMA3 & 0x1F) << 8) | (HDMA4 & 0xF0);
+ dst |= 0x8000;
+ while (length > 0)
+ {
+ space.write_byte(dst++, space.read_byte(src++));
+ length--;
+ }
+ HDMA1 = src >> 8;
+ HDMA2 = src & 0xF0;
+ HDMA3 = 0x1f & (dst >> 8);
+ HDMA4 = dst & 0xF0;
+ HDMA5--;
+ if ((HDMA5 & 0x7f) == 0x7f)
+ {
+ HDMA5 = 0xff;
+ m_hdma_enabled = 0;
+ }
+}
+
+
+TIMER_CALLBACK_MEMBER(cgb_lcd_device::lcd_timer_proc)
+{
+ static const int cgb_sprite_cycles[] = { 0, 8, 20, 32, 44, 52, 64, 76, 88, 96, 108 };
+
+ m_state = param;
+
+ if (LCDCONT & 0x80)
+ {
+ switch (m_state)
+ {
+ case GB_LCD_STATE_LYXX_PRE_M0: /* Just before switching to mode 0 */
+ m_mode = 0;
+ if (LCDSTAT & 0x08)
+ {
+ if (!m_mode_irq)
+ {
+ if (!m_line_irq && !m_delayed_line_irq)
+ {
+ m_mode_irq = 1;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ else
+ {
+ m_mode_irq = 0;
+ }
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LYXX_M0);
+ break;
+ case GB_LCD_STATE_LYXX_M0: /* Switch to mode 0 */
+ /* update current scanline */
+ update_scanline();
+ /* Increment the number of window lines drawn if enabled */
+ if (m_layer[1].enabled)
+ {
+ m_window_lines_drawn++;
+ }
+ m_previous_line = m_current_line;
+ /* Set Mode 0 lcdstate */
+ m_mode = 0;
+ LCDSTAT &= 0xFC;
+ m_oam_locked = UNLOCKED;
+ m_vram_locked = UNLOCKED;
+ /*
+ There seems to a kind of feature in the Game Boy hardware when the lowest bits of the
+ SCROLLX register equals 3 or 7, then the delayed M0 irq is triggered 4 cycles later
+ than usual.
+ The SGB probably has the same bug.
+ */
+ m_triggering_mode_irq = (LCDSTAT & 0x08) ? 1 : 0;
+ if ((SCROLLX & 0x03) == 0x03)
+ {
+ m_scrollx_adjust += 4;
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LYXX_M0_SCX3);
+ break;
+ }
+ case GB_LCD_STATE_LYXX_M0_SCX3:
+ /* Generate lcd interrupt if requested */
+ if (!m_mode_irq && m_triggering_mode_irq &&
+ ((!m_line_irq && m_delayed_line_irq) || !(LCDSTAT & 0x40)))
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ m_triggering_mode_irq = 0;
+ }
+ if ((SCROLLX & 0x03) == 0x03)
+ {
+ m_pal_locked = UNLOCKED;
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LYXX_M0_GBC_PAL);
+ break;
+ case GB_LCD_STATE_LYXX_M0_GBC_PAL:
+ m_pal_locked = UNLOCKED;
+ /* Check for HBLANK DMA */
+ if (m_hdma_enabled)
+ {
+ hdma_trans(0x10);
+// cpunum_set_reg(0, LR35902_DMA_CYCLES, 36);
+ }
+ else
+ {
+ m_hdma_possible = 1;
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(192 - m_scrollx_adjust - m_sprite_cycles), GB_LCD_STATE_LYXX_M0_PRE_INC);
+ break;
+ case GB_LCD_STATE_LYXX_M0_PRE_INC: /* Just before incrementing the line counter go to mode 2 internally */
+ m_cmp_line = CMPLINE;
+ if (CURLINE < 143)
+ {
+ m_mode = 2;
+ if (LCDSTAT & 0x20)
+ {
+ if (!m_mode_irq)
+ {
+ if (!m_line_irq && !m_delayed_line_irq)
+ {
+ m_mode_irq = 1;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ else
+ {
+ m_mode_irq = 0;
+ }
+ }
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LYXX_M0_INC);
+ break;
+ case GB_LCD_STATE_LYXX_M0_INC: /* Increment LY, stay in M0 for 4 more cycles */
+ increment_scanline();
+ m_delayed_line_irq = m_line_irq;
+ m_triggering_line_irq = ((m_cmp_line == CURLINE) && (LCDSTAT & 0x40)) ? 1 : 0;
+ m_line_irq = 0;
+ if (!m_mode_irq && !m_delayed_line_irq && m_triggering_line_irq && !(LCDSTAT & 0x20))
+ {
+ m_line_irq = m_triggering_line_irq;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ m_hdma_possible = 0;
+ /* Check if we're going into VBlank next */
+ if (CURLINE == 144)
+ {
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY9X_M1);
+ }
+ else
+ {
+ /* Internally switch to mode 2 */
+ m_mode = 2;
+ /* Generate lcd interrupt if requested */
+ if (!m_mode_irq && (LCDSTAT & 0x20) &&
+ ((!m_triggering_line_irq && !m_delayed_line_irq) || !(LCDSTAT & 0x40)))
+ {
+ m_mode_irq = 1;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LYXX_M2);
+ }
+ break;
+ case GB_LCD_STATE_LY00_M2: /* Switch to mode 2 on line #0 */
+ /* Set Mode 2 lcdstate */
+ m_mode = 2;
+ LCDSTAT = (LCDSTAT & 0xFC) | 0x02;
+ m_oam_locked = LOCKED;
+ /* Generate lcd interrupt if requested */
+ if ((LCDSTAT & 0x20) && !m_line_irq)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ /* Check for regular compensation of x-scroll register */
+ m_scrollx_adjust = (SCROLLX & 0x04) ? 4 : 0;
+ /* Mode 2 lasts approximately 80 clock cycles */
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(80), GB_LCD_STATE_LYXX_M3);
+ break;
+ case GB_LCD_STATE_LYXX_M2: /* Switch to mode 2 */
+ /* Update STAT register to the correct state */
+ LCDSTAT = (LCDSTAT & 0xFC) | 0x02;
+ m_oam_locked = LOCKED;
+ /* Generate lcd interrupt if requested */
+ if ((m_delayed_line_irq && m_triggering_line_irq && !(LCDSTAT & 0x20)) ||
+ (!m_mode_irq && !m_line_irq && !m_delayed_line_irq && (LCDSTAT & 0x20)))
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ m_line_irq = m_triggering_line_irq;
+ /* Check if LY==LYC STAT bit should be set */
+ if (CURLINE == CMPLINE)
+ {
+ LCDSTAT |= 0x04;
+ }
+ else
+ {
+ LCDSTAT &= ~0x04;
+ }
+ /* Check for regular compensation of x-scroll register */
+ m_scrollx_adjust = (SCROLLX & 0x04) ? 4 : 0;
+ /* Mode 2 last for approximately 80 clock cycles */
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(80), GB_LCD_STATE_LYXX_M3);
+ break;
+ case GB_LCD_STATE_LYXX_M3: /* Switch to mode 3 */
+ select_sprites();
+ m_sprite_cycles = cgb_sprite_cycles[m_sprCount];
+ /* Set Mode 3 lcdstate */
+ m_mode = 3;
+ LCDSTAT = (LCDSTAT & 0xFC) | 0x03;
+ m_vram_locked = LOCKED;
+ m_pal_locked = LOCKED;
+ /* Check for compensations of x-scroll register */
+ /* Mode 3 lasts for approximately 172+cycles needed to handle sprites clock cycles */
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(168 + m_scrollx_adjust + m_sprite_cycles), GB_LCD_STATE_LYXX_PRE_M0);
+ m_start_x = -1;
+ break;
+ case GB_LCD_STATE_LY9X_M1: /* Switch to or stay in mode 1 */
+ if (CURLINE == 144)
+ {
+ /* Trigger VBlank interrupt */
+ m_maincpu->set_input_line(VBL_INT, ASSERT_LINE);
+ /* Set VBlank lcdstate */
+ m_mode = 1;
+ LCDSTAT = (LCDSTAT & 0xFC) | 0x01;
+ /* Trigger LCD interrupt if requested */
+ if (LCDSTAT & 0x10)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ /* Check if LY==LYC STAT bit should be set */
+ if (CURLINE == CMPLINE)
+ {
+ LCDSTAT |= 0x04;
+ }
+ else
+ {
+ LCDSTAT &= ~0x04;
+ }
+ if (m_delayed_line_irq && m_triggering_line_irq)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(452), GB_LCD_STATE_LY9X_M1_INC);
+ break;
+ case GB_LCD_STATE_LY9X_M1_INC: /* Increment scanline counter */
+ increment_scanline();
+ m_delayed_line_irq = m_line_irq;
+ m_triggering_line_irq = ((CMPLINE == CURLINE) && (LCDSTAT & 0x40)) ? 1 : 0;
+ m_line_irq = 0;
+ if (!m_delayed_line_irq && m_triggering_line_irq)
+ {
+ m_line_irq = m_triggering_line_irq;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ if (m_current_line == 153)
+ {
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY00_M1);
+ }
+ else
+ {
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY9X_M1);
+ }
+ break;
+ case GB_LCD_STATE_LY00_M1: /* we stay in VBlank but current line counter should already be incremented */
+ /* Check LY=LYC for line #153 */
+ if (m_delayed_line_irq)
+ {
+ if (m_triggering_line_irq)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ m_delayed_line_irq = m_delayed_line_irq | m_line_irq;
+ if (CURLINE == CMPLINE)
+ {
+ LCDSTAT |= 0x04;
+ }
+ else
+ {
+ LCDSTAT &= ~0x04;
+ }
+ increment_scanline();
+ m_triggering_line_irq = ((CMPLINE == CURLINE) && (LCDSTAT & 0x40)) ? 1 : 0;
+ m_line_irq = 0;
+ LCDSTAT &= 0xFB;
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY00_M1_1);
+ break;
+ case GB_LCD_STATE_LY00_M1_1:
+ if (!m_delayed_line_irq && m_triggering_line_irq)
+ {
+ m_line_irq = m_triggering_line_irq;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY00_M1_2);
+ break;
+ case GB_LCD_STATE_LY00_M1_2: /* Rest of line #0 during VBlank */
+ if (m_delayed_line_irq && m_triggering_line_irq)
+ {
+ m_line_irq = m_triggering_line_irq;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ if (CURLINE == CMPLINE)
+ {
+ LCDSTAT |= 0x04;
+ }
+ else
+ {
+ LCDSTAT &= ~0x04;
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(444), GB_LCD_STATE_LY00_M0);
+ break;
+ case GB_LCD_STATE_LY00_M0: /* The STAT register seems to go to 0 for about 4 cycles */
+ /* Set Mode 0 lcdstat */
+ m_mode = 0;
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(4), GB_LCD_STATE_LY00_M2);
+ break;
+ }
+ }
+ else
+ {
+ increment_scanline();
+ if (m_current_line < 144)
+ {
+ update_scanline();
+ }
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(456));
+ }
+}
+
+
+void gb_lcd_device::lcd_switch_on()
+{
+ m_current_line = 0;
+ m_previous_line = 153;
+ m_window_lines_drawn = 0;
+ m_line_irq = 0;
+ m_delayed_line_irq = 0;
+ m_mode = 0;
+ m_oam_locked = LOCKED; /* TODO: Investigate whether this OAM locking is correct. */
+ /* Check for LY=LYC coincidence */
+ if (CURLINE == CMPLINE)
+ {
+ LCDSTAT |= 0x04;
+ /* Generate lcd interrupt if requested */
+ if (LCDSTAT & 0x40)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ m_state = GB_LCD_STATE_LY00_M2;
+ m_lcd_timer->adjust(m_maincpu->cycles_to_attotime(80), GB_LCD_STATE_LYXX_M3);
+}
+
+
+
+
+READ8_MEMBER(gb_lcd_device::vram_r)
+{
+ return (m_vram_locked == LOCKED) ? 0xff : m_vram[offset + (m_vram_bank * 0x2000)];
+}
+
+WRITE8_MEMBER(gb_lcd_device::vram_w)
+{
+ if (m_vram_locked == LOCKED)
+ return;
+
+ m_vram[offset + (m_vram_bank * 0x2000)] = data;
+}
+
+READ8_MEMBER(gb_lcd_device::oam_r)
+{
+ return (m_oam_locked == LOCKED) ? 0xff : m_oam[offset];
+}
+
+WRITE8_MEMBER(gb_lcd_device::oam_w)
+{
+ if (m_oam_locked == LOCKED || offset >= 0xa0)
+ return;
+
+ m_oam[offset] = data;
+}
+
+
+
+READ8_MEMBER(gb_lcd_device::video_r)
+{
+ return m_vid_regs[offset];
+}
+
+WRITE8_MEMBER(gb_lcd_device::video_w)
+{
+ switch (offset)
+ {
+ case 0x00: /* LCDC - LCD Control */
+ m_gb_chrgen_offs = (data & 0x10) ? 0x0000 : 0x0800;
+ m_gb_tile_no_mod = (data & 0x10) ? 0x00 : 0x80;
+ m_gb_bgdtab_offs = (data & 0x08) ? 0x1c00 : 0x1800;
+ m_gb_wndtab_offs = (data & 0x40) ? 0x1c00 : 0x1800;
+ /* if LCD controller is switched off, set STAT and LY to 00 */
+ if (!(data & 0x80))
+ {
+ LCDSTAT &= ~0x03;
+ CURLINE = 0;
+ m_oam_locked = UNLOCKED;
+ m_vram_locked = UNLOCKED;
+ }
+ /* If LCD is being switched on */
+ if (!(LCDCONT & 0x80) && (data & 0x80))
+ {
+ lcd_switch_on();
+ }
+ break;
+ case 0x01: /* STAT - LCD Status */
+ data = 0x80 | (data & 0x78) | (LCDSTAT & 0x07);
+ /*
+ Check for the STAT bug:
+ Writing to STAT when the LCD controller is active causes a STAT
+ interrupt to be triggered.
+ */
+ if (LCDCONT & 0x80)
+ {
+ /* Triggers seen so far:
+ - 0x40 -> 0x00 - trigger
+ - 0x00 -> 0x08 - trigger
+ - 0x08 -> 0x00 - don't trigger
+ - 0x00 -> 0x20 (mode 3) - trigger
+ - 0x00 -> 0x60 (mode 2) - don't trigger
+ - 0x20 -> 0x60 (mode 3) - trigger
+ - 0x20 -> 0x40 (mode 3) - trigger
+ - 0x40 -> 0x20 (mode 2) - don't trigger
+ - 0x40 -> 0x08 (mode 0) - don't trigger
+ - 0x00 -> 0x40 - trigger only if LY==LYC
+ - 0x20 -> 0x00/0x08/0x10/0x20/0x40 (mode 2, after m2int) - don't trigger
+ - 0x20 -> 0x00/0x08/0x10/0x20/0x40 (mode 3, after m2int) - don't trigger
+ */
+ if (!m_mode_irq && ((m_mode == 1) ||
+ ((LCDSTAT & 0x40) && !(data & 0x68)) ||
+ (!(LCDSTAT & 0x40) && (data & 0x40) && (LCDSTAT & 0x04)) ||
+ (!(LCDSTAT & 0x48) && (data & 0x08)) ||
+ ((LCDSTAT & 0x60) == 0x00 && (data & 0x60) == 0x20) ||
+ ((LCDSTAT & 0x60) == 0x20 && (data & 0x40))
+ ))
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ /*
+ - 0x20 -> 0x08/0x18/0x28/0x48 (mode 0, after m2int) - trigger
+ - 0x20 -> 0x00/0x10/0x20/0x40 (mode 0, after m2int) - trigger (stat bug)
+ - 0x00 -> 0xXX (mode 0) - trigger stat bug
+ */
+ if (m_mode_irq && m_mode == 0)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ break;
+ case 0x04: /* LY - LCD Y-coordinate */
+ return;
+ case 0x05: /* LYC */
+ if (CMPLINE != data)
+ {
+ if (CURLINE == data)
+ {
+ if (m_state != GB_LCD_STATE_LYXX_M0_INC && m_state != GB_LCD_STATE_LY9X_M1_INC)
+ {
+ LCDSTAT |= 0x04;
+ /* Generate lcd interrupt if requested */
+ if (LCDSTAT & 0x40)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ }
+ else
+ {
+ LCDSTAT &= 0xFB;
+ m_triggering_line_irq = 0;
+ }
+ }
+ break;
+ case 0x06: /* DMA - DMA Transfer and Start Address */
+ {
+ UINT8 *P = m_oam;
+ offset = (UINT16) data << 8;
+ for (data = 0; data < 0xA0; data++)
+ *P++ = space.read_byte(offset++);
+ }
+ return;
+ case 0x07: /* BGP - Background Palette */
+ update_scanline();
+ m_gb_bpal[0] = data & 0x3;
+ m_gb_bpal[1] = (data & 0xC) >> 2;
+ m_gb_bpal[2] = (data & 0x30) >> 4;
+ m_gb_bpal[3] = (data & 0xC0) >> 6;
+ break;
+ case 0x08: /* OBP0 - Object Palette 0 */
+// update_scanline();
+ m_gb_spal0[0] = data & 0x3;
+ m_gb_spal0[1] = (data & 0xC) >> 2;
+ m_gb_spal0[2] = (data & 0x30) >> 4;
+ m_gb_spal0[3] = (data & 0xC0) >> 6;
+ break;
+ case 0x09: /* OBP1 - Object Palette 1 */
+// update_scanline();
+ m_gb_spal1[0] = data & 0x3;
+ m_gb_spal1[1] = (data & 0xC) >> 2;
+ m_gb_spal1[2] = (data & 0x30) >> 4;
+ m_gb_spal1[3] = (data & 0xC0) >> 6;
+ break;
+ case 0x02: /* SCY - Scroll Y */
+ case 0x03: /* SCX - Scroll X */
+ update_scanline();
+ break;
+ case 0x0A: /* WY - Window Y position */
+ case 0x0B: /* WX - Window X position */
+ break;
+ default: /* Unknown register, no change */
+ return;
+ }
+ m_vid_regs[offset] = data;
+}
+
+READ8_MEMBER(cgb_lcd_device::video_r)
+{
+ switch (offset)
+ {
+ case 0x11: /* FF51 */
+ case 0x12: /* FF52 */
+ case 0x13: /* FF53 */
+ case 0x14: /* FF54 */
+ return 0xFF;
+ case 0x29: /* FF69 */
+ case 0x2B: /* FF6B */
+ if (m_pal_locked == LOCKED)
+ {
+ return 0xFF;
+ }
+ break;
+ }
+ return m_vid_regs[offset];
+}
+
+WRITE8_MEMBER(cgb_lcd_device::video_w)
+{
+ switch (offset)
+ {
+ case 0x00: /* LCDC - LCD Control */
+ m_gb_chrgen_offs = (data & 0x10) ? 0x0000 : 0x0800;
+ m_gbc_chrgen_offs = (data & 0x10) ? 0x2000 : 0x2800;
+ m_gb_tile_no_mod = (data & 0x10) ? 0x00 : 0x80;
+ m_gb_bgdtab_offs = (data & 0x08) ? 0x1c00 : 0x1800;
+ m_gbc_bgdtab_offs = (data & 0x08) ? 0x3c00 : 0x3800;
+ m_gb_wndtab_offs = (data & 0x40) ? 0x1c00 : 0x1800;
+ m_gbc_wndtab_offs = (data & 0x40) ? 0x3c00 : 0x3800;
+ /* if LCD controller is switched off, set STAT to 00 */
+ if (!(data & 0x80))
+ {
+ LCDSTAT &= ~0x03;
+ CURLINE = 0;
+ m_oam_locked = UNLOCKED;
+ m_vram_locked = UNLOCKED;
+ m_pal_locked = UNLOCKED;
+ }
+ /* If LCD is being switched on */
+ if (!(LCDCONT & 0x80) && (data & 0x80))
+ {
+ lcd_switch_on();
+ }
+ break;
+ case 0x01: /* STAT - LCD Status */
+ data = 0x80 | (data & 0x78) | (LCDSTAT & 0x07);
+ if (LCDCONT & 0x80)
+ {
+ /*
+ - 0x20 -> 0x08/0x18/0x28/0x48 (mode 0, after m2int) - trigger
+ */
+ if (m_mode_irq && m_mode == 0 && (LCDSTAT & 0x28) == 0x20 && (data & 0x08))
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ /* Check if line irqs are being disabled */
+ if (!(data & 0x40))
+ {
+ m_delayed_line_irq = 0;
+ }
+ /* Check if line irqs are being enabled */
+ if (!(LCDSTAT & 0x40) && (data & 0x40))
+ {
+ if (CMPLINE == CURLINE)
+ {
+ m_line_irq = 1;
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ }
+ break;
+ case 0x05: /* LYC */
+ if (CMPLINE != data)
+ {
+ if ((m_state != GB_LCD_STATE_LYXX_M0_PRE_INC && CURLINE == data) ||
+ (m_state == GB_LCD_STATE_LYXX_M0_INC && m_triggering_line_irq))
+ {
+ LCDSTAT |= 0x04;
+ /* Generate lcd interrupt if requested */
+ if (LCDSTAT & 0x40)
+ {
+ m_maincpu->set_input_line(LCD_INT, ASSERT_LINE);
+ }
+ }
+ else
+ {
+ LCDSTAT &= 0xFB;
+ m_triggering_line_irq = 0;
+ m_cmp_line = data;
+ }
+ }
+ break;
+ case 0x07: /* BGP - GB background palette */
+ update_scanline();
+ m_gb_bpal[0] = data & 0x3;
+ m_gb_bpal[1] = (data & 0xC) >> 2;
+ m_gb_bpal[2] = (data & 0x30) >> 4;
+ m_gb_bpal[3] = (data & 0xC0) >> 6;
+ break;
+ case 0x08: /* OBP0 - GB Object 0 palette */
+ m_gb_spal0[0] = data & 0x3;
+ m_gb_spal0[1] = (data & 0xC) >> 2;
+ m_gb_spal0[2] = (data & 0x30) >> 4;
+ m_gb_spal0[3] = (data & 0xC0) >> 6;
+ break;
+ case 0x09: /* OBP1 - GB Object 1 palette */
+ m_gb_spal1[0] = data & 0x3;
+ m_gb_spal1[1] = (data & 0xC) >> 2;
+ m_gb_spal1[2] = (data & 0x30) >> 4;
+ m_gb_spal1[3] = (data & 0xC0) >> 6;
+ break;
+ case 0x0c: /* Undocumented register involved in selecting gb/gbc mode */
+ logerror("Write to undocumented register: %X = %X\n", offset, data);
+ break;
+ case 0x0F: /* VBK - VRAM bank select */
+ m_vram_bank = data & 0x01;
+ data |= 0xFE;
+ break;
+ case 0x11: /* HDMA1 - HBL General DMA - Source High */
+ break;
+ case 0x12: /* HDMA2 - HBL General DMA - Source Low */
+ data &= 0xF0;
+ break;
+ case 0x13: /* HDMA3 - HBL General DMA - Destination High */
+ data &= 0x1F;
+ break;
+ case 0x14: /* HDMA4 - HBL General DMA - Destination Low */
+ data &= 0xF0;
+ break;
+ case 0x15: /* HDMA5 - HBL General DMA - Mode, Length */
+ if (!(data & 0x80))
+ {
+ if (m_hdma_enabled)
+ {
+ m_hdma_enabled = 0;
+ data = HDMA5 & 0x80;
+ }
+ else
+ {
+ /* General DMA */
+ hdma_trans(((data & 0x7F) + 1) * 0x10);
+// cpunum_set_reg(0, LR35902_DMA_CYCLES, 4 + (((data & 0x7F) + 1) * 32));
+ data = 0xff;
+ }
+ }
+ else
+ {
+ /* H-Blank DMA */
+ m_hdma_enabled = 1;
+ data &= 0x7f;
+ m_vid_regs[offset] = data;
+ /* Check if HDMA should be immediately performed */
+ if (m_hdma_possible)
+ {
+ hdma_trans(0x10);
+// cpunum_set_reg(0, LR35902_DMA_CYCLES, 36);
+ m_hdma_possible = 0;
+ }
+ }
+ break;
+ case 0x28: /* BCPS - Background palette specification */
+ GBCBCPS = data;
+ if (data & 0x01)
+ GBCBCPD = m_cgb_bpal[(data >> 1) & 0x1F] >> 8;
+ else
+ GBCBCPD = m_cgb_bpal[(data >> 1) & 0x1F] & 0xFF;
+ break;
+ case 0x29: /* BCPD - background palette data */
+ if (m_pal_locked == LOCKED)
+ {
+ return;
+ }
+ GBCBCPD = data;
+ if (GBCBCPS & 0x01)
+ m_cgb_bpal[(GBCBCPS >> 1) & 0x1F] = ((data << 8) | (m_cgb_bpal[(GBCBCPS >> 1) & 0x1F] & 0xFF)) & 0x7FFF;
+ else
+ m_cgb_bpal[(GBCBCPS >> 1) & 0x1F] = ((m_cgb_bpal[(GBCBCPS >> 1) & 0x1F] & 0xFF00) | data) & 0x7FFF;
+ if (GBCBCPS & 0x80)
+ {
+ GBCBCPS++;
+ GBCBCPS &= 0xBF;
+ }
+ break;
+ case 0x2A: /* OCPS - Object palette specification */
+ GBCOCPS = data;
+ if (data & 0x01)
+ GBCOCPD = m_cgb_spal[(data >> 1) & 0x1F] >> 8;
+ else
+ GBCOCPD = m_cgb_spal[(data >> 1) & 0x1F] & 0xFF;
+ break;
+ case 0x2B: /* OCPD - Object palette data */
+ if (m_pal_locked == LOCKED)
+ {
+ return;
+ }
+ GBCOCPD = data;
+ if (GBCOCPS & 0x01)
+ m_cgb_spal[(GBCOCPS >> 1) & 0x1F] = ((data << 8) | (m_cgb_spal[(GBCOCPS >> 1) & 0x1F] & 0xFF)) & 0x7FFF;
+ else
+ m_cgb_spal[(GBCOCPS >> 1) & 0x1F] = ((m_cgb_spal[(GBCOCPS >> 1) & 0x1F] & 0xFF00) | data) & 0x7FFF;
+ if (GBCOCPS & 0x80)
+ {
+ GBCOCPS++;
+ GBCOCPS &= 0xBF;
+ }
+ break;
+ /* Undocumented registers */
+ case 0x2C:
+ /* bit 0 can be read/written */
+ logerror("Write to undocumented register: %X = %X\n", offset, data);
+ data = 0xFE | (data & 0x01);
+ if (data & 0x01)
+ {
+ m_gbc_mode = 0;
+ }
+ break;
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ /* whole byte can be read/written */
+ logerror("Write to undocumented register: %X = %X\n", offset, data);
+ break;
+ case 0x35:
+ /* bit 4-6 can be read/written */
+ logerror("Write to undocumented register: %X = %X\n", offset, data);
+ data = 0x8F | (data & 0x70);
+ break;
+ case 0x36:
+ case 0x37:
+ logerror("Write to undocumented register: %X = %X\n", offset, data);
+ return;
+ default:
+ /* we didn't handle the write, so pass it to the GB handler */
+ gb_lcd_device::video_w(space, offset, data);
+ return;
+ }
+
+ m_vid_regs[offset] = data;
+}
+
+// Super Game Boy
+
+void sgb_lcd_device::sgb_io_write_pal(int offs, UINT8 *data)
+{
+ switch (offs)
+ {
+ case 0x00: /* PAL01 */
+ m_sgb_pal[0 * 4 + 0] = data[1] | (data[2] << 8);
+ m_sgb_pal[0 * 4 + 1] = data[3] | (data[4] << 8);
+ m_sgb_pal[0 * 4 + 2] = data[5] | (data[6] << 8);
+ m_sgb_pal[0 * 4 + 3] = data[7] | (data[8] << 8);
+ m_sgb_pal[1 * 4 + 0] = data[1] | (data[2] << 8);
+ m_sgb_pal[1 * 4 + 1] = data[9] | (data[10] << 8);
+ m_sgb_pal[1 * 4 + 2] = data[11] | (data[12] << 8);
+ m_sgb_pal[1 * 4 + 3] = data[13] | (data[14] << 8);
+ break;
+ case 0x01: /* PAL23 */
+ m_sgb_pal[2 * 4 + 0] = data[1] | (data[2] << 8);
+ m_sgb_pal[2 * 4 + 1] = data[3] | (data[4] << 8);
+ m_sgb_pal[2 * 4 + 2] = data[5] | (data[6] << 8);
+ m_sgb_pal[2 * 4 + 3] = data[7] | (data[8] << 8);
+ m_sgb_pal[3 * 4 + 0] = data[1] | (data[2] << 8);
+ m_sgb_pal[3 * 4 + 1] = data[9] | (data[10] << 8);
+ m_sgb_pal[3 * 4 + 2] = data[11] | (data[12] << 8);
+ m_sgb_pal[3 * 4 + 3] = data[13] | (data[14] << 8);
+ break;
+ case 0x02: /* PAL03 */
+ m_sgb_pal[0 * 4 + 0] = data[1] | (data[2] << 8);
+ m_sgb_pal[0 * 4 + 1] = data[3] | (data[4] << 8);
+ m_sgb_pal[0 * 4 + 2] = data[5] | (data[6] << 8);
+ m_sgb_pal[0 * 4 + 3] = data[7] | (data[8] << 8);
+ m_sgb_pal[3 * 4 + 0] = data[1] | (data[2] << 8);
+ m_sgb_pal[3 * 4 + 1] = data[9] | (data[10] << 8);
+ m_sgb_pal[3 * 4 + 2] = data[11] | (data[12] << 8);
+ m_sgb_pal[3 * 4 + 3] = data[13] | (data[14] << 8);
+ break;
+ case 0x03: /* PAL12 */
+ m_sgb_pal[1 * 4 + 0] = data[1] | (data[2] << 8);
+ m_sgb_pal[1 * 4 + 1] = data[3] | (data[4] << 8);
+ m_sgb_pal[1 * 4 + 2] = data[5] | (data[6] << 8);
+ m_sgb_pal[1 * 4 + 3] = data[7] | (data[8] << 8);
+ m_sgb_pal[2 * 4 + 0] = data[1] | (data[2] << 8);
+ m_sgb_pal[2 * 4 + 1] = data[9] | (data[10] << 8);
+ m_sgb_pal[2 * 4 + 2] = data[11] | (data[12] << 8);
+ m_sgb_pal[2 * 4 + 3] = data[13] | (data[14] << 8);
+ break;
+ case 0x04: /* ATTR_BLK */
+ {
+ UINT8 I, J, K, o;
+ for( K = 0; K < data[1]; K++ )
+ {
+ o = K * 6;
+ if( data[o + 2] & 0x1 )
+ {
+ for( I = data[ o + 4]; I <= data[o + 6]; I++ )
+ {
+ for( J = data[o + 5]; J <= data[o + 7]; J++ )
+ {
+ m_sgb_pal_map[I][J] = data[o + 3] & 0x3;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 0x05: /* ATTR_LIN */
+ {
+ UINT8 J, K;
+ if( data[1] > 15 )
+ data[1] = 15;
+ for( K = 0; K < data[1]; K++ )
+ {
+ if( data[K + 1] & 0x80 )
+ {
+ for( J = 0; J < 20; J++ )
+ {
+ m_sgb_pal_map[J][data[K + 1] & 0x1f] = (data[K + 1] & 0x60) >> 5;
+ }
+ }
+ else
+ {
+ for( J = 0; J < 18; J++ )
+ {
+ m_sgb_pal_map[data[K + 1] & 0x1f][J] = (data[K + 1] & 0x60) >> 5;
+ }
+ }
+ }
+ }
+ break;
+ case 0x06: /* ATTR_DIV */
+ {
+ UINT8 I, J;
+ if( data[1] & 0x40 ) /* Vertical */
+ {
+ for( I = 0; I < data[2]; I++ )
+ {
+ for( J = 0; J < 20; J++ )
+ {
+ m_sgb_pal_map[J][I] = (data[1] & 0xC) >> 2;
+ }
+ }
+ for( J = 0; J < 20; J++ )
+ {
+ m_sgb_pal_map[J][data[2]] = (data[1] & 0x30) >> 4;
+ }
+ for( I = data[2] + 1; I < 18; I++ )
+ {
+ for( J = 0; J < 20; J++ )
+ {
+ m_sgb_pal_map[J][I] = data[1] & 0x3;
+ }
+ }
+ }
+ else /* Horizontal */
+ {
+ for( I = 0; I < data[2]; I++ )
+ {
+ for( J = 0; J < 18; J++ )
+ {
+ m_sgb_pal_map[I][J] = (data[1] & 0xC) >> 2;
+ }
+ }
+ for( J = 0; J < 18; J++ )
+ {
+ m_sgb_pal_map[data[2]][J] = (data[1] & 0x30) >> 4;
+ }
+ for( I = data[2] + 1; I < 20; I++ )
+ {
+ for( J = 0; J < 18; J++ )
+ {
+ m_sgb_pal_map[I][J] = data[1] & 0x3;
+ }
+ }
+ }
+ }
+ break;
+ case 0x07: /* ATTR_CHR */
+ {
+ UINT16 I, sets;
+ UINT8 x, y;
+ sets = (data[3] | (data[4] << 8) );
+ if( sets > 360 )
+ sets = 360;
+ sets >>= 2;
+ sets += 6;
+ x = data[1];
+ y = data[2];
+ if( data[5] ) /* Vertical */
+ {
+ for( I = 6; I < sets; I++ )
+ {
+ m_sgb_pal_map[x][y++] = (data[I] & 0xC0) >> 6;
+ if( y > 17 )
+ {
+ y = 0;
+ x++;
+ if( x > 19 )
+ x = 0;
+ }
+
+ m_sgb_pal_map[x][y++] = (data[I] & 0x30) >> 4;
+ if( y > 17 )
+ {
+ y = 0;
+ x++;
+ if( x > 19 )
+ x = 0;
+ }
+
+ m_sgb_pal_map[x][y++] = (data[I] & 0xC) >> 2;
+ if( y > 17 )
+ {
+ y = 0;
+ x++;
+ if( x > 19 )
+ x = 0;
+ }
+
+ m_sgb_pal_map[x][y++] = data[I] & 0x3;
+ if( y > 17 )
+ {
+ y = 0;
+ x++;
+ if( x > 19 )
+ x = 0;
+ }
+ }
+ }
+ else /* horizontal */
+ {
+ for( I = 6; I < sets; I++ )
+ {
+ m_sgb_pal_map[x++][y] = (data[I] & 0xC0) >> 6;
+ if( x > 19 )
+ {
+ x = 0;
+ y++;
+ if( y > 17 )
+ y = 0;
+ }
+
+ m_sgb_pal_map[x++][y] = (data[I] & 0x30) >> 4;
+ if( x > 19 )
+ {
+ x = 0;
+ y++;
+ if( y > 17 )
+ y = 0;
+ }
+
+ m_sgb_pal_map[x++][y] = (data[I] & 0xC) >> 2;
+ if( x > 19 )
+ {
+ x = 0;
+ y++;
+ if( y > 17 )
+ y = 0;
+ }
+
+ m_sgb_pal_map[x++][y] = data[I] & 0x3;
+ if( x > 19 )
+ {
+ x = 0;
+ y++;
+ if( y > 17 )
+ y = 0;
+ }
+ }
+ }
+ }
+ break;
+ case 0x08: /* SOUND */
+ /* This command enables internal sound effects */
+ /* Not Implemented */
+ break;
+ case 0x09: /* SOU_TRN */
+ /* This command sends data to the SNES sound processor.
+ We'll need to emulate that for this to be used */
+ /* Not Implemented */
+ break;
+ case 0x0A: /* PAL_SET */
+ {
+ UINT16 index_;
+
+ /* Palette 0 */
+ index_ = (UINT16)(data[1] | (data[2] << 8)) * 4;
+ m_sgb_pal[0] = m_sgb_pal_data[index_];
+ m_sgb_pal[1] = m_sgb_pal_data[index_ + 1];
+ m_sgb_pal[2] = m_sgb_pal_data[index_ + 2];
+ m_sgb_pal[3] = m_sgb_pal_data[index_ + 3];
+ /* Palette 1 */
+ index_ = (UINT16)(data[3] | (data[4] << 8)) * 4;
+ m_sgb_pal[4] = m_sgb_pal_data[index_];
+ m_sgb_pal[5] = m_sgb_pal_data[index_ + 1];
+ m_sgb_pal[6] = m_sgb_pal_data[index_ + 2];
+ m_sgb_pal[7] = m_sgb_pal_data[index_ + 3];
+ /* Palette 2 */
+ index_ = (UINT16)(data[5] | (data[6] << 8)) * 4;
+ m_sgb_pal[8] = m_sgb_pal_data[index_];
+ m_sgb_pal[9] = m_sgb_pal_data[index_ + 1];
+ m_sgb_pal[10] = m_sgb_pal_data[index_ + 2];
+ m_sgb_pal[11] = m_sgb_pal_data[index_ + 3];
+ /* Palette 3 */
+ index_ = (UINT16)(data[7] | (data[8] << 8)) * 4;
+ m_sgb_pal[12] = m_sgb_pal_data[index_];
+ m_sgb_pal[13] = m_sgb_pal_data[index_ + 1];
+ m_sgb_pal[14] = m_sgb_pal_data[index_ + 2];
+ m_sgb_pal[15] = m_sgb_pal_data[index_ + 3];
+ /* Attribute File */
+ if (data[9] & 0x40)
+ m_sgb_window_mask = 0;
+ m_sgb_atf = (data[9] & 0x3f) * (18 * 5);
+ if (data[9] & 0x80)
+ {
+ for (int j = 0; j < 18; j++ )
+ {
+ for (int i = 0; i < 5; i++ )
+ {
+ m_sgb_pal_map[i * 4][j] = (m_sgb_atf_data[(j * 5) + m_sgb_atf + i] & 0xC0) >> 6;
+ m_sgb_pal_map[(i * 4) + 1][j] = (m_sgb_atf_data[(j * 5) + m_sgb_atf + i] & 0x30) >> 4;
+ m_sgb_pal_map[(i * 4) + 2][j] = (m_sgb_atf_data[(j * 5) + m_sgb_atf + i] & 0xC) >> 2;
+ m_sgb_pal_map[(i * 4) + 3][j] = m_sgb_atf_data[(j * 5) + m_sgb_atf + i] & 0x3;
+ }
+ }
+ }
+ }
+ break;
+ case 0x0B: /* PAL_TRN */
+ {
+ UINT16 col;
+
+ for (int i = 0; i < 2048; i++ )
+ {
+ col = (m_vram[0x0800 + (i * 2) + 1] << 8) | m_vram[0x0800 + (i * 2)];
+ m_sgb_pal_data[i] = col;
+ }
+ }
+ break;
+ case 0x0C: /* ATRC_EN */
+ /* Not Implemented */
+ break;
+ case 0x0D: /* TEST_EN */
+ /* Not Implemented */
+ break;
+ case 0x0E: /* ICON_EN */
+ /* Not Implemented */
+ break;
+ case 0x0F: /* DATA_SND */
+ /* Not Implemented */
+ break;
+ case 0x10: /* DATA_TRN */
+ /* Not Implemented */
+ break;
+ case 0x12: /* JUMP */
+ /* Not Implemented */
+ break;
+ case 0x13: /* CHR_TRN */
+ if (data[1] & 0x1)
+ memcpy(m_sgb_tile_data + 4096, m_vram + 0x0800, 4096);
+ else
+ memcpy(m_sgb_tile_data, m_vram + 0x0800, 4096);
+ break;
+ case 0x14: /* PCT_TRN */
+ {
+ UINT16 col;
+ if (m_sgb_border_hack)
+ {
+ memcpy(m_sgb_tile_map, m_vram + 0x1000, 2048);
+ for (int i = 0; i < 64; i++)
+ {
+ col = (m_vram[0x0800 + (i * 2) + 1 ] << 8) | m_vram[0x0800 + (i * 2)];
+ m_sgb_pal[SGB_BORDER_PAL_OFFSET + i] = col;
+ }
+ }
+ else /* Do things normally */
+ {
+ memcpy(m_sgb_tile_map, m_vram + 0x0800, 2048);
+ for (int i = 0; i < 64; i++)
+ {
+ col = (m_vram[0x1000 + (i * 2) + 1] << 8) | m_vram[0x1000 + (i * 2)];
+ m_sgb_pal[SGB_BORDER_PAL_OFFSET + i] = col;
+ }
+ }
+ }
+ break;
+ case 0x15: /* ATTR_TRN */
+ memcpy(m_sgb_atf_data, m_vram + 0x0800, 4050);
+ break;
+ case 0x16: /* ATTR_SET */
+ {
+ /* Attribute File */
+ if (data[1] & 0x40)
+ m_sgb_window_mask = 0;
+ m_sgb_atf = (data[1] & 0x3f) * (18 * 5);
+ for (int j = 0; j < 18; j++)
+ {
+ for (int i = 0; i < 5; i++)
+ {
+ m_sgb_pal_map[i * 4][j] = (m_sgb_atf_data[(j * 5) + m_sgb_atf + i] & 0xC0) >> 6;
+ m_sgb_pal_map[(i * 4) + 1][j] = (m_sgb_atf_data[(j * 5) + m_sgb_atf + i] & 0x30) >> 4;
+ m_sgb_pal_map[(i * 4) + 2][j] = (m_sgb_atf_data[(j * 5) + m_sgb_atf + i] & 0xC) >> 2;
+ m_sgb_pal_map[(i * 4) + 3][j] = m_sgb_atf_data[(j * 5) + m_sgb_atf + i] & 0x3;
+ }
+ }
+ }
+ break;
+ case 0x17: /* MASK_EN */
+ m_sgb_window_mask = data[1];
+ break;
+ case 0x18: /* OBJ_TRN */
+ /* Not Implemnted */
+ break;
+ case 0x19: /* ? */
+ /* Called by: dkl,dkl2,dkl3,zeldadx
+ But I don't know what it is for. */
+ /* Not Implemented */
+ break;
+ case 0x1E: /* Used by bootrom to transfer the gb cart header */
+ break;
+ case 0x1F: /* Used by bootrom to transfer the gb cart header */
+ break;
+ default:
+ logerror( "SGB: Unknown Command 0x%02x!\n", data[0] >> 3 );
+ }
+
+}