From f1a7993b4771c0250e9e6c539a37e65df59b25d5 Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Fri, 25 Apr 2014 13:29:12 +0300 Subject: [PATCH] bit of renaming , cleaning and documentation --- lib/asm/README.markdown | 9 ++------- lib/asm/arm_assembler.rb | 26 +++++++++++--------------- lib/asm/call_instruction.rb | 14 +++++++++++--- lib/asm/instruction.rb | 15 +++++++++++---- lib/asm/instruction_tools.rb | 6 +----- lib/asm/logic_instruction.rb | 7 ++++--- lib/asm/memory_instruction.rb | 6 +++--- lib/asm/stack_instruction.rb | 8 ++++---- lib/elf/README.markdown | 18 ++++++++++++++++++ lib/vm/README.markdown | 24 ++++++++++++++++++++++++ 10 files changed, 89 insertions(+), 44 deletions(-) create mode 100644 lib/elf/README.markdown create mode 100644 lib/vm/README.markdown diff --git a/lib/asm/README.markdown b/lib/asm/README.markdown index a42b0252..7559ae96 100644 --- a/lib/asm/README.markdown +++ b/lib/asm/README.markdown @@ -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 diff --git a/lib/asm/arm_assembler.rb b/lib/asm/arm_assembler.rb index 69b4ca3d..3ae83479 100644 --- a/lib/asm/arm_assembler.rb +++ b/lib/asm/arm_assembler.rb @@ -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 diff --git a/lib/asm/call_instruction.rb b/lib/asm/call_instruction.rb index 40dac13d..5c2e1de8 100644 --- a/lib/asm/call_instruction.rb +++ b/lib/asm/call_instruction.rb @@ -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] diff --git a/lib/asm/instruction.rb b/lib/asm/instruction.rb index 4dfaa844..6f7484a7 100644 --- a/lib/asm/instruction.rb +++ b/lib/asm/instruction.rb @@ -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 diff --git a/lib/asm/instruction_tools.rb b/lib/asm/instruction_tools.rb index 05c3bda4..a6368110 100644 --- a/lib/asm/instruction_tools.rb +++ b/lib/asm/instruction_tools.rb @@ -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 diff --git a/lib/asm/logic_instruction.rb b/lib/asm/logic_instruction.rb index 411cac11..0432d6eb 100644 --- a/lib/asm/logic_instruction.rb +++ b/lib/asm/logic_instruction.rb @@ -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 diff --git a/lib/asm/memory_instruction.rb b/lib/asm/memory_instruction.rb index 93544e56..cda69313 100644 --- a/lib/asm/memory_instruction.rb +++ b/lib/asm/memory_instruction.rb @@ -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 diff --git a/lib/asm/stack_instruction.rb b/lib/asm/stack_instruction.rb index 67d97b9b..ce31130f 100644 --- a/lib/asm/stack_instruction.rb +++ b/lib/asm/stack_instruction.rb @@ -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 diff --git a/lib/elf/README.markdown b/lib/elf/README.markdown new file mode 100644 index 00000000..06f60525 --- /dev/null +++ b/lib/elf/README.markdown @@ -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! \ No newline at end of file diff --git a/lib/vm/README.markdown b/lib/vm/README.markdown new file mode 100644 index 00000000..73c7965a --- /dev/null +++ b/lib/vm/README.markdown @@ -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.