commodore/c65.cpp: basic sprite drawing

This commit is contained in:
angelosa 2024-08-06 18:54:41 +02:00
parent 061098f2ed
commit 15ea86995e

View File

@ -357,12 +357,23 @@ private:
uint8_t m_VIC3_ControlA = 0U;
uint8_t m_VIC3_ControlB = 0U;
bool m_video_enable = false;
struct {
u16 x;
u8 y;
u8 clut;
}m_sprite[16];
u8 m_sprite_hi_xoffs;
u8 m_sprite_exp_x, m_sprite_exp_y;
u8 m_sprite_multicolor_enable;
u8 m_sprite_multicolor_clut[2];
bool m_blink_enable = false;
bitmap_ind16 m_bitmap;
emu_timer *m_scanline_timer;
TIMER_CALLBACK_MEMBER(scanline_cb);
std::tuple<u8, bool> get_tile_pixel(int y, int x);
std::tuple<u8, u8, bool> get_sprite_pixel(int y, int x);
void PalEntryFlush(uint8_t offset);
void IRQCheck(uint8_t irq_cause);
};
@ -381,16 +392,47 @@ void c65_state::video_reset()
void c65_state::vic4567_map(address_map &map)
{
// map(0x00, 0x0f) Sprite X/Y
// map(0x10, 0x10) bit 8 for Sprites X pos
/*
* x--- ---- bit 8 of beam V
* -x-- ---- Extended Color Mode
* --x- ---- Bitmap Mode
* ---x ---- Enable video output
* ---- x--- 25/24 visible rows
* ---- -xxx Screen Soft V Scroll
*/
// 53248/$d000 - 53263/$d00f Sprite X/Y
map(0x00, 0x00).select(0xe).lrw8(
NAME([this] (offs_t offset){
return m_sprite[offset >> 1].x & 0xff;
}),
NAME([this] (offs_t offset, u8 data) {
m_sprite[offset >> 1].x &= 0x100;
m_sprite[offset >> 1].x |= data;
})
);
map(0x01, 0x01).select(0xe).lrw8(
NAME([this] (offs_t offset){
return m_sprite[offset >> 1].y;
}),
NAME([this] (offs_t offset, u8 data) {
m_sprite[offset >> 1].y = data;
})
);
// 53264/$d010 bit 8 for Sprites X pos
map(0x10, 0x10).lrw8(
NAME([this] (offs_t offset) {
return m_sprite_hi_xoffs;
}),
NAME([this] (offs_t offset, u8 data) {
m_sprite_hi_xoffs = data;
for (int i = 0; i < 8; i++)
{
m_sprite[i].x &= 0xff;
m_sprite[i].x |= BIT(data, i) << 8;
}
})
);
/*
* 53265/$d011
* x--- ---- bit 8 of beam V
* -x-- ---- Extended Color Mode
* --x- ---- Bitmap Mode
* ---x ---- Enable video output
* ---- x--- 25/24 visible rows
* ---- -xxx Screen Soft V Scroll
*/
map(0x11, 0x11).lrw8(
NAME([this] (offs_t offset) {
return (m_screen->vpos() & 0x100) >> 1;
@ -414,17 +456,25 @@ void c65_state::vic4567_map(address_map &map)
m_sprite_enable = data;
})
);
/*
* ---x ---- Multicolor Mode
* ---- x--- 40/38 visible columns
* ---- -xxx Screen Soft Scroll H
*/
/*
* ---x ---- Multicolor Mode
* ---- x--- 40/38 visible columns
* ---- -xxx Screen Soft Scroll H
*/
// map(0x16, 0x16)
// map(0x17, 0x17) Sprite magnify V
/*
* xxxx ---- Screen RAM base (note bit 4 ignored in C=65 width 80)
* ---- xxx- Character Set base
*/
// 53271/$d017 Sprite magnify V
map(0x17, 0x17).lrw8(
NAME([this] (offs_t offset) {
return m_sprite_exp_y;
}),
NAME([this] (offs_t offset, u8 data) {
m_sprite_exp_y = data;
})
);
/*
* xxxx ---- Screen RAM base (note bit 4 ignored in C=65 width 80)
* ---- xxx- Character Set base
*/
map(0x18, 0x18).lrw8(
NAME([this] (offs_t offset) {
return m_VIC2_VS_CB_Base;
@ -433,13 +483,13 @@ void c65_state::vic4567_map(address_map &map)
m_VIC2_VS_CB_Base = data;
})
);
/*
* x--- ---- Latches high if any IRQ taken below (valid for pending reg only)
* ---- x--- Lightpen input IRQ
* ---- -x-- Sprite-Sprite collision IRQ
* ---- --x- Sprite-Background collision IRQ
* ---- ---x rasterline IRQ
*/
/*
* x--- ---- Latches high if any IRQ taken below (valid for pending reg only)
* ---- x--- Lightpen input IRQ
* ---- -x-- Sprite-Sprite collision IRQ
* ---- --x- Sprite-Background collision IRQ
* ---- ---x rasterline IRQ
*/
map(0x19, 0x19).lrw8(
NAME([this] (offs_t offset) {
return m_VIC2_IRQPend;
@ -459,10 +509,28 @@ void c65_state::vic4567_map(address_map &map)
})
);
// map(0x1b, 0x1b) Sprite-Background priority
// map(0x1c, 0x1c) Sprite multicolor enable
// map(0x1d, 0x1d) Sprite magnify X
// 53276/$d01c Sprite multicolor enable
map(0x1c, 0x1c).lrw8(
NAME([this] (offs_t offset) {
return m_sprite_multicolor_enable;
}),
NAME([this] (offs_t offset, u8 data) {
m_sprite_multicolor_enable = data;
})
);
// 53277/$d01d Sprite magnify X
map(0x1d, 0x1d).lrw8(
NAME([this] (offs_t offset) {
return m_sprite_exp_x;
}),
NAME([this] (offs_t offset, u8 data) {
m_sprite_exp_x = data;
})
);
// map(0x1e, 0x1e) Sprite-Sprite collision
// map(0x1f, 0x1f) Spirte-background collision
// 53280,$d020 border color
map(0x20, 0x20).lrw8(
NAME([this] (offs_t offset) {
return m_VIC2_EXTColor;
@ -482,25 +550,41 @@ void c65_state::vic4567_map(address_map &map)
})
);
// map(0x21, 0x24) background clut
// map(0x25, 0x26) sprite multicolor clut
// map(0x27, 0x2e) sprite color clut
/*
* KEY register, handles vic-iii and vic-ii modes via two consecutive writes
* 0xa5 -> 0x96 vic-iii mode
* any other write vic-ii mode (changes base memory map)
* vic-iv (MEGA65/C65GS) also has another KEY init sequence
*/
// 53285/$d025 - 53286/$d026 sprite multicolor clut
map(0x25, 0x26).lrw8(
NAME([this] (offs_t offset) {
return m_sprite_multicolor_clut[offset];
}),
NAME([this] (offs_t offset, u8 data) {
m_sprite_multicolor_clut[offset] = data & 0xf;
})
);
// 53287/$d027 - 53294/$d02e sprite color clut
map(0x27, 0x2e).lrw8(
NAME([this] (offs_t offset) {
return m_sprite[offset].clut;
}),
NAME([this] (offs_t offset, u8 data) {
m_sprite[offset].clut = data & 0xf;
})
);
/*
* KEY register, handles vic-iii and vic-ii modes via two consecutive writes
* 0xa5 -> 0x96 vic-iii mode
* any other write vic-ii mode (changes base memory map)
* vic-iv (MEGA65/C65GS) also has another KEY init sequence
*/
// map(0x2f, 0x2f)
/*
* x--- ---- overlay ROM at $e000
* -x-- ---- Move CROM at @9000
* --x- ---- overlay ROM at $c000
* ---x ---- overlay ROM at $a000
* ---- x--- overlay ROM at $8000
* ---- -x-- read PALette from [P]ROM
* ---- --x- EXT SYNC
* ---- ---x overlay CRAM at $dc00
*/
/*
* x--- ---- overlay ROM at $e000
* -x-- ---- Move CROM at @9000
* --x- ---- overlay ROM at $c000
* ---x ---- overlay ROM at $a000
* ---- x--- overlay ROM at $8000
* ---- -x-- read PALette from [P]ROM
* ---- --x- EXT SYNC
* ---- ---x overlay CRAM at $dc00
*/
map(0x30, 0x30).lrw8(
NAME([this] (offs_t offset) {
return m_VIC3_ControlA;
@ -517,16 +601,16 @@ void c65_state::vic4567_map(address_map &map)
logerror("\tROM @ e000 %d\n", BIT(data, 7));
})
);
/*
* x--- ---- H640
* -x-- ---- FAST
* --x- ---- ATTR
* ---x ---- BPM Bitplane Mode
* ---- x--- V400
* ---- -x-- H1280
* ---- --x- MONO
* ---- ---x INT
*/
/*
* x--- ---- H640
* -x-- ---- FAST
* --x- ---- ATTR
* ---x ---- BPM Bitplane Mode
* ---- x--- V400
* ---- -x-- H1280
* ---- --x- MONO
* ---- ---x INT
*/
map(0x31, 0x31).lrw8(
NAME([this] (offs_t offset) {
return m_VIC3_ControlB;
@ -553,11 +637,11 @@ void c65_state::vic4567_map(address_map &map)
std::tuple<u8, bool> c65_state::get_tile_pixel(int y, int x)
{
// TODO: move width as a screen setup
// TODO: move width as a screen setup
int pixel_width = (m_VIC3_ControlB & 0x80) ? 1 : 2;
int columns = 80 / pixel_width;
// TODO: Move init of these two in handlers
// TODO: Move init of these two in handlers
uint8_t *cptr = &m_ipl_rom->base()[((m_VIC3_ControlA & 0x40) ? 0x9000: 0xd000) + ((m_VIC2_VS_CB_Base & 0x2) << 10)];
const u32 base_offs = (m_VIC2_VS_CB_Base & 0xf0) << 6;
@ -581,16 +665,71 @@ std::tuple<u8, bool> c65_state::get_tile_pixel(int y, int x)
return std::make_tuple(highlight_color + ((enable_dot) ? foreground_color : background_color), enable_dot != 0);
}
std::tuple<u8, u8, bool> c65_state::get_sprite_pixel(int y, int x)
{
if (!m_sprite_enable)
return std::make_tuple(0, 0, false);
u8 enable_dot = 0;
u8 sprite_mask = 0;
const u32 base_offs = ((m_VIC2_VS_CB_Base & 0xf0) << 6) + 0x3f8;
// sprite #7 < #6 < ... < #0
for (int i = 7; i >= 0; i--)
{
if (!BIT(m_sprite_enable, i))
continue;
const int y_width = 0 + BIT(m_sprite_exp_y, i);
const int ysize = 21 << y_width;
const u16 yi = m_sprite[i].y;
if (!(y >= yi && y < yi + ysize))
continue;
const int x_width = 1 + BIT(m_sprite_exp_x, i);
const int xsize = 24 << x_width;
const u16 xi = m_sprite[i].x << 1;
if (!(x >= xi && x < xi + xsize))
continue;
u32 sprite_offset = m_workram[base_offs + i] << 6;
const int xm = (x - xi) >> x_width;
const int ym = (y - yi) >> y_width;
//if (i == 0) {
//printf("%d %08x %d %d|%d %d|%d %d\n", i, sprite_offset, xm, ym, xi, yi, x, y);
////machine().debug_break();
//}
u8 sprite_data = m_workram[(ym * 3) + (xm >> 3) + sprite_offset];
const bool is_multicolor = bool(BIT(m_sprite_multicolor_enable, i));
const u8 dot_mask = is_multicolor << 1 | 1;
const u8 shift_mask = 7 - is_multicolor;
const u8 color_shift = !is_multicolor;
u8 sprite_dot = (sprite_data >> (shift_mask - (xm & shift_mask))) & dot_mask;
if (sprite_dot)
{
const std::array<u8, 4> color_map = { 0, m_sprite_multicolor_clut[0], m_sprite[i].clut, m_sprite_multicolor_clut[1] };
sprite_mask |= 1 << i;
enable_dot = color_map[sprite_dot << color_shift];
}
}
return std::make_tuple(enable_dot, sprite_mask, enable_dot != 0);
}
TIMER_CALLBACK_MEMBER(c65_state::scanline_cb)
{
int y = param;
uint16_t *p = &m_bitmap.pix(y);
//popmessage("%02x %02x %02x", m_sprite_enable, m_sprite[0].x, m_sprite[0].y);
const int border_left = 0;
const int active_left = 32;
const int active_left = 24;
const int active_right = 640 + active_left;
const int border_right = active_right + active_left;
const int active_top = 17;
const int active_top = 30;
const int active_bottom = 200 + active_top;
int x = border_left;
@ -614,14 +753,19 @@ TIMER_CALLBACK_MEMBER(c65_state::scanline_cb)
p[x] = m_VIC2_EXTColor;
for (;x < active_right; x++)
{
u8 tile_dot;
bool is_foreground;
u8 tile_dot, sprite_dot, sprite_mask;
bool is_foreground, is_sprite;
// TODO: functional depending on mode
// NOTE: VIC-II and VIC-III can switch mid-frame, but latches occur in 8 scanline steps
// at least from/to a base C=64 tilemap mode.
// at least from/to a base C=64 tilemap mode.
std::tie(tile_dot, is_foreground) = get_tile_pixel(y - active_top, x - active_left);
// TODO: sprites fetch here
p[x] = m_palette->pen(tile_dot);
// HACK: are sprite positions in native coordinates from the border?
std::tie(sprite_dot, sprite_mask, is_sprite) = get_sprite_pixel(y + 20, x + active_left);
if (is_sprite)
p[x] = m_palette->pen(sprite_dot);
else
p[x] = m_palette->pen(tile_dot);
}
for (;x < border_right; x++)
p[x] = m_VIC2_EXTColor;
@ -963,7 +1107,7 @@ void c65_state::c65(machine_config &config)
m_screen->set_screen_update(FUNC(c65_state::screen_update));
// TODO: stub parameters
// C=64 / width 40 modes should actually be running in 320x200
m_screen->set_raw(MAIN_C65_CLOCK*4, 910, 0, 640+32 * 2, 262, 0, 200+17*2);
m_screen->set_raw(MAIN_C65_CLOCK*4, 910, 0, 640+24 * 2, 262, 0, 200+30*2);
m_screen->set_palette(m_palette);
GFXDECODE(config, m_gfxdecode, m_palette, gfx_c65);