OVR: Change the way to walk and rotate with the controller
[mercenary-reloaded.git] / src / libvideo / video.c
1 /* video emulation
2  *
3  * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
4  * All Rights Reserved
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <stdio.h>
21 #include <stdint.h>
22 #include "../libsdl/print.h"
23 #include "video.h"
24 #include "../libcpu/m68kcpu.h"
25
26 //#define DEBUG_COPPERLIST
27
28 static void planar2chunky(uint8_t *rgb, uint8_t *bitplanes[], uint16_t palette[], int planes, int width, int y_start, int y_end)
29 {
30         uint8_t red, green, blue;
31         int x, y, p, b, i;
32         uint8_t word[planes], chunk;
33         uint16_t rgb4;
34
35         /* we start memory read from the following calulated bitplane offset: */
36         i = y_start * (width / 8);
37         rgb += y_start * width * 3;
38         for (y = y_start; y < y_end; y++) {
39                 for (x = 0; x < width; x += 8) {
40                         for (p = 0; p < planes; p++)
41                                 word[p] = bitplanes[p][i];
42                         for (b = 0; b < 8; b++) {
43                                 chunk = (word[planes - 1] >> 7);
44                                 word[planes - 1] <<= 1;
45                                 for (p = planes - 2; p >= 0; p--) {
46                                         chunk = (chunk << 1) | (word[p] >> 7);
47                                         word[p] <<= 1;
48                                 }
49                                 rgb4 = palette[chunk];
50                                 red = (rgb4 >> 4) & 0xf0;
51                                 green = rgb4 & 0xf0;
52                                 blue = rgb4 << 4;
53                                 red = red | (red >> 4);
54                                 green = green | (green >> 4);
55                                 blue = blue | (blue >> 4);
56                                 *rgb++ = red;
57                                 *rgb++ = green;
58                                 *rgb++ = blue;
59                         }
60                         i++;
61                 }
62         }
63 }
64
65 static void planar2chunky2(uint8_t *rgb1, uint8_t *bitplanes[], uint16_t palette[], int planes, int width, int y_start, int y_end)
66 {
67         uint8_t *rgb2, red, green, blue;
68         int x, y, p, b, i;
69         uint8_t word[planes], chunk;
70         uint16_t rgb4;
71
72         rgb2 = rgb1 + width * 6;
73
74         /* we start memory read from the following calulated bitplane offset: */
75         i = y_start * (width / 8);
76         rgb1 += 2 * y_start * width * 6;
77         rgb2 += 2 * y_start * width * 6;
78         for (y = y_start; y < y_end; y++) {
79                 for (x = 0; x < width; x += 8) {
80                         for (p = 0; p < planes; p++)
81                                 word[p] = bitplanes[p][i];
82                         for (b = 0; b < 8; b++) {
83                                 chunk = (word[planes - 1] >> 7);
84                                 word[planes - 1] <<= 1;
85                                 for (p = planes - 2; p >= 0; p--) {
86                                         chunk = (chunk << 1) | (word[p] >> 7);
87                                         word[p] <<= 1;
88                                 }
89                                 rgb4 = palette[chunk];
90                                 red = (rgb4 >> 4) & 0xf0;
91                                 green = rgb4 & 0xf0;
92                                 blue = rgb4 << 4;
93                                 red = red | (red >> 4);
94                                 green = green | (green >> 4);
95                                 blue = blue | (blue >> 4);
96                                 *rgb1++ = red;
97                                 *rgb1++ = green;
98                                 *rgb1++ = blue;
99                                 *rgb1++ = red;
100                                 *rgb1++ = green;
101                                 *rgb1++ = blue;
102                                 *rgb2++ = red;
103                                 *rgb2++ = green;
104                                 *rgb2++ = blue;
105                                 *rgb2++ = red;
106                                 *rgb2++ = green;
107                                 *rgb2++ = blue;
108                         }
109                         i++;
110                 }
111                 rgb1 += width * 6;
112                 rgb2 += width * 6;
113         }
114 }
115
116 #define COP1LCH         0x080
117 #define COP1LCL         0x082
118 #define COP2LCH         0x084
119 #define COP2LCL         0x086
120 #define BPL1PTH         0x0e0
121 #define COLOR00         0x180
122
123 int emul_video(uint8_t *rgb, uint8_t *memory, uint16_t render_palette[], int width, int height, int diwstart, uint16_t *io, int start, int stop, int double_size)
124 {
125         uint32_t bitplane[8] = {0, 0, 0, 0, 0, 0, 0, 0};
126         uint8_t *bitmem[8];
127         uint16_t palette[16];
128         uint32_t copperlist;
129         uint16_t c1, c2;
130         int row, last_row, line, last_line, from, to;
131         int count;
132         int i;
133         int all_white;
134
135         /* special case where all palette entries are white, except for color 0 that is always black. (this happens during explosion) */
136         all_white = 1;
137         for (i = 1; i < 16; i++) {
138                 if (io[i * 2 + 0x180] != 0xfff)
139                         all_white = 0;
140         }
141
142         /* First set palette as specified in IO space.
143          * This is set with last IRQ, so it is used after VBL.
144          * Later the copper list causes them to be changed */
145         for (i = 0; i < 16; i++) {
146                 /* use palette if all value are white, else use the palette that is used during VBL-IRQ */
147                 if (all_white)
148                         palette[i] = io[i * 2 + 0x180];
149                 else {
150                         /* use palette that will be set during IRQ routine.
151                          * we need to copy it here, because we don't call IRQ routine for every rendered frame,
152                          * so the color registers are not set by IRQ routine for every frame.
153                          */
154                         palette[i] = render_palette[i];
155                 }
156         }
157
158         /* get copper list start pointer */
159         copperlist = (io[COP1LCH] << 16) | io[COP1LCL];
160         if (!copperlist) {
161                 print_error("Copper list pointer not initialized, please fix!\n");
162                 return all_white;
163         }
164
165 #ifdef DEBUG_COPPERLIST
166         printf("Copper list reading from pointer: %06x\n", copperlist);
167 #endif
168
169         /* parse copper list */
170         count = 0;
171         last_row = 0;
172         last_line = 0;
173         while (42) {
174                 if (++count == 100) {
175                         print_error("Copper list does not seem to terminate, please fix!\n");
176                         return all_white;
177                 }
178                 c1 = m68k_read_memory_16(copperlist);
179                 c2 = m68k_read_memory_16(copperlist + 2);
180 #ifdef DEBUG_COPPERLIST
181                 printf("%06x: C1=%04x C2=%04x\n", copperlist, c1, c2);
182 #endif
183                 copperlist += 4;
184                 if (c1 == 0x0088) {
185                         copperlist = (io[COP1LCH] << 16) | io[COP1LCL];
186 #ifdef DEBUG_COPPERLIST
187                         printf("switching to 1st copperlist=%06x\n", copperlist);
188 #endif
189                         continue;
190                 }
191                 if (c1 == 0x008a) {
192                         copperlist = (io[COP2LCH] << 16) | io[COP2LCL];
193 #ifdef DEBUG_COPPERLIST
194                         printf("switching to 2nd copperlist=%06x\n", copperlist);
195 #endif
196                         continue;
197                 }
198                 if (!(c1 & 1)) {
199                         /* MOVE */
200 #ifdef DEBUG_COPPERLIST
201                         printf("MOVE 0xdff%03x = 0x%04x\n", c1, c2);
202 #endif
203                         /* get bitplane pointers */
204                         if (c1 >= BPL1PTH && c1 <= BPL1PTH + 32) {
205                                 if ((c1 & 2))
206                                         bitplane[(c1 - BPL1PTH) / 4] = (bitplane[(c1 - BPL1PTH) / 4] & 0xffff0000) | c2;
207                                 else
208                                         bitplane[(c1 - BPL1PTH) / 4] = (bitplane[(c1 - BPL1PTH) / 4] & 0x0000ffff) | (c2 << 16);
209                         }
210                         /* get color registers */
211                         if (c1 >= COLOR00 && c1 <= COLOR00 + 32) {
212                                 palette[(c1 - COLOR00) / 2] = c2;
213                         }
214                 } else {
215                         if ((c2 & 1)) {
216                                 print_error("We suppport no SKIP command in copper list, please fix!\n");
217                                 continue;
218                         }
219                         /* WAIT */
220                         /* get new raster position.
221                          * if the value is lower or equal, we add 256 lines
222                          * we ignore column, since it is not relevant for this game.
223                          */
224                         row = (c1 >> 8) & ((c2 >> 8) | 0x80);
225                         if (row < (last_row & 0xff))
226                                 row |= 0x100;
227 #ifdef DEBUG_COPPERLIST
228                         printf("WAIT row = 0x%02x & 0x%02x = %d\n", c1 >> 8, (c2 >> 8) | 0x80, row);
229 #endif
230                         last_row = row;
231                         /* line relative to display window start */
232                         line = row - diwstart;
233                         /* continue: before display window start */
234                         if (line <= last_line)
235                                 continue;
236                         /* render up to height */
237                         if (line >= height)
238                                 line = height;
239                         /* check if bitplane pointers are set */
240 #ifdef DEBUG_COPPERLIST
241                         printf("Bitplanes:");
242 #endif
243                         for (i = 0; i < 4; i++) {
244 #ifdef DEBUG_COPPERLIST
245                                 printf(" %06x", bitplane[i]);
246 #endif
247                                 if (bitplane[i] == 0 || bitplane[i] + width * height / 8 >= 0x80000) {
248 #ifdef DEBUG_COPPERLIST
249                                         printf("\n");
250 #endif
251                                         print_error("Bitplane %d in copper list not set or out of range, please fix!\n", i);
252                                         return all_white;
253                                 }
254                                 bitmem[i] = memory + bitplane[i];
255                         }
256 #ifdef DEBUG_COPPERLIST
257                         printf("\n");
258 #endif
259                         /* render portion before WAIT line */
260 #ifdef DEBUG_COPPERLIST
261                         printf("Render from line %d to line %d\n", last_line, line - 1);
262 #endif
263 #ifdef DEBUG_COPPERLIST
264                         printf("Palette:");
265                         for (i = 0; i < 16; i++) {
266                                 printf(" %03x", palette[i] & 0xfff);
267                         }
268                         printf("\n");
269 #endif
270
271                         from = last_line;
272                         to = line;
273                         if (start > from)
274                                 from = start;
275                         if (stop < to)
276                                 to = stop;
277                         if (to > from) {
278                                 if (double_size)
279                                         planar2chunky2(rgb, bitmem, palette, 4, width, from, to);
280                                 else
281                                         planar2chunky(rgb, bitmem, palette, 4, width, from, to);
282                         }
283                         /* done if we rendered up to height */
284                         if (line == height)
285                                 break;
286                         /* remeber for next wait command */
287                         last_row = row;
288                         last_line = line;
289                 }
290         }
291
292         return all_white;
293 }