mirror of
https://github.com/mattrberry/crab.git
synced 2025-01-30 20:34:45 +01:00
move instr lookup to lookup table, move instrs into module across files
This commit is contained in:
parent
ea5322b0fa
commit
b026160553
5 changed files with 181 additions and 173 deletions
128
src/crab/arm/arm.cr
Normal file
128
src/crab/arm/arm.cr
Normal 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
11
src/crab/arm/branch.cr
Normal 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
|
36
src/crab/arm/data_processing.cr
Normal file
36
src/crab/arm/data_processing.cr
Normal 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
|
125
src/crab/cpu.cr
125
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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue