gnuboy-for-dfi/lcdc.c

180 lines
2.9 KiB
C

#include <stdlib.h>
#include "defs.h"
#include "hw.h"
#include "cpu.h"
#include "regs.h"
#include "lcd.h"
#define C (cpu.lcdc)
/*
* stat_trigger updates the STAT interrupt line to reflect whether any
* of the conditions set to be tested (by bits 3-6 of R_STAT) are met.
* This function should be called whenever any of the following occur:
* 1) LY or LYC changes.
* 2) A state transition affects the low 2 bits of R_STAT (see below).
* 3) The program writes to the upper bits of R_STAT.
* stat_trigger also updates bit 2 of R_STAT to reflect whether LY=LYC.
*/
void stat_trigger()
{
static const int condbits[4] = { 0x08, 0x10, 0x20, 0x00 };
int flag = 0;
if (R_LY == R_LYC)
{
R_STAT |= 0x04;
if (R_STAT & 0x40) flag = IF_STAT;
}
else R_STAT &= ~0x04;
if (R_STAT & condbits[R_STAT&3]) flag = IF_STAT;
if (!(R_LCDC & 0x80)) flag = 0;
hw_interrupt(flag, IF_STAT);
}
void stat_write(byte b)
{
R_STAT = (R_STAT & 0x07) | (b & 0x78);
if (!hw.cgb && !(R_STAT & 2)) /* DMG STAT write bug => interrupt */
hw_interrupt(IF_STAT, IF_STAT);
stat_trigger();
}
/*
* stat_change is called when a transition results in a change to the
* LCD STAT condition (the low 2 bits of R_STAT). It raises or lowers
* the VBLANK interrupt line appropriately and calls stat_trigger to
* update the STAT interrupt line.
*/
static void stat_change(int stat)
{
stat &= 3;
R_STAT = (R_STAT & 0x7C) | stat;
if (stat != 1) hw_interrupt(0, IF_VBLANK);
/* hw_interrupt((stat == 1) ? IF_VBLANK : 0, IF_VBLANK); */
stat_trigger();
}
void lcdc_change(byte b)
{
byte old = R_LCDC;
R_LCDC = b;
if ((R_LCDC ^ old) & 0x80) /* lcd on/off change */
{
R_LY = 0;
stat_change(2);
C = 40;
lcd_begin();
}
}
void lcdc_trans()
{
if (!(R_LCDC & 0x80))
{
while (C <= 0)
{
switch ((byte)(R_STAT & 3))
{
case 0:
case 1:
stat_change(2);
C += 40;
break;
case 2:
stat_change(3);
C += 86;
break;
case 3:
stat_change(0);
if (hw.hdma & 0x80)
hw_hdma();
else
C += 102;
break;
}
return;
}
}
while (C <= 0)
{
switch ((byte)(R_STAT & 3))
{
case 1:
if (!(hw.ilines & IF_VBLANK))
{
C += 218;
hw_interrupt(IF_VBLANK, IF_VBLANK);
break;
}
if (R_LY == 0)
{
lcd_begin();
stat_change(2);
C += 40;
break;
}
else if (R_LY < 152)
C += 228;
else if (R_LY == 152)
C += 28;
else
{
R_LY = -1;
C += 200;
}
R_LY++;
stat_trigger();
break;
case 2:
lcd_refreshline();
stat_change(3);
C += 86;
break;
case 3:
stat_change(0);
if (hw.hdma & 0x80)
hw_hdma();
/* FIXME -- how much of the hblank does hdma use?? */
/* else */
C += 102;
break;
case 0:
if (++R_LY >= 144)
{
if (cpu.halt)
{
hw_interrupt(IF_VBLANK, IF_VBLANK);
C += 228;
}
else C += 10;
stat_change(1);
break;
}
stat_change(2);
C += 40;
break;
}
}
}