src/mess/video/gb.c
| r1126 | r1127 | |
| 83 | 83 | int previous_line; /* Previous line we've drawn in */ |
| 84 | 84 | int start_x; /* Pixel to start drawing from (inclusive) */ |
| 85 | 85 | int end_x; /* Pixel to end drawing (exclusive) */ |
| 86 | | int lcd_int_state; /* Keep track of LCD_INT state */ |
| 87 | 86 | int mode; /* Keep track of internal STAT mode */ |
| 87 | int lcd_irq_line; |
| 88 | int line_irq; |
| 88 | 89 | int mode_irq; |
| 90 | int delayed_line_irq; |
| 89 | 91 | struct layer_struct layer[2]; |
| 90 | 92 | mame_timer *lcd_timer; |
| 91 | | mame_timer *vblank_delay_timer; |
| 92 | 93 | } gb_lcd; |
| 93 | 94 | |
| 94 | 95 | void (*update_scanline)(void); |
| 95 | 96 | |
| 96 | 97 | /* Prototypes */ |
| 97 | 98 | static TIMER_CALLBACK(gb_lcd_timer_proc); |
| 98 | | static TIMER_CALLBACK(gb_vblank_delay_proc); |
| 99 | 99 | static void gb_lcd_switch_on( void ); |
| 100 | | static void gb_lcd_int_check( void ); |
| 101 | 100 | |
| 102 | 101 | static unsigned char palette[] = |
| 103 | 102 | { |
| r1126 | r1127 | |
| 1052 | 1051 | |
| 1053 | 1052 | gb_lcd.lcd_timer = mame_timer_alloc( gb_lcd_timer_proc ); |
| 1054 | 1053 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(456,0), 0, time_never ); |
| 1055 | | |
| 1056 | | gb_lcd.vblank_delay_timer = mame_timer_alloc( gb_vblank_delay_proc ); |
| 1057 | | mame_timer_adjust( gb_lcd.vblank_delay_timer, time_never, 0, time_never ); |
| 1058 | 1054 | } |
| 1059 | 1055 | |
| 1060 | 1056 | void sgb_video_init( void ) { |
| r1126 | r1127 | |
| 1142 | 1138 | } |
| 1143 | 1139 | } |
| 1144 | 1140 | |
| 1145 | | static TIMER_CALLBACK(gb_vblank_delay_proc) |
| 1146 | | { |
| 1147 | | /* Trigger VBlank interrupt */ |
| 1148 | | cpunum_set_input_line(0, VBL_INT, HOLD_LINE); |
| 1149 | | } |
| 1150 | | |
| 1151 | | /* Check whether an LCD_INT interrupt should be triggered */ |
| 1152 | | static void gb_lcd_int_check( void ) { |
| 1153 | | int lcd_int_state = gb_lcd.mode_irq; |
| 1154 | | |
| 1155 | | /* Check if LY==LYC should trigger an interrupt */ |
| 1156 | | if ( LCDSTAT & 0x40 && LCDSTAT & 0x04 ) { |
| 1157 | | lcd_int_state = 1; |
| 1158 | | } |
| 1159 | | |
| 1160 | | if ( gb_lcd.lcd_int_state == 0 && lcd_int_state == 1 ) { |
| 1161 | | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1162 | | } |
| 1163 | | |
| 1164 | | gb_lcd.lcd_int_state = lcd_int_state; |
| 1165 | | } |
| 1166 | | |
| 1167 | | static void gb_lcd_check_mode_irq( void ) { |
| 1168 | | gb_lcd.mode_irq = 0; |
| 1169 | | |
| 1170 | | /* Check if stat 0 should trigger an interrupt */ |
| 1171 | | if ( LCDSTAT & 0x08 && gb_lcd.mode == 0x00 ) { |
| 1172 | | gb_lcd.mode_irq = 1; |
| 1173 | | } |
| 1174 | | |
| 1175 | | /* Check if stat 2 should trigger an interrupt */ |
| 1176 | | if ( LCDSTAT & 0x20 && gb_lcd.mode == 0x02 ) { |
| 1177 | | gb_lcd.mode_irq = 1; |
| 1178 | | } |
| 1179 | | |
| 1180 | | /* Check if stat 1 should trigger an interrupt */ |
| 1181 | | if ( LCDSTAT & 0x10 && gb_lcd.mode == 0x01 ) { |
| 1182 | | gb_lcd.mode_irq = 1; |
| 1183 | | } |
| 1184 | | |
| 1185 | | gb_lcd_int_check(); |
| 1186 | | } |
| 1187 | | |
| 1188 | 1141 | enum { |
| 1189 | 1142 | GB_LCD_STATE_LYXX_M3=1, |
| 1190 | 1143 | GB_LCD_STATE_LYXX_M0, |
| 1144 | GB_LCD_STATE_LYXX_M0_2, |
| 1191 | 1145 | GB_LCD_STATE_LYXX_M0_INC, |
| 1192 | 1146 | GB_LCD_STATE_LY00_M2, |
| 1193 | 1147 | GB_LCD_STATE_LYXX_M2, |
| 1194 | 1148 | GB_LCD_STATE_LY9X_M1, |
| 1195 | 1149 | GB_LCD_STATE_LY9X_M1_INC, |
| 1196 | 1150 | GB_LCD_STATE_LY00_M1, |
| 1151 | GB_LCD_STATE_LY00_M1_1, |
| 1197 | 1152 | GB_LCD_STATE_LY00_M1_2, |
| 1198 | 1153 | GB_LCD_STATE_LY00_M0 |
| 1199 | 1154 | }; |
| r1126 | r1127 | |
| 1215 | 1170 | gb_lcd.mode = 0; |
| 1216 | 1171 | LCDSTAT &= 0xFC; |
| 1217 | 1172 | /* Generate lcd interrupt if requested */ |
| 1218 | | gb_lcd_check_mode_irq(); |
| 1173 | if ( ( LCDSTAT & 0x08 ) && ! gb_lcd.line_irq && gb_lcd.delayed_line_irq ) { |
| 1174 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1175 | } |
| 1219 | 1176 | /* Check for HBLANK DMA */ |
| 1220 | 1177 | if( gbc_hdma_enabled ) |
| 1221 | 1178 | gbc_hdma(0x10); |
| r1126 | r1127 | |
| 1223 | 1180 | break; |
| 1224 | 1181 | case GB_LCD_STATE_LYXX_M0_INC: /* Increment LY, stay in M0 for 4 more cycles */ |
| 1225 | 1182 | gb_increment_scanline(); |
| 1183 | gb_lcd.delayed_line_irq = gb_lcd.line_irq; |
| 1184 | gb_lcd.line_irq = ( ( CMPLINE == CURLINE ) && ( LCDSTAT & 0x40 ) ) ? 1 : 0; |
| 1185 | if ( ! gb_lcd.delayed_line_irq && gb_lcd.line_irq ) { |
| 1186 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1187 | } |
| 1226 | 1188 | /* Reset LY==LYC STAT bit */ |
| 1227 | 1189 | LCDSTAT &= 0xFB; |
| 1228 | 1190 | /* Check if we're going into VBlank next */ |
| r1126 | r1127 | |
| 1232 | 1194 | /* Internally switch to mode 2 */ |
| 1233 | 1195 | gb_lcd.mode = 2; |
| 1234 | 1196 | /* Generate lcd interrupt if requested */ |
| 1235 | | gb_lcd_check_mode_irq(); |
| 1197 | if ( ( ! gb_lcd.delayed_line_irq || ! ( LCDSTAT & 0x40 ) ) && ( LCDSTAT & 0x20 ) ) { |
| 1198 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1199 | } |
| 1236 | 1200 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(4,0), GB_LCD_STATE_LYXX_M2, time_never ); |
| 1237 | 1201 | } |
| 1238 | 1202 | break; |
| r1126 | r1127 | |
| 1241 | 1205 | gb_lcd.mode = 2; |
| 1242 | 1206 | LCDSTAT = ( LCDSTAT & 0xFC ) | 0x02; |
| 1243 | 1207 | /* Generate lcd interrupt if requested */ |
| 1244 | | gb_lcd_check_mode_irq(); |
| 1208 | if ( LCDSTAT & 0x20 ) { |
| 1209 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1210 | } |
| 1245 | 1211 | /* Mode 2 lasts approximately 80 clock cycles */ |
| 1246 | 1212 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(80,0), GB_LCD_STATE_LYXX_M3, time_never ); |
| 1247 | 1213 | break; |
| 1248 | 1214 | case GB_LCD_STATE_LYXX_M2: /* Switch to mode 2 */ |
| 1249 | 1215 | /* Update STAT register to the correct state */ |
| 1250 | 1216 | LCDSTAT = (LCDSTAT & 0xFC) | 0x02; |
| 1217 | /* Generate lcd interrupt if requested */ |
| 1218 | if ( gb_lcd.delayed_line_irq && gb_lcd.line_irq ) { |
| 1219 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1220 | } |
| 1251 | 1221 | /* Check if LY==LYC STAT bit should be set */ |
| 1252 | 1222 | if ( CURLINE == CMPLINE ) { |
| 1253 | 1223 | LCDSTAT |= 0x04; |
| 1254 | | /* Generate lcd interrupt if requested */ |
| 1255 | | gb_lcd_int_check(); |
| 1256 | 1224 | } |
| 1257 | 1225 | /* Mode 2 last for approximately 80 clock cycles */ |
| 1258 | 1226 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(80,0), GB_LCD_STATE_LYXX_M3, time_never ); |
| r1126 | r1127 | |
| 1262 | 1230 | /* Set Mode 3 lcdstate */ |
| 1263 | 1231 | gb_lcd.mode = 3; |
| 1264 | 1232 | LCDSTAT = (LCDSTAT & 0xFC) | 0x03; |
| 1265 | | gb_lcd_check_mode_irq(); |
| 1266 | 1233 | /* Mode 3 lasts for approximately 172+#sprites*10 clock cycles */ |
| 1267 | 1234 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(172 + 10 * gb_lcd.sprCount,0), GB_LCD_STATE_LYXX_M0, time_never ); |
| 1268 | 1235 | gb_lcd.start_x = 0; |
| 1269 | 1236 | break; |
| 1270 | 1237 | case GB_LCD_STATE_LY9X_M1: /* Switch to or stay in mode 1 */ |
| 1271 | 1238 | if ( CURLINE == 144 ) { |
| 1272 | | /* Trigger VBlank interrupt generation */ |
| 1273 | | mame_timer_adjust( gb_lcd.vblank_delay_timer, MAME_TIME_IN_CYCLES(1,0), 0, time_never ); |
| 1239 | /* Trigger VBlank interrupt */ |
| 1240 | cpunum_set_input_line( 0, VBL_INT, HOLD_LINE ); |
| 1274 | 1241 | /* Set VBlank lcdstate */ |
| 1275 | 1242 | gb_lcd.mode = 1; |
| 1276 | 1243 | LCDSTAT = (LCDSTAT & 0xFC) | 0x01; |
| 1277 | 1244 | /* Trigger LCD interrupt if requested */ |
| 1278 | | gb_lcd_check_mode_irq(); |
| 1245 | if ( LCDSTAT & 0x10 ) { |
| 1246 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1247 | } |
| 1279 | 1248 | } |
| 1280 | 1249 | /* Check if LY==LYC STAT bit should be set */ |
| 1281 | 1250 | if ( CURLINE == CMPLINE ) { |
| 1282 | 1251 | LCDSTAT |= 0x04; |
| 1283 | | /* Trigger LCD interrupt if requested */ |
| 1284 | | gb_lcd_int_check(); |
| 1285 | 1252 | } |
| 1286 | 1253 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(452,0), GB_LCD_STATE_LY9X_M1_INC, time_never ); |
| 1287 | 1254 | break; |
| 1288 | 1255 | case GB_LCD_STATE_LY9X_M1_INC: /* Increment scanline counter */ |
| 1289 | 1256 | gb_increment_scanline(); |
| 1257 | gb_lcd.delayed_line_irq = gb_lcd.line_irq; |
| 1258 | gb_lcd.line_irq = ( ( CMPLINE == CURLINE ) && ( LCDSTAT & 0x40 ) ) ? 1 : 0; |
| 1259 | if ( ! gb_lcd.delayed_line_irq && gb_lcd.line_irq ) { |
| 1260 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1261 | } |
| 1290 | 1262 | /* Reset LY==LYC STAT bit */ |
| 1291 | 1263 | LCDSTAT &= 0xFB; |
| 1292 | 1264 | if ( gb_lcd.current_line == 153 ) { |
| r1126 | r1127 | |
| 1297 | 1269 | break; |
| 1298 | 1270 | case GB_LCD_STATE_LY00_M1: /* we stay in VBlank but current line counter should already be incremented */ |
| 1299 | 1271 | /* Check LY=LYC for line #153 */ |
| 1272 | if ( gb_lcd.delayed_line_irq ) { |
| 1273 | if ( gb_lcd.line_irq ) { |
| 1274 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1275 | } |
| 1276 | } else { |
| 1277 | gb_lcd.delayed_line_irq = gb_lcd.line_irq; |
| 1278 | } |
| 1300 | 1279 | if ( CURLINE == CMPLINE ) { |
| 1301 | 1280 | LCDSTAT |= 0x04; |
| 1302 | | gb_lcd_int_check(); |
| 1303 | 1281 | } |
| 1304 | 1282 | gb_increment_scanline(); |
| 1283 | gb_lcd.line_irq = ( ( CMPLINE == CURLINE ) && ( LCDSTAT & 0x40 ) ) ? 1 : 0; |
| 1284 | // if ( ! gb_lcd.delayed_line_irq && gb_lcd.line_irq ) { |
| 1285 | // cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1286 | // } |
| 1305 | 1287 | LCDSTAT &= 0xFB; |
| 1306 | | /* This 8 cycle period still needs to be verified on hardware */ |
| 1307 | | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(8,0), GB_LCD_STATE_LY00_M1_2, time_never ); |
| 1288 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(4/*8*/,0), GB_LCD_STATE_LY00_M1_1, time_never ); |
| 1308 | 1289 | break; |
| 1290 | case GB_LCD_STATE_LY00_M1_1: |
| 1291 | if ( ! gb_lcd.delayed_line_irq && gb_lcd.line_irq ) { |
| 1292 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1293 | } |
| 1294 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(4,0), GB_LCD_STATE_LY00_M1_2, time_never ); |
| 1295 | break; |
| 1309 | 1296 | case GB_LCD_STATE_LY00_M1_2: /* Rest of line #0 during VBlank */ |
| 1297 | if ( gb_lcd.delayed_line_irq && gb_lcd.line_irq ) { |
| 1298 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1299 | } |
| 1310 | 1300 | if ( CURLINE == CMPLINE ) { |
| 1311 | 1301 | LCDSTAT |= 0x04; |
| 1312 | | gb_lcd_int_check(); |
| 1313 | 1302 | } |
| 1314 | 1303 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(444,0), GB_LCD_STATE_LY00_M0, time_never ); |
| 1315 | 1304 | break; |
| r1126 | r1127 | |
| 1317 | 1306 | /* Set Mode 0 lcdstat */ |
| 1318 | 1307 | gb_lcd.mode = 0; |
| 1319 | 1308 | LCDSTAT = ( LCDSTAT & 0xFC ); |
| 1320 | | gb_lcd_check_mode_irq(); |
| 1321 | 1309 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(4,0), GB_LCD_STATE_LY00_M2, time_never ); |
| 1322 | 1310 | break; |
| 1323 | 1311 | } |
| r1126 | r1127 | |
| 1334 | 1322 | gb_lcd.current_line = 0; |
| 1335 | 1323 | gb_lcd.previous_line = 153; |
| 1336 | 1324 | gb_lcd.window_lines_drawn = 0; |
| 1337 | | gb_lcd.lcd_int_state = 0; |
| 1338 | | gb_lcd.mode_irq = 0; |
| 1325 | gb_lcd.line_irq = 0; |
| 1326 | gb_lcd.delayed_line_irq = 0; |
| 1339 | 1327 | gb_lcd.mode = 0; |
| 1340 | 1328 | /* Check for LY=LYC coincidence */ |
| 1341 | 1329 | if ( CURLINE == CMPLINE ) { |
| 1342 | 1330 | LCDSTAT |= 0x04; |
| 1343 | 1331 | /* Generate lcd interrupt if requested */ |
| 1344 | | gb_lcd_int_check(); |
| 1332 | if ( LCDSTAT & 0x40 ) { |
| 1333 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1334 | } |
| 1345 | 1335 | } |
| 1346 | 1336 | mame_timer_adjust( gb_lcd.lcd_timer, MAME_TIME_IN_CYCLES(80,0), GB_LCD_STATE_LYXX_M3, time_never ); |
| 1347 | 1337 | } |
| r1126 | r1127 | |
| 1402 | 1392 | interrupt to be triggered. |
| 1403 | 1393 | */ |
| 1404 | 1394 | if ( LCDCONT & 0x80 ) { |
| 1395 | /* Are these triggers correct? */ |
| 1405 | 1396 | if ( ( gb_lcd.mode != 2 ) || ( ! ( data & 0x40 ) && ! ( LCDSTAT & 0x40 ) ) ) { |
| 1406 | 1397 | cpunum_set_input_line(0, LCD_INT, HOLD_LINE); |
| 1407 | 1398 | } |
| 1399 | if ( ! ( LCDSTAT & 0x40 ) && ( data & 0x40 ) ) { |
| 1400 | if ( LCDSTAT & 0x04 ) { |
| 1401 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1402 | } |
| 1403 | } |
| 1408 | 1404 | } |
| 1409 | 1405 | break; |
| 1410 | 1406 | case 0x04: /* LY - LCD Y-coordinate */ |
| 1411 | 1407 | return; |
| 1412 | 1408 | case 0x05: /* LYC */ |
| 1413 | | if ( CURLINE == data ) { |
| 1414 | | LCDSTAT |= 0x04; |
| 1415 | | /* Generate lcd interrupt if requested */ |
| 1416 | | gb_lcd_int_check(); |
| 1417 | | } else { |
| 1418 | | LCDSTAT &= 0xFB; |
| 1409 | if ( CMPLINE != data ) { |
| 1410 | if ( CURLINE == data ) { |
| 1411 | LCDSTAT |= 0x04; |
| 1412 | /* Generate lcd interrupt if requested */ |
| 1413 | if ( LCDSTAT & 0x40 ) { |
| 1414 | cpunum_set_input_line( 0, LCD_INT, HOLD_LINE ); |
| 1415 | } |
| 1416 | } else { |
| 1417 | LCDSTAT &= 0xFB; |
| 1418 | } |
| 1419 | 1419 | } |
| 1420 | | /* Generate lcd interrupt if requested */ |
| 1421 | | gb_lcd_int_check(); |
| 1422 | 1420 | break; |
| 1423 | 1421 | case 0x06: /* DMA - DMA Transfer and Start Address */ |
| 1424 | 1422 | { |