Class: SnesUtils::LineAssembler
- Inherits:
-
Object
- Object
- SnesUtils::LineAssembler
- Defined in:
- lib/vas/vas.rb
Instance Method Summary collapse
- #assemble ⇒ Object
- #bit_instruction? ⇒ Boolean
- #contains_label?(operand) ⇒ Boolean
- #detect_opcode(mnemonic, operand) ⇒ Object
- #detect_operand(raw_operand) ⇒ Object
- #double_operand_instruction? ⇒ Boolean
- #fx_mov_instruction? ⇒ Boolean
-
#initialize(raw_line, **options) ⇒ LineAssembler
constructor
A new instance of LineAssembler.
- #inverted_dest_instruction? ⇒ Boolean
- #little_endian(operand, length) ⇒ Object
- #process_bit_operand(operand_data) ⇒ Object
- #process_double_operand_instruction(operand_data) ⇒ Object
- #process_fx_instruction(opcode_data, operand_data) ⇒ Object
- #process_fx_operand(operand_data, alt) ⇒ Object
- #process_operand(operand_data) ⇒ Object
- #process_rel_operand(operand) ⇒ Object
- #rel_instruction? ⇒ Boolean
- #replace_label(operand) ⇒ Object
- #replace_labels(operand) ⇒ Object
- #short_addr_instruction? ⇒ Boolean
- #special_fx_instruction?(alt) ⇒ Boolean
Constructor Details
#initialize(raw_line, **options) ⇒ LineAssembler
Returns a new instance of LineAssembler.
387 388 389 390 391 392 |
# File 'lib/vas/vas.rb', line 387 def initialize(raw_line, **) @line = raw_line.split(';').first.strip.chomp @current_address = ([:program_counter] + [:origin]) @cpu = [:cpu] @label_registry = [:label_registry] end |
Instance Method Details
#assemble ⇒ Object
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/vas/vas.rb', line 394 def assemble instruction = @line.split(' ') mnemonic = instruction[0].upcase raw_operand = instruction[1].to_s raw_operand = Vas.replace_eval_label(@label_registry, raw_operand) raw_operand = replace_label(raw_operand).downcase # TODO -> generalize replace_label_eval opcode_data = detect_opcode(mnemonic, raw_operand) raise "Invalid syntax #{@line}" unless opcode_data opcode = opcode_data[:opcode] @mode = opcode_data[:mode] @length = opcode_data[:length] operand_data = detect_operand(raw_operand) return process_fx_instruction(opcode_data, operand_data) if special_fx_instruction?(opcode_data[:alt]) operand = process_operand(operand_data) return [opcode, *operand] end |
#bit_instruction? ⇒ Boolean
585 586 587 |
# File 'lib/vas/vas.rb', line 585 def bit_instruction? SnesUtils.const_get(@cpu.capitalize)::Definitions::BIT_INSTRUCTIONS.include?(@mode) end |
#contains_label?(operand) ⇒ Boolean
418 419 420 |
# File 'lib/vas/vas.rb', line 418 def contains_label?(operand) Vas::LABEL_OPERATORS.any? { |s| operand.include?(s[-1,1]) } end |
#detect_opcode(mnemonic, operand) ⇒ Object
471 472 473 474 475 476 477 |
# File 'lib/vas/vas.rb', line 471 def detect_opcode(mnemonic, operand) SnesUtils.const_get(@cpu.capitalize)::Definitions::OPCODES_DATA.detect do |row| mode = row[:mode] regex = SnesUtils.const_get(@cpu.capitalize)::Definitions::MODES_REGEXES[mode] row[:mnemonic] == mnemonic && regex =~ operand end end |
#detect_operand(raw_operand) ⇒ Object
479 480 481 |
# File 'lib/vas/vas.rb', line 479 def detect_operand(raw_operand) SnesUtils.const_get(@cpu.capitalize)::Definitions::MODES_REGEXES[@mode].match(raw_operand) end |
#double_operand_instruction? ⇒ Boolean
581 582 583 |
# File 'lib/vas/vas.rb', line 581 def double_operand_instruction? SnesUtils.const_get(@cpu.capitalize)::Definitions::DOUBLE_OPERAND_INSTRUCTIONS.include?(@mode) end |
#fx_mov_instruction? ⇒ Boolean
597 598 599 |
# File 'lib/vas/vas.rb', line 597 def fx_mov_instruction? @cpu == Vas::SUPERFX && SnesUtils.const_get(@cpu.capitalize)::Definitions::MOV_INSTRUCTIONS.include?(@mode) end |
#inverted_dest_instruction? ⇒ Boolean
605 606 607 |
# File 'lib/vas/vas.rb', line 605 def inverted_dest_instruction? @cpu == Vas::SUPERFX && SnesUtils.const_get(@cpu.capitalize)::Definitions::INV_DEST_INSTRUCTIONS.include?(@mode) end |
#little_endian(operand, length) ⇒ Object
571 572 573 574 575 576 577 578 579 |
# File 'lib/vas/vas.rb', line 571 def little_endian(operand, length) if length > 2 [((operand >> 0) & 0xff), ((operand >> 8) & 0xff), ((operand >> 16) & 0xff)] elsif length > 1 [((operand >> 0) & 0xff), ((operand >> 8) & 0xff)] else operand end end |
#process_bit_operand(operand_data) ⇒ Object
545 546 547 548 549 550 551 552 553 |
# File 'lib/vas/vas.rb', line 545 def process_bit_operand(operand_data) m = operand_data[1].to_i(16) raise "Out of range: m > 0x1fff: #{m}" if m > 0x1fff b = operand_data[2].to_i(16) raise "Out of range: b > 7: #{b}" if b > 7 little_endian(m << 3 | b, 2) end |
#process_double_operand_instruction(operand_data) ⇒ Object
534 535 536 537 538 539 540 541 542 543 |
# File 'lib/vas/vas.rb', line 534 def process_double_operand_instruction(operand_data) if bit_instruction? process_bit_operand(operand_data) else operands = [operand_data[1], operand_data[2]].map { |o| o.to_i(16) } operand_2 = rel_instruction? ? process_rel_operand(operands[1]) : operands[1] rel_instruction? ? [operands[0], operand_2] : [operand_2, operands[0]] end end |
#process_fx_instruction(opcode_data, operand_data) ⇒ Object
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 |
# File 'lib/vas/vas.rb', line 492 def process_fx_instruction(opcode_data, operand_data) alt = opcode_data[:alt] return [alt, opcode_data[:opcode]].compact if @mode == :imp if fx_mov_instruction? reg1 = operand_data[1].to_i(10) raise "Invalid register R#{reg1}" if reg1 > 15 reg2 = operand_data[2].to_i(10) raise "Invalid register R#{reg2}" if reg2 > 15 raw_opcode = opcode_data[:opcode] tmp_opcode = raw_opcode | (reg2 << 8) | reg1 opcode = [((tmp_opcode >> 8) & 0xff), ((tmp_opcode >> 0) & 0xff)] operand = nil else base = @mode == :imm4 ? 16 : 10 index = inverted_dest_instruction? ? 2 : 1 opcode_suffix = operand_data[index].to_i(base) opcode_prefix = opcode_data[:opcode] opcode = (opcode_prefix << 4) | opcode_suffix operand = process_fx_operand(operand_data, alt.nil? ? 0 : 1) end [alt, *opcode, *operand].compact end |
#process_fx_operand(operand_data, alt) ⇒ Object
522 523 524 525 526 527 528 529 530 531 532 |
# File 'lib/vas/vas.rb', line 522 def process_fx_operand(operand_data, alt) return unless double_operand_instruction? index = inverted_dest_instruction? ? 1 : 2 operand = operand_data[index]&.to_i(16) raise 'Invalid address, must be multiple of 2' if short_addr_instruction? && operand.odd? operand /= 2 if short_addr_instruction? little_endian(operand, @length - 1 - alt) end |
#process_operand(operand_data) ⇒ Object
483 484 485 486 487 488 489 490 |
# File 'lib/vas/vas.rb', line 483 def process_operand(operand_data) if double_operand_instruction? process_double_operand_instruction(operand_data) else operand = operand_data[1]&.to_i(16) rel_instruction? ? process_rel_operand(operand) : little_endian(operand, @length - 1) end end |
#process_rel_operand(operand) ⇒ Object
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 |
# File 'lib/vas/vas.rb', line 555 def process_rel_operand(operand) relative_addr = operand - (@current_address & 0x00ffff) - @length if @cpu == Vas::WDC65816 && @mode == :rell raise "Relative address out of range: #{relative_addr}" if relative_addr < -32_768 || relative_addr > 32_767 relative_addr += 0x10000 if relative_addr < 0 little_endian(relative_addr, 2) else raise "Relative address out of range: #{relative_addr}" if relative_addr < -128 || relative_addr > 127 relative_addr += 0x100 if relative_addr < 0 relative_addr end end |
#rel_instruction? ⇒ Boolean
589 590 591 |
# File 'lib/vas/vas.rb', line 589 def rel_instruction? SnesUtils.const_get(@cpu.capitalize)::Definitions::REL_INSTRUCTIONS.include?(@mode) end |
#replace_label(operand) ⇒ Object
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 |
# File 'lib/vas/vas.rb', line 430 def replace_label(operand) return operand unless contains_label?(operand) unless matches = /(#{Vas::LABEL_OPERATORS.join('|')})(\w+)(\+(\d+))?/.match(operand) raise "Invalid label syntax: #{operand}" end mode = matches[1] label = matches[2] offset = matches[4].to_i label_data = @label_registry.detect { |l| l[0] == label } value = label_data ? label_data[1] : @current_address value += offset case mode when '@' value = value & 0x00ffff new_value = Vas::hex(value, 4) when '!' value = value # | (((@current_address >> 16) & 0xff) << 16) # BUG HERE new_value = Vas::hex(value, 6) when '<' value = value & 0x0000ff new_value = Vas::hex(value) when '>' value = (value & 0x00ff00) >> 8 new_value = Vas::hex(value) when '^' mode = '\^' value = (value & 0xff0000) >> 16 new_value = Vas::hex(value) else raise "Mode error: #{mode}" end operand.gsub(/(#{mode})\w+(\+(\d+))?/, new_value) end |
#replace_labels(operand) ⇒ Object
422 423 424 425 426 427 428 |
# File 'lib/vas/vas.rb', line 422 def replace_labels(operand) while contains_label?(operand) operand = replace_label(operand) end operand end |