src/mame/video/snes.c
| r7644 | r7645 | |
| 132 | 132 | SNES_COLOR_DEPTH_8BPP |
| 133 | 133 | }; |
| 134 | 134 | |
| 135 | | /***************************************** |
| 136 | | * snes_draw_blend() |
| 137 | | * |
| 138 | | * Routine for additive/subtractive blending |
| 139 | | * between the main and sub screens. |
| 140 | | *****************************************/ |
| 135 | /********************************************************************************** |
| 136 | * TODO: the draw_tile routines below should be merged/reworked to reduce everything |
| 137 | * to a single routine with the additional parameters: OAM/BG (to decide if checking |
| 138 | * against priority or not) and tile length (16 for hires BG, 8 otherwise) |
| 139 | **********************************************************************************/ |
| 141 | 140 | |
| 142 | | INLINE void snes_draw_blend( UINT16 offset, UINT16 *colour, UINT8 prevent_color_math, UINT8 black_pen_clip, int switch_screens ) |
| 143 | | { |
| 144 | | #ifdef SNES_LAYER_DEBUG |
| 145 | | if (debug_options.colormath_disabled) |
| 146 | | return; |
| 147 | | #endif /* SNES_LAYER_DEBUG */ |
| 148 | | |
| 149 | | /* when color math is applied to subscreen pixels, the blending depends on the blending used by the previous mainscreen |
| 150 | | pixel, except for subscreen pixel 0 which has no previous mainscreen pixel, see comments in snes_refresh_scanline */ |
| 151 | | if (switch_screens && offset > 0) |
| 152 | | offset -= 1; |
| 153 | | |
| 154 | | if ((black_pen_clip == SNES_CLIP_ALWAYS) || |
| 155 | | (black_pen_clip == SNES_CLIP_IN && snes_ppu.clipmasks[SNES_COLOR][offset]) || |
| 156 | | (black_pen_clip == SNES_CLIP_OUT && !snes_ppu.clipmasks[SNES_COLOR][offset])) |
| 157 | | *colour = 0; //clip to black before color math |
| 158 | | |
| 159 | | if (prevent_color_math == SNES_CLIP_ALWAYS) // blending mode 3 == always OFF |
| 160 | | return; |
| 161 | | |
| 162 | | if ((prevent_color_math == SNES_CLIP_NEVER) || |
| 163 | | (prevent_color_math == SNES_CLIP_IN && !snes_ppu.clipmasks[SNES_COLOR][offset]) || |
| 164 | | (prevent_color_math == SNES_CLIP_OUT && snes_ppu.clipmasks[SNES_COLOR][offset])) |
| 165 | | { |
| 166 | | UINT16 r, g, b; |
| 167 | | struct SCANLINE *subscreen; |
| 168 | | int clip_max = 0; // if add then clip to 0x1f, if sub then clip to 0 |
| 169 | | |
| 170 | | #ifdef SNES_LAYER_DEBUG |
| 171 | | /* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */ |
| 172 | | if (debug_options.draw_subscreen) |
| 173 | | { |
| 174 | | subscreen = switch_screens ? &scanlines[SNES_SUBSCREEN] : &scanlines[SNES_MAINSCREEN]; |
| 175 | | } |
| 176 | | else |
| 177 | | #endif /* SNES_LAYER_DEBUG */ |
| 178 | | { |
| 179 | | subscreen = switch_screens ? &scanlines[SNES_MAINSCREEN] : &scanlines[SNES_SUBSCREEN]; |
| 180 | | } |
| 181 | | |
| 182 | | if (snes_ppu.sub_add_mode) /* SNES_SUBSCREEN*/ |
| 183 | | { |
| 184 | | if (!BIT(snes_ppu.color_modes, 7)) |
| 185 | | { |
| 186 | | /* 0x00 add */ |
| 187 | | r = (*colour & 0x1f) + (subscreen->buffer[offset] & 0x1f); |
| 188 | | g = ((*colour & 0x3e0) >> 5) + ((subscreen->buffer[offset] & 0x3e0) >> 5); |
| 189 | | b = ((*colour & 0x7c00) >> 10) + ((subscreen->buffer[offset] & 0x7c00) >> 10); |
| 190 | | clip_max = 1; |
| 191 | | } |
| 192 | | else |
| 193 | | { |
| 194 | | /* 0x80 sub */ |
| 195 | | r = (*colour & 0x1f) - (subscreen->buffer[offset] & 0x1f); |
| 196 | | g = ((*colour & 0x3e0) >> 5) - ((subscreen->buffer[offset] & 0x3e0) >> 5); |
| 197 | | b = ((*colour & 0x7c00) >> 10) - ((subscreen->buffer[offset] & 0x7c00) >> 10); |
| 198 | | if (r > 0x1f) r = 0; |
| 199 | | if (g > 0x1f) g = 0; |
| 200 | | if (b > 0x1f) b = 0; |
| 201 | | } |
| 202 | | /* only halve if the color is not the back colour */ |
| 203 | | if (BIT(snes_ppu.color_modes, 6) && (subscreen->buffer[offset] != snes_cgram[FIXED_COLOUR])) |
| 204 | | { |
| 205 | | r >>= 1; |
| 206 | | g >>= 1; |
| 207 | | b >>= 1; |
| 208 | | } |
| 209 | | } |
| 210 | | else /* Fixed colour */ |
| 211 | | { |
| 212 | | if (!BIT(snes_ppu.color_modes, 7)) |
| 213 | | { |
| 214 | | /* 0x00 add */ |
| 215 | | r = (*colour & 0x1f) + (snes_cgram[FIXED_COLOUR] & 0x1f); |
| 216 | | g = ((*colour & 0x3e0) >> 5) + ((snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5); |
| 217 | | b = ((*colour & 0x7c00) >> 10) + ((snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10); |
| 218 | | clip_max = 1; |
| 219 | | } |
| 220 | | else |
| 221 | | { |
| 222 | | /* 0x80: sub */ |
| 223 | | r = (*colour & 0x1f) - (snes_cgram[FIXED_COLOUR] & 0x1f); |
| 224 | | g = ((*colour & 0x3e0) >> 5) - ((snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5); |
| 225 | | b = ((*colour & 0x7c00) >> 10) - ((snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10); |
| 226 | | if (r > 0x1f) r = 0; |
| 227 | | if (g > 0x1f) g = 0; |
| 228 | | if (b > 0x1f) b = 0; |
| 229 | | } |
| 230 | | /* halve if necessary */ |
| 231 | | if (BIT(snes_ppu.color_modes, 6)) |
| 232 | | { |
| 233 | | r >>= 1; |
| 234 | | g >>= 1; |
| 235 | | b >>= 1; |
| 236 | | } |
| 237 | | } |
| 238 | | |
| 239 | | /* according to anomie's docs, after addition has been performed, division by 2 happens *before* clipping to max, hence we clip now */ |
| 240 | | if (clip_max) |
| 241 | | { |
| 242 | | if (r > 0x1f) r = 0x1f; |
| 243 | | if (g > 0x1f) g = 0x1f; |
| 244 | | if (b > 0x1f) b = 0x1f; |
| 245 | | } |
| 246 | | |
| 247 | | *colour = ((r & 0x1f) | ((g & 0x1f) << 5) | ((b & 0x1f) << 10)); |
| 248 | | } |
| 249 | | } |
| 250 | | |
| 251 | 141 | /***************************************** |
| 252 | 142 | * snes_draw_tile() |
| 253 | 143 | * |
| 254 | | * Draw tiles with variable bit planes |
| 144 | * Draw BG tiles with variable bit planes |
| 255 | 145 | *****************************************/ |
| 256 | 146 | |
| 257 | 147 | INLINE void snes_draw_tile( UINT8 planes, UINT8 layer, UINT16 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT8 direct_colors, UINT16 pal, UINT8 hires ) |
| r7644 | r7645 | |
| 546 | 436 | /***************************************** |
| 547 | 437 | * snes_draw_tile_object() |
| 548 | 438 | * |
| 549 | | * Draw tiles with 4 bit planes(16 colors) |
| 550 | | * The same as snes_draw_tile_4() except |
| 551 | | * that it takes a blend parameter. |
| 439 | * Draw OAM tiles with 4 bit planes(16 colors) |
| 552 | 440 | *****************************************/ |
| 553 | 441 | |
| 554 | 442 | INLINE void snes_draw_tile_object( UINT16 tileaddr, INT16 x, UINT8 priority, UINT8 flip, UINT16 pal ) |
| r7644 | r7645 | |
| 648 | 536 | } |
| 649 | 537 | } |
| 650 | 538 | |
| 539 | /************************************************************************************************* |
| 540 | * SNES BG layers |
| 541 | * |
| 542 | * BG drawing theory of each scanline is quite easy: depending on the graphics Mode (0-7), there |
| 543 | * are up to 4 background layers. Pixels for each BG layer can have two different priorities. |
| 544 | * Depending on the line and on the BGHOFS and BGVOFS PPU registers, we first determine the tile |
| 545 | * address in snes_vram (by determining x,y coord and tile size and by calling snes_get_tmap_addr). |
| 546 | * Then, we load the correspondent data and we determine the tile properties: which priority to |
| 547 | * use, which palette etc. Finally, for each pixel of the tile appearing on screen, we check if |
| 548 | * the tile priority is higher than the BG/OAM already stored in that pixel for that line. If so |
| 549 | * we store the pixel in the buffer, otherwise we discard it. |
| 550 | * |
| 551 | * Of course, depending on the graphics Mode, it might be easier or harder to determine the proper |
| 552 | * tile address in vram (Mode 7 uses different registers, Mode 2, 4 and 6 uses OPT effect, etc.), |
| 553 | * but in general it works as described. |
| 554 | *************************************************************************************************/ |
| 555 | |
| 651 | 556 | /********************************************* |
| 652 | 557 | * snes_get_tmap_addr() |
| 653 | 558 | * |
| r7644 | r7645 | |
| 1071 | 976 | } |
| 1072 | 977 | } |
| 1073 | 978 | |
| 1074 | | /********************************************* |
| 1075 | | * snes_update_objects() |
| 979 | /************************************************************************************************* |
| 980 | * SNES Sprites |
| 1076 | 981 | * |
| 1077 | | * Update an entire line of sprites. |
| 1078 | | * FIXME: We need to support high priority bit |
| 1079 | | *********************************************/ |
| 982 | * 1. First of all: sprites are drawn one line in advance. We emulate this by caching the |
| 983 | * starting vram address, the sprite size and the "name select" at each line, and by using |
| 984 | * them the next line to output the proper sprites - see snes_update_obsel. |
| 985 | * |
| 986 | * 2. Each line can select its sprites among 128 available ones in oam_ram, hence we start |
| 987 | * by creating a list of the available objects (each one with its x,y coordinate, its size, |
| 988 | * its tile address, etc.) - see snes_oam_list_build. |
| 989 | * |
| 990 | * 3. Next, we start finding out which sprites will appear in the line: starting from |
| 991 | * FirstSprite, we count 32 OBJs which intersect our line and we store their indexes in the |
| 992 | * oam_itemlist array (if more than 32 sprites intersect our line, we set the Range Over |
| 993 | * flag); then, selecting among these sprites, we count 34 8x8 tiles which are visible |
| 994 | * in our line (i.e. whose x coord is between -size and 256) and we store the corresponding |
| 995 | * coordinates/priorities/palettes/etc. in the oam_tilelist array (if more than 34 tiles would |
| 996 | * appear on screen, we set the Time Over flag). |
| 997 | * Notice that when we populate oam_tilelist, we proceed from oam_itemlist[31] (or from the last |
| 998 | * item which intersects the scanline), towards oam_itemlist[0], i.e. the higher tiles (say |
| 999 | * oam_tilelist[34], or the last tile which appear on screen) will contain FirstSprite object, |
| 1000 | * or the sprites with closer index to FirstSprite which get displayed. This will play an |
| 1001 | * important role for sprite priority - see snes_update_objects_rto. |
| 1002 | * |
| 1003 | * 4. All the above happens at the beginning of each VIDEO_UPDATE. When we finally draw the |
| 1004 | * scanline, we pass through the oam_tilelist and we store the displayed pixels in our scanline |
| 1005 | * buffer. Notice that, for each pixel of a SNES sprite, only the priority of the topmost sprite |
| 1006 | * is tested against the priority of the BG pixel (because FirstSprite is on top of FirstSprite+1, |
| 1007 | * which is on top of FirstSprite+2, etc., and therefore other sprites are already covered by the |
| 1008 | * topmost one). To emulate this, we draw each tile over the previous ones no matter what |
| 1009 | * priorities are (differently from what we did with BGs): in the end, we will have in each pixel z |
| 1010 | * its topmost sprite and scanline.priority[z] will be the topmost sprite priority as expected. |
| 1011 | * Of course, sprite drawing must happen before BG drawing, so that afterwords BG pixels properly |
| 1012 | * test their priority with the one of the correct sprite - see snes_update_objects. |
| 1013 | *************************************************************************************************/ |
| 1080 | 1014 | |
| 1081 | 1015 | struct OAM |
| 1082 | 1016 | { |
| r7644 | r7645 | |
| 1088 | 1022 | |
| 1089 | 1023 | static struct OAM oam_spritelist[SNES_SCR_WIDTH / 2]; |
| 1090 | 1024 | |
| 1025 | static UINT8 oam_itemlist[32]; |
| 1026 | |
| 1027 | struct TILELIST { |
| 1028 | INT16 x; |
| 1029 | UINT16 priority, pal, tileaddr; |
| 1030 | int hflip; |
| 1031 | }; |
| 1032 | |
| 1033 | struct TILELIST oam_tilelist[34]; |
| 1034 | |
| 1035 | /********************************************* |
| 1036 | * snes_update_obsel() |
| 1037 | * |
| 1038 | * Update sprite settings for next line. |
| 1039 | *********************************************/ |
| 1040 | |
| 1091 | 1041 | static void snes_update_obsel( void ) |
| 1092 | 1042 | { |
| 1093 | 1043 | snes_ppu.layer[SNES_OAM].charmap = snes_ppu.oam.next_charmap; |
| r7644 | r7645 | |
| 1100 | 1050 | } |
| 1101 | 1051 | } |
| 1102 | 1052 | |
| 1053 | /********************************************* |
| 1054 | * snes_oam_list_build() |
| 1055 | * |
| 1056 | * Build a list of the available obj in OAM ram. |
| 1057 | *********************************************/ |
| 1058 | |
| 1103 | 1059 | static void snes_oam_list_build( void ) |
| 1104 | 1060 | { |
| 1105 | 1061 | UINT8 *oamram = (UINT8 *)snes_oam; |
| 1106 | 1062 | INT16 oam = 0x1ff; |
| 1107 | 1063 | UINT16 oam_extra = oam + 0x20; |
| 1108 | 1064 | UINT16 extra = 0; |
| 1109 | | int i; |
| 1065 | int ii; |
| 1110 | 1066 | |
| 1111 | 1067 | snes_ppu.update_oam_list = 0; // eventually, we can optimize the code by only calling this function when there is a change in size |
| 1112 | 1068 | |
| 1113 | | for (i = 127; i >= 0; i--) |
| 1069 | for (ii = 127; ii >= 0; ii--) |
| 1114 | 1070 | { |
| 1115 | | if (((i + 1) % 4) == 0) |
| 1071 | if (((ii + 1) % 4) == 0) |
| 1116 | 1072 | extra = oamram[oam_extra--]; |
| 1117 | 1073 | |
| 1118 | | oam_spritelist[i].vflip = (oamram[oam] & 0x80) >> 7; |
| 1119 | | oam_spritelist[i].hflip = (oamram[oam] & 0x40) >> 6; |
| 1120 | | oam_spritelist[i].priority_bits = (oamram[oam] & 0x30) >> 4; |
| 1121 | | oam_spritelist[i].pal = 128 + ((oamram[oam] & 0x0e) << 3); |
| 1122 | | oam_spritelist[i].tile = (oamram[oam--] & 0x1) << 8; |
| 1123 | | oam_spritelist[i].tile |= oamram[oam--]; |
| 1124 | | oam_spritelist[i].y = oamram[oam--] + 1; /* We seem to need to add one here.... */ |
| 1125 | | oam_spritelist[i].x = oamram[oam--]; |
| 1126 | | oam_spritelist[i].size = (extra & 0x80) >> 7; |
| 1074 | oam_spritelist[ii].vflip = (oamram[oam] & 0x80) >> 7; |
| 1075 | oam_spritelist[ii].hflip = (oamram[oam] & 0x40) >> 6; |
| 1076 | oam_spritelist[ii].priority_bits = (oamram[oam] & 0x30) >> 4; |
| 1077 | oam_spritelist[ii].pal = 128 + ((oamram[oam] & 0x0e) << 3); |
| 1078 | oam_spritelist[ii].tile = (oamram[oam--] & 0x1) << 8; |
| 1079 | oam_spritelist[ii].tile |= oamram[oam--]; |
| 1080 | oam_spritelist[ii].y = oamram[oam--] + 1; /* We seem to need to add one here.... */ |
| 1081 | oam_spritelist[ii].x = oamram[oam--]; |
| 1082 | oam_spritelist[ii].size = (extra & 0x80) >> 7; |
| 1127 | 1083 | extra <<= 1; |
| 1128 | | oam_spritelist[i].x |= ((extra & 0x80) << 1); |
| 1084 | oam_spritelist[ii].x |= ((extra & 0x80) << 1); |
| 1129 | 1085 | extra <<= 1; |
| 1130 | 1086 | |
| 1131 | | oam_spritelist[i].y *= snes_ppu.obj_interlace; |
| 1087 | oam_spritelist[ii].y *= snes_ppu.obj_interlace; |
| 1132 | 1088 | |
| 1133 | 1089 | /* Adjust if past maximum position */ |
| 1134 | | if (oam_spritelist[i].y >= snes_ppu.beam.last_visible_line * snes_ppu.interlace) |
| 1135 | | oam_spritelist[i].y -= 256 * snes_ppu.interlace; |
| 1090 | if (oam_spritelist[ii].y >= snes_ppu.beam.last_visible_line * snes_ppu.interlace) |
| 1091 | oam_spritelist[ii].y -= 256 * snes_ppu.interlace; |
| 1136 | 1092 | |
| 1137 | | oam_spritelist[i].x &= 0x1ff; |
| 1093 | oam_spritelist[ii].x &= 0x1ff; |
| 1138 | 1094 | |
| 1139 | 1095 | /* Determine object size */ |
| 1140 | 1096 | switch (snes_ppu.oam.size) |
| 1141 | 1097 | { |
| 1142 | 1098 | case 0: /* 8x8 or 16x16 */ |
| 1143 | | oam_spritelist[i].width = oam_spritelist[i].size ? 2 : 1; |
| 1144 | | oam_spritelist[i].height = oam_spritelist[i].size ? 2 : 1; |
| 1099 | oam_spritelist[ii].width = oam_spritelist[ii].size ? 2 : 1; |
| 1100 | oam_spritelist[ii].height = oam_spritelist[ii].size ? 2 : 1; |
| 1145 | 1101 | break; |
| 1146 | 1102 | case 1: /* 8x8 or 32x32 */ |
| 1147 | | oam_spritelist[i].width = oam_spritelist[i].size ? 4 : 1; |
| 1148 | | oam_spritelist[i].height = oam_spritelist[i].size ? 4 : 1; |
| 1103 | oam_spritelist[ii].width = oam_spritelist[ii].size ? 4 : 1; |
| 1104 | oam_spritelist[ii].height = oam_spritelist[ii].size ? 4 : 1; |
| 1149 | 1105 | break; |
| 1150 | 1106 | case 2: /* 8x8 or 64x64 */ |
| 1151 | | oam_spritelist[i].width = oam_spritelist[i].size ? 8 : 1; |
| 1152 | | oam_spritelist[i].height = oam_spritelist[i].size ? 8 : 1; |
| 1107 | oam_spritelist[ii].width = oam_spritelist[ii].size ? 8 : 1; |
| 1108 | oam_spritelist[ii].height = oam_spritelist[ii].size ? 8 : 1; |
| 1153 | 1109 | break; |
| 1154 | 1110 | case 3: /* 16x16 or 32x32 */ |
| 1155 | | oam_spritelist[i].width = oam_spritelist[i].size ? 4 : 2; |
| 1156 | | oam_spritelist[i].height = oam_spritelist[i].size ? 4 : 2; |
| 1111 | oam_spritelist[ii].width = oam_spritelist[ii].size ? 4 : 2; |
| 1112 | oam_spritelist[ii].height = oam_spritelist[ii].size ? 4 : 2; |
| 1157 | 1113 | break; |
| 1158 | 1114 | case 4: /* 16x16 or 64x64 */ |
| 1159 | | oam_spritelist[i].width = oam_spritelist[i].size ? 8 : 2; |
| 1160 | | oam_spritelist[i].height = oam_spritelist[i].size ? 8 : 2; |
| 1115 | oam_spritelist[ii].width = oam_spritelist[ii].size ? 8 : 2; |
| 1116 | oam_spritelist[ii].height = oam_spritelist[ii].size ? 8 : 2; |
| 1161 | 1117 | break; |
| 1162 | 1118 | case 5: /* 32x32 or 64x64 */ |
| 1163 | | oam_spritelist[i].width = oam_spritelist[i].size ? 8 : 4; |
| 1164 | | oam_spritelist[i].height = oam_spritelist[i].size ? 8 : 4; |
| 1119 | oam_spritelist[ii].width = oam_spritelist[ii].size ? 8 : 4; |
| 1120 | oam_spritelist[ii].height = oam_spritelist[ii].size ? 8 : 4; |
| 1165 | 1121 | break; |
| 1166 | 1122 | case 6: /* undocumented: 16x32 or 32x64 */ |
| 1167 | | oam_spritelist[i].width = oam_spritelist[i].size ? 4 : 2; |
| 1168 | | oam_spritelist[i].height = oam_spritelist[i].size ? 8 : 4; |
| 1169 | | if (snes_ppu.obj_interlace && !oam_spritelist[i].size) |
| 1170 | | oam_spritelist[i].height = 2; |
| 1123 | oam_spritelist[ii].width = oam_spritelist[ii].size ? 4 : 2; |
| 1124 | oam_spritelist[ii].height = oam_spritelist[ii].size ? 8 : 4; |
| 1125 | if (snes_ppu.obj_interlace && !oam_spritelist[ii].size) |
| 1126 | oam_spritelist[ii].height = 2; |
| 1171 | 1127 | break; |
| 1172 | 1128 | case 7: /* undocumented: 16x32 or 32x32 */ |
| 1173 | | oam_spritelist[i].width = oam_spritelist[i].size ? 4 : 2; |
| 1174 | | oam_spritelist[i].height = oam_spritelist[i].size ? 4 : 4; |
| 1175 | | if (snes_ppu.obj_interlace && !oam_spritelist[i].size) |
| 1176 | | oam_spritelist[i].height = 2; |
| 1129 | oam_spritelist[ii].width = oam_spritelist[ii].size ? 4 : 2; |
| 1130 | oam_spritelist[ii].height = oam_spritelist[ii].size ? 4 : 4; |
| 1131 | if (snes_ppu.obj_interlace && !oam_spritelist[ii].size) |
| 1132 | oam_spritelist[ii].height = 2; |
| 1177 | 1133 | break; |
| 1178 | 1134 | default: |
| 1179 | 1135 | /* we should never enter here... */ |
| r7644 | r7645 | |
| 1183 | 1139 | } |
| 1184 | 1140 | } |
| 1185 | 1141 | |
| 1186 | | static UINT8 oam_itemlist[32]; |
| 1142 | /********************************************* |
| 1143 | * is_sprite_on_scanline() |
| 1144 | * |
| 1145 | * Check if a given sprites intersect current |
| 1146 | * scanline |
| 1147 | *********************************************/ |
| 1187 | 1148 | |
| 1188 | | struct TILELIST { |
| 1189 | | INT16 x; |
| 1190 | | UINT16 priority, pal, tileaddr; |
| 1191 | | int hflip; |
| 1192 | | }; |
| 1193 | | |
| 1194 | | struct TILELIST oam_tilelist[34]; |
| 1195 | | |
| 1196 | 1149 | static int is_sprite_on_scanline( UINT16 curline, UINT8 sprite ) |
| 1197 | 1150 | { |
| 1198 | 1151 | //if sprite is entirely offscreen and doesn't wrap around to the left side of the screen, |
| r7644 | r7645 | |
| 1211 | 1164 | return 0; |
| 1212 | 1165 | } |
| 1213 | 1166 | |
| 1167 | /********************************************* |
| 1168 | * snes_update_objects_rto() |
| 1169 | * |
| 1170 | * Determine which OBJs will be drawn on this |
| 1171 | * scanline. |
| 1172 | *********************************************/ |
| 1173 | |
| 1214 | 1174 | static void snes_update_objects_rto( UINT16 curline ) |
| 1215 | 1175 | { |
| 1216 | | int active_sprite; |
| 1176 | int ii, jj, active_sprite; |
| 1217 | 1177 | UINT8 range_over, time_over; |
| 1218 | 1178 | INT8 xs, ys; |
| 1219 | 1179 | UINT8 line; |
| 1220 | 1180 | UINT8 height, width, vflip, hflip, priority, pal; |
| 1221 | 1181 | UINT16 tile; |
| 1222 | | INT16 i, x, y; |
| 1182 | INT16 x, y; |
| 1223 | 1183 | UINT32 name_sel = 0; |
| 1224 | 1184 | |
| 1225 | 1185 | snes_oam_list_build(); |
| r7644 | r7645 | |
| 1236 | 1196 | memset(oam_itemlist, 0xff, 32); |
| 1237 | 1197 | |
| 1238 | 1198 | /* populate the list of 32 objects */ |
| 1239 | | for (i = 0; i < 128; i++) |
| 1199 | for (ii = 0; ii < 128; ii++) |
| 1240 | 1200 | { |
| 1241 | | active_sprite = (i + snes_ppu.oam.first_sprite) & 0x7f; |
| 1201 | active_sprite = (ii + snes_ppu.oam.first_sprite) & 0x7f; |
| 1242 | 1202 | |
| 1243 | 1203 | if (!is_sprite_on_scanline(curline, active_sprite)) |
| 1244 | 1204 | continue; |
| r7644 | r7645 | |
| 1250 | 1210 | } |
| 1251 | 1211 | |
| 1252 | 1212 | /* reset the list of first 34 tiles to be drawn */ |
| 1253 | | for (i = 0; i < 34; i++) |
| 1254 | | oam_tilelist[i].tileaddr = 0xffff; |
| 1213 | for (ii = 0; ii < 34; ii++) |
| 1214 | oam_tilelist[ii].tileaddr = 0xffff; |
| 1255 | 1215 | |
| 1256 | 1216 | /* populate the list of 34 tiles */ |
| 1257 | | for (i = 31; i >= 0; i--) |
| 1217 | for (ii = 31; ii >= 0; ii--) |
| 1258 | 1218 | { |
| 1259 | | if (oam_itemlist[i] == 0xff) |
| 1219 | if (oam_itemlist[ii] == 0xff) |
| 1260 | 1220 | continue; |
| 1261 | 1221 | |
| 1262 | | active_sprite = oam_itemlist[i]; |
| 1222 | active_sprite = oam_itemlist[ii]; |
| 1263 | 1223 | |
| 1264 | 1224 | tile = oam_spritelist[active_sprite].tile; |
| 1265 | 1225 | x = oam_spritelist[active_sprite].x; |
| r7644 | r7645 | |
| 1284 | 1244 | line <<= 1; |
| 1285 | 1245 | tile <<= 5; |
| 1286 | 1246 | |
| 1287 | | int ii; |
| 1288 | | |
| 1289 | | for (ii = 0; ii < width; ii++) |
| 1247 | for (jj = 0; jj < width; jj++) |
| 1290 | 1248 | { |
| 1291 | | INT16 xx = (x + (ii << 3)) & 0x1ff; |
| 1249 | INT16 xx = (x + (jj << 3)) & 0x1ff; |
| 1292 | 1250 | |
| 1293 | 1251 | if (x != 256 && xx >= 256 && (xx + 7) < 512) |
| 1294 | 1252 | continue; |
| r7644 | r7645 | |
| 1296 | 1254 | if (time_over++ >= 34) |
| 1297 | 1255 | break; |
| 1298 | 1256 | |
| 1299 | | xs = (hflip) ? (width - 1 - ii) : ii; |
| 1257 | xs = (hflip) ? (width - 1 - jj) : jj; |
| 1300 | 1258 | oam_tilelist[time_over - 1].tileaddr = name_sel + tile + table_obj_offset[ys][xs] + line; |
| 1301 | 1259 | oam_tilelist[time_over - 1].hflip = hflip; |
| 1302 | 1260 | oam_tilelist[time_over - 1].x = xx; |
| r7644 | r7645 | |
| 1314 | 1272 | snes_ppu.stat77_flags |= 0x80; |
| 1315 | 1273 | } |
| 1316 | 1274 | |
| 1275 | /********************************************* |
| 1276 | * snes_update_objects() |
| 1277 | * |
| 1278 | * Update an entire line of sprites. |
| 1279 | *********************************************/ |
| 1280 | |
| 1317 | 1281 | static void snes_update_objects( UINT8 priority_oam0, UINT8 priority_oam1, UINT8 priority_oam2, UINT8 priority_oam3 ) |
| 1318 | 1282 | { |
| 1319 | 1283 | UINT8 pri, priority[4]; |
| 1320 | 1284 | UINT32 charaddr; |
| 1321 | | int i; |
| 1285 | int ii; |
| 1322 | 1286 | |
| 1323 | 1287 | #ifdef SNES_LAYER_DEBUG |
| 1324 | 1288 | if (debug_options.bg_disabled[SNES_OAM]) |
| r7644 | r7645 | |
| 1341 | 1305 | priority[3] = priority_oam3; |
| 1342 | 1306 | |
| 1343 | 1307 | /* finally draw the tiles from the tilelist */ |
| 1344 | | for (i = 0; i < 34; i++) |
| 1308 | for (ii = 0; ii < 34; ii++) |
| 1345 | 1309 | { |
| 1346 | | int tile = i; |
| 1310 | int tile = ii; |
| 1347 | 1311 | #ifdef SNES_LAYER_DEBUG |
| 1348 | 1312 | if (debug_options.sprite_reversed) |
| 1349 | | tile = 33 - i; |
| 1313 | tile = 33 - ii; |
| 1350 | 1314 | #endif /* SNES_LAYER_DEBUG */ |
| 1351 | 1315 | |
| 1352 | 1316 | if (oam_tilelist[tile].tileaddr == 0xffff) |
| r7644 | r7645 | |
| 1594 | 1558 | * snes_update_offsets() |
| 1595 | 1559 | * |
| 1596 | 1560 | * Update the offsets with the latest changes. |
| 1561 | * This is currently unused, but it could |
| 1562 | * possibly be handy for some minor optimization |
| 1597 | 1563 | *********************************************/ |
| 1598 | 1564 | |
| 1599 | 1565 | static void snes_update_offsets( void ) |
| r7644 | r7645 | |
| 1605 | 1571 | snes_ppu.update_offsets = 0; |
| 1606 | 1572 | } |
| 1607 | 1573 | |
| 1574 | /***************************************** |
| 1575 | * snes_draw_blend() |
| 1576 | * |
| 1577 | * Routine for additive/subtractive blending |
| 1578 | * between the main and sub screens, i.e. |
| 1579 | * color math. |
| 1580 | *****************************************/ |
| 1581 | |
| 1582 | INLINE void snes_draw_blend( UINT16 offset, UINT16 *colour, UINT8 prevent_color_math, UINT8 black_pen_clip, int switch_screens ) |
| 1583 | { |
| 1584 | #ifdef SNES_LAYER_DEBUG |
| 1585 | if (debug_options.colormath_disabled) |
| 1586 | return; |
| 1587 | #endif /* SNES_LAYER_DEBUG */ |
| 1588 | |
| 1589 | /* when color math is applied to subscreen pixels, the blending depends on the blending used by the previous mainscreen |
| 1590 | pixel, except for subscreen pixel 0 which has no previous mainscreen pixel, see comments in snes_refresh_scanline */ |
| 1591 | if (switch_screens && offset > 0) |
| 1592 | offset -= 1; |
| 1593 | |
| 1594 | if ((black_pen_clip == SNES_CLIP_ALWAYS) || |
| 1595 | (black_pen_clip == SNES_CLIP_IN && snes_ppu.clipmasks[SNES_COLOR][offset]) || |
| 1596 | (black_pen_clip == SNES_CLIP_OUT && !snes_ppu.clipmasks[SNES_COLOR][offset])) |
| 1597 | *colour = 0; //clip to black before color math |
| 1598 | |
| 1599 | if (prevent_color_math == SNES_CLIP_ALWAYS) // blending mode 3 == always OFF |
| 1600 | return; |
| 1601 | |
| 1602 | if ((prevent_color_math == SNES_CLIP_NEVER) || |
| 1603 | (prevent_color_math == SNES_CLIP_IN && !snes_ppu.clipmasks[SNES_COLOR][offset]) || |
| 1604 | (prevent_color_math == SNES_CLIP_OUT && snes_ppu.clipmasks[SNES_COLOR][offset])) |
| 1605 | { |
| 1606 | UINT16 r, g, b; |
| 1607 | struct SCANLINE *subscreen; |
| 1608 | int clip_max = 0; // if add then clip to 0x1f, if sub then clip to 0 |
| 1609 | |
| 1610 | #ifdef SNES_LAYER_DEBUG |
| 1611 | /* Toggle drawing of SNES_SUBSCREEN or SNES_MAINSCREEN */ |
| 1612 | if (debug_options.draw_subscreen) |
| 1613 | { |
| 1614 | subscreen = switch_screens ? &scanlines[SNES_SUBSCREEN] : &scanlines[SNES_MAINSCREEN]; |
| 1615 | } |
| 1616 | else |
| 1617 | #endif /* SNES_LAYER_DEBUG */ |
| 1618 | { |
| 1619 | subscreen = switch_screens ? &scanlines[SNES_MAINSCREEN] : &scanlines[SNES_SUBSCREEN]; |
| 1620 | } |
| 1621 | |
| 1622 | if (snes_ppu.sub_add_mode) /* SNES_SUBSCREEN*/ |
| 1623 | { |
| 1624 | if (!BIT(snes_ppu.color_modes, 7)) |
| 1625 | { |
| 1626 | /* 0x00 add */ |
| 1627 | r = (*colour & 0x1f) + (subscreen->buffer[offset] & 0x1f); |
| 1628 | g = ((*colour & 0x3e0) >> 5) + ((subscreen->buffer[offset] & 0x3e0) >> 5); |
| 1629 | b = ((*colour & 0x7c00) >> 10) + ((subscreen->buffer[offset] & 0x7c00) >> 10); |
| 1630 | clip_max = 1; |
| 1631 | } |
| 1632 | else |
| 1633 | { |
| 1634 | /* 0x80 sub */ |
| 1635 | r = (*colour & 0x1f) - (subscreen->buffer[offset] & 0x1f); |
| 1636 | g = ((*colour & 0x3e0) >> 5) - ((subscreen->buffer[offset] & 0x3e0) >> 5); |
| 1637 | b = ((*colour & 0x7c00) >> 10) - ((subscreen->buffer[offset] & 0x7c00) >> 10); |
| 1638 | if (r > 0x1f) r = 0; |
| 1639 | if (g > 0x1f) g = 0; |
| 1640 | if (b > 0x1f) b = 0; |
| 1641 | } |
| 1642 | /* only halve if the color is not the back colour */ |
| 1643 | if (BIT(snes_ppu.color_modes, 6) && (subscreen->buffer[offset] != snes_cgram[FIXED_COLOUR])) |
| 1644 | { |
| 1645 | r >>= 1; |
| 1646 | g >>= 1; |
| 1647 | b >>= 1; |
| 1648 | } |
| 1649 | } |
| 1650 | else /* Fixed colour */ |
| 1651 | { |
| 1652 | if (!BIT(snes_ppu.color_modes, 7)) |
| 1653 | { |
| 1654 | /* 0x00 add */ |
| 1655 | r = (*colour & 0x1f) + (snes_cgram[FIXED_COLOUR] & 0x1f); |
| 1656 | g = ((*colour & 0x3e0) >> 5) + ((snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5); |
| 1657 | b = ((*colour & 0x7c00) >> 10) + ((snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10); |
| 1658 | clip_max = 1; |
| 1659 | } |
| 1660 | else |
| 1661 | { |
| 1662 | /* 0x80: sub */ |
| 1663 | r = (*colour & 0x1f) - (snes_cgram[FIXED_COLOUR] & 0x1f); |
| 1664 | g = ((*colour & 0x3e0) >> 5) - ((snes_cgram[FIXED_COLOUR] & 0x3e0) >> 5); |
| 1665 | b = ((*colour & 0x7c00) >> 10) - ((snes_cgram[FIXED_COLOUR] & 0x7c00) >> 10); |
| 1666 | if (r > 0x1f) r = 0; |
| 1667 | if (g > 0x1f) g = 0; |
| 1668 | if (b > 0x1f) b = 0; |
| 1669 | } |
| 1670 | /* halve if necessary */ |
| 1671 | if (BIT(snes_ppu.color_modes, 6)) |
| 1672 | { |
| 1673 | r >>= 1; |
| 1674 | g >>= 1; |
| 1675 | b >>= 1; |
| 1676 | } |
| 1677 | } |
| 1678 | |
| 1679 | /* according to anomie's docs, after addition has been performed, division by 2 happens *before* clipping to max, hence we clip now */ |
| 1680 | if (clip_max) |
| 1681 | { |
| 1682 | if (r > 0x1f) r = 0x1f; |
| 1683 | if (g > 0x1f) g = 0x1f; |
| 1684 | if (b > 0x1f) b = 0x1f; |
| 1685 | } |
| 1686 | |
| 1687 | *colour = ((r & 0x1f) | ((g & 0x1f) << 5) | ((b & 0x1f) << 10)); |
| 1688 | } |
| 1689 | } |
| 1690 | |
| 1608 | 1691 | /********************************************* |
| 1609 | 1692 | * snes_refresh_scanline() |
| 1610 | 1693 | * |
| 1611 | 1694 | * Redraw the current line. |
| 1612 | 1695 | *********************************************/ |
| 1696 | /********************************************* |
| 1697 | * Notice that in hires and pseudo hires modes, |
| 1698 | * i.e. when 512 different pixels are present |
| 1699 | * in a scanline, a crt TV monitor would end |
| 1700 | * up blending adjacent pixels. To mimic this, |
| 1701 | * we add a small (optional) hack which enters |
| 1702 | * only in the very last stage of the scanline |
| 1703 | * drawing and which simulates the TV by |
| 1704 | * replacing the exact pixel color with an |
| 1705 | * average of the current and next pixel colors. |
| 1706 | * Credits (and thanks) to Blargg and Byuu for |
| 1707 | * the optimized averaging algorithm. |
| 1708 | *********************************************/ |
| 1613 | 1709 | |
| 1614 | 1710 | static void snes_refresh_scanline( running_machine *machine, bitmap_t *bitmap, UINT16 curline ) |
| 1615 | 1711 | { |
| r7644 | r7645 | |
| 1619 | 1715 | struct SCANLINE *scanline1, *scanline2; |
| 1620 | 1716 | UINT16 c; |
| 1621 | 1717 | UINT16 prev_colour = 0; |
| 1622 | | int average = input_port_read_safe(machine, "OPTIONS", 0) & 0x01; |
| 1718 | int blurring = input_port_read_safe(machine, "OPTIONS", 0) & 0x01; |
| 1623 | 1719 | |
| 1624 | 1720 | profiler_mark_start(PROFILER_VIDEO); |
| 1625 | 1721 | |
| r7644 | r7645 | |
| 1726 | 1822 | /* prepare the pixel from sub screen */ |
| 1727 | 1823 | c = scanline2->buffer[x]; |
| 1728 | 1824 | |
| 1729 | | /* in hires, subscreen pixels are blended as well: for each subscreen pixel, color math is applied if |
| 1730 | | it had been applied to the previous mainscreen pixel. What happens at subscreen pixel 0 (which has no |
| 1731 | | previous mainscreen pixel) is undocumented. Until more info are discovered, we (arbitrarily) apply to it |
| 1732 | | the same color math as the *next* mainscreen pixel (i.e. mainscreen pixel 0) */ |
| 1825 | /* in hires/pseudo-hires, subscreen pixels are blended as well: for each subscreen pixel, color math |
| 1826 | is applied if it had been applied to the previous mainscreen pixel. What happens at subscreen pixel 0 |
| 1827 | (which has no previous mainscreen pixel) is undocumented. Until more info are discovered, we (arbitrarily) |
| 1828 | apply to it the same color math as the *next* mainscreen pixel (i.e. mainscreen pixel 0), which seems as good as |
| 1829 | any other choice */ |
| 1733 | 1830 | if (x == 0 && !scanline1->blend_exception[0] && snes_ppu.layer[scanline1->layer[0]].color_math) |
| 1734 | 1831 | snes_draw_blend(0, &c, snes_ppu.prevent_color_math, snes_ppu.clip_to_black, 1); |
| 1735 | 1832 | else if (x > 0 && !scanline1->blend_exception[x - 1] && snes_ppu.layer[scanline1->layer[x - 1]].color_math) |
| r7644 | r7645 | |
| 1738 | 1835 | tmp_col[0] = c; |
| 1739 | 1836 | |
| 1740 | 1837 | /* average the first pixel if required, or draw it directly*/ |
| 1741 | | if (average) |
| 1742 | | c = (prev_colour + tmp_col[0] - ((prev_colour ^ tmp_col[0]) & 0x0421)) >> 1; |
| 1838 | if (blurring) |
| 1839 | c = (prev_colour + tmp_col[0] - ((prev_colour ^ tmp_col[0]) & 0x0421)) >> 1; // Hack code to mimic TV pixel blurring |
| 1743 | 1840 | else |
| 1744 | 1841 | c = tmp_col[0]; |
| 1745 | 1842 | |
| r7644 | r7645 | |
| 1750 | 1847 | *BITMAP_ADDR32(bitmap, curline, x * 2 + 0) = MAKE_RGB(pal5bit(r), pal5bit(g), pal5bit(b)); |
| 1751 | 1848 | prev_colour = tmp_col[0]; |
| 1752 | 1849 | |
| 1753 | | |
| 1754 | 1850 | /* average the second pixel if required, or draw it directly*/ |
| 1755 | | if (average) |
| 1756 | | c = (prev_colour + tmp_col[1] - ((prev_colour ^ tmp_col[1]) & 0x0421)) >> 1; |
| 1851 | if (blurring) |
| 1852 | c = (prev_colour + tmp_col[1] - ((prev_colour ^ tmp_col[1]) & 0x0421)) >> 1; // Hack code to mimic TV pixel blurring |
| 1757 | 1853 | else |
| 1758 | 1854 | c = tmp_col[1]; |
| 1759 | 1855 | |