fma-snes65816
Version:
SNES 65816 assembler backend for FMA
7 lines (6 loc) • 16.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = "\n; Extends the memory manager for some core functions\nclass CompilerMemoryManager\n macro allow(bank=nil, at=nil, range=nil, align=nil, located_at=nil)\n address_and = nil\n address_or = nil\n\n unless bank.nil?\n address_and = $FFFF\n address_or = bank << 16\n end\n\n unless at.nil?\n range = at..at\n end\n\n self.allow_range range, address_and, address_or, align, located_at\n end\n\n macro shadow(bank, range, shadow_bank, shadow_address)\n address_and = $FFFF\n address_or = bank << 16\n\n modify_add = shadow_address - range.first\n modify_and = $FFFF\n modify_or = shadow_bank << 16\n\n self.shadow_range range, address_and, address_or, modify_add, modify_and, modify_or\n end\n\n macro dp\n self.to_future_number.dp\n end\n\n macro long_address\n self.to_future_number.long_address\n end\n\n macro indirect(parameter=nil)\n self.to_future_number.indirect parameter\n end\n\n macro long_indirect(parameter=nil)\n self.to_future_number.long_indirect parameter\n end\n\n macro bank\n self.to_future_number.bank\n end\nend\n\n; Extends the compiler scope\nclass CompilerScope\n macro on_enter_function(function)\n if function.key? \"register_size_A\"\n self.set_size :A, function.register_size_A\n end\n\n if function.key? \"register_size_XY\"\n self.set_size :XY, function.register_size_XY\n end\n\n Compiler.current_scope.is_return_opcode = false\n Compiler.current_scope.current_function = function.name\n end\n\n macro on_leave_function(function)\n return if Compiler.current_scope.is_return_opcode\n\n RTS\n end\n\n macro size_hint_function(function)\n function.register_size_A = self.get_size(:A)\n function.register_size_XY = self.get_size(:XY)\n end\n\n macro on_call_function(function)\n self.size_hint_function function\n\n JSR function\n end\n\n macro set_size(name, size)\n name = \"XY\" if name == \"X\" || name == \"Y\"\n\n self[\"register_size_#{name}\"] = size\n end\n\n macro get_size(name)\n name = \"XY\" if name == \"X\" || name == \"Y\"\n\n if self.key? \"register_size_#{name}\"\n self[\"register_size_#{name}\"]\n else\n nil\n end\n end\nend\n\nmodule Snes65816\n\n opcodes = {}\n\n module OpcodeWriter\n\n macro im(opcode, value)\n Compiler.current_scope.db opcode\n Compiler.current_scope.db value\n end\n\n macro im_16(opcode, value)\n Compiler.current_scope.db opcode\n Compiler.current_scope.dw value\n end\n\n macro im_a(opcode, value)\n Compiler.current_scope.db opcode\n case A.size\n when 8\n Compiler.current_scope.db value\n when 16\n Compiler.current_scope.dw value\n else\n raise \"Unknown register size for A\"\n end\n end\n\n macro im_x(opcode, value)\n Compiler.current_scope.db opcode\n case X.size\n when 8\n Compiler.current_scope.db value\n when 16\n Compiler.current_scope.dw value\n else\n raise \"Unknown register size for X\"\n end\n end\n\n macro im_y(opcode, value)\n Compiler.current_scope.db opcode\n case Y.size\n when 8\n Compiler.current_scope.db value\n when 16\n Compiler.current_scope.dw value\n else\n raise \"Unknown register size for Y\"\n end\n end\n\n macro addr(opcode, value)\n Compiler.current_scope.db opcode\n Compiler.current_scope.dw value\n end\n\n macro addr_pc(opcode, value)\n Compiler.current_scope.db opcode\n Compiler.current_scope.dw value\n end\n\n macro idpx\n end\n\n macro sr(opcode, value)\n Compiler.current_scope.db opcode\n Compiler.current_scope.db value\n end\n\n macro dp(opcode, value)\n Compiler.current_scope.db opcode\n Compiler.current_scope.db value\n end\n\n macro ildp(opcode, value)\n Compiler.current_scope.db opcode\n Compiler.current_scope.db value\n end\n\n macro ildpy(opcode, value)\n Compiler.current_scope.db opcode\n Compiler.current_scope.db value\n end\n\n macro long(opcode, value)\n Compiler.current_scope.db opcode\n Compiler.current_scope.dw value & $FFFF\n Compiler.current_scope.db value >> 16\n end\n\n macro relb(opcode, address)\n Compiler.current_scope.db opcode\n Compiler.current_scope.db address - Compiler.current_scope.PC - 1\n end\n\n macro idpy(opcode, address)\n Compiler.current_scope.db opcode\n Compiler.current_scope.db address\n end\n\n macro idp\n end\n\n macro isry\n end\n\n macro dpx(opcode, address)\n Compiler.current_scope.db opcode\n Compiler.current_scope.db address\n end\n\n macro addry(opcode, address)\n Compiler.current_scope.db opcode\n Compiler.current_scope.dw address\n end\n\n macro addrx(opcode, address)\n Compiler.current_scope.db opcode\n Compiler.current_scope.dw address\n end\n\n macro longx(opcode, address)\n Compiler.current_scope.db opcode\n Compiler.current_scope.dw address & $FFFF\n Compiler.current_scope.db address >> 16\n end\n\n macro mv(opcode, left, right)\n Compiler.current_scope.db opcode\n Compiler.current_scope.db right\n Compiler.current_scope.db left\n end\n\n macro relw\n end\n\n macro dpy(opcode, address)\n Compiler.current_scope.db opcode\n Compiler.current_scope.db address\n end\n\n macro iaddrx(opcode, address)\n Compiler.current_scope.db opcode\n Compiler.current_scope.dw address\n end\n\n macro impl(opcode)\n Compiler.current_scope.db opcode\n end\n\n end\n\n RAM = CompilerMemoryManager.new\n Compiler.register_dynamic_memory RAM\n\n (0..255).each do |bank|\n RAM.allow bank: bank, range: 0..$FFFF\n end\n\n ROM = CompilerMemoryManager.new\n Compiler.register_static_memory ROM\n\n ;; All common data registers can be extended using this class\n class Register\n macro initialize(name)\n self.name = name\n end\n\n macro set_size(size)\n Compiler.current_scope.set_size self.name, size\n end\n\n macro size\n Compiler.current_scope.get_size self.name\n end\n\n macro A?\n self.name == \"A\"\n end\n\n macro X?\n self.name == \"X\"\n end\n\n macro Y?\n self.name == \"Y\"\n end\n\n macro S?\n self.name == \"S\"\n end\n\n macro size_8?\n self.size == 8\n end\n\n macro size_16?\n self.size == 16\n end\n end\n\n macro normalize_opcode_number(number)\n return {type: :none, number: nil} if number.nil?\n return {type: :addr, number: number} if number.is_a? Number\n\n if number.is_a? TypedNumber\n typed_number = number\n type = number.type\n number = number.number\n\n case type\n when :constant\n return {type: :constant, number: number}\n\n when :direct_page\n return {type: :dp, number: number}\n\n when :long_address\n if number.is_a? TypedNumber\n inner = normalize_opcode_number(number)\n return {type: \"long_#{inner.type}\", number: inner.number}\n else\n return {type: :long, number: number}\n end\n\n when :indirect\n if typed_number.parameter.nil?\n return {type: :indirect, number: number}\n elsif typed_number.parameter.X?\n return {type: :indirect_x, number: number}\n elsif typed_number.parameter.S?\n return {type: :indirect_s, number: number}\n elsif typed_number.parameter.Y?\n return {type: :indirect_y, number: number}\n else\n raise \"Unknown number type for opcode (INDIRECT)\"\n end\n\n else\n raise \"Unknown number type for opcode\"\n end\n elsif number.is_function?\n return {type: :addr, number: number}\n elsif number.is_a? Register\n return {type: number.name, number: nil}\n else\n return {type: :addr, number: number.to_future_number}\n end\n end\n\n ;; Invokes an opcode\n macro call_opcode(name, left=nil, right=nil)\n left = normalize_opcode_number left\n right = normalize_opcode_number right\n\n call_type = nil\n\n call_arguments = [left.number, right.number]\n call_arguments = [left.number] if right.number.nil?\n call_arguments = [] if left.number.nil?\n\n type = \"#{left.type}__#{right.type}\"\n\n case type\n when :none__none\n call_type = [:impl]\n when :A__none\n call_type = [:impl]\n when :constant__none\n call_type = [:im, :im_a, :im_x, :im_y, :im_16]\n when :addr__none\n call_type = [:addr, :addr_pc, :relb]\n when :addr__X\n call_type = [:addrx]\n when :addr__S\n call_type = [:sr]\n when :addr__Y\n call_type = [:addry]\n when :addr__addr\n call_type = [:mv]\n when :long__none\n call_type = [:long]\n when :long_indirect__none\n call_type = [:ildp]\n when :long_indirect__Y\n call_type = [:ildpy]\n when :dp__none\n call_type = [:dp]\n when :dp__X\n call_type = [:dpx]\n when :dp__Y\n call_type = [:dpy]\n when :long__X\n call_type = [:longx]\n when :indirect_x__none\n call_type = [:iaddrx]\n when :indirect__Y\n call_type = [:idpy]\n else\n raise \"Unexpected parameter type: #{type}\"\n end\n\n has_valid_call = false\n call_type.each do |type|\n if Snes65816.opcodes[name].key? type\n Compiler.current_scope.is_return_opcode = false\n\n config = Snes65816.opcodes[name][type]\n config.block.call config.opcode, *call_arguments\n has_valid_call = true\n end\n end\n\n unless has_valid_call\n Compiler.print call_type\n raise \"Opcode #{name} does not support parameter type\"\n end\n end\n\n ;; Registers a new operator\n macro operator(opcode, name, type, &block)\n name = name.upcase\n type = type.downcase\n\n macro ::\\{name} *args\n call_opcode name, *args\n end\n\n block = MacroPointer.new(&OpcodeWriter[type]) if block.nil?\n\n Snes65816.opcodes[name] = {} unless Snes65816.opcodes.key? name\n Snes65816.opcodes[name][type] = {\n opcode: opcode,\n block: block\n }\n end\n\n ;; Configures the ROM\n macro configure_banks(banks, address, shadows_banks_from=nil, shadows_addresses_from=nil, located_at=nil)\n if shadows_banks_from.nil? && shadows_addresses_from.nil?\n\n banks.each do |bank|\n ROM.allow bank: bank, range: address, located_at: located_at\n located_at += address.size\n end\n\n else\n\n banks.each do |bank|\n ROM.shadow bank: bank, range: address, shadow_bank: shadows_banks_from, shadow_address: shadows_addresses_from\n shadows_banks_from += 1\n end\n\n end\n end\n\n ;; Assigns the memory location to the scope\n macro locate_at(bank=nil, address=nil, range=nil, align=nil)\n address_and = nil\n address_or = nil\n\n unless bank.nil?\n address_and = $FFFF\n address_or = bank << 16\n end\n\n unless address.nil?\n range = address..address\n end\n\n Compiler.current_scope.locate_at range, address_and, address_or, align\n end\nend\n\n;; Allocates a new RAM scope\nmacro scope(name, bank=nil, at=nil, length=nil, in=Snes65816::RAM, shared=false, align=nil, shadows_bank=nil, shadows_address=nil, dump_usage=false)\n address_range = nil\n\n if in.nil?\n ram = Snes65816::RAM.allocate\n ram.detach\n else\n ram = in.allocate\n end\n ram.set_is_shared shared\n ram.set_dump_usage dump_usage\n\n unless bank.nil? && at.nil? && align.nil?\n ram.allow bank: bank, at: at, align: align\n end\n\n unless length.nil?\n ram.set_item_size length\n end\n\n unless shadows_bank.nil? && shadows_address.nil?\n range_from = at\n range_to = at + length - 1\n shadows_address = range_from if shadows_address.nil?\n ram.shadow bank: bank, range: range_from..range_to, shadow_bank: shadows_bank, shadow_address: shadows_address\n end\n\n callee[name] = ram\n ram\nend\n\n;; Declares a variable\nmacro declare(name, as, in=nil, bank=nil, at=nil, length=nil, range=nil)\n if callee.is_a? Class\n callee.on_initialize_members do |in|\n self[name] = declare name, as, in, bank, at, length\n end\n callee.register_variable_offset name, as, length\n return\n end\n\n raise \"Missing argument for declare: in\" if in.nil?\n\n ram = in.allocate\n\n unless bank.nil? && at.nil? && range.nil?\n ram.allow bank: bank, at: at, range: range\n end\n\n ram.set_item_type as\n ram.set_num_items length\n\n callee[name] = ram\n ram\nend\n\n;; Object\nclass Object\n macro A?\n false\n end\n\n macro X?\n false\n end\n\n macro Y?\n false\n end\n\n macro S?\n false\n end\nend\n\n;; Future number\nclass FutureNumber\n\n ;; Mark this number to be DP\n macro dp\n TypedNumber.new self, :direct_page\n end\n\n ;; Marks this number to be a long address\n macro long_address\n TypedNumber.new self, :long_address\n end\n\n ;; Mark this number to be DP\n macro indirect(parameter=nil)\n num = TypedNumber.new self, :indirect\n num.parameter = parameter\n num\n end\n\n ;; Marks this number to be a long address\n macro long_indirect(parameter=nil)\n TypedNumber.new self.indirect(parameter), :long_address\n end\n\n ;; Returns the address of this number without the bank\n macro address\n self & $FFFF\n end\n\n ;; Returns the bank of this number\n macro bank\n (self >> 16) & $FF\n end\n\nend\n\n;; Extends the number class to support ASM core handling\nclass Number\n\n ;; Mark this number to be DP\n macro dp\n TypedNumber.new self, :direct_page\n end\n\n ;; Marks this number to be a long address\n macro long_address\n TypedNumber.new self, :long_address\n end\n\n ;; Mark this number to be DP\n macro indirect(parameter=nil)\n num = TypedNumber.new self, :indirect\n num.parameter = parameter\n num\n end\n\n ;; Marks this number to be a long address\n macro long_indirect(parameter=nil)\n TypedNumber.new self.indirect(parameter), :long_address\n end\n\n ;; Returns the address of this number without the bank\n macro address\n self & $FFFF\n end\n\n ;; Returns the bank of this number\n macro bank\n (self >> 16) & $FF\n end\n\nend\n\n;; The accumulator\nA = Snes65816::Register.new \"A\"\n\n;; The index register X\nX = Snes65816::Register.new \"X\"\n\n;; The index register Y\nY = Snes65816::Register.new \"Y\"\n\n;; The stack register\nS = Snes65816::Register.new \"S\"\n\n;; Support dp(number) syntax\nmacro dp(number)\n number.dp\nend\n\n;; Indirect\nmacro indirect(number, parameter=nil)\n number.indirect(parameter)\nend\n\n;; Indirect\nmacro long_indirect(number, parameter=nil)\n number.long_indirect(parameter)\nend\n\n;; Sets the location of all functions\nmacro @locate_at(**kwargs)\n Snes65816.locate_at **kwargs\n\n yield\nend\n\n;; Tests registers\nmacro @requires(A=nil, XY=nil)\n unless A.nil?\n raise \"Register A is required to be of size #{A}\" unless ::A.size == A\n end\n\n unless XY.nil?\n raise \"Register X/Y is required to be of size #{XY}\" unless ::X.size == XY\n end\n\n yield\nend\n\n;; Sets the size of all functions\nmacro @register(A=nil, XY=nil)\n ::A.set_size A unless A.nil?\n ::X.set_size XY unless XY.nil?\n\n yield\nend\n\n;; Memory block declaration\nmacro memory_block(as=nil, name=nil, **kwargs)\n var = Compiler.define name do\n Snes65816.locate_at **kwargs\n\n yield Compiler.current_scope\n\n Compiler.current_scope.is_return_opcode = true\n end\n\n callee[as] = var.to_future_number unless as.nil?\n var.to_future_number\nend\n\nclass __RoutineList\n macro initialize(a, xy)\n self.a = a\n self.xy = xy\n end\n\n macro add(addr)\n addr.register_size_A = self.a unless self.a.nil?\n addr.register_size_XY = self.xy unless self.xy.nil?\n Compiler.current_scope.dw addr\n end\nend\n\n;; Creates a list of routines\nmacro routine_list(as=nil, name=nil, A=nil, XY=nil, **kwargs)\n var = Compiler.define name do\n Snes65816.locate_at **kwargs\n yield __RoutineList.new(A, XY)\n Compiler.current_scope.is_return_opcode = true\n end\n\n callee[as] = var.to_future_number unless as.nil?\n var.to_future_number\nend\n";
//# sourceMappingURL=snes65816.js.map