From b026160553b226fa1a252b61c2f86a65000a1861 Mon Sep 17 00:00:00 2001 From: Matthew Berry Date: Wed, 16 Sep 2020 22:54:15 -0700 Subject: [PATCH] move instr lookup to lookup table, move instrs into module across files --- src/crab/arm/arm.cr | 128 ++++++++++++++++++++++++++++++++ src/crab/arm/branch.cr | 11 +++ src/crab/arm/data_processing.cr | 36 +++++++++ src/crab/cpu.cr | 125 ++----------------------------- src/crab/types.cr | 54 -------------- 5 files changed, 181 insertions(+), 173 deletions(-) create mode 100644 src/crab/arm/arm.cr create mode 100644 src/crab/arm/branch.cr create mode 100644 src/crab/arm/data_processing.cr diff --git a/src/crab/arm/arm.cr b/src/crab/arm/arm.cr new file mode 100644 index 0000000..df1b87d --- /dev/null +++ b/src/crab/arm/arm.cr @@ -0,0 +1,128 @@ +# The `private defs` here are effectively meaningless since I only run ARM +# functions from the CPU where I include it, but I'm just using it as an +# indicator that the functions should not be called directly outside of the +# module. + +module ARM + def arm_execute(instr : Word) : Nil + if check_cond instr + hash = hash_instr instr + lut[hash].call instr + else + log "Skipping instruction, cond: #{hex_str instr >> 28}" + end + end + + private def hash_instr(instr : Word) : Word + (instr >> 16 & 0x0FF0) | (instr >> 4 & 0xF) + end + + private def check_cond(cond : Word) : Bool + case bits cond, 28..31 + when 0x0 then bit?(@cpsr, 30) # Z + when 0x1 then !bit?(@cpsr, 30) # !Z + when 0x2 then bit?(@cpsr, 29) # C + when 0x3 then !bit?(@cpsr, 29) # !C + when 0x4 then bit?(@cpsr, 31) # N + when 0x5 then !bit?(@cpsr, 31) # !N + when 0x6 then bit?(@cpsr, 28) # V + when 0x7 then !bit?(@cpsr, 28) # !V + when 0x8 then bit?(@cpsr, 29) && !bit?(@cpsr, 30) # C && !Z + when 0x9 then !bit?(@cpsr, 29) || bit?(@cpsr, 30) # !C || Z + when 0xA then bit?(@cpsr, 31) == bit?(@cpsr, 28) # N == V + when 0xB then bit?(@cpsr, 31) != bit?(@cpsr, 28) # N != V + when 0xC then !bit?(@cpsr, 30) && bit?(@cpsr, 31) == bit?(@cpsr, 28) # !Z && N == V + when 0xD then bit?(@cpsr, 30) || bit?(@cpsr, 31) != bit?(@cpsr, 28) # Z || N != V + when 0xE then true # always + else raise "Cond 0xF is reserved" + end + end + + def fill_lut : Slice(Proc(Word, Nil)) + lut = Slice(Proc(Word, Nil)).new 4096, ->arm_unimplemented(Word) + 4096.times do |idx| + if idx & 0b111100000000 == 0b111100000000 + # software interrupt + elsif idx & 0b111100000001 == 0b111000000001 + # coprocessor register transfer + elsif idx & 0b111100000001 == 0b111000000001 + # coprocessor data operation + elsif idx & 0b111000000000 == 0b110000000000 + # coprocessor data transfer + elsif idx & 0b111000000000 == 0b101000000000 + lut[idx] = ->arm_branch(Word) + elsif idx & 0b111000000000 == 0b100000000000 + # block data transfer + elsif idx & 0b111000000001 == 0b011000000001 + # undefined + elsif idx & 0b110000000000 == 0b010000000000 + # single data transfer + elsif idx & 0b111001001001 == 0b000001001001 + # halfword data transfer immediate offset + elsif idx & 0b111001001001 == 0b000000001001 + # halfword data transfer register offset + elsif idx & 0b111111111111 == 0b000100100001 + # branch exchange + elsif idx & 0b111110111111 == 0b000100001001 + # single data swap + elsif idx & 0b111110001111 == 0b000010001001 + # multiply long + elsif idx & 0b111111001111 == 0b000000001001 + # multiply + elsif idx & 0b110000000000 == 0b000000000000 + lut[idx] = ->arm_data_processing(Word) + else + lut[idx] = ->arm_unimplemented(Word) + end + end + lut + end + + def arm_unimplemented(instr : Word) : Nil + puts "Unimplemented instruction: #{hex_str instr}" + exit 1 + end + + def arm_unused(instr : Word) : Nil + puts "Unused instruction: #{hex_str instr}" + end + + # Logical shift left + def lsl(word : Word, bits : Int) : Word + word << bits + end + + # Logical shift right + def lsr(word : Word, bits : Int) : Word + word >> bits + end + + # Arithmetic shift right + def asr(word : Word, bits : Int) : Word + word // (2 ** bits) + end + + # Rotate right + def ror(word : Word, bits : Int) : Word + word >> bits | word << (32 - bits) + end + + def rotate_register(instr : Word) : Word + shift = bits(instr, 4..11) + reg = bits(instr, 0..3) + shift_type = bits(instr, 5..6) + shift_amount = if bit?(instr, 4) + shift_register = bits(instr, 8..11) + @r[shift_register] & 0xF + else + bits(instr, 7..11) + end + case shift_type + when 0b00 then lsl(reg, shift_amount) + when 0b01 then lsr(reg, shift_amount) + when 0b10 then asr(reg, shift_amount) + when 0b11 then ror(reg, shift_amount) + else raise "Impossible shift type: #{hex_str shift_type}" + end + end +end diff --git a/src/crab/arm/branch.cr b/src/crab/arm/branch.cr new file mode 100644 index 0000000..ad8b42d --- /dev/null +++ b/src/crab/arm/branch.cr @@ -0,0 +1,11 @@ +module ARM + def arm_branch(instr : Word) : Nil + link = bit?(instr, 24) + offset = instr & 0xFFFFFF + offset = (~offset &+ 1).to_i32 if bit?(offset, 23) # negative + offset <<= 2 + @r[14] = @r[15] - 4 if link + @r[15] &+= offset + clear_pipeline + end +end diff --git a/src/crab/arm/data_processing.cr b/src/crab/arm/data_processing.cr new file mode 100644 index 0000000..b7a8471 --- /dev/null +++ b/src/crab/arm/data_processing.cr @@ -0,0 +1,36 @@ +module ARM + def arm_data_processing(instr : Word) : Nil + # todo all resulting flags + imm_flag = bit?(instr, 25) + opcode = bits(instr, 21..24) + set_conditions = bit?(instr, 20) + rn = bits(instr, 16..19) + rd = bits(instr, 12..15) + operand_2 = if imm_flag # Operand 2 is an immediate + rotate = bits(instr, 8..11) + imm = bits(instr, 0..7) + ror(imm, 2 * rotate) + else # Operand 2 is a register + rotate_register bits(instr, 0..11) + end + case opcode + when 0x0 then @r[rd] = rn & operand_2 + when 0x1 then @r[rd] = rn ^ operand_2 + when 0x2 then @r[rd] = rn &- operand_2 + when 0x3 then @r[rd] = operand_2 &- rn + when 0x4 then @r[rd] = rn &+ operand_2 + when 0x5 then @r[rd] = rn &+ operand_2 &+ (bit?(@cpsr, 29) ? 1 : 0) + when 0x6 then @r[rd] = rn &- operand_2 &+ (bit?(@cpsr, 29) ? 1 : 0) &- 1 + when 0x7 then @r[rd] = operand_2 &- rn &+ (bit?(@cpsr, 29) ? 1 : 0) &- 1 + when 0x8 + when 0x9 + when 0xA + when 0xB + when 0xC then @r[rd] = rn | operand_2 + when 0xD then @r[rd] = operand_2 + when 0xE then @r[rd] = rn & ~operand_2 + when 0xF then @r[rd] = ~operand_2 + else raise "Unimplemented execution of data processing opcode: #{hex_str opcode}" + end + end +end diff --git a/src/crab/cpu.cr b/src/crab/cpu.cr index 9c5d54f..e2b528c 100644 --- a/src/crab/cpu.cr +++ b/src/crab/cpu.cr @@ -1,7 +1,12 @@ +require "./arm/*" + class CPU + include ARM + @r = Slice(Word).new 16 @cpsr : UInt32 = 0 @pipeline = Deque(Word).new 2 + getter lut : Slice(Proc(Word, Nil)) { fill_lut } def initialize(@gba : GBA) @r[0] = 0x08000000 @@ -24,129 +29,11 @@ class CPU @pipeline.clear end - def check_cond(cond : Int) : Bool - case cond & 0xF - when 0x0 then bit?(@cpsr, 30) # Z - when 0x1 then !bit?(@cpsr, 30) # !Z - when 0x2 then bit?(@cpsr, 29) # C - when 0x3 then !bit?(@cpsr, 29) # !C - when 0x4 then bit?(@cpsr, 31) # N - when 0x5 then !bit?(@cpsr, 31) # !N - when 0x6 then bit?(@cpsr, 28) # V - when 0x7 then !bit?(@cpsr, 28) # !V - when 0x8 then bit?(@cpsr, 29) && !bit?(@cpsr, 30) # C && !Z - when 0x9 then !bit?(@cpsr, 29) || bit?(@cpsr, 30) # !C || Z - when 0xA then bit?(@cpsr, 31) == bit?(@cpsr, 28) # N == V - when 0xB then bit?(@cpsr, 31) != bit?(@cpsr, 28) # N != V - when 0xC then !bit?(@cpsr, 30) && bit?(@cpsr, 31) == bit?(@cpsr, 28) # !Z && N == V - when 0xD then bit?(@cpsr, 30) || bit?(@cpsr, 31) != bit?(@cpsr, 28) # Z || N != V - when 0xE then true # always - else raise "Cond 0xF is reserved" - end - end - def tick : Nil fill_pipeline instr = @pipeline.shift print_state instr - execute instr - end - - # Logical shift left - def lsl(word : Word, bits : Int) : Word - word << bits - end - - # Logical shift right - def lsr(word : Word, bits : Int) : Word - word >> bits - end - - # Arithmetic shift right - def asr(word : Word, bits : Int) : Word - word // (2 ** bits) - end - - # Rotate right - def ror(word : Word, bits : Int) : Word - word >> bits | word << (32 - bits) - end - - def rotate_register(instr : Word) : Word - shift = bits(instr, 4..11) - reg = bits(instr, 0..3) - shift_type = bits(instr, 5..6) - shift_amount = if bit?(instr, 4) - shift_register = bits(instr, 8..11) - @r[shift_register] & 0xF - else - bits(instr, 7..11) - end - case shift_type - when 0b00 then lsl(reg, shift_amount) - when 0b01 then lsr(reg, shift_amount) - when 0b10 then asr(reg, shift_amount) - when 0b11 then ror(reg, shift_amount) - else raise "Impossible shift type: #{hex_str shift_type}" - end - end - - def hash_instr(instr : Word) : Word - ((instr >> 16) & 0x0FF0) | ((instr >> 4) & 0xF) - end - - def execute(instr : Word) : Nil - if check_cond instr >> 28 - hash = hash_instr instr - instr_type = Instr.from_hash hash - log "Execute #{hex_str instr}: #{instr_type}" - case instr_type - when Instr::BRANCH - link = bit?(instr, 24) - offset = instr & 0xFFFFFF - offset = (~offset &+ 1).to_i32 if bit?(offset, 23) # negative - offset <<= 2 - @r[14] = @r[15] - 4 if link - @r[15] &+= offset - clear_pipeline - when Instr::DATA_PROCESSING_PSR_TRANSFER - # todo all resulting flags - imm_flag = bit?(instr, 25) - opcode = bits(instr, 21..24) - set_conditions = bit?(instr, 20) - rn = bits(instr, 16..19) - rd = bits(instr, 12..15) - operand_2 = if imm_flag # Operand 2 is an immediate - rotate = bits(instr, 8..11) - imm = bits(instr, 0..7) - ror(imm, 2 * rotate) - else # Operand 2 is a register - rotate_register bits(instr, 0..11) - end - case opcode - when 0x0 then @r[rd] = rn & operand_2 - when 0x1 then @r[rd] = rn ^ operand_2 - when 0x2 then @r[rd] = rn &- operand_2 - when 0x3 then @r[rd] = operand_2 &- rn - when 0x4 then @r[rd] = rn &+ operand_2 - when 0x5 then @r[rd] = rn &+ operand_2 &+ (bit?(@cpsr, 29) ? 1 : 0) - when 0x6 then @r[rd] = rn &- operand_2 &+ (bit?(@cpsr, 29) ? 1 : 0) &- 1 - when 0x7 then @r[rd] = operand_2 &- rn &+ (bit?(@cpsr, 29) ? 1 : 0) &- 1 - when 0x8 - when 0x9 - when 0xA - when 0xB - when 0xC then @r[rd] = rn | operand_2 - when 0xD then @r[rd] = operand_2 - when 0xE then @r[rd] = rn & ~operand_2 - when 0xF then @r[rd] = ~operand_2 - else raise "Unimplemented execution of data processing opcode: #{hex_str opcode}" - end - else raise "Unimplemented execution of type: #{instr_type}" - end - else - puts "Skipping instruction, cond: #{hex_str instr >> 28}" - end + arm_execute instr end def print_state(instr : Word) : Nil diff --git a/src/crab/types.cr b/src/crab/types.cr index 79fb38e..fa8a2ae 100644 --- a/src/crab/types.cr +++ b/src/crab/types.cr @@ -1,57 +1,3 @@ alias Byte = UInt8 alias Word = UInt32 alias Words = Slice(UInt32) - -enum Instr - DATA_PROCESSING_PSR_TRANSFER - MULTIPLY - MULTIPLY_LONG - SINGLE_DATA_SWAP - BRANCH_EXCHANGE - HALFWORD_DATA_TRANSFER_REGISTER_OFFSET - HALFWORD_DATA_TRANSFER_IMMEDIATE_OFFSET - SINGLE_DATA_TRANSFER - UNDEFINED - BLOCK_DATA_TRANSFER - BRANCH - COPROCESSOR_DATA_TRANSFER - COPROCESSOR_DATA_OPERATION - COPROCESSOR_REGISTER_TRANSFER - SOFTWARE_INTERRUPT - - def self.from_hash(hash : Word) : Instr - if hash & 0b111100000000 == 0b111100000000 - SOFTWARE_INTERRUPT - elsif hash & 0b111100000001 == 0b111000000001 - COPROCESSOR_REGISTER_TRANSFER - elsif hash & 0b111100000001 == 0b111000000001 - COPROCESSOR_DATA_OPERATION - elsif hash & 0b111000000000 == 0b110000000000 - COPROCESSOR_DATA_TRANSFER - elsif hash & 0b111000000000 == 0b101000000000 - BRANCH - elsif hash & 0b111000000000 == 0b100000000000 - BLOCK_DATA_TRANSFER - elsif hash & 0b111000000001 == 0b011000000001 - UNDEFINED - elsif hash & 0b110000000000 == 0b010000000000 - SINGLE_DATA_TRANSFER - elsif hash & 0b111001001001 == 0b000001001001 - HALFWORD_DATA_TRANSFER_IMMEDIATE_OFFSET - elsif hash & 0b111001001001 == 0b000000001001 - HALFWORD_DATA_TRANSFER_REGISTER_OFFSET - elsif hash & 0b111111111111 == 0b000100100001 - BRANCH_EXCHANGE - elsif hash & 0b111110111111 == 0b000100001001 - SINGLE_DATA_SWAP - elsif hash & 0b111110001111 == 0b000010001001 - MULTIPLY_LONG - elsif hash & 0b111111001111 == 0b000000001001 - MULTIPLY - elsif hash & 0b110000000000 == 0b000000000000 - DATA_PROCESSING_PSR_TRANSFER - else - raise "Unimplemented from_hash #{hex_str hash}" - end - end -end