init
This commit is contained in:
commit
3f66f46ff3
12 changed files with 111390 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
/dist
|
||||||
|
*.egg-info/
|
||||||
|
__pycache__/
|
21
LICENSE
Normal file
21
LICENSE
Normal 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
2
MANIFEST.in
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
include src/supGeckoASM/include/*
|
||||||
|
include src/supGeckoASM/ldscript/*
|
16
README.md
Normal file
16
README.md
Normal 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
3
pyproject.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
30
setup.cfg
Normal file
30
setup.cfg
Normal 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
|
0
src/supGeckoASM/__init__.py
Normal file
0
src/supGeckoASM/__init__.py
Normal file
224
src/supGeckoASM/cli.py
Normal file
224
src/supGeckoASM/cli.py
Normal 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()
|
72
src/supGeckoASM/include/macros.inc
Normal file
72
src/supGeckoASM/include/macros.inc
Normal 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
|
55707
src/supGeckoASM/ldscript/NTSC-J_1.0.ld
Normal file
55707
src/supGeckoASM/ldscript/NTSC-J_1.0.ld
Normal file
File diff suppressed because it is too large
Load diff
55307
src/supGeckoASM/ldscript/NTSC-J_1.1.ld
Normal file
55307
src/supGeckoASM/ldscript/NTSC-J_1.1.ld
Normal file
File diff suppressed because it is too large
Load diff
5
src/supGeckoASM/ldscript/common.ld
Normal file
5
src/supGeckoASM/ldscript/common.ld
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
SECTIONS {
|
||||||
|
. = DEFINED($$) ? $$ : 0x817f9800;
|
||||||
|
.text : ALIGN(4) { *(.text) }
|
||||||
|
.rodata : ALIGN(4) { *(.rodata) }
|
||||||
|
}
|
Reference in a new issue