First beta version 1.0
[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 "video.h"
23 #include "../libcpu/m68kcpu.h"
24
25 //#define DEBUG_COPPERLIST
26
27 static void planar2chunky(uint8_t *rgb, uint8_t *bitplanes[], uint16_t palette[], int planes, int width, int y_start, int y_end)
28 {
29         int x, y, p, b, i;
30         uint8_t word[planes], chunk;
31         uint16_t rgb4;
32
33         /* we start memory read from the following calulated bitplane offset: */
34         i = y_start * (width / 8);
35         rgb += y_start * width * 3;
36         for (y = y_start; y < y_end; y++) {
37                 for (x = 0; x < width; x += 8) {
38                         for (p = 0; p < planes; p++)
39                                 word[p] = bitplanes[p][i];
40                         for (b = 0; b < 8; b++) {
41                                 chunk = (word[planes - 1] >> 7);
42                                 word[planes - 1] <<= 1;
43                                 for (p = planes - 2; p >= 0; p--) {
44                                         chunk = (chunk << 1) | (word[p] >> 7);
45                                         word[p] <<= 1;
46                                 }
47                                 rgb4 = palette[chunk];
48                                 *rgb++ = (rgb4 >> 4) & 0xf0;
49                                 *rgb++ = rgb4 & 0xf0;
50                                 *rgb++ = rgb4 << 4;
51                         }
52                         i++;
53                 }
54         }
55 }
56
57 #define COP1LCH         0x080
58 #define COP1LCL         0x082
59 #define COP2LCH         0x084
60 #define COP2LCL         0x086
61 #define BPL1PTH         0x0e0
62 #define COLOR00         0x180
63
64 void 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)
65 {
66         uint32_t bitplane[8] = {0, 0, 0, 0, 0, 0, 0, 0};
67         uint8_t *bitmem[8];
68         uint16_t palette[16];
69         uint32_t copperlist;
70         uint16_t c1, c2;
71         int row, last_row, line, last_line, from, to;
72         int count;
73         int i;
74         int all_white;
75
76         /* special case where all palette entries are white. (unknown reason, maybe due to teleporter travel) */
77         all_white = 1;
78         for (i = 0; i < 16; i++) {
79                 if (io[i * 2 + 0x180] != 0xfff)
80                         all_white = 0;
81         }
82
83         /* First set palette as specified in IO space.
84          * This is set with last IRQ, so it is used after VBL.
85          * Later the copper list causes them to be changed */
86         for (i = 0; i < 16; i++) {
87                 /* use palette if all value are white, else use the palette that is used during VBL-IRQ */
88                 if (all_white)
89                         palette[i] = io[i * 2 + 0x180];
90                 else {
91                         /* use palette that will be set during IRQ routine.
92                          * we need to copy it here, because we don't call IRQ routine for every rendered frame,
93                          * so the color registers are not set by IRQ routine for every frame.
94                          */
95                         palette[i] = render_palette[i];
96                 }
97         }
98
99         /* get copper list start pointer */
100         copperlist = (io[COP1LCH] << 16) | io[COP1LCL];
101         if (!copperlist) {
102                 fprintf(stderr, "Copper list pointer not initialized, please fix!\n");
103                 return;
104         }
105
106 #ifdef DEBUG_COPPERLIST
107         printf("Copper list reading from pointer: %06x\n", copperlist);
108 #endif
109
110         /* parse copper list */
111         count = 0;
112         last_row = 0;
113         last_line = 0;
114         while (42) {
115                 if (++count == 100) {
116                         fprintf(stderr, "Copper list does not seem to terminate, please fix!\n");
117                         return;
118                 }
119                 c1 = m68k_read_memory_16(copperlist);
120                 c2 = m68k_read_memory_16(copperlist + 2);
121 #ifdef DEBUG_COPPERLIST
122                 printf("%06x: C1=%04x C2=%04x\n", copperlist, c1, c2);
123 #endif
124                 copperlist += 4;
125                 if (c1 == 0x0088) {
126                         copperlist = (io[COP1LCH] << 16) | io[COP1LCL];
127 #ifdef DEBUG_COPPERLIST
128                         printf("switching to 1st copperlist=%06x\n", copperlist);
129 #endif
130                         continue;
131                 }
132                 if (c1 == 0x008a) {
133                         copperlist = (io[COP2LCH] << 16) | io[COP2LCL];
134 #ifdef DEBUG_COPPERLIST
135                         printf("switching to 2nd copperlist=%06x\n", copperlist);
136 #endif
137                         continue;
138                 }
139                 if (!(c1 & 1)) {
140                         /* MOVE */
141 #ifdef DEBUG_COPPERLIST
142                         printf("MOVE 0xdff%03x = 0x%04x\n", c1, c2);
143 #endif
144                         /* get bitplane pointers */
145                         if (c1 >= BPL1PTH && c1 <= BPL1PTH + 32) {
146                                 if ((c1 & 2))
147                                         bitplane[(c1 - BPL1PTH) / 4] = (bitplane[(c1 - BPL1PTH) / 4] & 0xffff0000) | c2;
148                                 else
149                                         bitplane[(c1 - BPL1PTH) / 4] = (bitplane[(c1 - BPL1PTH) / 4] & 0x0000ffff) | (c2 << 16);
150                         }
151                         /* get color registers */
152                         if (c1 >= COLOR00 && c1 <= COLOR00 + 32) {
153                                 palette[(c1 - COLOR00) / 2] = c2;
154                         }
155                 } else {
156                         if ((c2 & 1)) {
157                                 fprintf(stderr, "We suppport no SKIP command in copper list, please fix!\n");
158                                 continue;
159                         }
160                         /* WAIT */
161                         /* get new raster position.
162                          * if the value is lower or equal, we add 256 lines
163                          * we ignore column, since it is not relevant for this game.
164                          */
165                         row = (c1 >> 8) & ((c2 >> 8) | 0x80);
166                         if (row < (last_row & 0xff))
167                                 row |= 0x100;
168 #ifdef DEBUG_COPPERLIST
169                         printf("WAIT row = 0x%02x & 0x%02x = %d\n", c1 >> 8, (c2 >> 8) | 0x80, row);
170 #endif
171                         last_row = row;
172                         /* line relative to display window start */
173                         line = row - diwstart;
174                         /* continue: before display window start */
175                         if (line <= last_line)
176                                 continue;
177                         /* render up to height */
178                         if (line >= height)
179                                 line = height;
180                         /* check if bitplane pointers are set */
181 #ifdef DEBUG_COPPERLIST
182                         printf("Bitplanes:");
183 #endif
184                         for (i = 0; i < 4; i++) {
185 #ifdef DEBUG_COPPERLIST
186                                 printf(" %06x", bitplane[i]);
187 #endif
188                                 if (bitplane[i] == 0 || bitplane[i] + width * height / 8 >= 0x80000) {
189                                         printf("\n");
190                                         fprintf(stderr, "Bitplane %d in copper list not set or out of range, please fix!\n", i);
191                                         return;
192                                 }
193                                 bitmem[i] = memory + bitplane[i];
194                         }
195 #ifdef DEBUG_COPPERLIST
196                         printf("\n");
197 #endif
198                         /* render portion before WAIT line */
199 #ifdef DEBUG_COPPERLIST
200                         printf("Render from line %d to line %d\n", last_line, line - 1);
201 #endif
202 #ifdef DEBUG_COPPERLIST
203                         printf("Palette:");
204                         for (i = 0; i < 16; i++) {
205                                 printf(" %03x", palette[i] & 0xfff);
206                         }
207                         printf("\n");
208 #endif
209
210                         from = last_line;
211                         to = line;
212                         if (start > from)
213                                 from = start;
214                         if (stop < to)
215                                 to = stop;
216                         if (to > from)
217                                 planar2chunky(rgb, bitmem, palette, 4, width, from, to);
218                         /* done if we rendered up to height */
219                         if (line == height)
220                                 break;
221                         /* remeber for next wait command */
222                         last_row = row;
223                         last_line = line;
224                 }
225         }
226
227 }