/*************************************************************************************** * Genesis Plus * Video Display Processor (pixel output rendering) * * Support for all TMS99xx modes, Mode 4 & Mode 5 rendering * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Copyright (C) 2007-2016 Eke-Eke (Genesis Plus GX) * * Redistribution and use of this code or any derivative works are permitted * provided that the following conditions are met: * * - Redistributions may not be sold, nor may they be used in a commercial * product or activity. * * - Redistributions that are modified from the original source must include the * complete source code, including the source code for all components used by a * binary built from the modified sources. However, as a special exception, the * source code distributed need not include anything that is normally distributed * (in either source or binary form) with the major components (compiler, kernel, * and so on) of the operating system on which the executable runs, unless that * component itself accompanies the executable. * * - Redistributions must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************************/ #include "shared.h" #include "md_ntsc.h" #include "sms_ntsc.h" /*** NTSC Filters ***/ extern md_ntsc_t *md_ntsc; extern sms_ntsc_t *sms_ntsc; /* Output pixels type*/ #if defined(USE_8BPP_RENDERING) #define PIXEL_OUT_T uint8 #elif defined(USE_32BPP_RENDERING) #define PIXEL_OUT_T uint32 #else #define PIXEL_OUT_T uint16 #endif /* Pixel priority look-up tables information */ #define LUT_MAX (6) #define LUT_SIZE (0x10000) #ifdef ALIGN_LONG #undef READ_LONG #undef WRITE_LONG INLINE uint32 READ_LONG(void *address) { if ((uint32)address & 3) { #ifdef LSB_FIRST /* little endian version */ return ( *((uint8 *)address) + (*((uint8 *)address+1) << 8) + (*((uint8 *)address+2) << 16) + (*((uint8 *)address+3) << 24) ); #else /* big endian version */ return ( *((uint8 *)address+3) + (*((uint8 *)address+2) << 8) + (*((uint8 *)address+1) << 16) + (*((uint8 *)address) << 24) ); #endif /* LSB_FIRST */ } else return *(uint32 *)address; } INLINE void WRITE_LONG(void *address, uint32 data) { if ((uint32)address & 3) { #ifdef LSB_FIRST *((uint8 *)address) = data; *((uint8 *)address+1) = (data >> 8); *((uint8 *)address+2) = (data >> 16); *((uint8 *)address+3) = (data >> 24); #else *((uint8 *)address+3) = data; *((uint8 *)address+2) = (data >> 8); *((uint8 *)address+1) = (data >> 16); *((uint8 *)address) = (data >> 24); #endif /* LSB_FIRST */ return; } else *(uint32 *)address = data; } #endif /* ALIGN_LONG */ /* Draw 2-cell column (8-pixels high) */ /* Pattern cache base address: VHN NNNNNNNN NNYYYxxx with : x = Pattern Pixel (0-7) Y = Pattern Row (0-7) N = Pattern Number (0-2047) from pattern attribute H = Horizontal Flip bit from pattern attribute V = Vertical Flip bit from pattern attribute */ #define GET_LSB_TILE(ATTR, LINE) \ atex = atex_table[(ATTR >> 13) & 7]; \ src = (uint32 *)&bg_pattern_cache[(ATTR & 0x00001FFF) << 6 | (LINE)]; #define GET_MSB_TILE(ATTR, LINE) \ atex = atex_table[(ATTR >> 29) & 7]; \ src = (uint32 *)&bg_pattern_cache[(ATTR & 0x1FFF0000) >> 10 | (LINE)]; /* Draw 2-cell column (16 pixels high) */ /* Pattern cache base address: VHN NNNNNNNN NYYYYxxx with : x = Pattern Pixel (0-7) Y = Pattern Row (0-15) N = Pattern Number (0-1023) H = Horizontal Flip bit V = Vertical Flip bit */ #define GET_LSB_TILE_IM2(ATTR, LINE) \ atex = atex_table[(ATTR >> 13) & 7]; \ src = (uint32 *)&bg_pattern_cache[((ATTR & 0x000003FF) << 7 | (ATTR & 0x00001800) << 6 | (LINE)) ^ ((ATTR & 0x00001000) >> 6)]; #define GET_MSB_TILE_IM2(ATTR, LINE) \ atex = atex_table[(ATTR >> 29) & 7]; \ src = (uint32 *)&bg_pattern_cache[((ATTR & 0x03FF0000) >> 9 | (ATTR & 0x18000000) >> 10 | (LINE)) ^ ((ATTR & 0x10000000) >> 22)]; /* One column = 2 tiles Two pattern attributes are written in VRAM as two consecutives 16-bit words: P = priority bit C = color palette (2 bits) V = Vertical Flip bit H = Horizontal Flip bit N = Pattern Number (11 bits) (MSB) PCCVHNNN NNNNNNNN (LSB) (MSB) PCCVHNNN NNNNNNNN (LSB) PATTERN1 PATTERN2 Both pattern attributes are read from VRAM as one 32-bit word: LIT_ENDIAN: (MSB) PCCVHNNN NNNNNNNN PCCVHNNN NNNNNNNN (LSB) PATTERN2 PATTERN1 BIG_ENDIAN: (MSB) PCCVHNNN NNNNNNNN PCCVHNNN NNNNNNNN (LSB) PATTERN1 PATTERN2 In line buffers, one pixel = one byte: (msb) 0Pppcccc (lsb) with: P = priority bit (from pattern attribute) p = color palette (from pattern attribute) c = color data (from pattern cache) One pattern = 8 pixels = 8 bytes = two 32-bit writes per pattern */ #ifdef ALIGN_LONG #ifdef LSB_FIRST #define DRAW_COLUMN(ATTR, LINE) \ GET_LSB_TILE(ATTR, LINE) \ WRITE_LONG(dst, src[0] | atex); \ dst++; \ WRITE_LONG(dst, src[1] | atex); \ dst++; \ GET_MSB_TILE(ATTR, LINE) \ WRITE_LONG(dst, src[0] | atex); \ dst++; \ WRITE_LONG(dst, src[1] | atex); \ dst++; #define DRAW_COLUMN_IM2(ATTR, LINE) \ GET_LSB_TILE_IM2(ATTR, LINE) \ WRITE_LONG(dst, src[0] | atex); \ dst++; \ WRITE_LONG(dst, src[1] | atex); \ dst++; \ GET_MSB_TILE_IM2(ATTR, LINE) \ WRITE_LONG(dst, src[0] | atex); \ dst++; \ WRITE_LONG(dst, src[1] | atex); \ dst++; #else #define DRAW_COLUMN(ATTR, LINE) \ GET_MSB_TILE(ATTR, LINE) \ WRITE_LONG(dst, src[0] | atex); \ dst++; \ WRITE_LONG(dst, src[1] | atex); \ dst++; \ GET_LSB_TILE(ATTR, LINE) \ WRITE_LONG(dst, src[0] | atex); \ dst++; \ WRITE_LONG(dst, src[1] | atex); \ dst++; #define DRAW_COLUMN_IM2(ATTR, LINE) \ GET_MSB_TILE_IM2(ATTR, LINE) \ WRITE_LONG(dst, src[0] | atex); \ dst++; \ WRITE_LONG(dst, src[1] | atex); \ dst++; \ GET_LSB_TILE_IM2(ATTR, LINE) \ WRITE_LONG(dst, src[0] | atex); \ dst++; \ WRITE_LONG(dst, src[1] | atex); \ dst++; #endif #else /* NOT ALIGNED */ #ifdef LSB_FIRST #define DRAW_COLUMN(ATTR, LINE) \ GET_LSB_TILE(ATTR, LINE) \ *dst++ = (src[0] | atex); \ *dst++ = (src[1] | atex); \ GET_MSB_TILE(ATTR, LINE) \ *dst++ = (src[0] | atex); \ *dst++ = (src[1] | atex); #define DRAW_COLUMN_IM2(ATTR, LINE) \ GET_LSB_TILE_IM2(ATTR, LINE) \ *dst++ = (src[0] | atex); \ *dst++ = (src[1] | atex); \ GET_MSB_TILE_IM2(ATTR, LINE) \ *dst++ = (src[0] | atex); \ *dst++ = (src[1] | atex); #else #define DRAW_COLUMN(ATTR, LINE) \ GET_MSB_TILE(ATTR, LINE) \ *dst++ = (src[0] | atex); \ *dst++ = (src[1] | atex); \ GET_LSB_TILE(ATTR, LINE) \ *dst++ = (src[0] | atex); \ *dst++ = (src[1] | atex); #define DRAW_COLUMN_IM2(ATTR, LINE) \ GET_MSB_TILE_IM2(ATTR, LINE) \ *dst++ = (src[0] | atex); \ *dst++ = (src[1] | atex); \ GET_LSB_TILE_IM2(ATTR, LINE) \ *dst++ = (src[0] | atex); \ *dst++ = (src[1] | atex); #endif #endif /* ALIGN_LONG */ #ifdef ALT_RENDERER /* Draw background tiles directly using priority look-up table */ /* SRC_A = layer A rendered pixel line (4 bytes = 4 pixels at once) */ /* SRC_B = layer B cached pixel line (4 bytes = 4 pixels at once) */ /* Note: cache address is always aligned so no need to use READ_LONG macro */ /* This might be faster or slower than original method, depending on */ /* architecture (x86, PowerPC), cache size, memory access speed, etc... */ #ifdef LSB_FIRST #define DRAW_BG_TILE(SRC_A, SRC_B) \ *lb++ = table[((SRC_B << 8) & 0xff00) | (SRC_A & 0xff)]; \ *lb++ = table[(SRC_B & 0xff00) | ((SRC_A >> 8) & 0xff)]; \ *lb++ = table[((SRC_B >> 8) & 0xff00) | ((SRC_A >> 16) & 0xff)]; \ *lb++ = table[((SRC_B >> 16) & 0xff00) | ((SRC_A >> 24) & 0xff)]; #else #define DRAW_BG_TILE(SRC_A, SRC_B) \ *lb++ = table[((SRC_B >> 16) & 0xff00) | ((SRC_A >> 24) & 0xff)]; \ *lb++ = table[((SRC_B >> 8) & 0xff00) | ((SRC_A >> 16) & 0xff)]; \ *lb++ = table[(SRC_B & 0xff00) | ((SRC_A >> 8) & 0xff)]; \ *lb++ = table[((SRC_B << 8) & 0xff00) | (SRC_A & 0xff)]; #endif #ifdef ALIGN_LONG #ifdef LSB_FIRST #define DRAW_BG_COLUMN(ATTR, LINE, SRC_A, SRC_B) \ GET_LSB_TILE(ATTR, LINE) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ GET_MSB_TILE(ATTR, LINE) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) #define DRAW_BG_COLUMN_IM2(ATTR, LINE, SRC_A, SRC_B) \ GET_LSB_TILE_IM2(ATTR, LINE) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ GET_MSB_TILE_IM2(ATTR, LINE) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) #else #define DRAW_BG_COLUMN(ATTR, LINE, SRC_A, SRC_B) \ GET_MSB_TILE(ATTR, LINE) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ GET_LSB_TILE(ATTR, LINE) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) #define DRAW_BG_COLUMN_IM2(ATTR, LINE, SRC_A, SRC_B) \ GET_MSB_TILE_IM2(ATTR, LINE) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ GET_LSB_TILE_IM2(ATTR, LINE) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = READ_LONG((uint32 *)lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) #endif #else /* NOT ALIGNED */ #ifdef LSB_FIRST #define DRAW_BG_COLUMN(ATTR, LINE, SRC_A, SRC_B) \ GET_LSB_TILE(ATTR, LINE) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ GET_MSB_TILE(ATTR, LINE) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) #define DRAW_BG_COLUMN_IM2(ATTR, LINE, SRC_A, SRC_B) \ GET_LSB_TILE_IM2(ATTR, LINE) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ GET_MSB_TILE_IM2(ATTR, LINE) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) #else #define DRAW_BG_COLUMN(ATTR, LINE, SRC_A, SRC_B) \ GET_MSB_TILE(ATTR, LINE) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ GET_LSB_TILE(ATTR, LINE) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) #define DRAW_BG_COLUMN_IM2(ATTR, LINE, SRC_A, SRC_B) \ GET_MSB_TILE_IM2(ATTR, LINE) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ GET_LSB_TILE_IM2(ATTR, LINE) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[0] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) \ SRC_A = *(uint32 *)(lb); \ SRC_B = (src[1] | atex); \ DRAW_BG_TILE(SRC_A, SRC_B) #endif #endif /* ALIGN_LONG */ #endif /* ALT_RENDERER */ #define DRAW_SPRITE_TILE(WIDTH,ATTR,TABLE) \ for (i=0;i> 10); \ } \ } #define DRAW_SPRITE_TILE_ACCURATE(WIDTH,ATTR,TABLE) \ for (i=0;i> 1); \ status |= 0x20; \ } \ } \ } #define DRAW_SPRITE_TILE_ACCURATE_2X(WIDTH,ATTR,TABLE) \ for (i=0;i> 1); \ status |= 0x20; \ } \ temp &= 0x00FF; \ temp |= (lb[i+1] << 8); \ lb[i+1] = TABLE[temp | ATTR]; \ if ((temp & 0x8000) && !(status & 0x20)) \ { \ spr_col = (v_counter << 8) | ((xpos + i + 1 + 13) >> 1); \ status |= 0x20; \ } \ } \ } /* Pixels conversion macro */ /* 4-bit color channels are either compressed to 2/3-bit or dithered to 5/6/8-bit equivalents */ /* 3:3:2 RGB */ #if defined(USE_8BPP_RENDERING) #define MAKE_PIXEL(r,g,b) (((r) >> 1) << 5 | ((g) >> 1) << 2 | (b) >> 2) /* 5:5:5 RGB */ #elif defined(USE_15BPP_RENDERING) #if defined(USE_ABGR) #define MAKE_PIXEL(r,g,b) ((1 << 15) | (b) << 11 | ((b) >> 3) << 10 | (g) << 6 | ((g) >> 3) << 5 | (r) << 1 | (r) >> 3) #else #define MAKE_PIXEL(r,g,b) ((1 << 15) | (r) << 11 | ((r) >> 3) << 10 | (g) << 6 | ((g) >> 3) << 5 | (b) << 1 | (b) >> 3) #endif /* 5:6:5 RGB */ #elif defined(USE_16BPP_RENDERING) #define MAKE_PIXEL(r,g,b) ((r) << 12 | ((r) >> 3) << 11 | (g) << 7 | ((g) >> 2) << 5 | (b) << 1 | (b) >> 3) /* 8:8:8 RGB */ #elif defined(USE_32BPP_RENDERING) #define MAKE_PIXEL(r,g,b) ((0xff << 24) | (r) << 20 | (r) << 16 | (g) << 12 | (g) << 8 | (b) << 4 | (b)) #endif /* Window & Plane A clipping */ static struct clip_t { uint8 left; uint8 right; uint8 enable; } clip[2]; /* Pattern attribute (priority + palette bits) expansion table */ static const uint32 atex_table[] = { 0x00000000, 0x10101010, 0x20202020, 0x30303030, 0x40404040, 0x50505050, 0x60606060, 0x70707070 }; /* fixed Master System palette for Modes 0,1,2,3 */ static const uint8 tms_crom[16] = { 0x00, 0x00, 0x08, 0x0C, 0x10, 0x30, 0x01, 0x3C, 0x02, 0x03, 0x05, 0x0F, 0x04, 0x33, 0x15, 0x3F }; /* original SG-1000 palette */ #if defined(USE_8BPP_RENDERING) static const uint8 tms_palette[16] = { 0x00, 0x00, 0x39, 0x79, 0x4B, 0x6F, 0xC9, 0x5B, 0xE9, 0xED, 0xD5, 0xD9, 0x35, 0xCE, 0xDA, 0xFF }; #elif defined(USE_15BPP_RENDERING) static const uint16 tms_palette[16] = { 0x8000, 0x8000, 0x9308, 0xAF6F, 0xA95D, 0xBDDF, 0xE949, 0xA3BE, 0xFD4A, 0xFDEF, 0xEB0A, 0xF330, 0x92A7, 0xE177, 0xE739, 0xFFFF }; #elif defined(USE_16BPP_RENDERING) static const uint16 tms_palette[16] = { 0x0000, 0x0000, 0x2648, 0x5ECF, 0x52BD, 0x7BBE, 0xD289, 0x475E, 0xF2AA, 0xFBCF, 0xD60A, 0xE670, 0x2567, 0xC2F7, 0xCE59, 0xFFFF }; #elif defined(USE_32BPP_RENDERING) static const uint32 tms_palette[16] = { 0xFF000000, 0xFF000000, 0xFF21C842, 0xFF5EDC78, 0xFF5455ED, 0xFF7D76FC, 0xFFD4524D, 0xFF42EBF5, 0xFFFC5554, 0xFFFF7978, 0xFFD4C154, 0xFFE6CE80, 0xFF21B03B, 0xFFC95BB4, 0xFFCCCCCC, 0xFFFFFFFF }; #endif /* Cached and flipped patterns */ static uint8 bg_pattern_cache[0x80000] __attribute__((aligned(4))); /* Sprite pattern name offset look-up table (Mode 5) */ static uint8 name_lut[0x400]; /* Bitplane to packed pixel look-up table (Mode 4) */ static uint32 bp_lut[0x10000]; /* Layer priority pixel look-up tables */ static uint8 lut[LUT_MAX][LUT_SIZE]; /* Output pixel data look-up tables*/ static PIXEL_OUT_T pixel[0x100]; static PIXEL_OUT_T pixel_lut[3][0x200]; static PIXEL_OUT_T pixel_lut_m4[0x40]; /* Background & Sprite line buffers */ static uint8 linebuf[2][0x200]; /* Sprite limit flag */ static uint8 spr_ovr; /* Sprite parsing lists */ typedef struct { uint16 ypos; uint16 xpos; uint16 attr; uint16 size; } object_info_t; static object_info_t obj_info[2][20]; /* Sprite Counter */ static uint8 object_count[2]; /* Sprite Collision Info */ uint16 spr_col; /* Function pointers */ void (*render_bg)(int line); void (*render_obj)(int line); void (*parse_satb)(int line); void (*update_bg_pattern_cache)(int index); /*--------------------------------------------------------------------------*/ /* Sprite pattern name offset look-up table function (Mode 5) */ /*--------------------------------------------------------------------------*/ static void make_name_lut(void) { int vcol, vrow; int width, height; int flipx, flipy; int i; for (i = 0; i < 0x400; i += 1) { /* Sprite settings */ vcol = i & 3; vrow = (i >> 2) & 3; height = (i >> 4) & 3; width = (i >> 6) & 3; flipx = (i >> 8) & 1; flipy = (i >> 9) & 1; if ((vrow > height) || vcol > width) { /* Invalid settings (unused) */ name_lut[i] = -1; } else { /* Adjust column & row index if sprite is flipped */ if(flipx) vcol = (width - vcol); if(flipy) vrow = (height - vrow); /* Pattern offset (pattern order is up->down->left->right) */ name_lut[i] = vrow + (vcol * (height + 1)); } } } /*--------------------------------------------------------------------------*/ /* Bitplane to packed pixel look-up table function (Mode 4) */ /*--------------------------------------------------------------------------*/ static void make_bp_lut(void) { int x,i,j; uint32 out; /* ---------------------- */ /* Pattern color encoding */ /* -------------------------------------------------------------------------*/ /* 4 byteplanes are required to define one pattern line (8 pixels) */ /* A single pixel color is coded with 4 bits (c3 c2 c1 c0) */ /* Each bit is coming from byteplane bits, as explained below: */ /* pixel 0: c3 = bp3 bit 7, c2 = bp2 bit 7, c1 = bp1 bit 7, c0 = bp0 bit 7 */ /* pixel 1: c3 = bp3 bit 6, c2 = bp2 bit 6, c1 = bp1 bit 6, c0 = bp0 bit 6 */ /* ... */ /* pixel 7: c3 = bp3 bit 0, c2 = bp2 bit 0, c1 = bp1 bit 0, c0 = bp0 bit 0 */ /* -------------------------------------------------------------------------*/ for(i = 0; i < 0x100; i++) for(j = 0; j < 0x100; j++) { out = 0; for(x = 0; x < 8; x++) { /* pixel line data = hh00gg00ff00ee00dd00cc00bb00aa00 (32-bit) */ /* aa-hh = upper or lower 2-bit values of pixels 0-7 (shifted) */ out |= (j & (0x80 >> x)) ? (uint32)(8 << (x << 2)) : 0; out |= (i & (0x80 >> x)) ? (uint32)(4 << (x << 2)) : 0; } /* i = low byte in VRAM (bp0 or bp2) */ /* j = high byte in VRAM (bp1 or bp3) */ #ifdef LSB_FIRST bp_lut[(j << 8) | (i)] = out; #else bp_lut[(i << 8) | (j)] = out; #endif } } /*--------------------------------------------------------------------------*/ /* Layers priority pixel look-up tables functions */ /*--------------------------------------------------------------------------*/ /* Input (bx): d5-d0=color, d6=priority, d7=unused */ /* Input (ax): d5-d0=color, d6=priority, d7=unused */ /* Output: d5-d0=color, d6=priority, d7=zero */ static uint32 make_lut_bg(uint32 bx, uint32 ax) { int bf = (bx & 0x7F); int bp = (bx & 0x40); int b = (bx & 0x0F); int af = (ax & 0x7F); int ap = (ax & 0x40); int a = (ax & 0x0F); int c = (ap ? (a ? af : bf) : (bp ? (b ? bf : af) : (a ? af : bf))); /* Strip palette & priority bits from transparent pixels */ if((c & 0x0F) == 0x00) c &= 0x80; return (c); } /* Input (bx): d5-d0=color, d6=priority, d7=unused */ /* Input (sx): d5-d0=color, d6=priority, d7=unused */ /* Output: d5-d0=color, d6=priority, d7=intensity select (0=half/1=normal) */ static uint32 make_lut_bg_ste(uint32 bx, uint32 ax) { int bf = (bx & 0x7F); int bp = (bx & 0x40); int b = (bx & 0x0F); int af = (ax & 0x7F); int ap = (ax & 0x40); int a = (ax & 0x0F); int c = (ap ? (a ? af : bf) : (bp ? (b ? bf : af) : (a ? af : bf))); /* Half intensity when both pixels are low priority */ c |= ((ap | bp) << 1); /* Strip palette & priority bits from transparent pixels */ if((c & 0x0F) == 0x00) c &= 0x80; return (c); } /* Input (bx): d5-d0=color, d6=priority/1, d7=sprite pixel marker */ /* Input (sx): d5-d0=color, d6=priority, d7=unused */ /* Output: d5-d0=color, d6=priority, d7=sprite pixel marker */ static uint32 make_lut_obj(uint32 bx, uint32 sx) { int c; int bf = (bx & 0x7F); int bs = (bx & 0x80); int sf = (sx & 0x7F); if((sx & 0x0F) == 0) return bx; c = (bs ? bf : sf); /* Strip palette bits from transparent pixels */ if((c & 0x0F) == 0x00) c &= 0xC0; return (c | 0x80); } /* Input (bx): d5-d0=color, d6=priority, d7=opaque sprite pixel marker */ /* Input (sx): d5-d0=color, d6=priority, d7=unused */ /* Output: d5-d0=color, d6=zero/priority, d7=opaque sprite pixel marker */ static uint32 make_lut_bgobj(uint32 bx, uint32 sx) { int c; int bf = (bx & 0x3F); int bs = (bx & 0x80); int bp = (bx & 0x40); int b = (bx & 0x0F); int sf = (sx & 0x3F); int sp = (sx & 0x40); int s = (sx & 0x0F); if(s == 0) return bx; /* Previous sprite has higher priority */ if(bs) return bx; c = (sp ? sf : (bp ? (b ? bf : sf) : sf)); /* Strip palette & priority bits from transparent pixels */ if((c & 0x0F) == 0x00) c &= 0x80; return (c | 0x80); } /* Input (bx): d5-d0=color, d6=priority, d7=intensity (half/normal) */ /* Input (sx): d5-d0=color, d6=priority, d7=sprite marker */ /* Output: d5-d0=color, d6=intensity (half/normal), d7=(double/invalid) */ static uint32 make_lut_bgobj_ste(uint32 bx, uint32 sx) { int c; int bf = (bx & 0x3F); int bp = (bx & 0x40); int b = (bx & 0x0F); int bi = (bx & 0x80) >> 1; int sf = (sx & 0x3F); int sp = (sx & 0x40); int s = (sx & 0x0F); int si = sp | bi; if(sp) { if(s) { if((sf & 0x3E) == 0x3E) { if(sf & 1) { c = (bf | 0x00); } else { c = (bx & 0x80) ? (bf | 0x80) : (bf | 0x40); } } else { if(sf == 0x0E || sf == 0x1E || sf == 0x2E) { c = (sf | 0x40); } else { c = (sf | si); } } } else { c = (bf | bi); } } else { if(bp) { if(b) { c = (bf | bi); } else { if(s) { if((sf & 0x3E) == 0x3E) { if(sf & 1) { c = (bf | 0x00); } else { c = (bx & 0x80) ? (bf | 0x80) : (bf | 0x40); } } else { if(sf == 0x0E || sf == 0x1E || sf == 0x2E) { c = (sf | 0x40); } else { c = (sf | si); } } } else { c = (bf | bi); } } } else { if(s) { if((sf & 0x3E) == 0x3E) { if(sf & 1) { c = (bf | 0x00); } else { c = (bx & 0x80) ? (bf | 0x80) : (bf | 0x40); } } else { if(sf == 0x0E || sf == 0x1E || sf == 0x2E) { c = (sf | 0x40); } else { c = (sf | si); } } } else { c = (bf | bi); } } } if((c & 0x0f) == 0x00) c &= 0xC0; return (c); } /* Input (bx): d3-d0=color, d4=palette, d5=priority, d6=zero, d7=sprite pixel marker */ /* Input (sx): d3-d0=color, d7-d4=zero */ /* Output: d3-d0=color, d4=palette, d5=zero/priority, d6=zero, d7=sprite pixel marker */ static uint32 make_lut_bgobj_m4(uint32 bx, uint32 sx) { int c; int bf = (bx & 0x3F); int bs = (bx & 0x80); int bp = (bx & 0x20); int b = (bx & 0x0F); int s = (sx & 0x0F); int sf = (s | 0x10); /* force palette bit */ /* Transparent sprite pixel */ if(s == 0) return bx; /* Previous sprite has higher priority */ if(bs) return bx; /* note: priority bit is always 0 for Modes 0,1,2,3 */ c = (bp ? (b ? bf : sf) : sf); return (c | 0x80); } /*--------------------------------------------------------------------------*/ /* Pixel layer merging function */ /*--------------------------------------------------------------------------*/ INLINE void merge(uint8 *srca, uint8 *srcb, uint8 *dst, uint8 *table, int width) { do { *dst++ = table[(*srcb++ << 8) | (*srca++)]; } while (--width); } /*--------------------------------------------------------------------------*/ /* Pixel color lookup tables initialization */ /*--------------------------------------------------------------------------*/ static void palette_init(void) { int r, g, b, i; /************************************************/ /* Each R,G,B color channel is 4-bit with a */ /* total of 15 different intensity levels. */ /* */ /* Color intensity depends on the mode: */ /* */ /* normal : xxx0 (0-14) */ /* shadow : 0xxx (0-7) */ /* highlight: 1xxx - 1 (7-14) */ /* mode4 : xxxx(*) (0-15) */ /* GG mode : xxxx (0-15) */ /* */ /* with x = original CRAM value (2, 3 or 4-bit) */ /* (*) 2-bit CRAM value is expanded to 4-bit */ /************************************************/ /* Initialize Mode 5 pixel color look-up tables */ for (i = 0; i < 0x200; i++) { /* CRAM 9-bit value (BBBGGGRRR) */ r = (i >> 0) & 7; g = (i >> 3) & 7; b = (i >> 6) & 7; /* Convert to output pixel format */ pixel_lut[0][i] = MAKE_PIXEL(r,g,b); pixel_lut[1][i] = MAKE_PIXEL(r<<1,g<<1,b<<1); pixel_lut[2][i] = MAKE_PIXEL(r+7,g+7,b+7); } /* Initialize Mode 4 pixel color look-up table */ for (i = 0; i < 0x40; i++) { /* CRAM 6-bit value (000BBGGRR) */ r = (i >> 0) & 3; g = (i >> 2) & 3; b = (i >> 4) & 3; /* Expand to full range & convert to output pixel format */ pixel_lut_m4[i] = MAKE_PIXEL((r << 2) | r, (g << 2) | g, (b << 2) | b); } } /*--------------------------------------------------------------------------*/ /* Color palette update functions */ /*--------------------------------------------------------------------------*/ void color_update_m4(int index, unsigned int data) { switch (system_hw) { case SYSTEM_GG: { /* CRAM value (BBBBGGGGRRRR) */ int r = (data >> 0) & 0x0F; int g = (data >> 4) & 0x0F; int b = (data >> 8) & 0x0F; /* Convert to output pixel */ data = MAKE_PIXEL(r,g,b); break; } case SYSTEM_SG: case SYSTEM_SGII: { /* Fixed TMS99xx palette */ if (index & 0x0F) { /* Colors 1-15 */ data = tms_palette[index & 0x0F]; } else { /* Backdrop color */ data = tms_palette[reg[7] & 0x0F]; } break; } default: { /* Test M4 bit */ if (!(reg[0] & 0x04)) { if (system_hw & SYSTEM_MD) { /* Invalid Mode (black screen) */ data = 0x00; } else if (system_hw != SYSTEM_GGMS) { /* Fixed CRAM palette */ if (index & 0x0F) { /* Colors 1-15 */ data = tms_crom[index & 0x0F]; } else { /* Backdrop color */ data = tms_crom[reg[7] & 0x0F]; } } } /* Mode 4 palette */ data = pixel_lut_m4[data & 0x3F]; break; } } /* Input pixel: x0xiiiii (normal) or 01000000 (backdrop) */ if (reg[0] & 0x04) { /* Mode 4 */ pixel[0x00 | index] = data; pixel[0x20 | index] = data; pixel[0x80 | index] = data; pixel[0xA0 | index] = data; } else { /* TMS99xx modes (palette bit forced to 1 because Game Gear uses CRAM palette #1) */ if ((index == 0x40) || (index == (0x10 | (reg[7] & 0x0F)))) { /* Update backdrop color */ pixel[0x40] = data; /* Update transparent color */ pixel[0x10] = data; pixel[0x30] = data; pixel[0x90] = data; pixel[0xB0] = data; } if (index & 0x0F) { /* update non-transparent colors */ pixel[0x00 | index] = data; pixel[0x20 | index] = data; pixel[0x80 | index] = data; pixel[0xA0 | index] = data; } } } void color_update_m5(int index, unsigned int data) { /* Palette Mode */ if (!(reg[0] & 0x04)) { /* Color value is limited to 00X00X00X */ data &= 0x49; } if(reg[12] & 0x08) { /* Mode 5 (Shadow/Normal/Highlight) */ pixel[0x00 | index] = pixel_lut[0][data]; pixel[0x40 | index] = pixel_lut[1][data]; pixel[0x80 | index] = pixel_lut[2][data]; } else { /* Mode 5 (Normal) */ data = pixel_lut[1][data]; /* Input pixel: xxiiiiii */ pixel[0x00 | index] = data; pixel[0x40 | index] = data; pixel[0x80 | index] = data; } } /*--------------------------------------------------------------------------*/ /* Background layers rendering functions */ /*--------------------------------------------------------------------------*/ /* Graphics I */ void render_bg_m0(int line) { uint8 color, name, pattern; uint8 *lb = &linebuf[0][0x20]; uint8 *nt = &vram[((reg[2] << 10) & 0x3C00) + ((line & 0xF8) << 2)]; uint8 *ct = &vram[((reg[3] << 6) & 0x3FC0)]; uint8 *pg = &vram[((reg[4] << 11) & 0x3800) + (line & 7)]; /* 32 x 8 pixels */ int width = 32; do { name = *nt++; color = ct[name >> 3]; pattern = pg[name << 3]; *lb++ = 0x10 | ((color >> (((pattern >> 7) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 6) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 5) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 4) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 3) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 2) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 1) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 0) & 1) << 2)) & 0x0F); } while (--width); } /* Text */ void render_bg_m1(int line) { uint8 pattern; uint8 color = reg[7]; uint8 *lb = &linebuf[0][0x20]; uint8 *nt = &vram[((reg[2] << 10) & 0x3C00) + ((line >> 3) * 40)]; uint8 *pg = &vram[((reg[4] << 11) & 0x3800) + (line & 7)]; /* 40 x 6 pixels */ int width = 40; /* Left border (8 pixels) */ memset (lb, 0x40, 8); lb += 8; do { pattern = pg[*nt++ << 3]; *lb++ = 0x10 | ((color >> (((pattern >> 7) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 6) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 5) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 4) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 3) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 2) & 1) << 2)) & 0x0F); } while (--width); /* Right borders (8 pixels) */ memset(lb, 0x40, 8); } /* Text + extended PG */ void render_bg_m1x(int line) { uint8 pattern; uint8 *pg; uint8 color = reg[7]; uint8 *lb = &linebuf[0][0x20]; uint8 *nt = &vram[((reg[2] << 10) & 0x3C00) + ((line >> 3) * 40)]; uint16 pg_mask = ~0x3800 ^ (reg[4] << 11); /* 40 x 6 pixels */ int width = 40; /* Unused bits used as a mask on TMS99xx & 315-5124 VDP only */ if (system_hw > SYSTEM_SMS) { pg_mask |= 0x1800; } pg = &vram[((0x2000 + ((line & 0xC0) << 5)) & pg_mask) + (line & 7)]; /* Left border (8 pixels) */ memset (lb, 0x40, 8); lb += 8; do { pattern = pg[*nt++ << 3]; *lb++ = 0x10 | ((color >> (((pattern >> 7) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 6) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 5) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 4) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 3) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 2) & 1) << 2)) & 0x0F); } while (--width); /* Right borders (8 pixels) */ memset(lb, 0x40, 8); } /* Graphics II */ void render_bg_m2(int line) { uint8 color, pattern; uint16 name; uint8 *ct, *pg; uint8 *lb = &linebuf[0][0x20]; uint8 *nt = &vram[((reg[2] << 10) & 0x3C00) + ((line & 0xF8) << 2)]; uint16 ct_mask = ~0x3FC0 ^ (reg[3] << 6); uint16 pg_mask = ~0x3800 ^ (reg[4] << 11); /* 32 x 8 pixels */ int width = 32; /* Unused bits used as a mask on TMS99xx & 315-5124 VDP only */ if (system_hw > SYSTEM_SMS) { ct_mask |= 0x1FC0; pg_mask |= 0x1800; } ct = &vram[((0x2000 + ((line & 0xC0) << 5)) & ct_mask) + (line & 7)]; pg = &vram[((0x2000 + ((line & 0xC0) << 5)) & pg_mask) + (line & 7)]; do { name = *nt++ << 3 ; color = ct[name & ct_mask]; pattern = pg[name]; *lb++ = 0x10 | ((color >> (((pattern >> 7) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 6) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 5) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 4) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 3) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 2) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 1) & 1) << 2)) & 0x0F); *lb++ = 0x10 | ((color >> (((pattern >> 0) & 1) << 2)) & 0x0F); } while (--width); } /* Multicolor */ void render_bg_m3(int line) { uint8 color; uint8 *lb = &linebuf[0][0x20]; uint8 *nt = &vram[((reg[2] << 10) & 0x3C00) + ((line & 0xF8) << 2)]; uint8 *pg = &vram[((reg[4] << 11) & 0x3800) + ((line >> 2) & 7)]; /* 32 x 8 pixels */ int width = 32; do { color = pg[*nt++ << 3]; *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 0) & 0x0F); *lb++ = 0x10 | ((color >> 0) & 0x0F); *lb++ = 0x10 | ((color >> 0) & 0x0F); *lb++ = 0x10 | ((color >> 0) & 0x0F); } while (--width); } /* Multicolor + extended PG */ void render_bg_m3x(int line) { uint8 color; uint8 *pg; uint8 *lb = &linebuf[0][0x20]; uint8 *nt = &vram[((reg[2] << 10) & 0x3C00) + ((line & 0xF8) << 2)]; uint16 pg_mask = ~0x3800 ^ (reg[4] << 11); /* 32 x 8 pixels */ int width = 32; /* Unused bits used as a mask on TMS99xx & 315-5124 VDP only */ if (system_hw > SYSTEM_SMS) { pg_mask |= 0x1800; } pg = &vram[((0x2000 + ((line & 0xC0) << 5)) & pg_mask) + ((line >> 2) & 7)]; do { color = pg[*nt++ << 3]; *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 0) & 0x0F); *lb++ = 0x10 | ((color >> 0) & 0x0F); *lb++ = 0x10 | ((color >> 0) & 0x0F); *lb++ = 0x10 | ((color >> 0) & 0x0F); } while (--width); } /* Invalid (2+3/1+2+3) */ void render_bg_inv(int line) { uint8 color = reg[7]; uint8 *lb = &linebuf[0][0x20]; /* 40 x 6 pixels */ int width = 40; /* Left border (8 pixels) */ memset (lb, 0x40, 8); lb += 8; do { *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 4) & 0x0F); *lb++ = 0x10 | ((color >> 0) & 0x0F); *lb++ = 0x10 | ((color >> 0) & 0x0F); } while (--width); /* Right borders (8 pixels) */ memset(lb, 0x40, 8); } /* Mode 4 */ void render_bg_m4(int line) { int column; uint16 *nt; uint32 attr, atex, *src; /* 32 x 8 pixels */ int width = 32; /* Horizontal scrolling */ int index = ((reg[0] & 0x40) && (line < 0x10)) ? 0x100 : reg[0x08]; int shift = index & 7; /* Background line buffer */ uint32 *dst = (uint32 *)&linebuf[0][0x20 + shift]; /* Vertical scrolling */ int v_line = line + vscroll; /* Pattern name table mask */ uint16 nt_mask = ~0x3C00 ^ (reg[2] << 10); /* Unused bits used as a mask on TMS99xx & 315-5124 VDP only */ if (system_hw > SYSTEM_SMS) { nt_mask |= 0x400; } /* Test for extended modes (Master System II & Game gear VDP only) */ if (bitmap.viewport.h > 192) { /* Vertical scroll mask */ v_line = v_line % 256; /* Pattern name Table */ nt = (uint16 *)&vram[(0x3700 & nt_mask) + ((v_line >> 3) << 6)]; } else { /* Vertical scroll mask */ v_line = v_line % 224; /* Pattern name Table */ nt = (uint16 *)&vram[(0x3800 + ((v_line >> 3) << 6)) & nt_mask]; } /* Pattern row index */ v_line = (v_line & 7) << 3; /* Tile column index */ index = (0x100 - index) >> 3; /* Clip left-most column if required */ if (shift) { memset(&linebuf[0][0x20], 0, shift); index++; } /* Draw tiles */ for(column = 0; column < width; column++, index++) { /* Stop vertical scrolling for rightmost eight tiles */ if((column == 24) && (reg[0] & 0x80)) { /* Clear Pattern name table start address */ if (bitmap.viewport.h > 192) { nt = (uint16 *)&vram[(0x3700 & nt_mask) + ((line >> 3) << 6)]; } else { nt = (uint16 *)&vram[(0x3800 + ((line >> 3) << 6)) & nt_mask]; } /* Clear Pattern row index */ v_line = (line & 7) << 3; } /* Read name table attribute word */ attr = nt[index % width]; #ifndef LSB_FIRST attr = (((attr & 0xFF) << 8) | ((attr & 0xFF00) >> 8)); #endif /* Expand priority and palette bits */ atex = atex_table[(attr >> 11) & 3]; /* Cached pattern data line (4 bytes = 4 pixels at once) */ src = (uint32 *)&bg_pattern_cache[((attr & 0x7FF) << 6) | (v_line)]; /* Copy left & right half, adding the attribute bits in */ #ifdef ALIGN_LONG WRITE_LONG(dst, src[0] | atex); dst++; WRITE_LONG(dst, src[1] | atex); dst++; #else *dst++ = (src[0] | atex); *dst++ = (src[1] | atex); #endif } } /* Mode 5 */ #ifndef ALT_RENDERER void render_bg_m5(int line) { int column; uint32 atex, atbuf, *src, *dst; /* Common data */ uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; uint32 yscroll = *(uint32 *)&vsram[0]; uint32 pf_col_mask = playfield_col_mask; uint32 pf_row_mask = playfield_row_mask; uint32 pf_shift = playfield_shift; /* Window & Plane A */ int a = (reg[18] & 0x1F) << 3; int w = (reg[18] >> 7) & 1; /* Plane B width */ int start = 0; int end = bitmap.viewport.w >> 4; /* Plane B scroll */ #ifdef LSB_FIRST uint32 shift = (xscroll >> 16) & 0x0F; uint32 index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); uint32 v_line = (line + (yscroll >> 16)) & pf_row_mask; #else uint32 shift = (xscroll & 0x0F); uint32 index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); uint32 v_line = (line + yscroll) & pf_row_mask; #endif /* Plane B name table */ uint32 *nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; if(shift) { /* Plane B line buffer */ dst = (uint32 *)&linebuf[0][0x10 + shift]; atbuf = nt[(index - 1) & pf_col_mask]; DRAW_COLUMN(atbuf, v_line) } else { /* Plane B line buffer */ dst = (uint32 *)&linebuf[0][0x20]; } for(column = 0; column < end; column++, index++) { atbuf = nt[index & pf_col_mask]; DRAW_COLUMN(atbuf, v_line) } if (w == (line >= a)) { /* Window takes up entire line */ a = 0; w = 1; } else { /* Window and Plane A share the line */ a = clip[0].enable; w = clip[1].enable; } /* Plane A */ if (a) { /* Plane A width */ start = clip[0].left; end = clip[0].right; /* Plane A scroll */ #ifdef LSB_FIRST shift = (xscroll & 0x0F); index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); v_line = (line + yscroll) & pf_row_mask; #else shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); v_line = (line + (yscroll >> 16)) & pf_row_mask; #endif /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; if(shift) { /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x10 + shift + (start << 4)]; /* Window bug */ if (start) { atbuf = nt[index & pf_col_mask]; } else { atbuf = nt[(index - 1) & pf_col_mask]; } DRAW_COLUMN(atbuf, v_line) } else { /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; } for(column = start; column < end; column++, index++) { atbuf = nt[index & pf_col_mask]; DRAW_COLUMN(atbuf, v_line) } /* Window width */ start = clip[1].left; end = clip[1].right; } /* Window */ if (w) { /* Window name table */ nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; /* Pattern row index */ v_line = (line & 7) << 3; /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; for(column = start; column < end; column++) { atbuf = nt[column]; DRAW_COLUMN(atbuf, v_line) } } /* Merge background layers */ merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[(reg[12] & 0x08) >> 2], bitmap.viewport.w); } void render_bg_m5_vs(int line) { int column; uint32 atex, atbuf, *src, *dst; uint32 v_line, *nt; /* Common data */ uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; uint32 yscroll = 0; uint32 pf_col_mask = playfield_col_mask; uint32 pf_row_mask = playfield_row_mask; uint32 pf_shift = playfield_shift; uint32 *vs = (uint32 *)&vsram[0]; /* Window & Plane A */ int a = (reg[18] & 0x1F) << 3; int w = (reg[18] >> 7) & 1; /* Plane B width */ int start = 0; int end = bitmap.viewport.w >> 4; /* Plane B horizontal scroll */ #ifdef LSB_FIRST uint32 shift = (xscroll >> 16) & 0x0F; uint32 index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); #else uint32 shift = (xscroll & 0x0F); uint32 index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); #endif /* Left-most column vertical scrolling when partially shown horizontally (verified on PAL MD2) */ /* TODO: check on Genesis 3 models since it apparently behaves differently */ /* In H32 mode, vertical scrolling is disabled, in H40 mode, same value is used for both planes */ /* See Formula One / Kawasaki Superbike Challenge (H32) & Gynoug / Cutie Suzuki no Ringside Angel (H40) */ if (reg[12] & 1) { yscroll = vs[19] & (vs[19] >> 16); } if(shift) { /* Plane B vertical scroll */ v_line = (line + yscroll) & pf_row_mask; /* Plane B name table */ nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; /* Plane B line buffer */ dst = (uint32 *)&linebuf[0][0x10 + shift]; atbuf = nt[(index - 1) & pf_col_mask]; DRAW_COLUMN(atbuf, v_line) } else { /* Plane B line buffer */ dst = (uint32 *)&linebuf[0][0x20]; } for(column = 0; column < end; column++, index++) { /* Plane B vertical scroll */ #ifdef LSB_FIRST v_line = (line + (vs[column] >> 16)) & pf_row_mask; #else v_line = (line + vs[column]) & pf_row_mask; #endif /* Plane B name table */ nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; atbuf = nt[index & pf_col_mask]; DRAW_COLUMN(atbuf, v_line) } if (w == (line >= a)) { /* Window takes up entire line */ a = 0; w = 1; } else { /* Window and Plane A share the line */ a = clip[0].enable; w = clip[1].enable; } /* Plane A */ if (a) { /* Plane A width */ start = clip[0].left; end = clip[0].right; /* Plane A horizontal scroll */ #ifdef LSB_FIRST shift = (xscroll & 0x0F); index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); #else shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); #endif if(shift) { /* Plane A vertical scroll */ v_line = (line + yscroll) & pf_row_mask; /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x10 + shift + (start << 4)]; /* Window bug */ if (start) { atbuf = nt[index & pf_col_mask]; } else { atbuf = nt[(index - 1) & pf_col_mask]; } DRAW_COLUMN(atbuf, v_line) } else { /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; } for(column = start; column < end; column++, index++) { /* Plane A vertical scroll */ #ifdef LSB_FIRST v_line = (line + vs[column]) & pf_row_mask; #else v_line = (line + (vs[column] >> 16)) & pf_row_mask; #endif /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; atbuf = nt[index & pf_col_mask]; DRAW_COLUMN(atbuf, v_line) } /* Window width */ start = clip[1].left; end = clip[1].right; } /* Window */ if (w) { /* Window name table */ nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; /* Pattern row index */ v_line = (line & 7) << 3; /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; for(column = start; column < end; column++) { atbuf = nt[column]; DRAW_COLUMN(atbuf, v_line) } } /* Merge background layers */ merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[(reg[12] & 0x08) >> 2], bitmap.viewport.w); } void render_bg_m5_im2(int line) { int column; uint32 atex, atbuf, *src, *dst; /* Common data */ int odd = odd_frame; uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; uint32 yscroll = *(uint32 *)&vsram[0]; uint32 pf_col_mask = playfield_col_mask; uint32 pf_row_mask = playfield_row_mask; uint32 pf_shift = playfield_shift; /* Window & Plane A */ int a = (reg[18] & 0x1F) << 3; int w = (reg[18] >> 7) & 1; /* Plane B width */ int start = 0; int end = bitmap.viewport.w >> 4; /* Plane B scroll */ #ifdef LSB_FIRST uint32 shift = (xscroll >> 16) & 0x0F; uint32 index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); uint32 v_line = (line + (yscroll >> 17)) & pf_row_mask; #else uint32 shift = (xscroll & 0x0F); uint32 index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); uint32 v_line = (line + (yscroll >> 1)) & pf_row_mask; #endif /* Plane B name table */ uint32 *nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; if(shift) { /* Plane B line buffer */ dst = (uint32 *)&linebuf[0][0x10 + shift]; atbuf = nt[(index - 1) & pf_col_mask]; DRAW_COLUMN_IM2(atbuf, v_line) } else { /* Plane B line buffer */ dst = (uint32 *)&linebuf[0][0x20]; } for(column = 0; column < end; column++, index++) { atbuf = nt[index & pf_col_mask]; DRAW_COLUMN_IM2(atbuf, v_line) } if (w == (line >= a)) { /* Window takes up entire line */ a = 0; w = 1; } else { /* Window and Plane A share the line */ a = clip[0].enable; w = clip[1].enable; } /* Plane A */ if (a) { /* Plane A width */ start = clip[0].left; end = clip[0].right; /* Plane A scroll */ #ifdef LSB_FIRST shift = (xscroll & 0x0F); index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); v_line = (line + (yscroll >> 1)) & pf_row_mask; #else shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); v_line = (line + (yscroll >> 17)) & pf_row_mask; #endif /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; if(shift) { /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x10 + shift + (start << 4)]; /* Window bug */ if (start) { atbuf = nt[index & pf_col_mask]; } else { atbuf = nt[(index - 1) & pf_col_mask]; } DRAW_COLUMN_IM2(atbuf, v_line) } else { /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; } for(column = start; column < end; column++, index++) { atbuf = nt[index & pf_col_mask]; DRAW_COLUMN_IM2(atbuf, v_line) } /* Window width */ start = clip[1].left; end = clip[1].right; } /* Window */ if (w) { /* Window name table */ nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; /* Pattern row index */ v_line = ((line & 7) << 1 | odd) << 3; /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; for(column = start; column < end; column++) { atbuf = nt[column]; DRAW_COLUMN_IM2(atbuf, v_line) } } /* Merge background layers */ merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[(reg[12] & 0x08) >> 2], bitmap.viewport.w); } void render_bg_m5_im2_vs(int line) { int column; uint32 atex, atbuf, *src, *dst; uint32 v_line, *nt; /* Common data */ int odd = odd_frame; uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; uint32 yscroll = 0; uint32 pf_col_mask = playfield_col_mask; uint32 pf_row_mask = playfield_row_mask; uint32 pf_shift = playfield_shift; uint32 *vs = (uint32 *)&vsram[0]; /* Window & Plane A */ int a = (reg[18] & 0x1F) << 3; int w = (reg[18] >> 7) & 1; /* Plane B width */ int start = 0; int end = bitmap.viewport.w >> 4; /* Plane B horizontal scroll */ #ifdef LSB_FIRST uint32 shift = (xscroll >> 16) & 0x0F; uint32 index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); #else uint32 shift = (xscroll & 0x0F); uint32 index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); #endif /* Left-most column vertical scrolling when partially shown horizontally (verified on PAL MD2) */ /* TODO: check on Genesis 3 models since it apparently behaves differently */ /* In H32 mode, vertical scrolling is disabled, in H40 mode, same value is used for both planes */ /* See Formula One / Kawasaki Superbike Challenge (H32) & Gynoug / Cutie Suzuki no Ringside Angel (H40) */ if (reg[12] & 1) { yscroll = (vs[19] >> 1) & (vs[19] >> 17); } if(shift) { /* Plane B vertical scroll */ v_line = (line + yscroll) & pf_row_mask; /* Plane B name table */ nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; /* Plane B line buffer */ dst = (uint32 *)&linebuf[0][0x10 + shift]; atbuf = nt[(index - 1) & pf_col_mask]; DRAW_COLUMN_IM2(atbuf, v_line) } else { /* Plane B line buffer */ dst = (uint32 *)&linebuf[0][0x20]; } for(column = 0; column < end; column++, index++) { /* Plane B vertical scroll */ #ifdef LSB_FIRST v_line = (line + (vs[column] >> 17)) & pf_row_mask; #else v_line = (line + (vs[column] >> 1)) & pf_row_mask; #endif /* Plane B name table */ nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; atbuf = nt[index & pf_col_mask]; DRAW_COLUMN_IM2(atbuf, v_line) } if (w == (line >= a)) { /* Window takes up entire line */ a = 0; w = 1; } else { /* Window and Plane A share the line */ a = clip[0].enable; w = clip[1].enable; } /* Plane A */ if (a) { /* Plane A width */ start = clip[0].left; end = clip[0].right; /* Plane A horizontal scroll */ #ifdef LSB_FIRST shift = (xscroll & 0x0F); index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); #else shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); #endif if(shift) { /* Plane A vertical scroll */ v_line = (line + yscroll) & pf_row_mask; /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x10 + shift + (start << 4)]; /* Window bug */ if (start) { atbuf = nt[index & pf_col_mask]; } else { atbuf = nt[(index - 1) & pf_col_mask]; } DRAW_COLUMN_IM2(atbuf, v_line) } else { /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; } for(column = start; column < end; column++, index++) { /* Plane A vertical scroll */ #ifdef LSB_FIRST v_line = (line + (vs[column] >> 1)) & pf_row_mask; #else v_line = (line + (vs[column] >> 17)) & pf_row_mask; #endif /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; atbuf = nt[index & pf_col_mask]; DRAW_COLUMN_IM2(atbuf, v_line) } /* Window width */ start = clip[1].left; end = clip[1].right; } /* Window */ if (w) { /* Window name table */ nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; /* Pattern row index */ v_line = ((line & 7) << 1 | odd) << 3; /* Plane A line buffer */ dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; for(column = start; column < end; column++) { atbuf = nt[column]; DRAW_COLUMN_IM2(atbuf, v_line) } } /* Merge background layers */ merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[(reg[12] & 0x08) >> 2], bitmap.viewport.w); } #else void render_bg_m5(int line) { int column, start, end; uint32 atex, atbuf, *src, *dst; uint32 shift, index, v_line, *nt; uint8 *lb; /* Scroll Planes common data */ uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; uint32 yscroll = *(uint32 *)&vsram[0]; uint32 pf_col_mask = playfield_col_mask; uint32 pf_row_mask = playfield_row_mask; uint32 pf_shift = playfield_shift; /* Number of columns to draw */ int width = bitmap.viewport.w >> 4; /* Layer priority table */ uint8 *table = lut[(reg[12] & 8) >> 2]; /* Window vertical range (cell 0-31) */ int a = (reg[18] & 0x1F) << 3; /* Window position (0=top, 1=bottom) */ int w = (reg[18] >> 7) & 1; /* Test against current line */ if (w == (line >= a)) { /* Window takes up entire line */ a = 0; w = 1; } else { /* Window and Plane A share the line */ a = clip[0].enable; w = clip[1].enable; } /* Plane A */ if (a) { /* Plane A width */ start = clip[0].left; end = clip[0].right; /* Plane A scroll */ #ifdef LSB_FIRST shift = (xscroll & 0x0F); index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); v_line = (line + yscroll) & pf_row_mask; #else shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); v_line = (line + (yscroll >> 16)) & pf_row_mask; #endif /* Background line buffer */ dst = (uint32 *)&linebuf[0][0x20 + (start << 4) + shift]; /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; if(shift) { /* Left-most column is partially shown */ dst -= 4; /* Window bug */ if (start) { atbuf = nt[index & pf_col_mask]; } else { atbuf = nt[(index-1) & pf_col_mask]; } DRAW_COLUMN(atbuf, v_line) } for(column = start; column < end; column++, index++) { atbuf = nt[index & pf_col_mask]; DRAW_COLUMN(atbuf, v_line) } /* Window width */ start = clip[1].left; end = clip[1].right; } else { /* Window width */ start = 0; end = width; } /* Window Plane */ if (w) { /* Background line buffer */ dst = (uint32 *)&linebuf[0][0x20 + (start << 4)]; /* Window name table */ nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; /* Pattern row index */ v_line = (line & 7) << 3; for(column = start; column < end; column++) { atbuf = nt[column]; DRAW_COLUMN(atbuf, v_line) } } /* Plane B scroll */ #ifdef LSB_FIRST shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); v_line = (line + (yscroll >> 16)) & pf_row_mask; #else shift = (xscroll & 0x0F); index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); v_line = (line + yscroll) & pf_row_mask; #endif /* Plane B name table */ nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; /* Background line buffer */ lb = &linebuf[0][0x20]; if(shift) { /* Left-most column is partially shown */ lb -= (0x10 - shift); atbuf = nt[(index-1) & pf_col_mask]; DRAW_BG_COLUMN(atbuf, v_line, xscroll, yscroll) } for(column = 0; column < width; column++, index++) { atbuf = nt[index & pf_col_mask]; DRAW_BG_COLUMN(atbuf, v_line, xscroll, yscroll) } } void render_bg_m5_vs(int line) { int column, start, end; uint32 atex, atbuf, *src, *dst; uint32 shift, index, v_line, *nt; uint8 *lb; /* Scroll Planes common data */ uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; uint32 yscroll = 0; uint32 pf_col_mask = playfield_col_mask; uint32 pf_row_mask = playfield_row_mask; uint32 pf_shift = playfield_shift; uint32 *vs = (uint32 *)&vsram[0]; /* Number of columns to draw */ int width = bitmap.viewport.w >> 4; /* Layer priority table */ uint8 *table = lut[(reg[12] & 8) >> 2]; /* Window vertical range (cell 0-31) */ int a = (reg[18] & 0x1F) << 3; /* Window position (0=top, 1=bottom) */ int w = (reg[18] >> 7) & 1; /* Test against current line */ if (w == (line >= a)) { /* Window takes up entire line */ a = 0; w = 1; } else { /* Window and Plane A share the line */ a = clip[0].enable; w = clip[1].enable; } /* Left-most column vertical scrolling when partially shown horizontally */ /* Same value for both planes, only in 40-cell mode, verified on PAL MD2 */ /* See Gynoug, Cutie Suzuki no Ringside Angel, Formula One, Kawasaki Superbike Challenge */ if (reg[12] & 1) { yscroll = vs[19] & (vs[19] >> 16); } /* Plane A*/ if (a) { /* Plane A width */ start = clip[0].left; end = clip[0].right; /* Plane A horizontal scroll */ #ifdef LSB_FIRST shift = (xscroll & 0x0F); index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); #else shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); #endif /* Background line buffer */ dst = (uint32 *)&linebuf[0][0x20 + (start << 4) + shift]; if(shift) { /* Left-most column is partially shown */ dst -= 4; /* Plane A vertical scroll */ v_line = (line + yscroll) & pf_row_mask; /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; /* Window bug */ if (start) { atbuf = nt[index & pf_col_mask]; } else { atbuf = nt[(index-1) & pf_col_mask]; } DRAW_COLUMN(atbuf, v_line) } for(column = start; column < end; column++, index++) { /* Plane A vertical scroll */ #ifdef LSB_FIRST v_line = (line + vs[column]) & pf_row_mask; #else v_line = (line + (vs[column] >> 16)) & pf_row_mask; #endif /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; atbuf = nt[index & pf_col_mask]; DRAW_COLUMN(atbuf, v_line) } /* Window width */ start = clip[1].left; end = clip[1].right; } else { /* Window width */ start = 0; end = width; } /* Window Plane */ if (w) { /* Background line buffer */ dst = (uint32 *)&linebuf[0][0x20 + (start << 4)]; /* Window name table */ nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; /* Pattern row index */ v_line = (line & 7) << 3; for(column = start; column < end; column++) { atbuf = nt[column]; DRAW_COLUMN(atbuf, v_line) } } /* Plane B horizontal scroll */ #ifdef LSB_FIRST shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); #else shift = (xscroll & 0x0F); index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); #endif /* Background line buffer */ lb = &linebuf[0][0x20]; if(shift) { /* Left-most column is partially shown */ lb -= (0x10 - shift); /* Plane B vertical scroll */ v_line = (line + yscroll) & pf_row_mask; /* Plane B name table */ nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; atbuf = nt[(index-1) & pf_col_mask]; DRAW_BG_COLUMN(atbuf, v_line, xscroll, yscroll) } for(column = 0; column < width; column++, index++) { /* Plane B vertical scroll */ #ifdef LSB_FIRST v_line = (line + (vs[column] >> 16)) & pf_row_mask; #else v_line = (line + vs[column]) & pf_row_mask; #endif /* Plane B name table */ nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (v_line & 7) << 3; atbuf = nt[index & pf_col_mask]; DRAW_BG_COLUMN(atbuf, v_line, xscroll, yscroll) } } void render_bg_m5_im2(int line) { int column, start, end; uint32 atex, atbuf, *src, *dst; uint32 shift, index, v_line, *nt; uint8 *lb; /* Scroll Planes common data */ int odd = odd_frame; uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; uint32 yscroll = *(uint32 *)&vsram[0]; uint32 pf_col_mask = playfield_col_mask; uint32 pf_row_mask = playfield_row_mask; uint32 pf_shift = playfield_shift; /* Number of columns to draw */ int width = bitmap.viewport.w >> 4; /* Layer priority table */ uint8 *table = lut[(reg[12] & 8) >> 2]; /* Window vertical range (cell 0-31) */ int a = (reg[18] & 0x1F) << 3; /* Window position (0=top, 1=bottom) */ int w = (reg[18] >> 7) & 1; /* Test against current line */ if (w == (line >= a)) { /* Window takes up entire line */ a = 0; w = 1; } else { /* Window and Plane A share the line */ a = clip[0].enable; w = clip[1].enable; } /* Plane A */ if (a) { /* Plane A width */ start = clip[0].left; end = clip[0].right; /* Plane A scroll */ #ifdef LSB_FIRST shift = (xscroll & 0x0F); index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); v_line = (line + (yscroll >> 1)) & pf_row_mask; #else shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); v_line = (line + (yscroll >> 17)) & pf_row_mask; #endif /* Background line buffer */ dst = (uint32 *)&linebuf[0][0x20 + (start << 4) + shift]; /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; if(shift) { /* Left-most column is partially shown */ dst -= 4; /* Window bug */ if (start) { atbuf = nt[index & pf_col_mask]; } else { atbuf = nt[(index-1) & pf_col_mask]; } DRAW_COLUMN_IM2(atbuf, v_line) } for(column = start; column < end; column++, index++) { atbuf = nt[index & pf_col_mask]; DRAW_COLUMN_IM2(atbuf, v_line) } /* Window width */ start = clip[1].left; end = clip[1].right; } else { /* Window width */ start = 0; end = width; } /* Window Plane */ if (w) { /* Background line buffer */ dst = (uint32 *)&linebuf[0][0x20 + (start << 4)]; /* Window name table */ nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; /* Pattern row index */ v_line = ((line & 7) << 1 | odd) << 3; for(column = start; column < end; column++) { atbuf = nt[column]; DRAW_COLUMN_IM2(atbuf, v_line) } } /* Plane B scroll */ #ifdef LSB_FIRST shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); v_line = (line + (yscroll >> 17)) & pf_row_mask; #else shift = (xscroll & 0x0F); index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); v_line = (line + (yscroll >> 1)) & pf_row_mask; #endif /* Plane B name table */ nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; /* Background line buffer */ lb = &linebuf[0][0x20]; if(shift) { /* Left-most column is partially shown */ lb -= (0x10 - shift); atbuf = nt[(index-1) & pf_col_mask]; DRAW_BG_COLUMN_IM2(atbuf, v_line, xscroll, yscroll) } for(column = 0; column < width; column++, index++) { atbuf = nt[index & pf_col_mask]; DRAW_BG_COLUMN_IM2(atbuf, v_line, xscroll, yscroll) } } void render_bg_m5_im2_vs(int line) { int column, start, end; uint32 atex, atbuf, *src, *dst; uint32 shift, index, v_line, *nt; uint8 *lb; /* common data */ int odd = odd_frame; uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; uint32 yscroll = 0; uint32 pf_col_mask = playfield_col_mask; uint32 pf_row_mask = playfield_row_mask; uint32 pf_shift = playfield_shift; uint32 *vs = (uint32 *)&vsram[0]; /* Number of columns to draw */ int width = bitmap.viewport.w >> 4; /* Layer priority table */ uint8 *table = lut[(reg[12] & 8) >> 2]; /* Window vertical range (cell 0-31) */ uint32 a = (reg[18] & 0x1F) << 3; /* Window position (0=top, 1=bottom) */ uint32 w = (reg[18] >> 7) & 1; /* Test against current line */ if (w == (line >= a)) { /* Window takes up entire line */ a = 0; w = 1; } else { /* Window and Plane A share the line */ a = clip[0].enable; w = clip[1].enable; } /* Left-most column vertical scrolling when partially shown horizontally */ /* Same value for both planes, only in 40-cell mode, verified on PAL MD2 */ /* See Gynoug, Cutie Suzuki no Ringside Angel, Formula One, Kawasaki Superbike Challenge */ if (reg[12] & 1) { /* only in 40-cell mode, verified on MD2 */ yscroll = (vs[19] >> 1) & (vs[19] >> 17); } /* Plane A */ if (a) { /* Plane A width */ start = clip[0].left; end = clip[0].right; /* Plane A horizontal scroll */ #ifdef LSB_FIRST shift = (xscroll & 0x0F); index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); #else shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); #endif /* Background line buffer */ dst = (uint32 *)&linebuf[0][0x20 + (start << 4) + shift]; if(shift) { /* Left-most column is partially shown */ dst -= 4; /* Plane A vertical scroll */ v_line = (line + yscroll) & pf_row_mask; /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; /* Window bug */ if (start) { atbuf = nt[index & pf_col_mask]; } else { atbuf = nt[(index-1) & pf_col_mask]; } DRAW_COLUMN_IM2(atbuf, v_line) } for(column = start; column < end; column++, index++) { /* Plane A vertical scroll */ #ifdef LSB_FIRST v_line = (line + (vs[column] >> 1)) & pf_row_mask; #else v_line = (line + (vs[column] >> 17)) & pf_row_mask; #endif /* Plane A name table */ nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; atbuf = nt[index & pf_col_mask]; DRAW_COLUMN_IM2(atbuf, v_line) } /* Window width */ start = clip[1].left; end = clip[1].right; } else { /* Window width */ start = 0; end = width; } /* Window Plane */ if (w) { /* Background line buffer */ dst = (uint32 *)&linebuf[0][0x20 + (start << 4)]; /* Window name table */ nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; /* Pattern row index */ v_line = ((line & 7) << 1 | odd) << 3; for(column = start; column < end; column++) { atbuf = nt[column]; DRAW_COLUMN_IM2(atbuf, v_line) } } /* Plane B horizontal scroll */ #ifdef LSB_FIRST shift = (xscroll >> 16) & 0x0F; index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); #else shift = (xscroll & 0x0F); index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); #endif /* Background line buffer */ lb = &linebuf[0][0x20]; if(shift) { /* Left-most column is partially shown */ lb -= (0x10 - shift); /* Plane B vertical scroll */ v_line = (line + yscroll) & pf_row_mask; /* Plane B name table */ nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; atbuf = nt[(index-1) & pf_col_mask]; DRAW_BG_COLUMN_IM2(atbuf, v_line, xscroll, yscroll) } for(column = 0; column < width; column++, index++) { /* Plane B vertical scroll */ #ifdef LSB_FIRST v_line = (line + (vs[column] >> 17)) & pf_row_mask; #else v_line = (line + (vs[column] >> 1)) & pf_row_mask; #endif /* Plane B name table */ nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; atbuf = nt[index & pf_col_mask]; DRAW_BG_COLUMN_IM2(atbuf, v_line, xscroll, yscroll) } } #endif /*--------------------------------------------------------------------------*/ /* Sprite layer rendering functions */ /*--------------------------------------------------------------------------*/ void render_obj_tms(int line) { int x, start, end; uint8 *lb, *sg; uint8 color, pattern[2]; uint16 temp; /* Sprite list for current line */ object_info_t *object_info = obj_info[line]; int count = object_count[line]; /* Default sprite width (8 pixels) */ int width = 8; /* Adjust width for 16x16 sprites */ width <<= ((reg[1] & 0x02) >> 1); /* Adjust width for zoomed sprites */ width <<= (reg[1] & 0x01); /* Latch SOVR flag from previous line to VDP status */ status |= spr_ovr; /* Clear SOVR flag for current line */ spr_ovr = 0; /* Draw sprites in front-to-back order */ while (count--) { /* Sprite X position */ start = object_info->xpos; /* Sprite Color + Early Clock bit */ color = object_info->size; /* X position shift (32 pixels) */ start -= ((color & 0x80) >> 2); /* Pointer to line buffer */ lb = &linebuf[0][0x20 + start]; if ((start + width) > 256) { /* Clip sprites on right edge */ end = 256 - start; start = 0; } else { end = width; if (start < 0) { /* Clip sprites on left edge */ start = 0 - start; } else { start = 0; } } /* Sprite Color (0-15) */ color &= 0x0F; /* Sprite Pattern Name */ temp = object_info->attr; /* Mask two LSB for 16x16 sprites */ temp &= ~((reg[1] & 0x02) >> 0); temp &= ~((reg[1] & 0x02) >> 1); /* Pointer to sprite generator table */ sg = (uint8 *)&vram[((reg[6] << 11) & 0x3800) | (temp << 3) | object_info->ypos]; /* Sprite Pattern data (2 x 8 pixels) */ pattern[0] = sg[0x00]; pattern[1] = sg[0x10]; if (reg[1] & 0x01) { /* Zoomed sprites are rendered at half speed */ for (x=start; x> 4) & 1]; temp = (temp >> (7 - ((x >> 1) & 7))) & 0x01; temp = temp * color; temp |= (lb[x] << 8); lb[x] = lut[5][temp]; status |= ((temp & 0x8000) >> 10); temp &= 0x00FF; temp |= (lb[x+1] << 8); lb[x+1] = lut[5][temp]; status |= ((temp & 0x8000) >> 10); } } else { /* Normal sprites */ for (x=start; x> 3) & 1]; temp = (temp >> (7 - (x & 7))) & 0x01; temp = temp * color; temp |= (lb[x] << 8); lb[x] = lut[5][temp]; status |= ((temp & 0x8000) >> 10); } } /* Next sprite entry */ object_info++; } /* handle Game Gear reduced screen (160x144) */ if ((system_hw == SYSTEM_GG) && !config.gg_extra && (v_counter < bitmap.viewport.h)) { int line = v_counter - (bitmap.viewport.h - 144) / 2; if ((line < 0) || (line >= 144)) { memset(&linebuf[0][0x20], 0x40, 256); } else { if (bitmap.viewport.x > 0) { memset(&linebuf[0][0x20], 0x40, 48); memset(&linebuf[0][0x20+48+160], 0x40, 48); } } } } void render_obj_m4(int line) { int i, xpos, end; uint8 *src, *lb; uint16 temp; /* Sprite list for current line */ object_info_t *object_info = obj_info[line]; int count = object_count[line]; /* Default sprite width */ int width = 8; /* Sprite Generator address mask (LSB is masked for 8x16 sprites) */ uint16 sg_mask = (~0x1C0 ^ (reg[6] << 6)) & (~((reg[1] & 0x02) >> 1)); /* Zoomed sprites (not working on Genesis VDP) */ if (system_hw < SYSTEM_MD) { width <<= (reg[1] & 0x01); } /* Unused bits used as a mask on 315-5124 VDP only */ if (system_hw > SYSTEM_SMS) { sg_mask |= 0xC0; } /* Latch SOVR flag from previous line to VDP status */ status |= spr_ovr; /* Clear SOVR flag for current line */ spr_ovr = 0; /* Draw sprites in front-to-back order */ while (count--) { /* Sprite pattern index */ temp = (object_info->attr | 0x100) & sg_mask; /* Pointer to pattern cache line */ src = (uint8 *)&bg_pattern_cache[(temp << 6) | (object_info->ypos << 3)]; /* Sprite X position */ xpos = object_info->xpos; /* X position shift */ xpos -= (reg[0] & 0x08); if (xpos < 0) { /* Clip sprites on left edge */ src = src - xpos; end = xpos + width; xpos = 0; } else if ((xpos + width) > 256) { /* Clip sprites on right edge */ end = 256 - xpos; } else { /* Sprite maximal width */ end = width; } /* Pointer to line buffer */ lb = &linebuf[0][0x20 + xpos]; if (width > 8) { /* Draw sprite pattern (zoomed sprites are rendered at half speed) */ DRAW_SPRITE_TILE_ACCURATE_2X(end,0,lut[5]) /* 315-5124 VDP specific */ if (system_hw < SYSTEM_SMS2) { /* only 4 first sprites can be zoomed */ if (count == (object_count[line] - 4)) { /* Set default width for remaining sprites */ width = 8; } } } else { /* Draw sprite pattern */ DRAW_SPRITE_TILE_ACCURATE(end,0,lut[5]) } /* Next sprite entry */ object_info++; } /* handle Game Gear reduced screen (160x144) */ if ((system_hw == SYSTEM_GG) && !config.gg_extra && (v_counter < bitmap.viewport.h)) { int line = v_counter - (bitmap.viewport.h - 144) / 2; if ((line < 0) || (line >= 144)) { memset(&linebuf[0][0x20], 0x40, 256); } else { if (bitmap.viewport.x > 0) { memset(&linebuf[0][0x20], 0x40, 48); memset(&linebuf[0][0x20+48+160], 0x40, 48); } } } } void render_obj_m5(int line) { int i, column; int xpos, width; int pixelcount = 0; int masked = 0; uint8 *src, *s, *lb; uint32 temp, v_line; uint32 attr, name, atex; /* Sprite list for current line */ object_info_t *object_info = obj_info[line]; int count = object_count[line]; /* Draw sprites in front-to-back order */ while (count--) { /* Sprite X position */ xpos = object_info->xpos; /* Sprite masking */ if (xpos) { /* Requires at least one sprite with xpos > 0 */ spr_ovr = 1; } else if (spr_ovr) { /* Remaining sprites are not drawn */ masked = 1; } /* Display area offset */ xpos = xpos - 0x80; /* Sprite size */ temp = object_info->size; /* Sprite width */ width = 8 + ((temp & 0x0C) << 1); /* Update pixel count (off-screen sprites are included) */ pixelcount += width; /* Is sprite across visible area ? */ if (((xpos + width) > 0) && (xpos < bitmap.viewport.w) && !masked) { /* Sprite attributes */ attr = object_info->attr; /* Sprite vertical offset */ v_line = object_info->ypos; /* Sprite priority + palette bits */ atex = (attr >> 9) & 0x70; /* Pattern name base */ name = attr & 0x07FF; /* Mask vflip/hflip */ attr &= 0x1800; /* Pointer into pattern name offset look-up table */ s = &name_lut[((attr >> 3) & 0x300) | (temp << 4) | ((v_line & 0x18) >> 1)]; /* Pointer into line buffer */ lb = &linebuf[0][0x20 + xpos]; /* Max. number of sprite pixels rendered per line */ if (pixelcount > max_sprite_pixels) { /* Adjust number of pixels to draw */ width -= (pixelcount - max_sprite_pixels); } /* Number of tiles to draw */ width = width >> 3; /* Pattern row index */ v_line = (v_line & 7) << 3; /* Draw sprite patterns */ for (column = 0; column < width; column++, lb+=8) { temp = attr | ((name + s[column]) & 0x07FF); src = &bg_pattern_cache[(temp << 6) | (v_line)]; DRAW_SPRITE_TILE(8,atex,lut[1]) } } /* Sprite limit */ if (pixelcount >= max_sprite_pixels) { /* Sprite masking is effective on next line if max pixel width is reached */ spr_ovr = (pixelcount >= bitmap.viewport.w); /* Stop sprite rendering */ return; } /* Next sprite entry */ object_info++; } /* Clear sprite masking for next line */ spr_ovr = 0; } void render_obj_m5_ste(int line) { int i, column; int xpos, width; int pixelcount = 0; int masked = 0; uint8 *src, *s, *lb; uint32 temp, v_line; uint32 attr, name, atex; /* Sprite list for current line */ object_info_t *object_info = obj_info[line]; int count = object_count[line]; /* Clear sprite line buffer */ memset(&linebuf[1][0], 0, bitmap.viewport.w + 0x40); /* Draw sprites in front-to-back order */ while (count--) { /* Sprite X position */ xpos = object_info->xpos; /* Sprite masking */ if (xpos) { /* Requires at least one sprite with xpos > 0 */ spr_ovr = 1; } else if (spr_ovr) { /* Remaining sprites are not drawn */ masked = 1; } /* Display area offset */ xpos = xpos - 0x80; /* Sprite size */ temp = object_info->size; /* Sprite width */ width = 8 + ((temp & 0x0C) << 1); /* Update pixel count (off-screen sprites are included) */ pixelcount += width; /* Is sprite across visible area ? */ if (((xpos + width) > 0) && (xpos < bitmap.viewport.w) && !masked) { /* Sprite attributes */ attr = object_info->attr; /* Sprite vertical offset */ v_line = object_info->ypos; /* Sprite priority + palette bits */ atex = (attr >> 9) & 0x70; /* Pattern name base */ name = attr & 0x07FF; /* Mask vflip/hflip */ attr &= 0x1800; /* Pointer into pattern name offset look-up table */ s = &name_lut[((attr >> 3) & 0x300) | (temp << 4) | ((v_line & 0x18) >> 1)]; /* Pointer into line buffer */ lb = &linebuf[1][0x20 + xpos]; /* Adjust number of pixels to draw for sprite limit */ if (pixelcount > max_sprite_pixels) { width -= (pixelcount - max_sprite_pixels); } /* Number of tiles to draw */ width = width >> 3; /* Pattern row index */ v_line = (v_line & 7) << 3; /* Draw sprite patterns */ for (column = 0; column < width; column++, lb+=8) { temp = attr | ((name + s[column]) & 0x07FF); src = &bg_pattern_cache[(temp << 6) | (v_line)]; DRAW_SPRITE_TILE(8,atex,lut[3]) } } /* Sprite limit */ if (pixelcount >= max_sprite_pixels) { /* Sprite masking is effective on next line if max pixel width is reached */ spr_ovr = (pixelcount >= bitmap.viewport.w); /* Merge background & sprite layers */ merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[4], bitmap.viewport.w); /* Stop sprite rendering */ return; } /* Next sprite entry */ object_info++; } /* Clear sprite masking for next line */ spr_ovr = 0; /* Merge background & sprite layers */ merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[4], bitmap.viewport.w); } void render_obj_m5_im2(int line) { int i, column; int xpos, width; int pixelcount = 0; int masked = 0; int odd = odd_frame; uint8 *src, *s, *lb; uint32 temp, v_line; uint32 attr, name, atex; /* Sprite list for current line */ object_info_t *object_info = obj_info[line]; int count = object_count[line]; /* Draw sprites in front-to-back order */ while (count--) { /* Sprite X position */ xpos = object_info->xpos; /* Sprite masking */ if (xpos) { /* Requires at least one sprite with xpos > 0 */ spr_ovr = 1; } else if (spr_ovr) { /* Remaining sprites are not drawn */ masked = 1; } /* Display area offset */ xpos = xpos - 0x80; /* Sprite size */ temp = object_info->size; /* Sprite width */ width = 8 + ((temp & 0x0C) << 1); /* Update pixel count (off-screen sprites are included) */ pixelcount += width; /* Is sprite across visible area ? */ if (((xpos + width) > 0) && (xpos < bitmap.viewport.w) && !masked) { /* Sprite attributes */ attr = object_info->attr; /* Sprite y offset */ v_line = object_info->ypos; /* Sprite priority + palette bits */ atex = (attr >> 9) & 0x70; /* Pattern name base */ name = attr & 0x03FF; /* Mask vflip/hflip */ attr &= 0x1800; /* Pattern name offset lookup table */ s = &name_lut[((attr >> 3) & 0x300) | (temp << 4) | ((v_line & 0x18) >> 1)]; /* Pointer into line buffer */ lb = &linebuf[0][0x20 + xpos]; /* Adjust width for sprite limit */ if (pixelcount > max_sprite_pixels) { width -= (pixelcount - max_sprite_pixels); } /* Number of tiles to draw */ width = width >> 3; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; /* Render sprite patterns */ for(column = 0; column < width; column ++, lb+=8) { temp = attr | (((name + s[column]) & 0x3ff) << 1); src = &bg_pattern_cache[((temp << 6) | (v_line)) ^ ((attr & 0x1000) >> 6)]; DRAW_SPRITE_TILE(8,atex,lut[1]) } } /* Sprite Limit */ if (pixelcount >= max_sprite_pixels) { /* Sprite masking is effective on next line if max pixel width is reached */ spr_ovr = (pixelcount >= bitmap.viewport.w); /* Stop sprite rendering */ return; } /* Next sprite entry */ object_info++; } /* Clear sprite masking for next line */ spr_ovr = 0; } void render_obj_m5_im2_ste(int line) { int i, column; int xpos, width; int pixelcount = 0; int masked = 0; int odd = odd_frame; uint8 *src, *s, *lb; uint32 temp, v_line; uint32 attr, name, atex; /* Sprite list for current line */ object_info_t *object_info = obj_info[line]; int count = object_count[line]; /* Clear sprite line buffer */ memset(&linebuf[1][0], 0, bitmap.viewport.w + 0x40); /* Draw sprites in front-to-back order */ while (count--) { /* Sprite X position */ xpos = object_info->xpos; /* Sprite masking */ if (xpos) { /* Requires at least one sprite with xpos > 0 */ spr_ovr = 1; } else if (spr_ovr) { /* Remaining sprites are not drawn */ masked = 1; } /* Display area offset */ xpos = xpos - 0x80; /* Sprite size */ temp = object_info->size; /* Sprite width */ width = 8 + ((temp & 0x0C) << 1); /* Update pixel count (off-screen sprites are included) */ pixelcount += width; /* Is sprite across visible area ? */ if (((xpos + width) > 0) && (xpos < bitmap.viewport.w) && !masked) { /* Sprite attributes */ attr = object_info->attr; /* Sprite y offset */ v_line = object_info->ypos; /* Sprite priority + palette bits */ atex = (attr >> 9) & 0x70; /* Pattern name base */ name = attr & 0x03FF; /* Mask vflip/hflip */ attr &= 0x1800; /* Pattern name offset lookup table */ s = &name_lut[((attr >> 3) & 0x300) | (temp << 4) | ((v_line & 0x18) >> 1)]; /* Pointer into line buffer */ lb = &linebuf[1][0x20 + xpos]; /* Adjust width for sprite limit */ if (pixelcount > max_sprite_pixels) { width -= (pixelcount - max_sprite_pixels); } /* Number of tiles to draw */ width = width >> 3; /* Pattern row index */ v_line = (((v_line & 7) << 1) | odd) << 3; /* Render sprite patterns */ for(column = 0; column < width; column ++, lb+=8) { temp = attr | (((name + s[column]) & 0x3ff) << 1); src = &bg_pattern_cache[((temp << 6) | (v_line)) ^ ((attr & 0x1000) >> 6)]; DRAW_SPRITE_TILE(8,atex,lut[3]) } } /* Sprite Limit */ if (pixelcount >= max_sprite_pixels) { /* Sprite masking is effective on next line if max pixel width is reached */ spr_ovr = (pixelcount >= bitmap.viewport.w); /* Merge background & sprite layers */ merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[4], bitmap.viewport.w); /* Stop sprite rendering */ return; } /* Next sprite entry */ object_info++; } /* Clear sprite masking for next line */ spr_ovr = 0; /* Merge background & sprite layers */ merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[4], bitmap.viewport.w); } /*--------------------------------------------------------------------------*/ /* Sprites Parsing functions */ /*--------------------------------------------------------------------------*/ void parse_satb_tms(int line) { int i = 0; /* Sprite counter (4 max. per line) */ int count = 0; /* no sprites in Text modes */ if (!(reg[1] & 0x10)) { /* Y position */ int ypos; /* Sprite list for next line */ object_info_t *object_info = obj_info[(line + 1) & 1]; /* Pointer to sprite attribute table */ uint8 *st = &vram[(reg[5] << 7) & 0x3F80]; /* Sprite height (8 pixels by default) */ int height = 8; /* Adjust height for 16x16 sprites */ height <<= ((reg[1] & 0x02) >> 1); /* Adjust height for zoomed sprites */ height <<= (reg[1] & 0x01); /* Parse Sprite Table (32 entries) */ do { /* Sprite Y position */ ypos = st[i << 2]; /* Check end of sprite list marker */ if (ypos == 0xD0) { break; } /* Wrap Y coordinate for sprites > 256-32 */ if (ypos >= 224) { ypos -= 256; } /* Y range */ ypos = line - ypos; /* Sprite is visible on this line ? */ if ((ypos >= 0) && (ypos < height)) { /* Sprite overflow */ if (count == 4) { /* Flag is set only during active area */ if (line < bitmap.viewport.h) { spr_ovr = 0x40; } break; } /* Adjust Y range back for zoomed sprites */ ypos >>= (reg[1] & 0x01); /* Store sprite attributes for later processing */ object_info->ypos = ypos; object_info->xpos = st[(i << 2) + 1]; object_info->attr = st[(i << 2) + 2]; object_info->size = st[(i << 2) + 3]; /* Increment Sprite count */ ++count; /* Next sprite entry */ object_info++; } } while (++i < 32); } /* Update sprite count for next line */ object_count[(line + 1) & 1] = count; /* Insert number of last sprite entry processed */ status = (status & 0xE0) | (i & 0x1F); } void parse_satb_m4(int line) { int i = 0; uint8 *st; /* Sprite counter (8 max. per line) */ int count = 0; /* Y position */ int ypos; /* Sprite list for next line */ object_info_t *object_info = obj_info[(line + 1) & 1]; /* Sprite height (8x8 or 8x16) */ int height = 8 + ((reg[1] & 0x02) << 2); /* Sprite attribute table address mask */ uint16 st_mask = ~0x3F80 ^ (reg[5] << 7); /* Unused bits used as a mask on 315-5124 VDP only */ if (system_hw > SYSTEM_SMS) { st_mask |= 0x80; } /* Pointer to sprite attribute table */ st = &vram[st_mask & 0x3F00]; /* Parse Sprite Table (64 entries) */ do { /* Sprite Y position */ ypos = st[i]; /* Check end of sprite list marker (no effect in extended modes) */ if ((ypos == 208) && (bitmap.viewport.h == 192)) { break; } /* Wrap Y coordinate (NB: this is likely not 100% accurate and needs to be verified on real hardware) */ if (ypos > (bitmap.viewport.h + 16)) { ypos -= 256; } /* Y range */ ypos = line - ypos; /* Adjust Y range for zoomed sprites (not working on Mega Drive VDP) */ if (system_hw < SYSTEM_MD) { ypos >>= (reg[1] & 0x01); } /* Check if sprite is visible on this line */ if ((ypos >= 0) && (ypos < height)) { /* Sprite overflow */ if (count == 8) { /* Flag is set only during active area */ if ((line >= 0) && (line < bitmap.viewport.h)) { spr_ovr = 0x40; } break; } /* Store sprite attributes for later processing */ object_info->ypos = ypos; object_info->xpos = st[(0x80 + (i << 1)) & st_mask]; object_info->attr = st[(0x81 + (i << 1)) & st_mask]; /* Increment Sprite count */ ++count; /* Next sprite entry */ object_info++; } } while (++i < 64); /* Update sprite count for next line */ object_count[(line + 1) & 1] = count; } void parse_satb_m5(int line) { /* Y position */ int ypos; /* Sprite height (8,16,24,32 pixels)*/ int height; /* Sprite size data */ int size; /* Sprite link data */ int link = 0; /* Sprite counter */ int count = 0; /* max. number of rendered sprites (16 or 20 sprites per line by default) */ int max = bitmap.viewport.w >> 4; /* max. number of parsed sprites (64 or 80 sprites per line by default) */ int total = max_sprite_pixels >> 2; /* Pointer to sprite attribute table */ uint16 *p = (uint16 *) &vram[satb]; /* Pointer to internal RAM */ uint16 *q = (uint16 *) &sat[0]; /* Sprite list for next line */ object_info_t *object_info = obj_info[(line + 1) & 1]; /* Adjust line offset */ line += 0x81; do { /* Read Y position from internal SAT cache */ ypos = (q[link] >> im2_flag) & 0x1FF; /* Check if sprite Y position has been reached */ if (line >= ypos) { /* Read sprite size from internal SAT cache */ size = q[link + 1] >> 8; /* Sprite height */ height = 8 + ((size & 3) << 3); /* Y range */ ypos = line - ypos; /* Check if sprite is visible on current line */ if (ypos < height) { /* Sprite overflow */ if (count == max) { status |= 0x40; break; } /* Update sprite list (only name, attribute & xpos are parsed from VRAM) */ object_info->attr = p[link + 2]; object_info->xpos = p[link + 3] & 0x1ff; object_info->ypos = ypos; object_info->size = size & 0x0f; /* Increment Sprite count */ ++count; /* Next sprite entry */ object_info++; } } /* Read link data from internal SAT cache */ link = (q[link + 1] & 0x7F) << 2; /* Stop parsing if link data points to first entry (#0) or after the last entry (#64 in H32 mode, #80 in H40 mode) */ if ((link == 0) || (link >= bitmap.viewport.w)) break; } while (--total); /* Update sprite count for next line (line value already incremented) */ object_count[line & 1] = count; } /*--------------------------------------------------------------------------*/ /* Pattern cache update function */ /*--------------------------------------------------------------------------*/ void update_bg_pattern_cache_m4(int index) { int i; uint8 x, y, c; uint8 *dst; uint16 name, bp01, bp23; uint32 bp; for(i = 0; i < index; i++) { /* Get modified pattern name index */ name = bg_name_list[i]; /* Pattern cache base address */ dst = &bg_pattern_cache[name << 6]; /* Check modified lines */ for(y = 0; y < 8; y++) { if(bg_name_dirty[name] & (1 << y)) { /* Byteplane data */ bp01 = *(uint16 *)&vram[(name << 5) | (y << 2) | (0)]; bp23 = *(uint16 *)&vram[(name << 5) | (y << 2) | (2)]; /* Convert to pixel line data (4 bytes = 8 pixels)*/ /* (msb) p7p6 p5p4 p3p2 p1p0 (lsb) */ bp = (bp_lut[bp01] >> 2) | (bp_lut[bp23]); /* Update cached line (8 pixels = 8 bytes) */ for(x = 0; x < 8; x++) { /* Extract pixel data */ c = bp & 0x0F; /* Pattern cache data (one pattern = 8 bytes) */ /* byte0 <-> p0 p1 p2 p3 p4 p5 p6 p7 <-> byte7 (hflip = 0) */ /* byte0 <-> p7 p6 p5 p4 p3 p2 p1 p0 <-> byte7 (hflip = 1) */ dst[0x00000 | (y << 3) | (x)] = (c); /* vflip=0 & hflip=0 */ dst[0x08000 | (y << 3) | (x ^ 7)] = (c); /* vflip=0 & hflip=1 */ dst[0x10000 | ((y ^ 7) << 3) | (x)] = (c); /* vflip=1 & hflip=0 */ dst[0x18000 | ((y ^ 7) << 3) | (x ^ 7)] = (c); /* vflip=1 & hflip=1 */ /* Next pixel */ bp = bp >> 4; } } } /* Clear modified pattern flag */ bg_name_dirty[name] = 0; } } void update_bg_pattern_cache_m5(int index) { int i; uint8 x, y, c; uint8 *dst; uint16 name; uint32 bp; for(i = 0; i < index; i++) { /* Get modified pattern name index */ name = bg_name_list[i]; /* Pattern cache base address */ dst = &bg_pattern_cache[name << 6]; /* Check modified lines */ for(y = 0; y < 8; y ++) { if(bg_name_dirty[name] & (1 << y)) { /* Byteplane data (one pattern = 4 bytes) */ /* LIT_ENDIAN: byte0 (lsb) p2p3 p0p1 p6p7 p4p5 (msb) byte3 */ /* BIG_ENDIAN: byte0 (msb) p0p1 p2p3 p4p5 p6p7 (lsb) byte3 */ bp = *(uint32 *)&vram[(name << 5) | (y << 2)]; /* Update cached line (8 pixels = 8 bytes) */ for(x = 0; x < 8; x ++) { /* Extract pixel data */ c = bp & 0x0F; /* Pattern cache data (one pattern = 8 bytes) */ /* byte0 <-> p0 p1 p2 p3 p4 p5 p6 p7 <-> byte7 (hflip = 0) */ /* byte0 <-> p7 p6 p5 p4 p3 p2 p1 p0 <-> byte7 (hflip = 1) */ #ifdef LSB_FIRST /* Byteplane data = (msb) p4p5 p6p7 p0p1 p2p3 (lsb) */ dst[0x00000 | (y << 3) | (x ^ 3)] = (c); /* vflip=0, hflip=0 */ dst[0x20000 | (y << 3) | (x ^ 4)] = (c); /* vflip=0, hflip=1 */ dst[0x40000 | ((y ^ 7) << 3) | (x ^ 3)] = (c); /* vflip=1, hflip=0 */ dst[0x60000 | ((y ^ 7) << 3) | (x ^ 4)] = (c); /* vflip=1, hflip=1 */ #else /* Byteplane data = (msb) p0p1 p2p3 p4p5 p6p7 (lsb) */ dst[0x00000 | (y << 3) | (x ^ 7)] = (c); /* vflip=0, hflip=0 */ dst[0x20000 | (y << 3) | (x)] = (c); /* vflip=0, hflip=1 */ dst[0x40000 | ((y ^ 7) << 3) | (x ^ 7)] = (c); /* vflip=1, hflip=0 */ dst[0x60000 | ((y ^ 7) << 3) | (x)] = (c); /* vflip=1, hflip=1 */ #endif /* Next pixel */ bp = bp >> 4; } } } /* Clear modified pattern flag */ bg_name_dirty[name] = 0; } } /*--------------------------------------------------------------------------*/ /* Window & Plane A clipping update function (Mode 5) */ /*--------------------------------------------------------------------------*/ void window_clip(unsigned int data, unsigned int sw) { /* Window size and invert flags */ int hp = (data & 0x1f); int hf = (data >> 7) & 1; /* Perform horizontal clipping; the results are applied in reverse if the horizontal inversion flag is set */ int a = hf; int w = hf ^ 1; /* Display width (16 or 20 columns) */ sw = 16 + (sw << 2); if(hp) { if(hp > sw) { /* Plane W takes up entire line */ clip[w].left = 0; clip[w].right = sw; clip[w].enable = 1; clip[a].enable = 0; } else { /* Plane W takes left side, Plane A takes right side */ clip[w].left = 0; clip[a].right = sw; clip[a].left = clip[w].right = hp; clip[0].enable = clip[1].enable = 1; } } else { /* Plane A takes up entire line */ clip[a].left = 0; clip[a].right = sw; clip[a].enable = 1; clip[w].enable = 0; } } /*--------------------------------------------------------------------------*/ /* Init, reset routines */ /*--------------------------------------------------------------------------*/ void render_init(void) { int bx, ax; /* Initialize layers priority pixel look-up tables */ uint16 index; for (bx = 0; bx < 0x100; bx++) { for (ax = 0; ax < 0x100; ax++) { index = (bx << 8) | (ax); lut[0][index] = make_lut_bg(bx, ax); lut[1][index] = make_lut_bgobj(bx, ax); lut[2][index] = make_lut_bg_ste(bx, ax); lut[3][index] = make_lut_obj(bx, ax); lut[4][index] = make_lut_bgobj_ste(bx, ax); lut[5][index] = make_lut_bgobj_m4(bx,ax); } } /* Initialize pixel color look-up tables */ palette_init(); /* Make sprite pattern name index look-up table (Mode 5) */ make_name_lut(); /* Make bitplane to pixel look-up table (Mode 4) */ make_bp_lut(); } void render_reset(void) { /* Clear display bitmap */ memset(bitmap.data, 0, bitmap.pitch * bitmap.height); /* Clear line buffers */ memset(linebuf, 0, sizeof(linebuf)); /* Clear color palettes */ memset(pixel, 0, sizeof(pixel)); /* Clear pattern cache */ memset ((char *) bg_pattern_cache, 0, sizeof (bg_pattern_cache)); /* Reset Sprite infos */ spr_ovr = spr_col = object_count[0] = object_count[1] = 0; } /*--------------------------------------------------------------------------*/ /* Line rendering functions */ /*--------------------------------------------------------------------------*/ void render_line(int line) { /* Check display status */ if (reg[1] & 0x40) { /* Update pattern cache */ if (bg_list_index) { update_bg_pattern_cache(bg_list_index); bg_list_index = 0; } /* Render BG layer(s) */ render_bg(line); /* Render sprite layer */ render_obj(line & 1); /* Left-most column blanking */ if (reg[0] & 0x20) { if (system_hw > SYSTEM_SGII) { memset(&linebuf[0][0x20], 0x40, 8); } } /* Parse sprites for next line */ if (line < (bitmap.viewport.h - 1)) { parse_satb(line); } /* Horizontal borders */ if (bitmap.viewport.x > 0) { memset(&linebuf[0][0x20 - bitmap.viewport.x], 0x40, bitmap.viewport.x); memset(&linebuf[0][0x20 + bitmap.viewport.w], 0x40, bitmap.viewport.x); } } else { /* Master System & Game Gear VDP specific */ if (system_hw < SYSTEM_MD) { /* Update SOVR flag */ status |= spr_ovr; spr_ovr = 0; /* Sprites are still parsed when display is disabled */ parse_satb(line); } /* Blanked line */ memset(&linebuf[0][0x20 - bitmap.viewport.x], 0x40, bitmap.viewport.w + 2*bitmap.viewport.x); } /* Pixel color remapping */ remap_line(line); } void blank_line(int line, int offset, int width) { memset(&linebuf[0][0x20 + offset], 0x40, width); remap_line(line); } void remap_line(int line) { /* Line width */ int width = bitmap.viewport.w + 2*bitmap.viewport.x; /* Pixel line buffer */ uint8 *src = &linebuf[0][0x20 - bitmap.viewport.x]; /* Adjust line offset in framebuffer */ line = (line + bitmap.viewport.y) % lines_per_frame; /* Take care of Game Gear reduced screen when overscan is disabled */ if (line < 0) return; /* Adjust for interlaced output */ if (interlaced && config.render) { line = (line * 2) + odd_frame; } #if defined(USE_15BPP_RENDERING) || defined(USE_16BPP_RENDERING) /* NTSC Filter (only supported for 15 or 16-bit pixels rendering) */ if (config.ntsc) { if (reg[12] & 0x01) { md_ntsc_blit(md_ntsc, ( MD_NTSC_IN_T const * )pixel, src, width, line); } else { sms_ntsc_blit(sms_ntsc, ( SMS_NTSC_IN_T const * )pixel, src, width, line); } } else #endif { #ifdef CUSTOM_BLITTER CUSTOM_BLITTER(line, width, pixel, src) #else /* Convert VDP pixel data to output pixel format */ PIXEL_OUT_T *dst = ((PIXEL_OUT_T *)&bitmap.data[(line * bitmap.pitch)]); if (config.lcd) { do { RENDER_PIXEL_LCD(src,dst,pixel,config.lcd); } while (--width); } else { do { *dst++ = pixel[*src++]; } while (--width); } #endif } }