1 /* 2 * Looks up a vertical scroll value and sets some related variables. 3 */ 4 #undef LOOKUP_YSCROLL_REC 5 #define LOOKUP_YSCROLL_REC(rec_no) \ 6 do { \ 7 yscroll_amount = get_word(vsram + rec_no * 2) & 0x7ff; \ 8 \ 9 /* interlace ? */ \ 10 if (reg[12] & 2) \ 11 yscroll_amount >>= 1; \ 12 \ 13 /* Offset for the line */ \ 14 yscroll_amount += line; \ 15 \ 16 yoff = ((yscroll_amount >> 3) & (ysize - 1)); \ 17 tile_line = (tiles + ((xsize * yoff) & 0x1fff)); \ 18 scan = (yscroll_amount & 7); \ 19 } \ 20 while (0) 21 22 { 23 int xsize, ysize; 24 int x, scan = 0, w, xstart; 25 static int sizes[4] = { 32, 64, 64, 128 }; 26 unsigned which; 27 unsigned char *where, *hscroll_rec_ptr, *tiles, *tile_line = NULL; 28 int xoff, yoff, xoff_mask; 29 int hscroll_amount, yscroll_amount = 0; 30 uint8_t two_cell_vscroll = 0; 31 32 /* 33 * when VSCR bit is set in register 11, this is 'per 2-cell' 34 * vertical scrolling as opposed to full screen vscrolling. 35 */ 36 two_cell_vscroll = ((reg[11] >> 2) & 0x1); 37 38 #if PLANE == 0 39 // Plane 0 is only where the window isn't 40 // This should make Herzog Zwei split screen work perfectly, and clean 41 // up those little glitches on Sonic 3's level select. 42 if (reg[18] & 0x80) { 43 // Window goes down, plane 0 goes up! :) 44 if ((line >> 3) >= (reg[18] & 0x1f)) 45 return; 46 } 47 else { 48 // Window goes up, plane 0 goes down 49 if ((line >> 3) < (reg[18] & 0x1f)) 50 return; 51 } 52 #endif 53 54 /* 55 * Get the vertical/horizontal scroll plane sizes 56 * 57 * 0b00: 32 cell 58 * 0b01: 64 cell 59 * 0b10: prohibited, but unlicensed games use this 60 * turns out to be 64. 61 * 0b11: 128 cell 62 */ 63 xsize = (sizes[(reg[16] & 3)] << 1); 64 ysize = sizes[((reg[16] >> 4) & 3)]; 65 66 /* 67 * Here we compute pointer to the beginning of the hscroll table. 68 * The base address of the table is stored in reg[13] << 10. 69 */ 70 #if PLANE == 0 71 hscroll_rec_ptr = (vram + ((reg[13] << 10) & 0xfc00)); 72 tiles = (vram + (reg[2] << 10)); 73 #else // PLANE == 1 74 hscroll_rec_ptr = (vram + ((reg[13] << 10) & 0xfc00) + 2); 75 tiles = (vram + (reg[4] << 13)); 76 #endif 77 78 // Wide or narrow? 79 if (reg[12] & 1) { 80 w = 40; 81 xstart = -8; 82 } 83 else { 84 w = 32; 85 xstart = 24; 86 } 87 88 /* 89 * Lookup the horizontal offset. 90 * See Charles MacDonald's genvdp.txt for explanation. 91 */ 92 switch (reg[11] & 3) { 93 case 0: 94 // full screen 95 // NOP - pointer in the right place 96 break; 97 case 1: 98 // invalid, but populous uses it 99 hscroll_rec_ptr += ((line & 7) << 2); 100 break; 101 case 2: 102 // per tile 103 hscroll_rec_ptr += ((line & ~7) << 2); 104 break; 105 case 3: 106 // per line 107 hscroll_rec_ptr += (line << 2); 108 break; 109 } 110 111 hscroll_amount = get_word(hscroll_rec_ptr); 112 xoff_mask = xsize - 1; 113 xoff = ((-(hscroll_amount>>3) - 1)<<1) & xoff_mask; 114 where = dest + (xstart + (hscroll_amount & 7)) * (int) Bpp; 115 116 /* 117 * If this is not column vscroll mode, we look up the 118 * whole screen vertical scroll value once and once only. 119 */ 120 if (two_cell_vscroll == 0) 121 LOOKUP_YSCROLL_REC(PLANE); 122 123 /* 124 * Loop cells, we draw 2 more cells than expected (-1 and w) because 125 * previously off-screen cells can be horizontally scrolled on-screen. 126 */ 127 for (x = -1; (x <= w); x++) { 128 /* 129 * If we are in 2-cell vscroll mode then lookup the amount by 130 * which we should scroll this tile. 131 * 132 * If we are not in 2-cell vscroll then we looked up the value 133 * for the whole screen vscroll earlier. 134 * 135 * We lookup vscroll values on even x values and this is the 136 * vscroll value for the next two cells. Note that cell -1 is 137 * a special case as we never looked up the vscroll value for 138 * cell -2. 139 */ 140 if ((two_cell_vscroll) && ((x % 2 == 0) || (x == -1))) { 141 142 /* 143 * Note that the underflow and overflow of the table 144 * for cell -1 and cell w is intentional. 145 * 146 * http://gendev.spritesmind.net/forum/viewtopic.php?t=737&postdays=0&postorder=asc&start=30 147 */ 148 uint8_t cell_index = (uint8_t) x % w; 149 int vscroll_rec_no = 2 * (cell_index / 2); 150 151 /* 152 * The records alternate, PLANE A, PLANE B, PLANE A, 153 * ... 154 */ 155 #if PLANE == 1 156 vscroll_rec_no++; 157 #endif 158 LOOKUP_YSCROLL_REC(vscroll_rec_no); 159 } 160 161 #if PLANE == 0 162 if (reg[17] & 0x80) { 163 // Don't draw where the window will be 164 if (x >= ((reg[17] & 0x1f) << 1)) 165 goto skip; 166 } 167 else { 168 // + 1 so scroll layers in Sonic look right 169 if ((x + 1) < ((reg[17] & 0x1f) << 1)) 170 goto skip; 171 } 172 #endif 173 which = get_word(tile_line + xoff); 174 175 #if (FRONT == 0) && (PLANE == 1) 176 draw_tile_solid(which, scan, where); 177 #elif FRONT == 1 178 if (which >> 15) 179 draw_tile(which, scan, where); 180 #else 181 if (!(which >> 15)) 182 draw_tile(which, scan, where); 183 #endif 184 185 #if PLANE == 0 186 skip: 187 #endif 188 where += Bpp_times8; 189 xoff = ((xoff + 2) & xoff_mask); 190 } 191 } 192