// license:LGPL-2.1+ // copyright-holders:Angelo Salese /*************************************************************************** Device for Mazer Blazer/Great Guns custom Video Controller Unit Written by Angelo Salese, based off old implementation by Jarek Burczynski TODO: - understand what exactly modes 0x03 and 0x13 really reads in set_clr() and where it puts results (yeah, shared VCU RAM, but exactly where?). Almost surely Mazer Blazer tries to read the pixel data for collision detection and Great Guns read backs VRAM for VCU test (patched for now, btw). - Understand look-up tables in i/o space. - Understand how to handle layer clearance. - Understand how planes are really handled. - Understand how transparent pens are handled (is 0x0f always transparent or there's some clut gimmick? Great Guns title screen makes me think of the latter option) ***************************************************************************/ #include "emu.h" #include "video/mb_vcu.h" #include "video/resnet.h" //************************************************************************** // GLOBAL VARIABLES //************************************************************************** // device type definition DEFINE_DEVICE_TYPE(MB_VCU, mb_vcu_device, "mb_vcu", "Mazer Blazer custom VCU") //------------------------------------------------- // static_set_palette_tag: Set the tag of the // palette device //------------------------------------------------- void mb_vcu_device::static_set_palette_tag(device_t &device, const char *tag) { downcast(device).m_palette.set_tag(tag); } static ADDRESS_MAP_START( mb_vcu_vram, 0, 8, mb_vcu_device ) AM_RANGE(0x00000,0x7ffff) AM_RAM // enough for a 256x256x4 x 2 pages of framebuffer with 4 layers (TODO: doubled for simplicity) ADDRESS_MAP_END static ADDRESS_MAP_START( mb_vcu_pal_ram, 1, 8, mb_vcu_device ) AM_RANGE(0x0000, 0x00ff) AM_RAM AM_RANGE(0x0200, 0x02ff) AM_RAM AM_RANGE(0x0400, 0x04ff) AM_RAM AM_RANGE(0x0600, 0x06ff) AM_READWRITE(mb_vcu_paletteram_r,mb_vcu_paletteram_w) ADDRESS_MAP_END READ8_MEMBER( mb_vcu_device::mb_vcu_paletteram_r ) { return m_palram[offset]; } WRITE8_MEMBER( mb_vcu_device::mb_vcu_paletteram_w ) { int r,g,b, bit0, bit1, bit2; m_palram[offset] = data; /* red component */ bit1 = (m_palram[offset] >> 7) & 0x01; bit0 = (m_palram[offset] >> 6) & 0x01; r = combine_2_weights(m_weights_r, bit0, bit1); /* green component */ bit2 = (m_palram[offset] >> 5) & 0x01; bit1 = (m_palram[offset] >> 4) & 0x01; bit0 = (m_palram[offset] >> 3) & 0x01; g = combine_3_weights(m_weights_g, bit0, bit1, bit2); /* blue component */ bit2 = (m_palram[offset] >> 2) & 0x01; bit1 = (m_palram[offset] >> 1) & 0x01; bit0 = (m_palram[offset] >> 0) & 0x01; b = combine_3_weights(m_weights_b, bit0, bit1, bit2); m_palette->set_pen_color(offset, rgb_t(r, g, b)); } //------------------------------------------------- // memory_space_config - return a description of // any address spaces owned by this device //------------------------------------------------- std::vector> mb_vcu_device::memory_space_config() const { return std::vector> { std::make_pair(0, &m_videoram_space_config), std::make_pair(1, &m_paletteram_space_config) }; } //************************************************************************** // INLINE HELPERS //************************************************************************** //------------------------------------------------- // read_byte - read a byte at the given address //------------------------------------------------- inline uint8_t mb_vcu_device::read_byte(offs_t address) { return space(0).read_byte(address); } //------------------------------------------------- // write_byte - write a byte at the given address //------------------------------------------------- inline void mb_vcu_device::write_byte(offs_t address, uint8_t data) { space(0).write_byte(address, data); } //------------------------------------------------- // read_byte - read a byte at the given i/o //------------------------------------------------- inline uint8_t mb_vcu_device::read_io(offs_t address) { return space(1).read_byte(address); } //------------------------------------------------- // write_byte - write a byte at the given i/o //------------------------------------------------- inline void mb_vcu_device::write_io(offs_t address, uint8_t data) { space(1).write_byte(address, data); } //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // mb_vcu_device - constructor //------------------------------------------------- mb_vcu_device::mb_vcu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, MB_VCU, tag, owner, clock) , device_memory_interface(mconfig, *this) , device_video_interface(mconfig, *this) , m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 19, 0, nullptr, *ADDRESS_MAP_NAME(mb_vcu_vram)) , m_paletteram_space_config("palram", ENDIANNESS_LITTLE, 8, 16, 0, nullptr, *ADDRESS_MAP_NAME(mb_vcu_pal_ram)) , m_cpu(*this, finder_base::DUMMY_TAG) , m_palette(*this, finder_base::DUMMY_TAG) { } //------------------------------------------------- // device_validity_check - perform validity checks // on this device //------------------------------------------------- void mb_vcu_device::device_validity_check(validity_checker &valid) const { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void mb_vcu_device::device_start() { // TODO: m_screen_tag m_ram = make_unique_clear(0x800); m_palram = make_unique_clear(0x100); { static const int resistances_r[2] = { 4700, 2200 }; static const int resistances_gb[3] = { 10000, 4700, 2200 }; /* just to calculate coefficients for later use */ compute_resistor_weights(0, 255, -1.0, 3, resistances_gb, m_weights_g, 3600, 0, 3, resistances_gb, m_weights_b, 3600, 0, 2, resistances_r, m_weights_r, 3600, 0); } save_item(NAME(m_status)); save_pointer(NAME(m_ram.get()), 0x800); save_pointer(NAME(m_palram.get()), 0x100); save_item(NAME(m_param_offset_latch)); save_item(NAME(m_xpos)); save_item(NAME(m_ypos)); save_item(NAME(m_color1)); save_item(NAME(m_color2)); save_item(NAME(m_mode)); save_item(NAME(m_pix_xsize)); save_item(NAME(m_pix_ysize)); save_item(NAME(m_vregs)); save_item(NAME(m_bk_color)); save_item(NAME(m_vbank)); save_item(NAME(m_weights_r)); save_item(NAME(m_weights_g)); save_item(NAME(m_weights_b)); } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void mb_vcu_device::device_reset() { m_status = 1; for(int i=0;i<0x80000;i++) { write_byte(i,0x0f); } } //************************************************************************** // READ/WRITE HANDLERS //************************************************************************** // uint8_t *pcg = memregion("sub2")->base(); READ8_MEMBER( mb_vcu_device::read_ram ) { return m_ram[offset]; } WRITE8_MEMBER( mb_vcu_device::write_ram ) { m_ram[offset] = data; } WRITE8_MEMBER( mb_vcu_device::write_vregs ) { m_vregs[offset] = data; } /* latches RAM offset to send to params */ READ8_MEMBER( mb_vcu_device::load_params ) { m_param_offset_latch = offset; m_xpos = m_ram[m_param_offset_latch + 1] | (m_ram[m_param_offset_latch + 2]<<8); m_ypos = m_ram[m_param_offset_latch + 3] | (m_ram[m_param_offset_latch + 4]<<8); m_color1 = m_ram[m_param_offset_latch + 5]; m_color2 = m_ram[m_param_offset_latch + 6]; m_mode = m_ram[m_param_offset_latch + 7]; m_pix_xsize = m_ram[m_param_offset_latch + 8] + 1; m_pix_ysize = m_ram[m_param_offset_latch + 9] + 1; if(0) { printf("[0] %02x ",m_ram[m_param_offset_latch]); printf("X: %04x ",m_xpos); printf("Y: %04x ",m_ypos); printf("C1:%02x ",m_color1); printf("C2:%02x ",m_color2); printf("M :%02x ",m_mode); printf("XS:%02x ",m_pix_xsize); printf("YS:%02x ",m_pix_ysize); printf("\n"); } return 0; // open bus? } READ8_MEMBER( mb_vcu_device::load_gfx ) { int xi,yi; int dstx,dsty; uint8_t dot; int bits = 0; uint8_t pen = 0; uint8_t cur_layer; // cur_layer = (m_mode & 0x3); cur_layer = 0; switch(m_mode >> 2) { case 0x00: // 4bpp for(yi=0;yi= 0 && dsty >= 0 && dstx < 256 && dsty < 256) { dot = m_cpu->space(AS_PROGRAM).read_byte(((offset + (bits >> 3)) & 0x1fff) + 0x4000) >> (4-(bits & 7)); dot&= 0xf; //if(dot != 0xf || m_mode & 2) write_byte(dstx|dsty<<8|cur_layer<<16|m_vbank<<18, dot); } bits += 4; } } break; case 0x02: // 1bpp for(yi=0;yi= 0 && dsty >= 0 && dstx < 256 && dsty < 256) { dot = m_cpu->space(AS_PROGRAM).read_byte(((offset + (bits >> 3)) & 0x1fff) + 0x4000) >> (7-(bits & 7)); dot&= 1; pen = dot ? (m_color1 >> 4) : (m_color1 & 0xf); //if(pen != 0xf || m_mode & 2) write_byte(dstx|dsty<<8|cur_layer<<16|m_vbank<<18, pen); } bits++; } } break; case 0x03: //2bpp for (yi = 0; yi < m_pix_ysize; yi++) { for (xi = 0; xi < m_pix_xsize; xi++) { dstx = (m_xpos + xi); dsty = (m_ypos + yi); if(dstx >= 0 && dsty >= 0 && dstx < 256 && dsty < 256) { dot = m_cpu->space(AS_PROGRAM).read_byte(((offset + (bits >> 3)) & 0x1fff) + 0x4000) >> (6-(bits & 7)); switch(dot & 3) { case 0: pen = m_color1 & 0xf; break; case 1: pen = m_color1 >> 4; break; case 2: pen = m_color2 & 0xf; break; case 3: pen = m_color2 >> 4; break; } //if(pen != 0xf) write_byte(dstx|dsty<<8|cur_layer<<16|m_vbank<<18, pen); } bits+=2; } } break; default: popmessage("Unsupported draw mode"); break; } return 0; // open bus? } /* ---0 -111 (0x07) write to i/o? ---0 -011 (0x03) read to i/o? ---1 -011 (0x13) read to vram? */ READ8_MEMBER( mb_vcu_device::load_set_clr ) { int xi,yi; int dstx,dsty; // uint8_t dot; int bits = 0; if(m_mode == 0x13 || m_mode == 0x03) { printf("[0] %02x ",m_ram[m_param_offset_latch]); printf("X: %04x ",m_xpos); printf("Y: %04x ",m_ypos); printf("C1:%02x ",m_color1); printf("C2:%02x ",m_color2); printf("M :%02x ",m_mode); printf("XS:%02x ",m_pix_xsize); printf("YS:%02x ",m_pix_ysize); printf("VB:%02x ",m_vbank); printf("\n"); } switch(m_mode) { case 0x13: case 0x03: for (yi = 0; yi < m_pix_ysize; yi++) { for (xi = 0; xi < m_pix_xsize; xi++) { dstx = (m_xpos + xi); dsty = (m_ypos + yi); if(dstx < 256 && dsty < 256) { #if 0 dot = m_cpu->space(AS_PROGRAM).read_byte(((offset + (bits >> 3)) & 0x1fff) + 0x4000) >> (6-(bits & 7)); dot&= 3; switch(dot) { case 0: write_byte(dstx|dsty<<8, m_color1 & 0xf); break; case 1: write_byte(dstx|dsty<<8, m_color1 >> 4); break; case 2: write_byte(dstx|dsty<<8, m_color2 & 0xf); break; case 3: write_byte(dstx|dsty<<8, m_color2 >> 4); break; } #endif //write_byte(dstx|dsty<<8, m_mode >> 4); } bits+=2; } } break; case 0x07: for(int i=0;i> 7) & 0x01; bit0 = (m_bk_color >> 6) & 0x01; r = combine_2_weights(m_weights_r, bit0, bit1); /* green component */ bit2 = (m_bk_color >> 5) & 0x01; bit1 = (m_bk_color >> 4) & 0x01; bit0 = (m_bk_color >> 3) & 0x01; g = combine_3_weights(m_weights_g, bit0, bit1, bit2); /* blue component */ bit2 = (m_bk_color >> 2) & 0x01; bit1 = (m_bk_color >> 1) & 0x01; bit0 = (m_bk_color >> 0) & 0x01; b = combine_3_weights(m_weights_b, bit0, bit1, bit2); m_palette->set_pen_color(0x100, rgb_t(r, g, b)); } READ8_MEMBER( mb_vcu_device::status_r ) { /* ---- ---x busy or vblank flag */ return m_status; } WRITE8_MEMBER( mb_vcu_device::vbank_w ) { m_vbank = (data & 0x40) >> 6; } //------------------------------------------------- // update_screen - //------------------------------------------------- uint32_t mb_vcu_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { int x,y; uint8_t dot; bitmap.fill(0x100,cliprect); for(y=0;y<256;y++) { for(x=0;x<256;x++) { dot = read_byte((x >> 0)|(y<<8)|0<<16|(m_vbank ^ 1)<<18); //if(dot != 0xf) { dot|= m_vregs[1] << 4; bitmap.pix32(y,x) = m_palette->pen(dot); } } } #if 0 for(y=0;y<256;y++) { for(x=0;x<256;x++) { dot = read_byte((x >> 0)|(y<<8)|3<<16); if(dot != 0xf) { dot|= m_vregs[1] << 4; bitmap.pix32(y,x) = machine().pens[dot]; } } } for(y=0;y<256;y++) { for(x=0;x<256;x++) { dot = read_byte((x >> 0)|(y<<8)|0<<16); if(dot != 0xf) { dot|= m_vregs[1] << 4; bitmap.pix32(y,x) = machine().pens[dot]; } } } for(y=0;y<256;y++) { for(x=0;x<256;x++) { dot = read_byte((x >> 0)|(y<<8)|1<<16); if(dot != 0xf) { dot|= m_vregs[1] << 4; bitmap.pix32(y,x) = machine().pens[dot]; } } } #endif return 0; } void mb_vcu_device::screen_eof(void) { //for(int i=0;i<0x10000;i++) { //write_byte(i|0x00000|m_vbank<<18,0x0f); //write_byte(i|0x10000|m_vbank<<18,0x0f); //write_byte(i|0x30000|m_vbank<<18,0x0f); } }