Created
May 25, 2012 00:44
-
-
Save agrif/2785117 to your computer and use it in GitHub Desktop.
a python assembler / emulator for Notch's DCPU-16
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
import sys | |
from optparse import OptionParser | |
class Argument(object): | |
def __init__(self): | |
self._length = 0 | |
self._extra = 0 | |
self._repr = 'unknown' | |
self._code = 0 | |
self._cost = 0 | |
def __repr__(self): | |
return self._repr | |
def get_length(self): | |
return self._length | |
def get_code(self): | |
return self._code | |
def get_extra(self): | |
return self._extra & 0xffff | |
def get_cost(self): | |
return self._cost | |
def read(self, cpu): | |
raise NotImplementedError("read: " + self.__class__.__name__) | |
def write(self, cpu, val): | |
raise NotImplementedError("write: " + self.__class__.__name__) | |
instructions = [None, 'SET', 'ADD', 'SUB', 'MUL', 'DIV', 'MOD', 'SHL', 'SHR', 'AND', 'BOR', 'XOR', 'IFE', 'IFN', 'IFG', 'IFB'] | |
instructions_ext = [None, 'JSR'] | |
registers = ['A', 'B', 'C', 'X', 'Y', 'Z', 'I', 'J'] | |
specials = ['SP', 'PC', 'O'] | |
class RegisterArgument(Argument): | |
"""Handles A, B, C, X, Y, Z, I, and J (direct access) Also handles | |
special locations SP, PC, and O.""" | |
def __init__(self, register): | |
super(RegisterArgument, self).__init__() | |
self.register = register.upper() | |
if not self.register in registers + specials: | |
raise RuntimeError("invalid register: {0}".format(register)) | |
self._repr = self.register | |
try: | |
self._code = registers.index(self.register) | |
except ValueError: | |
self._code = 0x1b + specials.index(self.register) | |
def read(self, cpu): | |
if self.register == "SP": | |
return cpu.sp | |
elif self.register == "PC": | |
return cpu.pc | |
elif self.register == "O": | |
return cpu.o | |
else: | |
return cpu.read_register(self.register) | |
def write(self, cpu, val): | |
if self.register == "SP": | |
cpu.sp = val | |
elif self.register == "PC": | |
cpu.pc = val | |
elif self.register == "O": | |
cpu.o = val | |
else: | |
cpu.write_register(self.register, val) | |
class IndirectRegisterArgument(RegisterArgument): | |
"""Handles A, B, C, X, Y, Z, I, and J (indirect access).""" | |
def __init__(self, register): | |
super(IndirectRegisterArgument, self).__init__(register) | |
if self.register in specials: | |
raise RuntimeError("register cannot be used indirectly: {0}".format(self.register)) | |
self._repr = '[{0}]'.format(self.register) | |
self._code += 0x8 | |
def read(self, cpu): | |
addr = cpu.read_register(self.register) | |
return cpu.read(addr) | |
def write(self, cpu, val): | |
addr = cpu.read_register(self.register) | |
cpu.write(addr, val) | |
class IndirectOffsetRegisterArgument(RegisterArgument): | |
"""Handles A, B, C, X, Y, Z, I, and J (indirect access with offset).""" | |
def __init__(self, register, offset): | |
super(IndirectOffsetRegisterArgument, self).__init__(register) | |
if self.register in specials: | |
raise RuntimeError("register cannot be used indirectly: {0}".format(self.register)) | |
self.offset = offset | |
self._length += 1 | |
self._extra = offset | |
self._repr = '[{1}+{0}]'.format(self.register, self.offset) | |
self._code += 0x10 | |
self._cost += 1 | |
def read(self, cpu): | |
addr = cpu.read_register(self.register) | |
return cpu.read(addr + self.offset) | |
def write(self, cpu, val): | |
addr = cpu.read_register(self.register) | |
cpu.write(addr + self.offset, val) | |
class PopArgument(Argument): | |
"""Handles POP.""" | |
def __init__(self): | |
super(PopArgument, self).__init__() | |
self._repr = "POP" | |
self._code = 0x18 | |
def read(self, cpu): | |
val = cpu.read(cpu.sp) | |
cpu.sp += 1 | |
cpu.sp &= 0xffff | |
return val | |
def write(self, cpu, val): | |
cpu.write(cpu.sp, val) | |
cpu.sp += 1 | |
cpu.sp &= 0xffff | |
class PeekArgument(Argument): | |
"""Handles PEEK.""" | |
def __init__(self): | |
super(PeekArgument, self).__init__() | |
self._repr = "PEEK" | |
self._code = 0x19 | |
def read(self, cpu): | |
return cpu.read(cpu.sp) | |
def write(self, cpu, val): | |
cpu.write(cpu.sp, val) | |
class PushArgument(Argument): | |
"""Handles PUSH.""" | |
def __init__(self): | |
super(PushArgument, self).__init__() | |
self._repr = "PUSH" | |
self._code = 0x1a | |
def read(self, cpu): | |
cpu.sp -= 1 | |
cpu.sp &= 0xffff | |
val = cpu.read(cpu.sp) | |
return val | |
def write(self, cpu, val): | |
cpu.sp -= 1 | |
cpu.sp &= 0xffff | |
cpu.write(cpu.sp, val) | |
class LiteralArgument(Argument): | |
"""Handles literal constants, and indirect lookups to constant addresses.""" | |
def __init__(self, number, indirect=False, label=False): | |
super(LiteralArgument, self).__init__() | |
self.label = label | |
self.number = number | |
self.indirect = indirect | |
if label: | |
self.labelname = number | |
else: | |
self._extra = number | |
self._repr = "{0}".format(number) | |
self._code = 0x1f | |
if indirect: | |
self._repr = '[' + self._repr + ']' | |
self._code = 0x1e | |
self._length += 1 | |
self._cost += 1 | |
# inline small literals | |
if not label and self.number < 0x20 and self.number >= 0: | |
self._extra = 0 | |
self._code = 0x20 + self.number | |
self._length = 0 | |
self._cost = 0 | |
def read(self, cpu): | |
if self.indirect: | |
return cpu.read(self.number) | |
return self.number | |
def write(self, cpu, val): | |
if self.indirect: | |
cpu.write(self.number, val) | |
else: | |
# writing to a constant is allowed, but not useful | |
pass | |
def update_labels(self, labels, pc): | |
if not self.label: | |
return | |
if not self.labelname in labels: | |
raise RuntimeError("invalid label name: `:{0}'".format(self.labelname)) | |
self.number = labels[self.labelname] | |
self._extra = self.number | |
return # FIXME the following section breaks code | |
if self.number < 0x20 and self.number >= 0: | |
self._extra = 0 | |
self._code = 0x20 + self.number | |
self._length = 0 | |
self._cost = 0 | |
# move all the labels ahead of pc | |
for label in labels: | |
if labels[label] > pc: | |
labels[label] -= 1 | |
class Instruction(object): | |
def __init__(self, name, args): | |
self.name = name.upper() | |
if self.name and self.name not in instructions + instructions_ext: | |
raise RuntimeError('invalid instruction: {0}'.format(self.name)) | |
if self.name in instructions and len(args) != 2: | |
raise RuntimeError('instruction needs two arguments, has {1}: {0}'.format(self.name), len(args)) | |
if self.name in instructions_ext and len(args) != 1: | |
raise RuntimeError('instruction needs one arguments, has {1}: {0}'.format(self.name), len(args)) | |
self.args = args | |
def __repr__(self): | |
return "{0} {1}".format(self.name, ', '.join([repr(e) for e in self.args])) | |
def get_length(self): | |
"""Returns the instruction length, in words.""" | |
return 1 + sum([a.get_length() for a in self.args]) | |
def assemble(self): | |
"""Returns the byte code for this instruction.""" | |
code = 0 | |
try: | |
i = instructions_ext.index(self.name) | |
code |= (i << 4) | |
code |= (self.args[0].get_code() << 10) | |
except ValueError: | |
i = instructions.index(self.name) | |
code |= i | |
code |= (self.args[0].get_code() << 4) | |
code |= (self.args[1].get_code() << 10) | |
code = chr((code & 0xff00) >> 8) + chr(code & 0x00ff) | |
for arg in self.args: | |
if arg.get_length() == 0: | |
continue | |
extra = arg.get_extra() | |
code += chr((extra & 0xff00) >> 8) + chr(extra & 0x00ff) | |
return code | |
def execute(self, cpu): | |
"""Returns how many cpu cycles this instruction takes.""" | |
argcost = sum([a.get_cost() for a in self.args]) | |
if self.name == "SET": | |
self.args[0].write(cpu, self.args[1].read(cpu) & 0xffff) | |
return 1 + argcost | |
elif self.name == "ADD": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
a = a + b | |
self.args[0].write(cpu, a & 0xffff) | |
cpu.o = 1 if a > 0xffff else 0 | |
return 2 + argcost | |
elif self.name == "SUB": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
a = a - b | |
self.args[0].write(cpu, a & 0xffff) | |
cpu.o = 0xffff if a < 0 else 0 | |
return 2 + argcost | |
elif self.name == "MUL": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
a = a * b | |
self.args[0].write(cpu, a & 0xffff) | |
cpu.o = (a >> 16) & 0xffff | |
return 2 + argcost | |
elif self.name == "DIV": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
if b == 0: | |
self.args[0].write(cpu, 0) | |
cpu.o = 0 | |
else: | |
res = a / b | |
self.args[0].write(cpu, res & 0xffff) | |
cpu.o = ((a << 16) / b) & 0xffff | |
return 3 + argcost | |
elif self.name == "MOD": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
if b == 0: | |
self.args[0].write(cpu, 0) | |
else: | |
a = a % b | |
self.args[0].write(cpu, a & 0xffff) | |
return 3 + argcost | |
elif self.name == "SHL": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
a = a << b | |
self.args[0].write(cpu, a & 0xffff) | |
cpu.o = (a >> 16) & 0xffff | |
return 2 + argcost | |
elif self.name == "SHR": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
res = a >> b | |
self.args[0].write(cpu, res & 0xffff) | |
cpu.o = ((a << 16) >> b) & 0xffff | |
return 2 + argcost | |
elif self.name == "AND": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
a = a & b | |
self.args[0].write(cpu, a & 0xffff) | |
return 1 + argcost | |
elif self.name == "BOR": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
a = a | b | |
self.args[0].write(cpu, a & 0xffff) | |
return 1 + argcost | |
elif self.name == "XOR": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
a = a ^ b | |
self.args[0].write(cpu, a & 0xffff) | |
return 1 + argcost | |
elif self.name == "IFE": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
if a != b: | |
cpu.read_instruction() | |
argcost += 1 | |
return 2 + argcost | |
elif self.name == "IFN": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
if a == b: | |
cpu.read_instruction() | |
argcost += 1 | |
return 2 + argcost | |
elif self.name == "IFG": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
if a <= b: | |
cpu.read_instruction() | |
argcost += 1 | |
return 2 + argcost | |
elif self.name == "IFB": | |
a = self.args[0].read(cpu) | |
b = self.args[1].read(cpu) | |
if a & b == 0: | |
cpu.read_instruction() | |
argcost += 1 | |
return 2 + argcost | |
elif self.name == "JSR": | |
a = self.args[0].read(cpu) | |
cpu.sp -= 1 | |
cpu.sp &= 0xffff | |
cpu.write(cpu.sp, cpu.pc) | |
cpu.pc = a & 0xffff | |
return 2 + argcost | |
raise RuntimeError("unhandled instruction: {0}".format(self.name)) | |
class ParseError(Exception): | |
pass | |
def parse_int(i): | |
"""Nice integer literal parsing.""" | |
if i.startswith('0x'): | |
return int(i, 16) | |
return int(i) | |
def parse_argument(a, label_args, pc): | |
# check for indirectness | |
indirect = False | |
if a.startswith('[') and a.endswith(']'): | |
indirect = True | |
a = a[1:-1] | |
# handle simple registers | |
if a.upper() in registers + specials: | |
if indirect: | |
return IndirectRegisterArgument(a) | |
return RegisterArgument(a) | |
if indirect and '+' in a: | |
# this is a register + offset argument | |
offset, register = [s.strip() for s in a.split('+')] | |
offset = parse_int(offset) | |
return IndirectOffsetRegisterArgument(register, offset) | |
if a.upper() == 'POP': | |
return PopArgument() | |
if a.upper() == 'PEEK': | |
return PeekArgument() | |
if a.upper() == 'PUSH': | |
return PushArgument() | |
# all that's left is literals | |
try: | |
return LiteralArgument(parse_int(a), indirect=indirect) | |
except ValueError: | |
# this is a label | |
arg = LiteralArgument(a, indirect=indirect, label=True) | |
label_args.append((arg, pc)) | |
return arg | |
def parse(f): | |
instructions = [] | |
label_args = [] | |
pc = 0 | |
labels = {} | |
labels_instr = {} | |
for line in f.readlines(): | |
# strip off comments | |
line = line.strip().split(';', 1)[0] | |
# get a label, if it's present | |
if line.startswith(':'): | |
line = line.split() | |
label = line[0][1:] | |
line = ' '.join(line[1:]).strip() | |
if not label: | |
raise ParseError("invalid label `:{0}'".format(label)) | |
labels[label] = pc | |
labels_instr[label] = len(instructions) | |
# normalize whitespace | |
line = ' '.join(line.split()) | |
line.strip() | |
# skip blank lines | |
if not line: | |
continue | |
# parse the line | |
op, rest = line.split(' ', 1) | |
args = [s.strip() for s in rest.split(',')] | |
instr = Instruction(op, [parse_argument(a.strip(), label_args, pc) for a in args]) | |
instructions.append(instr) | |
pc += instr.get_length() | |
# update label arguments | |
for arg, pc in label_args: | |
arg.update_labels(labels, pc) | |
return instructions, labels_instr | |
class Memory(object): | |
def __init__(self, initial=""): | |
self.memory = [0] * 0x10000 | |
for i in xrange(len(initial) / 2): | |
msb = initial[i * 2] | |
lsb = initial[i * 2 + 1] | |
self.memory[i] = ord(lsb) | (ord(msb) << 8) | |
def read(self, loc): | |
return self.memory[loc] | |
def write(self, loc, val): | |
self.memory[loc] = val | |
def read_parameter(self, loc, typ): | |
"""Returns the number of words read, and a parsed argument.""" | |
if typ >= 0 and typ < 0x08: | |
return (0, RegisterArgument(registers[typ])) | |
elif typ < 0x10: | |
return (0, IndirectRegisterArgument(registers[typ - 0x08])) | |
elif typ < 0x18: | |
offset = self.memory[loc] | |
return (1, IndirectOffsetRegisterArgument(registers[typ - 0x10], offset)) | |
elif typ == 0x18: | |
return (0, PopArgument()) | |
elif typ == 0x19: | |
return (0, PeekArgument()) | |
elif typ == 0x1a: | |
return (0, PushArgument()) | |
elif typ <= 0x1d: | |
return (0, RegisterArgument(specials[typ - 0x1b])) | |
elif typ == 0x1e: | |
word = self.memory[loc] | |
return (1, LiteralArgument(word, indirect=True)) | |
elif typ == 0x1f: | |
word = self.memory[loc] | |
return (1, LiteralArgument(word, indirect=False)) | |
elif typ <= 0x3f: | |
return (0, LiteralArgument(typ - 0x20, indirect=False)) | |
raise RuntimeError("invalid value code: {0}".format(typ)) | |
def read_instruction(self, loc): | |
"""Returns the number of words read, and the instruction.""" | |
opcode = self.memory[loc] | |
read = 1 | |
o = 0x000f & opcode | |
a = (0x03f0 & opcode) >> 4 | |
b = (0xfc00 & opcode) >> 10 | |
if o == 0: | |
# extended code | |
instr = instructions_ext[a] | |
size, arg = self.read_parameter(loc + read, b) | |
read += size | |
return (read, Instruction(instr, [arg])) | |
else: | |
# normal code | |
instr = instructions[o] | |
size, arg1 = self.read_parameter(loc + read, a) | |
read += size | |
size, arg2 = self.read_parameter(loc + read, b) | |
read += size | |
return (read, Instruction(instr, [arg1, arg2])) | |
class CPU(object): | |
def __init__(self, memory): | |
self.memory = memory | |
self.registers = [0] * len(registers) | |
self.sp = 0 | |
self.pc = 0 | |
self.o = 0 | |
def __repr__(self): | |
s = "<CPU SP={0} PC={1} O={2}".format(self.sp, self.pc, self.o) | |
for i, val in enumerate(self.registers): | |
s += " {0}={1}".format(registers[i], val) | |
return s + ">" | |
def _get_register_index(self, reg): | |
return registers.index(reg) | |
def read(self, loc): | |
return self.memory.read(loc) | |
def read_register(self, reg): | |
return self.registers[self._get_register_index(reg)] | |
def read_instruction(self): | |
read, instr = self.memory.read_instruction(self.pc) | |
self.pc += read | |
return instr | |
def write(self, loc, val): | |
self.memory.write(loc, val) | |
def write_register(self, reg, val): | |
self.registers[self._get_register_index(reg)] = val | |
def main(): | |
parser = OptionParser(usage="usage: %prog [options] FILE [FILE ...]") | |
parser.add_option("-x", "--execute", action="store_true", dest="execute", help="execute the given file, instead of compiling") | |
parser.add_option("-b", "--binary", action="store_true", dest="binary", help="treat the input as a compiled binary. This will decompile the file, or execute it, depending on whether -x was given.") | |
parser.add_option("-o", "--output", dest="output", help="write all output to FILE", metavar="FILE") | |
options, args = parser.parse_args() | |
if len(args) > 1: | |
# FIXME | |
sys.stderr.write("multiple files are not yet supported\n") | |
return 1 | |
if len(args) == 0: | |
sys.stderr.write("you must provide at least one file\n") | |
sys.stderr.write("see --help for more info\n") | |
return 1 | |
infile = None | |
outfile = sys.stdout | |
if args[0] == '-': | |
infile = sys.stdin | |
else: | |
infile = open(args[0], 'r') | |
if not options.execute and not options.binary and not options.output: | |
outfile = open(args[0] + '.bin', 'wb') | |
if options.output: | |
outfile = open(options.output, 'w' if (options.execute or options.binary) else 'wb') | |
assert infile | |
assert outfile | |
if options.execute: | |
if options.binary: | |
bytecode = infile.read() | |
else: | |
i, _ = parse(infile) | |
bytecode = ''.join([el.assemble() for el in i]) | |
infile.close() | |
mem = Memory(bytecode) | |
cpu = CPU(mem) | |
oldpc = -1 | |
cycles = 0 | |
while cpu.pc != oldpc: | |
oldpc = cpu.pc | |
instr = cpu.read_instruction() | |
cycles += instr.execute(cpu) | |
outfile.write(repr(instr) + " ----> " + repr(cpu) + "\n") | |
outfile.write("\nhalted in {0} cycles\n".format(cycles)) | |
outfile.close() | |
return 0 | |
else: | |
if options.binary: | |
# decompile | |
bytecode = infile.read() | |
mem = Memory(bytecode) | |
pc = 0 | |
while pc < len(bytecode) / 2: | |
read, instr = mem.read_instruction(pc) | |
pc += read | |
outfile.write(repr(instr) + "\n") | |
else: | |
# compile | |
i, _ = parse(infile) | |
for el in i: | |
outfile.write(el.assemble()) | |
outfile.close() | |
infile.close() | |
return 0 | |
if __name__ == "__main__": | |
sys.exit(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
:main | |
SUB SP,2 | |
SET J,SP | |
SET Z,0 | |
SET [1+J],Z | |
SET Z,0 | |
SET [J],Z | |
SET PC,_l_3 | |
:_l_2 | |
SET A,[J] | |
MUL A,10 | |
ADD A,1 | |
SET Z,A | |
ADD Z,[1+J] | |
SET [1+J],Z | |
:_l_5 | |
SET Z,[J] | |
ADD Z,1 | |
SET [J],Z | |
:_l_3 | |
IFG 10,[J] | |
SET PC,_l_2 | |
:_l_4 | |
SET A,[1+J] | |
:_l_1 | |
ADD SP,2 | |
SET PC,POP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int main() | |
{ | |
int i; | |
int gather = 0; | |
for (i = 0; i < 10; i++) | |
{ | |
gather += i * 10 + 1; | |
} | |
return gather; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; Try some basic stuff | |
SET A, 0x30 | |
SET [0x1000], 0x20 | |
SUB A, [0x1000] | |
IFN A, 0x10 | |
SET PC, crash | |
;; Do a loopy thing | |
SET I, 10 | |
SET A, 0x2000 | |
:loop SET [0x2000+I], [A] | |
SUB I, 1 | |
IFN I, 0 | |
SET PC, loop | |
;; Call a subroutine | |
SET X, 0x4 | |
JSR testsub | |
SET PC, crash | |
:testsub | |
SHL X, 4 | |
SET PC, POP | |
;; Hang forever, X should now be 0x40 if everything went right. | |
:crash SET PC, crash |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/Makefile b/Makefile | |
index 76945b2..1b598d7 100644 | |
--- a/Makefile | |
+++ b/Makefile | |
@@ -5,6 +5,7 @@ LDFLAGS = -lm | |
MKDIR = mkdir -p | |
RM = rm -f | |
INSTALL = install | |
+TARGET = dcpu16 | |
ifdef COMSPEC | |
X = .exe | |
diff --git a/frontend/vc.c b/frontend/vc.c | |
index 00d7956..4d9eea0 100644 | |
--- a/frontend/vc.c | |
+++ b/frontend/vc.c | |
@@ -6,6 +6,7 @@ | |
#include <string.h> | |
#include <stdio.h> | |
#include <stdarg.h> | |
+#include <errno.h> | |
#ifdef _WIN32 | |
#include <io.h> | |
diff --git a/machines/dcpu16/machine.c b/machines/dcpu16/machine.c | |
new file mode 100644 | |
index 0000000..063d9be | |
--- /dev/null | |
+++ b/machines/dcpu16/machine.c | |
@@ -0,0 +1,815 @@ | |
+/* vbcc backend for Markus Persson's dcpu16 */ | |
+ | |
+#include "supp.h" | |
+ | |
+static char FILE_[]=__FILE__; | |
+ | |
+/* Public data that MUST be there. */ | |
+ | |
+/* Name and copyright. */ | |
+char cg_copyright[]="vbcc dcpu16 code-generator V0.0, by Aaron Griffith\nbased on generic code-generator V0.1b (c) in 2001 by Volker Barthelmann"; | |
+ | |
+/* Commandline-flags the code-generator accepts: | |
+ 0: just a flag | |
+ VALFLAG: a value must be specified | |
+ STRINGFLAG: a string can be specified | |
+ FUNCFLAG: a function will be called | |
+ apart from FUNCFLAG, all other versions can only be specified once */ | |
+int g_flags[MAXGF]={0}; | |
+ | |
+/* the flag-name, do not use names beginning with l, L, I, D or U, because | |
+ they collide with the frontend */ | |
+char *g_flags_name[MAXGF]={NULL}; | |
+ | |
+/* the results of parsing the command-line-flags will be stored here */ | |
+union ppi g_flags_val[MAXGF]; | |
+ | |
+/* Alignment-requirements for all types in bytes. */ | |
+zmax align[MAX_TYPE+1]; | |
+ | |
+/* Alignment that is sufficient for every object. */ | |
+zmax maxalign; | |
+ | |
+/* CHAR_BIT for the target machine. */ | |
+zmax char_bit; | |
+ | |
+/* sizes of the basic types (in bytes) */ | |
+zmax sizetab[MAX_TYPE+1]; | |
+ | |
+/* Minimum and Maximum values each type can have. */ | |
+/* Must be initialized in init_cg(). */ | |
+zmax t_min[MAX_TYPE+1]; | |
+zumax t_max[MAX_TYPE+1]; | |
+zumax tu_max[MAX_TYPE+1]; | |
+ | |
+/* Names of all registers. will be initialized in init_cg(), | |
+ register number 0 is invalid, valid registers start at 1 */ | |
+char *regnames[MAXR+1] = {"noreg", "A", "B", "C", "X", "Y", "Z", "I", "J"}; | |
+ | |
+/* The Size of each register in bytes. */ | |
+zmax regsize[MAXR+1]; | |
+ | |
+/* a type which can store each register. */ | |
+struct Typ *regtype[MAXR+1]; | |
+ | |
+/* regsa[reg]!=0 if a certain register is allocated and should */ | |
+/* not be used by the compiler pass. */ | |
+int regsa[MAXR+1] = {1, 0, 0, 0, 0, 0, 1, 0, 1}; | |
+ | |
+/* Specifies which registers may be scratched by functions. */ | |
+int regscratch[MAXR+1] = {1, 1, 1, 1, 1, 1, 0, 1, 0}; | |
+ | |
+/* specifies the priority for the register-allocator, if the same | |
+ estimated cost-saving can be obtained by several registers, the | |
+ one with the highest priority will be used */ | |
+int reg_prio[MAXR+1] = {0, 3, 3, 3, 2, 2, 2, 1, 1}; | |
+ | |
+/****************************************/ | |
+/* Private data and functions. */ | |
+/****************************************/ | |
+ | |
+/* alignment of basic data-types, used to initialize align[] */ | |
+static long malign[MAX_TYPE+1]= {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; | |
+/* sizes of basic data-types, used to initialize sizetab[] */ | |
+static long msizetab[MAX_TYPE+1]={1,1,1,1,2,4,2,4,4,0,2,0,0,0,2,0}; | |
+ | |
+/* used to initialize regtyp[] */ | |
+static struct Typ ltyp={INT}; | |
+ | |
+/* special reserved registers */ | |
+static int sp = 8; | |
+static int t1 = 6; | |
+static int f1 = 0; /* BROKEN floats */ | |
+ | |
+#define dt(t) (((t)&UNSIGNED)?udt[(t)&NQ]:sdt[(t)&NQ]) | |
+static char *sdt[MAX_TYPE+1]={"??","c","s","i","l","ll","f","d","ld","v","p"}; | |
+static char *udt[MAX_TYPE+1]={"??","uc","us","ui","ul","ull","f","d","ld","v","p"}; | |
+ | |
+static long stack; | |
+static int stack_valid; | |
+ | |
+/* return-instruction */ | |
+static char *ret; | |
+ | |
+/* how many branch labels we've used */ | |
+static long num_branches = 0; | |
+ | |
+/* label at the end of the function (if any) */ | |
+static int exit_label; | |
+ | |
+/* assembly-prefixes for labels and external identifiers */ | |
+static char *labprefix="_l_",*idprefix=""; | |
+ | |
+/* variables to keep track of the current stack-offset in the case of | |
+ a moving stack-pointer */ | |
+static long localsize,argsize; | |
+ | |
+static void emit_obj(FILE *f,struct obj *p,int t); | |
+ | |
+/* calculate the actual current offset of an object relativ to the | |
+ stack-pointer; we use a layout like this: | |
+ ------------------------------------------------ | |
+ | arguments to this function | | |
+ ------------------------------------------------ | |
+ | return-address [size=1] | | |
+ ------------------------------------------------ | |
+ | local variables [size=localsize] | | |
+ ------------------------------------------------ <-- %sp | |
+ | arguments to called functions [size=argsize] | | |
+ ------------------------------------------------ <-- SP | |
+ All sizes will be aligned as necessary. | |
+ In the case of FIXED_SP, the stack-pointer will be adjusted at | |
+ function-entry to leave enough space for the arguments and have it | |
+ aligned to 16 bytes. Therefore, when calling a function, the | |
+ stack-pointer is always aligned to 16 bytes. | |
+ For a moving stack-pointer, the stack-pointer will usually point | |
+ to the bottom of the area for local variables, but will move while | |
+ arguments are put on the stack. | |
+ | |
+ This is just an example layout. Other layouts are also possible. | |
+*/ | |
+ | |
+static long real_offset(struct obj *o) | |
+{ | |
+ long off=zm2l(o->v->offset); | |
+ if(off<0){ | |
+ /* function parameter */ | |
+ off=localsize+1-off-zm2l(maxalign); | |
+ } | |
+ | |
+ off+=zm2l(o->val.vmax); | |
+ return off; | |
+} | |
+ | |
+/* Initializes an addressing-mode structure and returns a pointer to | |
+ that object. Will not survive a second call! */ | |
+static struct obj *cam(int flags,int base,long offset) | |
+{ | |
+ static struct obj obj; | |
+ static struct AddressingMode am; | |
+ obj.am=&am; | |
+ return &obj; | |
+} | |
+ | |
+/* generate code to load the address of a variable into register r */ | |
+static void load_address(FILE *f,int r,struct obj *o,int type) | |
+/* Generates code to load the address of a variable into register r. */ | |
+{ | |
+ if(!(o->flags&VAR)) ierror(0); | |
+ if(o->v->storage_class==AUTO||o->v->storage_class==REGISTER){ | |
+ long off=real_offset(o); | |
+ emit(f,"\tSET\t%s,%s\n",regnames[r],regnames[sp]); | |
+ if(off) | |
+ emit(f,"\tADD\t%s,%ld\n",regnames[r],off); | |
+ }else{ | |
+ emit(f,"\tSET\t%s,",regnames[r]); | |
+ emit_obj(f,o,type); | |
+ emit(f,"\n"); | |
+ } | |
+} | |
+/* Generates code to load a memory object into register r. tmp is a | |
+ general purpose register which may be used. tmp can be r. */ | |
+static void load_reg(FILE *f,int r,struct obj *o,int type) | |
+{ | |
+ type&=NU; | |
+ if(o->flags&VARADR){ | |
+ load_address(f,r,o,POINTER); | |
+ }else{ | |
+ if((o->flags&(REG|DREFOBJ))==REG&&o->reg==r) | |
+ return; | |
+ emit(f,"\tSET\t%s,",regnames[r]); | |
+ emit_obj(f,o,type); | |
+ emit(f,"\n"); | |
+ } | |
+} | |
+ | |
+/* Generates code to store register r into memory object o. */ | |
+static void store_reg(FILE *f,int r,struct obj *o,int type) | |
+{ | |
+ type&=NQ; | |
+ emit(f,"\tSET\t"); | |
+ emit_obj(f,o,type); | |
+ emit(f,",%s\n",regnames[r]); | |
+} | |
+ | |
+/* Yields log2(x)+1 or 0. */ | |
+static long pof2(zumax x) | |
+{ | |
+ zumax p;int ln=1; | |
+ p=ul2zum(1L); | |
+ while(ln<=32&&zumleq(p,x)){ | |
+ if(zumeqto(x,p)) return ln; | |
+ ln++;p=zumadd(p,p); | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static struct IC *preload(FILE *,struct IC *); | |
+ | |
+static void function_top(FILE *,struct Var *,long); | |
+static void function_bottom(FILE *f,struct Var *,long); | |
+ | |
+#define isreg(x) ((p->x.flags&(REG|DREFOBJ))==REG) | |
+#define isconst(x) ((p->x.flags&(KONST|DREFOBJ))==KONST) | |
+ | |
+static int q1reg,q2reg,zreg; | |
+ | |
+static char *logicals[]={"BOR","XOR","AND"}; | |
+static char *arithmetics[]={"SHL","SHR","ADD","SUB","MUL","DIV","MOD"}; | |
+ | |
+/* Does some pre-processing like fetching operands from memory to | |
+ registers etc. */ | |
+static struct IC *preload(FILE *f,struct IC *p) | |
+{ | |
+ int r; | |
+ | |
+ if(isreg(q1)) | |
+ q1reg=p->q1.reg; | |
+ else | |
+ q1reg=0; | |
+ | |
+ if(isreg(q2)) | |
+ q2reg=p->q2.reg; | |
+ else | |
+ q2reg=0; | |
+ | |
+ if(isreg(z)){ | |
+ zreg=p->z.reg; | |
+ }else{ | |
+ if(ISFLOAT(ztyp(p))) | |
+ zreg=f1; | |
+ else | |
+ zreg=t1; | |
+ } | |
+ | |
+ if((p->q1.flags&(DREFOBJ|REG))==DREFOBJ&&!p->q1.am){ | |
+ p->q1.flags&=~DREFOBJ; | |
+ load_reg(f,t1,&p->q1,q1typ(p)); | |
+ p->q1.reg=t1; | |
+ p->q1.flags|=(REG|DREFOBJ); | |
+ } | |
+ | |
+ if((p->q2.flags&(DREFOBJ|REG))==DREFOBJ&&!p->q2.am){ | |
+ p->q2.flags&=~DREFOBJ; | |
+ load_reg(f,t1,&p->q2,q2typ(p)); | |
+ p->q2.reg=t1; | |
+ p->q2.flags|=(REG|DREFOBJ); | |
+ } | |
+ | |
+ return p; | |
+} | |
+ | |
+/* save the result (in zreg) into p->z */ | |
+void save_result(FILE *f,struct IC *p) | |
+{ | |
+ if((p->z.flags&(REG|DREFOBJ))==DREFOBJ&&!p->z.am){ | |
+ p->z.flags&=~DREFOBJ; | |
+ load_reg(f,t1,&p->z,POINTER); | |
+ p->z.reg=t1; | |
+ p->z.flags|=(REG|DREFOBJ); | |
+ } | |
+ if(isreg(z)){ | |
+ if(p->z.reg!=zreg) | |
+ emit(f,"\tmov.%s\t%s,%s\n",dt(ztyp(p)),regnames[p->z.reg],regnames[zreg]); | |
+ }else{ | |
+ store_reg(f,zreg,&p->z,ztyp(p)); | |
+ } | |
+} | |
+ | |
+/* prints an object */ | |
+static void emit_obj(FILE *f,struct obj *p,int t) | |
+{ | |
+ if((p->flags&(KONST|DREFOBJ))==(KONST|DREFOBJ)){ | |
+ emitval(f,&p->val,p->dtyp&NU); | |
+ return; | |
+ } | |
+ if(p->flags&DREFOBJ) emit(f,"["); | |
+ if(p->flags®){ | |
+ emit(f,"%s",regnames[p->reg]); | |
+ }else if(p->flags&VAR) { | |
+ if(p->v->storage_class==AUTO||p->v->storage_class==REGISTER){ | |
+ long off = real_offset(p); | |
+ if (off > 0) | |
+ emit(f,"[%ld+%s]",off,regnames[sp]); | |
+ else | |
+ emit(f, "[%s]",regnames[sp]); | |
+ } | |
+ else{ | |
+ if(!zmeqto(l2zm(0L),p->val.vmax)){emitval(f,&p->val,LONG);emit(f,"+");} | |
+ if(p->v->storage_class==STATIC){ | |
+ emit(f,"%s%ld",labprefix,zm2l(p->v->offset)); | |
+ }else{ | |
+ emit(f,"%s%s",idprefix,p->v->identifier); | |
+ } | |
+ } | |
+ } | |
+ if(p->flags&KONST){ | |
+ emitval(f,&p->val,t&NU); | |
+ } | |
+ if(p->flags&DREFOBJ) emit(f,"]"); | |
+} | |
+ | |
+/* Test if there is a sequence of FREEREGs containing FREEREG reg. | |
+ Used by peephole. */ | |
+static int exists_freereg(struct IC *p,int reg) | |
+{ | |
+ while(p&&(p->code==FREEREG||p->code==ALLOCREG)){ | |
+ if(p->code==FREEREG&&p->q1.reg==reg) return 1; | |
+ p=p->next; | |
+ } | |
+ return 0; | |
+} | |
+ | |
+/* generates the function entry code */ | |
+static void function_top(FILE *f,struct Var *v,long offset) | |
+{ | |
+ if(v->storage_class==EXTERN){ | |
+ emit(f,":%s%s\n",idprefix,v->identifier); | |
+ }else | |
+ emit(f,":%s%ld\n",labprefix,zm2l(v->offset)); | |
+ | |
+ /* allocate local variable space */ | |
+ if (offset > 0) | |
+ emit(f, "\tSUB\tSP,%ld\n", offset); | |
+ emit(f, "\tSET\t%s,SP\n",regnames[sp]); | |
+} | |
+/* generates the function exit code */ | |
+static void function_bottom(FILE *f,struct Var *v,long offset) | |
+{ | |
+ emit(f,ret); | |
+} | |
+ | |
+/****************************************/ | |
+/* End of private data and functions. */ | |
+/****************************************/ | |
+ | |
+/* Does necessary initializations for the code-generator. Gets called */ | |
+/* once at the beginning and should return 0 in case of problems. */ | |
+int init_cg(void) | |
+{ | |
+ int i; | |
+ /* Initialize some values which cannot be statically initialized */ | |
+ /* because they are stored in the target's arithmetic. */ | |
+ maxalign=l2zm(1L); | |
+ char_bit=l2zm(16L); | |
+ | |
+ for(i=0;i<=MAX_TYPE;i++){ | |
+ sizetab[i]=l2zm(msizetab[i]); | |
+ align[i]=l2zm(malign[i]); | |
+ } | |
+ | |
+ for(i=1;i<=MAXR;i++){ | |
+ regsize[i]=l2zm(2L); | |
+ regtype[i]=<yp; | |
+ } | |
+ | |
+ /* Use multiple ccs. */ | |
+ multiple_ccs=0; | |
+ | |
+ /* Initialize the min/max-settings. Note that the types of the */ | |
+ /* host system may be different from the target system and you may */ | |
+ /* only use the smallest maximum values ANSI guarantees if you */ | |
+ /* want to be portable. */ | |
+ /* That's the reason for the subtraction in t_min[INT]. Long could */ | |
+ /* be unable to represent -2147483648 on the host system. */ | |
+ t_min[CHAR]=l2zm(-128L); | |
+ t_min[SHORT]=l2zm(-32768L); | |
+ t_min[SHORT]=l2zm(-32768L); | |
+ t_min[INT]=l2zm(-32768L); | |
+ t_min[LONG]=zmsub(l2zm(-2147483647L),l2zm(1L)); | |
+ t_min[LLONG]=zmlshift(l2zm(1L),l2zm(63L)); | |
+ t_min[MAXINT]=t_min(LLONG); | |
+ t_max[CHAR]=ul2zum(127L); | |
+ t_max[SHORT]=ul2zum(32767UL); | |
+ t_max[INT]=ul2zum(32767UL); | |
+ t_max[LONG]=ul2zum(2147483647UL); | |
+ t_max[LLONG]=zumrshift(zumkompl(ul2zum(0UL)),ul2zum(1UL)); | |
+ t_max[MAXINT]=t_max(LLONG); | |
+ tu_max[CHAR]=ul2zum(255UL); | |
+ tu_max[SHORT]=ul2zum(65535UL); | |
+ tu_max[INT]=ul2zum(65535UL); | |
+ tu_max[LONG]=ul2zum(4294967295UL); | |
+ tu_max[LLONG]=zumkompl(ul2zum(0UL)); | |
+ tu_max[MAXINT]=t_max(UNSIGNED|LLONG); | |
+ | |
+ return 1; | |
+} | |
+ | |
+void init_db(FILE *f) | |
+{ | |
+} | |
+ | |
+int freturn(struct Typ *t) | |
+/* Returns the register in which variables of type t are returned. */ | |
+/* If the value cannot be returned in a register returns 0. */ | |
+/* A pointer MUST be returned in a register. The code-generator */ | |
+/* has to simulate a pseudo register if necessary. */ | |
+{ | |
+ if(ISFLOAT(t->flags)) | |
+ return 0; | |
+ if(ISSTRUCT(t->flags)||ISUNION(t->flags)) | |
+ return 0; | |
+ if(zmleq(szof(t),l2zm(4L))) | |
+ return 1; | |
+ else | |
+ return 0; | |
+} | |
+ | |
+int reg_pair(int r,struct rpair *p) | |
+/* Returns 0 if the register is no register pair. If r */ | |
+/* is a register pair non-zero will be returned and the */ | |
+/* structure pointed to p will be filled with the two */ | |
+/* elements. */ | |
+{ | |
+ return 0; | |
+} | |
+ | |
+int regok(int r,int t,int mode) | |
+/* Returns 0 if register r cannot store variables of */ | |
+/* type t. If t==POINTER and mode!=0 then it returns */ | |
+/* non-zero only if the register can store a pointer */ | |
+/* and dereference a pointer to mode. */ | |
+{ | |
+ if(r==0) | |
+ return 0; | |
+ t&=NQ; | |
+ if(t==POINTER&&r>=1&&r<=MAXR) | |
+ return 1; | |
+ if(t>=CHAR&&t<=LONG&&r>=1&&r<=MAXR) | |
+ return 1; | |
+ return 0; | |
+} | |
+ | |
+int dangerous_IC(struct IC *p) | |
+/* Returns zero if the IC p can be safely executed */ | |
+/* without danger of exceptions or similar things. */ | |
+/* vbcc may generate code in which non-dangerous ICs */ | |
+/* are sometimes executed although control-flow may */ | |
+/* never reach them (mainly when moving computations */ | |
+/* out of loops). */ | |
+/* Typical ICs that generate exceptions on some */ | |
+/* machines are: */ | |
+/* - accesses via pointers */ | |
+/* - division/modulo */ | |
+/* - overflow on signed integer/floats */ | |
+{ | |
+ int c=p->code; | |
+ if((p->q1.flags&DREFOBJ)||(p->q2.flags&DREFOBJ)||(p->z.flags&DREFOBJ)) | |
+ return 1; | |
+ if((c==DIV||c==MOD)&&!isconst(q2)) | |
+ return 1; | |
+ return 0; | |
+} | |
+ | |
+int must_convert(int o,int t,int const_expr) | |
+/* Returns zero if code for converting np to type t */ | |
+/* can be omitted. */ | |
+/* On the PowerPC cpu pointers and 32bit */ | |
+/* integers have the same representation and can use */ | |
+/* the same registers. */ | |
+{ | |
+ int op=o&NQ,tp=t&NQ; | |
+ if((op==INT||op==LONG||op==POINTER)&&(tp==INT||tp==LONG||tp==POINTER)) | |
+ return 0; | |
+ if(op==DOUBLE&&tp==LDOUBLE) return 0; | |
+ if(op==LDOUBLE&&tp==DOUBLE) return 0; | |
+ return 1; | |
+} | |
+ | |
+void gen_ds(FILE *f,zmax size,struct Typ *t) | |
+/* This function has to create <size> bytes of storage */ | |
+/* initialized with zero. */ | |
+{ | |
+ emit(f,"\t.space\t%ld\n",zm2l(size)); | |
+} | |
+ | |
+void gen_align(FILE *f,zmax align) | |
+/* This function has to make sure the next data is */ | |
+/* aligned to multiples of <align> bytes. */ | |
+{ | |
+ if(zm2l(align)>1) emit(f,"\t.align\t2\n"); | |
+} | |
+ | |
+void gen_var_head(FILE *f,struct Var *v) | |
+/* This function has to create the head of a variable */ | |
+/* definition, i.e. the label and information for */ | |
+/* linkage etc. */ | |
+{ | |
+ int constflag;char *sec; | |
+ if(v->clist) constflag=is_const(v->vtyp); | |
+ if(v->storage_class==STATIC){ | |
+ if(ISFUNC(v->vtyp->flags)) return; | |
+ emit(f,"\t.lcomm\t%s%ld,",labprefix,zm2l(v->offset)); | |
+ } | |
+ if(v->storage_class==EXTERN){ | |
+ emit(f,"\t.globl\t%s%s\n",idprefix,v->identifier); | |
+ if(v->flags&(DEFINED|TENTATIVE)){ | |
+ emit(f,"\t.global\t%s%s\n\t.%scomm\t%s%s,",idprefix,v->identifier,"l",idprefix,v->identifier); | |
+ } | |
+ } | |
+} | |
+ | |
+void gen_dc(FILE *f,int t,struct const_list *p) | |
+/* This function has to create static storage */ | |
+/* initialized with const-list p. */ | |
+{ | |
+ emit(f,"\tdc.%s\t",dt(t&NQ)); | |
+ if(!p->tree){ | |
+ if(ISFLOAT(t)){ | |
+ /* auch wieder nicht sehr schoen und IEEE noetig */ | |
+ unsigned char *ip; | |
+ ip=(unsigned char *)&p->val.vdouble; | |
+ emit(f,"0x%02x%02x%02x%02x",ip[0],ip[1],ip[2],ip[3]); | |
+ if((t&NQ)!=FLOAT){ | |
+ emit(f,",0x%02x%02x%02x%02x",ip[4],ip[5],ip[6],ip[7]); | |
+ } | |
+ }else{ | |
+ emitval(f,&p->val,t&NU); | |
+ } | |
+ }else{ | |
+ emit_obj(f,&p->tree->o,t&NU); | |
+ } | |
+ emit(f,"\n"); | |
+} | |
+ | |
+ | |
+/* The main code-generation routine. */ | |
+/* f is the stream the code should be written to. */ | |
+/* p is a pointer to a doubly linked list of ICs */ | |
+/* containing the function body to generate code for. */ | |
+/* v is a pointer to the function. */ | |
+/* offset is the size of the stackframe the function */ | |
+/* needs for local variables. */ | |
+ | |
+void gen_code(FILE *f,struct IC *p,struct Var *v,zmax offset) | |
+/* The main code-generation. */ | |
+{ | |
+ int c,t,i; | |
+ struct IC *m; | |
+ struct IC *last_cmp = NULL; | |
+ int last_cmp_t, last_cmp_test; | |
+ char ret_buffer[256]; | |
+ argsize=0; | |
+ if(DEBUG&1) printf("gen_code()\n"); | |
+ for(c=1;c<=MAXR;c++) regs[c]=regsa[c]; | |
+ | |
+ for(m=p;m;m=m->next){ | |
+ c=m->code;t=m->typf&NU; | |
+ if(c==ALLOCREG) {regs[m->q1.reg]=1;continue;} | |
+ if(c==FREEREG) {regs[m->q1.reg]=0;continue;} | |
+ | |
+ /* convert MULT/DIV/MOD with powers of two */ | |
+ if((t&NQ)<=LONG&&(m->q2.flags&(KONST|DREFOBJ))==KONST&&(t&NQ)<=LONG&&(c==MULT||((c==DIV||c==MOD)&&(t&UNSIGNED)))){ | |
+ eval_const(&m->q2.val,t); | |
+ i=pof2(vmax); | |
+ if(i){ | |
+ if(c==MOD){ | |
+ vmax=zmsub(vmax,l2zm(1L)); | |
+ m->code=AND; | |
+ }else{ | |
+ vmax=l2zm(i-1); | |
+ if(c==DIV) m->code=RSHIFT; else m->code=LSHIFT; | |
+ } | |
+ c=m->code; | |
+ gval.vmax=vmax; | |
+ eval_const(&gval,MAXINT); | |
+ if(c==AND){ | |
+ insert_const(&m->q2.val,t); | |
+ }else{ | |
+ insert_const(&m->q2.val,INT); | |
+ p->typf2=INT; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ for(c=1;c<=MAXR;c++){ | |
+ if(regsa[c]||regused[c]){ | |
+ BSET(regs_modified,c); | |
+ } | |
+ } | |
+ | |
+ localsize=zm2l(offset); | |
+ | |
+ function_top(f,v,localsize); | |
+ | |
+ /* clear out the local variables on return */ | |
+ ret = ret_buffer; | |
+ if (localsize > 0) | |
+ sprintf(ret, "\tADD\tSP,%ld\n\tSET\tPC,POP\n", localsize); | |
+ else | |
+ ret = "\tSET\tPC,POP\n"; | |
+ | |
+ for(;p;p=p->next){ | |
+ c=p->code;t=p->typf; | |
+ if(c==NOP) {p->z.flags=0;continue;} | |
+ if(c==ALLOCREG) {regs[p->q1.reg]=1;continue;} | |
+ if(c==FREEREG) {regs[p->q1.reg]=0;continue;} | |
+ if(c==LABEL) {emit(f,":%s%d\n",labprefix,t);continue;} | |
+ if(c==BRA){ | |
+ if(t==exit_label){ | |
+ | |
+ emit(f,ret); | |
+ }else | |
+ emit(f,"\tSET\tPC,%s%d\n",labprefix,t); | |
+ continue; | |
+ } | |
+ if(c>=BEQ&&c<BRA){ | |
+ if (last_cmp == NULL) ierror(0); | |
+ if (c == BEQ || c == BNE || c == BGT) { | |
+ if (c == BEQ) emit(f, "\tIFE\t"); | |
+ if (c == BNE) emit(f, "\tIFN\t"); | |
+ if (c == BGT) emit(f, "\tIFG\t"); | |
+ emit_obj(f, &last_cmp->q1, last_cmp_t); | |
+ emit(f, ","); | |
+ if (last_cmp_test) | |
+ emit(f, "0"); | |
+ else | |
+ emit_obj(f, &last_cmp->q2, last_cmp_t); | |
+ emit(f, "\n\tSET\tPC,%s%d\n", labprefix, t); | |
+ } else if (c == BLT) { | |
+ emit(f, "\tIFG\t"); | |
+ if (last_cmp_test) | |
+ emit(f, "0"); | |
+ else | |
+ emit_obj(f, &last_cmp->q2, last_cmp_t); | |
+ emit(f, ","); | |
+ emit_obj(f, &last_cmp->q1, last_cmp_t); | |
+ emit(f, "\n\tSET\tPC,%s%d\n", labprefix, t); | |
+ } else if (c == BLE || c == BGE) { | |
+ emit(f, "\tIFG\t"); | |
+ if (c == BLE) { | |
+ emit_obj(f, &last_cmp->q1, last_cmp_t); | |
+ emit(f, ","); | |
+ if (last_cmp_test) | |
+ emit(f, "0"); | |
+ else | |
+ emit_obj(f, &last_cmp->q2, last_cmp_t); | |
+ } else if (c == BGE) { | |
+ if (last_cmp_test) | |
+ emit(f, "0"); | |
+ else | |
+ emit_obj(f, &last_cmp->q2, last_cmp_t); | |
+ emit(f, ","); | |
+ emit_obj(f, &last_cmp->q1, last_cmp_t); | |
+ } | |
+ // jump forward one instruction | |
+ emit(f, "\n\tSET\tPC,%sbranch%ld\n", labprefix, num_branches); | |
+ emit(f, "\n\tSET\tPC,%s%d\n", labprefix, t); | |
+ emit(f, ":%sbranch%ld\n", labprefix, num_branches); | |
+ num_branches++; | |
+ } | |
+ continue; | |
+ } | |
+ if(c==MOVETOREG){ | |
+ load_reg(f,p->z.reg,&p->q1,regtype[p->z.reg]->flags); | |
+ continue; | |
+ } | |
+ if(c==MOVEFROMREG){ | |
+ store_reg(f,p->z.reg,&p->q1,regtype[p->z.reg]->flags); | |
+ continue; | |
+ } | |
+ if((c==ASSIGN||c==PUSH)&&((t&NQ)>POINTER||((t&NQ)==CHAR&&zm2l(p->q2.val.vmax)!=1))){ | |
+ ierror(0); | |
+ } | |
+ p=preload(f,p); | |
+ c=p->code; | |
+ if(c==SUBPFP) c=SUB; | |
+ if(c==ADDI2P) c=ADD; | |
+ if(c==SUBIFP) c=SUB; | |
+ if(c==CONVERT){ | |
+ if(ISFLOAT(q1typ(p))||ISFLOAT(ztyp(p))) ierror(0); | |
+ if(sizetab[q1typ(p)&NQ]<sizetab[ztyp(p)&NQ]){ | |
+ if(q1typ(p)&UNSIGNED) | |
+ emit(f,"\tzext.%s\t%s\n",dt(q1typ(p)),regnames[zreg]); | |
+ else | |
+ emit(f,"\tsext.%s\t%s\n",dt(q1typ(p)),regnames[zreg]); | |
+ } | |
+ save_result(f,p); | |
+ continue; | |
+ } | |
+ if(c==KOMPLEMENT){ | |
+ load_reg(f,zreg,&p->q1,t); | |
+ emit(f,"\tcpl.%s\t%s\n",dt(t),regnames[zreg]); | |
+ save_result(f,p); | |
+ continue; | |
+ } | |
+ if(c==SETRETURN){ | |
+ load_reg(f,p->z.reg,&p->q1,t); | |
+ BSET(regs_modified,p->z.reg); | |
+ continue; | |
+ } | |
+ if(c==GETRETURN){ | |
+ if(p->q1.reg){ | |
+ zreg=p->q1.reg; | |
+ save_result(f,p); | |
+ }else | |
+ p->z.flags=0; | |
+ continue; | |
+ } | |
+ if(c==CALL){ | |
+ int reg; | |
+ /*FIXME*/ | |
+#if 0 | |
+ if(stack_valid&&(p->q1.flags&(VAR|DREFOBJ))==VAR&&p->q1.v->fi&&(p->q1.v->fi->flags&ALL_STACK)){ | |
+ if(framesize+zum2ul(p->q1.v->fi->stack1)>stack) | |
+ stack=framesize+zum2ul(p->q1.v->fi->stack1); | |
+ }else | |
+ stack_valid=0; | |
+#endif | |
+ if((p->q1.flags&(VAR|DREFOBJ))==VAR&&p->q1.v->fi&&p->q1.v->fi->inline_asm){ | |
+ emit_inline_asm(f,p->q1.v->fi->inline_asm); | |
+ }else{ | |
+ emit(f,"\tJSR\t"); | |
+ emit_obj(f,&p->q1,t); | |
+ emit(f,"\n"); | |
+ if (argsize > 0) | |
+ emit(f, "\tADD\tSP,%ld\n", argsize); | |
+ emit(f, "\tSET\t%s,SP\n",regnames[sp]); | |
+ argsize = 0; | |
+ } | |
+ if((p->q1.flags&(VAR|DREFOBJ))==VAR&&p->q1.v->fi&&(p->q1.v->fi->flags&ALL_REGS)){ | |
+ bvunite(regs_modified,p->q1.v->fi->regs_modified,RSIZE); | |
+ }else{ | |
+ int i; | |
+ for(i=1;i<=MAXR;i++){ | |
+ if(regscratch[i]) BSET(regs_modified,i); | |
+ } | |
+ } | |
+ continue; | |
+ } | |
+ if(c==ASSIGN||c==PUSH){ | |
+ if(t==0) ierror(0); | |
+ if(c==PUSH){ | |
+ emit(f,"\tSET\tPUSH,"); | |
+ emit_obj(f,&p->q1,t); | |
+ emit(f,"\n"); | |
+ argsize += 1; | |
+ continue; | |
+ } | |
+ if(c==ASSIGN){ | |
+ load_reg(f,zreg,&p->q1,t); | |
+ save_result(f,p); | |
+ } | |
+ continue; | |
+ } | |
+ if(c==ADDRESS){ | |
+ load_address(f,zreg,&p->q1,POINTER); | |
+ save_result(f,p); | |
+ continue; | |
+ } | |
+ if(c==MINUS){ | |
+ load_reg(f,zreg,&p->q1,t); | |
+ emit(f,"\tneg.%s\t%s\n",dt(t),regnames[zreg]); | |
+ save_result(f,p); | |
+ continue; | |
+ } | |
+ if(c==TEST){ | |
+ last_cmp = p; | |
+ last_cmp_t = t; | |
+ last_cmp_test = 1; | |
+ continue; | |
+ } | |
+ if(c==COMPARE){ | |
+ last_cmp = p; | |
+ last_cmp_t = t; | |
+ last_cmp_test = 0; | |
+ continue; | |
+ } | |
+ if((c>=OR&&c<=AND)||(c>=LSHIFT&&c<=MOD)){ | |
+ load_reg(f,zreg,&p->q1,t); | |
+ if(c>=OR&&c<=AND) | |
+ emit(f,"\t%s\t%s,",logicals[c-OR],regnames[zreg]); | |
+ else | |
+ emit(f,"\t%s\t%s,",arithmetics[c-LSHIFT],regnames[zreg]); | |
+ emit_obj(f,&p->q2,t); | |
+ emit(f,"\n"); | |
+ save_result(f,p); | |
+ continue; | |
+ } | |
+ pric2(stdout,p); | |
+ ierror(0); | |
+ } | |
+ function_bottom(f,v,localsize); | |
+ if(stack_valid){ | |
+ if(!v->fi) v->fi=new_fi(); | |
+ v->fi->flags|=ALL_STACK; | |
+ v->fi->stack1=stack; | |
+ } | |
+} | |
+ | |
+int shortcut(int code,int typ) | |
+{ | |
+ return 0; | |
+} | |
+ | |
+int handle_pragma(const char *s) | |
+{ | |
+} | |
+void cleanup_cg(FILE *f) | |
+{ | |
+} | |
+void cleanup_db(FILE *f) | |
+{ | |
+} | |
+ | |
diff --git a/machines/dcpu16/machine.dt b/machines/dcpu16/machine.dt | |
new file mode 100644 | |
index 0000000..97f65f6 | |
--- /dev/null | |
+++ b/machines/dcpu16/machine.dt | |
@@ -0,0 +1,14 @@ | |
+S16BSBE S16BSLE | |
+S16BUBE S16BULE | |
+S16BSBE S16BSLE | |
+S16BUBE S16BULE | |
+S16BSBE S16BSLE | |
+S16BUBE S16BULE | |
+S32BSBE S32BSLE | |
+S32BUBE S32BULE | |
+S64BSBE S64BSLE | |
+S64BUBE S64BULE | |
+S32BIEEEBE | |
+S64BIEEEBE | |
+S64BIEEEBE | |
+S16BUBE S16BULE | |
diff --git a/machines/dcpu16/machine.h b/machines/dcpu16/machine.h | |
new file mode 100644 | |
index 0000000..fadbffd | |
--- /dev/null | |
+++ b/machines/dcpu16/machine.h | |
@@ -0,0 +1,92 @@ | |
+/* vbcc backend for Markus Persson's dcpu16 */ | |
+ | |
+#include "dt.h" | |
+ | |
+/* no special addressing modes */ | |
+struct AddressingMode{ | |
+ int never_used; | |
+}; | |
+ | |
+/* The number of registers of the target machine. */ | |
+#define MAXR 8 | |
+ | |
+/* Number of commandline-options the code-generator accepts. */ | |
+#define MAXGF 1 | |
+ | |
+/* If this is set to zero vbcc will not generate ICs where the */ | |
+/* target operand is the same as the 2nd source operand. */ | |
+/* This can sometimes simplify the code-generator, but usually */ | |
+/* the code is better if the code-generator allows it. */ | |
+#define USEQ2ASZ 1 | |
+ | |
+/* This specifies the smallest integer type that can be added to a */ | |
+/* pointer. */ | |
+#define MINADDI2P SHORT | |
+ | |
+/* If the bytes of an integer are ordered most significant byte */ | |
+/* byte first and then decreasing set BIGENDIAN to 1. */ | |
+#define BIGENDIAN 1 | |
+ | |
+/* If the bytes of an integer are ordered lest significant byte */ | |
+/* byte first and then increasing set LITTLEENDIAN to 1. */ | |
+#define LITTLEENDIAN 0 | |
+ | |
+/* Note that BIGENDIAN and LITTLEENDIAN are mutually exclusive. */ | |
+ | |
+/* If switch-statements should be generated as a sequence of */ | |
+/* SUB,TST,BEQ ICs rather than COMPARE,BEQ ICs set this to 1. */ | |
+/* This can yield better code on some machines. */ | |
+#define SWITCHSUBS 0 | |
+ | |
+/* In optimizing compilation certain library memcpy/strcpy-calls */ | |
+/* with length known at compile-time will be inlined using an */ | |
+/* ASSIGN-IC if the size is less or equal to INLINEMEMCPY. */ | |
+/* The type used for the ASSIGN-IC will be UNSIGNED|CHAR. */ | |
+#define INLINEMEMCPY 1024 | |
+ | |
+/* Parameters are sometimes passed in registers without __reg. */ | |
+#undef HAVE_REGPARMS | |
+ | |
+/* Arguments are pushed on to the stack right-to-left */ | |
+#undef ORDERED_PUSH | |
+ | |
+/* We have no target-specific variable attributes. */ | |
+#undef HAVE_TARGET_ATTRIBUTES | |
+ | |
+/* We have no target-specific pragmas */ | |
+#undef HAVE_TARGET_PRAGMAS | |
+ | |
+/* We do not keep track of all registers modified by a function. */ | |
+#undef HAVE_REGS_MODIFIED | |
+ | |
+/* We do not implement our own register allocation. */ | |
+#undef HAVE_TARGET_RALLOC | |
+ | |
+/* size of buffer for asm-output, this can be used to do | |
+ peephole-optimizations of the generated assembly-output */ | |
+#define EMIT_BUF_LEN 1024 /* should be enough */ | |
+/* number of asm-output lines buffered */ | |
+#define EMIT_BUF_DEPTH 4 | |
+ | |
+/* We have no asm_peephole to optimize assembly-output */ | |
+#define HAVE_TARGET_PEEPHOLE 0 | |
+ | |
+/* we do not have a mark_eff_ics function, this is used to prevent | |
+ optimizations on code which can already be implemented by efficient | |
+ assembly */ | |
+#undef HAVE_TARGET_EFF_IC | |
+ | |
+/* we only need the standard data types (no bit-types, different pointers | |
+ etc.) */ | |
+#undef HAVE_EXT_TYPES | |
+#undef HAVE_TGT_PRINTVAL | |
+ | |
+/* we do not need extra elements in the IC */ | |
+#undef HAVE_EXT_IC | |
+ | |
+/* we do use unsigned int as size_t (but unsigned long, the default) */ | |
+#define HAVE_INT_SIZET 1 | |
+ | |
+/* we do not need register-pairs */ | |
+#undef HAVE_REGPAIRS | |
+ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment