a742b5e01d
OAM DMA appears to be inactive during halt as well as CGB speed change (presumably stop in general). This adds some simple tests for that and adjusts the implementation accordingly. HDMA appears to be similarly affected (not covered by this change).
305 lines
7.7 KiB
Python
305 lines
7.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Quick and Dirty Game Boy Assembler
|
|
import re
|
|
import sys
|
|
import collections
|
|
|
|
class InputError(RuntimeError): pass
|
|
Op = collections.namedtuple('Op', ['string', 'code', 'size', 'assemble'])
|
|
|
|
def mkrestr(s):
|
|
return re.sub(r'\s+', r'\s*', re.sub(r'([,)(+])', r' \\\1 ', s))
|
|
|
|
imm8 = r'[0-9a-fA-F][0-9a-fA-F]'
|
|
imm16 = imm8 + imm8
|
|
labelregexp = re.compile('\w+\s+(\w+)')
|
|
|
|
def assembleNormal(outdata, outpos, indata, inpos, targets, op):
|
|
pass
|
|
|
|
def assembleImm8(outdata, outpos, indata, inpos, targets, op):
|
|
m = re.match(mkrestr(op.string).replace('imm8', '('+imm8+')'), indata[inpos])
|
|
outdata[outpos] = int(m.group(1), 0x10)
|
|
|
|
def assembleImm16(outdata, outpos, indata, inpos, targets, op):
|
|
m = re.match(mkrestr(op.string).replace('imm16', '('+imm16+')'), indata[inpos])
|
|
outdata[outpos ] = int(m.group(1)[2:4], 0x10)
|
|
outdata[outpos+1] = int(m.group(1)[0:2], 0x10)
|
|
|
|
def assembleJr(outdata, outpos, indata, inpos, targets, op):
|
|
label = labelregexp.match(indata[inpos]).group(1)
|
|
t = targets.get(label)
|
|
t = t - (outpos + 1) if t != None else int(label, 0x10)
|
|
|
|
if t < -0x80 or t > 0x7f:
|
|
raise InputError('line ' + str(i) + ': ' + indata[inpos], 'Relative jump target too far away')
|
|
|
|
outdata[outpos] = t & 0xff
|
|
|
|
def assembleJp(outdata, outpos, indata, inpos, targets, op):
|
|
label = labelregexp.match(indata[inpos]).group(1)
|
|
t = targets.get(label)
|
|
|
|
if t == None:
|
|
t = int(label, 0x10)
|
|
elif t >= 0x8000:
|
|
t = 0x4000 | (t & 0x3fff)
|
|
|
|
outdata[outpos ] = t & 0xFF
|
|
outdata[outpos+1] = t >> 8
|
|
|
|
def makeoplist():
|
|
l = []
|
|
|
|
def addop(opstr, opcode):
|
|
size = (opcode >= 0) + (opcode >= 0x100)
|
|
moperand = re.match(r'(.*imm8)|(jr.* target)|(.*imm16)|(call.* target|jp.* target)', opstr)
|
|
if moperand:
|
|
optype = [(0, assembleNormal), (1, assembleImm8), (1, assembleJr), (2, assembleImm16), (2, assembleJp)][moperand.lastindex]
|
|
l.append(Op(opstr, opcode, size + optype[0], optype[1]))
|
|
else:
|
|
l.append(Op(opstr, opcode, size, assembleNormal))
|
|
|
|
addop('nop', 0x00)
|
|
addop('ld bc, imm16', 0x01)
|
|
addop('ld(bc), a', 0x02)
|
|
addop('inc bc', 0x03)
|
|
addop('inc b', 0x04)
|
|
addop('dec b', 0x05)
|
|
addop('ld b, imm8', 0x06)
|
|
addop('ld a, (bc)', 0x0a)
|
|
addop('inc c', 0x0c)
|
|
addop('dec c', 0x0d)
|
|
addop('ld c, imm8', 0x0e)
|
|
addop('rrca', 0x0f)
|
|
addop('stop, imm8', 0x10)
|
|
addop('ld de, imm16', 0x11)
|
|
addop('ld(de), a', 0x12)
|
|
addop('inc d', 0x14)
|
|
addop('dec d', 0x15)
|
|
addop('ld d, imm8', 0x16)
|
|
addop('jr target', 0x18)
|
|
addop('add hl, de', 0x19)
|
|
addop('ld a, (de)', 0x1a)
|
|
addop('inc e', 0x1c)
|
|
addop('dec e', 0x1d)
|
|
addop('ld e, imm8', 0x1e)
|
|
addop('jrnz target', 0x20)
|
|
addop('ld hl, imm16', 0x21)
|
|
addop('ld(hl++), a', 0x22)
|
|
addop('inc h', 0x24)
|
|
addop('dec h', 0x25)
|
|
addop('ld a, (hl++)', 0x2a)
|
|
addop('inc l', 0x2c)
|
|
addop('dec l', 0x2d)
|
|
addop('ld sp, imm16', 0x31)
|
|
addop('ld(hl--), a', 0x32)
|
|
addop('ld(hl), imm8', 0x36)
|
|
addop('add hl, sp', 0x39)
|
|
addop('ld a, (hl--)', 0x3a)
|
|
addop('inc a', 0x3c)
|
|
addop('dec a', 0x3d)
|
|
addop('ld a, imm8', 0x3e)
|
|
addop('ld b, a', 0x47)
|
|
addop('ld c, a', 0x4f)
|
|
addop('ld d, a', 0x57)
|
|
addop('ld e, a', 0x5f)
|
|
addop('halt', 0x76)
|
|
addop('ld(hl), a', 0x77)
|
|
addop('ld a, b', 0x78)
|
|
addop('ld a, c', 0x79)
|
|
addop('ld a, d', 0x7a)
|
|
addop('ld a, e', 0x7b)
|
|
addop('ld a, h', 0x7c)
|
|
addop('ld a, l', 0x7d)
|
|
addop('ld a, (hl)', 0x7e)
|
|
addop('add a, b', 0x80)
|
|
addop('add a, e', 0x83)
|
|
addop('sub a, b', 0x90)
|
|
addop('sub a, c', 0x91)
|
|
addop('sub a, d', 0x92)
|
|
addop('and a, b', 0xa0)
|
|
addop('and a, c', 0xa1)
|
|
addop('and a, d', 0xa2)
|
|
addop('xor a, a', 0xaf)
|
|
addop('or a, a', 0xb7)
|
|
addop('cmp a, b', 0xb8)
|
|
addop('cmp a, c', 0xb9)
|
|
addop('cmp a, d', 0xba)
|
|
addop('cmp a, e', 0xbb)
|
|
addop('cmp a, h', 0xbc)
|
|
addop('retnz', 0xc0)
|
|
addop('pop bc', 0xc1)
|
|
addop('jpnz target', 0xc2)
|
|
addop('jp target', 0xc3)
|
|
addop('push bc', 0xc5)
|
|
addop('rst 00', 0xc7)
|
|
addop('ret', 0xc9)
|
|
addop('call target', 0xcd)
|
|
addop('pop de', 0xd1)
|
|
addop('push de', 0xd5)
|
|
addop('sub a, imm8', 0xd6)
|
|
addop('reti', 0xd9)
|
|
addop('ldff(imm8), a', 0xe0)
|
|
addop('pop hl', 0xe1)
|
|
addop('ldff(c), a', 0xe2)
|
|
addop('push hl', 0xe5)
|
|
addop('and a, imm8', 0xe6)
|
|
addop('ld(imm16), a', 0xea)
|
|
addop('xor a, imm8', 0xee)
|
|
addop('ldff a, (imm8)', 0xf0)
|
|
addop('pop af', 0xf1)
|
|
addop('ldff a, (c)', 0xf2)
|
|
addop('di', 0xf3)
|
|
addop('push af', 0xf5)
|
|
addop('ld a, (imm16)', 0xfa)
|
|
addop('ei', 0xfb)
|
|
addop('cmp a, imm8', 0xfe)
|
|
addop('sra a', 0xcb2f)
|
|
addop('swap a', 0xcb37)
|
|
addop('srl a', 0xcb3f)
|
|
addop('\w* : ', -1)
|
|
|
|
return l
|
|
|
|
def makeopregexp(oplist):
|
|
rxps = ''
|
|
for e in oplist:
|
|
rxps += '(' + mkrestr(e.string) + '$)|'
|
|
return re.compile(rxps[0:len(rxps)-1].replace('imm8', imm8).replace('imm16', imm16).replace('target', r'\s\w+'))
|
|
|
|
oplist = makeoplist()
|
|
opregexp = makeopregexp(oplist)
|
|
|
|
def maptargets(targets, indata, instart, addr):
|
|
for i in xrange(instart, len(indata)):
|
|
match = opregexp.match(indata[i])
|
|
if match == None:
|
|
break
|
|
|
|
op = oplist[match.lastindex - 1]
|
|
|
|
if op.size == 0:
|
|
targets[re.match('(\w*)\s*:', indata[i]).group(1)] = addr
|
|
|
|
addr += op.size
|
|
|
|
return i
|
|
|
|
def astext(outdata, addr, indata, instart, targets):
|
|
for i in xrange(instart, len(indata)):
|
|
match = opregexp.match(indata[i])
|
|
if match == None:
|
|
break
|
|
|
|
op = oplist[match.lastindex - 1]
|
|
outpos = addr
|
|
|
|
if op.code >= 0x100:
|
|
outdata[outpos ] = op.code >> 8
|
|
outdata[outpos+1] = op.code & 0xff
|
|
outpos += 2
|
|
elif op.code >= 0:
|
|
outdata[outpos] = op.code
|
|
outpos += 1
|
|
|
|
op.assemble(outdata, outpos, indata, i, targets, op)
|
|
|
|
addr += op.size
|
|
|
|
return i
|
|
|
|
def asdata(outdata, addr, indata, instart):
|
|
try:
|
|
for i in xrange(instart, len(indata)):
|
|
ints = [int(x, 0x10) for x in indata[i].split()]
|
|
outdata[addr:addr+len(ints)] = ints
|
|
addr += len(ints)
|
|
except ValueError:
|
|
pass
|
|
|
|
return i
|
|
|
|
def putgblogo(outdata):
|
|
outdata[0x104:0x134] = \
|
|
[0xce, 0xed, 0x66, 0x66, 0xcc, 0x0d, 0x00, 0x0b,
|
|
0x03, 0x73, 0x00, 0x83, 0x00, 0x0c, 0x00, 0x0d,
|
|
0x00, 0x08, 0x11, 0x1f, 0x88, 0x89, 0x00, 0x0e,
|
|
0xdc, 0xcc, 0x6e, 0xe6, 0xdd, 0xdd, 0xd9, 0x99,
|
|
0xbb, 0xbb, 0x67, 0x63, 0x6e, 0x0e, 0xec, 0xcc,
|
|
0xdd, 0xdc, 0x99, 0x9f, 0xbb, 0xb9, 0x33, 0x3e]
|
|
|
|
def putheadercsum(outdata):
|
|
outdata[0x14d] = sum([-(x+1) for x in outdata[0x134:0x14d]]) & 0xFF
|
|
|
|
def putromcsum(outdata):
|
|
csum = sum(outdata)
|
|
outdata[0x14e:0x150] = [csum >> 8 & 0xff, csum & 0xff]
|
|
|
|
def assembleFile(indata):
|
|
size = 0x8000
|
|
startpos = 0
|
|
|
|
for i in xrange(0, len(indata)):
|
|
spl = indata[i].split()
|
|
if len(spl) > 0 and spl[0] == '.size':
|
|
size = int(spl[1], 0x10)
|
|
startpos = i + 1
|
|
break
|
|
|
|
targets = {}
|
|
i = startpos
|
|
while i < len(indata):
|
|
spl = [x.strip() for x in indata[i].split('@')]
|
|
i += 1;
|
|
|
|
if spl[0] == '.text':
|
|
i = maptargets(targets, indata, i, int(spl[1], 0x10))
|
|
|
|
outdata = bytearray(size)
|
|
i = startpos
|
|
while i < len(indata):
|
|
if indata[i] != '':
|
|
spl = [x.strip() for x in indata[i].split('@')]
|
|
|
|
if spl[0] == '.text':
|
|
i = astext(outdata, int(spl[1], 0x10), indata, i+1, targets)
|
|
elif spl[0] == '.data':
|
|
i = asdata(outdata, int(spl[1], 0x10), indata, i+1)
|
|
else:
|
|
raise RuntimeError('line ' + str(i) + ': ' + indata[i])
|
|
else:
|
|
i += 1
|
|
|
|
|
|
putgblogo(outdata)
|
|
putheadercsum(outdata)
|
|
putromcsum(outdata)
|
|
|
|
return outdata
|
|
|
|
def readDataFromFile(filename):
|
|
infile = open(filename, 'r')
|
|
indata = [line.strip() for line in infile]
|
|
infile.close()
|
|
return indata
|
|
|
|
def writeDataToFile(filename, data):
|
|
outfile = open(filename, 'wb')
|
|
outfile.write(data)
|
|
outfile.close()
|
|
|
|
def outFilenameFromInFilename(inname, h143):
|
|
return inname.rsplit('.', 1)[0] + ('.gbc' if (h143 & 0x80) else '.gb')
|
|
|
|
def main():
|
|
for arg in sys.argv[1:]:
|
|
print 'processing ' + arg
|
|
outdata = assembleFile(readDataFromFile(arg))
|
|
writeDataToFile(outFilenameFromInFilename(arg, outdata[0x143]), outdata)
|
|
|
|
print "\nassembled " + str(len(sys.argv) - 1) + (' files' if len(sys.argv) != 2 else ' file')
|
|
|
|
if __name__ == "__main__":
|
|
main()
|