summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/spg290_ppu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/spg290_ppu.cpp')
-rw-r--r--src/devices/machine/spg290_ppu.cpp563
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;
+}