Skip to content

Commit

Permalink
parse sprites when screen disabled, check sprite collision regardless…
Browse files Browse the repository at this point in the history
… of bg prio

parsing the spirtes even when the screen is disabled means that sprite overflow
can occur.
this was needed to pass one of the vdptests.

see #29
  • Loading branch information
ITotalJustice committed Jul 9, 2022
1 parent d0e6767 commit c1041a8
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 55 deletions.
3 changes: 1 addition & 2 deletions src/core/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,7 @@ struct SMS_Vdp
{
// this is used for vram r/w and cram writes.
uint16_t addr;
// see [enum VDP_Code]
uint8_t code;
enum VDP_Code code;

uint8_t vram[1024 * 16];
bool dirty_vram[(1024 * 16) / 4];
Expand Down
125 changes: 72 additions & 53 deletions src/core/vdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ enum
SPRITE_EOF = 208,
};

// SOURCE: https://www.smspower.org/forums/8161-SMSDisplayTiming
// (divide mclks by 3)
// 59,736 (179,208 mclks, 228x262)
// frame_int 202 cycles into line 192 (607 mclks)
// line_int 202 cycles into triggering (608 mclks)

static FORCE_INLINE bool vdp_is_line_irq_wanted(const struct SMS_Core* sms)
{
Expand Down Expand Up @@ -239,10 +244,6 @@ void vdp_io_write(struct SMS_Core* sms, const uint8_t addr, const uint8_t value)
assert(value == 0xFF && "colour table bits not all set");
break;

case 0x4:
assert((value & 0xF) == 0xF && "Background Pattern Generator Base Address");
break;

// unused registers
case 0xB:
case 0xC:
Expand Down Expand Up @@ -767,6 +768,8 @@ static void vdp_render_sprites(struct SMS_Core* sms, pixel_width_t* scanline, co
const struct CachedPalette cpal = vdp_get_palette(sms, pattern_index);
const uint32_t palette = cpal.normal;//horizontal_flip ? cpal.flipped : cpal.normal;

// note: the order of the below ifs are important.
// opaque sprites can collide, even when behind background/
for (uint8_t x = 0; x < 8; ++x)
{
const int16_t x_index = x + sprite_x;
Expand All @@ -782,12 +785,6 @@ static void vdp_render_sprites(struct SMS_Core* sms, pixel_width_t* scanline, co
break;
}

// skip is bg has priority
if (prio->array[x_index])
{
continue;
}

const uint8_t palette_index = (palette >> (28 - (4 * x))) & 0xF;

// for sprites, pal0 is transparent
Expand All @@ -806,6 +803,12 @@ static void vdp_render_sprites(struct SMS_Core* sms, pixel_width_t* scanline, co
// keep track of this sprite already being rendered
drawn_sprites[x_index] = true;

// skip is bg has priority
if (prio->array[x_index])
{
continue;
}

// sprite cram index is the upper 16-bytes!
scanline[x_index] = VDP.colour[palette_index + 16];
}
Expand Down Expand Up @@ -932,17 +935,10 @@ void vdp_mark_palette_dirty(struct SMS_Core* sms)

bool vdp_has_interrupt(const struct SMS_Core* sms)
{
if (VDP.frame_interrupt_pending && vdp_is_vblank_irq_wanted(sms))
{
return true;
}
const bool frame_interrupt = VDP.frame_interrupt_pending && vdp_is_vblank_irq_wanted(sms);
const bool line_interrupt = VDP.line_interrupt_pending && vdp_is_line_irq_wanted(sms);

if (VDP.line_interrupt_pending && vdp_is_line_irq_wanted(sms))
{
return true;
}

return false;
return frame_interrupt || line_interrupt;
}

static void vdp_advance_line_counter(struct SMS_Core* sms)
Expand All @@ -963,50 +959,58 @@ static void vdp_advance_line_counter(struct SMS_Core* sms)

static void vdp_render_frame(struct SMS_Core* sms)
{
// only render if enabled and we have pixels
if (!vdp_is_display_enabled(sms) || !sms->pixels || sms->skip_frame)
// only render if display is enabled
if (!vdp_is_display_enabled(sms))
{
// on sms/gg, sprite overflow still happens with display disabled
if (!SMS_is_system_type_sg(sms))
{
vdp_parse_sprites(sms);
}
return;
}

if (vdp_is_display_active(sms))
// exit early if we have no pixels (this will break games that need sprite overflow and collision)
if (!sms->pixels || sms->skip_frame)
{
struct PriorityBuf prio = {0};
#ifndef SMS_PIXEL_WIDTH
pixel_width_t scanline[SMS_SCREEN_WIDTH] = {0};
#else
pixel_width_t* scanline = (pixel_width_t*)sms->pixels + (VDP.vcount * sms->pitch);
#endif
return;
}

if (SMS_is_system_type_sg(sms))
{
// this isn't correct, but it works :)
if ((VDP.registers[0] & 0x7) == 0)
{
vdp_mode1_render_background(sms, scanline);
}
else
{
vdp_mode2_render_background(sms, scanline);
}
struct PriorityBuf prio = {0};
#ifndef SMS_PIXEL_WIDTH
pixel_width_t scanline[SMS_SCREEN_WIDTH] = {0};
#else
pixel_width_t* scanline = (pixel_width_t*)sms->pixels + (VDP.vcount * sms->pitch);
#endif

vdp_mode1_render_sprites(sms, scanline);
if (SMS_is_system_type_sg(sms))
{
// this isn't correct, but it works :)
if ((VDP.registers[0] & 0x7) == 0)
{
vdp_mode1_render_background(sms, scanline);
}
else // sms / gg render
else
{
vdp_render_background(sms, scanline, &prio);
vdp_render_sprites(sms, scanline, &prio);
vdp_mode2_render_background(sms, scanline);
}

#ifndef SMS_PIXEL_WIDTH
write_scanline_to_frame(sms, scanline, VDP.vcount);
#endif
vdp_mode1_render_sprites(sms, scanline);
}
else // sms / gg render
{
vdp_render_background(sms, scanline, &prio);
vdp_render_sprites(sms, scanline, &prio);
}

#ifndef SMS_PIXEL_WIDTH
write_scanline_to_frame(sms, scanline, VDP.vcount);
#endif
}

static void vdp_tick(struct SMS_Core* sms)
{
if (LIKELY(VDP.vcount < 192))
if (LIKELY(vdp_is_display_active(sms)))
{
vdp_update_palette(sms);
vdp_render_frame(sms);
Expand Down Expand Up @@ -1073,14 +1077,29 @@ void vdp_run(struct SMS_Core* sms, const uint8_t cycles)
void vdp_init(struct SMS_Core* sms)
{
memset(&VDP, 0, sizeof(VDP));
// i think unused regs return 0xFF?
memset(VDP.registers, 0xFF, sizeof(VDP.registers));
// update palette
vdp_mark_palette_dirty(sms);

VDP.registers[0x0] = 0x36;
VDP.registers[0x1] = 0x80;
VDP.registers[0x6] = 0xFB;
// values on starup
VDP.registers[0x0] = 0x04; // %00000100 (taken from VDPTEST)
VDP.registers[0x1] = 0x20; // %00100000 (taken from VDPTEST)
VDP.registers[0x2] = 0xF1; // %11110001 (taken from VDPTEST)
VDP.registers[0x3] = 0xFF; // %11111111 (taken from VDPTEST)
VDP.registers[0x4] = 0x03; // %00000011 (taken from VDPTEST)
VDP.registers[0x5] = 0x81; // %10000001 (taken from VDPTEST)
VDP.registers[0x6] = 0xFB; // %11111011 (taken from VDPTEST)
VDP.registers[0x7] = 0x00; // %00000000 (taken from VDPTEST)
VDP.registers[0x8] = 0x00; // %00000000 (taken from VDPTEST)
VDP.registers[0x9] = 0x00; // %00000000 (taken from VDPTEST)
VDP.registers[0xA] = 0xFF; // %11111111 (taken from VDPTEST)
// vdp registers are write-only, so the the values of 0xB-0xF don't matter

if (1) // values after bios (todo: optional bios skip)
{
VDP.registers[0x0] = 0x36;
VDP.registers[0x1] = 0x80;
VDP.registers[0x6] = 0xFB;
}

VDP.line_counter = 0xFF;
}

0 comments on commit c1041a8

Please sign in to comment.