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