diff options
Diffstat (limited to 'src/devices/machine/spg290_ppu.cpp')
-rw-r--r-- | src/devices/machine/spg290_ppu.cpp | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/src/devices/machine/spg290_ppu.cpp b/src/devices/machine/spg290_ppu.cpp new file mode 100644 index 00000000000..8f47d61bf18 --- /dev/null +++ b/src/devices/machine/spg290_ppu.cpp @@ -0,0 +1,563 @@ +// license:BSD-3-Clause +// copyright-holders:Sandro Ronco +/***************************************************************************** + + SPG290 PPU + + TODO: + - Horizontal and vertical compression + - Characters flip + - Various graphics glitches + +*****************************************************************************/ + +#include "emu.h" +#include "spg290_ppu.h" + + +DEFINE_DEVICE_TYPE(SPG290_PPU, spg290_ppu_device, "spg290_ppu", "SPG290 PPU") + + +spg290_ppu_device::spg290_ppu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, SPG290_PPU, tag, owner, clock) + , m_screen(*this, finder_base::DUMMY_TAG) + , m_sprite_palette_ram(*this, "sprite_palette_ram") + , m_char_palette_ram(*this, "char_palette_ram") + , m_hoffset_ram(*this, "hoffset_ram") + , m_voffset_ram(*this, "voffset_ram") + , m_sprite_ram(*this, "sprite_ram") + , m_vblank_irq_cb(*this) + , m_space_read_cb(*this) +{ +} + + +void spg290_ppu_device::map(address_map &map) +{ + map(0x0000, 0x00ff).rw(FUNC(spg290_ppu_device::regs_r), FUNC(spg290_ppu_device::regs_w)); + map(0x1000, 0x17ff).ram().share(m_char_palette_ram); + map(0x1800, 0x1fff).ram().share(m_sprite_palette_ram); + map(0x2000, 0x27ff).ram().share(m_hoffset_ram); + map(0x3000, 0x37ff).ram().share(m_voffset_ram); + map(0x4000, 0x4fff).ram().share(m_sprite_ram); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void spg290_ppu_device::device_start() +{ + m_vblank_irq_cb.resolve_safe(); + m_space_read_cb.resolve_safe(0); + + save_item(NAME(m_control)); + save_item(NAME(m_sprite_control)); + save_item(NAME(m_irq_control)); + save_item(NAME(m_irq_status)); + save_item(NAME(m_sprite_max)); + save_item(NAME(m_sprite_buf_start)); + save_item(NAME(m_blend_mode)); + save_item(NAME(m_frame_buff)); + save_item(NAME(m_transrgb)); + save_item(NAME(m_vcomp_value)); + save_item(NAME(m_vcomp_offset)); + save_item(NAME(m_vcomp_step)); + save_item(NAME(m_irq_timing_v)); + save_item(NAME(m_irq_timing_h)); + save_item(NAME(m_vblank_lines)); + + save_item(STRUCT_MEMBER(m_txs, control)); + save_item(STRUCT_MEMBER(m_txs, attribute)); + save_item(STRUCT_MEMBER(m_txs, posx)); + save_item(STRUCT_MEMBER(m_txs, posy)); + save_item(STRUCT_MEMBER(m_txs, nptr)); + save_item(STRUCT_MEMBER(m_txs, blend)); + save_item(STRUCT_MEMBER(m_txs, buf_start)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void spg290_ppu_device::device_reset() +{ + m_control = 0; + m_sprite_control = 0; + m_irq_control = 0; + m_irq_status = 0; + m_sprite_max = 0; + m_sprite_buf_start = 0; + m_blend_mode = 0; + m_frame_buff[0] = 0; + m_frame_buff[1] = 0; + m_frame_buff[2] = 0; + m_transrgb = 0; + m_vcomp_value = 0; + m_vcomp_offset = 0; + m_vcomp_step = 0; + m_irq_timing_v = 0; + m_irq_timing_h = 0; + m_vblank_lines = 0; + memset(m_txs, 0, sizeof(m_txs)); + + m_vblank_irq_cb(CLEAR_LINE); +} + + +uint32_t spg290_ppu_device::regs_r(offs_t offset, uint32_t mem_mask) +{ + switch(offset << 2) + { + case 0x00: // PPU Control + return m_control; + case 0x04: // Sprite Control + return m_sprite_control; + case 0x08: // PPU Max Sprites + return m_sprite_max; + case 0x0c: // blend mode + return m_blend_mode; + case 0x10: // PPU Max Sprites + return m_transrgb; + case 0x20: // Text Layer 1 X pos + return m_txs[0].posx; + case 0x24: // Text Layer 1 Y pos + return m_txs[0].posy; + case 0x28: // Text Layer 1 attribute + return m_txs[0].attribute; + case 0x2c: // Text Layer 1 control + return m_txs[0].control; + case 0x30: // Text Layer 1 number ptr + return m_txs[0].nptr; + case 0x38: // Text Layer 1 buffer blend + return m_txs[0].blend; + case 0x3c: // Text Layer 2 X pos + return m_txs[1].posx; + case 0x40: // Text Layer 2 Y pos + return m_txs[1].posy; + case 0x44: // Text Layer 2 attribute + return m_txs[1].attribute; + case 0x48: // Text Layer 2 control + return m_txs[1].control; + case 0x4c: // Text Layer 2 number ptr + return m_txs[1].nptr; + case 0x54: // Text Layer 2 buffer blend + return m_txs[1].blend; + case 0x58: // Text Layer 3 X pos + return m_txs[2].posx; + case 0x5c: // Text Layer 3 Y pos + return m_txs[2].posy; + case 0x60: // Text Layer 3 attribute + return m_txs[2].attribute; + case 0x64: // Text Layer 3 control + return m_txs[2].control; + case 0x68: // Text Layer 3 number ptr + return m_txs[2].nptr; + case 0x70: // Tx3 buffer blend + return m_txs[2].blend; + case 0x74: // vertical comp value + return m_vcomp_value; + case 0x78: // vertical comp offset + return m_vcomp_offset; + case 0x7c: // vertical comp step + return m_vcomp_step; + case 0x80: // PPU IRQ Control + return m_irq_control; + case 0x84: // PPU IRQ ack + return m_irq_status; + case 0x88: // IRQTimingV + return m_irq_timing_v; + case 0x8c: // IRQTimingH + return m_irq_timing_h; + case 0x90: // vblank lines + return m_vblank_lines; + case 0x94: // lines count + return m_vblank_lines + m_screen->vpos(); + case 0xa0: case 0xa4: case 0xa8: // Tx1 buffer start + return m_txs[0].buf_start[offset & 3]; + case 0xac: case 0xb0: case 0xb4: // Tx2 buffer start + return m_txs[1].buf_start[(offset+1) & 3]; + case 0xb8: case 0xbc: case 0xc0: // Tx3 buffer starts + return m_txs[2].buf_start[(offset+2) & 3]; + case 0xc4: case 0xc8: case 0xcc: // frame buffer start + return m_frame_buff[(offset-1) & 3]; + case 0xd0: // Sprites buffer start + return m_sprite_buf_start; + default: + logerror("[%s] %s: unknown read %x\n", tag(), machine().describe_context(), offset << 2); + } + + return 0; +} + + +void spg290_ppu_device::regs_w(offs_t offset, uint32_t data, uint32_t mem_mask) +{ + switch(offset << 2) + { + case 0x00: // PPU Control + COMBINE_DATA(&m_control); + break; + case 0x04: // Sprite Control + COMBINE_DATA(&m_sprite_control); + break; + case 0x08: // PPU Max Sprites + COMBINE_DATA(&m_sprite_max); + break; + case 0x0c: // blend mode + COMBINE_DATA(&m_blend_mode); + break; + case 0x10: // PPU Max Sprites + COMBINE_DATA(&m_transrgb); + break; + case 0x20: // Text Layer 1 X pos + COMBINE_DATA(&m_txs[0].posx); + break; + case 0x24: // Text Layer 1 Y pos + COMBINE_DATA(&m_txs[0].posy); + break; + case 0x28: // Text Layer 1 attribute + COMBINE_DATA(&m_txs[0].attribute); + break; + case 0x2c: // Text Layer 1 control + COMBINE_DATA(&m_txs[0].control); + break; + case 0x30: // Text Layer 1 number ptr + COMBINE_DATA(&m_txs[0].nptr); + break; + case 0x38: // Text Layer 1 buffer blend + COMBINE_DATA(&m_txs[0].blend); + break; + case 0x3c: // Text Layer 2 X pos + COMBINE_DATA(&m_txs[1].posx); + break; + case 0x40: // Text Layer 2 Y pos + COMBINE_DATA(&m_txs[1].posy); + break; + case 0x44: // Text Layer 2 attribute + COMBINE_DATA(&m_txs[1].attribute); + break; + case 0x48: // Text Layer 2 control + COMBINE_DATA(&m_txs[1].control); + break; + case 0x4c: // Text Layer 2 number ptr + COMBINE_DATA(&m_txs[1].nptr); + break; + case 0x54: // Text Layer 2 buffer blend + COMBINE_DATA(&m_txs[1].blend); + break; + case 0x58: // Text Layer 3 X pos + COMBINE_DATA(&m_txs[2].posx); + break; + case 0x5c: // Text Layer 3 Y pos + COMBINE_DATA(&m_txs[2].posy); + break; + case 0x60: // Text Layer 3 attribute + COMBINE_DATA(&m_txs[2].attribute); + break; + case 0x64: // Text Layer 3 control + COMBINE_DATA(&m_txs[2].control); + break; + case 0x68: // Text Layer 3 number ptr + COMBINE_DATA(&m_txs[2].nptr); + break; + case 0x70: // Tx3 buffer blend + COMBINE_DATA(&m_txs[2].blend); + break; + case 0x74: // vertical comp value + COMBINE_DATA(&m_vcomp_value); + break; + case 0x78: // vertical comp offset + COMBINE_DATA(&m_vcomp_offset); + break; + case 0x7c: // vertical comp step + COMBINE_DATA(&m_vcomp_step); + break; + case 0x80: // PPU IRQ Control + COMBINE_DATA(&m_irq_control); + m_irq_status &= m_irq_control; + break; + case 0x84: // PPU IRQ ack + if (ACCESSING_BITS_0_7) + { + m_irq_status &= ~data; + + if (!(m_irq_status & m_irq_control & 0x07)) + m_vblank_irq_cb(CLEAR_LINE); + } + break; + case 0x88: // IRQTimingV + COMBINE_DATA(&m_irq_timing_v); + break; + case 0x8c: // IRQTimingH + COMBINE_DATA(&m_irq_timing_h); + break; + case 0x90: // vblank lines + COMBINE_DATA(&m_vblank_lines); + break; + case 0xa0: case 0xa4: case 0xa8: // Tx1 buffer start + COMBINE_DATA(&m_txs[0].buf_start[offset & 3]); + break; + case 0xac: case 0xb0: case 0xb4: // Tx2 buffer start + COMBINE_DATA(&m_txs[1].buf_start[(offset+1) & 3]); + break; + case 0xb8: case 0xbc: case 0xc0: // Tx3 buffer starts + COMBINE_DATA(&m_txs[2].buf_start[(offset+2) & 3]); + break; + case 0xc4: case 0xc8: case 0xcc: // frame buffer start + COMBINE_DATA(&m_frame_buff[(offset-1) & 3]); + break; + case 0xd0: // Sprites buffer start + COMBINE_DATA(&m_sprite_buf_start); + break; + default: + logerror("[%s] %s: unknown write %x = %x\n", tag(), machine().describe_context(), offset << 2, data); + } +} + +void spg290_ppu_device::screen_vblank(int state) +{ + if (state && (m_irq_control & 0x01)) // VBlanking Start IRQ + { + m_irq_status |= 0x01; + m_vblank_irq_cb(ASSERT_LINE); + } + else if (!state && (m_irq_control & 0x02)) // VBlanking End IRQ + { + m_irq_status |= 0x02; + m_vblank_irq_cb(ASSERT_LINE); + } +} + +inline rgb_t spg290_ppu_device::blend_colors(const rgb_t &c0, const rgb_t &c1, uint8_t level) +{ + if (m_blend_mode & 1) + { + int r = (c0.r() * level / 63) - (c1.r() * (63 - level) / 63); + int g = (c0.b() * level / 63) - (c1.b() * (63 - level) / 63); + int b = (c0.b() * level / 63) - (c1.b() * (63 - level) / 63); + return rgb_t(r, g, b); + } + else + { + int r = (c0.r() * level / 63) + (c1.r() * (63 - level) / 63); + int g = (c0.b() * level / 63) + (c1.b() * (63 - level) / 63); + int b = (c0.b() * level / 63) + (c1.b() * (63 - level) / 63); + return rgb_t(r, g, b); + } +} + +inline void spg290_ppu_device::argb1555(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t posy, uint16_t posx, uint16_t argb, uint8_t blend) +{ + if (!(argb & 0x8000) && cliprect.contains(posx, posy)) + { + rgb_t color = rgb_t(pal5bit(argb >> 10), pal5bit(argb >> 5), pal5bit(argb >> 0)); + if (blend) + color = blend_colors(bitmap.pix32(posy, posx), color, blend); + + bitmap.pix32(posy, posx) = color; + } +} + +inline void spg290_ppu_device::rgb565(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint16_t posy, uint16_t posx, uint16_t rgb, uint8_t blend) +{ + if ((!(m_transrgb & 0x10000) || (m_transrgb & 0xffff) != rgb) && cliprect.contains(posx, posy)) + { + rgb_t color = rgb_t(pal5bit(rgb >> 11), pal6bit(rgb >> 5), pal5bit(rgb >> 0)); + if (blend) + color = blend_colors(bitmap.pix32(posy, posx), color, blend); + + bitmap.pix32(posy, posx) = color; + } +} + +void spg290_ppu_device::blit_sprite(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, uint32_t buf_start) +{ + uint16_t sprite_num = control & 0xffff; + uint16_t sprite_x = (control >> 16) & 0x3ff; + uint16_t sprite_y = (attribute >> 16) & 0x3ff; + uint8_t sprite_hsize = 8 << ((attribute >> 4) & 0x03); + uint8_t sprite_vsize = 8 << ((attribute >> 6) & 0x03); + uint8_t bit_pixel = ((attribute & 3) + 1) << 1; + uint8_t sprite_flip = (attribute >> 2) & 0x03; + uint16_t pen_bank = ((attribute >> 8) & 0x1f) * 0x10; + uint8_t blend = (attribute & 0x8000) ? (attribute >> 26) & 0x3f : 0; + uint8_t pixel_word = 32 / bit_pixel; + uint8_t pixel_byte = 8 / bit_pixel; + uint8_t word_line = sprite_hsize / pixel_word; + uint16_t pen_mask = (1 << bit_pixel) - 1; + + if (sprite_num == 0) + return; + + uint32_t sprite_base = buf_start + (sprite_num * (sprite_hsize * sprite_vsize * bit_pixel / 8)); + + if (sprite_x > cliprect.max_x) sprite_x = (sprite_x & 0x3ff) - 0x400; + if (sprite_y > cliprect.max_y) sprite_y = (sprite_y & 0x3ff) - 0x400; + + for (int y=0; y < sprite_vsize; y++) + for (int x=0; x < word_line; x++) + { + uint32_t data = m_space_read_cb(sprite_base + (y * word_line + x) * 4); + + for (int i=0; i < 4; i++) + { + for (int b=0; b < pixel_byte; b++) + { + int bitpos = i * pixel_byte + b; + uint16_t pen = (data >> (8 - (b + 1) * bit_pixel)) & pen_mask; + uint16_t posx = cliprect.min_x; + if (sprite_flip & 0x01) + posx += sprite_x + (sprite_hsize - (x * pixel_word + bitpos)); + else + posx += sprite_x + x * pixel_word + bitpos; + + uint16_t posy = cliprect.min_y; + if (sprite_flip & 0x02) + posy += sprite_y + (sprite_vsize - y); + else + posy += sprite_y + y; + + argb1555(bitmap, cliprect, posy, posx, m_sprite_palette_ram[(pen_bank + pen) & 0x1ff], blend); + } + + data >>= 8; + } + } +} + +void spg290_ppu_device::blit_bitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, int posy, int posx, uint32_t nptr, uint32_t buf_start, uint8_t blend) +{ + int max_x = cliprect.max_x + posx; + int max_y = cliprect.max_y + posy; + for (int y=0; y < max_y; y++) + { + int line = (control & 0x04) ? 0 : y; + uint32_t tx_start = m_space_read_cb(nptr + line * 4) * 2; + + for (int x=0; x < max_x; x+=2) + { + uint16_t px = cliprect.min_x + x; + uint16_t py = cliprect.min_y + y; + uint32_t data = m_space_read_cb(buf_start + tx_start + x * 2); + + for (int b=0; b < 2; b++) + { + if (control & 0x1000) + rgb565(bitmap, cliprect, py - posy, px + b - posx, data & 0xffff, blend); + else + argb1555(bitmap, cliprect, py - posy, px + b - posx, data & 0xffff, blend); + + data >>= 16; + } + } + } +} + +void spg290_ppu_device::blit_character(bitmap_rgb32 &bitmap, const rectangle &cliprect, uint32_t control, uint32_t attribute, int posy, int posx, uint32_t nptr, uint32_t buf_start, uint8_t blend) +{ + uint8_t hsize = 8 << ((attribute >> 4) & 0x03); + uint8_t vsize = 8 << ((attribute >> 6) & 0x03); + uint8_t bit_pixel = ((attribute & 3) + 1) << 1; + uint16_t pen_bank = ((attribute >> 8) & 0x1f) * 0x10; + //uint8_t char_flip = (attribute >> 2) & 0x03; + uint8_t pixel_word = 32 / bit_pixel; + uint8_t chars_line = 1024 / hsize; + uint16_t pen_mask = (1 << bit_pixel) - 1; + + int max_x = (cliprect.max_x + posx) / vsize + 1; + int max_y = (cliprect.max_y + posy) / hsize + 1; + + for (int y=0; y <= max_y; y++) + { + for (int x=0; x <= max_x; x++) + { + int line = (control & 0x04) ? 0 : y; + uint32_t addr = line * chars_line + x; + uint32_t char_id = m_space_read_cb(nptr + addr * 2); + + if (addr & 1) + char_id >>= 16; + + uint32_t char_addr = buf_start + (char_id & 0xffff) * hsize * vsize; + + for (int cy=0; cy < vsize; cy++) + { + uint32_t line_addr = char_addr + cy * hsize; + for (int cx=0; cx < hsize; cx++) + { + uint32_t data = m_space_read_cb(line_addr + (cx * bit_pixel) / 8); + uint16_t px = cliprect.min_x + x * hsize + cx; + uint16_t py = cliprect.min_y + y * vsize + cy; + uint16_t pen = (data >> ((cx % pixel_word) * bit_pixel)) & pen_mask; + uint16_t pix = m_char_palette_ram[(pen_bank + pen) & 0x3ff]; + + if (control & 0x1000) + rgb565(bitmap, cliprect, py - posy, px - posx, pix, blend); + else + argb1555(bitmap, cliprect, py - posy, px - posx, pix, blend); + } + } + } + } +} + + +uint32_t spg290_ppu_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) +{ + bitmap.fill(rgb_t::black(), cliprect); + + if (!(m_control & 0x1000)) + return 0; + + for (int depth=0; depth<4; depth++) + { + //if (machine().input().code_pressed(KEYCODE_1_PAD) && depth == 0) continue; + //if (machine().input().code_pressed(KEYCODE_2_PAD) && depth == 1) continue; + //if (machine().input().code_pressed(KEYCODE_3_PAD) && depth == 2) continue; + //if (machine().input().code_pressed(KEYCODE_4_PAD) && depth == 3) continue; + + // draw the bitmap/text layers + for (int l=0; l<3; l++) + { + //if (machine().input().code_pressed(KEYCODE_7_PAD) && l == 0) continue; + //if (machine().input().code_pressed(KEYCODE_8_PAD) && l == 1) continue; + //if (machine().input().code_pressed(KEYCODE_9_PAD) && l == 2) continue; + + if ((m_txs[l].control & 0x08) && ((m_txs[l].attribute >> 13) & 3) == depth) + { + uint8_t blend = (m_txs[l].control & 0x0100) ? m_txs[l].blend : 0; + + // posx is 10-bit two's complement + int posx = m_txs[l].posx & 0x3ff; + if (posx & 0x200) + posx = (posx & 0x3ff) - 0x400; + + // posy is 9-bit two's complement + int posy = m_txs[l].posy & 0x1ff; + if (posy & 0x100) + posy = (posy & 0x1ff) - 0x200; + + if (m_txs[l].control & 0x01) + blit_bitmap(bitmap, cliprect, m_txs[l].control, m_txs[l].attribute, posy, posx, m_txs[l].nptr, m_txs[l].buf_start[0], blend); + else + blit_character(bitmap, cliprect, m_txs[l].control, m_txs[l].attribute, posy, posx, m_txs[l].nptr, m_txs[l].buf_start[0], blend); + } + } + + //if (machine().input().code_pressed(KEYCODE_0_PAD)) continue; + + // draw the sprites + if (m_sprite_control & 1) + { + for (int i=0; i <= (m_sprite_max & 0x1ff); i++) + { + if (((m_sprite_ram[i * 2 + 1] >> 13) & 3) == depth) + blit_sprite(bitmap, cliprect, m_sprite_ram[i * 2], m_sprite_ram[i * 2 + 1], m_sprite_buf_start); + } + } + } + + return 0; +} |