37f752bbeba36f6290239fcc14f7ed91ef46b5cd
[picodrive.git] / pico / mode4.c
1 /*
2  * mode4/SMS renderer
3  * (C) notaz, 2009-2010
4  *
5  * This work is licensed under the terms of MAME license.
6  * See COPYING file in the top-level directory.
7  */
8 /*
9  * TODO:
10  * - TMS9918 modes?
11  * - gg mode?
12  * - column scroll (reg 0 bit7)
13  * - 224/240 line modes
14  * - doubled sprites
15  */
16 #include "pico_int.h"
17
18 static void (*FinalizeLineM4)(int line);
19 static int skip_next_line;
20 static int screen_offset;
21
22 #define PLANAR_PIXEL(x,p) \
23   t = pack & (0x80808080 >> p); \
24   if (t) { \
25     t = ((t >> (7-p)) | (t >> (14-p)) | (t >> (21-p)) | (t >> (28-p))) & 0x0f; \
26     pd[x] = pal|t; \
27   }
28
29 static void TileNormM4(int sx, unsigned int pack, int pal)
30 {
31   unsigned char *pd = Pico.est.HighCol + sx;
32   unsigned int t;
33
34   PLANAR_PIXEL(0, 0)
35   PLANAR_PIXEL(1, 1)
36   PLANAR_PIXEL(2, 2)
37   PLANAR_PIXEL(3, 3)
38   PLANAR_PIXEL(4, 4)
39   PLANAR_PIXEL(5, 5)
40   PLANAR_PIXEL(6, 6)
41   PLANAR_PIXEL(7, 7)
42 }
43
44 static void TileFlipM4(int sx, unsigned int pack, int pal)
45 {
46   unsigned char *pd = Pico.est.HighCol + sx;
47   unsigned int t;
48
49   PLANAR_PIXEL(0, 7)
50   PLANAR_PIXEL(1, 6)
51   PLANAR_PIXEL(2, 5)
52   PLANAR_PIXEL(3, 4)
53   PLANAR_PIXEL(4, 3)
54   PLANAR_PIXEL(5, 2)
55   PLANAR_PIXEL(6, 1)
56   PLANAR_PIXEL(7, 0)
57 }
58
59 static void draw_sprites(int scanline)
60 {
61   struct PicoVideo *pv = &Pico.video;
62   unsigned int sprites_addr[8];
63   unsigned int sprites_x[8];
64   unsigned int pack;
65   unsigned char *sat;
66   int xoff = 8; // relative to HighCol, which is (screen - 8)
67   int sprite_base, addr_mask;
68   int i, s, h;
69
70   if (pv->reg[0] & 8)
71     xoff = 0;
72
73   sat = (unsigned char *)PicoMem.vram + ((pv->reg[5] & 0x7e) << 7);
74   if (pv->reg[1] & 2) {
75     addr_mask = 0xfe; h = 16;
76   } else {
77     addr_mask = 0xff; h = 8;
78   }
79   sprite_base = (pv->reg[6] & 4) << (13-2-1);
80
81   for (i = s = 0; i < 64; i++)
82   {
83     int y;
84     y = sat[i] + 1;
85     if (y == 0xd1)
86       break;
87     if (y + h <= scanline || scanline < y)
88       continue; // not on this line
89     if (s >= 8) {
90       pv->status |= SR_SOVR;
91       break;
92     }
93
94     sprites_x[s] = xoff + sat[0x80 + i*2];
95     sprites_addr[s] = sprite_base + ((sat[0x80 + i*2 + 1] & addr_mask) << (5-1)) +
96       ((scanline - y) << (2-1));
97     s++;
98   }
99
100   // really half-assed but better than nothing
101   if (s > 1)
102     pv->status |= SR_C;
103
104   // now draw all sprites backwards
105   for (--s; s >= 0; s--) {
106     pack = *(unsigned int *)(PicoMem.vram + sprites_addr[s]);
107     TileNormM4(sprites_x[s], pack, 0x10);
108   }
109 }
110
111 // tilex_ty_prio merged to reduce register pressure
112 static void draw_strip(const unsigned short *nametab, int dx, int cells, int tilex_ty_prio)
113 {
114   int oldcode = -1, blank = -1; // The tile we know is blank
115   int addr = 0, pal = 0;
116
117   // Draw tiles across screen:
118   for (; cells > 0; dx += 8, tilex_ty_prio++, cells--)
119   {
120     unsigned int pack;
121     int code;
122
123     code = nametab[tilex_ty_prio & 0x1f];
124     if (code == blank)
125       continue;
126     if ((code ^ tilex_ty_prio) & 0x1000) // priority differs?
127       continue;
128
129     if (code != oldcode) {
130       oldcode = code;
131       // Get tile address/2:
132       addr = (code & 0x1ff) << 4;
133       addr += tilex_ty_prio >> 16;
134       if (code & 0x0400)
135         addr ^= 0xe; // Y-flip
136
137       pal = (code>>7) & 0x10;
138     }
139
140     pack = *(unsigned int *)(PicoMem.vram + addr); /* Get 4 bitplanes / 8 pixels */
141     if (pack == 0) {
142       blank = code;
143       continue;
144     }
145     if (code & 0x0200) TileFlipM4(dx, pack, pal);
146     else               TileNormM4(dx, pack, pal);
147   }
148 }
149
150 static void DrawDisplayM4(int scanline)
151 {
152   struct PicoVideo *pv = &Pico.video;
153   unsigned short *nametab;
154   int line, tilex, dx, ty, cells;
155   int cellskip = 0; // XXX
156   int maxcells = 32;
157
158   // Find the line in the name table
159   line = pv->reg[9] + scanline; // vscroll + scanline
160   if (line >= 224)
161     line -= 224;
162
163   // Find name table:
164   nametab = PicoMem.vram;
165   nametab += (pv->reg[2] & 0x0e) << (10-1);
166   nametab += (line>>3) << (6-1);
167
168   dx = pv->reg[8]; // hscroll
169   if (scanline < 16 && (pv->reg[0] & 0x40))
170     dx = 0; // hscroll disabled for top 2 rows
171
172   tilex = ((-dx >> 3) + cellskip) & 0x1f;
173   ty = (line & 7) << 1; // Y-Offset into tile
174   cells = maxcells - cellskip;
175
176   dx = ((dx - 1) & 7) + 1;
177   if (dx != 8)
178     cells++; // have hscroll, need to draw 1 cell more
179   dx += cellskip << 3;
180
181   // low priority tiles
182   if (!(pv->debug_p & PVD_KILL_B))
183     draw_strip(nametab, dx, cells, tilex | 0x0000 | (ty << 16));
184
185   // sprites
186   if (!(pv->debug_p & PVD_KILL_S_LO))
187     draw_sprites(scanline);
188
189   // high priority tiles (use virtual layer switch just for fun)
190   if (!(pv->debug_p & PVD_KILL_A))
191     draw_strip(nametab, dx, cells, tilex | 0x1000 | (ty << 16));
192
193   if (pv->reg[0] & 0x20)
194     // first column masked
195     ((int *)Pico.est.HighCol)[2] = ((int *)Pico.est.HighCol)[3] = 0xe0e0e0e0;
196 }
197
198 void PicoFrameStartMode4(void)
199 {
200   int lines = 192;
201   skip_next_line = 0;
202   screen_offset = 24;
203   Pico.est.rendstatus = PDRAW_32_COLS;
204
205   if ((Pico.video.reg[0] & 6) == 6 && (Pico.video.reg[1] & 0x18)) {
206     if (Pico.video.reg[1] & 0x08) {
207       screen_offset = 0;
208       lines = 240;
209     }
210     else {
211       screen_offset = 8;
212       lines = 224;
213     }
214   }
215
216   if (Pico.est.rendstatus != rendstatus_old || lines != rendlines) {
217     emu_video_mode_change(screen_offset, lines, 1);
218     rendstatus_old = Pico.est.rendstatus;
219     rendlines = lines;
220   }
221
222   Pico.est.DrawLineDest = (char *)DrawLineDestBase + screen_offset * DrawLineDestIncrement;
223 }
224
225 void PicoLineMode4(int line)
226 {
227   if (skip_next_line > 0) {
228     skip_next_line--;
229     return;
230   }
231
232   if (PicoScanBegin != NULL)
233     skip_next_line = PicoScanBegin(line + screen_offset);
234
235   // Draw screen:
236   BackFill(Pico.video.reg[7] & 0x0f, 0, &Pico.est);
237   if (Pico.video.reg[1] & 0x40)
238     DrawDisplayM4(line);
239
240   if (FinalizeLineM4 != NULL)
241     FinalizeLineM4(line);
242
243   if (PicoScanEnd != NULL)
244     skip_next_line = PicoScanEnd(line + screen_offset);
245
246   Pico.est.DrawLineDest = (char *)Pico.est.DrawLineDest + DrawLineDestIncrement;
247 }
248
249 void PicoDoHighPal555M4(void)
250 {
251   unsigned int *spal=(void *)PicoMem.cram;
252   unsigned int *dpal=(void *)Pico.est.HighPal;
253   unsigned int t;
254   int i;
255
256   Pico.m.dirtyPal = 0;
257
258   /* cram is always stored as shorts, even though real hardware probably uses bytes */
259   for (i = 0x20/2; i > 0; i--, spal++, dpal++) {
260     t = *spal;
261 #ifdef USE_BGR555
262     t = ((t & 0x00030003)<< 3) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)<<10);
263 #else
264     t = ((t & 0x00030003)<<14) | ((t & 0x000c000c)<<7) | ((t & 0x00300030)>>1);
265 #endif
266     t |= t >> 2;
267     t |= (t >> 4) & 0x08610861;
268     *dpal = t;
269   }
270   Pico.est.HighPal[0xe0] = 0;
271 }
272
273 static void FinalizeLineRGB555M4(int line)
274 {
275   if (Pico.m.dirtyPal)
276     PicoDoHighPal555M4();
277
278   // standard FinalizeLine can finish it for us,
279   // with features like scaling and such
280   FinalizeLine555(0, line, &Pico.est);
281 }
282
283 static void FinalizeLine8bitM4(int line)
284 {
285   unsigned char *pd = Pico.est.DrawLineDest;
286
287   if (!(PicoOpt & POPT_DIS_32C_BORDER))
288     pd += 32;
289
290   memcpy(pd, Pico.est.HighCol + 8, 256);
291 }
292
293 void PicoDrawSetOutputMode4(pdso_t which)
294 {
295   switch (which)
296   {
297     case PDF_8BIT:   FinalizeLineM4 = FinalizeLine8bitM4; break;
298     case PDF_RGB555: FinalizeLineM4 = FinalizeLineRGB555M4; break;
299     default:         FinalizeLineM4 = NULL; break;
300   }
301 }
302
303 // vim:shiftwidth=2:ts=2:expandtab