bit of renaming , cleaning and documentation
This commit is contained in:
parent
6261451c4b
commit
f1a7993b47
@ -1,12 +1,7 @@
|
|||||||
Assembler in Ruby
|
Assembler in Ruby
|
||||||
=================
|
=================
|
||||||
|
|
||||||
Supporting arm, but aimed quite specifically at raspberry pi, arm v7, floating point included
|
Supporting arm, but aimed quite specifically at raspberry pi, arm v7, floating point included (later)
|
||||||
|
|
||||||
Outputs Elf object files, with relocation support.
|
|
||||||
|
|
||||||
Constant table support exists but isn't very good. Some addressing modes
|
|
||||||
are not supported or only partially supported.
|
|
||||||
|
|
||||||
Supported (pseudo)instructions:
|
Supported (pseudo)instructions:
|
||||||
|
|
||||||
@ -14,5 +9,5 @@ Supported (pseudo)instructions:
|
|||||||
mov, mvn, strb, str, ldrb, ldr, push, pop, b, bl, bx, swi
|
mov, mvn, strb, str, ldrb, ldr, push, pop, b, bl, bx, swi
|
||||||
- Conditional versions of above
|
- Conditional versions of above
|
||||||
|
|
||||||
Thanks to Cyndis for starting this arm/elf project in the first place: https://github.com/cyndis/as
|
Thanks to Mikko for starting this arm/elf project in the first place: https://github.com/cyndis/as
|
||||||
|
|
||||||
|
@ -16,12 +16,12 @@ module Asm
|
|||||||
end
|
end
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@values = []
|
@codes = []
|
||||||
@position = 0 # marks not set
|
@position = 0 # marks not set
|
||||||
@labels = []
|
@labels = []
|
||||||
@string_table = {}
|
@string_table = {}
|
||||||
end
|
end
|
||||||
attr_reader :values , :position
|
attr_reader :codes , :position
|
||||||
|
|
||||||
def instruction(clazz,name, *args)
|
def instruction(clazz,name, *args)
|
||||||
opcode = name.to_s
|
opcode = name.to_s
|
||||||
@ -39,7 +39,7 @@ module Asm
|
|||||||
raise "Invalid argument #{arg.inspect} for instruction"
|
raise "Invalid argument #{arg.inspect} for instruction"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
add_value clazz.new(opcode , arg_nodes)
|
add_code clazz.new(opcode , arg_nodes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ module Asm
|
|||||||
#put the strings at the end of the assembled code.
|
#put the strings at the end of the assembled code.
|
||||||
# adding them will fix their position and make them assemble after
|
# adding them will fix their position and make them assemble after
|
||||||
@string_table.values.each do |data|
|
@string_table.values.each do |data|
|
||||||
add_value data
|
add_code data
|
||||||
end
|
end
|
||||||
io = StringIO.new
|
io = StringIO.new
|
||||||
assemble(io)
|
assemble(io)
|
||||||
@ -93,8 +93,8 @@ module Asm
|
|||||||
end
|
end
|
||||||
|
|
||||||
def add_string str
|
def add_string str
|
||||||
value = @string_table[str]
|
code = @string_table[str]
|
||||||
return value if value
|
return code if code
|
||||||
data = Asm::StringLiteral.new(str)
|
data = Asm::StringLiteral.new(str)
|
||||||
@string_table[str] = data
|
@string_table[str] = data
|
||||||
end
|
end
|
||||||
@ -103,11 +103,11 @@ module Asm
|
|||||||
@string_table.values
|
@string_table.values
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_value(val)
|
def add_code(kode)
|
||||||
val.at(@position)
|
kode.at(@position)
|
||||||
length = val.length
|
length = kode.length
|
||||||
@position += length
|
@position += length
|
||||||
@values << val
|
@codes << kode
|
||||||
end
|
end
|
||||||
|
|
||||||
def label name
|
def label name
|
||||||
@ -116,12 +116,8 @@ module Asm
|
|||||||
label
|
label
|
||||||
end
|
end
|
||||||
|
|
||||||
def label! name
|
|
||||||
label(name).set!
|
|
||||||
end
|
|
||||||
|
|
||||||
def assemble(io)
|
def assemble(io)
|
||||||
@values.each do |obj|
|
@codes.each do |obj|
|
||||||
obj.assemble io
|
obj.assemble io
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
module Asm
|
module Asm
|
||||||
# ADDRESSING MODE 4 , Calling
|
# There are only three call instructions in arm branch (b), call (bl) and syscall (swi)
|
||||||
|
|
||||||
|
# A branch could be called a jump as it has no notion of returning
|
||||||
|
|
||||||
|
# A call has the bl code as someone thought "branch with link" is a useful name.
|
||||||
|
# The pc is put into the link register to make a return possible
|
||||||
|
# a return is affected by moving the stored link register into the pc, effectively a branch
|
||||||
|
|
||||||
|
# swi (SoftWareInterrupt) or system call is how we call the kernel.
|
||||||
|
# in Arm the register layout is different and so we have to place the syscall code into register 7
|
||||||
|
# Registers 0-6 hold the call values as for a normal c call
|
||||||
|
|
||||||
class CallInstruction < Instruction
|
class CallInstruction < Instruction
|
||||||
include Asm::InstructionTools
|
|
||||||
|
|
||||||
def initialize(opcode , args)
|
def initialize(opcode , args)
|
||||||
super(opcode,args)
|
super(opcode,args)
|
||||||
end
|
end
|
||||||
|
|
||||||
def assemble(io)
|
def assemble(io)
|
||||||
s = @update_status_flag? 1 : 0
|
|
||||||
case opcode
|
case opcode
|
||||||
when :b, :bl
|
when :b, :bl
|
||||||
arg = args[0]
|
arg = args[0]
|
||||||
|
@ -34,11 +34,18 @@ module Asm
|
|||||||
@args = args
|
@args = args
|
||||||
@operand = 0
|
@operand = 0
|
||||||
end
|
end
|
||||||
attr_reader :opcode, :args , :position , :cond , :operand , :update_status_flag
|
|
||||||
|
attr_reader :opcode, :args
|
||||||
|
# Many arm instructions may be conditional, where the default condition is always (al)
|
||||||
|
# InstructionTools::COND_CODES names them, and this attribute reflects it
|
||||||
|
attr_reader :cond
|
||||||
|
attr_reader :operand
|
||||||
|
|
||||||
def affect_status
|
# Logic instructions may be executed with or without affecting the status register
|
||||||
@s
|
# Only when an instruction affects the status is a subsequent compare instruction effective
|
||||||
end
|
# But to make the conditional execution (see cond) work for more than one instruction, one needs to
|
||||||
|
# be able to execute without changing the status
|
||||||
|
attr_reader :update_status_flag
|
||||||
|
|
||||||
# arm intrucioons are pretty sensible, and always 4 bytes (thumb not supported)
|
# arm intrucioons are pretty sensible, and always 4 bytes (thumb not supported)
|
||||||
def length
|
def length
|
||||||
|
@ -44,10 +44,6 @@ module Asm
|
|||||||
COND_CODES[@cond] or throw "no code found for #{@cond}"
|
COND_CODES[@cond] or throw "no code found for #{@cond}"
|
||||||
end
|
end
|
||||||
|
|
||||||
OPC_DATA_PROCESSING = 0b00
|
|
||||||
OPC_MEMORY_ACCESS = 0b01
|
|
||||||
OPC_STACK = 0b10
|
|
||||||
|
|
||||||
REGISTERS = { 'r0' => 0, 'r1' => 1, 'r2' => 2, 'r3' => 3, 'r4' => 4, 'r5' => 5,
|
REGISTERS = { 'r0' => 0, 'r1' => 1, 'r2' => 2, 'r3' => 3, 'r4' => 4, 'r5' => 5,
|
||||||
'r6' => 6, 'r7' => 7, 'r8' => 8, 'r9' => 9, 'r10' => 10, 'r11' => 11,
|
'r6' => 6, 'r7' => 7, 'r8' => 8, 'r9' => 9, 'r10' => 10, 'r11' => 11,
|
||||||
'r12' => 12, 'r13' => 13, 'r14' => 14, 'r15' => 15, 'a1' => 0, 'a2' => 1,
|
'r12' => 12, 'r13' => 13, 'r14' => 14, 'r15' => 15, 'a1' => 0, 'a2' => 1,
|
||||||
@ -60,7 +56,7 @@ module Asm
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_u8_with_rr(arg)
|
def calculate_u8_with_rr(arg)
|
||||||
parts = arg.value.to_s(2).rjust(32,'0').scan(/^(0*)(.+?)0*$/).flatten
|
parts = arg.value.to_s(2).rjust(32,'0').scan(/^(0*)(.+?)0*$/).flatten
|
||||||
pre_zeros = parts[0].length
|
pre_zeros = parts[0].length
|
||||||
|
@ -7,11 +7,11 @@ module Asm
|
|||||||
|
|
||||||
def initialize( opcode , args)
|
def initialize( opcode , args)
|
||||||
super(opcode , args)
|
super(opcode , args)
|
||||||
@inst_class = OPC_DATA_PROCESSING
|
@rn = nil
|
||||||
@i = 0
|
@i = 0
|
||||||
@rd = args[0]
|
@rd = args[0]
|
||||||
end
|
end
|
||||||
attr_accessor :inst_class, :i, :rn, :rd
|
attr_accessor :i, :rn, :rd
|
||||||
|
|
||||||
# Build representation for source value
|
# Build representation for source value
|
||||||
def build
|
def build
|
||||||
@ -71,13 +71,14 @@ module Asm
|
|||||||
|
|
||||||
def assemble(io)
|
def assemble(io)
|
||||||
build
|
build
|
||||||
|
instuction_class = 0b00 # OPC_DATA_PROCESSING
|
||||||
val = operand.is_a?(Register) ? operand.bits : operand
|
val = operand.is_a?(Register) ? operand.bits : operand
|
||||||
val |= (rd.bits << 12)
|
val |= (rd.bits << 12)
|
||||||
val |= (rn.bits << 12+4)
|
val |= (rn.bits << 12+4)
|
||||||
val |= (update_status_flag << 12+4+4)#20
|
val |= (update_status_flag << 12+4+4)#20
|
||||||
val |= (op_bit_code << 12+4+4 +1)
|
val |= (op_bit_code << 12+4+4 +1)
|
||||||
val |= (i << 12+4+4 +1+4)
|
val |= (i << 12+4+4 +1+4)
|
||||||
val |= (inst_class << 12+4+4 +1+4+1)
|
val |= (instuction_class << 12+4+4 +1+4+1)
|
||||||
val |= (cond_bit_code << 12+4+4 +1+4+1+2)
|
val |= (cond_bit_code << 12+4+4 +1+4+1+2)
|
||||||
io.write_uint32 val
|
io.write_uint32 val
|
||||||
end
|
end
|
||||||
|
@ -8,7 +8,6 @@ module Asm
|
|||||||
|
|
||||||
def initialize(opcode , args)
|
def initialize(opcode , args)
|
||||||
super( opcode , args )
|
super( opcode , args )
|
||||||
@inst_class = OPC_MEMORY_ACCESS
|
|
||||||
@i = 0 #I flag (third bit)
|
@i = 0 #I flag (third bit)
|
||||||
@pre_post_index = 0 #P flag
|
@pre_post_index = 0 #P flag
|
||||||
@add_offset = 0 #U flag
|
@add_offset = 0 #U flag
|
||||||
@ -18,7 +17,7 @@ module Asm
|
|||||||
@rn = reg "r0" # register zero = zero bit pattern
|
@rn = reg "r0" # register zero = zero bit pattern
|
||||||
@rd = reg "r0" # register zero = zero bit pattern
|
@rd = reg "r0" # register zero = zero bit pattern
|
||||||
end
|
end
|
||||||
attr_accessor :inst_class, :i, :pre_post_index, :add_offset,
|
attr_accessor :i, :pre_post_index, :add_offset,
|
||||||
:byte_access, :w, :is_load, :rn, :rd
|
:byte_access, :w, :is_load, :rn, :rd
|
||||||
|
|
||||||
# Build representation for target address
|
# Build representation for target address
|
||||||
@ -63,6 +62,7 @@ module Asm
|
|||||||
# so it doesn't matter. Will see
|
# so it doesn't matter. Will see
|
||||||
@add_offset = 1
|
@add_offset = 1
|
||||||
@pre_post_index = 1
|
@pre_post_index = 1
|
||||||
|
instuction_class = 0b01 # OPC_MEMORY_ACCESS
|
||||||
val = operand
|
val = operand
|
||||||
val |= (rd.bits << 12 )
|
val |= (rd.bits << 12 )
|
||||||
val |= (rn.bits << 12+4) #16
|
val |= (rn.bits << 12+4) #16
|
||||||
@ -72,7 +72,7 @@ module Asm
|
|||||||
val |= (add_offset << 12+4 +4+1+1+1)
|
val |= (add_offset << 12+4 +4+1+1+1)
|
||||||
val |= (pre_post_index << 12+4 +4+1+1+1+1)#24
|
val |= (pre_post_index << 12+4 +4+1+1+1+1)#24
|
||||||
val |= (i << 12+4 +4+1+1+1+1 +1)
|
val |= (i << 12+4 +4+1+1+1+1 +1)
|
||||||
val |= (inst_class << 12+4 +4+1+1+1+1 +1+1)
|
val |= (instuction_class<<12+4 +4+1+1+1+1 +1+1)
|
||||||
val |= (cond_bit_code << 12+4 +4+1+1+1+1 +1+1+2)
|
val |= (cond_bit_code << 12+4 +4+1+1+1+1 +1+1+2)
|
||||||
io.write_uint32 val
|
io.write_uint32 val
|
||||||
end
|
end
|
||||||
|
@ -7,7 +7,6 @@ module Asm
|
|||||||
|
|
||||||
def initialize(opcode , args)
|
def initialize(opcode , args)
|
||||||
super(opcode,args)
|
super(opcode,args)
|
||||||
@inst_class = Asm::Instruction::OPC_STACK
|
|
||||||
@update_status_flag= 0
|
@update_status_flag= 0
|
||||||
@rn = reg "r0" # register zero = zero bit pattern
|
@rn = reg "r0" # register zero = zero bit pattern
|
||||||
# downward growing, decrement before memory access
|
# downward growing, decrement before memory access
|
||||||
@ -23,11 +22,12 @@ module Asm
|
|||||||
@is_pop = 1
|
@is_pop = 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
attr_accessor :cond, :inst_class, :pre_post_index, :up_down,
|
attr_accessor :pre_post_index, :up_down,
|
||||||
:update_status_flag, :write_base, :is_pop, :rn, :operand
|
:update_status_flag, :write_base, :is_pop, :rn
|
||||||
|
|
||||||
def assemble(io)
|
def assemble(io)
|
||||||
build
|
build
|
||||||
|
instuction_class = 0b10 # OPC_STACK
|
||||||
cond = @cond.is_a?(Symbol) ? COND_CODES[@cond] : @cond
|
cond = @cond.is_a?(Symbol) ? COND_CODES[@cond] : @cond
|
||||||
rn = reg "sp" # sp register
|
rn = reg "sp" # sp register
|
||||||
#assemble of old
|
#assemble of old
|
||||||
@ -38,7 +38,7 @@ module Asm
|
|||||||
val |= (update_status_flag << 16+4+ 1+1)
|
val |= (update_status_flag << 16+4+ 1+1)
|
||||||
val |= (up_down << 16+4+ 1+1+1)
|
val |= (up_down << 16+4+ 1+1+1)
|
||||||
val |= (pre_post_index << 16+4+ 1+1+1+1)#24
|
val |= (pre_post_index << 16+4+ 1+1+1+1)#24
|
||||||
val |= (inst_class << 16+4+ 1+1+1+1 +2)
|
val |= (instuction_class << 16+4+ 1+1+1+1 +2)
|
||||||
val |= (cond << 16+4+ 1+1+1+1 +2+2)
|
val |= (cond << 16+4+ 1+1+1+1 +2+2)
|
||||||
io.write_uint32 val
|
io.write_uint32 val
|
||||||
end
|
end
|
||||||
|
18
lib/elf/README.markdown
Normal file
18
lib/elf/README.markdown
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Minimal elf support
|
||||||
|
===================
|
||||||
|
|
||||||
|
This is really minnimal and works only for our current use case
|
||||||
|
- no external functions (all syscalls)
|
||||||
|
- only position independant code (no relocation)
|
||||||
|
- embedded data (into text), no data section
|
||||||
|
|
||||||
|
I was close to going the wilson way, ie assmble, load into memory and jump
|
||||||
|
|
||||||
|
But it is nice to produce executables. Also easier to test, what with segfaults and such.
|
||||||
|
|
||||||
|
Executalbe files are not supported (yet?), but object files work. So the only thing that remains is to
|
||||||
|
call the linker on the produced object file. The resulting file is an executable that actually works!!
|
||||||
|
|
||||||
|
Thanks to Mikko for starting this arm/elf project in the first place: https://github.com/cyndis/as
|
||||||
|
|
||||||
|
This part definately needs tlc, so anyone who is interested, dig in!
|
24
lib/vm/README.markdown
Normal file
24
lib/vm/README.markdown
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
Parser
|
||||||
|
================
|
||||||
|
|
||||||
|
This includes the parser and generated ast.
|
||||||
|
|
||||||
|
Parslet is really great in that it:
|
||||||
|
- does not generate code but instean gives a clean dsl to define a grammar
|
||||||
|
- uses ruby modules so one can split the grammars up
|
||||||
|
- has a seperate tranform stage to generate an ast layer
|
||||||
|
|
||||||
|
Especially the last point is great. Since it is seperate it does not clutter up the actual grammar.
|
||||||
|
And it can generate a layer that has no links to the actual parser anymore, thus saving/automating
|
||||||
|
a complete tranformation process.
|
||||||
|
|
||||||
|
|
||||||
|
Virtual Machine
|
||||||
|
===============
|
||||||
|
|
||||||
|
This is the logic that uses the generated ast to produce code, using the asm layer.
|
||||||
|
|
||||||
|
Apart from shuffeling things around from one layer to the other, it keeps track about registers and
|
||||||
|
provides the stack glue. All the stuff a compiler would usually do.
|
||||||
|
|
||||||
|
Also all syscalls are abstracted as functions.
|
Loading…
x
Reference in New Issue
Block a user