From 2810109764958026c5790684fbb01ba874e8f4c6 Mon Sep 17 00:00:00 2001 From: bx Date: Wed, 1 Sep 2021 22:05:19 +0100 Subject: [PATCH] re-worked assemlber to load from file and have real labels --- assembler.rb | 422 +++++++++++++++++++--------------------------- exe-header.asm.rb | 18 ++ main.asm.rb | 78 +++++++++ 3 files changed, 272 insertions(+), 246 deletions(-) create mode 100644 exe-header.asm.rb create mode 100644 main.asm.rb diff --git a/assembler.rb b/assembler.rb index 4173c14..bbd331e 100644 --- a/assembler.rb +++ b/assembler.rb @@ -1,27 +1,5 @@ -$bytes = [] - -def zero_fill(pos) - while ($bytes.length <= pos) do - $bytes.push(0) - end -end - -def word(i) - $bytes.push((i & 0x000000ff), - (i & 0x0000ff00) >> 8, - (i & 0x00ff0000) >> 16, - (i & 0xff000000) >> 24) -end - -def string(s) - $bytes.push *s.bytes - $bytes.push 0 # null terminate - while (($bytes.length % 4) != 0) do - $bytes.push 0 - end -end - +# assembler.rb # # # REGISTER NAMES # # # def r0() 0 end # zero @@ -57,244 +35,196 @@ def sp() 29 end # stack pointer def fp() 30 end # frame pointer def ra() 31 end # return address +class Assembler + def initialize(base, size) + @base_addr = base + @file_size = size + end + + def inspect + "(Assembler base_addr:0x#{@base_addr.to_s 16} file_size:0x#{@file_size.to_s 16})" + end + + # TODO: Sub-labels + + def label(l) + return if @first_pass == false + raise "error redefinition of label #{l.to_s}" if @labels.member?(l.to_s) + @labels[l.to_s] = @bytes.length + @base_addr + end + + def method_missing(m, *args, &block) + return 0 if @first_pass + if m.to_s[0, 2] == "l_" then + return @labels[m.to_s[2..]] if @labels.member?(m.to_s[2..]) + raise "label doesn't exist #{m.to_s}" + else + super + end + end + + def assemble(source) + @first_pass = true + @labels = {} + @bytes = [] + binding.eval source + + @first_pass = false + @bytes = [] + binding.eval source + + puts "assembled 0x#{@bytes.length.to_s 16} (#{@bytes.length}) bytes" + raise "CODE IS GREATER THAN FILESIZE" if @bytes.length > @file_size + zero_fill @file_size - 1 + + return @bytes + end + + def zero_fill(pos) + while (@bytes.length <= pos) do + @bytes.push(0) + end + end + + def word(i) + @bytes.push((i & 0x000000ff), + (i & 0x0000ff00) >> 8, + (i & 0x00ff0000) >> 16, + (i & 0xff000000) >> 24) + end + + def string(s) + @bytes.push *s.bytes + @bytes.push 0 # null terminate + while ((@bytes.length % 4) != 0) do + @bytes.push 0 + end + end -# # # INSTRUCTIONS # # # + # # # INSTRUCTIONS # # # -# ops should always be in format of -# [src] [args] -> [dest] + # ops should always be in format of + # [src] [args] -> [dest] + + def jmp(addr) + # TODO: this probs wont show up since we have < 3mb of ram but + # we can't jump to an address with a different 4 most significant bits + raise "jmp addr must be 4 byte aligned" if addr % 4 != 0 + op = 0b0000_1000_0000_0000_0000_0000_0000_0000 + op = op | ((addr >> 2) & 0b11_11111111_11111111_11111111) + word op + end + + def jal(addr) + # TODO: same problem as jmp + raise "jmp addr must be 4 byte aligned" if addr % 4 != 0 + op = 0b0000_1100_0000_0000_0000_0000_0000_0000 + op = op | ((addr >> 2) & 0b11_11111111_11111111_11111111) + word op + end + + # load upper immediate + # NOTE: val will be shifted left by 16 + def lui(val, dest) + # oooooo ----- ddddd iiiiiiiiiiiiiiii + op = 0b001111_00000_00000_0000000000000000 + op |= 0xffff & val + op |= (0b11111 & dest) << 16 + word op + end + + # shift right logical + def srl(src, amt, dest) + # oooooo ----- sssss ddddd hhhhh oooooo + op = 0b000000_00000_00000_00000_00000_000010 + op |= (src & 0b11111) << 16 + op |= (amt & 0b11111) << 6 + op |= (dest & 0b11111) << 11 + word op + end + + # or immediate + def ori(src, bits, dest) + # oooooo sssss ddddd iiiiiiii iiiiiiii + op = 0b001101_00000_00000_00000000_00000000 + op |= (src & 0b11111) << 21 + op |= (bits & 0xffff) + op |= (dest & 0b11111) << 16 + word op + end + + # store word + # NOTE: this doesn't work unaligned + def sw(src, offset, dest) + # oooooo ddddd sssss iiiiiiii iiiiiiii + op = 0b101011_00000_00000_00000000_00000000 + op |= (src & 0b11111) << 16 + op |= (offset & 0xffff) + op |= (dest & 0b11111) << 21 + word op + end + + # add immediate unsigned + def addiu(src, val, dest) + # oooooo sssss ddddd iiiiiiii iiiiiiii + op = 0b001001_00000_00000_00000000_00000000 + op |= (src & 0b11111) << 21 + op |= (val & 0xffff) + op |= (dest & 0b11111) << 16 + word op + end + + # add immediate + def addi(src, val, dest) + # oooooo sssss ddddd iiiiiiii iiiiiiii + op = 0b001000_00000_00000_00000000_00000000 + op |= (src & 0b11111) << 21 + op |= (val & 0xffff) + op |= (dest & 0b11111) << 16 + word op + end + + # jump register + def jr(reg) + # oooooo rrrrr ----- ----- ----- oooooo + op = 0b000000_00000_00000_00000_00000_001000 + op |= (reg & 0b11111) << 21 + word op + end + + # # # PSEUDO-INSTRUCTIONS # # # + def nop + word 0x00000000 + end + + # load word immediate + def lwi(val, dest) + lui val >> 16, dest + ori dest, val, dest + end -def jmp(addr) - # TODO: this probs wont show up since we have < 3mb of ram but - # we can't jump to an address with a different 4 most significant bits - raise "jmp addr must be 4 byte aligned" if addr % 4 != 0 - op = 0b0000_1000_0000_0000_0000_0000_0000_0000 - op = op | ((addr >> 2) & 0b11_11111111_11111111_11111111) - word op end -def jal(addr) - # TODO: same problem as jmp - raise "jmp addr must be 4 byte aligned" if addr % 4 != 0 - op = 0b0000_1100_0000_0000_0000_0000_0000_0000 - op = op | ((addr >> 2) & 0b11_11111111_11111111_11111111) - word op -end - -# load upper immediate -# NOTE: val will be shifted left by 16 -def lui(val, dest) - # oooooo ----- ddddd iiiiiiiiiiiiiiii - op = 0b001111_00000_00000_0000000000000000 - op |= 0xffff & val - op |= (0b11111 & dest) << 16 - word op -end - -# shift right logical -def srl(src, amt, dest) - # oooooo ----- sssss ddddd hhhhh oooooo - op = 0b000000_00000_00000_00000_00000_000010 - op |= (src & 0b11111) << 16 - op |= (amt & 0b11111) << 6 - op |= (dest & 0b11111) << 11 - word op -end - -# or immediate -def ori(src, bits, dest) - # oooooo sssss ddddd iiiiiiii iiiiiiii - op = 0b001101_00000_00000_00000000_00000000 - op |= (src & 0b11111) << 21 - op |= (bits & 0xffff) - op |= (dest & 0b11111) << 16 - word op -end - -# store word -# NOTE: this doesn't work unaligned -def sw(src, offset, dest) - # oooooo ddddd sssss iiiiiiii iiiiiiii - op = 0b101011_00000_00000_00000000_00000000 - op |= (src & 0b11111) << 16 - op |= (offset & 0xffff) - op |= (dest & 0b11111) << 21 - word op -end - -# add immediate unsigned -def addiu(src, val, dest) - # oooooo sssss ddddd iiiiiiii iiiiiiii - op = 0b001001_00000_00000_00000000_00000000 - op |= (src & 0b11111) << 21 - op |= (val & 0xffff) - op |= (dest & 0b11111) << 16 - word op -end - -# add immediate -def addi(src, val, dest) - # oooooo sssss ddddd iiiiiiii iiiiiiii - op = 0b001000_00000_00000_00000000_00000000 - op |= (src & 0b11111) << 21 - op |= (val & 0xffff) - op |= (dest & 0b11111) << 16 - word op -end - -# jump register -def jr(reg) - # oooooo rrrrr ----- ----- ----- oooooo - op = 0b000000_00000_00000_00000_00000_001000 - op |= (reg & 0b11111) << 21 - word op -end - - - -# # # PSEUDO-INSTRUCTIONS # # # -def nop - word 0x00000000 -end - -# load word immediate -def lwi(val, dest) - lui val >> 16, dest - ori dest, val, dest -end - - - - - -def label - return $bytes.length + $base_addr -end - -# this is a terrible idea and im doing it -#$labels = {} -#def method_missing(m, *args) -# puts m.to_s -# super -#end - # # # CONSTANTS # # # -$base_addr = 0x80010000 # 0x10000 # 0x80010000 -$file_size = 0x800 * 3 -$gp0 = 0x1F801810 -$gp1 = 0x1F801814 +base_addr = 0x80010000 # 0x10000 # 0x80010000 +file_size = 0x800 * 3 -# # # EXE HEADER # # # -string "PS-X EXE" -zero_fill 0x00f -word $base_addr # initial pc -word 0x00000000 # initial GP/R28 -word $base_addr # destintation address in RAM -word $file_size # file size excluding header (must be N * 0x800) -word 0x00000000 # Unknown/Unused -word 0x00000000 # Unknown/Unused -word 0x00000000 # Memfill start address -word 0x00000000 # Memfill size in bytes -word 0x801ffff0 # Initial SP/R29 & FP/R30 Base -word 0x00000000 # Initial SP/R29 & FP/R30 Offs -zero_fill 0x4b # Reserved for A(43h) Function -# Ascii marker would go here -zero_fill 0x7ff -$exe_header = $bytes[0..] -$bytes = [] +f = File.new "exe-header.asm.rb", "r" +exe_header = Assembler.new(base_addr, 0x800).assemble f.read +f.close - - - -# # # PROGRAM CODE # # # -nop -jmp $base_addr + 0x400 -nop - -l_hello_world = label -string "ello wrld!\n" - - - -l_printf = label -addiu r0, 0xa0, at # address of bios func -jr at -addiu r0, 0x3f, t1 # printf number - - - -zero_fill 0x400 - 1 -nop - - - -lwi l_hello_world, a0 -jal l_printf -addiu sp, -16, sp # delay slot -addiu sp, 16, sp - -nop ; nop ; nop ; nop - - -# THIS IS NEEDED -lwi $gp1, t0 -lwi 0x03_00_00_00, t1 # display enable -sw t1, 0, t0 - -lwi $gp0, t0 - -lwi 0xe1_000000 + 0b0_0_0_1_0_01_00_0_0000, t1 -sw t1, 0, t0 - -# THIS IS NEEDED -lwi 0xe4_000000 + (640) + (480 << 10), t1 # Drawing Area bottom right -sw t1, 0, t0 - -def line(st, en) - lwi 0x40_ffffff, t1 # monochrome line, color ffffff - sw t1, 0, t0 - - lwi st, t1 # line vert 1 - sw t1, 0, t0 - - lwi en, t1 # line vert 2 - sw t1, 0, t0 -end - -# l_end_loop = label -line 0x0010_0010, 0x0030_0030 -line 0x0030_0030, 0x0060_0000 -line 0x0060_000a, 0x00f0_0060 - -nop ; nop ; nop ; nop -nop ; nop ; nop ; nop -nop ; nop ; nop ; nop -nop ; nop ; nop ; nop - -l_end_loop = label -nop -lwi l_end_loop, t9 -jr t9 -nop - - - - - - -puts "assembled 0x#{$bytes.length.to_s 16} (#{$bytes.length}) bytes" -raise "CODE IS GREATER THAN FILESIZE" if $bytes.length > $file_size -zero_fill $file_size - 1 +f = File.new "main.asm.rb", "r" +binary = Assembler.new(base_addr, file_size).assemble f.read +f.close f = File.new "LOADTHIS.EXE", "wb" -f.write $exe_header.pack("C*") -f.write $bytes.pack("C*") +f.write exe_header.pack("C*") +f.write binary.pack("C*") f.close - - diff --git a/exe-header.asm.rb b/exe-header.asm.rb new file mode 100644 index 0000000..3892ff4 --- /dev/null +++ b/exe-header.asm.rb @@ -0,0 +1,18 @@ + +# exe-header.asm.rb + +string "PS-X EXE" +zero_fill 0x00f +word @base_addr # initial pc +word 0x00000000 # initial GP/R28 +word @base_addr # destintation address in RAM +word @file_size # file size excluding header (must be N * 0x800) +word 0x00000000 # Unknown/Unused +word 0x00000000 # Unknown/Unused +word 0x00000000 # Memfill start address +word 0x00000000 # Memfill size in bytes +word 0x801ffff0 # Initial SP/R29 & FP/R30 Base +word 0x00000000 # Initial SP/R29 & FP/R30 Offs +zero_fill 0x4b # Reserved for A(43h) Function +# Ascii marker would go here +zero_fill 0x7ff diff --git a/main.asm.rb b/main.asm.rb new file mode 100644 index 0000000..187014c --- /dev/null +++ b/main.asm.rb @@ -0,0 +1,78 @@ + +# main.asm.rb + +$gp0 = 0x1F801810 +$gp1 = 0x1F801814 + +jmp l_start +nop + +label :hello_world + string "ello wrld!\n" + +label :printf + addiu r0, 0xa0, at # address of bios func + jr at + addiu r0, 0x3f, t1 # printf number + +label :draw_line + # todo: draw line function, take args in a0 - a3 + # tood: implement shift left so we can use that + lwi $gp0, t0 + + lwi 0x40_ffffff, t1 # monochrome line, color ffffff + sw t1, 0, t0 + + # lwi st, t1 # line vert 1 + sw t1, 0, t0 + + # lwi en, t1 # line vert 2 + sw t1, 0, t0 + +label :start + nop ; nop ; nop ; nop + + # THIS IS NEEDED + lwi $gp1, t0 + lwi 0x03_00_00_00, t1 # display enable + sw t1, 0, t0 + + lwi $gp0, t0 + + lwi 0xe1_000000 + 0b0_0_0_1_0_01_00_0_0000, t1 + sw t1, 0, t0 + + # THIS IS NEEDED + lwi 0xe4_000000 + (640) + (480 << 10), t1 # Drawing Area bottom right + sw t1, 0, t0 + + def line(st, en) + lwi 0x40_ffffff, t1 # monochrome line, color ffffff + sw t1, 0, t0 + + lwi st, t1 # line vert 1 + sw t1, 0, t0 + + lwi en, t1 # line vert 2 + sw t1, 0, t0 + end + + line 0x0010_0010, 0x0030_0030 + line 0x0030_0030, 0x0060_0000 + line 0x0060_000a, 0x00f0_0060 + + lwi l_hello_world, a0 + jal l_printf + addiu sp, -16, sp # delay slot + addiu sp, 16, sp + +label :end_loop + lwi l_end_loop, t9 + jr t9 + nop + + + + + +