Organize code into modules.
This commit is contained in:
parent
cd0e5c2919
commit
96059d6e04
|
@ -2,9 +2,9 @@ require "./chalk/*"
|
|||
require "option_parser"
|
||||
|
||||
module Chalk
|
||||
config = Config.parse!
|
||||
config = Ui::Config.parse!
|
||||
exit unless config.validate!
|
||||
|
||||
compiler = Compiler.new config
|
||||
compiler = Compiler::Compiler.new config
|
||||
compiler.run
|
||||
end
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
module Chalk
|
||||
class BuiltinFunction
|
||||
getter param_count : Int32
|
||||
module Builtin
|
||||
class BuiltinFunction
|
||||
getter param_count : Int32
|
||||
|
||||
def initialize(@param_count)
|
||||
def initialize(@param_count)
|
||||
end
|
||||
|
||||
def generate!(codegen)
|
||||
end
|
||||
end
|
||||
|
||||
def generate!(codegen)
|
||||
end
|
||||
end
|
||||
class InlineFunction
|
||||
getter param_count : Int32
|
||||
|
||||
class InlineFunction
|
||||
getter param_count : Int32
|
||||
def initialize(@param_count)
|
||||
end
|
||||
|
||||
def initialize(@param_count)
|
||||
end
|
||||
|
||||
def generate!(codegen, params, table, target, free)
|
||||
def generate!(codegen, params, table, target, free)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,141 +2,143 @@ require "./ir.cr"
|
|||
require "./emitter.cr"
|
||||
|
||||
module Chalk
|
||||
class CodeGenerator
|
||||
include Emitter
|
||||
module Compiler
|
||||
class CodeGenerator
|
||||
include Emitter
|
||||
|
||||
RETURN_REG = 14
|
||||
STACK_REG = 13
|
||||
RETURN_REG = 14
|
||||
STACK_REG = 13
|
||||
|
||||
property instructions : Array(Instruction)
|
||||
property instructions : Array(Ir::Instruction)
|
||||
|
||||
def initialize(table, @function : TreeFunction)
|
||||
@registers = 0
|
||||
@instructions = [] of Instruction
|
||||
@table = Table.new table
|
||||
def initialize(table, @function : Trees::TreeFunction)
|
||||
@registers = 0
|
||||
@instructions = [] of Ir::Instruction
|
||||
@table = Table.new table
|
||||
|
||||
@function.params.each do |param|
|
||||
@table[param] = VarEntry.new @registers
|
||||
@registers += 1
|
||||
end
|
||||
end
|
||||
|
||||
def generate!(tree, function : InlineFunction, table, target, free)
|
||||
function.generate!(self, tree.params, table, target, free)
|
||||
end
|
||||
|
||||
def generate!(tree, function : TreeFunction | BuiltinFunction, table, target, free)
|
||||
start_at = free
|
||||
# Move I to stack
|
||||
setis
|
||||
# Get to correct stack position
|
||||
addi STACK_REG
|
||||
# Store variables
|
||||
store (start_at - 1) unless start_at == 0
|
||||
# Increment I and stack position
|
||||
load free, start_at
|
||||
opr TokenType::OpAdd, STACK_REG, free
|
||||
addi free
|
||||
|
||||
# Calculate the parameters
|
||||
tree.params.each do |param|
|
||||
generate! param, table, free, free + 1
|
||||
free += 1
|
||||
end
|
||||
# Call the function
|
||||
tree.params.size.times do |time|
|
||||
loadr time, time + start_at
|
||||
end
|
||||
call tree.name
|
||||
|
||||
# Reduce stack pointer
|
||||
load free, start_at
|
||||
opr TokenType::OpSub, STACK_REG, free
|
||||
# Move I to stack
|
||||
setis
|
||||
# Get to correct stack position
|
||||
addi STACK_REG
|
||||
# Restore
|
||||
restore (start_at - 1) unless start_at == 0
|
||||
# Get call value into target
|
||||
loadr target, RETURN_REG
|
||||
end
|
||||
|
||||
def generate!(tree, table, target, free)
|
||||
case tree
|
||||
when TreeId
|
||||
entry = table[tree.id]?
|
||||
raise "Unknown variable" unless entry &&
|
||||
entry.is_a?(VarEntry)
|
||||
loadr target, entry.register
|
||||
when TreeLit
|
||||
load target, tree.lit
|
||||
when TreeOp
|
||||
generate! tree.left, table, target, free
|
||||
generate! tree.right, table, free, free + 1
|
||||
opr tree.op, target, free
|
||||
when TreeCall
|
||||
entry = table[tree.name]?
|
||||
raise "Unknown function" unless entry &&
|
||||
entry.is_a?(FunctionEntry)
|
||||
function = entry.function
|
||||
raise "Invalid call" if tree.params.size != function.param_count
|
||||
generate! tree, function, table, target, free
|
||||
when TreeBlock
|
||||
table = Table.new(table)
|
||||
tree.children.each do |child|
|
||||
free += generate! child, table, free, free + 1
|
||||
@function.params.each do |param|
|
||||
@table[param] = VarEntry.new @registers
|
||||
@registers += 1
|
||||
end
|
||||
when TreeVar
|
||||
entry = table[tree.name]?
|
||||
if entry == nil
|
||||
entry = VarEntry.new free
|
||||
end
|
||||
|
||||
def generate!(tree, function : Builtin::InlineFunction, table, target, free)
|
||||
function.generate!(self, tree.params, table, target, free)
|
||||
end
|
||||
|
||||
def generate!(tree, function : Trees::TreeFunction | Builtin::BuiltinFunction, table, target, free)
|
||||
start_at = free
|
||||
# Move I to stack
|
||||
setis
|
||||
# Get to correct stack position
|
||||
addi STACK_REG
|
||||
# Store variables
|
||||
store (start_at - 1) unless start_at == 0
|
||||
# Increment I and stack position
|
||||
load free, start_at
|
||||
opr TokenType::OpAdd, STACK_REG, free
|
||||
addi free
|
||||
|
||||
# Calculate the parameters
|
||||
tree.params.each do |param|
|
||||
generate! param, table, free, free + 1
|
||||
free += 1
|
||||
table[tree.name] = entry
|
||||
end
|
||||
raise "Unknown variable" unless entry.is_a?(VarEntry)
|
||||
generate! tree.expr, table, entry.register, free
|
||||
return 1
|
||||
when TreeAssign
|
||||
entry = table[tree.name]?
|
||||
raise "Unknown variable" unless entry &&
|
||||
entry.is_a?(VarEntry)
|
||||
generate! tree.expr, table, entry.register, free
|
||||
when TreeIf
|
||||
generate! tree.condition, table, free, free + 1
|
||||
sne free, 0
|
||||
jump_inst = jr 0
|
||||
# Call the function
|
||||
tree.params.size.times do |time|
|
||||
loadr time, time + start_at
|
||||
end
|
||||
call tree.name
|
||||
|
||||
old_size = @instructions.size
|
||||
generate! tree.block, table, free, free + 1
|
||||
jump_after = jr 0
|
||||
jump_inst.offset = @instructions.size - old_size + 1
|
||||
|
||||
old_size = @instructions.size
|
||||
generate! tree.otherwise, table, free, free + 1 if tree.otherwise
|
||||
jump_after.offset = @instructions.size - old_size + 1
|
||||
when TreeWhile
|
||||
before_cond = @instructions.size
|
||||
generate! tree.condition, table, free, free + 1
|
||||
sne free, 0
|
||||
cond_jump = jr 0
|
||||
|
||||
old_size = @instructions.size
|
||||
generate! tree.block, table, free, free + 1
|
||||
after_jump = jr 0
|
||||
|
||||
cond_jump.offset = @instructions.size - old_size + 1
|
||||
after_jump.offset = before_cond - instructions.size + 1
|
||||
when TreeReturn
|
||||
generate! tree.rvalue, table, RETURN_REG, free
|
||||
ret
|
||||
# Reduce stack pointer
|
||||
load free, start_at
|
||||
opr TokenType::OpSub, STACK_REG, free
|
||||
# Move I to stack
|
||||
setis
|
||||
# Get to correct stack position
|
||||
addi STACK_REG
|
||||
# Restore
|
||||
restore (start_at - 1) unless start_at == 0
|
||||
# Get call value into target
|
||||
loadr target, RETURN_REG
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
def generate!
|
||||
generate!(@function.block, @table, -1, @registers)
|
||||
return @instructions
|
||||
def generate!(tree, table, target, free)
|
||||
case tree
|
||||
when Trees::TreeId
|
||||
entry = table[tree.id]?
|
||||
raise "Unknown variable" unless entry &&
|
||||
entry.is_a?(VarEntry)
|
||||
loadr target, entry.register
|
||||
when Trees::TreeLit
|
||||
load target, tree.lit
|
||||
when Trees::TreeOp
|
||||
generate! tree.left, table, target, free
|
||||
generate! tree.right, table, free, free + 1
|
||||
opr tree.op, target, free
|
||||
when Trees::TreeCall
|
||||
entry = table[tree.name]?
|
||||
raise "Unknown function" unless entry &&
|
||||
entry.is_a?(FunctionEntry)
|
||||
function = entry.function
|
||||
raise "Invalid call" if tree.params.size != function.param_count
|
||||
generate! tree, function, table, target, free
|
||||
when Trees::TreeBlock
|
||||
table = Table.new(table)
|
||||
tree.children.each do |child|
|
||||
free += generate! child, table, free, free + 1
|
||||
end
|
||||
when Trees::TreeVar
|
||||
entry = table[tree.name]?
|
||||
if entry == nil
|
||||
entry = VarEntry.new free
|
||||
free += 1
|
||||
table[tree.name] = entry
|
||||
end
|
||||
raise "Unknown variable" unless entry.is_a?(VarEntry)
|
||||
generate! tree.expr, table, entry.register, free
|
||||
return 1
|
||||
when Trees::TreeAssign
|
||||
entry = table[tree.name]?
|
||||
raise "Unknown variable" unless entry &&
|
||||
entry.is_a?(VarEntry)
|
||||
generate! tree.expr, table, entry.register, free
|
||||
when Trees::TreeIf
|
||||
generate! tree.condition, table, free, free + 1
|
||||
sne free, 0
|
||||
jump_inst = jr 0
|
||||
|
||||
old_size = @instructions.size
|
||||
generate! tree.block, table, free, free + 1
|
||||
jump_after = jr 0
|
||||
jump_inst.offset = @instructions.size - old_size + 1
|
||||
|
||||
old_size = @instructions.size
|
||||
generate! tree.otherwise, table, free, free + 1 if tree.otherwise
|
||||
jump_after.offset = @instructions.size - old_size + 1
|
||||
when Trees::TreeWhile
|
||||
before_cond = @instructions.size
|
||||
generate! tree.condition, table, free, free + 1
|
||||
sne free, 0
|
||||
cond_jump = jr 0
|
||||
|
||||
old_size = @instructions.size
|
||||
generate! tree.block, table, free, free + 1
|
||||
after_jump = jr 0
|
||||
|
||||
cond_jump.offset = @instructions.size - old_size + 1
|
||||
after_jump.offset = before_cond - instructions.size + 1
|
||||
when Trees::TreeReturn
|
||||
generate! tree.rvalue, table, RETURN_REG, free
|
||||
ret
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
def generate!
|
||||
generate!(@function.block, @table, -1, @registers)
|
||||
return @instructions
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,164 +3,166 @@ require "./constant_folder.cr"
|
|||
require "./table.cr"
|
||||
|
||||
module Chalk
|
||||
class Compiler
|
||||
def initialize(@config : Config)
|
||||
@logger = Logger.new STDOUT
|
||||
@logger.debug("Initialized compiler")
|
||||
@logger.level = Logger::DEBUG
|
||||
end
|
||||
|
||||
private def create_trees(file)
|
||||
string = File.read(file)
|
||||
@logger.debug("Tokenizing")
|
||||
lexer = Lexer.new
|
||||
tokens = lexer.lex string
|
||||
if tokens.size == 0 && string != ""
|
||||
raise "Unable to tokenize file."
|
||||
module Compiler
|
||||
class Compiler
|
||||
def initialize(@config : Ui::Config)
|
||||
@logger = Logger.new STDOUT
|
||||
@logger.debug("Initialized compiler")
|
||||
@logger.level = Logger::DEBUG
|
||||
end
|
||||
@logger.debug("Finished tokenizing")
|
||||
@logger.debug("Beginning parsing")
|
||||
parser = Parser.new
|
||||
if trees = parser.parse?(tokens)
|
||||
@logger.debug("Finished parsing")
|
||||
@logger.debug("Beginning constant folding")
|
||||
folder = ConstantFolder.new
|
||||
trees.map! do |tree|
|
||||
@logger.debug("Constant folding #{tree.name}")
|
||||
tree.apply(folder).as(TreeFunction)
|
||||
|
||||
private def create_trees(file)
|
||||
string = File.read(file)
|
||||
@logger.debug("Tokenizing")
|
||||
lexer = Lexer.new
|
||||
tokens = lexer.lex string
|
||||
if tokens.size == 0 && string != ""
|
||||
raise "Unable to tokenize file."
|
||||
end
|
||||
@logger.debug("Done constant folding")
|
||||
return trees
|
||||
end
|
||||
raise "Unable to parse file."
|
||||
end
|
||||
|
||||
private def create_table(trees)
|
||||
table = Table.new
|
||||
@logger.debug("Creating symbol table")
|
||||
trees.each do |tree|
|
||||
@logger.debug("Storing #{tree.name} in symbol table")
|
||||
table[tree.name] = FunctionEntry.new tree
|
||||
end
|
||||
@logger.debug("Done creating symbol table")
|
||||
|
||||
table["draw"] = FunctionEntry.new InlineDrawFunction.new
|
||||
table["get_key"] = FunctionEntry.new InlineAwaitKeyFunction.new
|
||||
table["get_font"] = FunctionEntry.new InlineGetFontFunction.new
|
||||
table["set_delay"] = FunctionEntry.new InlineSetDelayFunction.new
|
||||
table["get_delay"] = FunctionEntry.new InlineGetDelayFunction.new
|
||||
return table
|
||||
end
|
||||
|
||||
private def create_code(tree : TreeFunction, table, instruction = ReturnInstruction.new)
|
||||
optimizer = Optimizer.new
|
||||
generator = CodeGenerator.new table, tree
|
||||
@logger.debug("Generating code for #{tree.name}")
|
||||
code = generator.generate!
|
||||
code << instruction
|
||||
return optimizer.optimize(code)
|
||||
end
|
||||
|
||||
private def create_code(tree : BuiltinFunction, table, instruction = nil)
|
||||
instructions = [] of Instruction
|
||||
tree.generate!(instructions)
|
||||
return instructions
|
||||
end
|
||||
|
||||
private def create_code(trees : Array(TreeFunction), table)
|
||||
code = {} of String => Array(Instruction)
|
||||
trees.each do |tree|
|
||||
code[tree.name] = create_code(tree, table)
|
||||
end
|
||||
return code
|
||||
end
|
||||
|
||||
private def run_tree
|
||||
trees = create_trees(@config.file)
|
||||
trees.each do |it|
|
||||
STDOUT << it
|
||||
end
|
||||
end
|
||||
|
||||
private def run_intermediate
|
||||
trees = create_trees(@config.file)
|
||||
table = create_table(trees)
|
||||
code = create_code(trees, table)
|
||||
code.each do |name, insts|
|
||||
puts "Code for #{name}:"
|
||||
insts.each { |it| puts it }
|
||||
puts "-----"
|
||||
end
|
||||
end
|
||||
|
||||
private def generate_binary(table, instructions, dest)
|
||||
context = InstructionContext.new table, instructions.size
|
||||
binary = instructions.map_with_index { |it, i| it.to_bin(context, i).to_u16 }
|
||||
binary.each do |inst|
|
||||
first = (inst >> 8).to_u8
|
||||
dest.write_byte(first)
|
||||
second = (inst & 0xff).to_u8
|
||||
dest.write_byte(second)
|
||||
end
|
||||
end
|
||||
|
||||
private def collect_calls(table)
|
||||
open = Set(String).new
|
||||
done = Set(String).new
|
||||
|
||||
open << "main"
|
||||
while !open.empty?
|
||||
first = open.first
|
||||
open.delete first
|
||||
|
||||
entry = table[first]?
|
||||
raise "Unknown function" unless entry && entry.is_a?(FunctionEntry)
|
||||
function = entry.function
|
||||
next if function.is_a?(InlineFunction)
|
||||
done << first
|
||||
next unless function.is_a?(TreeFunction)
|
||||
|
||||
visitor = CallVisitor.new
|
||||
function.accept(visitor)
|
||||
open.concat(visitor.calls - done)
|
||||
end
|
||||
return done
|
||||
end
|
||||
|
||||
private def run_binary
|
||||
all_instructions = [] of Instruction
|
||||
trees = create_trees(@config.file)
|
||||
table = create_table(trees)
|
||||
names = collect_calls(table)
|
||||
names.delete "main"
|
||||
|
||||
main_entry = table["main"]?.as(FunctionEntry)
|
||||
all_instructions.concat create_code(main_entry.function.as(TreeFunction),
|
||||
table, JumpRelativeInstruction.new 0)
|
||||
main_entry.addr = 0
|
||||
|
||||
names.each do |name|
|
||||
entry = table[name]?.as(FunctionEntry)
|
||||
entry.addr = all_instructions.size
|
||||
function = entry.function
|
||||
raise "Trying to compile inlined function" if function.is_a?(InlineFunction)
|
||||
all_instructions.concat create_code(function, table)
|
||||
all_instructions << ReturnInstruction.new
|
||||
@logger.debug("Finished tokenizing")
|
||||
@logger.debug("Beginning parsing")
|
||||
parser = ParserCombinators::Parser.new
|
||||
if trees = parser.parse?(tokens)
|
||||
@logger.debug("Finished parsing")
|
||||
@logger.debug("Beginning constant folding")
|
||||
folder = Trees::ConstantFolder.new
|
||||
trees.map! do |tree|
|
||||
@logger.debug("Constant folding #{tree.name}")
|
||||
tree.apply(folder).as(Trees::TreeFunction)
|
||||
end
|
||||
@logger.debug("Done constant folding")
|
||||
return trees
|
||||
end
|
||||
raise "Unable to parse file."
|
||||
end
|
||||
|
||||
file = File.open("out.ch8", "w")
|
||||
generate_binary(table, all_instructions, file)
|
||||
file.close
|
||||
end
|
||||
private def create_table(trees)
|
||||
table = Table.new
|
||||
@logger.debug("Creating symbol table")
|
||||
trees.each do |tree|
|
||||
@logger.debug("Storing #{tree.name} in symbol table")
|
||||
table[tree.name] = FunctionEntry.new tree
|
||||
end
|
||||
@logger.debug("Done creating symbol table")
|
||||
|
||||
def run
|
||||
case @config.mode
|
||||
when OutputMode::Tree
|
||||
run_tree
|
||||
when OutputMode::Intermediate
|
||||
run_intermediate
|
||||
when OutputMode::Binary
|
||||
run_binary
|
||||
table["draw"] = FunctionEntry.new Builtin::InlineDrawFunction.new
|
||||
table["get_key"] = FunctionEntry.new Builtin::InlineAwaitKeyFunction.new
|
||||
table["get_font"] = FunctionEntry.new Builtin::InlineGetFontFunction.new
|
||||
table["set_delay"] = FunctionEntry.new Builtin::InlineSetDelayFunction.new
|
||||
table["get_delay"] = FunctionEntry.new Builtin::InlineGetDelayFunction.new
|
||||
return table
|
||||
end
|
||||
|
||||
private def create_code(tree : Trees::TreeFunction, table, instruction = Ir::ReturnInstruction.new)
|
||||
optimizer = Optimizer.new
|
||||
generator = CodeGenerator.new table, tree
|
||||
@logger.debug("Generating code for #{tree.name}")
|
||||
code = generator.generate!
|
||||
code << instruction
|
||||
return optimizer.optimize(code)
|
||||
end
|
||||
|
||||
private def create_code(tree : Builtin::BuiltinFunction, table, instruction = nil)
|
||||
instructions = [] of Ir::Instruction
|
||||
tree.generate!(instructions)
|
||||
return instructions
|
||||
end
|
||||
|
||||
private def create_code(trees : Array(Trees::TreeFunction), table)
|
||||
code = {} of String => Array(Ir::Instruction)
|
||||
trees.each do |tree|
|
||||
code[tree.name] = create_code(tree, table)
|
||||
end
|
||||
return code
|
||||
end
|
||||
|
||||
private def run_tree
|
||||
trees = create_trees(@config.file)
|
||||
trees.each do |it|
|
||||
STDOUT << it
|
||||
end
|
||||
end
|
||||
|
||||
private def run_intermediate
|
||||
trees = create_trees(@config.file)
|
||||
table = create_table(trees)
|
||||
code = create_code(trees, table)
|
||||
code.each do |name, insts|
|
||||
puts "Code for #{name}:"
|
||||
insts.each { |it| puts it }
|
||||
puts "-----"
|
||||
end
|
||||
end
|
||||
|
||||
private def generate_binary(table, instructions, dest)
|
||||
context = Ir::InstructionContext.new table, instructions.size
|
||||
binary = instructions.map_with_index { |it, i| it.to_bin(context, i).to_u16 }
|
||||
binary.each do |inst|
|
||||
first = (inst >> 8).to_u8
|
||||
dest.write_byte(first)
|
||||
second = (inst & 0xff).to_u8
|
||||
dest.write_byte(second)
|
||||
end
|
||||
end
|
||||
|
||||
private def collect_calls(table)
|
||||
open = Set(String).new
|
||||
done = Set(String).new
|
||||
|
||||
open << "main"
|
||||
while !open.empty?
|
||||
first = open.first
|
||||
open.delete first
|
||||
|
||||
entry = table[first]?
|
||||
raise "Unknown function" unless entry && entry.is_a?(FunctionEntry)
|
||||
function = entry.function
|
||||
next if function.is_a?(Builtin::InlineFunction)
|
||||
done << first
|
||||
next unless function.is_a?(Trees::TreeFunction)
|
||||
|
||||
visitor = Trees::CallVisitor.new
|
||||
function.accept(visitor)
|
||||
open.concat(visitor.calls - done)
|
||||
end
|
||||
return done
|
||||
end
|
||||
|
||||
private def run_binary
|
||||
all_instructions = [] of Ir::Instruction
|
||||
trees = create_trees(@config.file)
|
||||
table = create_table(trees)
|
||||
names = collect_calls(table)
|
||||
names.delete "main"
|
||||
|
||||
main_entry = table["main"]?.as(FunctionEntry)
|
||||
all_instructions.concat create_code(main_entry.function.as(Trees::TreeFunction),
|
||||
table, Ir::JumpRelativeInstruction.new 0)
|
||||
main_entry.addr = 0
|
||||
|
||||
names.each do |name|
|
||||
entry = table[name]?.as(FunctionEntry)
|
||||
entry.addr = all_instructions.size
|
||||
function = entry.function
|
||||
raise "Trying to compile inlined function" if function.is_a?(Builtin::InlineFunction)
|
||||
all_instructions.concat create_code(function, table)
|
||||
all_instructions << Ir::ReturnInstruction.new
|
||||
end
|
||||
|
||||
file = File.open("out.ch8", "w")
|
||||
generate_binary(table, all_instructions, file)
|
||||
file.close
|
||||
end
|
||||
|
||||
def run
|
||||
case @config.mode
|
||||
when Ui::OutputMode::Tree
|
||||
run_tree
|
||||
when Ui::OutputMode::Intermediate
|
||||
run_intermediate
|
||||
when Ui::OutputMode::Binary
|
||||
run_binary
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,51 +1,53 @@
|
|||
module Chalk
|
||||
enum OutputMode
|
||||
Tree,
|
||||
Intermediate,
|
||||
Binary
|
||||
end
|
||||
|
||||
class Config
|
||||
property file : String
|
||||
property mode : OutputMode
|
||||
|
||||
def initialize(@file = "",
|
||||
@mode = OutputMode::Tree)
|
||||
module Ui
|
||||
enum OutputMode
|
||||
Tree,
|
||||
Intermediate,
|
||||
Binary
|
||||
end
|
||||
|
||||
def self.parse!
|
||||
config = self.new
|
||||
OptionParser.parse! do |parser|
|
||||
parser.banner = "Usage: chalk [arguments]"
|
||||
parser.on("-m", "--mode=MODE", "Set the mode of the compiler.") do |mode|
|
||||
case mode.downcase
|
||||
when "tree", "t"
|
||||
config.mode = OutputMode::Tree
|
||||
when "intermediate", "i"
|
||||
config.mode = OutputMode::Intermediate
|
||||
when "binary", "b"
|
||||
config.mode = OutputMode::Binary
|
||||
else
|
||||
puts "Invalid mode type. Using default."
|
||||
class Config
|
||||
property file : String
|
||||
property mode : OutputMode
|
||||
|
||||
def initialize(@file = "",
|
||||
@mode = OutputMode::Tree)
|
||||
end
|
||||
|
||||
def self.parse!
|
||||
config = self.new
|
||||
OptionParser.parse! do |parser|
|
||||
parser.banner = "Usage: chalk [arguments]"
|
||||
parser.on("-m", "--mode=MODE", "Set the mode of the compiler.") do |mode|
|
||||
case mode.downcase
|
||||
when "tree", "t"
|
||||
config.mode = OutputMode::Tree
|
||||
when "intermediate", "i"
|
||||
config.mode = OutputMode::Intermediate
|
||||
when "binary", "b"
|
||||
config.mode = OutputMode::Binary
|
||||
else
|
||||
puts "Invalid mode type. Using default."
|
||||
end
|
||||
end
|
||||
parser.on("-f", "--file=FILE", "Set the input file to compile.") do |file|
|
||||
config.file = file
|
||||
end
|
||||
parser.on("-h", "--help", "Show this message.") { puts parser }
|
||||
end
|
||||
parser.on("-f", "--file=FILE", "Set the input file to compile.") do |file|
|
||||
config.file = file
|
||||
end
|
||||
parser.on("-h", "--help", "Show this message.") { puts parser }
|
||||
return config
|
||||
end
|
||||
return config
|
||||
end
|
||||
|
||||
def validate!
|
||||
if file == ""
|
||||
puts "No source file specified."
|
||||
return false
|
||||
elsif !File.exists? file
|
||||
puts "Unable to open source file."
|
||||
return false
|
||||
def validate!
|
||||
if file == ""
|
||||
puts "No source file specified."
|
||||
return false
|
||||
elsif !File.exists? file
|
||||
puts "Unable to open source file."
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,33 +1,35 @@
|
|||
require "./tree.cr"
|
||||
|
||||
module Chalk
|
||||
class ConstantFolder < Transformer
|
||||
private def perform_op(op, left, right)
|
||||
case op
|
||||
when TokenType::OpAdd
|
||||
left + right
|
||||
when TokenType::OpSub
|
||||
left - right
|
||||
when TokenType::OpMul
|
||||
left*right
|
||||
when TokenType::OpDiv
|
||||
left/right
|
||||
when TokenType::OpAnd
|
||||
left & right
|
||||
when TokenType::OpOr
|
||||
left | right
|
||||
else TokenType::OpXor
|
||||
left ^ right
|
||||
module Trees
|
||||
class ConstantFolder < Transformer
|
||||
private def perform_op(op, left, right)
|
||||
case op
|
||||
when Compiler::TokenType::OpAdd
|
||||
left + right
|
||||
when Compiler::TokenType::OpSub
|
||||
left - right
|
||||
when Compiler::TokenType::OpMul
|
||||
left*right
|
||||
when Compiler::TokenType::OpDiv
|
||||
left/right
|
||||
when Compiler::TokenType::OpAnd
|
||||
left & right
|
||||
when Compiler::TokenType::OpOr
|
||||
left | right
|
||||
else Compiler::TokenType::OpXor
|
||||
left ^ right
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def transform(tree : TreeOp)
|
||||
if tree.left.is_a?(TreeLit) && tree.right.is_a?(TreeLit)
|
||||
return TreeLit.new perform_op(tree.op,
|
||||
tree.left.as(TreeLit).lit,
|
||||
tree.right.as(TreeLit).lit)
|
||||
def transform(tree : Trees::TreeOp)
|
||||
if tree.left.is_a?(Trees::TreeLit) && tree.right.is_a?(Trees::TreeLit)
|
||||
return Trees::TreeLit.new perform_op(tree.op,
|
||||
tree.left.as(Trees::TreeLit).lit,
|
||||
tree.right.as(Trees::TreeLit).lit)
|
||||
end
|
||||
return tree
|
||||
end
|
||||
return tree
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,75 +1,77 @@
|
|||
module Chalk
|
||||
module Emitter
|
||||
def load(into, value)
|
||||
inst = LoadInstruction.new into, value.to_i32
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
module Compiler
|
||||
module Emitter
|
||||
def load(into, value)
|
||||
inst = Ir::LoadInstruction.new into, value.to_i32
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def loadr(into, from)
|
||||
inst = LoadRegInstruction.new into, from
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
def loadr(into, from)
|
||||
inst = Ir::LoadRegInstruction.new into, from
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def op(op, into, from)
|
||||
inst = OpInstruction.new op, into, from
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
def op(op, into, from)
|
||||
inst = Ir::OpInstruction.new op, into, from
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def opr(op, into, from)
|
||||
inst = OpRegInstruction.new op, into, from
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
def opr(op, into, from)
|
||||
inst = Ir::OpRegInstruction.new op, into, from
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def sne(l, r)
|
||||
inst = SkipNeInstruction.new l, r
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
def sne(l, r)
|
||||
inst = Ir::SkipNeInstruction.new l, r
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def jr(o)
|
||||
inst = JumpRelativeInstruction.new o
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
def jr(o)
|
||||
inst = Ir::JumpRelativeInstruction.new o
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def store(up_to)
|
||||
inst = StoreInstruction.new up_to
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
def store(up_to)
|
||||
inst = Ir::StoreInstruction.new up_to
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def restore(up_to)
|
||||
inst = RestoreInstruction.new up_to
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
def restore(up_to)
|
||||
inst = Ir::RestoreInstruction.new up_to
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def ret
|
||||
inst = ReturnInstruction.new
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
def ret
|
||||
inst = Ir::ReturnInstruction.new
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def call(func)
|
||||
inst = CallInstruction.new func
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
def call(func)
|
||||
inst = Ir::CallInstruction.new func
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def setis
|
||||
inst = SetIStackInstruction.new
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
def setis
|
||||
inst = Ir::SetIStackInstruction.new
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
|
||||
def addi(reg)
|
||||
inst = AddIRegInstruction.new reg
|
||||
@instructions << inst
|
||||
return inst
|
||||
def addi(reg)
|
||||
inst = Ir::AddIRegInstruction.new reg
|
||||
@instructions << inst
|
||||
return inst
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
module Chalk
|
||||
class CallVisitor < Visitor
|
||||
property calls : Set(String)
|
||||
module Trees
|
||||
class CallVisitor < Visitor
|
||||
property calls : Set(String)
|
||||
|
||||
def initialize
|
||||
@calls = Set(String).new
|
||||
end
|
||||
def initialize
|
||||
@calls = Set(String).new
|
||||
end
|
||||
|
||||
def visit(t : TreeCall)
|
||||
@calls << t.name
|
||||
def visit(t : TreeCall)
|
||||
@calls << t.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,58 +1,60 @@
|
|||
module Chalk
|
||||
class InlineDrawFunction < InlineFunction
|
||||
def initialize
|
||||
@param_count = 3
|
||||
end
|
||||
|
||||
def generate!(emitter, params, table, target, free)
|
||||
if !params[2].is_a?(TreeLit)
|
||||
raise "Third parameter must be a constant."
|
||||
module Builtin
|
||||
class InlineDrawFunction < InlineFunction
|
||||
def initialize
|
||||
@param_count = 3
|
||||
end
|
||||
emitter.generate! params[0], table, free, free + 1
|
||||
emitter.generate! params[1], table, free + 1, free + 2
|
||||
emitter.instructions << DrawInstruction.new free, free + 1, params[2].as(TreeLit).lit.to_i32
|
||||
end
|
||||
end
|
||||
|
||||
class InlineAwaitKeyFunction < InlineFunction
|
||||
def initialize
|
||||
@param_count = 0
|
||||
def generate!(emitter, params, table, target, free)
|
||||
if !params[2].is_a?(Trees::TreeLit)
|
||||
raise "Third parameter must be a constant."
|
||||
end
|
||||
emitter.generate! params[0], table, free, free + 1
|
||||
emitter.generate! params[1], table, free + 1, free + 2
|
||||
emitter.instructions << Ir::DrawInstruction.new free, free + 1, params[2].as(Trees::TreeLit).lit.to_i32
|
||||
end
|
||||
end
|
||||
|
||||
def generate!(emitter, params, table, target, free)
|
||||
emitter.instructions << AwaitKeyInstruction.new target
|
||||
end
|
||||
end
|
||||
class InlineAwaitKeyFunction < InlineFunction
|
||||
def initialize
|
||||
@param_count = 0
|
||||
end
|
||||
|
||||
class InlineGetFontFunction < InlineFunction
|
||||
def initialize
|
||||
@param_count = 1
|
||||
def generate!(emitter, params, table, target, free)
|
||||
emitter.instructions << Ir::AwaitKeyInstruction.new target
|
||||
end
|
||||
end
|
||||
|
||||
def generate!(emitter, params, table, target, free)
|
||||
emitter.generate! params[0], table, free, free + 1
|
||||
emitter.instructions << GetFontInstruction.new free
|
||||
end
|
||||
end
|
||||
class InlineGetFontFunction < InlineFunction
|
||||
def initialize
|
||||
@param_count = 1
|
||||
end
|
||||
|
||||
class InlineSetDelayFunction < InlineFunction
|
||||
def initialize
|
||||
@param_count = 1
|
||||
def generate!(emitter, params, table, target, free)
|
||||
emitter.generate! params[0], table, free, free + 1
|
||||
emitter.instructions << Ir::GetFontInstruction.new free
|
||||
end
|
||||
end
|
||||
|
||||
def generate!(emitter, params, table, target, free)
|
||||
emitter.generate! params[0], table, free, free + 1
|
||||
emitter.instructions << SetDelayTimerInstruction.new free
|
||||
end
|
||||
end
|
||||
class InlineSetDelayFunction < InlineFunction
|
||||
def initialize
|
||||
@param_count = 1
|
||||
end
|
||||
|
||||
class InlineGetDelayFunction < InlineFunction
|
||||
def initialize
|
||||
@param_count = 0
|
||||
def generate!(emitter, params, table, target, free)
|
||||
emitter.generate! params[0], table, free, free + 1
|
||||
emitter.instructions << Ir::SetDelayTimerInstruction.new free
|
||||
end
|
||||
end
|
||||
|
||||
def generate!(emitter, params, table, target, free)
|
||||
emitter.instructions << GetDelayTimerInstruction.new target
|
||||
class InlineGetDelayFunction < InlineFunction
|
||||
def initialize
|
||||
@param_count = 0
|
||||
end
|
||||
|
||||
def generate!(emitter, params, table, target, free)
|
||||
emitter.instructions << Ir::GetDelayTimerInstruction.new target
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
606
src/chalk/ir.cr
606
src/chalk/ir.cr
|
@ -1,373 +1,375 @@
|
|||
require "./lexer.cr"
|
||||
|
||||
module Chalk
|
||||
class Instruction
|
||||
def to_bin(i, index)
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
class InstructionContext
|
||||
property table : Table
|
||||
property stack : Int32
|
||||
|
||||
def initialize(@table, @stack)
|
||||
end
|
||||
end
|
||||
|
||||
class LoadInstruction < Instruction
|
||||
property register : Int32
|
||||
property value : Int32
|
||||
|
||||
def initialize(@register, @value)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "load R"
|
||||
@register.to_s(16, io)
|
||||
io << " " << @value
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
0x6000 | (@register << 8) | @value
|
||||
end
|
||||
end
|
||||
|
||||
class LoadRegInstruction < Instruction
|
||||
property into : Int32
|
||||
property from : Int32
|
||||
|
||||
def initialize(@into, @from)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "loadr R"
|
||||
@into.to_s(16, io)
|
||||
io << " R"
|
||||
@from.to_s(16, io)
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
0x8000 | (@into << 8) | (@from << 4)
|
||||
end
|
||||
end
|
||||
|
||||
class OpInstruction < Instruction
|
||||
property op : TokenType
|
||||
property into : Int32
|
||||
property value : Int32
|
||||
|
||||
def initialize(@op, @into, @value)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "op " << op << " R"
|
||||
@into.to_s(16, io)
|
||||
io << " " << @value
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
case op
|
||||
when TokenType::OpAdd
|
||||
return 0x7000 | (@into << 8) | @value
|
||||
else
|
||||
raise "Invalid instruction"
|
||||
module Ir
|
||||
class Instruction
|
||||
def to_bin(i, index)
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class OpRegInstruction < Instruction
|
||||
property op : TokenType
|
||||
property into : Int32
|
||||
property from : Int32
|
||||
class InstructionContext
|
||||
property table : Compiler::Table
|
||||
property stack : Int32
|
||||
|
||||
def initialize(@op, @into, @from)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "opr " << op << " R"
|
||||
@into.to_s(16, io)
|
||||
io << " R"
|
||||
@from.to_s(16, io)
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
code = 0
|
||||
case op
|
||||
when TokenType::OpAdd
|
||||
code = 4
|
||||
when TokenType::OpSub
|
||||
code = 5
|
||||
when TokenType::OpOr
|
||||
code = 1
|
||||
when TokenType::OpAnd
|
||||
code = 2
|
||||
when TokenType::OpXor
|
||||
code = 3
|
||||
else
|
||||
raise "Invalid instruction"
|
||||
def initialize(@table, @stack)
|
||||
end
|
||||
return 0x8000 | (@into << 8) | (@from << 4) | code
|
||||
end
|
||||
end
|
||||
|
||||
class StoreInstruction < Instruction
|
||||
property up_to : Int32
|
||||
|
||||
def initialize(@up_to)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "store R"
|
||||
@up_to.to_s(16, io)
|
||||
class LoadInstruction < Instruction
|
||||
property register : Int32
|
||||
property value : Int32
|
||||
|
||||
def initialize(@register, @value)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "load R"
|
||||
@register.to_s(16, io)
|
||||
io << " " << @value
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
0x6000 | (@register << 8) | @value
|
||||
end
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0xf055 | (@up_to << 8)
|
||||
end
|
||||
end
|
||||
class LoadRegInstruction < Instruction
|
||||
property into : Int32
|
||||
property from : Int32
|
||||
|
||||
class RestoreInstruction < Instruction
|
||||
property up_to : Int32
|
||||
def initialize(@into, @from)
|
||||
end
|
||||
|
||||
def initialize(@up_to)
|
||||
def to_s(io)
|
||||
io << "loadr R"
|
||||
@into.to_s(16, io)
|
||||
io << " R"
|
||||
@from.to_s(16, io)
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
0x8000 | (@into << 8) | (@from << 4)
|
||||
end
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "restore R"
|
||||
@up_to.to_s(16, io)
|
||||
class OpInstruction < Instruction
|
||||
property op : Compiler::TokenType
|
||||
property into : Int32
|
||||
property value : Int32
|
||||
|
||||
def initialize(@op, @into, @value)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "op " << op << " R"
|
||||
@into.to_s(16, io)
|
||||
io << " " << @value
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
case op
|
||||
when Compiler::TokenType::OpAdd
|
||||
return 0x7000 | (@into << 8) | @value
|
||||
else
|
||||
raise "Invalid instruction"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0xf065 | (@up_to << 8)
|
||||
end
|
||||
end
|
||||
class OpRegInstruction < Instruction
|
||||
property op : Compiler::TokenType
|
||||
property into : Int32
|
||||
property from : Int32
|
||||
|
||||
class ReturnInstruction < Instruction
|
||||
def initialize
|
||||
def initialize(@op, @into, @from)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "opr " << op << " R"
|
||||
@into.to_s(16, io)
|
||||
io << " R"
|
||||
@from.to_s(16, io)
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
code = 0
|
||||
case op
|
||||
when Compiler::TokenType::OpAdd
|
||||
code = 4
|
||||
when Compiler::TokenType::OpSub
|
||||
code = 5
|
||||
when Compiler::TokenType::OpOr
|
||||
code = 1
|
||||
when Compiler::TokenType::OpAnd
|
||||
code = 2
|
||||
when Compiler::TokenType::OpXor
|
||||
code = 3
|
||||
else
|
||||
raise "Invalid instruction"
|
||||
end
|
||||
return 0x8000 | (@into << 8) | (@from << 4) | code
|
||||
end
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "return"
|
||||
class StoreInstruction < Instruction
|
||||
property up_to : Int32
|
||||
|
||||
def initialize(@up_to)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "store R"
|
||||
@up_to.to_s(16, io)
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0xf055 | (@up_to << 8)
|
||||
end
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0x00ee
|
||||
end
|
||||
end
|
||||
class RestoreInstruction < Instruction
|
||||
property up_to : Int32
|
||||
|
||||
class JumpRelativeInstruction < Instruction
|
||||
property offset : Int32
|
||||
def initialize(@up_to)
|
||||
end
|
||||
|
||||
def initialize(@offset)
|
||||
def to_s(io)
|
||||
io << "restore R"
|
||||
@up_to.to_s(16, io)
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0xf065 | (@up_to << 8)
|
||||
end
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "jr " << @offset
|
||||
class ReturnInstruction < Instruction
|
||||
def initialize
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "return"
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0x00ee
|
||||
end
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0x1000 | ((@offset + index) * 2 + 0x200)
|
||||
end
|
||||
end
|
||||
class JumpRelativeInstruction < Instruction
|
||||
property offset : Int32
|
||||
|
||||
class SkipEqInstruction < Instruction
|
||||
property left : Int32
|
||||
property right : Int32
|
||||
def initialize(@offset)
|
||||
end
|
||||
|
||||
def initialize(@left, @right)
|
||||
def to_s(io)
|
||||
io << "jr " << @offset
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0x1000 | ((@offset + index) * 2 + 0x200)
|
||||
end
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "seq R"
|
||||
@left.to_s(16, io)
|
||||
io << " " << right
|
||||
class SkipEqInstruction < Instruction
|
||||
property left : Int32
|
||||
property right : Int32
|
||||
|
||||
def initialize(@left, @right)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "seq R"
|
||||
@left.to_s(16, io)
|
||||
io << " " << right
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0x3000 | (@left << 8) | @right
|
||||
end
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0x3000 | (@left << 8) | @right
|
||||
end
|
||||
end
|
||||
class SkipNeInstruction < Instruction
|
||||
property left : Int32
|
||||
property right : Int32
|
||||
|
||||
class SkipNeInstruction < Instruction
|
||||
property left : Int32
|
||||
property right : Int32
|
||||
def initialize(@left, @right)
|
||||
end
|
||||
|
||||
def initialize(@left, @right)
|
||||
def to_s(io)
|
||||
io << "sne R"
|
||||
@left.to_s(16, io)
|
||||
io << " " << right
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0x4000 | (@left << 8) | @right
|
||||
end
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "sne R"
|
||||
@left.to_s(16, io)
|
||||
io << " " << right
|
||||
class SkipRegEqInstruction < Instruction
|
||||
property left : Int32
|
||||
property right : Int32
|
||||
|
||||
def initialize(@left, @right)
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "seqr R"
|
||||
@left.to_s(16, io)
|
||||
io << " R"
|
||||
@right.to_s(16, io)
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0x5000 | (@left << 8) | (@right << 4)
|
||||
end
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0x4000 | (@left << 8) | @right
|
||||
end
|
||||
end
|
||||
class SkipRegNeInstruction < Instruction
|
||||
property left : Int32
|
||||
property right : Int32
|
||||
|
||||
class SkipRegEqInstruction < Instruction
|
||||
property left : Int32
|
||||
property right : Int32
|
||||
def initialize(@left, @right)
|
||||
end
|
||||
|
||||
def initialize(@left, @right)
|
||||
def to_s(io)
|
||||
io << "sner R"
|
||||
@left.to_s(16, io)
|
||||
io << " R"
|
||||
@right.to_s(16, io)
|
||||
end
|
||||
|
||||
def to_bin(i, index)
|
||||
return 0x9000 | (@left << 8) | (@right << 4)
|
||||
end
|
||||
end
|
||||
|
||||
def to_s(io)
|
||||
io << "seqr R"
|
||||