bit of renaming , cleaning and documentation

This commit is contained in:
Torsten Ruger 2014-04-25 13:29:12 +03:00
parent 6261451c4b
commit f1a7993b47
10 changed files with 89 additions and 44 deletions

View File

@ -1,12 +1,7 @@
Assembler in Ruby
=================
Supporting arm, but aimed quite specifically at raspberry pi, arm v7, floating point included
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.
Supporting arm, but aimed quite specifically at raspberry pi, arm v7, floating point included (later)
Supported (pseudo)instructions:
@ -14,5 +9,5 @@ Supported (pseudo)instructions:
mov, mvn, strb, str, ldrb, ldr, push, pop, b, bl, bx, swi
- 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

View File

@ -16,12 +16,12 @@ module Asm
end
def initialize
@values = []
@codes = []
@position = 0 # marks not set
@labels = []
@string_table = {}
end
attr_reader :values , :position
attr_reader :codes , :position
def instruction(clazz,name, *args)
opcode = name.to_s
@ -39,7 +39,7 @@ module Asm
raise "Invalid argument #{arg.inspect} for instruction"
end
end
add_value clazz.new(opcode , arg_nodes)
add_code clazz.new(opcode , arg_nodes)
end
@ -85,7 +85,7 @@ module Asm
#put the strings at the end of the assembled code.
# adding them will fix their position and make them assemble after
@string_table.values.each do |data|
add_value data
add_code data
end
io = StringIO.new
assemble(io)
@ -93,8 +93,8 @@ module Asm
end
def add_string str
value = @string_table[str]
return value if value
code = @string_table[str]
return code if code
data = Asm::StringLiteral.new(str)
@string_table[str] = data
end
@ -103,11 +103,11 @@ module Asm
@string_table.values
end
def add_value(val)
val.at(@position)
length = val.length
def add_code(kode)
kode.at(@position)
length = kode.length
@position += length
@values << val
@codes << kode
end
def label name
@ -116,12 +116,8 @@ module Asm
label
end
def label! name
label(name).set!
end
def assemble(io)
@values.each do |obj|
@codes.each do |obj|
obj.assemble io
end
end

View File

@ -1,15 +1,23 @@
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
include Asm::InstructionTools
def initialize(opcode , args)
super(opcode,args)
end
def assemble(io)
s = @update_status_flag? 1 : 0
case opcode
when :b, :bl
arg = args[0]

View File

@ -34,11 +34,18 @@ module Asm
@args = args
@operand = 0
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
@s
end
# Logic instructions may be executed with or without affecting the status register
# Only when an instruction affects the status is a subsequent compare instruction effective
# 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)
def length

View File

@ -44,10 +44,6 @@ module Asm
COND_CODES[@cond] or throw "no code found for #{@cond}"
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,
'r6' => 6, 'r7' => 7, 'r8' => 8, 'r9' => 9, 'r10' => 10, 'r11' => 11,
'r12' => 12, 'r13' => 13, 'r14' => 14, 'r15' => 15, 'a1' => 0, 'a2' => 1,
@ -60,7 +56,7 @@ module Asm
end
def calculate_u8_with_rr(arg)
parts = arg.value.to_s(2).rjust(32,'0').scan(/^(0*)(.+?)0*$/).flatten
pre_zeros = parts[0].length

View File

@ -7,11 +7,11 @@ module Asm
def initialize( opcode , args)
super(opcode , args)
@inst_class = OPC_DATA_PROCESSING
@rn = nil
@i = 0
@rd = args[0]
end
attr_accessor :inst_class, :i, :rn, :rd
attr_accessor :i, :rn, :rd
# Build representation for source value
def build
@ -71,13 +71,14 @@ module Asm
def assemble(io)
build
instuction_class = 0b00 # OPC_DATA_PROCESSING
val = operand.is_a?(Register) ? operand.bits : operand
val |= (rd.bits << 12)
val |= (rn.bits << 12+4)
val |= (update_status_flag << 12+4+4)#20
val |= (op_bit_code << 12+4+4 +1)
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)
io.write_uint32 val
end

View File

@ -8,7 +8,6 @@ module Asm
def initialize(opcode , args)
super( opcode , args )
@inst_class = OPC_MEMORY_ACCESS
@i = 0 #I flag (third bit)
@pre_post_index = 0 #P flag
@add_offset = 0 #U flag
@ -18,7 +17,7 @@ module Asm
@rn = reg "r0" # register zero = zero bit pattern
@rd = reg "r0" # register zero = zero bit pattern
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
# Build representation for target address
@ -63,6 +62,7 @@ module Asm
# so it doesn't matter. Will see
@add_offset = 1
@pre_post_index = 1
instuction_class = 0b01 # OPC_MEMORY_ACCESS
val = operand
val |= (rd.bits << 12 )
val |= (rn.bits << 12+4) #16
@ -72,7 +72,7 @@ module Asm
val |= (add_offset << 12+4 +4+1+1+1)
val |= (pre_post_index << 12+4 +4+1+1+1+1)#24
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)
io.write_uint32 val
end

View File

@ -7,7 +7,6 @@ module Asm
def initialize(opcode , args)
super(opcode,args)
@inst_class = Asm::Instruction::OPC_STACK
@update_status_flag= 0
@rn = reg "r0" # register zero = zero bit pattern
# downward growing, decrement before memory access
@ -23,11 +22,12 @@ module Asm
@is_pop = 1
end
end
attr_accessor :cond, :inst_class, :pre_post_index, :up_down,
:update_status_flag, :write_base, :is_pop, :rn, :operand
attr_accessor :pre_post_index, :up_down,
:update_status_flag, :write_base, :is_pop, :rn
def assemble(io)
build
instuction_class = 0b10 # OPC_STACK
cond = @cond.is_a?(Symbol) ? COND_CODES[@cond] : @cond
rn = reg "sp" # sp register
#assemble of old
@ -38,7 +38,7 @@ module Asm
val |= (update_status_flag << 16+4+ 1+1)
val |= (up_down << 16+4+ 1+1+1)
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)
io.write_uint32 val
end

18
lib/elf/README.markdown Normal file
View 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
View 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.