2016-03-30 20:07:55 +00:00
# include <stdbool.h>
# include <stdlib.h>
# include <assert.h>
2016-03-30 20:36:24 +00:00
# include <string.h>
2020-12-25 12:14:17 +00:00
# include <math.h>
2016-03-30 20:07:55 +00:00
# include "gb.h"
2018-03-03 13:47:36 +00:00
/* FIFO functions */
2018-03-04 21:27:31 +00:00
static inline unsigned fifo_size ( GB_fifo_t * fifo )
2018-03-03 13:47:36 +00:00
{
2018-04-06 15:26:04 +00:00
return ( fifo - > write_end - fifo - > read_end ) & ( GB_FIFO_LENGTH - 1 ) ;
2018-03-03 13:47:36 +00:00
}
static void fifo_clear ( GB_fifo_t * fifo )
{
fifo - > read_end = fifo - > write_end = 0 ;
}
static GB_fifo_item_t * fifo_pop ( GB_fifo_t * fifo )
{
GB_fifo_item_t * ret = & fifo - > fifo [ fifo - > read_end ] ;
fifo - > read_end + + ;
2018-04-06 15:26:04 +00:00
fifo - > read_end & = ( GB_FIFO_LENGTH - 1 ) ;
2018-03-03 13:47:36 +00:00
return ret ;
}
2018-03-09 16:52:36 +00:00
static void fifo_push_bg_row ( GB_fifo_t * fifo , uint8_t lower , uint8_t upper , uint8_t palette , bool bg_priority , bool flip_x )
2018-03-03 13:47:36 +00:00
{
2018-03-09 16:52:36 +00:00
if ( ! flip_x ) {
2021-02-22 12:45:30 +00:00
unrolled for ( unsigned i = 8 ; i - - ; ) {
2018-03-09 16:52:36 +00:00
fifo - > fifo [ fifo - > write_end ] = ( GB_fifo_item_t ) {
( lower > > 7 ) | ( ( upper > > 7 ) < < 1 ) ,
palette ,
0 ,
bg_priority ,
} ;
lower < < = 1 ;
upper < < = 1 ;
fifo - > write_end + + ;
2018-04-06 15:26:04 +00:00
fifo - > write_end & = ( GB_FIFO_LENGTH - 1 ) ;
2018-03-09 16:52:36 +00:00
}
}
else {
2021-02-22 12:45:30 +00:00
unrolled for ( unsigned i = 8 ; i - - ; ) {
2018-03-09 16:52:36 +00:00
fifo - > fifo [ fifo - > write_end ] = ( GB_fifo_item_t ) {
( lower & 1 ) | ( ( upper & 1 ) < < 1 ) ,
palette ,
0 ,
bg_priority ,
} ;
lower > > = 1 ;
upper > > = 1 ;
fifo - > write_end + + ;
2018-04-06 15:26:04 +00:00
fifo - > write_end & = ( GB_FIFO_LENGTH - 1 ) ;
2018-03-09 16:52:36 +00:00
}
2018-03-03 13:47:36 +00:00
}
}
2018-03-04 21:27:31 +00:00
static void fifo_overlay_object_row ( GB_fifo_t * fifo , uint8_t lower , uint8_t upper , uint8_t palette , bool bg_priority , uint8_t priority , bool flip_x )
{
while ( fifo_size ( fifo ) < 8 ) {
fifo - > fifo [ fifo - > write_end ] = ( GB_fifo_item_t ) { 0 , } ;
fifo - > write_end + + ;
2018-04-06 15:26:04 +00:00
fifo - > write_end & = ( GB_FIFO_LENGTH - 1 ) ;
2018-03-04 21:27:31 +00:00
}
uint8_t flip_xor = flip_x ? 0 : 0x7 ;
2021-02-22 12:45:30 +00:00
unrolled for ( unsigned i = 8 ; i - - ; ) {
2018-03-04 21:27:31 +00:00
uint8_t pixel = ( lower > > 7 ) | ( ( upper > > 7 ) < < 1 ) ;
2018-04-06 15:26:04 +00:00
GB_fifo_item_t * target = & fifo - > fifo [ ( fifo - > read_end + ( i ^ flip_xor ) ) & ( GB_FIFO_LENGTH - 1 ) ] ;
2018-03-04 21:27:31 +00:00
if ( pixel ! = 0 & & ( target - > pixel = = 0 | | target - > priority > priority ) ) {
target - > pixel = pixel ;
target - > palette = palette ;
target - > bg_priority = bg_priority ;
target - > priority = priority ;
}
lower < < = 1 ;
upper < < = 1 ;
}
}
2017-02-19 00:22:50 +00:00
/*
2021-12-17 19:16:23 +00:00
Each line is 456 cycles . Without scrolling , objects or a window :
2017-02-19 00:22:50 +00:00
Mode 2 - 80 cycles / OAM Transfer
Mode 3 - 172 cycles / Rendering
Mode 0 - 204 cycles / HBlank
Mode 1 is VBlank
*/
2018-02-24 22:48:45 +00:00
# define MODE2_LENGTH (80)
2019-02-16 02:19:16 +00:00
# define LINE_LENGTH (456)
2017-02-19 00:22:50 +00:00
# define LINES (144)
# define WIDTH (160)
2020-02-08 11:28:46 +00:00
# define BORDERED_WIDTH 256
# define BORDERED_HEIGHT 224
2018-02-24 22:48:45 +00:00
# define FRAME_LENGTH (LCDC_PERIOD)
2018-02-20 19:17:12 +00:00
# define VIRTUAL_LINES (FRAME_LENGTH / LINE_LENGTH) // = 154
2017-02-19 00:22:50 +00:00
2016-10-26 21:14:02 +00:00
typedef struct __attribute__ ( ( packed ) ) {
2016-06-18 17:29:11 +00:00
uint8_t y ;
uint8_t x ;
uint8_t tile ;
uint8_t flags ;
2021-11-06 23:10:58 +00:00
} object_t ;
2016-03-30 20:07:55 +00:00
2021-12-04 13:04:46 +00:00
void GB_display_vblank ( GB_gameboy_t * gb )
2018-02-24 22:48:45 +00:00
{
2018-02-10 12:42:14 +00:00
gb - > vblank_just_occured = true ;
2021-12-04 13:04:46 +00:00
gb - > cycles_since_vblank_callback = 0 ;
gb - > lcd_disabled_outside_of_vblank = false ;
2018-11-16 14:04:40 +00:00
2019-07-15 17:47:16 +00:00
/* TODO: Slow in turbo mode! */
if ( GB_is_hle_sgb ( gb ) ) {
2018-11-16 14:04:40 +00:00
GB_sgb_render ( gb ) ;
}
2017-04-21 13:00:53 +00:00
if ( gb - > turbo ) {
if ( GB_timing_sync_turbo ( gb ) ) {
2016-03-30 20:07:55 +00:00
return ;
}
}
2017-04-19 18:55:58 +00:00
2020-02-15 17:21:43 +00:00
bool is_ppu_stopped = ! GB_is_cgb ( gb ) & & gb - > stopped & & gb - > io_registers [ GB_IO_LCDC ] & 0x80 ;
2021-12-04 13:04:46 +00:00
if ( ! gb - > disable_rendering & & ( ( ! ( gb - > io_registers [ GB_IO_LCDC ] & 0x80 ) | | is_ppu_stopped ) | | gb - > cgb_repeated_a_frame | | gb - > frame_skip_state = = GB_FRAMESKIP_LCD_TURNED_ON ) ) {
2017-08-12 18:42:47 +00:00
/* LCD is off, set screen to white or black (if LCD is on in stop mode) */
2020-02-15 17:21:43 +00:00
if ( ! GB_is_sgb ( gb ) ) {
2020-01-29 12:19:11 +00:00
uint32_t color = 0 ;
if ( GB_is_cgb ( gb ) ) {
2020-03-25 18:33:13 +00:00
color = GB_convert_rgb15 ( gb , 0x7FFF , false ) ;
2020-01-29 12:19:11 +00:00
}
else {
2020-02-15 17:21:43 +00:00
color = is_ppu_stopped ?
gb - > background_palettes_rgb [ 0 ] :
2020-01-29 12:19:11 +00:00
gb - > background_palettes_rgb [ 4 ] ;
}
2020-02-08 11:28:46 +00:00
if ( gb - > border_mode = = GB_BORDER_ALWAYS ) {
for ( unsigned y = 0 ; y < LINES ; y + + ) {
for ( unsigned x = 0 ; x < WIDTH ; x + + ) {
gb - > screen [ x + y * BORDERED_WIDTH + ( BORDERED_WIDTH - WIDTH ) / 2 + ( BORDERED_HEIGHT - LINES ) / 2 * BORDERED_WIDTH ] = color ;
}
}
}
else {
for ( unsigned i = 0 ; i < WIDTH * LINES ; i + + ) {
gb - > screen [ i ] = color ;
}
2018-11-14 22:21:21 +00:00
}
}
2020-02-08 11:28:46 +00:00
}
2021-04-02 22:29:43 +00:00
if ( ! gb - > disable_rendering & & gb - > border_mode = = GB_BORDER_ALWAYS & & ! GB_is_sgb ( gb ) ) {
2020-02-08 11:28:46 +00:00
GB_borrow_sgb_border ( gb ) ;
uint32_t border_colors [ 16 * 4 ] ;
2022-01-30 16:11:35 +00:00
if ( ! gb - > has_sgb_border & & GB_is_cgb ( gb ) & & gb - > model < = GB_MODEL_CGB_E ) {
2021-03-28 23:47:57 +00:00
uint16_t colors [ ] = {
2020-02-08 11:28:46 +00:00
0x2095 , 0x5129 , 0x1EAF , 0x1EBA , 0x4648 ,
0x30DA , 0x69AD , 0x2B57 , 0x2B5D , 0x632C ,
0x1050 , 0x3C84 , 0x0E07 , 0x0E18 , 0x2964 ,
} ;
2022-02-13 14:58:44 +00:00
unsigned index = gb - > rom ? gb - > rom [ 0x14E ] % 5 : 0 ;
2021-11-03 22:32:15 +00:00
if ( gb - > model = = GB_MODEL_CGB_0 ) {
2022-01-09 13:30:33 +00:00
index = 1 ; // CGB 0 was only available in indigo!
2021-11-03 22:32:15 +00:00
}
2022-01-05 19:55:46 +00:00
else if ( gb - > model = = GB_MODEL_CGB_A ) {
2022-01-09 13:30:33 +00:00
index = 0 ; // CGB A was only available in red!
2022-01-05 19:55:46 +00:00
}
2021-03-28 23:47:57 +00:00
gb - > borrowed_border . palette [ 0 ] = LE16 ( colors [ index ] ) ;
gb - > borrowed_border . palette [ 10 ] = LE16 ( colors [ 5 + index ] ) ;
gb - > borrowed_border . palette [ 14 ] = LE16 ( colors [ 10 + index ] ) ;
2020-02-08 11:28:46 +00:00
}
for ( unsigned i = 0 ; i < 16 * 4 ; i + + ) {
2021-03-28 23:47:57 +00:00
border_colors [ i ] = GB_convert_rgb15 ( gb , LE16 ( gb - > borrowed_border . palette [ i ] ) , true ) ;
2020-02-08 11:28:46 +00:00
}
for ( unsigned tile_y = 0 ; tile_y < 28 ; tile_y + + ) {
for ( unsigned tile_x = 0 ; tile_x < 32 ; tile_x + + ) {
if ( tile_x > = 6 & & tile_x < 26 & & tile_y > = 5 & & tile_y < 23 ) {
continue ;
}
2021-03-28 23:47:57 +00:00
uint16_t tile = LE16 ( gb - > borrowed_border . map [ tile_x + tile_y * 32 ] ) ;
2021-03-30 21:54:55 +00:00
uint8_t flip_x = ( tile & 0x4000 ) ? 0 : 7 ;
uint8_t flip_y = ( tile & 0x8000 ) ? 7 : 0 ;
2020-02-08 11:28:46 +00:00
uint8_t palette = ( tile > > 10 ) & 3 ;
for ( unsigned y = 0 ; y < 8 ; y + + ) {
2021-03-30 21:54:55 +00:00
unsigned base = ( tile & 0xFF ) * 32 + ( y ^ flip_y ) * 2 ;
2020-02-08 11:28:46 +00:00
for ( unsigned x = 0 ; x < 8 ; x + + ) {
2021-03-30 21:54:55 +00:00
uint8_t bit = 1 < < ( x ^ flip_x ) ;
uint8_t color = ( ( gb - > borrowed_border . tiles [ base ] & bit ) ? 1 : 0 ) |
( ( gb - > borrowed_border . tiles [ base + 1 ] & bit ) ? 2 : 0 ) |
( ( gb - > borrowed_border . tiles [ base + 16 ] & bit ) ? 4 : 0 ) |
( ( gb - > borrowed_border . tiles [ base + 17 ] & bit ) ? 8 : 0 ) ;
2020-02-08 11:28:46 +00:00
uint32_t * output = gb - > screen + tile_x * 8 + x + ( tile_y * 8 + y ) * 256 ;
if ( color = = 0 ) {
* output = border_colors [ 0 ] ;
}
else {
* output = border_colors [ color + palette * 16 ] ;
}
}
}
2018-11-14 22:21:21 +00:00
}
2017-05-27 14:30:12 +00:00
}
2016-03-30 20:36:24 +00:00
}
2020-04-29 13:06:11 +00:00
GB_handle_rumble ( gb ) ;
2016-03-30 20:36:24 +00:00
2020-02-08 11:28:46 +00:00
if ( gb - > vblank_callback ) {
gb - > vblank_callback ( gb ) ;
}
2017-04-21 13:00:53 +00:00
GB_timing_sync ( gb ) ;
2016-03-30 20:07:55 +00:00
}
2020-12-25 12:14:17 +00:00
static inline void temperature_tint ( double temperature , double * r , double * g , double * b )
{
if ( temperature > = 0 ) {
* r = 1 ;
* g = pow ( 1 - temperature , 0.375 ) ;
if ( temperature > = 0.75 ) {
* b = 0 ;
}
else {
* b = sqrt ( 0.75 - temperature ) ;
}
}
else {
* b = 1 ;
double squared = pow ( temperature , 2 ) ;
* g = 0.125 * squared + 0.3 * temperature + 1.0 ;
* r = 0.21875 * squared + 0.5 * temperature + 1.0 ;
}
}
2016-06-18 17:29:11 +00:00
static inline uint8_t scale_channel ( uint8_t x )
2016-03-30 20:07:55 +00:00
{
return ( x < < 3 ) | ( x > > 2 ) ;
}
2017-10-12 14:22:22 +00:00
static inline uint8_t scale_channel_with_curve ( uint8_t x )
{
2020-12-24 21:17:20 +00:00
return ( uint8_t [ ] ) { 0 , 6 , 12 , 20 , 28 , 36 , 45 , 56 , 66 , 76 , 88 , 100 , 113 , 125 , 137 , 149 , 161 , 172 , 182 , 192 , 202 , 210 , 218 , 225 , 232 , 238 , 243 , 247 , 250 , 252 , 254 , 255 } [ x ] ;
2017-10-12 14:22:22 +00:00
}
2019-09-13 14:13:21 +00:00
static inline uint8_t scale_channel_with_curve_agb ( uint8_t x )
{
2020-12-24 21:17:20 +00:00
return ( uint8_t [ ] ) { 0 , 3 , 8 , 14 , 20 , 26 , 33 , 40 , 47 , 54 , 62 , 70 , 78 , 86 , 94 , 103 , 112 , 120 , 129 , 138 , 147 , 157 , 166 , 176 , 185 , 195 , 205 , 215 , 225 , 235 , 245 , 255 } [ x ] ;
2019-09-13 14:13:21 +00:00
}
static inline uint8_t scale_channel_with_curve_sgb ( uint8_t x )
{
return ( uint8_t [ ] ) { 0 , 2 , 5 , 9 , 15 , 20 , 27 , 34 , 42 , 50 , 58 , 67 , 76 , 85 , 94 , 104 , 114 , 123 , 133 , 143 , 153 , 163 , 173 , 182 , 192 , 202 , 211 , 220 , 229 , 238 , 247 , 255 } [ x ] ;
}
2020-02-08 11:28:46 +00:00
uint32_t GB_convert_rgb15 ( GB_gameboy_t * gb , uint16_t color , bool for_border )
2017-10-12 14:22:22 +00:00
{
uint8_t r = ( color ) & 0x1F ;
uint8_t g = ( color > > 5 ) & 0x1F ;
uint8_t b = ( color > > 10 ) & 0x1F ;
2020-02-08 11:28:46 +00:00
if ( gb - > color_correction_mode = = GB_COLOR_CORRECTION_DISABLED | | ( for_border & & ! gb - > has_sgb_border ) ) {
2017-10-12 14:22:22 +00:00
r = scale_channel ( r ) ;
g = scale_channel ( g ) ;
b = scale_channel ( b ) ;
}
2020-12-25 12:14:17 +00:00
else if ( GB_is_sgb ( gb ) | | for_border ) {
r = scale_channel_with_curve_sgb ( r ) ;
g = scale_channel_with_curve_sgb ( g ) ;
b = scale_channel_with_curve_sgb ( b ) ;
}
2017-10-12 14:22:22 +00:00
else {
2022-01-30 16:11:35 +00:00
bool agb = gb - > model > GB_MODEL_CGB_E ;
2019-09-13 14:13:21 +00:00
r = agb ? scale_channel_with_curve_agb ( r ) : scale_channel_with_curve ( r ) ;
g = agb ? scale_channel_with_curve_agb ( g ) : scale_channel_with_curve ( g ) ;
b = agb ? scale_channel_with_curve_agb ( b ) : scale_channel_with_curve ( b ) ;
2017-10-12 14:22:22 +00:00
if ( gb - > color_correction_mode ! = GB_COLOR_CORRECTION_CORRECT_CURVES ) {
2019-09-13 14:13:21 +00:00
uint8_t new_r , new_g , new_b ;
if ( agb ) {
2020-02-17 21:05:11 +00:00
new_g = ( g * 6 + b * 1 ) / 7 ;
2019-09-13 14:13:21 +00:00
}
else {
new_g = ( g * 3 + b ) / 4 ;
}
2020-02-17 21:05:11 +00:00
new_r = r ;
new_b = b ;
2020-03-25 18:33:13 +00:00
if ( gb - > color_correction_mode = = GB_COLOR_CORRECTION_REDUCE_CONTRAST ) {
r = new_r ;
g = new_r ;
b = new_r ;
new_r = new_r * 7 / 8 + ( g + b ) / 16 ;
new_g = new_g * 7 / 8 + ( r + b ) / 16 ;
new_b = new_b * 7 / 8 + ( r + g ) / 16 ;
new_r = new_r * ( 224 - 32 ) / 255 + 32 ;
new_g = new_g * ( 220 - 36 ) / 255 + 36 ;
new_b = new_b * ( 216 - 40 ) / 255 + 40 ;
}
2021-06-25 16:57:56 +00:00
else if ( gb - > color_correction_mode = = GB_COLOR_CORRECTION_LOW_CONTRAST ) {
r = new_r ;
g = new_r ;
b = new_r ;
new_r = new_r * 7 / 8 + ( g + b ) / 16 ;
new_g = new_g * 7 / 8 + ( r + b ) / 16 ;
new_b = new_b * 7 / 8 + ( r + g ) / 16 ;
new_r = new_r * ( 162 - 67 ) / 255 + 67 ;
new_g = new_g * ( 167 - 62 ) / 255 + 62 ;
new_b = new_b * ( 157 - 58 ) / 255 + 58 ;
}
2020-03-25 18:33:13 +00:00
else if ( gb - > color_correction_mode = = GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS ) {
2017-10-12 14:22:22 +00:00
uint8_t old_max = MAX ( r , MAX ( g , b ) ) ;
uint8_t new_max = MAX ( new_r , MAX ( new_g , new_b ) ) ;
if ( new_max ! = 0 ) {
new_r = new_r * old_max / new_max ;
new_g = new_g * old_max / new_max ;
new_b = new_b * old_max / new_max ;
}
uint8_t old_min = MIN ( r , MIN ( g , b ) ) ;
uint8_t new_min = MIN ( new_r , MIN ( new_g , new_b ) ) ;
2022-02-13 14:58:44 +00:00
if ( new_min ! = 0xFF ) {
new_r = 0xFF - ( 0xFF - new_r ) * ( 0xFF - old_min ) / ( 0xFF - new_min ) ;
new_g = 0xFF - ( 0xFF - new_g ) * ( 0xFF - old_min ) / ( 0xFF - new_min ) ;
new_b = 0xFF - ( 0xFF - new_b ) * ( 0xFF - old_min ) / ( 0xFF - new_min ) ;
2017-10-12 14:22:22 +00:00
}
}
r = new_r ;
g = new_g ;
b = new_b ;
}
}
2020-12-25 12:14:17 +00:00
if ( gb - > light_temperature ) {
double light_r , light_g , light_b ;
temperature_tint ( gb - > light_temperature , & light_r , & light_g , & light_b ) ;
r = round ( light_r * r ) ;
g = round ( light_g * g ) ;
b = round ( light_b * b ) ;
}
2017-10-12 14:22:22 +00:00
return gb - > rgb_encode_callback ( gb , r , g , b ) ;
}
2016-06-18 17:29:11 +00:00
void GB_palette_changed ( GB_gameboy_t * gb , bool background_palette , uint8_t index )
2016-03-30 20:07:55 +00:00
{
2018-06-16 10:59:33 +00:00
if ( ! gb - > rgb_encode_callback | | ! GB_is_cgb ( gb ) ) return ;
2021-12-17 19:16:23 +00:00
uint8_t * palette_data = background_palette ? gb - > background_palettes_data : gb - > object_palettes_data ;
2016-06-18 17:29:11 +00:00
uint16_t color = palette_data [ index & ~ 1 ] | ( palette_data [ index | 1 ] < < 8 ) ;
2016-03-30 20:07:55 +00:00
2021-12-17 19:16:23 +00:00
( background_palette ? gb - > background_palettes_rgb : gb - > object_palettes_rgb ) [ index / 2 ] = GB_convert_rgb15 ( gb , color , false ) ;
2017-10-12 14:22:22 +00:00
}
void GB_set_color_correction_mode ( GB_gameboy_t * gb , GB_color_correction_mode_t mode )
{
gb - > color_correction_mode = mode ;
2018-06-16 10:59:33 +00:00
if ( GB_is_cgb ( gb ) ) {
2017-10-16 17:48:39 +00:00
for ( unsigned i = 0 ; i < 32 ; i + + ) {
GB_palette_changed ( gb , false , i * 2 ) ;
GB_palette_changed ( gb , true , i * 2 ) ;
}
2017-10-12 14:22:22 +00:00
}
2016-03-30 20:07:55 +00:00
}
2020-12-25 12:14:17 +00:00
void GB_set_light_temperature ( GB_gameboy_t * gb , double temperature )
{
gb - > light_temperature = temperature ;
if ( GB_is_cgb ( gb ) ) {
for ( unsigned i = 0 ; i < 32 ; i + + ) {
GB_palette_changed ( gb , false , i * 2 ) ;
GB_palette_changed ( gb , true , i * 2 ) ;
}
}
}
2016-06-12 16:39:05 +00:00
/*
2017-02-19 00:22:50 +00:00
STAT interrupt is implemented based on this finding :
http : //board.byuu.org/phpbb3/viewtopic.php?p=25527#p25531
General timing is based on GiiBiiAdvance ' s documents :
https : //github.com/AntonioND/giibiiadvance
2016-06-12 16:39:05 +00:00
*/
2018-02-24 22:48:45 +00:00
void GB_STAT_update ( GB_gameboy_t * gb )
2016-03-30 20:07:55 +00:00
{
2018-02-28 22:12:04 +00:00
if ( ! ( gb - > io_registers [ GB_IO_LCDC ] & 0x80 ) ) return ;
2022-02-05 15:43:48 +00:00
if ( GB_is_dma_active ( gb ) & & ( gb - > io_registers [ GB_IO_STAT ] & 3 ) = = 2 ) {
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
}
2018-02-28 22:12:04 +00:00
2018-06-03 23:07:38 +00:00
bool previous_interrupt_line = gb - > stat_interrupt_line ;
2018-02-24 22:48:45 +00:00
/* Set LY=LYC bit */
2018-07-03 18:56:32 +00:00
/* TODO: This behavior might not be correct for CGB revisions other than C and E */
if ( gb - > ly_for_comparison ! = ( uint16_t ) - 1 | | gb - > model < = GB_MODEL_CGB_C ) {
2018-05-11 09:38:55 +00:00
if ( gb - > ly_for_comparison = = gb - > io_registers [ GB_IO_LYC ] ) {
gb - > lyc_interrupt_line = true ;
gb - > io_registers [ GB_IO_STAT ] | = 4 ;
}
else {
if ( gb - > ly_for_comparison ! = ( uint16_t ) - 1 ) {
gb - > lyc_interrupt_line = false ;
}
gb - > io_registers [ GB_IO_STAT ] & = ~ 4 ;
2018-04-27 10:40:39 +00:00
}
2017-02-19 00:22:50 +00:00
}
2018-06-03 23:07:38 +00:00
switch ( gb - > mode_for_interrupt ) {
case 0 : gb - > stat_interrupt_line = gb - > io_registers [ GB_IO_STAT ] & 8 ; break ;
2018-02-24 22:48:45 +00:00
case 1 : gb - > stat_interrupt_line = gb - > io_registers [ GB_IO_STAT ] & 0x10 ; break ;
2018-06-03 23:07:38 +00:00
case 2 : gb - > stat_interrupt_line = gb - > io_registers [ GB_IO_STAT ] & 0x20 ; break ;
default : gb - > stat_interrupt_line = false ;
2017-09-08 20:46:38 +00:00
}
2018-02-24 22:48:45 +00:00
/* User requested a LY=LYC interrupt and the LY=LYC bit is on */
2018-04-27 10:40:39 +00:00
if ( ( gb - > io_registers [ GB_IO_STAT ] & 0x40 ) & & gb - > lyc_interrupt_line ) {
2018-02-24 22:48:45 +00:00
gb - > stat_interrupt_line = true ;
2017-09-08 20:46:38 +00:00
}
2017-09-08 14:25:01 +00:00
2018-04-01 18:45:56 +00:00
if ( gb - > stat_interrupt_line & & ! previous_interrupt_line ) {
2018-02-24 22:48:45 +00:00
gb - > io_registers [ GB_IO_IF ] | = 2 ;
}
}
void GB_lcd_off ( GB_gameboy_t * gb )
{
gb - > display_state = 0 ;
gb - > display_cycles = 0 ;
/* When the LCD is disabled, state is constant */
2017-02-19 00:22:50 +00:00
2022-01-30 23:02:31 +00:00
if ( gb - > hdma_on_hblank & & ( gb - > io_registers [ GB_IO_STAT ] & 3 ) ) {
gb - > hdma_on = true ;
}
2018-03-18 18:32:19 +00:00
/* When the LCD is off, LY is 0 and STAT mode is 0. */
2018-02-24 22:48:45 +00:00
gb - > io_registers [ GB_IO_LY ] = 0 ;
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
2022-01-30 23:02:31 +00:00
2018-02-24 22:48:45 +00:00
gb - > oam_read_blocked = false ;
gb - > vram_read_blocked = false ;
gb - > oam_write_blocked = false ;
gb - > vram_write_blocked = false ;
2019-01-19 17:32:26 +00:00
gb - > cgb_palettes_blocked = false ;
2018-02-24 22:48:45 +00:00
gb - > current_line = 0 ;
gb - > ly_for_comparison = 0 ;
2018-03-23 23:58:37 +00:00
2018-03-27 12:46:00 +00:00
gb - > accessed_oam_row = - 1 ;
2020-02-26 20:24:08 +00:00
gb - > wy_triggered = false ;
2021-12-10 17:49:52 +00:00
if ( unlikely ( gb - > lcd_line_callback ) ) {
gb - > lcd_line_callback ( gb , 0 ) ;
}
2018-02-24 22:48:45 +00:00
}
2022-01-23 19:05:29 +00:00
static inline uint8_t oam_read ( GB_gameboy_t * gb , uint8_t addr )
{
if ( unlikely ( gb - > oam_ppu_blocked ) ) {
return 0xFF ;
}
2022-02-13 14:58:44 +00:00
if ( unlikely ( gb - > dma_current_dest < = 0xA0 & & gb - > dma_current_dest > 0 ) ) { // TODO: what happens in the last and first M cycles?
2022-02-05 12:52:09 +00:00
if ( gb - > hdma_in_progress ) {
return GB_read_oam ( gb , ( gb - > hdma_current_src & ~ 1 ) | ( addr & 1 ) ) ;
}
2022-01-23 19:05:29 +00:00
return gb - > oam [ ( ( gb - > dma_current_dest - 1 + ( gb - > halted | | gb - > stopped ) ) & ~ 1 ) | ( addr & 1 ) ] ;
}
return gb - > oam [ addr ] ;
}
2018-03-23 16:07:14 +00:00
static void add_object_from_index ( GB_gameboy_t * gb , unsigned index )
2018-03-04 20:21:56 +00:00
{
2022-02-06 11:02:15 +00:00
if ( likely ( ! GB_is_dma_active ( gb ) | | gb - > halted | | gb - > stopped ) ) {
gb - > mode2_y_bus = oam_read ( gb , index * 4 ) ;
gb - > mode2_x_bus = oam_read ( gb , index * 4 + 1 ) ;
}
2018-03-23 16:07:14 +00:00
if ( gb - > n_visible_objs = = 10 ) return ;
2018-04-07 10:59:36 +00:00
/* TODO: It appears that DMA blocks PPU access to OAM, but it needs verification. */
2022-02-05 16:50:33 +00:00
if ( unlikely ( GB_is_dma_active ( gb ) & & ( gb - > halted | | gb - > stopped ) ) ) {
2022-01-23 19:05:29 +00:00
if ( gb - > model < GB_MODEL_CGB_E ) {
return ;
}
2022-02-05 16:50:33 +00:00
/* CGB-0 to CGB-D: Halted DMA blocks Mode 2;
2022-01-23 19:05:29 +00:00
Pre - CGB : Unit specific behavior , some units read FFs , some units read using
several different corruption pattterns . For simplicity , we emulate
FFs . */
2018-04-07 10:59:36 +00:00
}
2020-02-15 17:21:43 +00:00
2022-01-23 19:05:29 +00:00
if ( unlikely ( gb - > oam_ppu_blocked ) ) {
2020-02-15 17:21:43 +00:00
return ;
}
2022-02-06 11:02:15 +00:00
2018-03-04 20:21:56 +00:00
bool height_16 = ( gb - > io_registers [ GB_IO_LCDC ] & 4 ) ! = 0 ;
2022-02-05 16:50:33 +00:00
signed y = gb - > mode2_y_bus - 16 ;
/* This reverse sorts the visible objects by location and priority */
2018-03-23 16:07:14 +00:00
if ( y < = gb - > current_line & & y + ( height_16 ? 16 : 8 ) > gb - > current_line ) {
unsigned j = 0 ;
for ( ; j < gb - > n_visible_objs ; j + + ) {
2022-02-05 16:50:33 +00:00
if ( gb - > objects_x [ j ] < = gb - > mode2_x_bus ) break ;
2018-03-04 20:21:56 +00:00
}
2018-03-23 16:07:14 +00:00
memmove ( gb - > visible_objs + j + 1 , gb - > visible_objs + j , gb - > n_visible_objs - j ) ;
2022-01-14 13:07:50 +00:00
memmove ( gb - > objects_x + j + 1 , gb - > objects_x + j , gb - > n_visible_objs - j ) ;
memmove ( gb - > objects_y + j + 1 , gb - > objects_y + j , gb - > n_visible_objs - j ) ;
2018-03-23 16:07:14 +00:00
gb - > visible_objs [ j ] = index ;
2022-02-05 16:50:33 +00:00
gb - > objects_x [ j ] = gb - > mode2_x_bus ;
gb - > objects_y [ j ] = gb - > mode2_y_bus ;
2018-03-23 16:07:14 +00:00
gb - > n_visible_objs + + ;
2018-03-04 20:21:56 +00:00
}
}
2021-10-26 22:40:28 +00:00
static uint8_t data_for_tile_sel_glitch ( GB_gameboy_t * gb , bool * should_use , bool * cgb_d_glitch )
2020-11-20 14:24:16 +00:00
{
/*
Based on Matt Currie ' s research here :
https : //github.com/mattcurrie/mealybug-tearoom-tests/blob/master/the-comprehensive-game-boy-ppu-documentation.md#tile_sel-bit-4
*/
* should_use = true ;
2021-10-26 22:40:28 +00:00
* cgb_d_glitch = false ;
2020-11-20 14:24:16 +00:00
if ( gb - > io_registers [ GB_IO_LCDC ] & 0x10 ) {
2021-10-26 22:40:28 +00:00
if ( gb - > model ! = GB_MODEL_CGB_D ) {
* should_use = ! ( gb - > current_tile & 0x80 ) ;
return gb - > current_tile ;
}
* cgb_d_glitch = true ;
* should_use = false ;
gb - > io_registers [ GB_IO_LCDC ] & = ~ 0x10 ;
if ( gb - > fetcher_state = = 3 ) {
* should_use = false ;
* cgb_d_glitch = true ;
return 0 ;
}
return 0 ;
2020-11-20 14:24:16 +00:00
}
return gb - > data_for_sel_glitch ;
}
2018-03-03 13:47:36 +00:00
static void render_pixel_if_possible ( GB_gameboy_t * gb )
{
2021-12-17 19:12:26 +00:00
const GB_fifo_item_t * fifo_item = NULL ;
const GB_fifo_item_t * oam_fifo_item = NULL ;
2018-03-04 21:27:31 +00:00
bool draw_oam = false ;
bool bg_enabled = true , bg_priority = false ;
2018-04-06 15:26:04 +00:00
2020-02-28 20:36:51 +00:00
if ( fifo_size ( & gb - > bg_fifo ) ) {
2018-03-03 15:22:23 +00:00
fifo_item = fifo_pop ( & gb - > bg_fifo ) ;
2018-03-09 16:52:36 +00:00
bg_priority = fifo_item - > bg_priority ;
2020-02-28 20:36:51 +00:00
if ( fifo_size ( & gb - > oam_fifo ) ) {
oam_fifo_item = fifo_pop ( & gb - > oam_fifo ) ;
2021-12-17 19:12:26 +00:00
if ( oam_fifo_item - > pixel & & ( gb - > io_registers [ GB_IO_LCDC ] & 2 ) & & unlikely ( ! gb - > objects_disabled ) ) {
2020-02-28 20:36:51 +00:00
draw_oam = true ;
bg_priority | = oam_fifo_item - > bg_priority ;
}
2018-03-04 21:27:31 +00:00
}
}
2020-02-28 20:36:51 +00:00
if ( ! fifo_item ) return ;
2020-02-24 19:23:06 +00:00
2018-03-03 13:47:36 +00:00
/* Drop pixels for scrollings */
2020-02-08 11:28:46 +00:00
if ( gb - > position_in_line > = 160 | | ( gb - > disable_rendering & & ! gb - > sgb ) ) {
2018-03-03 13:47:36 +00:00
gb - > position_in_line + + ;
return ;
}
2018-03-03 17:36:21 +00:00
/* Mixing */
if ( ( gb - > io_registers [ GB_IO_LCDC ] & 0x1 ) = = 0 ) {
if ( gb - > cgb_mode ) {
2018-03-20 22:02:35 +00:00
bg_priority = false ;
2018-03-03 17:36:21 +00:00
}
else {
bg_enabled = false ;
}
}
2021-12-17 19:12:26 +00:00
if ( unlikely ( gb - > background_disabled ) ) {
bg_enabled = false ;
static const GB_fifo_item_t empty_item = { 0 , } ;
fifo_item = & empty_item ;
}
2019-07-17 21:53:11 +00:00
uint8_t icd_pixel = 0 ;
2020-02-08 11:28:46 +00:00
uint32_t * dest = NULL ;
if ( ! gb - > sgb ) {
if ( gb - > border_mode ! = GB_BORDER_ALWAYS ) {
2020-03-06 12:41:13 +00:00
dest = gb - > screen + gb - > lcd_x + gb - > current_line * WIDTH ;
2020-02-08 11:28:46 +00:00
}
else {
2020-03-06 12:41:13 +00:00
dest = gb - > screen + gb - > lcd_x + gb - > current_line * BORDERED_WIDTH + ( BORDERED_WIDTH - WIDTH ) / 2 + ( BORDERED_HEIGHT - LINES ) / 2 * BORDERED_WIDTH ;
2020-02-08 11:28:46 +00:00
}
}
2018-04-05 09:27:01 +00:00
{
uint8_t pixel = bg_enabled ? fifo_item - > pixel : 0 ;
2018-03-04 21:27:31 +00:00
if ( pixel & & bg_priority ) {
draw_oam = false ;
}
2018-03-03 17:36:21 +00:00
if ( ! gb - > cgb_mode ) {
pixel = ( ( gb - > io_registers [ GB_IO_BGP ] > > ( pixel < < 1 ) ) & 3 ) ;
}
2018-11-15 23:53:01 +00:00
if ( gb - > sgb ) {
2019-02-15 15:04:48 +00:00
if ( gb - > current_lcd_line < LINES ) {
2020-03-06 12:41:13 +00:00
gb - > sgb - > screen_buffer [ gb - > lcd_x + gb - > current_lcd_line * WIDTH ] = gb - > stopped ? 0 : pixel ;
2019-02-15 15:04:48 +00:00
}
2018-11-14 22:21:21 +00:00
}
2019-07-15 20:02:58 +00:00
else if ( gb - > model & GB_MODEL_NO_SFC_BIT ) {
2019-07-16 17:44:27 +00:00
if ( gb - > icd_pixel_callback ) {
2019-07-17 21:53:11 +00:00
icd_pixel = pixel ;
2019-07-16 17:44:27 +00:00
}
2019-07-15 20:02:58 +00:00
}
2020-02-15 17:21:43 +00:00
else if ( gb - > cgb_palettes_ppu_blocked ) {
* dest = gb - > rgb_encode_callback ( gb , 0 , 0 , 0 ) ;
}
2018-11-14 22:21:21 +00:00
else {
2020-02-08 11:28:46 +00:00
* dest = gb - > background_palettes_rgb [ fifo_item - > palette * 4 + pixel ] ;
2018-11-14 22:21:21 +00:00
}
2018-03-03 17:36:21 +00:00
}
2018-03-04 21:27:31 +00:00
if ( draw_oam ) {
uint8_t pixel = oam_fifo_item - > pixel ;
if ( ! gb - > cgb_mode ) {
2018-03-30 14:06:27 +00:00
/* Todo: Verify access timings */
2018-03-04 21:27:31 +00:00
pixel = ( ( gb - > io_registers [ oam_fifo_item - > palette ? GB_IO_OBP1 : GB_IO_OBP0 ] > > ( pixel < < 1 ) ) & 3 ) ;
}
2018-11-15 23:53:01 +00:00
if ( gb - > sgb ) {
2019-02-15 15:04:48 +00:00
if ( gb - > current_lcd_line < LINES ) {
2020-03-06 12:41:13 +00:00
gb - > sgb - > screen_buffer [ gb - > lcd_x + gb - > current_lcd_line * WIDTH ] = gb - > stopped ? 0 : pixel ;
2019-02-15 15:04:48 +00:00
}
2018-11-14 22:21:21 +00:00
}
2019-07-15 20:02:58 +00:00
else if ( gb - > model & GB_MODEL_NO_SFC_BIT ) {
2019-07-16 17:44:27 +00:00
if ( gb - > icd_pixel_callback ) {
2019-07-17 21:53:11 +00:00
icd_pixel = pixel ;
2019-07-16 17:44:27 +00:00
}
2019-07-15 20:02:58 +00:00
}
2020-02-15 17:21:43 +00:00
else if ( gb - > cgb_palettes_ppu_blocked ) {
* dest = gb - > rgb_encode_callback ( gb , 0 , 0 , 0 ) ;
}
2018-11-14 22:21:21 +00:00
else {
2021-12-17 19:16:23 +00:00
* dest = gb - > object_palettes_rgb [ oam_fifo_item - > palette * 4 + pixel ] ;
2018-11-14 22:21:21 +00:00
}
2018-03-04 21:27:31 +00:00
}
2020-04-24 17:37:57 +00:00
2019-07-17 21:53:11 +00:00
if ( gb - > model & GB_MODEL_NO_SFC_BIT ) {
if ( gb - > icd_pixel_callback ) {
gb - > icd_pixel_callback ( gb , icd_pixel ) ;
}
}
2018-03-04 21:27:31 +00:00
2018-03-03 13:47:36 +00:00
gb - > position_in_line + + ;
2020-03-06 12:41:13 +00:00
gb - > lcd_x + + ;
2020-02-29 15:06:08 +00:00
gb - > window_is_being_fetched = false ;
2018-03-03 13:47:36 +00:00
}
2018-03-04 20:21:56 +00:00
2022-01-16 11:50:59 +00:00
static inline void dma_sync ( GB_gameboy_t * gb , unsigned * cycles )
{
if ( unlikely ( GB_is_dma_active ( gb ) ) ) {
unsigned offset = * cycles - gb - > display_cycles ; // Time passed in 8MHz ticks
if ( offset ) {
* cycles = gb - > display_cycles ;
if ( ! gb - > cgb_double_speed ) {
offset > > = 1 ; // Convert to T-cycles
}
unsigned old = gb - > dma_cycles ;
gb - > dma_cycles = offset ;
GB_dma_run ( gb ) ;
gb - > dma_cycles = old - offset ;
}
}
}
2018-03-10 22:17:57 +00:00
/* All verified CGB timings are based on CGB CPU E. CGB CPUs >= D are known to have
slightly different timings than CPUs < = C .
Todo : Add support to CPU C and older */
2018-04-05 12:33:21 +00:00
static inline uint8_t fetcher_y ( GB_gameboy_t * gb )
{
2020-02-23 21:16:45 +00:00
return gb - > wx_triggered ? gb - > window_y : gb - > current_line + gb - > io_registers [ GB_IO_SCY ] ;
2018-04-05 12:33:21 +00:00
}
2022-01-16 11:50:59 +00:00
static inline uint8_t vram_read ( GB_gameboy_t * gb , uint16_t addr )
{
if ( unlikely ( gb - > vram_ppu_blocked ) ) {
return 0xFF ;
}
2022-01-30 23:02:31 +00:00
if ( unlikely ( gb - > hdma_in_progress ) ) {
2022-01-30 12:38:58 +00:00
gb - > addr_for_hdma_conflict = addr ;
return 0 ;
}
// TODO: what if both?
2022-02-13 14:58:44 +00:00
else if ( unlikely ( gb - > dma_current_dest < = 0xA0 & & gb - > dma_current_dest > 0 & & ( gb - > dma_current_src & 0xE000 ) = = 0x8000 ) ) { // TODO: what happens in the last and first M cycles?
2022-01-16 11:50:59 +00:00
// DMAing from VRAM!
2022-01-17 20:07:24 +00:00
/* TODO: AGS has its own, very different pattern, but AGS is not currently a supported model */
2022-01-17 20:56:13 +00:00
/* TODO: Research this when researching odd modes */
2022-01-22 20:52:34 +00:00
/* TODO: probably not 100% on the first few reads during halt/stop modes*/
2022-01-23 19:05:29 +00:00
unsigned offset = 1 - ( gb - > halted | | gb - > stopped ) ;
2022-01-17 20:07:24 +00:00
if ( GB_is_cgb ( gb ) ) {
if ( gb - > dma_ppu_vram_conflict ) {
addr = ( gb - > dma_ppu_vram_conflict_addr & 0x1FFF ) | ( addr & 0x2000 ) ;
}
2022-01-22 20:52:34 +00:00
else if ( gb - > dma_cycles_modulo & & ! gb - > halted & & ! gb - > stopped ) {
2022-01-17 20:07:24 +00:00
addr & = 0x2000 ;
2022-01-23 19:05:29 +00:00
addr | = ( ( gb - > dma_current_src - offset ) & 0x1FFF ) ;
2022-01-17 20:07:24 +00:00
}
else {
2022-01-23 19:05:29 +00:00
addr & = 0x2000 | ( ( gb - > dma_current_src - offset ) & 0x1FFF ) ;
2022-01-17 20:07:24 +00:00
gb - > dma_ppu_vram_conflict_addr = addr ;
2022-01-22 20:52:34 +00:00
gb - > dma_ppu_vram_conflict = ! gb - > halted & & ! gb - > stopped ;
2022-01-17 20:07:24 +00:00
}
}
else {
2022-01-23 19:05:29 +00:00
addr | = ( ( gb - > dma_current_src - offset ) & 0x1FFF ) ;
2022-01-17 20:07:24 +00:00
}
2022-01-23 19:05:29 +00:00
gb - > oam [ gb - > dma_current_dest - offset ] = gb - > vram [ ( addr & 0x1FFF ) | ( gb - > cgb_vram_bank ? 0x2000 : 0 ) ] ;
2022-01-16 11:50:59 +00:00
}
return gb - > vram [ addr ] ;
}
static void advance_fetcher_state_machine ( GB_gameboy_t * gb , unsigned * cycles )
2018-04-06 16:29:49 +00:00
{
typedef enum {
GB_FETCHER_GET_TILE ,
GB_FETCHER_GET_TILE_DATA_LOWER ,
GB_FETCHER_GET_TILE_DATA_HIGH ,
GB_FETCHER_PUSH ,
GB_FETCHER_SLEEP ,
} fetcher_step_t ;
2021-10-08 16:29:43 +00:00
static const fetcher_step_t fetcher_state_machine [ 8 ] = {
2018-04-06 16:29:49 +00:00
GB_FETCHER_SLEEP ,
GB_FETCHER_GET_TILE ,
GB_FETCHER_SLEEP ,
GB_FETCHER_GET_TILE_DATA_LOWER ,
GB_FETCHER_SLEEP ,
GB_FETCHER_GET_TILE_DATA_HIGH ,
GB_FETCHER_PUSH ,
GB_FETCHER_PUSH ,
} ;
2020-03-04 21:43:05 +00:00
switch ( fetcher_state_machine [ gb - > fetcher_state & 7 ] ) {
2018-04-06 16:29:49 +00:00
case GB_FETCHER_GET_TILE : {
2022-01-16 11:50:59 +00:00
dma_sync ( gb , cycles ) ;
2018-04-06 16:29:49 +00:00
uint16_t map = 0x1800 ;
2020-02-23 21:16:45 +00:00
if ( ! ( gb - > io_registers [ GB_IO_LCDC ] & 0x20 ) ) {
gb - > wx_triggered = false ;
2020-03-01 21:58:28 +00:00
gb - > wx166_glitch = false ;
2020-02-23 21:16:45 +00:00
}
2018-04-06 16:29:49 +00:00
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
2020-02-23 21:16:45 +00:00
if ( gb - > io_registers [ GB_IO_LCDC ] & 0x08 & & ! gb - > wx_triggered ) {
2018-04-06 16:29:49 +00:00
map = 0x1C00 ;
}
2020-02-23 21:16:45 +00:00
else if ( gb - > io_registers [ GB_IO_LCDC ] & 0x40 & & gb - > wx_triggered ) {
2018-04-06 16:29:49 +00:00
map = 0x1C00 ;
}
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
uint8_t y = fetcher_y ( gb ) ;
2020-02-23 21:16:45 +00:00
uint8_t x = 0 ;
if ( gb - > wx_triggered ) {
x = gb - > window_tile_x ;
}
else {
2021-10-08 16:29:43 +00:00
/* TODO: There is some CGB timing error around here.
Adjusting SCX by 7 or less shouldn ' t have an effect on a CGB ,
but SameBoy is affected by a change of both 7 and 6 ( but not less ) . */
x = ( ( gb - > io_registers [ GB_IO_SCX ] + gb - > position_in_line + 8 ) / 8 ) & 0x1F ;
2020-02-23 21:16:45 +00:00
}
2018-07-03 19:14:53 +00:00
if ( gb - > model > GB_MODEL_CGB_C ) {
2018-07-03 19:25:09 +00:00
/* This value is cached on the CGB-D and newer, so it cannot be used to mix tiles together */
2018-04-06 16:29:49 +00:00
gb - > fetcher_y = y ;
}
2020-05-29 20:10:23 +00:00
gb - > last_tile_index_address = map + x + y / 8 * 32 ;
2022-01-16 11:50:59 +00:00
gb - > current_tile = vram_read ( gb , gb - > last_tile_index_address ) ;
2018-06-16 10:59:33 +00:00
if ( GB_is_cgb ( gb ) ) {
2018-04-06 16:29:49 +00:00
/* The CGB actually accesses both the tile index AND the attributes in the same T-cycle.
2020-05-29 20:10:23 +00:00
This probably means the CGB has a 16 - bit data bus for the VRAM . */
2022-01-16 11:50:59 +00:00
gb - > current_tile_attributes = vram_read ( gb , gb - > last_tile_index_address + 0x2000 ) ;
2018-04-06 16:29:49 +00:00
}
}
2018-04-07 00:00:26 +00:00
gb - > fetcher_state + + ;
2018-04-06 16:29:49 +00:00
break ;
case GB_FETCHER_GET_TILE_DATA_LOWER : {
2022-01-16 11:50:59 +00:00
dma_sync ( gb , cycles ) ;
2020-11-20 14:24:16 +00:00
bool use_glitched = false ;
2021-10-26 22:40:28 +00:00
bool cgb_d_glitch = false ;
2020-11-20 14:24:16 +00:00
if ( gb - > tile_sel_glitch ) {
2021-10-26 22:40:28 +00:00
gb - > current_tile_data [ 0 ] = data_for_tile_sel_glitch ( gb , & use_glitched , & cgb_d_glitch ) ;
2020-11-20 14:24:16 +00:00
}
2018-04-06 16:29:49 +00:00
uint8_t y_flip = 0 ;
uint16_t tile_address = 0 ;
2018-07-03 19:14:53 +00:00
uint8_t y = gb - > model > GB_MODEL_CGB_C ? gb - > fetcher_y : fetcher_y ( gb ) ;
2018-04-06 16:29:49 +00:00
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
if ( gb - > io_registers [ GB_IO_LCDC ] & 0x10 ) {
tile_address = gb - > current_tile * 0x10 ;
}
else {
tile_address = ( int8_t ) gb - > current_tile * 0x10 + 0x1000 ;
}
if ( gb - > current_tile_attributes & 8 ) {
tile_address + = 0x2000 ;
}
if ( gb - > current_tile_attributes & 0x40 ) {
y_flip = 0x7 ;
}
2020-11-20 14:24:16 +00:00
if ( ! use_glitched ) {
gb - > current_tile_data [ 0 ] =
2022-01-16 11:50:59 +00:00
vram_read ( gb , tile_address + ( ( y & 7 ) ^ y_flip ) * 2 ) ;
2020-11-20 14:24:16 +00:00
}
2021-10-26 22:40:28 +00:00
if ( ( gb - > io_registers [ GB_IO_LCDC ] & 0x10 ) & & gb - > tile_sel_glitch ) {
2020-11-20 14:24:16 +00:00
gb - > data_for_sel_glitch =
2022-01-16 11:50:59 +00:00
vram_read ( gb , tile_address + ( ( y & 7 ) ^ y_flip ) * 2 ) ;
2020-02-15 17:21:43 +00:00
}
2021-10-26 22:40:28 +00:00
else if ( cgb_d_glitch ) {
2022-01-16 11:50:59 +00:00
gb - > data_for_sel_glitch = vram_read ( gb , gb - > current_tile * 0x10 + ( ( y & 7 ) ^ y_flip ) * 2 ) ;
2021-10-26 22:40:28 +00:00
}
2018-04-06 16:29:49 +00:00
}
2018-04-07 00:00:26 +00:00
gb - > fetcher_state + + ;
2018-04-06 16:29:49 +00:00
break ;
case GB_FETCHER_GET_TILE_DATA_HIGH : {
2022-01-16 11:50:59 +00:00
dma_sync ( gb , cycles ) ;
2020-11-20 14:24:16 +00:00
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
bool use_glitched = false ;
2021-10-26 22:40:28 +00:00
bool cgb_d_glitch = false ;
2020-11-20 14:24:16 +00:00
if ( gb - > tile_sel_glitch ) {
2021-10-26 22:40:28 +00:00
gb - > current_tile_data [ 1 ] = data_for_tile_sel_glitch ( gb , & use_glitched , & cgb_d_glitch ) ;
2020-11-20 14:24:16 +00:00
}
2018-04-06 16:29:49 +00:00
uint16_t tile_address = 0 ;
2018-07-03 19:14:53 +00:00
uint8_t y = gb - > model > GB_MODEL_CGB_C ? gb - > fetcher_y : fetcher_y ( gb ) ;
2018-04-06 16:29:49 +00:00
if ( gb - > io_registers [ GB_IO_LCDC ] & 0x10 ) {
tile_address = gb - > current_tile * 0x10 ;
}
else {
tile_address = ( int8_t ) gb - > current_tile * 0x10 + 0x1000 ;
}
if ( gb - > current_tile_attributes & 8 ) {
tile_address + = 0x2000 ;
}
uint8_t y_flip = 0 ;
if ( gb - > current_tile_attributes & 0x40 ) {
y_flip = 0x7 ;
}
2021-10-26 22:40:28 +00:00
gb - > last_tile_data_address = tile_address + ( ( y & 7 ) ^ y_flip ) * 2 + 1 - cgb_d_glitch ;
2020-11-20 14:24:16 +00:00
if ( ! use_glitched ) {
gb - > current_tile_data [ 1 ] =
2022-01-16 11:50:59 +00:00
vram_read ( gb , gb - > last_tile_data_address ) ;
2020-11-20 14:24:16 +00:00
}
2021-10-26 22:40:28 +00:00
if ( ( gb - > io_registers [ GB_IO_LCDC ] & 0x10 ) & & gb - > tile_sel_glitch ) {
2022-01-16 11:50:59 +00:00
gb - > data_for_sel_glitch = vram_read ( gb , gb - > last_tile_data_address ) ;
2021-10-26 22:40:28 +00:00
}
else if ( cgb_d_glitch ) {
2022-01-16 11:50:59 +00:00
gb - > data_for_sel_glitch = vram_read ( gb , gb - > current_tile * 0x10 + ( ( y & 7 ) ^ y_flip ) * 2 + 1 ) ;
2020-02-15 17:21:43 +00:00
}
2018-04-06 16:29:49 +00:00
}
2020-03-03 00:21:19 +00:00
if ( gb - > wx_triggered ) {
gb - > window_tile_x + + ;
2022-02-13 14:58:44 +00:00
gb - > window_tile_x & = 0x1F ;
2018-04-06 16:29:49 +00:00
}
2020-03-03 00:21:19 +00:00
// fallthrough
2018-04-06 16:29:49 +00:00
case GB_FETCHER_PUSH : {
2020-03-04 21:43:05 +00:00
if ( gb - > fetcher_state < 7 ) {
2020-03-03 00:21:19 +00:00
gb - > fetcher_state + + ;
}
2018-04-07 10:02:53 +00:00
if ( fifo_size ( & gb - > bg_fifo ) > 0 ) break ;
2020-02-23 21:16:45 +00:00
2018-04-06 16:29:49 +00:00
fifo_push_bg_row ( & gb - > bg_fifo , gb - > current_tile_data [ 0 ] , gb - > current_tile_data [ 1 ] ,
gb - > current_tile_attributes & 7 , gb - > current_tile_attributes & 0x80 , gb - > current_tile_attributes & 0x20 ) ;
2020-02-23 22:20:58 +00:00
gb - > fetcher_state = 0 ;
2018-04-06 16:29:49 +00:00
}
break ;
case GB_FETCHER_SLEEP :
2018-04-07 00:00:26 +00:00
{
gb - > fetcher_state + + ;
}
2018-04-06 16:29:49 +00:00
break ;
2022-01-03 15:17:35 +00:00
nodefault ;
2018-04-06 16:29:49 +00:00
}
}
2022-01-14 13:07:50 +00:00
static uint16_t get_object_line_address ( GB_gameboy_t * gb , uint8_t y , uint8_t tile , uint8_t flags )
{
2020-02-21 14:43:51 +00:00
bool height_16 = ( gb - > io_registers [ GB_IO_LCDC ] & 4 ) ! = 0 ; /* Todo: Which T-cycle actually reads this? */
2022-01-14 13:07:50 +00:00
uint8_t tile_y = ( gb - > current_line - y ) & ( height_16 ? 0xF : 7 ) ;
2018-04-06 16:29:49 +00:00
2022-01-14 13:07:50 +00:00
if ( flags & 0x40 ) { /* Flip Y */
2020-02-21 14:43:51 +00:00
tile_y ^ = height_16 ? 0xF : 7 ;
}
/* Todo: I'm not 100% sure an access to OAM can't trigger the OAM bug while we're accessing this */
2022-01-14 13:07:50 +00:00
uint16_t line_address = ( height_16 ? tile & 0xFE : tile ) * 0x10 + tile_y * 2 ;
2020-02-21 14:43:51 +00:00
2022-01-14 13:07:50 +00:00
if ( gb - > cgb_mode & & ( flags & 0x8 ) ) { /* Use VRAM bank 2 */
2020-02-21 14:43:51 +00:00
line_address + = 0x2000 ;
}
return line_address ;
2018-04-06 16:29:49 +00:00
}
2021-12-25 23:47:59 +00:00
static inline uint8_t flip ( uint8_t x )
{
x = ( x & 0xF0 ) > > 4 | ( x & 0x0F ) < < 4 ;
x = ( x & 0xCC ) > > 2 | ( x & 0x33 ) < < 2 ;
x = ( x & 0xAA ) > > 1 | ( x & 0x55 ) < < 1 ;
return x ;
}
static inline void get_tile_data ( const GB_gameboy_t * gb , uint8_t tile_x , uint8_t y , uint16_t map , uint8_t * attributes , uint8_t * data0 , uint8_t * data1 )
{
uint8_t current_tile = gb - > vram [ map + ( tile_x & 0x1F ) + y / 8 * 32 ] ;
* attributes = GB_is_cgb ( gb ) ? gb - > vram [ 0x2000 + map + ( tile_x & 0x1F ) + y / 8 * 32 ] : 0 ;
uint16_t tile_address = 0 ;
/* Todo: Verified for DMG (Tested: SGB2), CGB timing is wrong. */
if ( gb - > io_registers [ GB_IO_LCDC ] & 0x10 ) {
tile_address = current_tile * 0x10 ;
}
else {
tile_address = ( int8_t ) current_tile * 0x10 + 0x1000 ;
}
if ( * attributes & 8 ) {
tile_address + = 0x2000 ;
}
uint8_t y_flip = 0 ;
if ( * attributes & 0x40 ) {
y_flip = 0x7 ;
}
* data0 = gb - > vram [ tile_address + ( ( y & 7 ) ^ y_flip ) * 2 ] ;
* data1 = gb - > vram [ tile_address + ( ( y & 7 ) ^ y_flip ) * 2 + 1 ] ;
if ( * attributes & 0x20 ) {
* data0 = flip ( * data0 ) ;
* data1 = flip ( * data1 ) ;
}
}
static void render_line ( GB_gameboy_t * gb )
{
if ( gb - > disable_rendering ) return ;
if ( ! gb - > screen ) return ;
if ( gb - > current_line > 144 ) return ; // Corrupt save state
struct {
unsigned pixel : 2 ; // Color, 0-3
unsigned priority : 6 ; // Object priority – 0 in DMG, OAM index in CGB
unsigned palette : 3 ; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG)
bool bg_priority : 1 ; // BG priority bit
2021-12-26 00:38:54 +00:00
} _object_buffer [ 160 + 16 ] ; // allocate extra to avoid per pixel checks
static const uint8_t empty_object_buffer [ sizeof ( _object_buffer ) ] ;
const typeof ( _object_buffer [ 0 ] ) * object_buffer ;
2021-12-25 23:47:59 +00:00
if ( gb - > n_visible_objs & & ! gb - > objects_disabled & & ( gb - > io_registers [ GB_IO_LCDC ] & 2 ) ) {
2021-12-26 00:38:54 +00:00
object_buffer = & _object_buffer [ 0 ] ;
2021-12-25 23:47:59 +00:00
object_t * objects = ( object_t * ) & gb - > oam ;
2021-12-26 00:38:54 +00:00
memset ( _object_buffer , 0 , sizeof ( _object_buffer ) ) ;
2021-12-25 23:47:59 +00:00
while ( gb - > n_visible_objs ) {
unsigned object_index = gb - > visible_objs [ gb - > n_visible_objs - 1 ] ;
unsigned priority = gb - > object_priority = = GB_OBJECT_PRIORITY_X ? 0 : object_index ;
const object_t * object = & objects [ object_index ] ;
gb - > n_visible_objs - - ;
2022-01-14 13:07:50 +00:00
uint16_t line_address = get_object_line_address ( gb , object - > y , object - > tile , object - > flags ) ;
2021-12-25 23:47:59 +00:00
uint8_t data0 = gb - > vram [ line_address ] ;
uint8_t data1 = gb - > vram [ line_address + 1 ] ;
2021-12-26 21:24:08 +00:00
if ( gb - > n_visible_objs = = 0 ) {
gb - > data_for_sel_glitch = data1 ;
}
2021-12-25 23:47:59 +00:00
if ( object - > flags & 0x20 ) {
data0 = flip ( data0 ) ;
data1 = flip ( data1 ) ;
}
2021-12-26 00:38:54 +00:00
typeof ( _object_buffer [ 0 ] ) * p = _object_buffer + object - > x ;
2021-12-25 23:47:59 +00:00
if ( object - > x > = 168 ) {
continue ;
}
unrolled for ( unsigned x = 0 ; x < 8 ; x + + ) {
unsigned pixel = ( data0 > > 7 ) | ( ( data1 > > 7 ) < < 1 ) ;
data0 < < = 1 ;
data1 < < = 1 ;
if ( pixel & & ( ! p - > pixel | | priority < p - > priority ) ) {
p - > pixel = pixel ;
p - > priority = priority ;
if ( gb - > cgb_mode ) {
p - > palette = object - > flags & 0x7 ;
}
else {
p - > palette = ( object - > flags & 0x10 ) > > 4 ;
}
p - > bg_priority = object - > flags & 0x80 ;
}
p + + ;
}
}
}
2021-12-26 00:38:54 +00:00
else {
object_buffer = ( const void * ) empty_object_buffer ;
}
2021-12-25 23:47:59 +00:00
uint32_t * restrict p = gb - > screen ;
typeof ( object_buffer [ 0 ] ) * object_buffer_pointer = object_buffer + 8 ;
if ( gb - > border_mode = = GB_BORDER_ALWAYS ) {
p + = ( BORDERED_WIDTH - ( WIDTH ) ) / 2 + BORDERED_WIDTH * ( BORDERED_HEIGHT - LINES ) / 2 ;
p + = BORDERED_WIDTH * gb - > current_line ;
}
else {
p + = WIDTH * gb - > current_line ;
}
if ( unlikely ( gb - > background_disabled ) | | ( ! gb - > cgb_mode & & ! ( gb - > io_registers [ GB_IO_LCDC ] & 1 ) ) ) {
uint32_t bg = gb - > background_palettes_rgb [ gb - > cgb_mode ? 0 : ( gb - > io_registers [ GB_IO_BGP ] & 3 ) ] ;
for ( unsigned i = 160 ; i - - ; ) {
if ( unlikely ( object_buffer_pointer - > pixel ) ) {
uint8_t pixel = object_buffer_pointer - > pixel ;
if ( ! gb - > cgb_mode ) {
pixel = ( ( gb - > io_registers [ GB_IO_OBP0 + object_buffer_pointer - > palette ] > > ( pixel < < 1 ) ) & 3 ) ;
}
* ( p + + ) = gb - > object_palettes_rgb [ pixel + ( object_buffer_pointer - > palette & 7 ) * 4 ] ;
}
else {
* ( p + + ) = bg ;
}
object_buffer_pointer + + ;
}
return ;
}
unsigned pixels = 0 ;
uint8_t tile_x = gb - > io_registers [ GB_IO_SCX ] / 8 ;
unsigned fractional_scroll = gb - > io_registers [ GB_IO_SCX ] & 7 ;
uint16_t map = 0x1800 ;
if ( gb - > io_registers [ GB_IO_LCDC ] & 0x08 ) {
map = 0x1C00 ;
}
uint8_t y = gb - > current_line + gb - > io_registers [ GB_IO_SCY ] ;
uint8_t attributes ;
uint8_t data0 , data1 ;
get_tile_data ( gb , tile_x , y , map , & attributes , & data0 , & data1 ) ;
# define DO_PIXEL() \
uint8_t pixel = ( data0 > > 7 ) | ( ( data1 > > 7 ) < < 1 ) ; \
data0 < < = 1 ; \
data1 < < = 1 ; \
\
if ( unlikely ( object_buffer_pointer - > pixel ) & & ( pixel = = 0 | | ! ( object_buffer_pointer - > bg_priority | | ( attributes & 0x80 ) ) | | ! ( gb - > io_registers [ GB_IO_LCDC ] & 1 ) ) ) { \
pixel = object_buffer_pointer - > pixel ; \
if ( ! gb - > cgb_mode ) { \
pixel = ( ( gb - > io_registers [ GB_IO_OBP0 + object_buffer_pointer - > palette ] > > ( pixel < < 1 ) ) & 3 ) ; \
} \
* ( p + + ) = gb - > object_palettes_rgb [ pixel + ( object_buffer_pointer - > palette & 7 ) * 4 ] ; \
} \
else { \
if ( ! gb - > cgb_mode ) { \
pixel = ( ( gb - > io_registers [ GB_IO_BGP ] > > ( pixel < < 1 ) ) & 3 ) ; \
} \
* ( p + + ) = gb - > background_palettes_rgb [ pixel + ( attributes & 7 ) * 4 ] ; \
} \
pixels + + ; \
object_buffer_pointer + + \
// First 1-8 pixels
data0 < < = fractional_scroll ;
data1 < < = fractional_scroll ;
bool check_window = gb - > wy_triggered & & ( gb - > io_registers [ GB_IO_LCDC ] & 0x20 ) ;
for ( unsigned i = fractional_scroll ; i < 8 ; i + + ) {
if ( check_window & & gb - > io_registers [ GB_IO_WX ] = = pixels + 7 ) {
activate_window :
check_window = false ;
map = gb - > io_registers [ GB_IO_LCDC ] & 0x40 ? 0x1C00 : 0x1800 ;
tile_x = - 1 ;
y = + + gb - > window_y ;
break ;
}
DO_PIXEL ( ) ;
}
tile_x + + ;
while ( pixels < 160 - 8 ) {
get_tile_data ( gb , tile_x , y , map , & attributes , & data0 , & data1 ) ;
for ( unsigned i = 0 ; i < 8 ; i + + ) {
if ( check_window & & gb - > io_registers [ GB_IO_WX ] = = pixels + 7 ) {
goto activate_window ;
}
DO_PIXEL ( ) ;
}
tile_x + + ;
}
gb - > fetcher_state = ( 160 - pixels ) & 7 ;
get_tile_data ( gb , tile_x , y , map , & attributes , & data0 , & data1 ) ;
while ( pixels < 160 ) {
if ( check_window & & gb - > io_registers [ GB_IO_WX ] = = pixels + 7 ) {
goto activate_window ;
}
DO_PIXEL ( ) ;
}
tile_x + + ;
get_tile_data ( gb , tile_x , y , map , & attributes , gb - > current_tile_data , gb - > current_tile_data + 1 ) ;
# undef DO_PIXEL
}
static void render_line_sgb ( GB_gameboy_t * gb )
{
if ( gb - > current_line > 144 ) return ; // Corrupt save state
struct {
unsigned pixel : 2 ; // Color, 0-3
unsigned palette : 1 ; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG)
bool bg_priority : 1 ; // BG priority bit
2021-12-26 00:38:54 +00:00
} _object_buffer [ 160 + 16 ] ; // allocate extra to avoid per pixel checks
static const uint8_t empty_object_buffer [ sizeof ( _object_buffer ) ] ;
const typeof ( _object_buffer [ 0 ] ) * object_buffer ;
2021-12-25 23:47:59 +00:00
if ( gb - > n_visible_objs & & ! gb - > objects_disabled & & ( gb - > io_registers [ GB_IO_LCDC ] & 2 ) ) {
2021-12-26 00:38:54 +00:00
object_buffer = & _object_buffer [ 0 ] ;
2021-12-25 23:47:59 +00:00
object_t * objects = ( object_t * ) & gb - > oam ;
2021-12-26 00:38:54 +00:00
memset ( _object_buffer , 0 , sizeof ( _object_buffer ) ) ;
2021-12-25 23:47:59 +00:00
while ( gb - > n_visible_objs ) {
const object_t * object = & objects [ gb - > visible_objs [ gb - > n_visible_objs - 1 ] ] ;
gb - > n_visible_objs - - ;
2022-01-14 13:07:50 +00:00
uint16_t line_address = get_object_line_address ( gb , object - > y , object - > tile , object - > flags ) ;
2021-12-25 23:47:59 +00:00
uint8_t data0 = gb - > vram [ line_address ] ;
uint8_t data1 = gb - > vram [ line_address + 1 ] ;
if ( object - > flags & 0x20 ) {
data0 = flip ( data0 ) ;
data1 = flip ( data1 ) ;
}
2021-12-26 00:38:54 +00:00
typeof ( _object_buffer [ 0 ] ) * p = _object_buffer + object - > x ;
2021-12-25 23:47:59 +00:00
if ( object - > x > = 168 ) {
continue ;
}
unrolled for ( unsigned x = 0 ; x < 8 ; x + + ) {
unsigned pixel = ( data0 > > 7 ) | ( ( data1 > > 7 ) < < 1 ) ;
data0 < < = 1 ;
data1 < < = 1 ;
if ( ! p - > pixel ) {
p - > pixel = pixel ;
p - > palette = ( object - > flags & 0x10 ) > > 4 ;
p - > bg_priority = object - > flags & 0x80 ;
}
p + + ;
}
}
}
2021-12-26 00:38:54 +00:00
else {
object_buffer = ( const void * ) empty_object_buffer ;
}
2021-12-25 23:47:59 +00:00
uint8_t * restrict p = gb - > sgb - > screen_buffer ;
typeof ( object_buffer [ 0 ] ) * object_buffer_pointer = object_buffer + 8 ;
p + = WIDTH * gb - > current_line ;
if ( unlikely ( gb - > background_disabled ) | | ( ! gb - > cgb_mode & & ! ( gb - > io_registers [ GB_IO_LCDC ] & 1 ) ) ) {
for ( unsigned i = 160 ; i - - ; ) {
if ( unlikely ( object_buffer_pointer - > pixel ) ) {
uint8_t pixel = object_buffer_pointer - > pixel ;
pixel = ( ( gb - > io_registers [ GB_IO_OBP0 + object_buffer_pointer - > palette ] > > ( pixel < < 1 ) ) & 3 ) ;
* ( p + + ) = pixel ;
}
else {
* ( p + + ) = gb - > io_registers [ GB_IO_BGP ] & 3 ;
}
object_buffer_pointer + + ;
}
return ;
}
unsigned pixels = 0 ;
uint8_t tile_x = gb - > io_registers [ GB_IO_SCX ] / 8 ;
unsigned fractional_scroll = gb - > io_registers [ GB_IO_SCX ] & 7 ;
uint16_t map = 0x1800 ;
if ( gb - > io_registers [ GB_IO_LCDC ] & 0x08 ) {
map = 0x1C00 ;
}
uint8_t y = gb - > current_line + gb - > io_registers [ GB_IO_SCY ] ;
uint8_t attributes ;
uint8_t data0 , data1 ;
get_tile_data ( gb , tile_x , y , map , & attributes , & data0 , & data1 ) ;
# define DO_PIXEL() \
uint8_t pixel = ( data0 > > 7 ) | ( ( data1 > > 7 ) < < 1 ) ; \
data0 < < = 1 ; \
data1 < < = 1 ; \
\
if ( unlikely ( object_buffer_pointer - > pixel ) & & ( pixel = = 0 | | ! object_buffer_pointer - > bg_priority | | ! ( gb - > io_registers [ GB_IO_LCDC ] & 1 ) ) ) { \
pixel = object_buffer_pointer - > pixel ; \
pixel = ( ( gb - > io_registers [ GB_IO_OBP0 + object_buffer_pointer - > palette ] > > ( pixel < < 1 ) ) & 3 ) ; \
* ( p + + ) = pixel ; \
} \
else { \
pixel = ( ( gb - > io_registers [ GB_IO_BGP ] > > ( pixel < < 1 ) ) & 3 ) ; \
* ( p + + ) = pixel ; \
} \
pixels + + ; \
object_buffer_pointer + + \
// First 1-8 pixels
data0 < < = fractional_scroll ;
data1 < < = fractional_scroll ;
bool check_window = gb - > wy_triggered & & ( gb - > io_registers [ GB_IO_LCDC ] & 0x20 ) ;
for ( unsigned i = fractional_scroll ; i < 8 ; i + + ) {
if ( check_window & & gb - > io_registers [ GB_IO_WX ] = = pixels + 7 ) {
activate_window :
check_window = false ;
map = gb - > io_registers [ GB_IO_LCDC ] & 0x40 ? 0x1C00 : 0x1800 ;
tile_x = - 1 ;
y = + + gb - > window_y ;
break ;
}
DO_PIXEL ( ) ;
}
tile_x + + ;
while ( pixels < 160 - 8 ) {
get_tile_data ( gb , tile_x , y , map , & attributes , & data0 , & data1 ) ;
for ( unsigned i = 0 ; i < 8 ; i + + ) {
if ( check_window & & gb - > io_registers [ GB_IO_WX ] = = pixels + 7 ) {
goto activate_window ;
}
DO_PIXEL ( ) ;
}
tile_x + + ;
}
get_tile_data ( gb , tile_x , y , map , & attributes , & data0 , & data1 ) ;
while ( pixels < 160 ) {
if ( check_window & & gb - > io_registers [ GB_IO_WX ] = = pixels + 7 ) {
goto activate_window ;
}
DO_PIXEL ( ) ;
}
}
static inline uint16_t mode3_batching_length ( GB_gameboy_t * gb )
{
if ( gb - > model & GB_MODEL_NO_SFC_BIT ) return 0 ;
if ( gb - > hdma_on ) return 0 ;
2022-02-03 20:34:14 +00:00
if ( gb - > stopped ) return 0 ;
2022-01-11 22:26:18 +00:00
if ( GB_is_dma_active ( gb ) ) return 0 ;
2021-12-25 23:47:59 +00:00
if ( gb - > wy_triggered & & ( gb - > io_registers [ GB_IO_LCDC ] & 0x20 ) & & ( gb - > io_registers [ GB_IO_WX ] < 8 | | gb - > io_registers [ GB_IO_WX ] = = 166 ) ) {
return 0 ;
}
// No objects or window, timing is trivial
if ( gb - > n_visible_objs = = 0 & & ! ( gb - > wy_triggered & & ( gb - > io_registers [ GB_IO_LCDC ] & 0x20 ) ) ) return 167 + ( gb - > io_registers [ GB_IO_SCX ] & 7 ) ;
if ( gb - > hdma_on_hblank ) return 0 ;
// 300 is a bit more than the maximum Mode 3 length
// No HBlank interrupt
if ( ! ( gb - > io_registers [ GB_IO_STAT ] & 0x8 ) ) return 300 ;
// No STAT interrupt requested
if ( ! ( gb - > interrupt_enable & 2 ) ) return 300 ;
return 0 ;
}
2018-06-02 01:00:10 +00:00
/*
TODO : It seems that the STAT register ' s mode bits are always " late " by 4 T - cycles .
The PPU logic can be greatly simplified if that delay is simply emulated .
*/
2021-12-25 23:47:59 +00:00
void GB_display_run ( GB_gameboy_t * gb , unsigned cycles , bool force )
2018-02-24 22:48:45 +00:00
{
2021-12-04 13:04:46 +00:00
gb - > cycles_since_vblank_callback + = cycles / 2 ;
2019-06-07 10:53:50 +00:00
/* The PPU does not advance while in STOP mode on the DMG */
if ( gb - > stopped & & ! GB_is_cgb ( gb ) ) {
2021-12-04 13:04:46 +00:00
if ( gb - > cycles_since_vblank_callback > = LCDC_PERIOD ) {
GB_display_vblank ( gb ) ;
2019-06-07 10:53:50 +00:00
}
return ;
}
2018-02-24 22:48:45 +00:00
2021-12-25 23:47:59 +00:00
GB_BATCHABLE_STATE_MACHINE ( gb , display , cycles , 2 , ! force ) {
2018-02-24 22:48:45 +00:00
GB_STATE ( gb , display , 1 ) ;
GB_STATE ( gb , display , 2 ) ;
2021-12-25 23:47:59 +00:00
GB_STATE ( gb , display , 3 ) ;
GB_STATE ( gb , display , 4 ) ;
GB_STATE ( gb , display , 5 ) ;
2018-02-24 22:48:45 +00:00
GB_STATE ( gb , display , 6 ) ;
GB_STATE ( gb , display , 7 ) ;
GB_STATE ( gb , display , 8 ) ;
2018-03-23 16:07:14 +00:00
// GB_STATE(gb, display, 9);
2018-02-24 22:48:45 +00:00
GB_STATE ( gb , display , 10 ) ;
GB_STATE ( gb , display , 11 ) ;
GB_STATE ( gb , display , 12 ) ;
GB_STATE ( gb , display , 13 ) ;
GB_STATE ( gb , display , 14 ) ;
GB_STATE ( gb , display , 15 ) ;
GB_STATE ( gb , display , 16 ) ;
GB_STATE ( gb , display , 17 ) ;
2021-12-14 18:27:38 +00:00
GB_STATE ( gb , display , 19 ) ;
2018-03-03 15:22:23 +00:00
GB_STATE ( gb , display , 20 ) ;
2018-03-04 20:21:56 +00:00
GB_STATE ( gb , display , 21 ) ;
GB_STATE ( gb , display , 22 ) ;
2018-03-05 19:17:37 +00:00
GB_STATE ( gb , display , 23 ) ;
2021-06-18 23:14:16 +00:00
GB_STATE ( gb , display , 24 ) ;
2018-04-02 22:43:24 +00:00
GB_STATE ( gb , display , 26 ) ;
2018-04-07 00:00:26 +00:00
GB_STATE ( gb , display , 27 ) ;
GB_STATE ( gb , display , 28 ) ;
2018-05-11 09:38:55 +00:00
GB_STATE ( gb , display , 29 ) ;
2018-07-03 19:25:09 +00:00
GB_STATE ( gb , display , 30 ) ;
2020-12-23 21:49:57 +00:00
GB_STATE ( gb , display , 31 ) ;
2019-01-19 17:32:26 +00:00
GB_STATE ( gb , display , 32 ) ;
GB_STATE ( gb , display , 33 ) ;
GB_STATE ( gb , display , 34 ) ;
GB_STATE ( gb , display , 35 ) ;
GB_STATE ( gb , display , 36 ) ;
2019-02-16 02:19:16 +00:00
GB_STATE ( gb , display , 37 ) ;
GB_STATE ( gb , display , 38 ) ;
2020-02-21 14:16:02 +00:00
GB_STATE ( gb , display , 39 ) ;
2020-02-21 14:43:51 +00:00
GB_STATE ( gb , display , 40 ) ;
2020-02-21 19:44:44 +00:00
GB_STATE ( gb , display , 41 ) ;
2020-02-27 20:49:34 +00:00
GB_STATE ( gb , display , 42 ) ;
2018-02-24 22:48:45 +00:00
}
if ( ! ( gb - > io_registers [ GB_IO_LCDC ] & 0x80 ) ) {
while ( true ) {
2021-12-04 13:04:46 +00:00
if ( gb - > cycles_since_vblank_callback < LCDC_PERIOD ) {
GB_SLEEP ( gb , display , 1 , LCDC_PERIOD - gb - > cycles_since_vblank_callback ) ;
}
GB_display_vblank ( gb ) ;
2020-05-29 22:25:21 +00:00
gb - > cgb_repeated_a_frame = true ;
2017-06-17 19:17:58 +00:00
}
2018-02-24 22:48:45 +00:00
return ;
}
2018-03-05 19:17:37 +00:00
2020-03-26 18:54:18 +00:00
gb - > is_odd_frame = false ;
2018-06-16 10:59:33 +00:00
if ( ! GB_is_cgb ( gb ) ) {
2018-03-23 09:58:51 +00:00
GB_SLEEP ( gb , display , 23 , 1 ) ;
}
2018-02-24 22:48:45 +00:00
2019-02-16 02:19:16 +00:00
/* Handle mode 2 on the very first line 0 */
2018-02-24 22:48:45 +00:00
gb - > current_line = 0 ;
2020-02-23 21:16:45 +00:00
gb - > window_y = - 1 ;
2020-12-23 21:49:57 +00:00
gb - > wy_triggered = false ;
2020-02-23 21:16:45 +00:00
2018-02-24 22:48:45 +00:00
gb - > ly_for_comparison = 0 ;
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
2018-06-03 23:07:38 +00:00
gb - > mode_for_interrupt = - 1 ;
2018-02-24 22:48:45 +00:00
gb - > oam_read_blocked = false ;
gb - > vram_read_blocked = false ;
gb - > oam_write_blocked = false ;
gb - > vram_write_blocked = false ;
2019-01-19 17:32:26 +00:00
gb - > cgb_palettes_blocked = false ;
gb - > cycles_for_line = MODE2_LENGTH - 4 ;
GB_STAT_update ( gb ) ;
GB_SLEEP ( gb , display , 2 , MODE2_LENGTH - 4 ) ;
gb - > oam_write_blocked = true ;
gb - > cycles_for_line + = 2 ;
2018-02-24 22:48:45 +00:00
GB_STAT_update ( gb ) ;
2019-01-19 17:32:26 +00:00
GB_SLEEP ( gb , display , 34 , 2 ) ;
2018-02-24 22:48:45 +00:00
2019-02-16 02:19:16 +00:00
gb - > n_visible_objs = 0 ;
gb - > cycles_for_line + = 8 ; // Mode 0 is shorter on the first line 0, so we augment cycles_for_line by 8 extra cycles.
2018-02-24 22:48:45 +00:00
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
gb - > io_registers [ GB_IO_STAT ] | = 3 ;
2018-06-03 23:07:38 +00:00
gb - > mode_for_interrupt = 3 ;
2019-02-16 02:19:16 +00:00
gb - > oam_write_blocked = true ;
2018-02-24 22:48:45 +00:00
gb - > oam_read_blocked = true ;
2019-02-16 02:19:16 +00:00
gb - > vram_read_blocked = gb - > cgb_double_speed ;
gb - > vram_write_blocked = gb - > cgb_double_speed ;
if ( ! GB_is_cgb ( gb ) ) {
gb - > vram_read_blocked = true ;
gb - > vram_write_blocked = true ;
2018-03-09 21:31:49 +00:00
}
2019-02-16 02:19:16 +00:00
gb - > cycles_for_line + = 2 ;
GB_SLEEP ( gb , display , 37 , 2 ) ;
2018-03-09 21:31:49 +00:00
2019-02-16 02:19:16 +00:00
gb - > cgb_palettes_blocked = true ;
2020-05-29 13:30:40 +00:00
gb - > cycles_for_line + = ( GB_is_cgb ( gb ) & & gb - > model < = GB_MODEL_CGB_C ) ? 2 : 3 ;
GB_SLEEP ( gb , display , 38 , ( GB_is_cgb ( gb ) & & gb - > model < = GB_MODEL_CGB_C ) ? 2 : 3 ) ;
2019-01-19 17:32:26 +00:00
2019-02-16 02:19:16 +00:00
gb - > vram_read_blocked = true ;
gb - > vram_write_blocked = true ;
2020-02-23 21:48:08 +00:00
gb - > wx_triggered = false ;
2020-03-01 21:58:28 +00:00
gb - > wx166_glitch = false ;
2019-02-16 02:19:16 +00:00
goto mode_3_start ;
2018-02-24 22:48:45 +00:00
while ( true ) {
/* Lines 0 - 143 */
2020-02-23 21:16:45 +00:00
gb - > window_y = - 1 ;
2018-02-24 22:48:45 +00:00
for ( ; gb - > current_line < LINES ; gb - > current_line + + ) {
2021-12-10 17:49:52 +00:00
if ( unlikely ( gb - > lcd_line_callback ) ) {
gb - > lcd_line_callback ( gb , gb - > current_line ) ;
}
2020-02-23 21:16:45 +00:00
2019-01-19 17:32:26 +00:00
gb - > oam_write_blocked = GB_is_cgb ( gb ) & & ! gb - > cgb_double_speed ;
2018-03-27 12:46:00 +00:00
gb - > accessed_oam_row = 0 ;
2019-01-19 17:32:26 +00:00
GB_SLEEP ( gb , display , 35 , 2 ) ;
gb - > oam_write_blocked = GB_is_cgb ( gb ) ;
GB_SLEEP ( gb , display , 6 , 1 ) ;
2018-02-24 22:48:45 +00:00
gb - > io_registers [ GB_IO_LY ] = gb - > current_line ;
2017-06-18 18:27:07 +00:00
gb - > oam_read_blocked = true ;
2018-05-11 09:38:55 +00:00
gb - > ly_for_comparison = gb - > current_line ? - 1 : 0 ;
2018-02-28 22:12:04 +00:00
2018-03-08 20:11:10 +00:00
/* The OAM STAT interrupt occurs 1 T-cycle before STAT actually changes, except on line 0.
2018-06-08 14:16:15 +00:00
PPU glitch ? */
2018-06-02 21:36:05 +00:00
if ( gb - > current_line ! = 0 ) {
2018-06-03 23:07:38 +00:00
gb - > mode_for_interrupt = 2 ;
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
2018-02-28 22:12:04 +00:00
}
2018-06-16 10:59:33 +00:00
else if ( ! GB_is_cgb ( gb ) ) {
2018-03-17 18:34:55 +00:00
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
}
2018-06-03 23:07:38 +00:00
GB_STAT_update ( gb ) ;
2018-03-08 20:11:10 +00:00
2018-03-03 13:47:36 +00:00
GB_SLEEP ( gb , display , 7 , 1 ) ;
2018-02-24 22:48:45 +00:00
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
gb - > io_registers [ GB_IO_STAT ] | = 2 ;
2018-06-03 23:07:38 +00:00
gb - > mode_for_interrupt = 2 ;
2017-06-18 18:27:07 +00:00
gb - > oam_write_blocked = true ;
2018-02-24 22:48:45 +00:00
gb - > ly_for_comparison = gb - > current_line ;
2018-06-03 23:07:38 +00:00
GB_STAT_update ( gb ) ;
gb - > mode_for_interrupt = - 1 ;
2018-02-24 22:48:45 +00:00
GB_STAT_update ( gb ) ;
2018-03-23 16:07:14 +00:00
gb - > n_visible_objs = 0 ;
2018-02-24 22:48:45 +00:00
2022-01-11 22:26:18 +00:00
if ( ! GB_is_dma_active ( gb ) & & ! gb - > oam_ppu_blocked ) {
2021-12-25 23:47:59 +00:00
GB_BATCHPOINT ( gb , display , 5 , 80 ) ;
}
2018-03-23 16:07:14 +00:00
for ( gb - > oam_search_index = 0 ; gb - > oam_search_index < 40 ; gb - > oam_search_index + + ) {
2018-06-16 10:59:33 +00:00
if ( GB_is_cgb ( gb ) ) {
2018-03-27 12:46:00 +00:00
add_object_from_index ( gb , gb - > oam_search_index ) ;
2019-06-07 10:53:50 +00:00
/* The CGB does not care about the accessed OAM row as there's no OAM bug */
2018-03-27 12:46:00 +00:00
}
2018-03-23 16:07:14 +00:00
GB_SLEEP ( gb , display , 8 , 2 ) ;
2018-06-16 10:59:33 +00:00
if ( ! GB_is_cgb ( gb ) ) {
2018-03-27 12:46:00 +00:00
add_object_from_index ( gb , gb - > oam_search_index ) ;
gb - > accessed_oam_row = ( gb - > oam_search_index & ~ 1 ) * 4 + 8 ;
}
2018-03-23 16:07:14 +00:00
if ( gb - > oam_search_index = = 37 ) {
2018-06-16 10:59:33 +00:00
gb - > vram_read_blocked = ! GB_is_cgb ( gb ) ;
2018-03-23 16:07:14 +00:00
gb - > vram_write_blocked = false ;
2019-01-19 17:32:26 +00:00
gb - > cgb_palettes_blocked = false ;
2018-06-16 10:59:33 +00:00
gb - > oam_write_blocked = GB_is_cgb ( gb ) ;
2018-03-23 16:07:14 +00:00
}
}
2019-02-16 02:19:16 +00:00
gb - > cycles_for_line = MODE2_LENGTH + 4 ;
2018-03-27 12:46:00 +00:00
gb - > accessed_oam_row = - 1 ;
2017-02-19 00:22:50 +00:00
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
2018-02-24 22:48:45 +00:00
gb - > io_registers [ GB_IO_STAT ] | = 3 ;
2018-06-03 23:07:38 +00:00
gb - > mode_for_interrupt = 3 ;
2018-03-10 22:17:57 +00:00
gb - > vram_read_blocked = true ;
2018-02-24 22:48:45 +00:00
gb - > vram_write_blocked = true ;
2019-01-19 17:32:26 +00:00
gb - > cgb_palettes_blocked = false ;
2018-02-24 22:48:45 +00:00
gb - > oam_write_blocked = true ;
2019-02-16 02:19:16 +00:00
gb - > oam_read_blocked = true ;
2018-02-24 22:48:45 +00:00
GB_STAT_update ( gb ) ;
2018-04-01 18:45:56 +00:00
2019-02-16 02:19:16 +00:00
uint8_t idle_cycles = 3 ;
if ( GB_is_cgb ( gb ) & & gb - > model < = GB_MODEL_CGB_C ) {
idle_cycles = 2 ;
}
gb - > cycles_for_line + = idle_cycles ;
GB_SLEEP ( gb , display , 10 , idle_cycles ) ;
gb - > cgb_palettes_blocked = true ;
gb - > cycles_for_line + = 2 ;
GB_SLEEP ( gb , display , 32 , 2 ) ;
mode_3_start :
2020-12-24 18:50:47 +00:00
/* TODO: Timing seems incorrect, might need an access conflict handling. */
2020-12-23 21:49:57 +00:00
if ( ( gb - > io_registers [ GB_IO_LCDC ] & 0x20 ) & &
2020-12-24 18:50:47 +00:00
gb - > io_registers [ GB_IO_WY ] = = gb - > current_line ) {
2020-12-23 21:49:57 +00:00
gb - > wy_triggered = true ;
}
2019-02-16 02:19:16 +00:00
2018-03-03 13:47:36 +00:00
fifo_clear ( & gb - > bg_fifo ) ;
2018-03-04 21:27:31 +00:00
fifo_clear ( & gb - > oam_fifo ) ;
2018-04-06 15:26:04 +00:00
/* Fill the FIFO with 8 pixels of "junk", it's going to be dropped anyway. */
fifo_push_bg_row ( & gb - > bg_fifo , 0 , 0 , 0 , false , false ) ;
2018-03-30 14:06:27 +00:00
/* Todo: find out actual access time of SCX */
2018-03-03 13:47:36 +00:00
gb - > position_in_line = - ( gb - > io_registers [ GB_IO_SCX ] & 7 ) - 8 ;
2020-03-06 12:41:13 +00:00
gb - > lcd_x = 0 ;
2019-07-19 17:19:09 +00:00
2021-12-17 19:16:23 +00:00
gb - > extra_penalty_for_object_at_0 = MIN ( ( gb - > io_registers [ GB_IO_SCX ] & 7 ) , 5 ) ;
2019-01-19 17:32:26 +00:00
2018-03-03 13:47:36 +00:00
/* The actual rendering cycle */
2018-04-06 16:29:49 +00:00
gb - > fetcher_state = 0 ;
2021-12-25 23:47:59 +00:00
if ( ( gb - > mode3_batching_length = mode3_batching_length ( gb ) ) ) {
GB_BATCHPOINT ( gb , display , 3 , gb - > mode3_batching_length ) ;
if ( GB_BATCHED_CYCLES ( gb , display ) > = gb - > mode3_batching_length ) {
// Successfully batched!
gb - > lcd_x = gb - > position_in_line = 160 ;
gb - > cycles_for_line + = gb - > mode3_batching_length ;
if ( gb - > sgb ) {
render_line_sgb ( gb ) ;
}
else {
render_line ( gb ) ;
}
GB_SLEEP ( gb , display , 4 , gb - > mode3_batching_length ) ;
goto skip_slow_mode_3 ;
}
}
2018-03-03 13:47:36 +00:00
while ( true ) {
2020-02-27 20:49:34 +00:00
/* Handle window */
2020-03-03 00:21:19 +00:00
/* TODO: It appears that WX checks if the window begins *next* pixel, not *this* pixel. For this reason,
2020-03-01 21:58:28 +00:00
WX = 167 has no effect at all ( It checks if the PPU X position is 161 , which never happens ) and WX = 166
has weird artifacts ( It appears to activate the window during HBlank , as PPU X is temporarily 160 at
that point . The code should be updated to represent this , and this will fix the time travel hack in
WX ' s access conflict code . */
2020-02-27 20:49:34 +00:00
if ( ! gb - > wx_triggered & & gb - > wy_triggered & & ( gb - > io_registers [ GB_IO_LCDC ] & 0x20 ) ) {
2020-03-01 21:58:28 +00:00
bool should_activate_window = false ;
if ( gb - > io_registers [ GB_IO_WX ] = = 0 ) {
static const uint8_t scx_to_wx0_comparisons [ ] = { - 7 , - 9 , - 10 , - 11 , - 12 , - 13 , - 14 , - 14 } ;
2020-12-23 21:49:57 +00:00
if ( gb - > position_in_line = = scx_to_wx0_comparisons [ gb - > io_registers [ GB_IO_SCX ] & 7 ] ) {
2020-03-01 21:58:28 +00:00
should_activate_window = true ;
}
}
else if ( gb - > wx166_glitch ) {
static const uint8_t scx_to_wx166_comparisons [ ] = { - 8 , - 9 , - 10 , - 11 , - 12 , - 13 , - 14 , - 15 } ;
if ( gb - > position_in_line = = scx_to_wx166_comparisons [ gb - > io_registers [ GB_IO_SCX ] & 7 ] ) {
should_activate_window = true ;
}
}
else if ( gb - > io_registers [ GB_IO_WX ] < 166 + GB_is_cgb ( gb ) ) {
2020-03-06 12:41:13 +00:00
if ( gb - > io_registers [ GB_IO_WX ] = = ( uint8_t ) ( gb - > position_in_line + 7 ) ) {
2020-03-04 21:34:36 +00:00
should_activate_window = true ;
}
2020-03-06 12:41:13 +00:00
else if ( gb - > io_registers [ GB_IO_WX ] = = ( uint8_t ) ( gb - > position_in_line + 6 ) & & ! gb - > wx_just_changed ) {
should_activate_window = true ;
/* LCD-PPU horizontal desync! It only appears to happen on DMGs, but not all of them.
This doesn ' t seem to be CPU revision dependent , but most revisions */
if ( ( gb - > model & GB_MODEL_FAMILY_MASK ) = = GB_MODEL_DMG_FAMILY & & ! GB_is_sgb ( gb ) ) {
if ( gb - > lcd_x > 0 ) {
gb - > lcd_x - - ;
}
}
}
2020-02-27 20:49:34 +00:00
}
2020-03-01 21:58:28 +00:00
if ( should_activate_window ) {
2020-02-27 20:49:34 +00:00
gb - > window_y + + ;
2020-03-01 21:58:28 +00:00
/* TODO: Verify fetcher access timings in this case */
if ( gb - > io_registers [ GB_IO_WX ] = = 0 & & ( gb - > io_registers [ GB_IO_SCX ] & 7 ) ) {
gb - > cycles_for_line + + ;
GB_SLEEP ( gb , display , 42 , 1 ) ;
2020-02-27 20:49:34 +00:00
}
2020-03-01 21:58:28 +00:00
gb - > wx_triggered = true ;
gb - > window_tile_x = 0 ;
fifo_clear ( & gb - > bg_fifo ) ;
gb - > fetcher_state = 0 ;
gb - > window_is_being_fetched = true ;
}
else if ( ! GB_is_cgb ( gb ) & & gb - > io_registers [ GB_IO_WX ] = = 166 & & gb - > io_registers [ GB_IO_WX ] = = ( uint8_t ) ( gb - > position_in_line + 7 ) ) {
gb - > window_y + + ;
2020-02-27 20:49:34 +00:00
}
}
2020-02-29 15:06:08 +00:00
2020-02-29 22:23:50 +00:00
/* TODO: What happens when WX=0? */
2020-02-29 15:06:08 +00:00
if ( ! GB_is_cgb ( gb ) & & gb - > wx_triggered & & ! gb - > window_is_being_fetched & &
gb - > fetcher_state = = 0 & & gb - > io_registers [ GB_IO_WX ] = = ( uint8_t ) ( gb - > position_in_line + 7 ) ) {
// Insert a pixel right at the FIFO's end
gb - > bg_fifo . read_end - - ;
gb - > bg_fifo . read_end & = GB_FIFO_LENGTH - 1 ;
gb - > bg_fifo . fifo [ gb - > bg_fifo . read_end ] = ( GB_fifo_item_t ) { 0 , } ;
gb - > window_is_being_fetched = false ;
}
2020-02-27 20:49:34 +00:00
2018-03-04 20:21:56 +00:00
/* Handle objects */
2021-12-17 19:16:23 +00:00
/* When the object enabled bit is off, this proccess is skipped entirely on the DMG, but not on the CGB.
2018-03-23 17:01:27 +00:00
On the CGB , this bit is checked only when the pixel is actually popped from the FIFO . */
2018-04-04 21:51:37 +00:00
while ( gb - > n_visible_objs ! = 0 & &
2018-04-06 08:37:49 +00:00
( gb - > position_in_line < 160 | | gb - > position_in_line > = ( uint8_t ) ( - 8 ) ) & &
2022-01-14 13:07:50 +00:00
gb - > objects_x [ gb - > n_visible_objs - 1 ] < ( uint8_t ) ( gb - > position_in_line + 8 ) ) {
2018-04-04 21:51:37 +00:00
gb - > n_visible_objs - - ;
}
2020-02-21 14:16:02 +00:00
gb - > during_object_fetch = true ;
2018-03-04 20:21:56 +00:00
while ( gb - > n_visible_objs ! = 0 & &
2018-06-16 10:59:33 +00:00
( gb - > io_registers [ GB_IO_LCDC ] & 2 | | GB_is_cgb ( gb ) ) & &
2022-01-14 13:07:50 +00:00
gb - > objects_x [ gb - > n_visible_objs - 1 ] = = ( uint8_t ) ( gb - > position_in_line + 8 ) ) {
2018-03-04 20:21:56 +00:00
2020-02-29 22:17:45 +00:00
while ( gb - > fetcher_state < 5 | | fifo_size ( & gb - > bg_fifo ) = = 0 ) {
2022-01-16 11:50:59 +00:00
advance_fetcher_state_machine ( gb , & cycles ) ;
2018-04-07 00:00:26 +00:00
gb - > cycles_for_line + + ;
GB_SLEEP ( gb , display , 27 , 1 ) ;
2020-02-21 14:16:02 +00:00
if ( gb - > object_fetch_aborted ) {
2020-02-21 13:14:33 +00:00
goto abort_fetching_object ;
}
2018-04-07 00:00:26 +00:00
}
2018-04-07 00:26:10 +00:00
/* Todo: Measure if penalty occurs before or after waiting for the fetcher. */
2021-12-17 19:16:23 +00:00
if ( gb - > extra_penalty_for_object_at_0 ! = 0 ) {
2022-01-14 13:07:50 +00:00
if ( gb - > objects_x [ gb - > n_visible_objs - 1 ] = = 0 ) {
2021-12-17 19:16:23 +00:00
gb - > cycles_for_line + = gb - > extra_penalty_for_object_at_0 ;
GB_SLEEP ( gb , display , 28 , gb - > extra_penalty_for_object_at_0 ) ;
gb - > extra_penalty_for_object_at_0 = 0 ;
2020-02-21 13:14:33 +00:00
if ( gb - > object_fetch_aborted ) {
goto abort_fetching_object ;
}
2018-03-30 14:06:27 +00:00
}
2018-03-04 20:21:56 +00:00
}
2020-02-23 22:20:58 +00:00
/* TODO: Can this be deleted? { */
2022-01-16 11:50:59 +00:00
advance_fetcher_state_machine ( gb , & cycles ) ;
2020-02-21 19:44:44 +00:00
gb - > cycles_for_line + + ;
GB_SLEEP ( gb , display , 41 , 1 ) ;
if ( gb - > object_fetch_aborted ) {
goto abort_fetching_object ;
}
2020-02-23 22:20:58 +00:00
/* } */
2018-04-07 10:59:36 +00:00
2022-01-16 11:50:59 +00:00
advance_fetcher_state_machine ( gb , & cycles ) ;
dma_sync ( gb , & cycles ) ;
2022-01-14 13:07:50 +00:00
gb - > object_low_line_address = get_object_line_address ( gb ,
gb - > objects_y [ gb - > n_visible_objs - 1 ] ,
2022-02-06 11:02:15 +00:00
gb - > mode2_y_bus = oam_read ( gb , gb - > visible_objs [ gb - > n_visible_objs - 1 ] * 4 + 2 ) ,
2022-01-14 13:07:50 +00:00
gb - > object_flags = oam_read ( gb , gb - > visible_objs [ gb - > n_visible_objs - 1 ] * 4 + 3 )
) ;
2018-03-04 21:27:31 +00:00
2022-01-16 11:50:59 +00:00
gb - > cycles_for_line + = 2 ;
GB_SLEEP ( gb , display , 20 , 2 ) ;
2020-02-21 14:43:51 +00:00
if ( gb - > object_fetch_aborted ) {
goto abort_fetching_object ;
2018-03-04 21:27:31 +00:00
}
2022-01-16 11:50:59 +00:00
/* TODO: timing not verified */
dma_sync ( gb , & cycles ) ;
gb - > object_tile_data [ 0 ] = vram_read ( gb , gb - > object_low_line_address ) ;
gb - > cycles_for_line + = 2 ;
GB_SLEEP ( gb , display , 39 , 2 ) ;
if ( gb - > object_fetch_aborted ) {
goto abort_fetching_object ;
}
/* TODO: timing not verified */
dma_sync ( gb , & cycles ) ;
gb - > object_tile_data [ 1 ] = vram_read ( gb , gb - > object_low_line_address + 1 ) ;
2020-02-21 14:43:51 +00:00
gb - > during_object_fetch = false ;
gb - > cycles_for_line + + ;
GB_SLEEP ( gb , display , 40 , 1 ) ;
2018-03-04 21:27:31 +00:00
2022-01-14 13:07:50 +00:00
uint8_t palette = ( gb - > object_flags & 0x10 ) ? 1 : 0 ;
2018-03-04 21:27:31 +00:00
if ( gb - > cgb_mode ) {
2022-01-14 13:07:50 +00:00
palette = gb - > object_flags & 0x7 ;
2018-03-04 21:27:31 +00:00
}
fifo_overlay_object_row ( & gb - > oam_fifo ,
2022-01-16 11:50:59 +00:00
gb - > object_tile_data [ 0 ] ,
gb - > object_tile_data [ 1 ] ,
2018-03-04 21:27:31 +00:00
palette ,
2022-01-14 13:07:50 +00:00
gb - > object_flags & 0x80 ,
2020-02-15 13:32:06 +00:00
gb - > object_priority = = GB_OBJECT_PRIORITY_INDEX ? gb - > visible_objs [ gb - > n_visible_objs - 1 ] : 0 ,
2022-01-14 13:07:50 +00:00
gb - > object_flags & 0x20 ) ;
2020-11-20 14:24:16 +00:00
2022-01-14 13:07:50 +00:00
gb - > data_for_sel_glitch = gb - > vram_ppu_blocked ? 0xFF : gb - > vram [ gb - > object_low_line_address + 1 ] ;
2018-03-04 20:21:56 +00:00
gb - > n_visible_objs - - ;
}
2020-02-21 13:14:33 +00:00
abort_fetching_object :
2020-02-21 14:16:02 +00:00
gb - > object_fetch_aborted = false ;
gb - > during_object_fetch = false ;
2018-04-06 16:29:49 +00:00
2018-03-03 15:22:23 +00:00
render_pixel_if_possible ( gb ) ;
2022-01-16 11:50:59 +00:00
advance_fetcher_state_machine ( gb , & cycles ) ;
2018-04-07 10:02:53 +00:00
2018-03-03 15:22:23 +00:00
if ( gb - > position_in_line = = 160 ) break ;
gb - > cycles_for_line + + ;
2018-03-04 20:21:56 +00:00
GB_SLEEP ( gb , display , 21 , 1 ) ;
}
2021-12-25 23:47:59 +00:00
skip_slow_mode_3 :
2018-07-03 19:25:09 +00:00
2021-12-26 17:57:18 +00:00
/* TODO: This seems incorrect (glitches Tesserae), verify further */
/*
2020-11-20 14:24:16 +00:00
if ( gb - > fetcher_state = = 4 | | gb - > fetcher_state = = 5 ) {
gb - > data_for_sel_glitch = gb - > current_tile_data [ 0 ] ;
}
else {
gb - > data_for_sel_glitch = gb - > current_tile_data [ 1 ] ;
}
2021-12-26 17:57:18 +00:00
*/
2020-03-06 12:41:13 +00:00
while ( gb - > lcd_x ! = 160 & & ! gb - > disable_rendering & & gb - > screen & & ! gb - > sgb ) {
/* Oh no! The PPU and LCD desynced! Fill the rest of the line whith white. */
uint32_t * dest = NULL ;
if ( gb - > border_mode ! = GB_BORDER_ALWAYS ) {
dest = gb - > screen + gb - > lcd_x + gb - > current_line * WIDTH ;
}
else {
dest = gb - > screen + gb - > lcd_x + gb - > current_line * BORDERED_WIDTH + ( BORDERED_WIDTH - WIDTH ) / 2 + ( BORDERED_HEIGHT - LINES ) / 2 * BORDERED_WIDTH ;
}
* dest = gb - > background_palettes_rgb [ 0 ] ;
gb - > lcd_x + + ;
}
2020-03-01 21:58:28 +00:00
/* TODO: Verify timing */
if ( ! GB_is_cgb ( gb ) & & gb - > wy_triggered & & ( gb - > io_registers [ GB_IO_LCDC ] & 0x20 ) & & gb - > io_registers [ GB_IO_WX ] = = 166 ) {
gb - > wx166_glitch = true ;
}
else {
gb - > wx166_glitch = false ;
}
gb - > wx_triggered = false ;
2018-07-03 19:25:09 +00:00
if ( GB_is_cgb ( gb ) & & gb - > model < = GB_MODEL_CGB_C ) {
gb - > cycles_for_line + + ;
GB_SLEEP ( gb , display , 30 , 1 ) ;
}
2018-03-23 09:58:51 +00:00
if ( ! gb - > cgb_double_speed ) {
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
2018-06-03 23:07:38 +00:00
gb - > mode_for_interrupt = 0 ;
2018-03-23 09:58:51 +00:00
gb - > oam_read_blocked = false ;
gb - > vram_read_blocked = false ;
gb - > oam_write_blocked = false ;
gb - > vram_write_blocked = false ;
}
gb - > cycles_for_line + + ;
GB_SLEEP ( gb , display , 22 , 1 ) ;
2018-03-04 20:21:56 +00:00
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
2018-06-03 23:07:38 +00:00
gb - > mode_for_interrupt = 0 ;
2018-03-04 20:21:56 +00:00
gb - > oam_read_blocked = false ;
gb - > vram_read_blocked = false ;
gb - > oam_write_blocked = false ;
gb - > vram_write_blocked = false ;
2018-02-24 22:48:45 +00:00
GB_STAT_update ( gb ) ;
2018-03-19 21:49:53 +00:00
/* Todo: Measure this value */
2019-01-19 17:32:26 +00:00
gb - > cycles_for_line + = 2 ;
GB_SLEEP ( gb , display , 33 , 2 ) ;
gb - > cgb_palettes_blocked = ! gb - > cgb_double_speed ;
2022-02-02 21:01:38 +00:00
if ( gb - > hdma_on_hblank & & ! gb - > halted & & ! gb - > stopped ) {
2022-01-30 18:09:58 +00:00
gb - > hdma_on = true ;
}
2019-01-19 17:32:26 +00:00
gb - > cycles_for_line + = 2 ;
GB_SLEEP ( gb , display , 36 , 2 ) ;
gb - > cgb_palettes_blocked = false ;
2018-03-19 21:49:53 +00:00
2020-12-23 21:49:57 +00:00
GB_SLEEP ( gb , display , 11 , LINE_LENGTH - gb - > cycles_for_line - 2 ) ;
/*
TODO : Verify double speed timing
2020-12-24 18:50:47 +00:00
TODO : Timing differs on a DMG
2020-12-23 21:49:57 +00:00
*/
if ( ( gb - > io_registers [ GB_IO_LCDC ] & 0x20 ) & &
2020-12-24 18:50:47 +00:00
( gb - > io_registers [ GB_IO_WY ] = = gb - > current_line ) ) {
2020-12-23 21:49:57 +00:00
gb - > wy_triggered = true ;
}
GB_SLEEP ( gb , display , 31 , 2 ) ;
2021-06-18 23:14:16 +00:00
if ( gb - > current_line ! = LINES - 1 ) {
gb - > mode_for_interrupt = 2 ;
}
2019-07-19 17:19:09 +00:00
2019-07-19 17:37:58 +00:00
// Todo: unverified timing
gb - > current_lcd_line + + ;
if ( gb - > current_lcd_line = = LINES & & GB_is_sgb ( gb ) ) {
2021-12-04 13:04:46 +00:00
GB_display_vblank ( gb ) ;
2019-07-19 17:37:58 +00:00
}
2019-07-19 17:19:09 +00:00
if ( gb - > icd_hreset_callback ) {
gb - > icd_hreset_callback ( gb ) ;
}
2018-02-24 22:48:45 +00:00
}
2020-03-01 21:58:28 +00:00
gb - > wx166_glitch = false ;
2018-02-24 22:48:45 +00:00
/* Lines 144 - 152 */
for ( ; gb - > current_line < VIRTUAL_LINES - 1 ; gb - > current_line + + ) {
2018-05-11 09:38:55 +00:00
gb - > ly_for_comparison = - 1 ;
2021-12-10 17:49:52 +00:00
if ( unlikely ( gb - > lcd_line_callback ) ) {
gb - > lcd_line_callback ( gb , gb - > current_line ) ;
}
2021-12-14 18:27:38 +00:00
GB_STAT_update ( gb ) ;
2018-04-02 22:43:24 +00:00
GB_SLEEP ( gb , display , 26 , 2 ) ;
2021-12-14 18:27:38 +00:00
gb - > io_registers [ GB_IO_LY ] = gb - > current_line ;
2021-06-17 22:20:05 +00:00
if ( gb - > current_line = = LINES & & ! gb - > stat_interrupt_line & & ( gb - > io_registers [ GB_IO_STAT ] & 0x20 ) ) {
gb - > io_registers [ GB_IO_IF ] | = 2 ;
2018-03-10 13:52:22 +00:00
}
2018-04-02 22:43:24 +00:00
GB_SLEEP ( gb , display , 12 , 2 ) ;
2018-02-24 22:48:45 +00:00
gb - > ly_for_comparison = gb - > current_line ;
2021-06-18 23:14:16 +00:00
GB_STAT_update ( gb ) ;
GB_SLEEP ( gb , display , 24 , 1 ) ;
2018-02-24 22:48:45 +00:00
if ( gb - > current_line = = LINES ) {
2018-04-02 22:43:24 +00:00
/* Entering VBlank state triggers the OAM interrupt */
2017-02-19 00:22:50 +00:00
gb - > io_registers [ GB_IO_STAT ] & = ~ 3 ;
2018-02-24 22:48:45 +00:00
gb - > io_registers [ GB_IO_STAT ] | = 1 ;
gb - > io_registers [ GB_IO_IF ] | = 1 ;
2021-06-17 22:20:05 +00:00
if ( ! gb - > stat_interrupt_line & & ( gb - > io_registers [ GB_IO_STAT ] & 0x20 ) ) {
gb - > io_registers [ GB_IO_IF ] | = 2 ;
}
2018-06-03 23:07:38 +00:00
gb - > mode_for_interrupt = 1 ;
2018-04-02 22:43:24 +00:00
GB_STAT_update ( gb ) ;
2018-02-24 22:48:45 +00:00
if ( gb - > frame_skip_state = = GB_FRAMESKIP_LCD_TURNED_ON ) {
2020-01-30 23:29:59 +00:00
if ( GB_is_cgb ( gb ) ) {
2021-12-04 13:04:46 +00:00
GB_display_vblank ( gb ) ;
2020-01-30 23:29:59 +00:00
gb - > frame_skip_state = GB_FRAMESKIP_FIRST_FRAME_SKIPPED ;
}
else {
if ( ! GB_is_sgb ( gb ) | | gb - > current_lcd_line < LINES ) {
2020-03-26 18:54:18 +00:00
gb - > is_odd_frame ^ = true ;
2021-12-04 13:04:46 +00:00
GB_display_vblank ( gb ) ;
2020-01-30 23:29:59 +00:00
}
gb - > frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED ;
2019-06-18 20:16:28 +00:00
}
2017-02-19 00:22:50 +00:00
}
2018-02-24 22:48:45 +00:00
else {
2019-06-18 20:16:28 +00:00
if ( ! GB_is_sgb ( gb ) | | gb - > current_lcd_line < LINES ) {
2020-03-26 18:54:18 +00:00
gb - > is_odd_frame ^ = true ;
2021-12-04 13:04:46 +00:00
GB_display_vblank ( gb ) ;
2019-06-21 13:58:56 +00:00
}
2020-05-29 22:25:21 +00:00
if ( gb - > frame_skip_state = = GB_FRAMESKIP_FIRST_FRAME_SKIPPED ) {
gb - > cgb_repeated_a_frame = true ;
gb - > frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED ;
}
else {
gb - > cgb_repeated_a_frame = false ;
}
2017-02-19 00:22:50 +00:00
}
}
2021-06-18 23:14:16 +00:00
GB_SLEEP ( gb , display , 13 , LINE_LENGTH - 5 ) ;
2017-02-19 00:22:50 +00:00
}
2018-04-24 21:08:06 +00:00
/* TODO: Verified on SGB2 and CGB-E. Actual interrupt timings not tested. */
2018-02-24 22:48:45 +00:00
/* Lines 153 */
2018-05-11 09:38:55 +00:00
gb - > ly_for_comparison = - 1 ;
2018-02-24 22:48:45 +00:00
GB_STAT_update ( gb ) ;
2021-12-14 18:27:38 +00:00
GB_SLEEP ( gb , display , 19 , 2 ) ;
gb - > io_registers [ GB_IO_LY ] = 153 ;
GB_SLEEP ( gb , display , 14 , ( gb - > model > GB_MODEL_CGB_C ) ? 2 : 4 ) ;
2017-02-19 00:22:50 +00:00
2021-12-14 18:27:38 +00:00
if ( gb - > model < = GB_MODEL_CGB_C & & ! gb - > cgb_double_speed ) {
2018-05-11 09:38:55 +00:00
gb - > io_registers [ GB_IO_LY ] = 0 ;
}
gb - > ly_for_comparison = 153 ;
2018-02-24 22:48:45 +00:00
GB_STAT_update ( gb ) ;
2018-07-03 18:56:32 +00:00
GB_SLEEP ( gb , display , 15 , ( gb - > model > GB_MODEL_CGB_C ) ? 4 : 2 ) ;
2017-02-19 00:22:50 +00:00
2018-05-11 09:38:55 +00:00
gb - > io_registers [ GB_IO_LY ] = 0 ;
2018-07-03 18:56:32 +00:00
gb - > ly_for_comparison = ( gb - > model > GB_MODEL_CGB_C ) ? 153 : - 1 ;
2018-02-24 22:48:45 +00:00
GB_STAT_update ( gb ) ;
2018-03-03 13:47:36 +00:00
GB_SLEEP ( gb , display , 16 , 4 ) ;
2017-09-08 14:25:01 +00:00
2018-02-24 22:48:45 +00:00
gb - > ly_for_comparison = 0 ;
GB_STAT_update ( gb ) ;
2018-05-11 09:38:55 +00:00
GB_SLEEP ( gb , display , 29 , 12 ) ; /* Writing to LYC during this period on a CGB has side effects */
GB_SLEEP ( gb , display , 17 , LINE_LENGTH - 24 ) ;
2017-09-08 14:25:01 +00:00
2018-02-24 22:48:45 +00:00
gb - > current_line = 0 ;
2020-12-23 21:49:57 +00:00
gb - > wy_triggered = false ;
2020-02-23 21:16:45 +00:00
2019-07-15 20:02:58 +00:00
// TODO: not the correct timing
2019-07-19 17:19:09 +00:00
gb - > current_lcd_line = 0 ;
2019-07-15 20:02:58 +00:00
if ( gb - > icd_vreset_callback ) {
gb - > icd_vreset_callback ( gb ) ;
}
2016-03-30 20:07:55 +00:00
}
}
2016-10-26 21:14:02 +00:00
void GB_draw_tileset ( GB_gameboy_t * gb , uint32_t * dest , GB_palette_type_t palette_type , uint8_t palette_index )
{
uint32_t none_palette [ 4 ] ;
uint32_t * palette = NULL ;
2018-06-16 10:59:33 +00:00
switch ( GB_is_cgb ( gb ) ? palette_type : GB_PALETTE_NONE ) {
2016-10-26 21:14:02 +00:00
default :
case GB_PALETTE_NONE :
none_palette [ 0 ] = gb - > rgb_encode_callback ( gb , 0xFF , 0xFF , 0xFF ) ;
none_palette [ 1 ] = gb - > rgb_encode_callback ( gb , 0xAA , 0xAA , 0xAA ) ;
none_palette [ 2 ] = gb - > rgb_encode_callback ( gb , 0x55 , 0x55 , 0x55 ) ;
none_palette [ 3 ] = gb - > rgb_encode_callback ( gb , 0 , 0 , 0 ) ;
palette = none_palette ;
break ;
case GB_PALETTE_BACKGROUND :
2017-04-19 20:26:39 +00:00
palette = gb - > background_palettes_rgb + ( 4 * ( palette_index & 7 ) ) ;
2016-10-26 21:14:02 +00:00
break ;
case GB_PALETTE_OAM :
2021-12-17 19:16:23 +00:00
palette = gb - > object_palettes_rgb + ( 4 * ( palette_index & 7 ) ) ;
2016-10-26 21:14:02 +00:00
break ;
}
for ( unsigned y = 0 ; y < 192 ; y + + ) {
for ( unsigned x = 0 ; x < 256 ; x + + ) {
2018-06-16 10:59:33 +00:00
if ( x > = 128 & & ! GB_is_cgb ( gb ) ) {
2017-04-19 20:26:39 +00:00
* ( dest + + ) = gb - > background_palettes_rgb [ 0 ] ;
2016-10-26 21:14:02 +00:00
continue ;
}
uint16_t tile = ( x % 128 ) / 8 + y / 8 * 16 ;
uint16_t tile_address = tile * 0x10 + ( x > = 128 ? 0x2000 : 0 ) ;
uint8_t pixel = ( ( ( gb - > vram [ tile_address + ( y & 7 ) * 2 ] > > ( ( ~ x ) & 7 ) ) & 1 ) |
( ( gb - > vram [ tile_address + ( y & 7 ) * 2 + 1 ] > > ( ( ~ x ) & 7 ) ) & 1 ) < < 1 ) ;
if ( ! gb - > cgb_mode ) {
if ( palette_type = = GB_PALETTE_BACKGROUND ) {
pixel = ( ( gb - > io_registers [ GB_IO_BGP ] > > ( pixel < < 1 ) ) & 3 ) ;
}
else if ( ! gb - > cgb_mode ) {
if ( palette_type = = GB_PALETTE_OAM ) {
pixel = ( ( gb - > io_registers [ palette_index = = 0 ? GB_IO_OBP0 : GB_IO_OBP1 ] > > ( pixel < < 1 ) ) & 3 ) ;
}
}
}
* ( dest + + ) = palette [ pixel ] ;
}
}
}
void GB_draw_tilemap ( GB_gameboy_t * gb , uint32_t * dest , GB_palette_type_t palette_type , uint8_t palette_index , GB_map_type_t map_type , GB_tileset_type_t tileset_type )
{
uint32_t none_palette [ 4 ] ;
uint32_t * palette = NULL ;
uint16_t map = 0x1800 ;
2018-06-16 10:59:33 +00:00
switch ( GB_is_cgb ( gb ) ? palette_type : GB_PALETTE_NONE ) {
2016-10-26 21:14:02 +00:00
case GB_PALETTE_NONE :
none_palette [ 0 ] = gb - > rgb_encode_callback ( gb , 0xFF , 0xFF , 0xFF ) ;
none_palette [ 1 ] = gb - > rgb_encode_callback ( gb , 0xAA , 0xAA , 0xAA ) ;
none_palette [ 2 ] = gb - > rgb_encode_callback ( gb , 0x55 , 0x55 , 0x55 ) ;
none_palette [ 3 ] = gb - > rgb_encode_callback ( gb , 0 , 0 , 0 ) ;
palette = none_palette ;
break ;
case GB_PALETTE_BACKGROUND :
2017-04-19 20:26:39 +00:00
palette = gb - > background_palettes_rgb + ( 4 * ( palette_index & 7 ) ) ;
2016-10-26 21:14:02 +00:00
break ;
case GB_PALETTE_OAM :
2021-12-17 19:16:23 +00:00
palette = gb - > object_palettes_rgb + ( 4 * ( palette_index & 7 ) ) ;
2016-10-26 21:14:02 +00:00
break ;
case GB_PALETTE_AUTO :
break ;
}
if ( map_type = = GB_MAP_9C00 | | ( map_type = = GB_MAP_AUTO & & gb - > io_registers [ GB_IO_LCDC ] & 0x08 ) ) {
2022-02-13 14:58:44 +00:00
map = 0x1C00 ;
2016-10-26 21:14:02 +00:00
}
if ( tileset_type = = GB_TILESET_AUTO ) {
tileset_type = ( gb - > io_registers [ GB_IO_LCDC ] & 0x10 ) ? GB_TILESET_8800 : GB_TILESET_8000 ;
}
for ( unsigned y = 0 ; y < 256 ; y + + ) {
for ( unsigned x = 0 ; x < 256 ; x + + ) {
uint8_t tile = gb - > vram [ map + x / 8 + y / 8 * 32 ] ;
uint16_t tile_address ;
uint8_t attributes = 0 ;
if ( tileset_type = = GB_TILESET_8800 ) {
tile_address = tile * 0x10 ;
}
else {
tile_address = ( int8_t ) tile * 0x10 + 0x1000 ;
}
if ( gb - > cgb_mode ) {
attributes = gb - > vram [ map + x / 8 + y / 8 * 32 + 0x2000 ] ;
}
if ( attributes & 0x8 ) {
tile_address + = 0x2000 ;
}
uint8_t pixel = ( ( ( gb - > vram [ tile_address + ( ( ( attributes & 0x40 ) ? ~ y : y ) & 7 ) * 2 ] > > ( ( ( attributes & 0x20 ) ? x : ~ x ) & 7 ) ) & 1 ) |
( ( gb - > vram [ tile_address + ( ( ( attributes & 0x40 ) ? ~ y : y ) & 7 ) * 2 + 1 ] > > ( ( ( attributes & 0x20 ) ? x : ~ x ) & 7 ) ) & 1 ) < < 1 ) ;
if ( ! gb - > cgb_mode & & ( palette_type = = GB_PALETTE_BACKGROUND | | palette_type = = GB_PALETTE_AUTO ) ) {
pixel = ( ( gb - > io_registers [ GB_IO_BGP ] > > ( pixel < < 1 ) ) & 3 ) ;
}
if ( palette ) {
* ( dest + + ) = palette [ pixel ] ;
}
else {
2017-04-19 20:26:39 +00:00
* ( dest + + ) = gb - > background_palettes_rgb [ ( attributes & 7 ) * 4 + pixel ] ;
2016-10-26 21:14:02 +00:00
}
}
}
}
2021-12-17 19:16:23 +00:00
uint8_t GB_get_oam_info ( GB_gameboy_t * gb , GB_oam_info_t * dest , uint8_t * object_height )
2016-10-26 21:14:02 +00:00
{
uint8_t count = 0 ;
2021-12-17 19:16:23 +00:00
* object_height = ( gb - > io_registers [ GB_IO_LCDC ] & 4 ) ? 16 : 8 ;
2016-10-26 21:14:02 +00:00
uint8_t oam_to_dest_index [ 40 ] = { 0 , } ;
2021-10-24 16:15:28 +00:00
for ( signed y = 0 ; y < LINES ; y + + ) {
2021-12-17 19:16:23 +00:00
object_t * object = ( object_t * ) & gb - > oam ;
uint8_t objects_in_line = 0 ;
2022-02-05 15:44:10 +00:00
bool obscured = false ;
2021-12-17 19:16:23 +00:00
for ( uint8_t i = 0 ; i < 40 ; i + + , object + + ) {
signed object_y = object - > y - 16 ;
// Is object not in this line?
if ( object_y > y | | object_y + * object_height < = y ) continue ;
if ( + + objects_in_line = = 11 ) obscured = true ;
2016-10-26 21:14:02 +00:00
GB_oam_info_t * info = NULL ;
if ( ! oam_to_dest_index [ i ] ) {
info = dest + count ;
oam_to_dest_index [ i ] = + + count ;
2021-12-17 19:16:23 +00:00
info - > x = object - > x ;
info - > y = object - > y ;
info - > tile = * object_height = = 16 ? object - > tile & 0xFE : object - > tile ;
info - > flags = object - > flags ;
2016-10-26 21:14:02 +00:00
info - > obscured_by_line_limit = false ;
2021-12-17 19:16:23 +00:00
info - > oam_addr = 0xFE00 + i * sizeof ( * object ) ;
2016-10-26 21:14:02 +00:00
}
else {
info = dest + oam_to_dest_index [ i ] - 1 ;
}
info - > obscured_by_line_limit | = obscured ;
}
}
for ( unsigned i = 0 ; i < count ; i + + ) {
uint16_t vram_address = dest [ i ] . tile * 0x10 ;
uint8_t flags = dest [ i ] . flags ;
uint8_t palette = gb - > cgb_mode ? ( flags & 7 ) : ( ( flags & 0x10 ) ? 1 : 0 ) ;
2018-06-16 10:59:33 +00:00
if ( GB_is_cgb ( gb ) & & ( flags & 0x8 ) ) {
2017-04-19 20:26:39 +00:00
vram_address + = 0x2000 ;
}
2016-10-26 21:14:02 +00:00
2021-12-17 19:16:23 +00:00
for ( unsigned y = 0 ; y < * object_height ; y + + ) {
2021-02-22 12:45:30 +00:00
unrolled for ( unsigned x = 0 ; x < 8 ; x + + ) {
2016-10-26 21:14:02 +00:00
uint8_t color = ( ( ( gb - > vram [ vram_address ] > > ( ( ~ x ) & 7 ) ) & 1 ) |
( ( gb - > vram [ vram_address + 1 ] > > ( ( ~ x ) & 7 ) ) & 1 ) < < 1 ) ;
if ( ! gb - > cgb_mode ) {
color = ( gb - > io_registers [ palette ? GB_IO_OBP1 : GB_IO_OBP0 ] > > ( color < < 1 ) ) & 3 ;
}
2021-12-17 19:16:23 +00:00
dest [ i ] . image [ ( ( flags & 0x20 ) ? 7 - x : x ) + ( ( flags & 0x40 ) ? * object_height - 1 - y : y ) * 8 ] = gb - > object_palettes_rgb [ palette * 4 + color ] ;
2016-10-26 21:14:02 +00:00
}
vram_address + = 2 ;
}
}
return count ;
2017-02-19 00:22:50 +00:00
}
2017-08-19 22:34:12 +00:00
2020-03-26 18:54:18 +00:00
bool GB_is_odd_frame ( GB_gameboy_t * gb )
2017-08-19 22:34:12 +00:00
{
2020-03-26 18:54:18 +00:00
return gb - > is_odd_frame ;
2017-08-19 22:34:12 +00:00
}
2021-12-17 19:12:26 +00:00
void GB_set_object_rendering_disabled ( GB_gameboy_t * gb , bool disabled )
{
gb - > objects_disabled = disabled ;
}
void GB_set_background_rendering_disabled ( GB_gameboy_t * gb , bool disabled )
{
gb - > background_disabled = disabled ;
}
bool GB_is_object_rendering_disabled ( GB_gameboy_t * gb )
{
return gb - > objects_disabled ;
}
bool GB_is_background_rendering_disabled ( GB_gameboy_t * gb )
{
return gb - > background_disabled ;
}