4abace6b7c0ef120a0f6836d322cbaba6375fbda
[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 int TileNormM4(int sx, int addr, int pal)
30 {
31   unsigned char *pd = Pico.est.HighCol + sx;
32   unsigned int pack, t;
33
34   pack = *(unsigned int *)(Pico.vram + addr); /* Get 4 bitplanes / 8 pixels */
35   if (pack)
36   {
37     PLANAR_PIXEL(0, 0)
38     PLANAR_PIXEL(1, 1)
39     PLANAR_PIXEL(2, 2)
40     PLANAR_PIXEL(3, 3)
41     PLANAR_PIXEL(4, 4)
42     PLANAR_PIXEL(5, 5)
43     PLANAR_PIXEL(6, 6)
44     PLANAR_PIXEL(7, 7)
45     return 0;
46   }
47
48   return 1; /* Tile blank */
49 }
50
51 static int TileFlipM4(int sx,int addr,int pal)
52 {
53   unsigned char *pd = Pico.est.HighCol + sx;
54   unsigned int pack, t;
55
56   pack = *(unsigned int *)(Pico.vram + addr); /* Get 4 bitplanes / 8 pixels */
57   if (pack)
58   {
59     PLANAR_PIXEL(0, 7)
60     PLANAR_PIXEL(1, 6)
61     PLANAR_PIXEL(2, 5)
62     PLANAR_PIXEL(3, 4)
63     PLANAR_PIXEL(4, 3)
64     PLANAR_PIXEL(5, 2)
65     PLANAR_PIXEL(6, 1)
66     PLANAR_PIXEL(7, 0)
67     return 0;
68   }
69
70   return 1; /* Tile blank */
71 }
72
73 static void draw_sprites(int scanline)
74 {
75   struct PicoVideo *pv = &Pico.video;
76   unsigned int sprites_addr[8];
77   unsigned int sprites_x[8];
78   unsigned char *sat;
79   int xoff = 8; // relative to HighCol, which is (screen - 8)
80   int sprite_base, addr_mask;
81   int i, s, h;
82
83   if (pv->reg[0] & 8)
84     xoff = 0;
85
86   sat = (unsigned char *)Pico.vram + ((pv->reg[5] & 0x7e) << 7);
87   if (pv->reg[1] & 2) {
88     addr_mask = 0xfe; h = 16;
89   } else {
90     addr_mask = 0xff; h = 8;
91   }
92   sprite_base = (pv->reg[6] & 4) << (13-2-1);
93
94   for (i = s = 0; i < 64 && s < 8; i++)
95   {
96     int y;
97     y = sat[i] + 1;
98     if (y == 0xd1)
99       break;
100     if (y + h <= scanline || scanline < y)
101       continue; // not on this line
102
103     sprites_x[s] = xoff + sat[0x80 + i*2];
104     sprites_addr[s] = sprite_base + ((sat[0x80 + i*2 + 1] & addr_mask) << (5-1)) +
105       ((scanline - y) << (2-1));
106     s++;
107   }
108
109   // now draw all sprites backwards
110   for (--s; s >= 0; s--)
111     TileNormM4(sprites_x[s], sprites_addr[s], 0x10);
112 }
113
114 // tilex_ty_prio merged to reduce register pressure
115 static void draw_strip(const unsigned short *nametab, int dx, int cells, int tilex_ty_prio)
116 {
117   int oldcode = -1, blank = -1; // The tile we know is blank
118   int addr = 0, pal = 0;
119
120   // Draw tiles across screen:
121   for (; cells > 0; dx += 8, tilex_ty_prio++, cells--)
122   {
123     int code, zero;
124
125     code = nametab[tilex_ty_prio & 0x1f];
126     if (code == blank)
127       continue;
128     if ((code ^ tilex_ty_prio) & 0x1000) // priority differs?
129       continue;
130
131     if (code != oldcode) {
132       oldcode = code;
133       // Get tile address/2:
134       addr = (code & 0x1ff) << 4;
135       addr += tilex_ty_prio >> 16;
136       if (code & 0x0400)
137         addr ^= 0xe; // Y-flip
138
139       pal = (code>>7) & 0x10;
140     }
141
142     if (code&0x0200) zero = TileFlipM4(dx, addr, pal);
143     else             zero = TileNormM4(dx, addr, pal);
144
145     if (zero)
146       blank = code; // We know this tile is blank now
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 = Pico.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 (PicoDrawMask & PDRAW_LAYERB_ON)
183     draw_strip(nametab, dx, cells, tilex | 0x0000 | (ty << 16));
184
185   // sprites
186   if (PicoDrawMask & PDRAW_SPRITES_LOW_ON)
187     draw_sprites(scanline);
188
189   // high priority tiles (use virtual layer switch just for fun)
190   if (PicoDrawMask & PDRAW_LAYERA_ON)
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 *)Pico.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   memcpy32((int *)pd, (int *)(Pico.est.HighCol+8), 256/4);
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