move instr lookup to lookup table, move instrs into module across files

This commit is contained in:
Matthew Berry 2020-09-16 22:54:15 -07:00
parent ea5322b0fa
commit b026160553
5 changed files with 181 additions and 173 deletions

128
src/crab/arm/arm.cr Normal file
View file

@ -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

11
src/crab/arm/branch.cr Normal file
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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