Archived
1
0
Fork 0
This commit is contained in:
sup39 2022-10-14 18:46:03 +09:00
commit 3f66f46ff3
12 changed files with 111390 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/dist
*.egg-info/
__pycache__/

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 sup39[サポミク]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

2
MANIFEST.in Normal file
View file

@ -0,0 +1,2 @@
include src/supGeckoASM/include/*
include src/supGeckoASM/ldscript/*

16
README.md Normal file
View file

@ -0,0 +1,16 @@
# supGeckoASM
A tool to make gecko code from ASM
## Supported code type
### C0
### C2
```ld
$$ = 0;
$C2$.xxx = ...;
```
### 06
```ld
$$ = ...;
$b$.xxx = ...;
$bl$.yyy = ...;
```

3
pyproject.toml Normal file
View file

@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta"

30
setup.cfg Normal file
View file

@ -0,0 +1,30 @@
[metadata]
name = supGeckoASM
version = 0.0.1a2
author = sup39
author_email = sms@sup39.dev
description = A tool to make gecko code from ASM
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/sup39/supGeckoASM
license = MIT
project_urls =
Bug Tracker = https://github.com/sup39/supGeckoASM/issues
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
Operating System :: Microsoft :: Windows
[options]
include_package_data = True
packages = find:
python_requires = >=3.8
install_requires =
pywin32 >= 304
[options.packages.find]
where = src
[options.entry_points]
console_scripts =
supGeckoASM = supGeckoASM.cli:main

View file

224
src/supGeckoASM/cli.py Normal file
View file

@ -0,0 +1,224 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2022 sup39[サポミク]
import shutil
from distutils import spawn
import tempfile
import os
import subprocess
import sys
import re
from collections import defaultdict, Counter
import logging
import win32clipboard
def pbcopy(content):
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(content)
win32clipboard.CloseClipboard()
def normalize_dolver(s):
if re.match(r'^(?:JP?|N(?:TSC)?[-_]?J)(?:1\.?0|\.0)?$|^1\.0$', s):
return 'NTSC-J_1.0'
if re.match(r'^(?:JP?A|N(?:TSC)?[-_]?J)(?:1\.?1|\.1|A)?$|^1\.1$', s):
return 'NTSC-J_1.1'
#if re.match(r'^EU|P|PAL$', s):
# return 'PAL'
#if re.match(r'^US?|N(?:TSC)?[-_]?U$', s):
# return 'NTSC-U'
return None
def system(argv, *args, **kwargs):
r = subprocess.run(argv, *args, capture_output=True, text=True, **kwargs)
if r.stderr: logger.error(r.stderr)
return r
def asm2gecko(fnIn, dolver):
__dirname__ = os.path.dirname(__file__)
includeDir, ldscriptDir = (f'{__dirname__}/{name}' for name in ['include', 'ldscript'])
distDir = tempfile.mkdtemp()
distASM, distOBJ, distLOBJ, distBIN = (f'{distDir}/0.{ext}' for ext in ['s', 'o', 'l.o', 'bin'])
def cleanup():
shutil.rmtree(distDir)
try:
# include macros.inc
with open(distASM, 'w') as fw, open(fnIn, 'r') as fr:
print(f'.include "macros.inc"', file=fw)
for line in fr: fw.write(line)
# assemble
if system([
'powerpc-eabi-as',
'-o', distOBJ,
'-I', includeDir,
distASM,
]).returncode: return cleanup()
# link
extraLDFlags = []
extraLDScript = re.sub('\.s', '.ld', fnIn)
if os.path.isfile(extraLDScript):
extraLDFlags += ['-T', extraLDScript]
if system([
'powerpc-eabi-ld',
'-o', distLOBJ,
'-T', f'{ldscriptDir}/{dolver}.ld',
*extraLDFlags,
'-T', f'{ldscriptDir}/common.ld',
distOBJ,
]).returncode: return cleanup()
# binary
if system([
'powerpc-eabi-objcopy',
'-O', 'binary',
distLOBJ, distBIN,
]).returncode: return cleanup()
# gecko symbols
asmSymbs = {}
geckoSymbs = {}
r = system([
'powerpc-eabi-objdump',
'-h', '-t', distLOBJ,
])
lines = r.stdout.split('\n')
geckoBase = int(lines[5].split()[3], 16)
isC2 = False
for line in lines[8:]:
if not re.match('^[0-9a-f]{8} \w', line): continue
cols = line.split()
if len(cols) != 5: continue
addr, _, sec, _, name = cols
if sec == '.text':
asmSymbs[name] = addr
else:
if name == '$$' and int(addr) == 0:
isC2 = True
m = re.match(r'\$(bl?|C2)\$(.*)', name)
if m is None: continue
ct, name = m.groups()
if name in geckoSymbs:
logger.error('Conflict symbols: $%s$, $%s$ for `%s`'%(
geckoSymbs[name][0], ct, name,
))
return cleanup()
geckoSymbs[name] = (ct, addr)
# binary
if system([
'powerpc-eabi-objcopy',
'-O', 'binary',
distLOBJ, distBIN,
]).returncode: cleanup()
with open(distBIN, 'rb') as f:
codeBin = f.read()
# make code
codes = []
codeSymbs = []
append_code = lambda a, b: codes.append('%08X %08X'%(a, b))
append_hex_code = lambda a, b: codes.append(('%s %s'%(a, b)).upper())
def dump_bin_code(raw):
for a, b in re.findall(r'(.{8})(.{8})', raw.hex()):
append_hex_code(a, b)
if len(geckoSymbs) == 0:
# C0
if len(codeBin)&4: codeBin += b'\x4E\x80\x00\x20'
append_code(0xC0000000, len(codeBin)>>3)
dump_bin_code(codeBin)
elif isC2:
# C2
## calc size of each C2 code
sizes = {}
pairs = sorted((asmSymbs[name], name) for name, addr in geckoSymbs.items() if name in asmSymbs)
for (addr, name), (addr1, name1) in zip(pairs, pairs[1:]):
sizes[name] = int(addr1, 16)-int(addr, 16)
addr, name = pairs[-1]
sizes[name] = len(codeBin)-int(addr, 16)
## make code
for name, (ct, src) in geckoSymbs.items():
if ct != 'C2': continue
dst = asmSymbs.get(name, None)
if dst is None: continue
size = sizes[name]
src = int(src, 16)
dst = int(dst, 16)
c0 = 0xC200_0000 | src&0x01ff_ffff
c1 = (size>>3)+1
append_code(c0, c1)
## dump code
code = codeBin[dst:dst+size]
if size & 4 == 0: code += b'\x60\x00\x00\x00'
code += b'\x00\x00\x00\x00'
dump_bin_code(code)
else:
## 04 b/bl code
for name, (ct, src) in geckoSymbs.items():
if ct not in ['b', 'bl']: continue
dst = asmSymbs.get(name, None)
if dst is not None:
src = int(src, 16)
dst = int(dst, 16)
c0 = 0x0400_0000 | src&0x01ff_ffff
c1 = (0x4C000000 if dst<src else 0x48000000) + \
(dst-src) + (1 if ct == 'bl' else 0)
append_code(c0, c1)
codeSymbs.append((name, ct, src, dst))
## 06 bin code
if len(codeBin):
append_code(0x0600_0000 | geckoBase&0x01ff_ffff, len(codeBin))
dump_bin_code(codeBin+b'\x00'*7)
except:
import traceback
traceback.print_exc()
return cleanup()
# DONE
cleanup()
return codes, codeSymbs, asmSymbs, isC2
def main():
logging.basicConfig()
logger = logging.getLogger('supGeckoCode')
# check required bin
for exe in ['powerpc-eabi-as', 'powerpc-eabi-ld', 'powerpc-eabi-objdump', 'powerpc-eabi-objcopy']:
if spawn.find_executable(exe) is None:
logger.error('Cannot find powerpc-eabi-{as,ld,objdump,objcopy}')
argv = sys.argv
argc = len(argv)
if argc <= 1:
logger.error('Usage: %s {*.s} [JP|JPA]'%argv[0])
sys.exit(1)
fnIn = argv[1]
dolver = normalize_dolver(argv[2]) if argc > 2 else 'NTSC-J_1.0'
if dolver is None:
logger.error('Unknown dol version: %s'%argv[2])
sys.exit(1)
r = asm2gecko(fnIn, dolver)
if r is not None:
codes, codeSymbs, asmSymbs, isC2 = r
pbcopy('\n'.join(codes))
# print asm symbols
if not isC2:
for name, addr in asmSymbs.items():
print(addr.upper(), name)
print()
# print gecko symbols
for name, ct, src, dst in codeSymbs:
print('%-2s [%08X] @[%08X] %s'%(ct, dst, src, name))
print()
# code length
print('Code length:', len(codes), 'line(s)')
if __name__ == '__main__':
main()

View file

@ -0,0 +1,72 @@
.set r0, 0
.set r1, 1
.set r2, 2
.set r3, 3
.set r4, 4
.set r5, 5
.set r6, 6
.set r7, 7
.set r8, 8
.set r9, 9
.set r10, 10
.set r11, 11
.set r12, 12
.set r13, 13
.set r14, 14
.set r15, 15
.set r16, 16
.set r17, 17
.set r18, 18
.set r19, 19
.set r20, 20
.set r21, 21
.set r22, 22
.set r23, 23
.set r24, 24
.set r25, 25
.set r26, 26
.set r27, 27
.set r28, 28
.set r29, 29
.set r30, 30
.set r31, 31
.set f0, 0
.set f1, 1
.set f2, 2
.set f3, 3
.set f4, 4
.set f5, 5
.set f6, 6
.set f7, 7
.set f8, 8
.set f9, 9
.set f10, 10
.set f11, 11
.set f12, 12
.set f13, 13
.set f14, 14
.set f15, 15
.set f16, 16
.set f17, 17
.set f18, 18
.set f19, 19
.set f20, 20
.set f21, 21
.set f22, 22
.set f23, 23
.set f24, 24
.set f25, 25
.set f26, 26
.set f27, 27
.set f28, 28
.set f29, 29
.set f30, 30
.set f31, 31
.set cr0, 0
.set cr1, 1
.set cr2, 2
.set cr3, 3
.set cr4, 4
.set cr5, 5
.set cr6, 6
.set cr7, 7

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,5 @@
SECTIONS {
. = DEFINED($$) ? $$ : 0x817f9800;
.text : ALIGN(4) { *(.text) }
.rodata : ALIGN(4) { *(.rodata) }
}