0bfe8d59 |
1 | /*************************************************************************** |
2 | * Copyright (C) 2010 PCSX4ALL Team * |
3 | * Copyright (C) 2010 Unai * |
4 | * Copyright (C) 2016 Senquack (dansilsby <AT> gmail <DOT> com) * |
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 2 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, write to the * |
18 | * Free Software Foundation, Inc., * |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA. * |
20 | ***************************************************************************/ |
21 | |
22 | #include <stddef.h> |
23 | #include "plugins.h" |
24 | #include "psxcommon.h" |
25 | //#include "port.h" |
26 | #include "gpu_senquack.h" |
27 | |
28 | #define VIDEO_WIDTH 320 |
29 | |
30 | #ifdef TIME_IN_MSEC |
31 | #define TPS 1000 |
32 | #else |
33 | #define TPS 1000000 |
34 | #endif |
35 | |
36 | #define IS_PAL (gpu_senquack.GPU_GP1&(0x08<<17)) |
37 | |
38 | //senquack - Original 512KB of guard space seems not to be enough, as Xenogears |
39 | // accesses outside this range and crashes in town intro fight sequence. |
40 | // Increased to 2MB total (double PSX VRAM) and Xenogears no longer |
41 | // crashes, but some textures are still messed up. Also note that alignment min |
42 | // is 16 bytes, needed for pixel-skipping rendering/blitting in high horiz res. |
43 | // Extra 4KB is for guard room at beginning. |
44 | // TODO: Determine cause of out-of-bounds write/reads. <-- Note: this is largely |
45 | // solved by adoption of PCSX Rearmed's 'gpulib' in gpulib_if.cpp, which |
46 | // replaces this file (gpu.cpp) |
47 | //u16 GPU_FrameBuffer[(FRAME_BUFFER_SIZE+512*1024)/2] __attribute__((aligned(32))); |
48 | static u16 GPU_FrameBuffer[(FRAME_BUFFER_SIZE*2 + 4096)/2] __attribute__((aligned(32))); |
49 | |
50 | /////////////////////////////////////////////////////////////////////////////// |
51 | // GPU fixed point math |
52 | #include "gpu_fixedpoint.h" |
53 | |
54 | /////////////////////////////////////////////////////////////////////////////// |
55 | // Inner loop driver instantiation file |
56 | #include "gpu_inner.h" |
57 | |
58 | /////////////////////////////////////////////////////////////////////////////// |
59 | // GPU internal image drawing functions |
60 | #include "gpu_raster_image.h" |
61 | |
62 | /////////////////////////////////////////////////////////////////////////////// |
63 | // GPU internal line drawing functions |
64 | #include "gpu_raster_line.h" |
65 | |
66 | /////////////////////////////////////////////////////////////////////////////// |
67 | // GPU internal polygon drawing functions |
68 | #include "gpu_raster_polygon.h" |
69 | |
70 | /////////////////////////////////////////////////////////////////////////////// |
71 | // GPU internal sprite drawing functions |
72 | #include "gpu_raster_sprite.h" |
73 | |
74 | /////////////////////////////////////////////////////////////////////////////// |
75 | // GPU command buffer execution/store |
76 | #include "gpu_command.h" |
77 | |
78 | /////////////////////////////////////////////////////////////////////////////// |
79 | static void gpuReset(void) |
80 | { |
81 | memset((void*)&gpu_senquack, 0, sizeof(gpu_senquack)); |
82 | gpu_senquack.vram = (u16*)GPU_FrameBuffer + (4096/2); //4kb guard room in front |
83 | gpu_senquack.GPU_GP1 = 0x14802000; |
84 | gpu_senquack.DrawingArea[2] = 256; |
85 | gpu_senquack.DrawingArea[3] = 240; |
86 | gpu_senquack.DisplayArea[2] = 256; |
87 | gpu_senquack.DisplayArea[3] = 240; |
88 | gpu_senquack.DisplayArea[5] = 240; |
89 | gpu_senquack.TextureWindow[0] = 0; |
90 | gpu_senquack.TextureWindow[1] = 0; |
91 | gpu_senquack.TextureWindow[2] = 255; |
92 | gpu_senquack.TextureWindow[3] = 255; |
93 | //senquack - new vars must be updated whenever texture window is changed: |
94 | // (used for polygon-drawing in gpu_inner.h, gpu_raster_polygon.h) |
95 | const u32 fb = FIXED_BITS; // # of fractional fixed-pt bits of u4/v4 |
96 | gpu_senquack.u_msk = (((u32)gpu_senquack.TextureWindow[2]) << fb) | ((1 << fb) - 1); |
97 | gpu_senquack.v_msk = (((u32)gpu_senquack.TextureWindow[3]) << fb) | ((1 << fb) - 1); |
98 | |
99 | // Configuration options |
100 | gpu_senquack.config = gpu_senquack_config_ext; |
101 | gpu_senquack.ilace_mask = gpu_senquack.config.ilace_force; |
102 | gpu_senquack.frameskip.skipCount = gpu_senquack.config.frameskip_count; |
103 | |
104 | SetupLightLUT(); |
105 | SetupDitheringConstants(); |
106 | } |
107 | |
108 | /////////////////////////////////////////////////////////////////////////////// |
109 | long GPU_init(void) |
110 | { |
111 | gpuReset(); |
112 | |
113 | #ifdef GPU_UNAI_USE_INT_DIV_MULTINV |
114 | // s_invTable |
115 | for(unsigned int i=1;i<=(1<<TABLE_BITS);++i) |
116 | { |
117 | s_invTable[i-1]=0x7fffffff/i; |
118 | } |
119 | #endif |
120 | |
121 | gpu_senquack.fb_dirty = true; |
122 | gpu_senquack.dma.last_dma = NULL; |
123 | return (0); |
124 | } |
125 | |
126 | /////////////////////////////////////////////////////////////////////////////// |
127 | long GPU_shutdown(void) |
128 | { |
129 | return 0; |
130 | } |
131 | |
132 | /////////////////////////////////////////////////////////////////////////////// |
133 | long GPU_freeze(u32 bWrite, GPUFreeze_t* p2) |
134 | { |
135 | if (!p2) return (0); |
136 | if (p2->ulFreezeVersion != 1) return (0); |
137 | |
138 | if (bWrite) |
139 | { |
140 | p2->ulStatus = gpu_senquack.GPU_GP1; |
141 | memset(p2->ulControl, 0, sizeof(p2->ulControl)); |
142 | // save resolution and registers for P.E.Op.S. compatibility |
143 | p2->ulControl[3] = (3 << 24) | ((gpu_senquack.GPU_GP1 >> 23) & 1); |
144 | p2->ulControl[4] = (4 << 24) | ((gpu_senquack.GPU_GP1 >> 29) & 3); |
145 | p2->ulControl[5] = (5 << 24) | (gpu_senquack.DisplayArea[0] | (gpu_senquack.DisplayArea[1] << 10)); |
146 | p2->ulControl[6] = (6 << 24) | (2560 << 12); |
147 | p2->ulControl[7] = (7 << 24) | (gpu_senquack.DisplayArea[4] | (gpu_senquack.DisplayArea[5] << 10)); |
148 | p2->ulControl[8] = (8 << 24) | ((gpu_senquack.GPU_GP1 >> 17) & 0x3f) | ((gpu_senquack.GPU_GP1 >> 10) & 0x40); |
149 | memcpy((void*)p2->psxVRam, (void*)gpu_senquack.vram, FRAME_BUFFER_SIZE); |
150 | return (1); |
151 | } |
152 | else |
153 | { |
154 | extern void GPU_writeStatus(u32 data); |
155 | gpu_senquack.GPU_GP1 = p2->ulStatus; |
156 | memcpy((void*)gpu_senquack.vram, (void*)p2->psxVRam, FRAME_BUFFER_SIZE); |
157 | GPU_writeStatus((5 << 24) | p2->ulControl[5]); |
158 | GPU_writeStatus((7 << 24) | p2->ulControl[7]); |
159 | GPU_writeStatus((8 << 24) | p2->ulControl[8]); |
160 | gpuSetTexture(gpu_senquack.GPU_GP1); |
161 | return (1); |
162 | } |
163 | return (0); |
164 | } |
165 | |
166 | /////////////////////////////////////////////////////////////////////////////// |
167 | // GPU DMA comunication |
168 | |
169 | /////////////////////////////////////////////////////////////////////////////// |
170 | u8 PacketSize[256] = |
171 | { |
172 | 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15 |
173 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 |
174 | 3, 3, 3, 3, 6, 6, 6, 6, 4, 4, 4, 4, 8, 8, 8, 8, // 32-47 |
175 | 5, 5, 5, 5, 8, 8, 8, 8, 7, 7, 7, 7, 11, 11, 11, 11, // 48-63 |
176 | 2, 2, 2, 2, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, // 64-79 |
177 | 3, 3, 3, 3, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, // 80-95 |
178 | 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 2, 2, // 96-111 |
179 | 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, // 112-127 |
180 | 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128- |
181 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144 |
182 | 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160 |
183 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // |
184 | 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // |
185 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // |
186 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // |
187 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // |
188 | }; |
189 | |
190 | /////////////////////////////////////////////////////////////////////////////// |
191 | INLINE void gpuSendPacket() |
192 | { |
193 | gpuSendPacketFunction(gpu_senquack.PacketBuffer.U4[0]>>24); |
194 | } |
195 | |
196 | /////////////////////////////////////////////////////////////////////////////// |
197 | INLINE void gpuCheckPacket(u32 uData) |
198 | { |
199 | if (gpu_senquack.PacketCount) |
200 | { |
201 | gpu_senquack.PacketBuffer.U4[gpu_senquack.PacketIndex++] = uData; |
202 | --gpu_senquack.PacketCount; |
203 | } |
204 | else |
205 | { |
206 | gpu_senquack.PacketBuffer.U4[0] = uData; |
207 | gpu_senquack.PacketCount = PacketSize[uData >> 24]; |
208 | gpu_senquack.PacketIndex = 1; |
209 | } |
210 | if (!gpu_senquack.PacketCount) gpuSendPacket(); |
211 | } |
212 | |
213 | /////////////////////////////////////////////////////////////////////////////// |
214 | void GPU_writeDataMem(u32* dmaAddress, int dmaCount) |
215 | { |
216 | #ifdef ENABLE_GPU_LOG_SUPPORT |
217 | fprintf(stdout,"GPU_writeDataMem(%d)\n",dmaCount); |
218 | #endif |
219 | u32 data; |
220 | const u16 *VIDEO_END = (u16*)gpu_senquack.vram+(FRAME_BUFFER_SIZE/2)-1; |
221 | gpu_senquack.GPU_GP1 &= ~0x14000000; |
222 | |
223 | while (dmaCount) |
224 | { |
225 | if (gpu_senquack.dma.FrameToWrite) |
226 | { |
227 | while (dmaCount) |
228 | { |
229 | dmaCount--; |
230 | data = *dmaAddress++; |
231 | if ((&gpu_senquack.dma.pvram[gpu_senquack.dma.px])>(VIDEO_END)) gpu_senquack.dma.pvram-=512*1024; |
232 | gpu_senquack.dma.pvram[gpu_senquack.dma.px] = data; |
233 | if (++gpu_senquack.dma.px >= gpu_senquack.dma.x_end) |
234 | { |
235 | gpu_senquack.dma.px = 0; |
236 | gpu_senquack.dma.pvram += 1024; |
237 | if (++gpu_senquack.dma.py >= gpu_senquack.dma.y_end) |
238 | { |
239 | gpu_senquack.dma.FrameToWrite = false; |
240 | gpu_senquack.GPU_GP1 &= ~0x08000000; |
241 | gpu_senquack.fb_dirty = true; |
242 | break; |
243 | } |
244 | } |
245 | if ((&gpu_senquack.dma.pvram[gpu_senquack.dma.px])>(VIDEO_END)) gpu_senquack.dma.pvram-=512*1024; |
246 | gpu_senquack.dma.pvram[gpu_senquack.dma.px] = data>>16; |
247 | if (++gpu_senquack.dma.px >= gpu_senquack.dma.x_end) |
248 | { |
249 | gpu_senquack.dma.px = 0; |
250 | gpu_senquack.dma.pvram += 1024; |
251 | if (++gpu_senquack.dma.py >= gpu_senquack.dma.y_end) |
252 | { |
253 | gpu_senquack.dma.FrameToWrite = false; |
254 | gpu_senquack.GPU_GP1 &= ~0x08000000; |
255 | gpu_senquack.fb_dirty = true; |
256 | break; |
257 | } |
258 | } |
259 | } |
260 | } |
261 | else |
262 | { |
263 | data = *dmaAddress++; |
264 | dmaCount--; |
265 | gpuCheckPacket(data); |
266 | } |
267 | } |
268 | |
269 | gpu_senquack.GPU_GP1 = (gpu_senquack.GPU_GP1 | 0x14000000) & ~0x60000000; |
270 | } |
271 | |
272 | long GPU_dmaChain(u32 *rambase, u32 start_addr) |
273 | { |
274 | #ifdef ENABLE_GPU_LOG_SUPPORT |
275 | fprintf(stdout,"GPU_dmaChain(0x%x)\n",start_addr); |
276 | #endif |
277 | |
278 | u32 addr, *list; |
279 | u32 len, count; |
280 | long dma_words = 0; |
281 | |
282 | if (gpu_senquack.dma.last_dma) *gpu_senquack.dma.last_dma |= 0x800000; |
283 | |
284 | gpu_senquack.GPU_GP1 &= ~0x14000000; |
285 | |
286 | addr = start_addr & 0xffffff; |
287 | for (count = 0; addr != 0xffffff; count++) |
288 | { |
289 | list = rambase + (addr & 0x1fffff) / 4; |
290 | len = list[0] >> 24; |
291 | addr = list[0] & 0xffffff; |
292 | |
293 | dma_words += 1 + len; |
294 | |
295 | // add loop detection marker |
296 | list[0] |= 0x800000; |
297 | |
298 | if (len) GPU_writeDataMem(list + 1, len); |
299 | |
300 | if (addr & 0x800000) |
301 | { |
302 | #ifdef ENABLE_GPU_LOG_SUPPORT |
303 | fprintf(stdout,"GPU_dmaChain(LOOP)\n"); |
304 | #endif |
305 | break; |
306 | } |
307 | } |
308 | |
309 | // remove loop detection markers |
310 | addr = start_addr & 0x1fffff; |
311 | while (count-- > 0) |
312 | { |
313 | list = rambase + addr / 4; |
314 | addr = list[0] & 0x1fffff; |
315 | list[0] &= ~0x800000; |
316 | } |
317 | |
318 | if (gpu_senquack.dma.last_dma) *gpu_senquack.dma.last_dma &= ~0x800000; |
319 | gpu_senquack.dma.last_dma = rambase + (start_addr & 0x1fffff) / 4; |
320 | |
321 | gpu_senquack.GPU_GP1 = (gpu_senquack.GPU_GP1 | 0x14000000) & ~0x60000000; |
322 | |
323 | return dma_words; |
324 | } |
325 | |
326 | /////////////////////////////////////////////////////////////////////////////// |
327 | void GPU_writeData(u32 data) |
328 | { |
329 | const u16 *VIDEO_END = (u16*)gpu_senquack.vram+(FRAME_BUFFER_SIZE/2)-1; |
330 | #ifdef ENABLE_GPU_LOG_SUPPORT |
331 | fprintf(stdout,"GPU_writeData()\n"); |
332 | #endif |
333 | gpu_senquack.GPU_GP1 &= ~0x14000000; |
334 | |
335 | if (gpu_senquack.dma.FrameToWrite) |
336 | { |
337 | if ((&gpu_senquack.dma.pvram[gpu_senquack.dma.px])>(VIDEO_END)) gpu_senquack.dma.pvram-=512*1024; |
338 | gpu_senquack.dma.pvram[gpu_senquack.dma.px]=(u16)data; |
339 | if (++gpu_senquack.dma.px >= gpu_senquack.dma.x_end) |
340 | { |
341 | gpu_senquack.dma.px = 0; |
342 | gpu_senquack.dma.pvram += 1024; |
343 | if (++gpu_senquack.dma.py >= gpu_senquack.dma.y_end) |
344 | { |
345 | gpu_senquack.dma.FrameToWrite = false; |
346 | gpu_senquack.GPU_GP1 &= ~0x08000000; |
347 | gpu_senquack.fb_dirty = true; |
348 | } |
349 | } |
350 | if (gpu_senquack.dma.FrameToWrite) |
351 | { |
352 | if ((&gpu_senquack.dma.pvram[gpu_senquack.dma.px])>(VIDEO_END)) gpu_senquack.dma.pvram-=512*1024; |
353 | gpu_senquack.dma.pvram[gpu_senquack.dma.px]=data>>16; |
354 | if (++gpu_senquack.dma.px >= gpu_senquack.dma.x_end) |
355 | { |
356 | gpu_senquack.dma.px = 0; |
357 | gpu_senquack.dma.pvram += 1024; |
358 | if (++gpu_senquack.dma.py >= gpu_senquack.dma.y_end) |
359 | { |
360 | gpu_senquack.dma.FrameToWrite = false; |
361 | gpu_senquack.GPU_GP1 &= ~0x08000000; |
362 | gpu_senquack.fb_dirty = true; |
363 | } |
364 | } |
365 | } |
366 | } |
367 | else |
368 | { |
369 | gpuCheckPacket(data); |
370 | } |
371 | gpu_senquack.GPU_GP1 |= 0x14000000; |
372 | } |
373 | |
374 | |
375 | /////////////////////////////////////////////////////////////////////////////// |
376 | void GPU_readDataMem(u32* dmaAddress, int dmaCount) |
377 | { |
378 | const u16 *VIDEO_END = (u16*)gpu_senquack.vram+(FRAME_BUFFER_SIZE/2)-1; |
379 | #ifdef ENABLE_GPU_LOG_SUPPORT |
380 | fprintf(stdout,"GPU_readDataMem(%d)\n",dmaCount); |
381 | #endif |
382 | if(!gpu_senquack.dma.FrameToRead) return; |
383 | |
384 | gpu_senquack.GPU_GP1 &= ~0x14000000; |
385 | do |
386 | { |
387 | if ((&gpu_senquack.dma.pvram[gpu_senquack.dma.px])>(VIDEO_END)) gpu_senquack.dma.pvram-=512*1024; |
388 | // lower 16 bit |
389 | //senquack - 64-bit fix (from notaz) |
390 | //u32 data = (unsigned long)gpu_senquack.dma.pvram[gpu_senquack.dma.px]; |
391 | u32 data = (u32)gpu_senquack.dma.pvram[gpu_senquack.dma.px]; |
392 | |
393 | if (++gpu_senquack.dma.px >= gpu_senquack.dma.x_end) |
394 | { |
395 | gpu_senquack.dma.px = 0; |
396 | gpu_senquack.dma.pvram += 1024; |
397 | } |
398 | |
399 | if ((&gpu_senquack.dma.pvram[gpu_senquack.dma.px])>(VIDEO_END)) gpu_senquack.dma.pvram-=512*1024; |
400 | // higher 16 bit (always, even if it's an odd width) |
401 | //senquack - 64-bit fix (from notaz) |
402 | //data |= (unsigned long)(gpu_senquack.dma.pvram[gpu_senquack.dma.px])<<16; |
403 | data |= (u32)(gpu_senquack.dma.pvram[gpu_senquack.dma.px])<<16; |
404 | |
405 | *dmaAddress++ = data; |
406 | |
407 | if (++gpu_senquack.dma.px >= gpu_senquack.dma.x_end) |
408 | { |
409 | gpu_senquack.dma.px = 0; |
410 | gpu_senquack.dma.pvram += 1024; |
411 | if (++gpu_senquack.dma.py >= gpu_senquack.dma.y_end) |
412 | { |
413 | gpu_senquack.dma.FrameToRead = false; |
414 | gpu_senquack.GPU_GP1 &= ~0x08000000; |
415 | break; |
416 | } |
417 | } |
418 | } while (--dmaCount); |
419 | |
420 | gpu_senquack.GPU_GP1 = (gpu_senquack.GPU_GP1 | 0x14000000) & ~0x60000000; |
421 | } |
422 | |
423 | |
424 | |
425 | /////////////////////////////////////////////////////////////////////////////// |
426 | u32 GPU_readData(void) |
427 | { |
428 | const u16 *VIDEO_END = (u16*)gpu_senquack.vram+(FRAME_BUFFER_SIZE/2)-1; |
429 | #ifdef ENABLE_GPU_LOG_SUPPORT |
430 | fprintf(stdout,"GPU_readData()\n"); |
431 | #endif |
432 | gpu_senquack.GPU_GP1 &= ~0x14000000; |
433 | if (gpu_senquack.dma.FrameToRead) |
434 | { |
435 | if ((&gpu_senquack.dma.pvram[gpu_senquack.dma.px])>(VIDEO_END)) gpu_senquack.dma.pvram-=512*1024; |
436 | gpu_senquack.GPU_GP0 = gpu_senquack.dma.pvram[gpu_senquack.dma.px]; |
437 | if (++gpu_senquack.dma.px >= gpu_senquack.dma.x_end) |
438 | { |
439 | gpu_senquack.dma.px = 0; |
440 | gpu_senquack.dma.pvram += 1024; |
441 | if (++gpu_senquack.dma.py >= gpu_senquack.dma.y_end) |
442 | { |
443 | gpu_senquack.dma.FrameToRead = false; |
444 | gpu_senquack.GPU_GP1 &= ~0x08000000; |
445 | } |
446 | } |
447 | if ((&gpu_senquack.dma.pvram[gpu_senquack.dma.px])>(VIDEO_END)) gpu_senquack.dma.pvram-=512*1024; |
448 | gpu_senquack.GPU_GP0 |= gpu_senquack.dma.pvram[gpu_senquack.dma.px]<<16; |
449 | if (++gpu_senquack.dma.px >= gpu_senquack.dma.x_end) |
450 | { |
451 | gpu_senquack.dma.px = 0; |
452 | gpu_senquack.dma.pvram += 1024; |
453 | if (++gpu_senquack.dma.py >= gpu_senquack.dma.y_end) |
454 | { |
455 | gpu_senquack.dma.FrameToRead = false; |
456 | gpu_senquack.GPU_GP1 &= ~0x08000000; |
457 | } |
458 | } |
459 | |
460 | } |
461 | gpu_senquack.GPU_GP1 |= 0x14000000; |
462 | |
463 | return (gpu_senquack.GPU_GP0); |
464 | } |
465 | |
466 | /////////////////////////////////////////////////////////////////////////////// |
467 | u32 GPU_readStatus(void) |
468 | { |
469 | return gpu_senquack.GPU_GP1; |
470 | } |
471 | |
472 | INLINE void GPU_NoSkip(void) |
473 | { |
474 | #ifdef ENABLE_GPU_LOG_SUPPORT |
475 | fprintf(stdout,"GPU_NoSkip()\n"); |
476 | #endif |
477 | gpu_senquack.frameskip.wasSkip = gpu_senquack.frameskip.isSkip; |
478 | if (gpu_senquack.frameskip.isSkip) |
479 | { |
480 | gpu_senquack.frameskip.isSkip = false; |
481 | gpu_senquack.frameskip.skipGPU = false; |
482 | } |
483 | else |
484 | { |
485 | gpu_senquack.frameskip.isSkip = gpu_senquack.frameskip.skipFrame; |
486 | gpu_senquack.frameskip.skipGPU = gpu_senquack.frameskip.skipFrame; |
487 | } |
488 | } |
489 | |
490 | /////////////////////////////////////////////////////////////////////////////// |
491 | void GPU_writeStatus(u32 data) |
492 | { |
493 | #ifdef ENABLE_GPU_LOG_SUPPORT |
494 | fprintf(stdout,"GPU_writeStatus(%d,%d)\n",data>>24,data & 0xff); |
495 | #endif |
496 | switch (data >> 24) { |
497 | case 0x00: |
498 | gpuReset(); |
499 | break; |
500 | case 0x01: |
501 | gpu_senquack.GPU_GP1 &= ~0x08000000; |
502 | gpu_senquack.PacketCount = 0; |
503 | gpu_senquack.dma.FrameToRead = gpu_senquack.dma.FrameToWrite = false; |
504 | break; |
505 | case 0x02: |
506 | gpu_senquack.GPU_GP1 &= ~0x08000000; |
507 | gpu_senquack.PacketCount = 0; |
508 | gpu_senquack.dma.FrameToRead = gpu_senquack.dma.FrameToWrite = false; |
509 | break; |
510 | case 0x03: |
511 | gpu_senquack.GPU_GP1 = (gpu_senquack.GPU_GP1 & ~0x00800000) | ((data & 1) << 23); |
512 | break; |
513 | case 0x04: |
514 | if (data == 0x04000000) gpu_senquack.PacketCount = 0; |
515 | gpu_senquack.GPU_GP1 = (gpu_senquack.GPU_GP1 & ~0x60000000) | ((data & 3) << 29); |
516 | break; |
517 | case 0x05: |
518 | // Start of Display Area in VRAM |
519 | gpu_senquack.DisplayArea[0] = data & 0x3ff; // X (0..1023) |
520 | gpu_senquack.DisplayArea[1] = (data >> 10) & 0x1ff; // Y (0..511) |
521 | GPU_NoSkip(); |
522 | break; |
523 | case 0x06: |
524 | // GP1(06h) - Horizontal Display range (on Screen) |
525 | // 0-11 X1 (260h+0) ;12bit ;\counted in 53.222400MHz units, |
526 | // 12-23 X2 (260h+320*8) ;12bit ;/relative to HSYNC |
527 | |
528 | // senquack - gpu_senquack completely ignores GP1(0x06) command and |
529 | // lacks even a place in DisplayArea[] array to store the values. |
530 | // It seems to have been concerned only with vertical display range |
531 | // and centering top/bottom. I will not add support here, and |
532 | // focus instead on the gpulib version (gpulib_if.cpp) which uses |
533 | // gpulib for its PS1->host framebuffer blitting. |
534 | break; |
535 | case 0x07: |
536 | // GP1(07h) - Vertical Display range (on Screen) |
537 | // 0-9 Y1 (NTSC=88h-(224/2), (PAL=A3h-(264/2)) ;\scanline numbers on screen, |
538 | // 10-19 Y2 (NTSC=88h+(224/2), (PAL=A3h+(264/2)) ;/relative to VSYNC |
539 | // 20-23 Not used (zero) |
540 | { |
541 | u32 v1=data & 0x000003FF; //(short)(data & 0x3ff); |
542 | u32 v2=(data & 0x000FFC00) >> 10; //(short)((data>>10) & 0x3ff); |
543 | if ((gpu_senquack.DisplayArea[4]!=v1)||(gpu_senquack.DisplayArea[5]!=v2)) |
544 | { |
545 | gpu_senquack.DisplayArea[4] = v1; |
546 | gpu_senquack.DisplayArea[5] = v2; |
547 | #ifdef ENABLE_GPU_LOG_SUPPORT |
548 | fprintf(stdout,"video_clear(CHANGE_Y)\n"); |
549 | #endif |
550 | video_clear(); |
551 | } |
552 | } |
553 | break; |
554 | case 0x08: |
555 | { |
556 | static const u32 HorizontalResolution[8] = { 256, 368, 320, 384, 512, 512, 640, 640 }; |
557 | static const u32 VerticalResolution[4] = { 240, 480, 256, 480 }; |
558 | gpu_senquack.GPU_GP1 = (gpu_senquack.GPU_GP1 & ~0x007F0000) | ((data & 0x3F) << 17) | ((data & 0x40) << 10); |
559 | #ifdef ENABLE_GPU_LOG_SUPPORT |
560 | fprintf(stdout,"GPU_writeStatus(RES=%dx%d,BITS=%d,PAL=%d)\n",HorizontalResolution[(gpu_senquack.GPU_GP1 >> 16) & 7], |
561 | VerticalResolution[(gpu_senquack.GPU_GP1 >> 19) & 3],(gpu_senquack.GPU_GP1&0x00200000?24:15),(IS_PAL?1:0)); |
562 | #endif |
563 | // Video mode change |
564 | u32 new_width = HorizontalResolution[(gpu_senquack.GPU_GP1 >> 16) & 7]; |
565 | u32 new_height = VerticalResolution[(gpu_senquack.GPU_GP1 >> 19) & 3]; |
566 | |
567 | if (gpu_senquack.DisplayArea[2] != new_width || gpu_senquack.DisplayArea[3] != new_height) |
568 | { |
569 | // Update width |
570 | gpu_senquack.DisplayArea[2] = new_width; |
571 | |
572 | if (PixelSkipEnabled()) { |
573 | // Set blit_mask for high horizontal resolutions. This allows skipping |
574 | // rendering pixels that would never get displayed on low-resolution |
575 | // platforms that use simple pixel-dropping scaler. |
576 | switch (gpu_senquack.DisplayArea[2]) |
577 | { |
578 | case 512: gpu_senquack.blit_mask = 0xa4; break; // GPU_BlitWWSWWSWS |
579 | case 640: gpu_senquack.blit_mask = 0xaa; break; // GPU_BlitWS |
580 | default: gpu_senquack.blit_mask = 0; break; |
581 | } |
582 | } else { |
583 | gpu_senquack.blit_mask = 0; |
584 | } |
585 | |
586 | // Update height |
587 | gpu_senquack.DisplayArea[3] = new_height; |
588 | |
589 | if (LineSkipEnabled()) { |
590 | // Set rendering line-skip (only render every other line in high-res |
591 | // 480 vertical mode, or, optionally, force it for all video modes) |
592 | |
593 | if (gpu_senquack.DisplayArea[3] == 480) { |
594 | if (gpu_senquack.config.ilace_force) { |
595 | gpu_senquack.ilace_mask = 3; // Only need 1/4 of lines |
596 | } else { |
597 | gpu_senquack.ilace_mask = 1; // Only need 1/2 of lines |
598 | } |
599 | } else { |
600 | // Vert resolution changed from 480 to lower one |
601 | gpu_senquack.ilace_mask = gpu_senquack.config.ilace_force; |
602 | } |
603 | } else { |
604 | gpu_senquack.ilace_mask = 0; |
605 | } |
606 | |
607 | #ifdef ENABLE_GPU_LOG_SUPPORT |
608 | fprintf(stdout,"video_clear(CHANGE_RES)\n"); |
609 | #endif |
610 | video_clear(); |
611 | } |
612 | |
613 | } |
614 | break; |
615 | case 0x10: |
616 | switch (data & 0xff) { |
617 | case 2: gpu_senquack.GPU_GP0 = gpu_senquack.tex_window; break; |
618 | case 3: gpu_senquack.GPU_GP0 = (gpu_senquack.DrawingArea[1] << 10) | gpu_senquack.DrawingArea[0]; break; |
619 | case 4: gpu_senquack.GPU_GP0 = ((gpu_senquack.DrawingArea[3]-1) << 10) | (gpu_senquack.DrawingArea[2]-1); break; |
620 | case 5: case 6: gpu_senquack.GPU_GP0 = (((u32)gpu_senquack.DrawingOffset[1] & 0x7ff) << 11) | ((u32)gpu_senquack.DrawingOffset[0] & 0x7ff); break; |
621 | case 7: gpu_senquack.GPU_GP0 = 2; break; |
622 | case 8: case 15: gpu_senquack.GPU_GP0 = 0xBFC03720; break; |
623 | } |
624 | break; |
625 | } |
626 | } |
627 | |
628 | // Blitting functions |
629 | #include "gpu_blit.h" |
630 | |
631 | static void gpuVideoOutput(void) |
632 | { |
633 | int h0, x0, y0, w0, h1; |
634 | |
635 | x0 = gpu_senquack.DisplayArea[0]; |
636 | y0 = gpu_senquack.DisplayArea[1]; |
637 | |
638 | w0 = gpu_senquack.DisplayArea[2]; |
639 | h0 = gpu_senquack.DisplayArea[3]; // video mode |
640 | |
641 | h1 = gpu_senquack.DisplayArea[5] - gpu_senquack.DisplayArea[4]; // display needed |
642 | if (h0 == 480) h1 = Min2(h1*2,480); |
643 | |
644 | bool isRGB24 = (gpu_senquack.GPU_GP1 & 0x00200000 ? true : false); |
645 | u16* dst16 = SCREEN; |
646 | u16* src16 = (u16*)gpu_senquack.vram; |
647 | |
648 | // PS1 fb read wraps around (fixes black screen in 'Tobal no. 1') |
649 | unsigned int src16_offs_msk = 1024*512-1; |
650 | unsigned int src16_offs = (x0 + y0*1024) & src16_offs_msk; |
651 | |
652 | // Height centering |
653 | int sizeShift = 1; |
654 | if (h0 == 256) { |
655 | h0 = 240; |
656 | } else if (h0 == 480) { |
657 | sizeShift = 2; |
658 | } |
659 | if (h1 > h0) { |
660 | src16_offs = (src16_offs + (((h1-h0) / 2) * 1024)) & src16_offs_msk; |
661 | h1 = h0; |
662 | } else if (h1<h0) { |
663 | dst16 += ((h0-h1) >> sizeShift) * VIDEO_WIDTH; |
664 | } |
665 | |
666 | |
667 | /* Main blitter */ |
668 | int incY = (h0==480) ? 2 : 1; |
669 | h0=(h0==480 ? 2048 : 1024); |
670 | |
671 | { |
672 | const int li=gpu_senquack.ilace_mask; |
673 | bool pi = ProgressiveInterlaceEnabled(); |
674 | bool pif = gpu_senquack.prog_ilace_flag; |
675 | switch ( w0 ) |
676 | { |
677 | case 256: |
678 | for(int y1=y0+h1; y0<y1; y0+=incY) |
679 | { |
680 | if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) |
681 | GPU_BlitWWDWW(src16 + src16_offs, dst16, isRGB24); |
682 | dst16 += VIDEO_WIDTH; |
683 | src16_offs = (src16_offs + h0) & src16_offs_msk; |
684 | } |
685 | break; |
686 | case 368: |
687 | for(int y1=y0+h1; y0<y1; y0+=incY) |
688 | { |
689 | if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) |
690 | GPU_BlitWWWWWWWWS(src16 + src16_offs, dst16, isRGB24, 4); |
691 | dst16 += VIDEO_WIDTH; |
692 | src16_offs = (src16_offs + h0) & src16_offs_msk; |
693 | } |
694 | break; |
695 | case 320: |
696 | // Ensure 32-bit alignment for GPU_BlitWW() blitter: |
697 | src16_offs &= ~1; |
698 | for(int y1=y0+h1; y0<y1; y0+=incY) |
699 | { |
700 | if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) |
701 | GPU_BlitWW(src16 + src16_offs, dst16, isRGB24); |
702 | dst16 += VIDEO_WIDTH; |
703 | src16_offs = (src16_offs + h0) & src16_offs_msk; |
704 | } |
705 | break; |
706 | case 384: |
707 | for(int y1=y0+h1; y0<y1; y0+=incY) |
708 | { |
709 | if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) |
710 | GPU_BlitWWWWWS(src16 + src16_offs, dst16, isRGB24); |
711 | dst16 += VIDEO_WIDTH; |
712 | src16_offs = (src16_offs + h0) & src16_offs_msk; |
713 | } |
714 | break; |
715 | case 512: |
716 | for(int y1=y0+h1; y0<y1; y0+=incY) |
717 | { |
718 | if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) |
719 | GPU_BlitWWSWWSWS(src16 + src16_offs, dst16, isRGB24); |
720 | dst16 += VIDEO_WIDTH; |
721 | src16_offs = (src16_offs + h0) & src16_offs_msk; |
722 | } |
723 | break; |
724 | case 640: |
725 | for(int y1=y0+h1; y0<y1; y0+=incY) |
726 | { |
727 | if (( 0 == (y0&li) ) && ((!pi) || (pif=!pif))) |
728 | GPU_BlitWS(src16 + src16_offs, dst16, isRGB24); |
729 | dst16 += VIDEO_WIDTH; |
730 | src16_offs = (src16_offs + h0) & src16_offs_msk; |
731 | } |
732 | break; |
733 | } |
734 | gpu_senquack.prog_ilace_flag = !gpu_senquack.prog_ilace_flag; |
735 | } |
736 | video_flip(); |
737 | } |
738 | |
739 | // Update frames-skip each second>>3 (8 times per second) |
740 | #define GPU_FRAMESKIP_UPDATE 3 |
741 | |
742 | static void GPU_frameskip (bool show) |
743 | { |
744 | u32 now=get_ticks(); // current frame |
745 | |
746 | // Update frameskip |
747 | if (gpu_senquack.frameskip.skipCount==0) gpu_senquack.frameskip.skipFrame=false; // frameskip off |
748 | else if (gpu_senquack.frameskip.skipCount==7) { if (show) gpu_senquack.frameskip.skipFrame=!gpu_senquack.frameskip.skipFrame; } // frameskip medium |
749 | else if (gpu_senquack.frameskip.skipCount==8) gpu_senquack.frameskip.skipFrame=true; // frameskip maximum |
750 | else |
751 | { |
752 | static u32 spd=100; // speed % |
753 | static u32 frames=0; // frames counter |
754 | static u32 prev=now; // previous fps calculation |
755 | frames++; |
756 | if ((now-prev)>=(TPS>>GPU_FRAMESKIP_UPDATE)) |
757 | { |
758 | if (IS_PAL) spd=(frames<<1); |
759 | else spd=((frames*1001)/600); |
760 | spd<<=GPU_FRAMESKIP_UPDATE; |
761 | frames=0; |
762 | prev=now; |
763 | } |
764 | switch(gpu_senquack.frameskip.skipCount) |
765 | { |
766 | case 1: if (spd<50) gpu_senquack.frameskip.skipFrame=true; else gpu_senquack.frameskip.skipFrame=false; break; // frameskip on (spd<50%) |
767 | case 2: if (spd<60) gpu_senquack.frameskip.skipFrame=true; else gpu_senquack.frameskip.skipFrame=false; break; // frameskip on (spd<60%) |
768 | case 3: if (spd<70) gpu_senquack.frameskip.skipFrame=true; else gpu_senquack.frameskip.skipFrame=false; break; // frameskip on (spd<70%) |
769 | case 4: if (spd<80) gpu_senquack.frameskip.skipFrame=true; else gpu_senquack.frameskip.skipFrame=false; break; // frameskip on (spd<80%) |
770 | case 5: if (spd<90) gpu_senquack.frameskip.skipFrame=true; else gpu_senquack.frameskip.skipFrame=false; break; // frameskip on (spd<90%) |
771 | } |
772 | } |
773 | } |
774 | |
775 | /////////////////////////////////////////////////////////////////////////////// |
776 | void GPU_updateLace(void) |
777 | { |
778 | // Interlace bit toggle |
779 | gpu_senquack.GPU_GP1 ^= 0x80000000; |
780 | |
781 | // Update display? |
782 | if ((gpu_senquack.fb_dirty) && (!gpu_senquack.frameskip.wasSkip) && (!(gpu_senquack.GPU_GP1&0x00800000))) |
783 | { |
784 | // Display updated |
785 | gpuVideoOutput(); |
786 | GPU_frameskip(true); |
787 | #ifdef ENABLE_GPU_LOG_SUPPORT |
788 | fprintf(stdout,"GPU_updateLace(UPDATE)\n"); |
789 | #endif |
790 | } else { |
791 | GPU_frameskip(false); |
792 | #ifdef ENABLE_GPU_LOG_SUPPORT |
793 | fprintf(stdout,"GPU_updateLace(SKIP)\n"); |
794 | #endif |
795 | } |
796 | |
797 | if ((!gpu_senquack.frameskip.skipCount) && (gpu_senquack.DisplayArea[3] == 480)) gpu_senquack.frameskip.skipGPU=true; // Tekken 3 hack |
798 | |
799 | gpu_senquack.fb_dirty=false; |
800 | gpu_senquack.dma.last_dma = NULL; |
801 | } |
802 | |
803 | // Allows frontend to signal plugin to redraw screen after returning to emu |
804 | void GPU_requestScreenRedraw() |
805 | { |
806 | gpu_senquack.fb_dirty = true; |
807 | } |
808 | |
809 | void GPU_getScreenInfo(GPUScreenInfo_t *sinfo) |
810 | { |
811 | bool depth24 = (gpu_senquack.GPU_GP1 & 0x00200000 ? true : false); |
812 | int16_t hres = (uint16_t)gpu_senquack.DisplayArea[2]; |
813 | int16_t vres = (uint16_t)gpu_senquack.DisplayArea[3]; |
814 | int16_t w = hres; // Original gpu_senquack doesn't support width < 100% |
815 | int16_t h = gpu_senquack.DisplayArea[5] - gpu_senquack.DisplayArea[4]; |
816 | if (vres == 480) |
817 | h *= 2; |
818 | if (h <= 0 || h > vres) |
819 | h = vres; |
820 | |
821 | sinfo->vram = (uint8_t*)gpu_senquack.vram; |
822 | sinfo->x = (uint16_t)gpu_senquack.DisplayArea[0]; |
823 | sinfo->y = (uint16_t)gpu_senquack.DisplayArea[1]; |
824 | sinfo->w = w; |
825 | sinfo->h = h; |
826 | sinfo->hres = hres; |
827 | sinfo->vres = vres; |
828 | sinfo->depth24 = depth24; |
829 | sinfo->pal = IS_PAL; |
830 | } |