$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 while (($bytes.length % 4) != 0) do $bytes.push 0 end end # # # REGISTER NAMES # # # def r0() 0 end # zero def at() 1 end # assembler temporary # subroutine return values, may be changed by subroutines def v0() 2 end ; def v0() 3 end # subroutine arguments, may be changed by subroutines def a0() 4 end ; def a1() 5 end def a2() 6 end ; def a3() 7 end # temporaries, may be changed by subroutines def t0() 8 end ; def t1() 9 end def t2() 10 end ; def t3() 11 end def t4() 12 end ; def t5() 13 end def t6() 14 end ; def t7() 15 end # static variables, must be saved by subs def s0() 16 end ; def s1() 17 end def s2() 18 end ; def s3() 19 end def s4() 20 end ; def s5() 21 end def s6() 22 end ; def s7() 23 end # temporaries, may be changed by subroutines def t8() 24 end ; def t9() 25 end # reserved for kernel, detroyed by some irq handlers def k0() 26 end ; def k1() 27 end def gp() 28 end # global pointer, rarely used def sp() 29 end # stack pointer def fp() 30 end # frame pointer def ra() 31 end # return address # # # INSTRUCTIONS # # # # 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 # # # 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 # # # 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 = [] # # # PROGRAM CODE # # # nop # ops should always be in format of # src [args] -> dest # 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 addiu r0, -10, t3 #addiu t3, 6, t3 addiu t3, -10, t3 nop addi r0, -10, t4 #addi t4, 6, t4 addi t4, -10, t4 nop nop jmp l_end_loop 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 "LOADTHIS.EXE", "wb" f.write $exe_header.pack("C*") f.write $bytes.pack("C*") f.close