original source from gpsp09-2xb_src.tar.bz2
[gpsp.git] / x86 / x86_stub.S
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