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