kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
558 wiersze
17 KiB
C
558 wiersze
17 KiB
C
|
|
||
|
#include "shared.h"
|
||
|
#include "vcnt.h"
|
||
|
#include "hcnt.h"
|
||
|
#include "hvc.h"
|
||
|
|
||
|
/* Pack and unpack CRAM data */
|
||
|
#define PACK_CRAM(d) ((((d)&0xE00)>>9)|(((d)&0x0E0)>>2)|(((d)&0x00E)<<5))
|
||
|
#define UNPACK_CRAM(d) ((((d)&0x1C0)>>5)|((d)&0x038)<<2|(((d)&0x007)<<9))
|
||
|
|
||
|
/* Mark a pattern as dirty */
|
||
|
#define MARK_BG_DIRTY(addr) \
|
||
|
{ \
|
||
|
int name = (addr >> 5) & 0x7FF; \
|
||
|
if(bg_name_dirty[name] == 0) \
|
||
|
{ \
|
||
|
bg_name_list[bg_list_index] = name; \
|
||
|
bg_list_index += 1; \
|
||
|
} \
|
||
|
bg_name_dirty[name] |= (1 << ((addr >> 2) & 0x07)); \
|
||
|
}
|
||
|
|
||
|
/* Tables that define the playfield layout */
|
||
|
uint8 shift_table[] = {6, 7, 0, 8};
|
||
|
uint8 col_mask_table[] = {0x1F, 0x3F, 0x1F, 0x7F};
|
||
|
uint16 row_mask_table[] = {0x0FF, 0x1FF, 0x2FF, 0x3FF};
|
||
|
uint32 y_mask_table[] = {0x1FC0, 0x1FC0, 0x1F80, 0x1F00};
|
||
|
|
||
|
uint8 sat[0x400]; /* Internal copy of sprite attribute table */
|
||
|
//uint8 vram[0x10000]; /* Video RAM (64Kx8) */
|
||
|
uint8 cram[0x80]; /* On-chip color RAM (64x9) */
|
||
|
uint8 vsram[0x80]; /* On-chip vertical scroll RAM (40x11) */
|
||
|
uint8 reg[0x20]; /* Internal VDP registers (23x8) */
|
||
|
uint16 addr; /* Address register */
|
||
|
uint16 addr_latch; /* Latched A15, A14 of address */
|
||
|
uint8 code; /* Code register */
|
||
|
uint8 pending; /* Pending write flag */
|
||
|
uint16 buffer; /* Read buffer */
|
||
|
uint16 status; /* VDP status flags */
|
||
|
uint16 ntab; /* Name table A base address */
|
||
|
uint16 ntbb; /* Name table B base address */
|
||
|
uint16 ntwb; /* Name table W base address */
|
||
|
uint16 satb; /* Sprite attribute table base address */
|
||
|
uint16 hscb; /* Horizontal scroll table base address */
|
||
|
uint16 sat_base_mask; /* Base bits of SAT */
|
||
|
uint16 sat_addr_mask; /* Index bits of SAT */
|
||
|
uint8 is_color_dirty; /* 1= One or more colors have changed */
|
||
|
uint8 color_dirty[0x40]; /* 1= This color is dirty */
|
||
|
uint8 border; /* Border color index */
|
||
|
uint8 is_border_dirty; /* 1= The border color has changed */
|
||
|
//uint8 bg_name_dirty[0x800]; /* 1= This pattern is dirty */
|
||
|
//uint16 bg_name_list[0x800]; /* List of modified pattern indices */
|
||
|
uint16 bg_list_index; /* # of modified patterns in list */
|
||
|
//uint8 bg_pattern_cache[0x80000];/* Cached and flipped patterns */
|
||
|
uint8 playfield_shift; /* Width of planes A, B (in bits) */
|
||
|
uint8 playfield_col_mask; /* Vertical scroll mask */
|
||
|
uint16 playfield_row_mask; /* Horizontal scroll mask */
|
||
|
uint32 y_mask; /* Name table Y-index bits mask */
|
||
|
int hint_pending; /* 0= Line interrupt is pending */
|
||
|
int vint_pending; /* 1= Frame interrupt is pending */
|
||
|
int counter; /* Raster counter */
|
||
|
int dma_fill; /* 1= DMA fill has been requested */
|
||
|
int im2_flag; /* 1= Interlace mode 2 is being used */
|
||
|
int frame_end; /* End-of-frame (IRQ line) */
|
||
|
int v_counter; /* VDP scan line counter */
|
||
|
int v_update; /* 1= VC was updated by a ctrl or HV read */
|
||
|
void (*color_update)(int index, uint16 data);
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
/* Init, reset, shutdown functions */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
|
||
|
void vdp_init(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void vdp_reset(void)
|
||
|
{
|
||
|
memset(sat, 0, sizeof(sat));
|
||
|
memset(vram, 0, VRAM_SIZE);
|
||
|
memset(cram, 0, sizeof(cram));
|
||
|
memset(vsram, 0, sizeof(vsram));
|
||
|
memset(reg, 0, sizeof(reg));
|
||
|
|
||
|
addr = addr_latch = code = pending = buffer = status = 0;
|
||
|
ntab = ntbb = ntwb = satb = hscb = 0;
|
||
|
sat_base_mask = 0xFE00;
|
||
|
sat_addr_mask = 0x01FF;
|
||
|
|
||
|
/* Mark all colors as dirty to force a palette update */
|
||
|
is_color_dirty = 1;
|
||
|
memset(color_dirty, 1, 0x40);
|
||
|
border = 0x00;
|
||
|
is_border_dirty = 1;
|
||
|
|
||
|
memset(bg_name_dirty, 0, BGNAMEDIRTY_SIZE);
|
||
|
memset(bg_name_list, 0, BGNAME_SIZE);
|
||
|
bg_list_index = 0;
|
||
|
memset(bg_pattern_cache, 0, BGPATTERN_CACH_SIZE);
|
||
|
|
||
|
playfield_shift = 6;
|
||
|
playfield_col_mask = 0x1F;
|
||
|
playfield_row_mask = 0x0FF;
|
||
|
y_mask = 0x1FC0;
|
||
|
|
||
|
hint_pending = vint_pending = 0;
|
||
|
counter = 0;
|
||
|
frame_end = 0xE0;
|
||
|
v_counter = v_update = 0;
|
||
|
|
||
|
/* Initialize viewport */
|
||
|
gbitmap.viewport.x = 0x20;
|
||
|
gbitmap.viewport.y = 0x20;
|
||
|
gbitmap.viewport.w = 256;
|
||
|
gbitmap.viewport.h = 224;
|
||
|
gbitmap.viewport.oh = 256;
|
||
|
gbitmap.viewport.ow = 224;
|
||
|
gbitmap.viewport.changed = 1;
|
||
|
}
|
||
|
|
||
|
void vdp_shutdown(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
/* Memory access functions */
|
||
|
/*--------------------------------------------------------------------------*/
|
||
|
|
||
|
void vdp_ctrl_w(uint16 data)
|
||
|
{
|
||
|
if(pending == 0)
|
||
|
{
|
||
|
if((data & 0xC000) == 0x8000)
|
||
|
{
|
||
|
uint8 r = (data >> 8) & 0x1F;
|
||
|
uint8 d = (data >> 0) & 0xFF;
|
||
|
vdp_reg_w(r, d);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pending = 1;
|
||
|
}
|
||
|
|
||
|
addr = ((addr_latch & 0xC000) | (data & 0x3FFF)) & 0xFFFF;
|
||
|
code = ((code & 0x3C) | ((data >> 14) & 0x03)) & 0x3F;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Clear pending flag */
|
||
|
pending = 0;
|
||
|
|
||
|
/* Update address and code registers */
|
||
|
addr = ((addr & 0x3FFF) | ((data & 3) << 14)) & 0xFFFF;
|
||
|
code = ((code & 0x03) | ((data >> 2) & 0x3C)) & 0x3F;
|
||
|
|
||
|
/* Save address bits A15 and A14 */
|
||
|
addr_latch = (addr & 0xC000);
|
||
|
|
||
|
if((code & 0x20) && (reg[1] & 0x10))
|
||
|
{
|
||
|
switch(reg[23] & 0xC0)
|
||
|
{
|
||
|
case 0x00: /* V bus to VDP DMA */
|
||
|
case 0x40: /* V bus to VDP DMA */
|
||
|
dma_vbus();
|
||
|
break;
|
||
|
|
||
|
case 0x80: /* VRAM fill */
|
||
|
dma_fill = 1;
|
||
|
break;
|
||
|
|
||
|
case 0xC0: /* VRAM copy */
|
||
|
dma_copy();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint16 vdp_ctrl_r(void)
|
||
|
{
|
||
|
#if 0
|
||
|
int cycles = m68k_cycles_run();
|
||
|
uint16 temp = status;
|
||
|
|
||
|
/* Clear pending flag */
|
||
|
pending = 0;
|
||
|
|
||
|
/* VBlank flag is set when the screen is disabled */
|
||
|
if((reg[1] & 0x40) == 0x00)
|
||
|
{
|
||
|
temp |= 0x0008;
|
||
|
}
|
||
|
|
||
|
/* Clear collision flag on reads */
|
||
|
status &= ~0x0020;
|
||
|
|
||
|
/* Set HBlank flag based on H counter */
|
||
|
if(reg[12] & 1)
|
||
|
{
|
||
|
int hc = cycle2hc40[(cycles % 487)];
|
||
|
if((hc >= 0xB6) && (hc <= 0xFF)) temp |= 0x0004;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int hc = cycle2hc32[(cycles % 487)];
|
||
|
if((hc >= 0x93) && (hc <= 0xFF)) temp |= 0x0004;
|
||
|
}
|
||
|
|
||
|
/* Flag FIFO as empty */
|
||
|
temp |= 0x0200;
|
||
|
|
||
|
/* Clear unused bits */
|
||
|
temp &= 0x03FF;
|
||
|
|
||
|
return (temp);
|
||
|
#else
|
||
|
uint16 temp = (0x4e71 & 0xFC00);
|
||
|
pending = 0;
|
||
|
status &= ~0x0020; // clear sprite hit flag on reads
|
||
|
status |= 0x0200; // fifo empty
|
||
|
status ^= 0x0004; /* hack (red zone) */
|
||
|
temp |= (status & 0x03BF); // clear spr over
|
||
|
return (temp);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
void vdp_data_w(uint16 data)
|
||
|
{
|
||
|
/* Clear pending flag */
|
||
|
pending = 0;
|
||
|
|
||
|
switch(code & 0x0F)
|
||
|
{
|
||
|
case 0x01: /* VRAM */
|
||
|
/* Byte-swap data if A0 is set */
|
||
|
if(addr & 1) data = (data >> 8) | (data << 8);
|
||
|
|
||
|
/* Copy SAT data to the internal SAT */
|
||
|
if((addr & sat_base_mask) == satb)
|
||
|
{
|
||
|
*(uint16 *)&sat[addr & sat_addr_mask] = data;
|
||
|
}
|
||
|
|
||
|
/* Only write unique data to VRAM */
|
||
|
if(data != *(uint16 *)&vram[addr & 0xFFFE])
|
||
|
{
|
||
|
/* Write data to VRAM */
|
||
|
*(uint16 *)&vram[addr & 0xFFFE] = data;
|
||
|
|
||
|
/* Update the pattern cache */
|
||
|
MARK_BG_DIRTY(addr);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 0x03: /* CRAM */
|
||
|
{
|
||
|
uint16 *p = (uint16 *)&cram[(addr & 0x7E)];
|
||
|
data &= 0x0EEE;
|
||
|
if(data != *p)
|
||
|
{
|
||
|
int index = (addr >> 1) & 0x3F;
|
||
|
*p = PACK_CRAM(data);
|
||
|
|
||
|
if((index & 0x0F) != 0x00)
|
||
|
{
|
||
|
color_dirty[index] = is_color_dirty = 1;
|
||
|
}
|
||
|
|
||
|
if(index == border)
|
||
|
{
|
||
|
is_border_dirty = 1;
|
||
|
if(color_update)
|
||
|
{
|
||
|
color_update(0x00, *p);
|
||
|
color_update(0x40, *p);
|
||
|
color_update(0x80, *p);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(color_update) color_update(index, *p);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 0x05: /* VSRAM */
|
||
|
*(uint16 *)&vsram[(addr & 0x7E)] = data;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Bump address register */
|
||
|
addr += reg[15];
|
||
|
|
||
|
if(dma_fill)
|
||
|
{
|
||
|
int length = (reg[20] << 8 | reg[19]) & 0xFFFF;
|
||
|
if(!length) length = 0x10000;
|
||
|
|
||
|
do {
|
||
|
vram[(addr & 0xFFFF)] = (data >> 8) & 0xFF;
|
||
|
MARK_BG_DIRTY(addr);
|
||
|
addr += reg[15];
|
||
|
} while(--length);
|
||
|
dma_fill = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
uint16 vdp_data_r(void)
|
||
|
{
|
||
|
uint16 temp = 0;
|
||
|
|
||
|
/* Clear pending flag */
|
||
|
pending = 0;
|
||
|
|
||
|
switch(code & 0x0F)
|
||
|
{
|
||
|
case 0x00: /* VRAM */
|
||
|
temp = *(uint16 *)&vram[(addr & 0xFFFE)];
|
||
|
break;
|
||
|
|
||
|
case 0x08: /* CRAM */
|
||
|
temp = *(uint16 *)&cram[(addr & 0x7E)];
|
||
|
temp = UNPACK_CRAM(temp);
|
||
|
break;
|
||
|
|
||
|
case 0x04: /* VSRAM */
|
||
|
temp = *(uint16 *)&vsram[(addr & 0x7E)];
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Bump address register */
|
||
|
addr += reg[15];
|
||
|
|
||
|
/* return data */
|
||
|
return (temp);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
The reg[] array is updated at the *end* of this function, so the new
|
||
|
register data can be compared with the previous data.
|
||
|
*/
|
||
|
void vdp_reg_w(uint8 r, uint8 d)
|
||
|
{
|
||
|
switch(r)
|
||
|
{
|
||
|
case 0x00: /* CTRL #1 */
|
||
|
|
||
|
if(hint_pending)
|
||
|
{
|
||
|
if(d & 0x10)
|
||
|
{
|
||
|
m68k_set_irq(4);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Cancel pending level 4 interrupt */
|
||
|
m68k_set_irq(0);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 0x01: /* CTRL #2 */
|
||
|
|
||
|
if(vint_pending)
|
||
|
{
|
||
|
if(d & 0x20)
|
||
|
{
|
||
|
m68k_set_irq(6);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Cancel pending level 6 interrupt */
|
||
|
m68k_set_irq(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Change the frame timing */
|
||
|
frame_end = (d & 8) ? 0xF0 : 0xE0;
|
||
|
|
||
|
/* Check if the viewport height has actually been changed */
|
||
|
if((reg[1] & 8) != (d & 8))
|
||
|
{
|
||
|
/* Update the height of the viewport */
|
||
|
gbitmap.viewport.oh = gbitmap.viewport.h;
|
||
|
gbitmap.viewport.h = (d & 8) ? 240 : 224;
|
||
|
gbitmap.viewport.changed = 1;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 0x02: /* NTAB */
|
||
|
ntab = (d << 10) & 0xE000;
|
||
|
break;
|
||
|
|
||
|
case 0x03: /* NTWB */
|
||
|
ntwb = (d << 10) & 0xF800;
|
||
|
if(reg[12] & 1) ntwb &= 0xF000;
|
||
|
break;
|
||
|
|
||
|
case 0x04: /* NTBB */
|
||
|
ntbb = (d << 13) & 0xE000;
|
||
|
break;
|
||
|
|
||
|
case 0x05: /* SATB */
|
||
|
sat_base_mask = (reg[12] & 1) ? 0xFC00 : 0xFE00;
|
||
|
sat_addr_mask = (reg[12] & 1) ? 0x03FF : 0x01FF;
|
||
|
satb = (d << 9) & sat_base_mask;
|
||
|
break;
|
||
|
|
||
|
case 0x07:
|
||
|
d &= 0x3F;
|
||
|
|
||
|
/* Check if the border color has actually changed */
|
||
|
if(border != d)
|
||
|
{
|
||
|
/* Mark the border color as modified */
|
||
|
border = d;
|
||
|
is_border_dirty = 1;
|
||
|
if(color_update)
|
||
|
{
|
||
|
color_update(0x00, *(uint16 *)&cram[(border << 1)]);
|
||
|
color_update(0x40, *(uint16 *)&cram[(border << 1)]);
|
||
|
color_update(0x80, *(uint16 *)&cram[(border << 1)]);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 0x0C:
|
||
|
|
||
|
/* Check if the viewport width has actually been changed */
|
||
|
if((reg[0x0C] & 1) != (d & 1))
|
||
|
{
|
||
|
/* Update the width of the viewport */
|
||
|
gbitmap.viewport.ow = gbitmap.viewport.w;
|
||
|
gbitmap.viewport.w = (d & 1) ? 320 : 256;
|
||
|
gbitmap.viewport.changed = 1;
|
||
|
}
|
||
|
|
||
|
/* See if the S/TE mode bit has changed */
|
||
|
if((reg[0x0C] & 8) != (d & 8))
|
||
|
{
|
||
|
int i;
|
||
|
reg[0x0C] = d;
|
||
|
|
||
|
/* Update colors */
|
||
|
if(color_update)
|
||
|
{
|
||
|
for(i = 0; i < 0x40; i += 1)
|
||
|
{
|
||
|
color_update(i, *(uint16 *)&cram[i << 1]);
|
||
|
}
|
||
|
color_update(0x00, *(uint16 *)&cram[border << 1]);
|
||
|
color_update(0x40, *(uint16 *)&cram[border << 1]);
|
||
|
color_update(0x80, *(uint16 *)&cram[border << 1]);
|
||
|
}
|
||
|
/* Flush palette */
|
||
|
is_color_dirty = is_border_dirty = 1;
|
||
|
memset(color_dirty, 1, 0x40);
|
||
|
}
|
||
|
|
||
|
/* Check interlace mode 2 setting */
|
||
|
im2_flag = ((d & 0x06) == 0x06) ? 1 : 0;
|
||
|
|
||
|
/* The following register updates check this value */
|
||
|
reg[0x0C] = d;
|
||
|
|
||
|
/* Update display-dependant registers */
|
||
|
vdp_reg_w(0x03, reg[0x03]);
|
||
|
vdp_reg_w(0x05, reg[0x05]);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 0x0D: /* HSCB */
|
||
|
hscb = (d << 10) & 0xFC00;
|
||
|
break;
|
||
|
|
||
|
case 0x10: /* Playfield size */
|
||
|
playfield_shift = shift_table[(d & 3)];
|
||
|
playfield_col_mask = col_mask_table[(d & 3)];
|
||
|
playfield_row_mask = row_mask_table[(d >> 4) & 3];
|
||
|
y_mask = y_mask_table[(d & 3)];
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Write new register value */
|
||
|
reg[r] = d;
|
||
|
}
|
||
|
|
||
|
|
||
|
uint16 vdp_hvc_r(void)
|
||
|
{
|
||
|
int cycles = m68k_cycles_run();
|
||
|
uint8 *hctab = (reg[12] & 1) ? cycle2hc40 : cycle2hc32;
|
||
|
int vc = vc28[v_counter];
|
||
|
int hc = hctab[(cycles % 487)];
|
||
|
return (vc << 8 | hc);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
VRAM to VRAM copy
|
||
|
Read byte from VRAM (source), write to VRAM (addr),
|
||
|
bump source and add r15 to addr.
|
||
|
|
||
|
- see how source addr is affected
|
||
|
(can it make high source byte inc?)
|
||
|
*/
|
||
|
void dma_copy(void)
|
||
|
{
|
||
|
int length = (reg[20] << 8 | reg[19]) & 0xFFFF;
|
||
|
int source = (reg[22] << 8 | reg[21]) & 0xFFFF;
|
||
|
if(!length) length = 0x10000;
|
||
|
|
||
|
do {
|
||
|
uint8 temp = vram[source];
|
||
|
vram[addr] = temp;
|
||
|
MARK_BG_DIRTY(addr);
|
||
|
source = (source + 1) & 0xFFFF;
|
||
|
addr = (addr + reg[15]) & 0xFFFF;
|
||
|
} while (--length);
|
||
|
|
||
|
reg[19] = (length >> 0) & 0xFF;
|
||
|
reg[20] = (length >> 8) & 0xFF;
|
||
|
}
|
||
|
|
||
|
|
||
|
void dma_vbus(void)
|
||
|
{
|
||
|
uint32 base, source = ((reg[23] & 0x7F) << 17 | reg[22] << 9 | reg[21] << 1) & 0xFFFFFE;
|
||
|
uint32 length = (reg[20] << 8 | reg[19]) & 0xFFFF;
|
||
|
|
||
|
if(!length) length = 0x10000;
|
||
|
base = source;
|
||
|
|
||
|
do {
|
||
|
uint16 temp = vdp_dma_r(source);
|
||
|
source += 2;
|
||
|
source = ((base & 0xFE0000) | (source & 0x1FFFF));
|
||
|
vdp_data_w(temp);
|
||
|
} while (--length);
|
||
|
|
||
|
reg[19] = (length >> 0) & 0xFF;
|
||
|
reg[20] = (length >> 8) & 0xFF;
|
||
|
|
||
|
reg[21] = (source >> 1) & 0xFF;
|
||
|
reg[22] = (source >> 9) & 0xFF;
|
||
|
reg[23] = (reg[23] & 0x80) | ((source >> 17) & 0x7F);
|
||
|
}
|
||
|
|
||
|
|
||
|
void vdp_test_w(uint16 value)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|