Files
psopeeps-newserv/system/client-functions/System/RunDOL.ppc.s
T
2025-06-04 00:16:43 -07:00

149 lines
4.1 KiB
ArmAsm

# This function is required for loading DOLs. If it's not present, newserv can't
# serve DOL files to GameCube clients.
entry_ptr:
reloc0:
.offsetof start
start:
disable_interrupts:
mfmsr r3
rlwinm r3, r3, 0, 17, 15
mtmsr r3
bl get_current_addr
dol_base_ptr:
.zero
get_current_addr:
mflr r31
# TODO: It'd be nice to be able to use an expression for the immediate value
# here - something like (dol_base_ptr - start), for example
subi r31, r31, 0x10 # r31 = base of data to copy to low memory (start label)
# If this code is not running from low memory (80001800-80003000), then copy
# it there and branch to it
lis r3, 0x8000
ori r3, r3, 0x3000
cmp r31, r3
blt run_dol
copy_code_to_low_memory:
bl get_end_ptr
sub r30, r3, r31 # r30 = size of code to copy (for cache flushing later)
subi r5, r3, 4 # r5 = end ptr
subi r4, r31, 4
lis r3, 0x8000
ori r3, r3, 0x17FC
copy_code_to_low_memory__again:
lwzu r0, [r4 + 4]
stwu [r3 + 4], r0
cmp r4, r5
bne copy_code_to_low_memory__again
# Flush the data cache and clear the instruction cache before running the
# moved code
lis r3, 0x8000
ori r3, r3, 0x1800
mr r4, r30
mtlr r3
b flush_cached_code_writes
run_dol:
lwz r30, [r31 + 0x10] # r30 = data base ptr
# Decompress the file first. If the compressed size is zero, then skip this
# step (the file is not compressed). The header consists of two fields:
# compressed size followed by decompressed size.
lwz r6, [r30]
cmplwi r6, 0
beq run_dol__not_compressed
lwz r5, [r30 + 4]
addi r4, r30, 8 # Compressed data immediately follows the 2 header fields
sub r3, r30, r5 # Decompress to immediately before the compressed data
mr r30, r3 # Save DOL header pointer for after decompression
bl prs_decompress
b run_dol__decompressed
run_dol__not_compressed:
addi r30, r30, 8
run_dol__decompressed:
# DOL files are very simple: they have up to 7 text sections, up to 11 data
# sections, and a BSS section and an entrypoint. No imports or other fancy
# things to do - we just have to move a bunch of bytes around.
mr r29, r30 # r29 = DOL header iterator
addi r28, r29, 0x48 # r28 = DOL header iterator end value
run_dol__move_section:
lwz r4, [r29] # r4 = file offset of section data
add r4, r4, r30 # r4 = address of section data
lwz r3, [r29 + 0x48] # r3 = dest address of section data
lwz r5, [r29 + 0x90] # r5 = number of bytes to move
cmplwi r5, 0 # If size is 0, skip the section entirely
beq skip_section
subi r3, r3, 1
subi r4, r4, 1
add r5, r4, r5 # r5 = source end pointer
run_dol__move_section_data__again:
# TODO: We probably should implement memmove-like semantics here, in case the
# DOL loads at an unusually late address. This is probably very rare.
lbzu r0, [r4 + 1]
stbu [r3 + 1], r0
cmp r4, r5
bne run_dol__move_section_data__again
# Flush the data cache and invalidate the instruction cache after copying the
# section data. Technically we don't have to do this for data sections, but
# I'm lazy and it doesn't take too long.
lwz r3, [r29 + 0x48] # r3 = dest address of section data
lwz r4, [r29 + 0x90] # r4 = size of section data
bl flush_cached_code_writes
skip_section:
# Move to the next section
addi r29, r29, 4
cmp r29, r28
bne run_dol__move_section
run_dol__zero_bss:
lwz r3, [r30 + 0xD8] # r3 = BSS address
lwz r4, [r30 + 0xDC] # r4 = BSS size
cmplwi r4, 0
beq run_dol__skip_zero_bss
add r4, r3, r4 # r4 = BSS end address
subi r3, r3, 1
li r0, 0
run_dol__zero_bss__again:
stbu [r3 + 1], r0
cmp r3, r4
bne run_dol__zero_bss__again
run_dol__skip_zero_bss:
run_dol__go_to_entrypoint:
lwz r0, [r30 + 0xE0] # r30 = entrypoint
mtctr r0
bctr
flush_cached_code_writes:
.include FlushCachedCode
blr
prs_decompress:
.include PRSDecompress
return_end_ptr:
mflr r3
bctr
get_end_ptr:
mflr r0
mtctr r0
bl return_end_ptr