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