pavone@467: /* pavone@467: Copyright 2013 Michael Pavone pavone@470: This file is part of BlastEm. pavone@467: BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. pavone@467: */ pavone@20: #include "vdp.h" pavone@75: #include "blastem.h" pavone@20: #include pavone@20: #include pavone@426: #include "render.h" pavone@20: pavone@622: #define NTSC_INACTIVE_START 224 pavone@622: #define PAL_INACTIVE_START 240 pavone@20: #define BUF_BIT_PRIORITY 0x40 pavone@20: #define MAP_BIT_PRIORITY 0x8000 pavone@20: #define MAP_BIT_H_FLIP 0x800 pavone@20: #define MAP_BIT_V_FLIP 0x1000 pavone@20: pavone@39: #define SCROLL_BUFFER_SIZE 32 pavone@436: #define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1) pavone@436: #define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2) pavone@39: pavone@328: #define MCLKS_SLOT_H40 16 pavone@328: #define MCLKS_SLOT_H32 20 pavone@622: #define VINT_SLOT_H40 4 //21 slots before HSYNC, 16 during, 10 after pavone@711: #define VINT_SLOT_H32 4 //old value was 23, but recent tests suggest the actual value is close to the H40 one pavone@922: #define HSYNC_SLOT_H40 228 pavone@697: #define HSYNC_END_H40 (HSYNC_SLOT_H40+17) pavone@331: #define HSYNC_END_H32 (33 * MCLKS_SLOT_H32) pavone@647: #define HBLANK_START_H40 178 //should be 179 according to Nemesis, but 178 seems to fit slightly better with my test ROM results pavone@647: #define HBLANK_END_H40 0 //should be 5.5 according to Nemesis, but 0 seems to fit better with my test ROM results pavone@647: #define HBLANK_START_H32 233 //should be 147 according to Nemesis which is very different from my test ROM result pavone@647: #define HBLANK_END_H32 0 //should be 5 according to Nemesis, but 0 seems to fit better with my test ROM results pavone@647: #define LINE_CHANGE_H40 165 pavone@647: #define LINE_CHANGE_H32 132 pavone@717: #define VBLANK_START_H40 (LINE_CHANGE_H40+2) pavone@717: #define VBLANK_START_H32 (LINE_CHANGE_H32+2) pavone@460: #define FIFO_LATENCY 3 pavone@328: pavone@426: int32_t color_map[1 << 12]; pavone@426: uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255}; pavone@426: pavone@437: uint8_t debug_base[][3] = { pavone@437: {127, 127, 127}, //BG pavone@437: {0, 0, 127}, //A pavone@437: {127, 0, 0}, //Window pavone@437: {0, 127, 0}, //B pavone@437: {127, 0, 127} //Sprites pavone@437: }; pavone@437: pavone@426: uint8_t color_map_init_done; pavone@426: pavone@623: void init_vdp_context(vdp_context * context, uint8_t region_pal) pavone@20: { pavone@58: memset(context, 0, sizeof(*context)); pavone@20: context->vdpmem = malloc(VRAM_SIZE); pavone@58: memset(context->vdpmem, 0, VRAM_SIZE); pavone@505: /* pavone@488: */ pavone@505: if (headless) { pavone@505: context->oddbuf = context->framebuf = malloc(FRAMEBUF_ENTRIES * (32 / 8)); pavone@505: memset(context->framebuf, 0, FRAMEBUF_ENTRIES * (32 / 8)); pavone@505: context->evenbuf = malloc(FRAMEBUF_ENTRIES * (32 / 8)); pavone@505: memset(context->evenbuf, 0, FRAMEBUF_ENTRIES * (32 / 8)); pavone@505: } else { pavone@505: render_alloc_surfaces(context); pavone@505: } pavone@488: context->framebuf = context->oddbuf; pavone@39: context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2); pavone@54: memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2); pavone@20: context->tmp_buf_a = context->linebuf + LINEBUF_SIZE; pavone@39: context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE; pavone@26: context->sprite_draws = MAX_DRAWS; pavone@471: context->fifo_write = 0; pavone@471: context->fifo_read = -1; pavone@505: pavone@426: if (!color_map_init_done) { pavone@426: uint8_t b,g,r; pavone@426: for (uint16_t color = 0; color < (1 << 12); color++) { pavone@426: if (color & FBUF_SHADOW) { pavone@426: b = levels[(color >> 9) & 0x7]; pavone@426: g = levels[(color >> 5) & 0x7]; pavone@426: r = levels[(color >> 1) & 0x7]; pavone@426: } else if(color & FBUF_HILIGHT) { pavone@426: b = levels[((color >> 9) & 0x7) + 7]; pavone@426: g = levels[((color >> 5) & 0x7) + 7]; pavone@426: r = levels[((color >> 1) & 0x7) + 7]; pavone@426: } else { pavone@426: b = levels[(color >> 8) & 0xE]; pavone@426: g = levels[(color >> 4) & 0xE]; pavone@426: r = levels[color & 0xE]; pavone@426: } pavone@426: color_map[color] = render_map_color(r, g, b); pavone@426: } pavone@426: color_map_init_done = 1; pavone@426: } pavone@437: for (uint8_t color = 0; color < (1 << (3 + 1 + 1 + 1)); color++) pavone@437: { pavone@437: uint8_t src = color & DBG_SRC_MASK; pavone@437: if (src > DBG_SRC_S) { pavone@437: context->debugcolors[color] = 0; pavone@437: } else { pavone@437: uint8_t r,g,b; pavone@437: b = debug_base[src][0]; pavone@437: g = debug_base[src][1]; pavone@437: r = debug_base[src][2]; pavone@437: if (color & DBG_PRIORITY) pavone@437: { pavone@437: if (b) { pavone@437: b += 48; pavone@437: } pavone@437: if (g) { pavone@437: g += 48; pavone@437: } pavone@437: if (r) { pavone@437: r += 48; pavone@437: } pavone@437: } pavone@437: if (color & DBG_SHADOW) { pavone@437: b /= 2; pavone@437: g /= 2; pavone@437: r /=2 ; pavone@437: } pavone@437: if (color & DBG_HILIGHT) { pavone@437: if (b) { pavone@437: b += 72; pavone@437: } pavone@437: if (g) { pavone@437: g += 72; pavone@437: } pavone@437: if (r) { pavone@437: r += 72; pavone@437: } pavone@437: } pavone@437: context->debugcolors[color] = render_map_color(r, g, b); pavone@437: } pavone@437: } pavone@623: if (region_pal) { pavone@623: context->flags2 |= FLAG2_REGION_PAL; pavone@623: } pavone@20: } pavone@20: pavone@884: void vdp_free(vdp_context *context) pavone@884: { pavone@884: free(context->vdpmem); pavone@884: free(context->linebuf); pavone@884: if (headless) { pavone@884: free(context->oddbuf); pavone@884: free(context->evenbuf); pavone@884: } else { pavone@884: render_free_surfaces(context); pavone@884: } pavone@884: free(context); pavone@884: } pavone@884: pavone@460: int is_refresh(vdp_context * context, uint32_t slot) pavone@460: { pavone@622: if (context->regs[REG_MODE_4] & BIT_H40) { pavone@622: return slot == 250 || slot == 26 || slot == 59 || slot == 90 || slot == 122 || slot == 154; pavone@460: } else { pavone@460: //TODO: Figure out which slots are refresh when display is off in 32-cell mode pavone@460: //These numbers are guesses based on H40 numbers pavone@622: return slot == 243 || slot == 19 || slot == 51 || slot == 83 || slot == 115; pavone@460: //The numbers below are the refresh slots during active display pavone@622: //return (slot == 29 || slot == 61 || slot == 93 || slot == 125); pavone@460: } pavone@460: } pavone@460: pavone@21: void render_sprite_cells(vdp_context * context) pavone@20: { pavone@21: if (context->cur_slot >= context->sprite_draws) { pavone@21: sprite_draw * d = context->sprite_draw_list + context->cur_slot; pavone@450: pavone@20: uint16_t dir; pavone@20: int16_t x; pavone@20: if (d->h_flip) { pavone@20: x = d->x_pos + 7; pavone@20: dir = -1; pavone@20: } else { pavone@20: x = d->x_pos; pavone@20: dir = 1; pavone@20: } pavone@27: //printf("Draw Slot %d of %d, Rendering sprite cell from %X to x: %d\n", context->cur_slot, context->sprite_draws, d->address, x); pavone@26: context->cur_slot--; pavone@143: for (uint16_t address = d->address; address != ((d->address+4) & 0xFFFF); address++) { pavone@27: if (x >= 0 && x < 320 && !(context->linebuf[x] & 0xF)) { pavone@494: if (context->linebuf[x] && (context->vdpmem[address] >> 4)) { pavone@494: context->flags2 |= FLAG2_SPRITE_COLLIDE; pavone@494: } pavone@20: context->linebuf[x] = (context->vdpmem[address] >> 4) | d->pal_priority; pavone@20: } pavone@20: x += dir; pavone@27: if (x >= 0 && x < 320 && !(context->linebuf[x] & 0xF)) { pavone@494: if (context->linebuf[x] && (context->vdpmem[address] & 0xF)) { pavone@494: context->flags2 |= FLAG2_SPRITE_COLLIDE; pavone@494: } pavone@20: context->linebuf[x] = (context->vdpmem[address] & 0xF) | d->pal_priority; pavone@20: } pavone@20: x += dir; pavone@20: } pavone@20: } pavone@20: } pavone@20: pavone@322: void vdp_print_sprite_table(vdp_context * context) pavone@322: { pavone@322: uint16_t sat_address = (context->regs[REG_SAT] & 0x7F) << 9; pavone@322: uint16_t current_index = 0; pavone@322: uint8_t count = 0; pavone@322: do { pavone@322: uint16_t address = current_index * 8 + sat_address; pavone@322: uint8_t height = ((context->vdpmem[address+2] & 0x3) + 1) * 8; pavone@322: uint8_t width = (((context->vdpmem[address+2] >> 2) & 0x3) + 1) * 8; pavone@322: int16_t y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & 0x1FF; pavone@323: int16_t x = ((context->vdpmem[address+ 6] & 0x3) << 8 | context->vdpmem[address + 7]) & 0x1FF; pavone@322: uint16_t link = context->vdpmem[address+3] & 0x7F; pavone@323: uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3; pavone@323: uint8_t pri = context->vdpmem[address + 4] >> 7; pavone@450: uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5; pavone@515: printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern); pavone@322: current_index = link; pavone@322: count++; pavone@322: } while (current_index != 0 && count < 80); pavone@322: } pavone@322: pavone@705: #define VRAM_READ 0 //0000 pavone@705: #define VRAM_WRITE 1 //0001 pavone@705: //2 would trigger register write 0010 pavone@705: #define CRAM_WRITE 3 //0011 pavone@705: #define VSRAM_READ 4 //0100 pavone@705: #define VSRAM_WRITE 5//0101 pavone@705: //6 would trigger regsiter write 0110 pavone@980: //7 is a mystery //0111 pavone@705: #define CRAM_READ 8 //1000 pavone@705: //9 is also a mystery //1001 pavone@705: //A would trigger register write 1010 pavone@705: //B is a mystery 1011 pavone@705: #define VRAM_READ8 0xC //1100 pavone@705: //D is a mystery 1101 pavone@705: //E would trigger register write 1110 pavone@705: //F is a mystery 1111 pavone@980: pavone@980: //Possible theory on how bits work pavone@980: //CD0 = Read/Write flag pavone@980: //CD2,(CD1|CD3) = RAM type pavone@980: // 00 = VRAM pavone@980: // 01 = CRAM pavone@980: // 10 = VSRAM pavone@980: // 11 = VRAM8 pavone@980: //Would result in pavone@980: // 7 = VRAM8 write pavone@980: // 9 = CRAM write alias pavone@980: // B = CRAM write alias pavone@980: // D = VRAM8 write alias pavone@980: // F = VRAM8 write alais pavone@980: pavone@705: #define DMA_START 0x20 pavone@705: pavone@705: const char * cd_name(uint8_t cd) pavone@705: { pavone@705: switch (cd & 0xF) pavone@705: { pavone@705: case VRAM_READ: pavone@705: return "VRAM read"; pavone@705: case VRAM_WRITE: pavone@705: return "VRAM write"; pavone@705: case CRAM_WRITE: pavone@705: return "CRAM write"; pavone@705: case VSRAM_READ: pavone@705: return "VSRAM read"; pavone@705: case VSRAM_WRITE: pavone@705: return "VSRAM write"; pavone@705: case VRAM_READ8: pavone@705: return "VRAM read (undocumented 8-bit mode)"; pavone@705: default: pavone@705: return "invalid"; pavone@705: } pavone@705: } pavone@705: pavone@327: void vdp_print_reg_explain(vdp_context * context) pavone@327: { pavone@327: char * hscroll[] = {"full", "7-line", "cell", "line"}; pavone@327: printf("**Mode Group**\n" pavone@327: "00: %.2X | H-ints %s, Pal Select %d, HVC latch %s, Display gen %s\n" pavone@327: "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d\n" pavone@327: "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n" pavone@327: "0C: %.2X | Width: %d, Shadow/Highlight: %s\n", pavone@757: context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", (context->regs[REG_MODE_1] & BIT_PAL_SEL) != 0, pavone@327: context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled", pavone@450: context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled", pavone@327: context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4, pavone@327: context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full", pavone@327: hscroll[context->regs[REG_MODE_3] & 0x3], pavone@327: context->regs[REG_MODE_4], context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32, context->regs[REG_MODE_4] & BIT_HILIGHT ? "enabled" : "disabled"); pavone@327: printf("\n**Table Group**\n" pavone@327: "02: %.2X | Scroll A Name Table: $%.4X\n" pavone@327: "03: %.2X | Window Name Table: $%.4X\n" pavone@327: "04: %.2X | Scroll B Name Table: $%.4X\n" pavone@327: "05: %.2X | Sprite Attribute Table: $%.4X\n" pavone@327: "0D: %.2X | HScroll Data Table: $%.4X\n", pavone@327: context->regs[REG_SCROLL_A], (context->regs[REG_SCROLL_A] & 0x38) << 10, pavone@327: context->regs[REG_WINDOW], (context->regs[REG_WINDOW] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x3C : 0x3E)) << 10, pavone@327: context->regs[REG_SCROLL_B], (context->regs[REG_SCROLL_B] & 0x7) << 13, pavone@621: context->regs[REG_SAT], (context->regs[REG_SAT] & (context->regs[REG_MODE_4] & BIT_H40 ? 0x7E : 0x7F)) << 9, pavone@621: context->regs[REG_HSCROLL], (context->regs[REG_HSCROLL] & 0x3F) << 10); pavone@327: char * sizes[] = {"32", "64", "invalid", "128"}; pavone@327: printf("\n**Misc Group**\n" pavone@327: "07: %.2X | Backdrop Color: $%X\n" pavone@327: "0A: %.2X | H-Int Counter: %u\n" pavone@327: "0F: %.2X | Auto-increment: $%X\n" pavone@327: "10: %.2X | Scroll A/B Size: %sx%s\n", pavone@505: context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR], pavone@450: context->regs[REG_HINT], context->regs[REG_HINT], pavone@327: context->regs[REG_AUTOINC], context->regs[REG_AUTOINC], pavone@327: context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]); pavone@621: char * src_types[] = {"68K", "68K", "Copy", "Fill"}; pavone@621: printf("\n**DMA Group**\n" pavone@621: "13: %.2X |\n" pavone@621: "14: %.2X | DMA Length: $%.4X words\n" pavone@621: "15: %.2X |\n" pavone@621: "16: %.2X |\n" pavone@621: "17: %.2X | DMA Source Address: $%.6X, Type: %s\n", pavone@621: context->regs[REG_DMALEN_L], pavone@621: context->regs[REG_DMALEN_H], context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L], pavone@621: context->regs[REG_DMASRC_L], pavone@621: context->regs[REG_DMASRC_M], pavone@621: context->regs[REG_DMASRC_H], pavone@629: context->regs[REG_DMASRC_H] << 17 | context->regs[REG_DMASRC_M] << 9 | context->regs[REG_DMASRC_L] << 1, pavone@621: src_types[context->regs[REG_DMASRC_H] >> 6 & 3]); pavone@438: printf("\n**Internal Group**\n" pavone@438: "Address: %X\n" pavone@705: "CD: %X - %s\n" pavone@647: "Pending: %s\n" pavone@647: "VCounter: %d\n" pavone@647: "HCounter: %d\n", pavone@705: context->address, context->cd, cd_name(context->cd), (context->flags & FLAG_PENDING) ? "true" : "false", pavone@647: context->vcounter, context->hslot*2); pavone@450: pavone@450: //TODO: Window Group, DMA Group pavone@327: } pavone@327: pavone@20: void scan_sprite_table(uint32_t line, vdp_context * context) pavone@20: { pavone@20: if (context->sprite_index && context->slot_counter) { pavone@20: line += 1; pavone@20: line &= 0xFF; pavone@413: uint16_t ymask, ymin; pavone@413: uint8_t height_mult; pavone@413: if (context->double_res) { pavone@413: line *= 2; pavone@413: if (context->framebuf != context->oddbuf) { pavone@413: line++; pavone@413: } pavone@413: ymask = 0x3FF; pavone@413: ymin = 256; pavone@413: height_mult = 16; pavone@413: } else { pavone@413: ymask = 0x1FF; pavone@413: ymin = 128; pavone@413: height_mult = 8; pavone@413: } pavone@20: context->sprite_index &= 0x7F; pavone@622: if (context->regs[REG_MODE_4] & BIT_H40) { pavone@38: if (context->sprite_index >= MAX_SPRITES_FRAME) { pavone@38: context->sprite_index = 0; pavone@38: return; pavone@38: } pavone@38: } else if(context->sprite_index >= MAX_SPRITES_FRAME_H32) { pavone@38: context->sprite_index = 0; pavone@38: return; pavone@38: } pavone@20: //TODO: Read from SAT cache rather than from VRAM pavone@20: uint16_t sat_address = (context->regs[REG_SAT] & 0x7F) << 9; pavone@20: uint16_t address = context->sprite_index * 8 + sat_address; pavone@413: line += ymin; pavone@415: uint16_t y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & ymask; pavone@413: uint8_t height = ((context->vdpmem[address+2] & 0x3) + 1) * height_mult; pavone@21: //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height); pavone@21: if (y <= line && line < (y + height)) { pavone@27: //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line); pavone@20: context->sprite_info_list[--(context->slot_counter)].size = context->vdpmem[address+2]; pavone@20: context->sprite_info_list[context->slot_counter].index = context->sprite_index; pavone@413: context->sprite_info_list[context->slot_counter].y = y-ymin; pavone@20: } pavone@20: context->sprite_index = context->vdpmem[address+3] & 0x7F; pavone@20: if (context->sprite_index && context->slot_counter) pavone@20: { pavone@20: address = context->sprite_index * 8 + sat_address; pavone@415: y = ((context->vdpmem[address] & 0x3) << 8 | context->vdpmem[address+1]) & ymask; pavone@413: height = ((context->vdpmem[address+2] & 0x3) + 1) * height_mult; pavone@323: //printf("Sprite %d | y: %d, height: %d\n", context->sprite_index, y, height); pavone@21: if (y <= line && line < (y + height)) { pavone@27: //printf("Sprite %d at y: %d with height %d is on line %d\n", context->sprite_index, y, height, line); pavone@20: context->sprite_info_list[--(context->slot_counter)].size = context->vdpmem[address+2]; pavone@20: context->sprite_info_list[context->slot_counter].index = context->sprite_index; pavone@413: context->sprite_info_list[context->slot_counter].y = y-ymin; pavone@20: } pavone@21: context->sprite_index = context->vdpmem[address+3] & 0x7F; pavone@20: } pavone@20: } pavone@20: } pavone@20: pavone@20: void read_sprite_x(uint32_t line, vdp_context * context) pavone@20: { pavone@34: if (context->cur_slot >= context->slot_counter) { pavone@34: if (context->sprite_draws) { pavone@34: line += 1; pavone@34: line &= 0xFF; pavone@34: //in tiles pavone@34: uint8_t width = ((context->sprite_info_list[context->cur_slot].size >> 2) & 0x3) + 1; pavone@34: //in pixels pavone@34: uint8_t height = ((context->sprite_info_list[context->cur_slot].size & 0x3) + 1) * 8; pavone@413: if (context->double_res) { pavone@413: line *= 2; pavone@413: if (context->framebuf != context->oddbuf) { pavone@413: line++; pavone@413: } pavone@413: height *= 2; pavone@413: } pavone@34: uint16_t att_addr = ((context->regs[REG_SAT] & 0x7F) << 9) + context->sprite_info_list[context->cur_slot].index * 8 + 4; pavone@450: uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1]; pavone@34: uint8_t pal_priority = (tileinfo >> 9) & 0x70; pavone@34: uint8_t row; pavone@34: if (tileinfo & MAP_BIT_V_FLIP) { pavone@34: row = (context->sprite_info_list[context->cur_slot].y + height - 1) - line; pavone@34: } else { pavone@34: row = line-context->sprite_info_list[context->cur_slot].y; pavone@34: } pavone@413: uint16_t address; pavone@413: if (context->double_res) { pavone@413: address = ((tileinfo & 0x3FF) << 6) + row * 4; pavone@413: } else { pavone@413: address = ((tileinfo & 0x7FF) << 5) + row * 4; pavone@413: } pavone@323: int16_t x = ((context->vdpmem[att_addr+ 2] & 0x3) << 8 | context->vdpmem[att_addr + 3]) & 0x1FF; pavone@36: if (x) { pavone@36: context->flags |= FLAG_CAN_MASK; pavone@36: } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) { pavone@36: context->flags |= FLAG_MASKED; pavone@36: } pavone@450: pavone@36: context->flags &= ~FLAG_DOT_OFLOW; pavone@36: int16_t i; pavone@36: if (context->flags & FLAG_MASKED) { pavone@36: for (i=0; i < width && context->sprite_draws; i++) { pavone@36: --context->sprite_draws; pavone@36: context->sprite_draw_list[context->sprite_draws].x_pos = -128; pavone@34: } pavone@36: } else { pavone@34: x -= 128; pavone@34: int16_t base_x = x; pavone@34: int16_t dir; pavone@34: if (tileinfo & MAP_BIT_H_FLIP) { pavone@34: x += (width-1) * 8; pavone@34: dir = -8; pavone@34: } else { pavone@34: dir = 8; pavone@34: } pavone@34: //printf("Sprite %d | x: %d, y: %d, width: %d, height: %d, pal_priority: %X, row: %d, tile addr: %X\n", context->sprite_info_list[context->cur_slot].index, x, context->sprite_info_list[context->cur_slot].y, width, height, pal_priority, row, address); pavone@35: for (i=0; i < width && context->sprite_draws; i++, x += dir) { pavone@34: --context->sprite_draws; pavone@34: context->sprite_draw_list[context->sprite_draws].address = address + i * height * 4; pavone@34: context->sprite_draw_list[context->sprite_draws].x_pos = x; pavone@34: context->sprite_draw_list[context->sprite_draws].pal_priority = pal_priority; pavone@34: context->sprite_draw_list[context->sprite_draws].h_flip = (tileinfo & MAP_BIT_H_FLIP) ? 1 : 0; pavone@34: } pavone@34: } pavone@36: if (i < width) { pavone@36: context->flags |= FLAG_DOT_OFLOW; pavone@36: } pavone@36: context->cur_slot--; pavone@20: } else { pavone@34: context->flags |= FLAG_DOT_OFLOW; pavone@20: } pavone@20: } pavone@20: } pavone@20: pavone@427: void write_cram(vdp_context * context, uint16_t address, uint16_t value) pavone@427: { pavone@427: uint16_t addr = (address/2) & (CRAM_SIZE-1); pavone@427: context->cram[addr] = value; pavone@427: context->colors[addr] = color_map[value & 0xEEE]; pavone@427: context->colors[addr + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW]; pavone@427: context->colors[addr + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT]; pavone@427: } pavone@427: pavone@980: #define CRAM_BITS 0xEEE pavone@980: #define VSRAM_BITS 0x7FF pavone@980: #define VSRAM_DIRTY_BITS 0xF800 pavone@980: pavone@984: void vdp_advance_dma(vdp_context * context) pavone@984: { pavone@984: context->regs[REG_DMASRC_L] += 1; pavone@984: if (!context->regs[REG_DMASRC_L]) { pavone@984: context->regs[REG_DMASRC_M] += 1; pavone@984: } pavone@984: context->address += context->regs[REG_AUTOINC]; pavone@984: uint16_t dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1; pavone@984: context->regs[REG_DMALEN_H] = dma_len >> 8; pavone@984: context->regs[REG_DMALEN_L] = dma_len; pavone@984: if (!dma_len) { pavone@984: context->flags &= ~FLAG_DMA_RUN; pavone@984: context->cd &= 0xF; pavone@984: } pavone@984: } pavone@984: pavone@20: void external_slot(vdp_context * context) pavone@20: { pavone@984: if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80 && context->fifo_read < 0) { pavone@984: context->fifo_read = (context->fifo_write-1) & (FIFO_SIZE-1); pavone@984: fifo_entry * cur = context->fifo + context->fifo_read; pavone@984: cur->cycle = context->cycles; pavone@984: cur->address = context->address; pavone@984: cur->partial = 2; pavone@984: vdp_advance_dma(context); pavone@984: } pavone@471: fifo_entry * start = context->fifo + context->fifo_read; pavone@471: if (context->fifo_read >= 0 && start->cycle <= context->cycles) { pavone@460: switch (start->cd & 0xF) pavone@460: { pavone@460: case VRAM_WRITE: pavone@460: if (start->partial) { pavone@471: //printf("VRAM Write: %X to %X at %d (line %d, slot %d)\n", start->value, start->address ^ 1, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); pavone@478: context->vdpmem[start->address ^ 1] = start->partial == 2 ? start->value >> 8 : start->value; pavone@460: } else { pavone@471: //printf("VRAM Write High: %X to %X at %d (line %d, slot %d)\n", start->value >> 8, start->address, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); pavone@460: context->vdpmem[start->address] = start->value >> 8; pavone@460: start->partial = 1; pavone@460: //skip auto-increment and removal of entry from fifo pavone@460: return; pavone@460: } pavone@460: break; pavone@460: case CRAM_WRITE: { pavone@460: //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); pavone@479: write_cram(context, start->address, start->partial == 2 ? context->fifo[context->fifo_write].value : start->value); pavone@460: break; pavone@460: } pavone@460: case VSRAM_WRITE: pavone@460: if (((start->address/2) & 63) < VSRAM_SIZE) { pavone@718: //printf("VSRAM Write: %X to %X @ vcounter: %d, hslot: %d, cycle: %d\n", start->value, context->address, context->vcounter, context->hslot, context->cycles); pavone@479: context->vsram[(start->address/2) & 63] = start->partial == 2 ? context->fifo[context->fifo_write].value : start->value; pavone@460: } pavone@460: pavone@460: break; pavone@460: } pavone@471: context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1); pavone@471: if (context->fifo_read == context->fifo_write) { pavone@983: if ((context->cd & 0x20) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { pavone@983: context->flags |= FLAG_DMA_RUN; pavone@983: } pavone@471: context->fifo_read = -1; pavone@460: } pavone@980: } else if ((context->flags & FLAG_DMA_RUN) && (context->regs[REG_DMASRC_H] & 0xC0) == 0xC0) { pavone@980: if (context->flags & FLAG_READ_FETCHED) { pavone@980: context->vdpmem[context->address ^ 1] = context->prefetch; pavone@980: pavone@980: //Update DMA state pavone@984: vdp_advance_dma(context); pavone@980: pavone@980: context->flags &= ~FLAG_READ_FETCHED; pavone@980: } else { pavone@980: context->prefetch = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1]; pavone@980: pavone@980: context->flags |= FLAG_READ_FETCHED; pavone@980: } pavone@980: } else if (!(context->cd & 1) && !(context->flags & FLAG_READ_FETCHED)){ pavone@980: switch(context->cd & 0xF) pavone@980: { pavone@980: case VRAM_READ: pavone@980: if (context->flags2 & FLAG2_READ_PENDING) { pavone@980: context->prefetch |= context->vdpmem[context->address | 1]; pavone@980: context->flags |= FLAG_READ_FETCHED; pavone@980: context->flags2 &= ~FLAG2_READ_PENDING; pavone@980: //Should this happen after the prefetch or after the read? pavone@980: //context->address += context->regs[REG_AUTOINC]; pavone@980: } else { pavone@980: context->prefetch = context->vdpmem[context->address & 0xFFFE] << 8; pavone@980: context->flags2 |= FLAG2_READ_PENDING; pavone@980: } pavone@980: break; pavone@980: case VRAM_READ8: pavone@980: context->prefetch = context->vdpmem[context->address ^ 1]; pavone@980: context->prefetch |= context->fifo[context->fifo_write].value & 0xFF00; pavone@980: context->flags |= FLAG_READ_FETCHED; pavone@980: //Should this happen after the prefetch or after the read? pavone@980: //context->address += context->regs[REG_AUTOINC]; pavone@980: break; pavone@980: case CRAM_READ: pavone@980: context->prefetch = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS; pavone@980: context->prefetch |= context->fifo[context->fifo_write].value & ~CRAM_BITS; pavone@980: context->flags |= FLAG_READ_FETCHED; pavone@980: //Should this happen after the prefetch or after the read? pavone@980: //context->address += context->regs[REG_AUTOINC]; pavone@980: break; pavone@980: case VSRAM_READ: { pavone@980: uint16_t address = (context->address /2) & 63; pavone@980: if (address >= VSRAM_SIZE) { pavone@980: address = 0; pavone@980: } pavone@980: context->prefetch = context->vsram[address] & VSRAM_BITS; pavone@980: context->prefetch |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS; pavone@980: context->flags |= FLAG_READ_FETCHED; pavone@980: //Should this happen after the prefetch or after the read? pavone@980: //context->address += context->regs[REG_AUTOINC]; pavone@980: break; pavone@980: } pavone@980: } pavone@460: } pavone@460: } pavone@460: pavone@924: void run_dma_src(vdp_context * context, int32_t slot) pavone@460: { pavone@75: //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode pavone@75: //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations pavone@471: if (context->fifo_write == context->fifo_read) { pavone@460: return; pavone@460: } pavone@478: fifo_entry * cur = NULL; pavone@984: if (!(context->regs[REG_DMASRC_H] & 0x80)) pavone@460: { pavone@984: //68K -> VDP pavone@924: if (slot == -1 || !is_refresh(context, slot-1)) { pavone@478: cur = context->fifo + context->fifo_write; pavone@622: cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY; pavone@478: cur->address = context->address; pavone@478: cur->value = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); pavone@478: cur->cd = context->cd; pavone@478: cur->partial = 0; pavone@478: if (context->fifo_read < 0) { pavone@478: context->fifo_read = context->fifo_write; pavone@478: } pavone@478: context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); pavone@984: vdp_advance_dma(context); pavone@75: } pavone@54: } pavone@20: } pavone@20: pavone@40: #define WINDOW_RIGHT 0x80 pavone@40: #define WINDOW_DOWN 0x80 pavone@40: pavone@25: void read_map_scroll(uint16_t column, uint16_t vsram_off, uint32_t line, uint16_t address, uint16_t hscroll_val, vdp_context * context) pavone@20: { pavone@417: uint16_t window_line_shift, v_offset_mask, vscroll_shift; pavone@414: if (context->double_res) { pavone@413: line *= 2; pavone@413: if (context->framebuf != context->oddbuf) { pavone@413: line++; pavone@413: } pavone@417: window_line_shift = 4; pavone@417: v_offset_mask = 0xF; pavone@417: vscroll_shift = 4; pavone@417: } else { pavone@417: window_line_shift = 3; pavone@417: v_offset_mask = 0x7; pavone@417: vscroll_shift = 3; pavone@414: } pavone@40: if (!vsram_off) { pavone@40: uint16_t left_col, right_col; pavone@40: if (context->regs[REG_WINDOW_H] & WINDOW_RIGHT) { pavone@920: left_col = (context->regs[REG_WINDOW_H] & 0x1F) * 2 + 2; pavone@41: right_col = 42; pavone@41: } else { pavone@40: left_col = 0; pavone@40: right_col = (context->regs[REG_WINDOW_H] & 0x1F) * 2; pavone@40: if (right_col) { pavone@40: right_col += 2; pavone@40: } pavone@40: } pavone@41: uint16_t top_line, bottom_line; pavone@41: if (context->regs[REG_WINDOW_V] & WINDOW_DOWN) { pavone@417: top_line = (context->regs[REG_WINDOW_V] & 0x1F) << window_line_shift; pavone@417: bottom_line = context->double_res ? 481 : 241; pavone@41: } else { pavone@41: top_line = 0; pavone@417: bottom_line = (context->regs[REG_WINDOW_V] & 0x1F) << window_line_shift; pavone@41: } pavone@41: if ((column >= left_col && column < right_col) || (line >= top_line && line < bottom_line)) { pavone@41: uint16_t address = context->regs[REG_WINDOW] << 10; pavone@41: uint16_t line_offset, offset, mask; pavone@622: if (context->regs[REG_MODE_4] & BIT_H40) { pavone@41: address &= 0xF000; pavone@417: line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF; pavone@41: mask = 0x7F; pavone@450: pavone@41: } else { pavone@41: address &= 0xF800; pavone@417: line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF; pavone@41: mask = 0x3F; pavone@41: } pavone@417: if (context->double_res) { pavone@417: mask <<= 1; pavone@417: mask |= 1; pavone@417: } pavone@42: offset = address + line_offset + (((column - 2) * 2) & mask); pavone@41: context->col_1 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1]; pavone@54: //printf("Window | top: %d, bot: %d, left: %d, right: %d, base: %X, line: %X offset: %X, tile: %X, reg: %X\n", top_line, bottom_line, left_col, right_col, address, line_offset, offset, ((context->col_1 & 0x3FF) << 5), context->regs[REG_WINDOW]); pavone@42: offset = address + line_offset + (((column - 1) * 2) & mask); pavone@41: context->col_2 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1]; pavone@417: context->v_offset = (line) & v_offset_mask; pavone@41: context->flags |= FLAG_WINDOW; pavone@41: return; pavone@40: } pavone@40: context->flags &= ~FLAG_WINDOW; pavone@40: } pavone@20: uint16_t vscroll; pavone@20: switch(context->regs[REG_SCROLL] & 0x30) pavone@20: { pavone@20: case 0: pavone@20: vscroll = 0xFF; pavone@20: break; pavone@20: case 0x10: pavone@20: vscroll = 0x1FF; pavone@20: break; pavone@20: case 0x20: pavone@20: //TODO: Verify this behavior pavone@20: vscroll = 0; pavone@20: break; pavone@20: case 0x30: pavone@20: vscroll = 0x3FF; pavone@20: break; pavone@20: } pavone@414: if (context->double_res) { pavone@413: vscroll <<= 1; pavone@413: vscroll |= 1; pavone@414: } pavone@718: //TODO: Further research on vscroll latch behavior and the "first column bug" pavone@718: if (!column) { pavone@718: if (context->regs[REG_MODE_3] & BIT_VSCROLL) { pavone@718: if (context->regs[REG_MODE_4] & BIT_H40) { pavone@718: //Based on observed behavior documented by Eke-Eke, I'm guessing the VDP pavone@718: //ends up fetching the last value on the VSRAM bus in the H40 case pavone@718: //getting the last latched value should be close enough for now pavone@718: if (!vsram_off) { pavone@718: context->vscroll_latch[0] = context->vscroll_latch[1]; pavone@718: } pavone@718: } else { pavone@718: //supposedly it's always forced to 0 in the H32 case pavone@718: context->vscroll_latch[0] = context->vscroll_latch[1] = 0; pavone@718: } pavone@718: } else { pavone@718: context->vscroll_latch[vsram_off] = context->vsram[vsram_off]; pavone@718: } pavone@718: } else if (context->regs[REG_MODE_3] & BIT_VSCROLL) { pavone@710: context->vscroll_latch[vsram_off] = context->vsram[column - 2 + vsram_off]; pavone@710: } pavone@710: vscroll &= context->vscroll_latch[vsram_off] + line; pavone@414: context->v_offset = vscroll & v_offset_mask; pavone@26: //printf("%s | line %d, vsram: %d, vscroll: %d, v_offset: %d\n",(vsram_off ? "B" : "A"), line, context->vsram[context->regs[REG_MODE_3] & 0x4 ? column : 0], vscroll, context->v_offset); pavone@414: vscroll >>= vscroll_shift; pavone@20: uint16_t hscroll_mask; pavone@20: uint16_t v_mul; pavone@20: switch(context->regs[REG_SCROLL] & 0x3) pavone@20: { pavone@20: case 0: pavone@108: hscroll_mask = 0x1F; pavone@20: v_mul = 64; pavone@20: break; pavone@20: case 0x1: pavone@39: hscroll_mask = 0x3F; pavone@20: v_mul = 128; pavone@20: break; pavone@20: case 0x2: pavone@20: //TODO: Verify this behavior pavone@20: hscroll_mask = 0; pavone@20: v_mul = 0; pavone@20: break; pavone@20: case 0x3: pavone@108: hscroll_mask = 0x7F; pavone@20: v_mul = 256; pavone@20: break; pavone@20: } pavone@28: uint16_t hscroll, offset; pavone@28: for (int i = 0; i < 2; i++) { pavone@39: hscroll = (column - 2 + i - ((hscroll_val/8) & 0xFFFE)) & hscroll_mask; pavone@39: offset = address + ((vscroll * v_mul + hscroll*2) & 0x1FFF); pavone@39: //printf("%s | line: %d, col: %d, x: %d, hs_mask %X, scr reg: %X, tbl addr: %X\n", (vsram_off ? "B" : "A"), line, (column-2+i), hscroll, hscroll_mask, context->regs[REG_SCROLL], offset); pavone@28: uint16_t col_val = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1]; pavone@28: if (i) { pavone@28: context->col_2 = col_val; pavone@28: } else { pavone@28: context->col_1 = col_val; pavone@28: } pavone@28: } pavone@20: } pavone@20: pavone@20: void read_map_scroll_a(uint16_t column, uint32_t line, vdp_context * context) pavone@20: { pavone@25: read_map_scroll(column, 0, line, (context->regs[REG_SCROLL_A] & 0x38) << 10, context->hscroll_a, context); pavone@20: } pavone@20: pavone@20: void read_map_scroll_b(uint16_t column, uint32_t line, vdp_context * context) pavone@20: { pavone@25: read_map_scroll(column, 1, line, (context->regs[REG_SCROLL_B] & 0x7) << 13, context->hscroll_b, context); pavone@20: } pavone@20: pavone@436: void render_map(uint16_t col, uint8_t * tmp_buf, uint8_t offset, vdp_context * context) pavone@20: { pavone@413: uint16_t address; pavone@413: uint8_t shift, add; pavone@413: if (context->double_res) { pavone@413: address = ((col & 0x3FF) << 6); pavone@413: shift = 1; pavone@413: add = context->framebuf != context->oddbuf ? 1 : 0; pavone@413: } else { pavone@413: address = ((col & 0x7FF) << 5); pavone@413: shift = 0; pavone@413: add = 0; pavone@413: } pavone@20: if (col & MAP_BIT_V_FLIP) { pavone@414: address += 28 - 4 * context->v_offset/*((context->v_offset << shift) + add)*/; pavone@20: } else { pavone@414: address += 4 * context->v_offset/*((context->v_offset << shift) + add)*/; pavone@20: } pavone@20: uint16_t pal_priority = (col >> 9) & 0x70; pavone@20: int32_t dir; pavone@20: if (col & MAP_BIT_H_FLIP) { pavone@436: offset += 7; pavone@436: offset &= SCROLL_BUFFER_MASK; pavone@20: dir = -1; pavone@20: } else { pavone@20: dir = 1; pavone@20: } pavone@20: for (uint32_t i=0; i < 4; i++, address++) pavone@20: { pavone@436: tmp_buf[offset] = pal_priority | (context->vdpmem[address] >> 4); pavone@436: offset += dir; pavone@436: offset &= SCROLL_BUFFER_MASK; pavone@436: tmp_buf[offset] = pal_priority | (context->vdpmem[address] & 0xF); pavone@436: offset += dir; pavone@436: offset &= SCROLL_BUFFER_MASK; pavone@20: } pavone@20: } pavone@20: pavone@20: void render_map_1(vdp_context * context) pavone@20: { pavone@436: render_map(context->col_1, context->tmp_buf_a, context->buf_a_off, context); pavone@20: } pavone@20: pavone@20: void render_map_2(vdp_context * context) pavone@20: { pavone@436: render_map(context->col_2, context->tmp_buf_a, context->buf_a_off+8, context); pavone@20: } pavone@20: pavone@20: void render_map_3(vdp_context * context) pavone@20: { pavone@436: render_map(context->col_1, context->tmp_buf_b, context->buf_b_off, context); pavone@20: } pavone@20: pavone@20: void render_map_output(uint32_t line, int32_t col, vdp_context * context) pavone@20: { pavone@20: if (line >= 240) { pavone@20: return; pavone@20: } pavone@436: render_map(context->col_2, context->tmp_buf_b, context->buf_b_off+8, context); pavone@719: uint32_t *dst; pavone@436: uint8_t *sprite_buf, *plane_a, *plane_b; pavone@436: int plane_a_off, plane_b_off; pavone@20: if (col) pavone@20: { pavone@20: col-=2; pavone@719: dst = context->framebuf; pavone@719: dst += line * 320 + col * 8; pavone@722: if (context->debug < 2) { pavone@722: sprite_buf = context->linebuf + col * 8; pavone@722: uint8_t a_src, src; pavone@722: if (context->flags & FLAG_WINDOW) { pavone@722: plane_a_off = context->buf_a_off; pavone@722: a_src = DBG_SRC_W; pavone@722: } else { pavone@722: plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF); pavone@722: a_src = DBG_SRC_A; pavone@722: } pavone@722: plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF); pavone@722: //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7)); pavone@450: pavone@722: if (context->regs[REG_MODE_4] & BIT_HILIGHT) { pavone@722: for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { pavone@722: plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK); pavone@722: plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK); pavone@748: uint8_t pixel = context->regs[REG_BG_COLOR]; pavone@748: uint32_t *colors = context->colors; pavone@722: src = DBG_SRC_BG; pavone@722: if (*plane_b & 0xF) { pavone@722: pixel = *plane_b; pavone@722: src = DBG_SRC_B; pavone@722: } pavone@748: uint8_t intensity = *plane_b & BUF_BIT_PRIORITY; pavone@722: if (*plane_a & 0xF && (*plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) { pavone@722: pixel = *plane_a; pavone@722: src = DBG_SRC_A; pavone@722: } pavone@748: intensity |= *plane_a & BUF_BIT_PRIORITY; pavone@748: if (*sprite_buf & 0xF && (*sprite_buf & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) { pavone@748: if ((*sprite_buf & 0x3F) == 0x3E) { pavone@748: intensity += BUF_BIT_PRIORITY; pavone@748: } else if ((*sprite_buf & 0x3F) == 0x3F) { pavone@748: intensity = 0; pavone@748: } else { pavone@722: pixel = *sprite_buf; pavone@722: src = DBG_SRC_S; pavone@722: if ((pixel & 0xF) == 0xE) { pavone@748: intensity = BUF_BIT_PRIORITY; pavone@748: } else { pavone@748: intensity |= pixel & BUF_BIT_PRIORITY; pavone@722: } pavone@722: } pavone@748: } pavone@748: if (!intensity) { pavone@505: src |= DBG_SHADOW; pavone@748: colors += CRAM_SIZE; pavone@748: } else if (intensity == BUF_BIT_PRIORITY*2) { pavone@748: src |= DBG_HILIGHT; pavone@748: colors += CRAM_SIZE*2; pavone@722: } pavone@884: pavone@722: uint32_t outpixel; pavone@722: if (context->debug) { pavone@722: outpixel = context->debugcolors[src]; pavone@722: } else { pavone@748: outpixel = colors[pixel & 0x3F]; pavone@722: } pavone@722: *(dst++) = outpixel; pavone@722: } pavone@722: } else { pavone@722: for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { pavone@722: plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK); pavone@722: plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK); pavone@722: uint8_t pixel = context->regs[REG_BG_COLOR]; pavone@722: src = DBG_SRC_BG; pavone@722: if (*plane_b & 0xF) { pavone@722: pixel = *plane_b; pavone@722: src = DBG_SRC_B; pavone@722: } pavone@722: if (*plane_a & 0xF && (*plane_a & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) { pavone@722: pixel = *plane_a; pavone@722: src = DBG_SRC_A; pavone@722: } pavone@722: if (*sprite_buf & 0xF && (*sprite_buf & BUF_BIT_PRIORITY) >= (pixel & BUF_BIT_PRIORITY)) { pavone@230: pixel = *sprite_buf; pavone@437: src = DBG_SRC_S; pavone@230: } pavone@722: uint32_t outpixel; pavone@722: if (context->debug) { pavone@722: outpixel = context->debugcolors[src]; pavone@722: } else { pavone@722: outpixel = context->colors[pixel & 0x3F]; pavone@722: } pavone@722: *(dst++) = outpixel; pavone@230: } pavone@722: } pavone@722: } else if (context->debug == 2) { pavone@722: if (col < 32) { pavone@722: *(dst++) = context->colors[col * 2]; pavone@722: *(dst++) = context->colors[col * 2]; pavone@722: *(dst++) = context->colors[col * 2]; pavone@722: *(dst++) = context->colors[col * 2]; pavone@722: *(dst++) = context->colors[col * 2 + 1]; pavone@722: *(dst++) = context->colors[col * 2 + 1]; pavone@722: *(dst++) = context->colors[col * 2 + 1]; pavone@722: *(dst++) = context->colors[col * 2 + 1]; pavone@722: *(dst++) = context->colors[col * 2 + 2]; pavone@722: *(dst++) = context->colors[col * 2 + 2]; pavone@722: *(dst++) = context->colors[col * 2 + 2]; pavone@722: *(dst++) = context->colors[col * 2 + 2]; pavone@722: *(dst++) = context->colors[col * 2 + 3]; pavone@722: *(dst++) = context->colors[col * 2 + 3]; pavone@722: *(dst++) = context->colors[col * 2 + 3]; pavone@722: *(dst++) = context->colors[col * 2 + 3]; pavone@722: } else if (col == 32 || line >= 192) { pavone@722: for (int32_t i = 0; i < 16; i ++) { pavone@722: *(dst++) = 0; pavone@437: } pavone@722: } else { pavone@722: for (int32_t i = 0; i < 16; i ++) { pavone@722: *(dst++) = context->colors[line / 3 + (col - 34) * 0x20]; pavone@722: } pavone@20: } pavone@230: } else { pavone@771: uint32_t base = (context->debug - 3) * 0x200; pavone@771: uint32_t cell = base + (line / 8) * (context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32) + col; pavone@771: uint32_t address = (cell * 32 + (line % 8) * 4) & 0xFFFF; pavone@722: for (int32_t i = 0; i < 4; i ++) { pavone@722: *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)]; pavone@722: *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)]; pavone@722: address++; pavone@722: } pavone@722: cell++; pavone@771: address = (cell * 32 + (line % 8) * 4) & 0xFFFF; pavone@722: for (int32_t i = 0; i < 4; i ++) { pavone@722: *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)]; pavone@722: *(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)]; pavone@722: address++; pavone@230: } pavone@20: } pavone@20: } pavone@436: context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; pavone@436: context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; pavone@20: } pavone@20: pavone@822: uint32_t const h40_hsync_cycles[] = {19, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 18, 20, 20, 20, 19}; pavone@822: pavone@822: void vdp_advance_line(vdp_context *context) pavone@822: { pavone@822: context->vcounter++; pavone@822: context->vcounter &= 0x1FF; pavone@822: if (context->flags2 & FLAG2_REGION_PAL) { pavone@822: if (context->latched_mode & BIT_PAL) { pavone@822: if (context->vcounter == 0x10B) { pavone@822: context->vcounter = 0x1D2; pavone@822: } pavone@822: } else if (context->vcounter == 0x103){ pavone@822: context->vcounter = 0x1CA; pavone@822: } pavone@822: } else if (!(context->latched_mode & BIT_PAL) && context->vcounter == 0xEB) { pavone@822: context->vcounter = 0x1E5; pavone@822: } pavone@884: pavone@822: if (context->vcounter > (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) { pavone@822: context->hint_counter = context->regs[REG_HINT]; pavone@822: } else if (context->hint_counter) { pavone@822: context->hint_counter--; pavone@822: } else { pavone@822: context->flags2 |= FLAG2_HINT_PENDING; pavone@822: context->pending_hint_start = context->cycles; pavone@822: context->hint_counter = context->regs[REG_HINT]; pavone@822: } pavone@822: } pavone@822: pavone@822: #define CHECK_ONLY if (context->cycles >= target_cycles) { return; } pavone@924: #define CHECK_LIMIT if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } context->hslot++; context->cycles += slot_cycles; CHECK_ONLY pavone@822: pavone@20: #define COLUMN_RENDER_BLOCK(column, startcyc) \ pavone@20: case startcyc:\ pavone@822: read_map_scroll_a(column, context->vcounter, context);\ pavone@822: CHECK_LIMIT\ pavone@922: case ((startcyc+1)&0xFF):\ pavone@20: external_slot(context);\ pavone@822: CHECK_LIMIT\ pavone@922: case ((startcyc+2)&0xFF):\ pavone@20: render_map_1(context);\ pavone@822: CHECK_LIMIT\ pavone@922: case ((startcyc+3)&0xFF):\ pavone@20: render_map_2(context);\ pavone@822: CHECK_LIMIT\ pavone@922: case ((startcyc+4)&0xFF):\ pavone@822: read_map_scroll_b(column, context->vcounter, context);\ pavone@822: CHECK_LIMIT\ pavone@922: case ((startcyc+5)&0xFF):\ pavone@822: read_sprite_x(context->vcounter, context);\ pavone@822: CHECK_LIMIT\ pavone@922: case ((startcyc+6)&0xFF):\ pavone@20: render_map_3(context);\ pavone@822: CHECK_LIMIT\ pavone@922: case ((startcyc+7)&0xFF):\ pavone@822: render_map_output(context->vcounter, column, context);\ pavone@822: CHECK_LIMIT pavone@20: pavone@20: #define COLUMN_RENDER_BLOCK_REFRESH(column, startcyc) \ pavone@20: case startcyc:\ pavone@822: read_map_scroll_a(column, context->vcounter, context);\ pavone@822: CHECK_LIMIT\ pavone@20: case (startcyc+1):\ pavone@822: /* refresh, no don't run dma src */\ pavone@822: context->hslot++;\ pavone@822: context->cycles += slot_cycles;\ pavone@822: CHECK_ONLY\ pavone@20: case (startcyc+2):\ pavone@20: render_map_1(context);\ pavone@822: CHECK_LIMIT\ pavone@20: case (startcyc+3):\ pavone@20: render_map_2(context);\ pavone@822: CHECK_LIMIT\ pavone@20: case (startcyc+4):\ pavone@822: read_map_scroll_b(column, context->vcounter, context);\ pavone@822: CHECK_LIMIT\ pavone@20: case (startcyc+5):\ pavone@822: read_sprite_x(context->vcounter, context);\ pavone@822: CHECK_LIMIT\ pavone@20: case (startcyc+6):\ pavone@20: render_map_3(context);\ pavone@822: CHECK_LIMIT\ pavone@20: case (startcyc+7):\ pavone@822: render_map_output(context->vcounter, column, context);\ pavone@822: CHECK_LIMIT pavone@884: pavone@822: #define SPRITE_RENDER_H40(slot) \ pavone@822: case slot:\ pavone@822: render_sprite_cells( context);\ pavone@822: scan_sprite_table(context->vcounter, context);\ pavone@924: if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \ pavone@822: if (slot == 182) {\ pavone@822: context->hslot = 229;\ pavone@922: context->cycles += h40_hsync_cycles[0];\ pavone@822: } else {\ pavone@822: context->hslot++;\ pavone@822: if (slot >= HSYNC_SLOT_H40 && slot < HSYNC_END_H40) {\ pavone@822: context->cycles += h40_hsync_cycles[slot - HSYNC_SLOT_H40];\ pavone@822: } else {\ pavone@822: context->cycles += slot_cycles;\ pavone@822: }\ pavone@822: }\ pavone@822: CHECK_ONLY pavone@884: pavone@822: #define SPRITE_RENDER_H32(slot) \ pavone@822: case slot:\ pavone@822: render_sprite_cells( context);\ pavone@822: scan_sprite_table(context->vcounter, context);\ pavone@924: if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } \ pavone@822: if (slot == 147) {\ pavone@822: context->hslot = 233;\ pavone@822: } else {\ pavone@822: context->hslot++;\ pavone@822: }\ pavone@822: context->cycles += slot_cycles;\ pavone@822: CHECK_ONLY pavone@884: pavone@20: pavone@822: void vdp_h40(vdp_context * context, uint32_t target_cycles) pavone@20: { pavone@20: uint16_t address; pavone@20: uint32_t mask; pavone@822: uint32_t const slot_cycles = MCLKS_SLOT_H40; pavone@822: switch(context->hslot) pavone@822: { pavone@822: for (;;) pavone@20: { pavone@922: //sprite attribute table scan starts pavone@647: case 165: pavone@822: context->sprite_index = 0x80; pavone@822: context->slot_counter = MAX_SPRITES_LINE; pavone@21: render_sprite_cells( context); pavone@822: scan_sprite_table(context->vcounter, context); pavone@822: CHECK_LIMIT pavone@922: SPRITE_RENDER_H40(166) pavone@922: SPRITE_RENDER_H40(167) pavone@922: case 168: pavone@922: external_slot(context); pavone@922: CHECK_LIMIT pavone@922: SPRITE_RENDER_H40(169) pavone@922: SPRITE_RENDER_H40(170) pavone@922: SPRITE_RENDER_H40(171) pavone@822: SPRITE_RENDER_H40(172) pavone@822: SPRITE_RENDER_H40(173) pavone@822: SPRITE_RENDER_H40(174) pavone@822: SPRITE_RENDER_H40(175) pavone@822: SPRITE_RENDER_H40(176) pavone@822: SPRITE_RENDER_H40(177) pavone@822: SPRITE_RENDER_H40(178) pavone@822: SPRITE_RENDER_H40(179) pavone@822: SPRITE_RENDER_H40(180) pavone@822: SPRITE_RENDER_H40(181) pavone@922: //!HSYNC asserted pavone@822: SPRITE_RENDER_H40(182) pavone@822: SPRITE_RENDER_H40(229) pavone@822: SPRITE_RENDER_H40(230) pavone@822: SPRITE_RENDER_H40(231) pavone@822: SPRITE_RENDER_H40(232) pavone@822: SPRITE_RENDER_H40(233) pavone@822: SPRITE_RENDER_H40(234) pavone@822: SPRITE_RENDER_H40(235) pavone@822: SPRITE_RENDER_H40(236) pavone@822: SPRITE_RENDER_H40(237) pavone@822: SPRITE_RENDER_H40(238) pavone@822: SPRITE_RENDER_H40(239) pavone@822: SPRITE_RENDER_H40(240) pavone@822: SPRITE_RENDER_H40(241) pavone@922: case 242: pavone@20: address = (context->regs[REG_HSCROLL] & 0x3F) << 10; pavone@20: mask = 0; pavone@20: if (context->regs[REG_MODE_3] & 0x2) { pavone@20: mask |= 0xF8; pavone@20: } pavone@20: if (context->regs[REG_MODE_3] & 0x1) { pavone@20: mask |= 0x7; pavone@20: } pavone@822: address += (context->vcounter & mask) * 4; pavone@20: context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1]; pavone@20: context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3]; pavone@822: //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b); pavone@924: if (context->flags & FLAG_DMA_RUN) { run_dma_src(context, -1); } pavone@822: context->hslot++; pavone@822: context->cycles += h40_hsync_cycles[14]; pavone@822: CHECK_ONLY pavone@20: //!HSYNC high pavone@922: SPRITE_RENDER_H40(243) pavone@922: SPRITE_RENDER_H40(244) pavone@922: SPRITE_RENDER_H40(245) pavone@922: SPRITE_RENDER_H40(246) pavone@922: case 247: pavone@822: read_map_scroll_a(0, context->vcounter, context); pavone@822: CHECK_LIMIT pavone@922: SPRITE_RENDER_H40(248) pavone@922: case 249: pavone@20: render_map_1(context); pavone@822: scan_sprite_table(context->vcounter, context);//Just a guess pavone@922: CHECK_LIMIT pavone@922: case 250: pavone@20: render_map_2(context); pavone@822: scan_sprite_table(context->vcounter, context);//Just a guess pavone@822: CHECK_LIMIT pavone@922: case 251: pavone@822: read_map_scroll_b(0, context->vcounter, context); pavone@822: CHECK_LIMIT pavone@922: SPRITE_RENDER_H40(252) pavone@922: case 253: pavone@20: render_map_3(context); pavone@822: scan_sprite_table(context->vcounter, context);//Just a guess pavone@822: CHECK_LIMIT pavone@922: case 254: pavone@822: if (context->vcounter == (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) { pavone@822: context->flags2 |= FLAG2_VINT_PENDING; pavone@822: context->pending_vint_start = context->cycles; pavone@822: } pavone@822: render_map_output(context->vcounter, 0, context); pavone@822: scan_sprite_table(context->vcounter, context);//Just a guess pavone@20: //reverse context slot counter so it counts the number of sprite slots pavone@20: //filled rather than the number of available slots pavone@21: //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter; pavone@21: context->cur_slot = MAX_SPRITES_LINE-1; pavone@20: context->sprite_draws = MAX_DRAWS; pavone@36: context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED); pavone@822: CHECK_LIMIT pavone@922: COLUMN_RENDER_BLOCK(2, 255) pavone@922: COLUMN_RENDER_BLOCK(4, 7) pavone@922: COLUMN_RENDER_BLOCK(6, 15) pavone@922: COLUMN_RENDER_BLOCK_REFRESH(8, 23) pavone@922: COLUMN_RENDER_BLOCK(10, 31) pavone@922: COLUMN_RENDER_BLOCK(12, 39) pavone@922: COLUMN_RENDER_BLOCK(14, 47) pavone@922: COLUMN_RENDER_BLOCK_REFRESH(16, 55) pavone@922: COLUMN_RENDER_BLOCK(18, 63) pavone@922: COLUMN_RENDER_BLOCK(20, 71) pavone@922: COLUMN_RENDER_BLOCK(22, 79) pavone@922: COLUMN_RENDER_BLOCK_REFRESH(24, 87) pavone@922: COLUMN_RENDER_BLOCK(26, 95) pavone@922: COLUMN_RENDER_BLOCK(28, 103) pavone@922: COLUMN_RENDER_BLOCK(30, 111) pavone@922: COLUMN_RENDER_BLOCK_REFRESH(32, 119) pavone@922: COLUMN_RENDER_BLOCK(34, 127) pavone@922: COLUMN_RENDER_BLOCK(36, 135) pavone@922: COLUMN_RENDER_BLOCK(38, 143) pavone@922: COLUMN_RENDER_BLOCK_REFRESH(40, 151) pavone@922: case 159: pavone@922: external_slot(context); pavone@922: CHECK_LIMIT pavone@922: case 160: pavone@922: external_slot(context); pavone@922: CHECK_LIMIT pavone@922: //sprite render to line buffer starts pavone@922: case 161: pavone@922: context->cur_slot = MAX_DRAWS-1; pavone@922: memset(context->linebuf, 0, LINEBUF_SIZE); pavone@922: render_sprite_cells(context); pavone@922: CHECK_LIMIT pavone@922: case 162: pavone@922: render_sprite_cells(context); pavone@922: CHECK_LIMIT pavone@922: case 163: pavone@922: render_sprite_cells(context); pavone@922: CHECK_LIMIT pavone@922: case 164: pavone@922: render_sprite_cells(context); pavone@922: vdp_advance_line(context); pavone@922: if (context->vcounter == (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) { pavone@922: context->hslot++; pavone@922: context->cycles += slot_cycles; pavone@922: return; pavone@922: } pavone@922: CHECK_LIMIT pavone@20: } pavone@822: default: pavone@822: context->hslot++; pavone@822: context->cycles += slot_cycles; pavone@822: return; pavone@822: } pavone@20: } pavone@20: pavone@822: void vdp_h32(vdp_context * context, uint32_t target_cycles) pavone@20: { pavone@37: uint16_t address; pavone@37: uint32_t mask; pavone@822: uint32_t const slot_cycles = MCLKS_SLOT_H32; pavone@822: switch(context->hslot) pavone@822: { pavone@822: for (;;) pavone@20: { pavone@923: //sprite attribute table scan starts pavone@647: case 132: pavone@822: context->sprite_index = 0x80; pavone@822: context->slot_counter = MAX_SPRITES_LINE_H32; pavone@37: render_sprite_cells( context); pavone@822: scan_sprite_table(context->vcounter, context); pavone@822: CHECK_LIMIT pavone@923: SPRITE_RENDER_H32(133) pavone@923: SPRITE_RENDER_H32(134) pavone@923: SPRITE_RENDER_H32(135) pavone@923: SPRITE_RENDER_H32(136) pavone@923: SPRITE_RENDER_H32(137) pavone@923: SPRITE_RENDER_H32(138) pavone@822: SPRITE_RENDER_H32(139) pavone@822: SPRITE_RENDER_H32(140) pavone@822: SPRITE_RENDER_H32(141) pavone@923: case 142: pavone@923: external_slot(context); pavone@923: CHECK_LIMIT pavone@822: SPRITE_RENDER_H32(143) pavone@822: SPRITE_RENDER_H32(144) pavone@822: SPRITE_RENDER_H32(145) pavone@822: SPRITE_RENDER_H32(146) pavone@822: SPRITE_RENDER_H32(147) pavone@923: //HSYNC start pavone@923: SPRITE_RENDER_H32(233) pavone@822: SPRITE_RENDER_H32(234) pavone@822: SPRITE_RENDER_H32(235) pavone@822: SPRITE_RENDER_H32(236) pavone@822: SPRITE_RENDER_H32(237) pavone@822: SPRITE_RENDER_H32(238) pavone@822: SPRITE_RENDER_H32(239) pavone@923: case 240: pavone@37: external_slot(context); pavone@822: CHECK_LIMIT pavone@923: case 241: pavone@37: address = (context->regs[REG_HSCROLL] & 0x3F) << 10; pavone@37: mask = 0; pavone@37: if (context->regs[REG_MODE_3] & 0x2) { pavone@37: mask |= 0xF8; pavone@37: } pavone@37: if (context->regs[REG_MODE_3] & 0x1) { pavone@37: mask |= 0x7; pavone@37: } pavone@822: address += (context->vcounter & mask) * 4; pavone@37: context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1]; pavone@37: context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3]; pavone@822: //printf("%d: HScroll A: %d, HScroll B: %d\n", context->vcounter, context->hscroll_a, context->hscroll_b); pavone@822: CHECK_LIMIT pavone@923: SPRITE_RENDER_H32(242) pavone@923: SPRITE_RENDER_H32(243) pavone@923: SPRITE_RENDER_H32(244) pavone@923: SPRITE_RENDER_H32(245) pavone@37: //!HSYNC high pavone@923: case 246: pavone@822: read_map_scroll_a(0, context->vcounter, context); pavone@822: CHECK_LIMIT pavone@923: SPRITE_RENDER_H32(247) pavone@923: case 248: pavone@37: render_map_1(context); pavone@822: scan_sprite_table(context->vcounter, context);//Just a guess pavone@822: CHECK_LIMIT pavone@923: case 249: pavone@37: render_map_2(context); pavone@822: scan_sprite_table(context->vcounter, context);//Just a guess pavone@923: CHECK_LIMIT pavone@923: case 250: pavone@822: read_map_scroll_b(0, context->vcounter, context); pavone@822: CHECK_LIMIT pavone@923: case 251: pavone@37: render_sprite_cells(context); pavone@822: scan_sprite_table(context->vcounter, context); pavone@822: CHECK_LIMIT pavone@923: case 252: pavone@37: render_map_3(context); pavone@822: scan_sprite_table(context->vcounter, context);//Just a guess pavone@822: CHECK_LIMIT pavone@923: case 253: pavone@822: render_map_output(context->vcounter, 0, context); pavone@822: scan_sprite_table(context->vcounter, context);//Just a guess pavone@37: //reverse context slot counter so it counts the number of sprite slots pavone@37: //filled rather than the number of available slots pavone@37: //context->slot_counter = MAX_SPRITES_LINE - context->slot_counter; pavone@37: context->cur_slot = MAX_SPRITES_LINE_H32-1; pavone@37: context->sprite_draws = MAX_DRAWS_H32; pavone@37: context->flags &= (~FLAG_CAN_MASK & ~FLAG_MASKED); pavone@822: CHECK_LIMIT pavone@923: COLUMN_RENDER_BLOCK(2, 254) pavone@923: COLUMN_RENDER_BLOCK(4, 6) pavone@923: COLUMN_RENDER_BLOCK(6, 14) pavone@923: COLUMN_RENDER_BLOCK_REFRESH(8, 22) pavone@923: COLUMN_RENDER_BLOCK(10, 30) pavone@923: COLUMN_RENDER_BLOCK(12, 38) pavone@923: COLUMN_RENDER_BLOCK(14, 46) pavone@923: COLUMN_RENDER_BLOCK_REFRESH(16, 54) pavone@923: COLUMN_RENDER_BLOCK(18, 62) pavone@923: COLUMN_RENDER_BLOCK(20, 70) pavone@923: COLUMN_RENDER_BLOCK(22, 78) pavone@923: COLUMN_RENDER_BLOCK_REFRESH(24, 86) pavone@923: COLUMN_RENDER_BLOCK(26, 94) pavone@923: COLUMN_RENDER_BLOCK(28, 102) pavone@923: COLUMN_RENDER_BLOCK(30, 110) pavone@923: COLUMN_RENDER_BLOCK_REFRESH(32, 118) pavone@923: case 126: pavone@923: external_slot(context); pavone@923: CHECK_LIMIT pavone@923: case 127: pavone@923: external_slot(context); pavone@923: CHECK_LIMIT pavone@923: //sprite render to line buffer starts pavone@923: case 128: pavone@923: context->cur_slot = MAX_DRAWS_H32-1; pavone@923: memset(context->linebuf, 0, LINEBUF_SIZE); pavone@923: render_sprite_cells(context); pavone@923: CHECK_LIMIT pavone@923: case 129: pavone@923: render_sprite_cells(context); pavone@923: CHECK_LIMIT pavone@923: case 130: pavone@923: render_sprite_cells(context); pavone@923: CHECK_LIMIT pavone@923: case 131: pavone@923: render_sprite_cells(context); pavone@923: vdp_advance_line(context); pavone@923: if (context->vcounter == (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START)) { pavone@923: context->hslot++; pavone@923: context->cycles += slot_cycles; pavone@923: return; pavone@923: } pavone@923: CHECK_LIMIT pavone@20: } pavone@822: default: pavone@822: context->hslot++; pavone@822: context->cycles += MCLKS_SLOT_H32; pavone@503: } pavone@503: } pavone@503: pavone@20: void latch_mode(vdp_context * context) pavone@20: { pavone@622: context->latched_mode = context->regs[REG_MODE_2] & BIT_PAL; pavone@20: } pavone@20: pavone@330: void check_render_bg(vdp_context * context, int32_t line, uint32_t slot) pavone@54: { pavone@622: int starti = -1; pavone@622: if (context->regs[REG_MODE_4] & BIT_H40) { pavone@622: if (slot >= 12 && slot < 172) { pavone@622: uint32_t x = (slot-12)*2; pavone@622: starti = line * 320 + x; pavone@622: } pavone@622: } else { pavone@622: if (slot >= 11 && slot < 139) { pavone@622: uint32_t x = (slot-11)*2; pavone@622: starti = line * 320 + x; pavone@622: } pavone@622: } pavone@622: if (starti >= 0) { pavone@719: uint32_t color = context->colors[context->regs[REG_BG_COLOR]]; pavone@719: uint32_t * start = context->framebuf; pavone@719: start += starti; pavone@719: for (int i = 0; i < 2; i++) { pavone@719: *(start++) = color; pavone@54: } pavone@54: } pavone@54: } pavone@54: pavone@717: pavone@20: void vdp_run_context(vdp_context * context, uint32_t target_cycles) pavone@20: { pavone@20: while(context->cycles < target_cycles) pavone@20: { pavone@697: uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START; pavone@822: //line 0x1FF is basically active even though it's not displayed pavone@822: uint8_t active_slot = context->vcounter < inactive_start || context->vcounter == 0x1FF; pavone@622: uint8_t is_h40 = context->regs[REG_MODE_4] & BIT_H40; pavone@822: if (context->regs[REG_MODE_2] & DISPLAY_ENABLE && active_slot) { pavone@822: if (is_h40) { pavone@822: vdp_h40(context, target_cycles); pavone@330: } else { pavone@822: vdp_h32(context, target_cycles); pavone@330: } pavone@330: } else { pavone@622: if (is_h40) { pavone@922: if (context->hslot == 161) { pavone@822: context->cur_slot = MAX_DRAWS-1; pavone@822: memset(context->linebuf, 0, LINEBUF_SIZE); pavone@922: } else if (context->hslot == 165) { pavone@822: context->sprite_index = 0x80; pavone@822: context->slot_counter = MAX_SPRITES_LINE; pavone@503: } pavone@20: } else { pavone@923: if (context->hslot == 128) { pavone@822: context->cur_slot = MAX_DRAWS_H32-1; pavone@822: memset(context->linebuf, 0, LINEBUF_SIZE); pavone@923: } else if (context->hslot == 132) { pavone@822: context->sprite_index = 0x80; pavone@822: context->slot_counter = MAX_SPRITES_LINE_H32; pavone@822: } pavone@20: } pavone@822: if(context->vcounter == inactive_start) { pavone@822: uint32_t intslot = context->regs[REG_MODE_4] & BIT_H40 ? VINT_SLOT_H40 : VINT_SLOT_H32; pavone@822: if (context->hslot == intslot) { pavone@822: context->flags2 |= FLAG2_VINT_PENDING; pavone@822: context->pending_vint_start = context->cycles; pavone@822: } pavone@822: } pavone@822: uint32_t inccycles; pavone@822: if (is_h40) { pavone@922: if (context->hslot == 182) { pavone@922: inccycles = h40_hsync_cycles[0]; pavone@922: } else if (context->hslot < HSYNC_SLOT_H40 || context->hslot >= HSYNC_END_H40) { pavone@822: inccycles = MCLKS_SLOT_H40; pavone@822: } else { pavone@822: inccycles = h40_hsync_cycles[context->hslot-HSYNC_SLOT_H40]; pavone@822: } pavone@822: } else { pavone@822: inccycles = MCLKS_SLOT_H32; pavone@822: } pavone@822: if (!is_refresh(context, context->hslot)) { pavone@54: external_slot(context); pavone@54: } pavone@822: if (context->vcounter < inactive_start) { pavone@822: check_render_bg(context, context->vcounter, context->hslot); pavone@54: } pavone@822: if (context->flags & FLAG_DMA_RUN && !is_refresh(context, context->hslot)) { pavone@822: run_dma_src(context, context->hslot); pavone@822: } pavone@822: context->cycles += inccycles; pavone@622: context->hslot++; pavone@622: if (is_h40) { pavone@647: if (context->hslot == LINE_CHANGE_H40) { pavone@717: vdp_advance_line(context); pavone@699: if (context->vcounter == (inactive_start + 8)) { pavone@699: context->frame++; pavone@699: } pavone@622: } else if (context->hslot == 183) { pavone@622: context->hslot = 229; pavone@622: } pavone@622: } else { pavone@647: if (context->hslot == LINE_CHANGE_H32) { pavone@717: vdp_advance_line(context); pavone@699: if (context->vcounter == (inactive_start + 8)) { pavone@699: context->frame++; pavone@699: } pavone@622: } else if (context->hslot == 148) { pavone@622: context->hslot = 233; pavone@622: } pavone@622: } pavone@622: } pavone@20: } pavone@20: } pavone@20: pavone@20: uint32_t vdp_run_to_vblank(vdp_context * context) pavone@20: { pavone@622: uint32_t target_cycles = ((context->latched_mode & BIT_PAL) ? PAL_INACTIVE_START : NTSC_INACTIVE_START) * MCLKS_LINE; pavone@20: vdp_run_context(context, target_cycles); pavone@20: return context->cycles; pavone@20: } pavone@20: pavone@75: void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles) pavone@75: { pavone@75: for(;;) { pavone@75: uint32_t dmalen = (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]; pavone@75: if (!dmalen) { pavone@75: dmalen = 0x10000; pavone@75: } pavone@622: uint32_t min_dma_complete = dmalen * (context->regs[REG_MODE_4] & BIT_H40 ? 16 : 20); pavone@75: if ((context->regs[REG_DMASRC_H] & 0xC0) == 0xC0 || (context->cd & 0xF) == VRAM_WRITE) { pavone@75: //DMA copies take twice as long to complete since they require a read and a write pavone@75: //DMA Fills and transfers to VRAM also take twice as long as it requires 2 writes for a single word pavone@75: min_dma_complete *= 2; pavone@75: } pavone@75: min_dma_complete += context->cycles; pavone@75: if (target_cycles < min_dma_complete) { pavone@75: vdp_run_context(context, target_cycles); pavone@75: return; pavone@75: } else { pavone@75: vdp_run_context(context, min_dma_complete); pavone@75: if (!(context->flags & FLAG_DMA_RUN)) { pavone@75: return; pavone@75: } pavone@75: } pavone@75: } pavone@75: } pavone@75: pavone@75: int vdp_control_port_write(vdp_context * context, uint16_t value) pavone@54: { pavone@471: //printf("control port write: %X at %d\n", value, context->cycles); pavone@149: if (context->flags & FLAG_DMA_RUN) { pavone@149: return -1; pavone@149: } pavone@54: if (context->flags & FLAG_PENDING) { pavone@54: context->address = (context->address & 0x3FFF) | (value << 14); pavone@983: //It seems like the DMA enable bit doesn't so much enable DMA so much pavone@983: //as it enables changing CD5 from control port writes pavone@983: uint8_t preserve = (context->regs[REG_MODE_2] & BIT_DMA_ENABLE) ? 0x3 : 0x23; pavone@983: context->cd = (context->cd & preserve) | ((value >> 2) & ~preserve & 0xFF); pavone@75: context->flags &= ~FLAG_PENDING; pavone@980: //Should these be taken care of here or after the first write? pavone@980: context->flags &= ~FLAG_READ_FETCHED; pavone@980: context->flags2 &= ~FLAG2_READ_PENDING; pavone@453: //printf("New Address: %X, New CD: %X\n", context->address, context->cd); pavone@983: if (context->cd & 0x20) { pavone@327: // pavone@75: if((context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { pavone@75: //DMA copy or 68K -> VDP, transfer starts immediately pavone@75: context->flags |= FLAG_DMA_RUN; pavone@131: context->dma_cd = context->cd; pavone@717: //printf("DMA start (length: %X) at cycle %d, frame: %d, vcounter: %d, hslot: %d\n", (context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L], context->cycles, context->frame, context->vcounter, context->hslot); pavone@75: if (!(context->regs[REG_DMASRC_H] & 0x80)) { pavone@327: //printf("DMA Address: %X, New CD: %X, Source: %X, Length: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L]); pavone@75: return 1; pavone@327: } else { pavone@327: //printf("DMA Copy Address: %X, New CD: %X, Source: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); pavone@75: } pavone@327: } else { pavone@453: //printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd); pavone@75: } pavone@63: } pavone@54: } else { pavone@54: if ((value & 0xC000) == 0x8000) { pavone@54: //Register write pavone@54: uint8_t reg = (value >> 8) & 0x1F; pavone@475: if (reg < (context->regs[REG_MODE_2] & BIT_MODE_5 ? VDP_REGS : 0xA)) { pavone@453: //printf("register %d set to %X\n", reg, value & 0xFF); pavone@480: if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) { pavone@480: context->hv_latch = vdp_hv_counter_read(context); pavone@480: } pavone@505: if (reg == REG_BG_COLOR) { pavone@505: value &= 0x3F; pavone@505: } pavone@718: /*if (reg == REG_MODE_4 && ((value ^ context->regs[reg]) & BIT_H40)) { pavone@717: printf("Mode changed from H%d to H%d @ %d, frame: %d\n", context->regs[reg] & BIT_H40 ? 40 : 32, value & BIT_H40 ? 40 : 32, context->cycles, context->frame); pavone@718: }*/ pavone@54: context->regs[reg] = value; pavone@413: if (reg == REG_MODE_4) { pavone@413: context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); pavone@415: if (!context->double_res) { pavone@415: context->framebuf = context->oddbuf; pavone@415: } pavone@983: } pavone@476: context->cd &= 0x3C; pavone@54: } pavone@54: } else { pavone@54: context->flags |= FLAG_PENDING; pavone@54: context->address = (context->address &0xC000) | (value & 0x3FFF); pavone@54: context->cd = (context->cd &0x3C) | (value >> 14); pavone@980: //Should these be taken care of here or after the second write? pavone@980: //context->flags &= ~FLAG_READ_FETCHED; pavone@980: //context->flags2 &= ~FLAG2_READ_PENDING; pavone@54: } pavone@54: } pavone@75: return 0; pavone@54: } pavone@54: pavone@149: int vdp_data_port_write(vdp_context * context, uint16_t value) pavone@54: { pavone@471: //printf("data port write: %X at %d\n", value, context->cycles); pavone@460: if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { pavone@149: return -1; pavone@149: } pavone@980: if (context->flags & FLAG_PENDING) { pavone@980: context->flags &= ~FLAG_PENDING; pavone@980: //Should these be cleared here? pavone@980: context->flags &= ~FLAG_READ_FETCHED; pavone@980: context->flags2 &= ~FLAG2_READ_PENDING; pavone@980: } pavone@109: /*if (context->fifo_cur == context->fifo_end) { pavone@54: printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles); pavone@109: }*/ pavone@460: if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { pavone@460: context->flags &= ~FLAG_DMA_RUN; pavone@460: } pavone@471: while (context->fifo_write == context->fifo_read) { pavone@622: vdp_run_context(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)); pavone@54: } pavone@471: fifo_entry * cur = context->fifo + context->fifo_write; pavone@622: cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY; pavone@471: cur->address = context->address; pavone@471: cur->value = value; pavone@471: cur->cd = context->cd; pavone@471: cur->partial = 0; pavone@471: if (context->fifo_read < 0) { pavone@471: context->fifo_read = context->fifo_write; pavone@471: } pavone@471: context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); pavone@138: context->address += context->regs[REG_AUTOINC]; pavone@149: return 0; pavone@54: } pavone@54: pavone@470: void vdp_test_port_write(vdp_context * context, uint16_t value) pavone@470: { pavone@470: //TODO: implement test register pavone@470: } pavone@470: pavone@54: uint16_t vdp_control_port_read(vdp_context * context) pavone@54: { pavone@54: context->flags &= ~FLAG_PENDING; pavone@980: //Bits 15-10 are not fixed like Charles MacDonald's doc suggests, but instead open bus values that reflect 68K prefetch pavone@981: uint16_t value = get_open_bus_value() & 0xFC00; pavone@471: if (context->fifo_read < 0) { pavone@54: value |= 0x200; pavone@54: } pavone@471: if (context->fifo_read == context->fifo_write) { pavone@54: value |= 0x100; pavone@54: } pavone@317: if (context->flags2 & FLAG2_VINT_PENDING) { pavone@413: value |= 0x80; pavone@413: } pavone@494: if (context->flags & FLAG_DOT_OFLOW) { pavone@494: value |= 0x40; pavone@494: } pavone@494: if (context->flags2 & FLAG2_SPRITE_COLLIDE) { pavone@494: value |= 0x20; pavone@494: //TODO: Test when this is actually cleared pavone@494: context->flags2 &= ~FLAG2_SPRITE_COLLIDE; pavone@494: } pavone@413: if ((context->regs[REG_MODE_4] & BIT_INTERLACE) && context->framebuf == context->oddbuf) { pavone@413: value |= 0x10; pavone@317: } pavone@622: uint32_t line= context->vcounter; pavone@622: uint32_t slot = context->hslot; pavone@717: uint32_t inactive_start = (context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START); pavone@921: if ((line >= inactive_start && line < 0x1FF) || !(context->regs[REG_MODE_2] & BIT_DISP_EN)) { pavone@318: value |= 0x8; pavone@318: } pavone@622: if (context->regs[REG_MODE_4] & BIT_H40) { pavone@622: if (slot < HBLANK_END_H40 || slot > HBLANK_START_H40) { pavone@622: value |= 0x4; pavone@622: } pavone@622: } else { pavone@622: if (slot < HBLANK_END_H32 || slot > HBLANK_START_H32) { pavone@622: value |= 0x4; pavone@622: } pavone@318: } pavone@983: if (context->cd & 0x20) { pavone@141: value |= 0x2; pavone@75: } pavone@714: if (context->flags2 & FLAG2_REGION_PAL) { pavone@317: value |= 0x1; pavone@317: } pavone@459: //printf("status read at cycle %d returned %X\n", context->cycles, value); pavone@54: return value; pavone@54: } pavone@54: pavone@54: uint16_t vdp_data_port_read(vdp_context * context) pavone@54: { pavone@980: if (context->flags & FLAG_PENDING) { pavone@980: context->flags &= ~FLAG_PENDING; pavone@980: //Should these be cleared here? pavone@980: context->flags &= ~FLAG_READ_FETCHED; pavone@980: context->flags2 &= ~FLAG2_READ_PENDING; pavone@980: } pavone@138: if (context->cd & 1) { pavone@54: return 0; pavone@54: } pavone@980: while (!(context->flags & FLAG_READ_FETCHED)) { pavone@622: vdp_run_context(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)); pavone@54: } pavone@980: context->flags &= ~FLAG_READ_FETCHED; pavone@980: //Should this happen after the prefetch or after the read? pavone@54: context->address += context->regs[REG_AUTOINC]; pavone@980: return context->prefetch; pavone@54: } pavone@54: pavone@137: uint16_t vdp_hv_counter_read(vdp_context * context) pavone@137: { pavone@480: if (context->regs[REG_MODE_1] & BIT_HVC_LATCH) { pavone@480: return context->hv_latch; pavone@480: } pavone@622: uint32_t line= context->vcounter & 0xFF; pavone@647: uint32_t linecyc = context->hslot; pavone@137: linecyc &= 0xFF; pavone@413: if (context->double_res) { pavone@413: line <<= 1; pavone@413: if (line & 0x100) { pavone@413: line |= 1; pavone@413: } pavone@413: } pavone@137: return (line << 8) | linecyc; pavone@137: } pavone@137: pavone@470: uint16_t vdp_test_port_read(vdp_context * context) pavone@470: { pavone@470: //TODO: Find out what actually gets returned here pavone@470: return 0xFFFF; pavone@470: } pavone@470: pavone@65: void vdp_adjust_cycles(vdp_context * context, uint32_t deduction) pavone@65: { pavone@65: context->cycles -= deduction; pavone@717: if (context->pending_vint_start >= deduction) { pavone@717: context->pending_vint_start -= deduction; pavone@717: } else { pavone@717: context->pending_vint_start = 0; pavone@717: } pavone@717: if (context->pending_hint_start >= deduction) { pavone@717: context->pending_hint_start -= deduction; pavone@717: } else { pavone@717: context->pending_hint_start = 0; pavone@717: } pavone@471: if (context->fifo_read >= 0) { pavone@471: int32_t idx = context->fifo_read; pavone@471: do { pavone@471: if (context->fifo[idx].cycle >= deduction) { pavone@471: context->fifo[idx].cycle -= deduction; pavone@471: } else { pavone@471: context->fifo[idx].cycle = 0; pavone@471: } pavone@471: idx = (idx+1) & (FIFO_SIZE-1); pavone@471: } while(idx != context->fifo_write); pavone@65: } pavone@65: } pavone@65: pavone@717: uint32_t vdp_cycles_hslot_wrap_h40(vdp_context * context) pavone@717: { pavone@717: if (context->hslot < 183) { pavone@717: return MCLKS_LINE - context->hslot * MCLKS_SLOT_H40; pavone@717: } else if (context->hslot < HSYNC_END_H40) { pavone@717: uint32_t before_hsync = context->hslot < HSYNC_SLOT_H40 ? (HSYNC_SLOT_H40 - context->hslot) * MCLKS_SLOT_H40 : 0; pavone@717: uint32_t hsync = 0; pavone@717: for (int i = context->hslot <= HSYNC_SLOT_H40 ? 0 : context->hslot - HSYNC_SLOT_H40; i < sizeof(h40_hsync_cycles)/sizeof(uint32_t); i++) pavone@717: { pavone@717: hsync += h40_hsync_cycles[i]; pavone@717: } pavone@717: uint32_t after_hsync = (256- HSYNC_END_H40) * MCLKS_SLOT_H40; pavone@717: return before_hsync + hsync + after_hsync; pavone@717: } else { pavone@717: return (256-context->hslot) * MCLKS_SLOT_H40; pavone@717: } pavone@717: } pavone@717: pavone@623: uint32_t vdp_cycles_next_line(vdp_context * context) pavone@623: { pavone@623: if (context->regs[REG_MODE_4] & BIT_H40) { pavone@647: if (context->hslot < LINE_CHANGE_H40) { pavone@697: return (LINE_CHANGE_H40 - context->hslot) * MCLKS_SLOT_H40; pavone@623: } else { pavone@717: return vdp_cycles_hslot_wrap_h40(context) + LINE_CHANGE_H40 * MCLKS_SLOT_H40; pavone@623: } pavone@623: } else { pavone@647: if (context->hslot < LINE_CHANGE_H32) { pavone@647: return (LINE_CHANGE_H32 - context->hslot) * MCLKS_SLOT_H32; pavone@623: } else if (context->hslot < 148) { pavone@647: return MCLKS_LINE - (context->hslot - LINE_CHANGE_H32) * MCLKS_SLOT_H32; pavone@623: } else { pavone@647: return (256-context->hslot + LINE_CHANGE_H32) * MCLKS_SLOT_H32; pavone@623: } pavone@623: } pavone@623: } pavone@623: pavone@623: uint32_t vdp_cycles_to_line(vdp_context * context, uint32_t target) pavone@623: { pavone@623: uint32_t jump_start, jump_dst; pavone@623: if (context->flags2 & FLAG2_REGION_PAL) { pavone@623: if (context->latched_mode & BIT_PAL) { pavone@623: jump_start = 0x10B; pavone@623: jump_dst = 0x1D2; pavone@623: } else { pavone@623: jump_start = 0x103; pavone@623: jump_dst = 0x1CA; pavone@623: } pavone@623: } else { pavone@623: if (context->latched_mode & BIT_PAL) { pavone@623: jump_start = 0; pavone@623: jump_dst = 0; pavone@623: } else { pavone@623: jump_start = 0xEB; pavone@623: jump_dst = 0x1E5; pavone@623: } pavone@623: } pavone@623: uint32_t lines; pavone@623: if (context->vcounter < target) { pavone@623: if (target < jump_start) { pavone@623: lines = target - context->vcounter; pavone@623: } else { pavone@623: lines = jump_start - context->vcounter + target - jump_dst; pavone@623: } pavone@623: } else { pavone@623: if (context->vcounter < jump_start) { pavone@718: lines = jump_start - context->vcounter + 512 - jump_dst; pavone@623: } else { pavone@718: lines = 512 - context->vcounter; pavone@623: } pavone@623: if (target < jump_start) { pavone@623: lines += target; pavone@623: } else { pavone@623: lines += jump_start + target - jump_dst; pavone@623: } pavone@623: } pavone@623: return MCLKS_LINE * (lines - 1) + vdp_cycles_next_line(context); pavone@623: } pavone@623: pavone@680: uint32_t vdp_frame_end_line(vdp_context * context) pavone@623: { pavone@623: uint32_t frame_end; pavone@623: if (context->flags2 & FLAG2_REGION_PAL) { pavone@623: if (context->latched_mode & BIT_PAL) { pavone@623: frame_end = PAL_INACTIVE_START + 8; pavone@623: } else { pavone@623: frame_end = NTSC_INACTIVE_START + 8; pavone@623: } pavone@623: } else { pavone@623: if (context->latched_mode & BIT_PAL) { pavone@623: frame_end = 512; pavone@623: } else { pavone@623: frame_end = NTSC_INACTIVE_START + 8; pavone@623: } pavone@623: } pavone@680: return frame_end; pavone@680: } pavone@680: pavone@680: uint32_t vdp_cycles_to_frame_end(vdp_context * context) pavone@680: { pavone@680: return context->cycles + vdp_cycles_to_line(context, vdp_frame_end_line(context)); pavone@680: } pavone@680: pavone@317: uint32_t vdp_next_hint(vdp_context * context) pavone@317: { pavone@327: if (!(context->regs[REG_MODE_1] & BIT_HINT_EN)) { pavone@317: return 0xFFFFFFFF; pavone@317: } pavone@317: if (context->flags2 & FLAG2_HINT_PENDING) { pavone@717: return context->pending_hint_start; pavone@317: } pavone@622: uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START; pavone@623: uint32_t hint_line; pavone@708: if (context->vcounter + context->hint_counter >= inactive_start) { pavone@724: if (context->regs[REG_HINT] > inactive_start) { pavone@724: return 0xFFFFFFFF; pavone@724: } pavone@623: hint_line = context->regs[REG_HINT]; pavone@623: } else { pavone@623: hint_line = context->vcounter + context->hint_counter + 1; pavone@317: } pavone@623: pavone@623: return context->cycles + vdp_cycles_to_line(context, hint_line); pavone@317: } pavone@317: pavone@317: uint32_t vdp_next_vint(vdp_context * context) pavone@317: { pavone@327: if (!(context->regs[REG_MODE_2] & BIT_VINT_EN)) { pavone@317: return 0xFFFFFFFF; pavone@317: } pavone@317: if (context->flags2 & FLAG2_VINT_PENDING) { pavone@717: return context->pending_vint_start; pavone@317: } pavone@623: pavone@623: pavone@623: return vdp_next_vint_z80(context); pavone@317: } pavone@317: pavone@333: uint32_t vdp_next_vint_z80(vdp_context * context) pavone@333: { pavone@622: uint32_t inactive_start = context->latched_mode & BIT_PAL ? PAL_INACTIVE_START : NTSC_INACTIVE_START; pavone@623: if (context->vcounter == inactive_start) { pavone@623: if (context->regs[REG_MODE_4] & BIT_H40) { pavone@699: if (context->hslot >= LINE_CHANGE_H40) { pavone@717: return context->cycles + vdp_cycles_hslot_wrap_h40(context) + VINT_SLOT_H40 * MCLKS_SLOT_H40; pavone@717: } else if (context->hslot <= VINT_SLOT_H40) { pavone@623: return context->cycles + (VINT_SLOT_H40 - context->hslot) * MCLKS_SLOT_H40; pavone@623: } pavone@623: } else { pavone@699: if (context->hslot >= LINE_CHANGE_H32) { pavone@623: if (context->hslot < 148) { pavone@623: return context->cycles + (VINT_SLOT_H32 + 148 - context->hslot + 256 - 233) * MCLKS_SLOT_H32; pavone@623: } else { pavone@623: return context->cycles + (VINT_SLOT_H32 + 256 - context->hslot) * MCLKS_SLOT_H32; pavone@623: } pavone@717: } else if (context->hslot <= VINT_SLOT_H32) { pavone@623: return context->cycles + (VINT_SLOT_H32 - context->hslot) * MCLKS_SLOT_H32; pavone@623: } pavone@623: } pavone@623: } pavone@623: int32_t cycles_to_vint = vdp_cycles_to_line(context, inactive_start); pavone@622: if (context->regs[REG_MODE_4] & BIT_H40) { pavone@717: cycles_to_vint += MCLKS_LINE - (LINE_CHANGE_H40 - VINT_SLOT_H40) * MCLKS_SLOT_H40; pavone@333: } else { pavone@699: cycles_to_vint += (VINT_SLOT_H32 + 148 - LINE_CHANGE_H32 + 256 - 233) * MCLKS_SLOT_H32; pavone@333: } pavone@623: return context->cycles + cycles_to_vint; pavone@333: } pavone@333: pavone@953: void vdp_int_ack(vdp_context * context) pavone@317: { pavone@952: //Apparently the VDP interrupt controller is not very smart pavone@952: //Instead of paying attention to what interrupt is being acknowledged it just pavone@952: //clears the pending flag for whatever interrupt it is currently asserted pavone@952: //which may be different from the interrupt it was asserting when the 68k pavone@952: //started the interrupt process. The window for this is narrow and depends pavone@952: //on the latency between the int enable register write and the interrupt being pavone@952: //asserted, but Fatal Rewind depends on this due to some buggy code pavone@952: if ((context->flags2 & FLAG2_VINT_PENDING) && (context->regs[REG_MODE_2] & BIT_VINT_EN)) { pavone@317: context->flags2 &= ~FLAG2_VINT_PENDING; pavone@952: } else if((context->flags2 & FLAG2_HINT_PENDING) && (context->regs[REG_MODE_1] & BIT_HINT_EN)) { pavone@317: context->flags2 &= ~FLAG2_HINT_PENDING; pavone@317: } pavone@317: } pavone@317: