2823a4c8 |
1 | # gameplaySP |
2 | # |
3 | # Copyright (C) 2006 Exophase <exophase@gmail.com> |
4 | # |
5 | # This program is free software; you can redistribute it and/or |
6 | # modify it under the terms of the GNU General Public License as |
7 | # published by the Free Software Foundation; either version 2 of |
8 | # the License, or (at your option) any later version. |
9 | # |
10 | # This program is distributed in the hope that it will be useful, |
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | # General Public License for more details. |
14 | # |
15 | # You should have received a copy of the GNU General Public License |
16 | # along with this program; if not, write to the Free Software |
17 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
18 | |
19 | .align 4 |
20 | |
eac69717 |
21 | #ifndef _WIN32 |
22 | #define _x86_update_gba x86_update_gba |
23 | #define _x86_indirect_branch_arm x86_indirect_branch_arm |
24 | #define _x86_indirect_branch_thumb x86_indirect_branch_thumb |
25 | #define _x86_indirect_branch_dual x86_indirect_branch_dual |
26 | #define _execute_store_u8 execute_store_u8 |
27 | #define _execute_store_u16 execute_store_u16 |
28 | #define _execute_store_u32 execute_store_u32 |
29 | #define _execute_store_cpsr execute_store_cpsr |
30 | #define _execute_arm_translate execute_arm_translate |
31 | #define _step_debug_x86 step_debug_x86 |
32 | #define _memory_map_read memory_map_read |
33 | #define _memory_map_write memory_map_write |
34 | #define _reg reg |
35 | #define _oam_update oam_update |
36 | #define _iwram iwram |
37 | #define _ewram ewram |
38 | #define _vram vram |
39 | #define _oam_ram oam_ram |
40 | #define _bios_rom bios_rom |
41 | #define _io_registers io_registers |
42 | #define _spsr spsr |
43 | |
44 | #define _step_debug step_debug |
45 | #define _update_gba update_gba |
46 | #define _block_lookup_address_arm block_lookup_address_arm |
47 | #define _block_lookup_address_thumb block_lookup_address_thumb |
48 | #define _block_lookup_address_dual block_lookup_address_dual |
49 | #define _write_io_register8 write_io_register8 |
50 | #define _write_io_register16 write_io_register16 |
51 | #define _write_io_register32 write_io_register32 |
52 | #define _palette_ram palette_ram |
53 | #define _palette_ram_converted palette_ram_converted |
54 | #define _flush_translation_cache_ram flush_translation_cache_ram |
55 | #define _write_eeprom write_eeprom |
56 | #define _write_backup write_backup |
57 | #define _write_rtc write_rtc |
58 | #define _execute_store_cpsr_body execute_store_cpsr_body |
59 | #endif |
60 | |
2823a4c8 |
61 | .global _x86_update_gba |
62 | .global _x86_indirect_branch_arm |
63 | .global _x86_indirect_branch_thumb |
64 | .global _x86_indirect_branch_dual |
65 | .global _execute_store_u8 |
66 | .global _execute_store_u16 |
67 | .global _execute_store_u32 |
68 | .global _execute_store_cpsr |
69 | .global _execute_arm_translate |
70 | .global _step_debug_x86 |
71 | |
72 | .global _memory_map_read |
73 | .global _memory_map_write |
74 | .global _reg |
75 | |
76 | .global _oam_update |
77 | |
78 | .global _iwram |
79 | .global _ewram |
80 | .global _vram |
81 | .global _oam_ram |
82 | .global _bios_rom |
83 | .global _io_registers |
84 | |
85 | .extern _spsr |
86 | |
87 | .equ REG_SP, (13 * 4) |
88 | .equ REG_LR, (14 * 4) |
89 | .equ REG_PC, (15 * 4) |
90 | .equ REG_N_FLAG, (16 * 4) |
91 | .equ REG_Z_FLAG, (17 * 4) |
92 | .equ REG_C_FLAG, (18 * 4) |
93 | .equ REG_V_FLAG, (19 * 4) |
94 | .equ REG_CPSR, (20 * 4) |
95 | .equ REG_SAVE, (21 * 4) |
96 | .equ REG_SAVE2, (22 * 4) |
97 | .equ REG_SAVE3, (23 * 4) |
98 | .equ CPU_MODE, (29 * 4) |
99 | .equ CPU_HALT_STATE, (30 * 4) |
100 | .equ CHANGED_PC_STATUS, (31 * 4) |
101 | |
102 | # destroys ecx and edx |
103 | |
104 | .macro collapse_flag offset, shift |
105 | mov \offset(%ebx), %ecx |
106 | shl $\shift, %ecx |
107 | or %ecx, %edx |
108 | .endm |
109 | |
110 | .macro collapse_flags_no_update |
111 | xor %edx, %edx |
112 | collapse_flag REG_N_FLAG, 31 |
113 | collapse_flag REG_Z_FLAG, 30 |
114 | collapse_flag REG_C_FLAG, 29 |
115 | collapse_flag REG_V_FLAG, 28 |
116 | mov REG_CPSR(%ebx), %ecx |
117 | and $0xFF, %ecx |
118 | or %ecx, %edx |
119 | .endm |
120 | |
121 | |
122 | .macro collapse_flags |
123 | collapse_flags_no_update |
124 | mov %edx, REG_CPSR(%ebx) |
125 | .endm |
126 | |
127 | .macro extract_flag shift, offset |
128 | mov REG_CPSR(%ebx), %edx |
129 | shr $\shift, %edx |
130 | and $0x01, %edx |
eac69717 |
131 | mov %edx, \offset(%ebx) |
2823a4c8 |
132 | .endm |
133 | |
134 | .macro extract_flags |
135 | extract_flag 31, REG_N_FLAG |
136 | extract_flag 30, REG_Z_FLAG |
137 | extract_flag 29, REG_C_FLAG |
138 | extract_flag 28, REG_V_FLAG |
139 | .endm |
140 | |
141 | # Process a hardware event. Since an interrupt might be |
142 | # raised we have to check if the PC has changed. |
143 | |
144 | # eax: current address |
145 | |
146 | st: |
147 | .asciz "u\n" |
148 | |
149 | _x86_update_gba: |
150 | mov %eax, REG_PC(%ebx) # current PC = eax |
151 | collapse_flags # update cpsr, trashes ecx and edx |
152 | |
153 | call _update_gba # process the next event |
154 | |
155 | mov %eax, %edi # edi = new cycle count |
156 | # did the PC change? |
157 | cmpl $1, CHANGED_PC_STATUS(%ebx) |
158 | je lookup_pc |
159 | ret # if not, go back to caller |
160 | |
161 | # Perform this on an indirect branch that will definitely go to |
162 | # ARM code, IE anything that changes the PC in ARM mode except |
163 | # for BX and data processing to PC with the S bit set. |
164 | |
165 | # eax: GBA address to branch to |
166 | # edi: Cycle counter |
167 | |
168 | _x86_indirect_branch_arm: |
169 | call _block_lookup_address_arm |
170 | jmp *%eax |
171 | |
172 | # For indirect branches that'll definitely go to Thumb. In |
173 | # Thumb mode any indirect branches except for BX. |
174 | |
175 | _x86_indirect_branch_thumb: |
176 | call _block_lookup_address_thumb |
177 | jmp *%eax |
178 | |
179 | # For indirect branches that can go to either Thumb or ARM, |
180 | # mainly BX (also data processing to PC with S bit set, be |
181 | # sure to adjust the target with a 1 in the lowest bit for this) |
182 | |
183 | _x86_indirect_branch_dual: |
184 | call _block_lookup_address_dual |
185 | jmp *%eax |
186 | |
187 | |
188 | # General ext memory routines |
189 | |
190 | ext_store_ignore: |
191 | ret # ignore these writes |
192 | |
193 | write_epilogue: |
194 | cmp $0, %eax # 0 return means nothing happened |
195 | jz no_alert # if so we can leave |
196 | |
197 | collapse_flags # make sure flags are good for function call |
198 | cmp $2, %eax # see if it was an SMC trigger |
199 | je smc_write |
200 | |
201 | alert_loop: |
202 | call _update_gba # process the next event |
203 | |
204 | # see if the halt status has changed |
205 | mov CPU_HALT_STATE(%ebx), %edx |
206 | |
207 | cmp $0, %edx # 0 means it has |
208 | jnz alert_loop # if not go again |
209 | |
210 | mov %eax, %edi # edi = new cycle count |
211 | jmp lookup_pc # pc has definitely changed |
212 | |
213 | no_alert: |
214 | ret |
215 | |
216 | ext_store_eeprom: |
217 | jmp _write_eeprom # perform eeprom write |
218 | |
219 | |
220 | # 8bit ext memory routines |
221 | |
222 | ext_store_io8: |
223 | and $0x3FF, %eax # wrap around address |
224 | and $0xFF, %edx |
225 | call _write_io_register8 # perform 8bit I/O register write |
226 | jmp write_epilogue # see if it requires any system update |
227 | |
228 | ext_store_palette8: |
229 | and $0x3FE, %eax # wrap around address and align to 16bits |
230 | jmp ext_store_palette16b # perform 16bit palette write |
231 | |
232 | ext_store_vram8: |
233 | and $0x1FFFE, %eax # wrap around address and align to 16bits |
234 | mov %dl, %dh # copy lower 8bits of value into full 16bits |
235 | cmp $0x18000, %eax # see if address is in upper region |
236 | jb ext_store_vram8b |
237 | sub $0x8000, %eax # if so wrap down |
238 | |
239 | ext_store_vram8b: |
240 | mov %dx, _vram(%eax) # perform 16bit store |
241 | ret |
242 | |
243 | ext_store_oam8: |
244 | movl $1, _oam_update # flag OAM update |
245 | and $0x3FE, %eax # wrap around address and align to 16bits |
246 | mov %dl, %dh # copy lower 8bits of value into full 16bits |
247 | mov %dx, _oam_ram(%eax) # perform 16bit store |
248 | ret |
249 | |
250 | ext_store_backup: |
251 | and $0xFF, %edx # make value 8bit |
252 | and $0xFFFF, %eax # mask address |
253 | jmp _write_backup # perform backup write |
254 | |
255 | ext_store_u8_jtable: |
256 | .long ext_store_ignore # 0x00 BIOS, ignore |
257 | .long ext_store_ignore # 0x01 invalid, ignore |
258 | .long ext_store_ignore # 0x02 EWRAM, should have been hit already |
259 | .long ext_store_ignore # 0x03 IWRAM, should have been hit already |
260 | .long ext_store_io8 # 0x04 I/O registers |
261 | .long ext_store_palette8 # 0x05 Palette RAM |
262 | .long ext_store_vram8 # 0x06 VRAM |
263 | .long ext_store_oam8 # 0x07 OAM RAM |
264 | .long ext_store_ignore # 0x08 gamepak (no RTC accepted in 8bit) |
265 | .long ext_store_ignore # 0x09 gamepak, ignore |
266 | .long ext_store_ignore # 0x0A gamepak, ignore |
267 | .long ext_store_ignore # 0x0B gamepak, ignore |
268 | .long ext_store_ignore # 0x0C gamepak, ignore |
269 | .long ext_store_eeprom # 0x0D EEPROM (possibly) |
270 | .long ext_store_backup # 0x0E Flash ROM/SRAM |
271 | |
272 | ext_store_u8: |
273 | mov %eax, %ecx # ecx = address |
274 | shr $24, %ecx # ecx = address >> 24 |
275 | cmp $15, %ecx |
276 | ja ext_store_ignore |
277 | # ecx = ext_store_u8_jtable[address >> 24] |
278 | mov ext_store_u8_jtable(, %ecx, 4), %ecx |
279 | jmp *%ecx # jump to table index |
280 | |
281 | # eax: address to write to |
282 | # edx: value to write |
283 | # ecx: current pc |
284 | |
285 | _execute_store_u8: |
286 | mov %ecx, REG_PC(%ebx) # write out the PC |
287 | mov %eax, %ecx # ecx = address |
288 | test $0xF0000000, %ecx # check address range |
289 | jnz ext_store_u8 # if above perform an extended write |
290 | shr $15, %ecx # ecx = page number of address |
291 | # load the corresponding memory map offset |
292 | mov _memory_map_write(, %ecx, 4), %ecx |
293 | test %ecx, %ecx # see if it's NULL |
294 | jz ext_store_u8 # if so perform an extended write |
295 | and $0x7FFF, %eax # isolate the lower 15bits of the address |
296 | mov %dl, (%eax, %ecx) # store the value |
297 | # check for self-modifying code |
298 | testb $0xFF, -32768(%eax, %ecx) |
299 | jne smc_write |
300 | ret # return |
301 | |
302 | _execute_store_u16: |
303 | mov %ecx, REG_PC(%ebx) # write out the PC |
304 | and $~0x01, %eax # fix alignment |
305 | mov %eax, %ecx # ecx = address |
306 | test $0xF0000000, %ecx # check address range |
307 | jnz ext_store_u16 # if above perform an extended write |
308 | shr $15, %ecx # ecx = page number of address |
309 | # load the corresponding memory map offset |
310 | mov _memory_map_write(, %ecx, 4), %ecx |
311 | test %ecx, %ecx # see if it's NULL |
312 | jz ext_store_u16 # if so perform an extended write |
313 | and $0x7FFF, %eax # isolate the lower 15bits of the address |
314 | mov %dx, (%eax, %ecx) # store the value |
315 | # check for self-modifying code |
316 | testw $0xFFFF, -32768(%eax, %ecx) |
317 | jne smc_write |
318 | ret # return |
319 | |
320 | # 16bit ext memory routines |
321 | |
322 | ext_store_io16: |
323 | and $0x3FF, %eax # wrap around address |
324 | and $0xFFFF, %edx |
325 | call _write_io_register16 # perform 16bit I/O register write |
326 | jmp write_epilogue # see if it requires any system update |
327 | |
328 | ext_store_palette16: |
329 | and $0x3FF, %eax # wrap around address |
330 | |
331 | ext_store_palette16b: # entry point for 8bit write |
332 | mov %dx, _palette_ram(%eax) # write out palette value |
333 | mov %edx, %ecx # cx = dx |
334 | shl $11, %ecx # cx <<= 11 (red component is in high bits) |
335 | mov %dh, %cl # bottom bits of cx = top bits of dx |
336 | shr $2, %cl # move the blue component to the bottom of cl |
337 | and $0x03E0, %dx # isolate green component of dx |
338 | shl $1, %dx # make green component 6bits |
339 | or %edx, %ecx # combine green component into ecx |
340 | # write out the freshly converted palette value |
341 | mov %cx, _palette_ram_converted(%eax) |
342 | ret # done |
343 | |
344 | ext_store_vram16: |
345 | and $0x1FFFF, %eax # wrap around address |
346 | cmp $0x18000, %eax # see if address is in upper region |
347 | jb ext_store_vram16b |
348 | sub $0x8000, %eax # if so wrap down |
349 | |
350 | ext_store_vram16b: |
351 | mov %dx, _vram(%eax) # perform 16bit store |
352 | ret |
353 | |
354 | ext_store_oam16: |
355 | movl $1, _oam_update # flag OAM update |
356 | and $0x3FF, %eax # wrap around address |
357 | mov %dx, _oam_ram(%eax) # perform 16bit store |
358 | ret |
359 | |
360 | ext_store_rtc: |
361 | and $0xFFFF, %edx # make value 16bit |
362 | and $0xFF, %eax # mask address |
363 | jmp _write_rtc # write out RTC register |
364 | |
365 | ext_store_u16_jtable: |
366 | .long ext_store_ignore # 0x00 BIOS, ignore |
367 | .long ext_store_ignore # 0x01 invalid, ignore |
368 | .long ext_store_ignore # 0x02 EWRAM, should have been hit already |
369 | .long ext_store_ignore # 0x03 IWRAM, should have been hit already |
370 | .long ext_store_io16 # 0x04 I/O registers |
371 | .long ext_store_palette16 # 0x05 Palette RAM |
372 | .long ext_store_vram16 # 0x06 VRAM |
373 | .long ext_store_oam16 # 0x07 OAM RAM |
374 | .long ext_store_rtc # 0x08 gamepak or RTC |
375 | .long ext_store_ignore # 0x09 gamepak, ignore |
376 | .long ext_store_ignore # 0x0A gamepak, ignore |
377 | .long ext_store_ignore # 0x0B gamepak, ignore |
378 | .long ext_store_ignore # 0x0C gamepak, ignore |
379 | .long ext_store_eeprom # 0x0D EEPROM (possibly) |
380 | .long ext_store_ignore # 0x0E Flash ROM/SRAM must be 8bit |
381 | |
382 | ext_store_u16: |
383 | mov %eax, %ecx # ecx = address |
384 | shr $24, %ecx # ecx = address >> 24 |
385 | cmp $15, %ecx |
386 | ja ext_store_ignore |
387 | # ecx = ext_store_u16_jtable[address >> 24] |
388 | mov ext_store_u16_jtable(, %ecx, 4), %ecx |
389 | jmp *%ecx # jump to table index |
390 | |
391 | _execute_store_u32: |
392 | mov %ecx, REG_PC(%ebx) # write out the PC |
393 | and $~0x03, %eax # fix alignment |
394 | mov %eax, %ecx # ecx = address |
395 | test $0xF0000000, %ecx # check address range |
396 | jnz ext_store_u32 # if above perform an extended write |
397 | shr $15, %ecx # ecx = page number of address |
398 | # load the corresponding memory map offset |
399 | mov _memory_map_write(, %ecx, 4), %ecx |
400 | test %ecx, %ecx # see if it's NULL |
401 | jz ext_store_u32 # if so perform an extended write |
402 | and $0x7FFF, %eax # isolate the lower 15bits of the address |
403 | mov %edx, (%eax, %ecx) # store the value |
404 | # check for self-modifying code |
405 | testl $0xFFFFFFFF, -32768(%eax, %ecx) |
406 | jne smc_write |
407 | ret # return it |
408 | |
409 | # 32bit ext memory routines |
410 | |
411 | ext_store_io32: |
412 | and $0x3FF, %eax # wrap around address |
413 | call _write_io_register32 # perform 32bit I/O register write |
414 | jmp write_epilogue # see if it requires any system update |
415 | |
416 | ext_store_palette32: |
417 | and $0x3FF, %eax # wrap around address |
418 | call ext_store_palette16b # write first 16bits |
419 | add $2, %eax # go to next address |
420 | shr $16, %edx # go to next 16bits |
421 | jmp ext_store_palette16b # write next 16bits |
422 | |
423 | ext_store_vram32: |
424 | and $0x1FFFF, %eax # wrap around address |
425 | cmp $0x18000, %eax # see if address is in upper region |
426 | jb ext_store_vram32b |
427 | sub $0x8000, %eax # if so wrap down |
428 | |
429 | ext_store_vram32b: |
430 | mov %edx, _vram(%eax) # perform 32bit store |
431 | ret |
432 | |
433 | ext_store_oam32: |
434 | movl $1, _oam_update # flag OAM update |
435 | and $0x3FF, %eax # wrap around address |
436 | mov %edx, _oam_ram(%eax) # perform 32bit store |
437 | ret |
438 | |
439 | ext_store_u32_jtable: |
440 | .long ext_store_ignore # 0x00 BIOS, ignore |
441 | .long ext_store_ignore # 0x01 invalid, ignore |
442 | .long ext_store_ignore # 0x02 EWRAM, should have been hit already |
443 | .long ext_store_ignore # 0x03 IWRAM, should have been hit already |
444 | .long ext_store_io32 # 0x04 I/O registers |
445 | .long ext_store_palette32 # 0x05 Palette RAM |
446 | .long ext_store_vram32 # 0x06 VRAM |
447 | .long ext_store_oam32 # 0x07 OAM RAM |
448 | .long ext_store_ignore # 0x08 gamepak, ignore (no RTC in 32bit) |
449 | .long ext_store_ignore # 0x09 gamepak, ignore |
450 | .long ext_store_ignore # 0x0A gamepak, ignore |
451 | .long ext_store_ignore # 0x0B gamepak, ignore |
452 | .long ext_store_ignore # 0x0C gamepak, ignore |
453 | .long ext_store_eeprom # 0x0D EEPROM (possibly) |
454 | .long ext_store_ignore # 0x0E Flash ROM/SRAM must be 8bit |
455 | |
456 | |
457 | ext_store_u32: |
458 | mov %eax, %ecx # ecx = address |
459 | shr $24, %ecx # ecx = address >> 24 |
460 | cmp $15, %ecx |
461 | ja ext_store_ignore |
462 | # ecx = ext_store_u32_jtable[address >> 24] |
463 | mov ext_store_u32_jtable(, %ecx, 4), %ecx |
464 | jmp *%ecx |
465 | |
466 | # %eax = new_cpsr |
467 | # %edx = store_mask |
468 | |
469 | _execute_store_cpsr: |
470 | mov %edx, REG_SAVE(%ebx) # save store_mask |
471 | mov %ecx, REG_SAVE2(%ebx) # save PC too |
472 | |
473 | mov %eax, %ecx # ecx = new_cpsr |
474 | and %edx, %ecx # ecx = new_cpsr & store_mask |
475 | mov REG_CPSR(%ebx), %eax # eax = cpsr |
476 | not %edx # edx = ~store_mask |
477 | and %edx, %eax # eax = cpsr & ~store_mask |
478 | or %ecx, %eax # eax = new cpsr combined with old |
479 | |
480 | call _execute_store_cpsr_body # do the dirty work in this C function |
481 | |
482 | extract_flags # pull out flag vars from new CPSR |
483 | |
484 | cmp $0, %eax # see if return value is 0 |
485 | jnz changed_pc_cpsr # might have changed the PC |
486 | |
487 | ret # return |
488 | |
489 | changed_pc_cpsr: |
490 | add $4, %esp # get rid of current return address |
491 | call _block_lookup_address_arm # lookup new PC |
492 | jmp *%eax |
493 | |
494 | smc_write: |
495 | call _flush_translation_cache_ram |
496 | |
497 | lookup_pc: |
498 | add $4, %esp |
499 | movl $0, CHANGED_PC_STATUS(%ebx) |
500 | mov REG_PC(%ebx), %eax |
501 | testl $0x20, REG_CPSR(%ebx) |
502 | jz lookup_pc_arm |
503 | |
504 | lookup_pc_thumb: |
505 | call _block_lookup_address_thumb |
506 | jmp *%eax |
507 | |
508 | lookup_pc_arm: |
509 | call _block_lookup_address_arm |
510 | jmp *%eax |
511 | |
512 | # eax: cycle counter |
513 | |
514 | _execute_arm_translate: |
eac69717 |
515 | movl (_reg), %ebx # load base register |
2823a4c8 |
516 | extract_flags # load flag variables |
517 | movl %eax, %edi # load edi cycle counter |
518 | |
519 | movl REG_PC(%ebx), %eax # load PC |
520 | |
521 | testl $0x20, REG_CPSR(%ebx) |
522 | jnz 1f |
523 | |
524 | call _block_lookup_address_arm |
525 | jmp *%eax # jump to it |
526 | |
527 | 1: |
528 | call _block_lookup_address_thumb |
529 | jmp *%eax |
530 | |
531 | _step_debug_x86: |
532 | collapse_flags |
533 | # mov $100, %edi |
534 | mov %edi, %edx |
535 | jmp _step_debug |
536 | |
537 | .comm _memory_map_read 0x8000 |
538 | .comm _memory_map_write 0x8000 |
eac69717 |
539 | .comm _reg 4 |
2823a4c8 |
540 | |
541 | |