Advertisement
12Me21

Assembler

Jun 30th, 2017
414
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.38 KB | None | 0 0
  1. --#########
  2. --Constants
  3. --#########
  4. --opcodes
  5. local JUMP={value=17,skip=1}
  6. local OPCODES={
  7.     mov={value=0},["and"]={value=1},["or"]={value=2},xor={value=3},
  8.     add={value=4},adc={value=5},sub={value=6},sbb={value=7},
  9.     swm={value=8,skip=1},test={value=9},cmp={value=14},hlt={value=16,skip=3};
  10.     jmp=JUMP,jn=JUMP,jb=JUMP,jnae=JUMP,
  11.     jc=JUMP,jnb=JUMP,jae=JUMP,jnc=JUMP,
  12.     jo=JUMP,jno=JUMP,js=JUMP,jns=JUMP,
  13.     je=JUMP,jz=JUMP,jne=JUMP,jnz=JUMP,
  14.     jle=JUMP,jng=JUMP,jnle=JUMP,jg=JUMP,
  15.     jl=JUMP,jnge=JUMP,jnl=JUMP,jge=JUMP,
  16.     jbe=JUMP,jna=JUMP,jnbe=JUMP,ja=JUMP;
  17.     rol={value=18},ror={value=19},shl={value=20},shr={value=21},shld={value=22},shrd={value=23},
  18.     bump={value=24,skip=2},wait={value=25,skip=2},send={value=26},recv={value=27},
  19.     push={value=28,skip=1},pop={value=28,skip=2},call={value=28,skip=1},ret={value=28,skip=3}
  20. }
  21. --jump modes
  22. local JUMPS={
  23.     jmp=0,jn=1,jb=2,jnae=2,
  24.     jc=2,jnb=3,jae=3,jnc=3,
  25.     jo=4,jno=5,js=6,jns=7,
  26.     je=8,jz=8,jne=9,jnz=9,
  27.     jle=10,jng=10,jnle=11,jg=11,
  28.     jl=12,jnge=12,jnl=13,jge=13,
  29.     jbe=14,jna=14,jnbe=15,ja=15
  30. }
  31. --registers
  32. local REGISTERS={
  33.     r0=0,r1=1,r2=2,r3=3,r4=4,r5=5,r6=6,r7=7,
  34.     r8=8,r9=9,r10=10,r11=11,r12=12,r13=13,r14=14,r15=15,
  35.     sp=14,ip=15
  36. }
  37. --addressing modes
  38. local MODES={
  39.     ["r,r"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0x0},{offset=0,size=4,arg=1},{offset=4,size=4,arg=2}},
  40.     ["r,[r]"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0x1},{offset=0,size=4,arg=1},{offset=4,size=4,arg=2}},
  41.     ["r,i"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0x2},{offset=0,size=4,arg=1},{offset=4,size=16,arg=2}},
  42.     ["r,[i]"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0x3},{offset=0,size=4,arg=1},{offset=4,size=16,arg=2}},
  43.     ["[r],r"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0x4},{offset=0,size=4,arg=1},{offset=4,size=4,arg=2}},
  44.     ["[i],r"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0x5},{offset=4,size=16,arg=1},{offset=0,size=4,arg=2}},
  45.     ["[r],i"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0x6},{offset=0,size=4,arg=1},{offset=4,size=16,arg=2}},
  46.     ["[i],i"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0x7},{offset=4,size=16,arg=1},{offset=0,size=4,arg=2}},
  47.     ["r,[r+r]"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0x9},{offset=0,size=4,arg=1},{offset=16,size=4,arg=2},{offset=15,size=1,value=0},{offset=4,size=4,arg=3}},
  48.     ["r,[r-r]"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0x9},{offset=0,size=4,arg=1},{offset=16,size=4,arg=2},{offset=15,size=1,value=1},{offset=4,size=4,arg=3}},
  49.     ["r,[r+i]"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0xB},{offset=0,size=4,arg=1},{offset=16,size=4,arg=2},{offset=15,size=1,value=0},{offset=4,size=11,arg=3}},
  50.     ["r,[r-i]"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0xB},{offset=0,size=4,arg=1},{offset=16,size=4,arg=2},{offset=15,size=1,value=1},{offset=4,size=11,arg=3}},
  51.     ["[r+r],r"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0xC},{offset=16,size=4,arg=1},{offset=15,size=1,value=0},{offset=0,size=4,arg=2},{offset=4,size=4,arg=3}},
  52.     ["[r-r],r"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0xC},{offset=16,size=4,arg=1},{offset=15,size=1,value=1},{offset=0,size=4,arg=2},{offset=4,size=4,arg=3}},
  53.     ["[r+i],r"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0xD},{offset=16,size=4,arg=1},{offset=15,size=1,value=0},{offset=4,size=11,arg=2},{offset=0,size=4,arg=3}},
  54.     ["[r-i],r"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0xD},{offset=16,size=4,arg=1},{offset=15,size=1,value=1},{offset=4,size=11,arg=2},{offset=0,size=4,arg=3}},
  55.     ["[r+r],i"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0xE},{offset=16,size=4,arg=1},{offset=15,size=1,value=0},{offset=0,size=4,arg=2},{offset=4,size=4,arg=3}},
  56.     ["[r-r],i"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0xE},{offset=16,size=4,arg=1},{offset=15,size=1,value=1},{offset=0,size=4,arg=2},{offset=4,size=4,arg=3}},
  57.     ["[r+i],i"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0xF},{offset=16,size=4,arg=1},{offset=15,size=1,value=0},{offset=4,size=11,arg=2},{offset=0,size=4,arg=3}},
  58.     ["[r-i],i"]={{offset=24,size=5,arg=0},{offset=20,size=4,value=0xF},{offset=16,size=4,arg=1},{offset=15,size=1,value=1},{offset=4,size=11,arg=2},{offset=0,size=4,arg=3}}
  59. }
  60. --information about computer
  61. local COMPUTER={
  62.     ramx=69,
  63.     ramy=-99,
  64.     qrtzctype=409248261,
  65.     ramwidth=128,
  66.     ramheight=16
  67. }
  68. COMPUTER.ramsize=COMPUTER.ramwidth*COMPUTER.ramheight
  69. --display an error message and line number(optional) if condition is falsey
  70. local function check(condition,message,linenumber,argnumber)
  71.     if not(condition) then
  72.         if linenumber then
  73.             if argnumber then
  74.                 error("Error: "..message.." on line "..tostring(linenumber)..":"..tostring(argnumber),0)
  75.             else
  76.                 error("Error: "..message.." on line "..tostring(linenumber),0)
  77.             end
  78.         else
  79.             error("Error: "..message,0)
  80.         end
  81.     end
  82. end
  83. --tonumber with support for binary literals
  84. local function tonumber2(value)
  85.     if type(value)=="string" and value:sub(1,3)=="0b" then
  86.         return tonumber(value:sub(3),2)
  87.     else
  88.         return tonumber(value)
  89.     end
  90. end
  91. --magic label function
  92. local function makelabel(label,globallabel)
  93.     if label:sub(1,1)=="." then
  94.         if not(globallabel) then return nil,globallabel end
  95.         return globallabel..label,globallabel
  96.     else
  97.         return label,label:match("^[^%.]*")
  98.     end
  99. end
  100. --check if valid labelname
  101. local function islabel(label)
  102.     return not(tonumber2(label) or REGISTERS[label] or label:find("[\"+%-%[%],\"]"))
  103. end
  104. --remove comments from a line
  105. local function removecomment(line,commentchar)
  106.     local instring,escape
  107.     for i=1,#line do
  108.         local char=line:sub(i,i)
  109.         if escape then
  110.             escape=false
  111.         elseif char=="\\" then
  112.             escape=true
  113.         elseif not(instring) and (char=="\"" or char=="'") then
  114.             instring=char
  115.         elseif instring==char then
  116.             instring=false
  117.         elseif not(instring) and char==commentchar then
  118.             return line:sub(1,i-1)
  119.         end
  120.     end
  121.     return line
  122. end
  123. --get value of number/label/register
  124. local function decodenumber(value,labels,globallabel)
  125.     if type(value)=="number" then
  126.         return value
  127.     elseif REGISTERS[value] then
  128.         return REGISTERS[value]
  129.     elseif tonumber2(value) then
  130.         return tonumber2(value)
  131.     elseif labels and globallabel then
  132.         value=makelabel(value,globallabel)
  133.         if labels[value] then return labels[value] end
  134.     else
  135.         return value
  136.     end
  137. end
  138. --parse arguments
  139. local function parseargs(args)
  140.     local pattern=""
  141.     local arglist={}
  142.     local instring,escape
  143.     local token=""
  144.     --push token
  145.     local function sub_pushtoken()
  146.         if token~="" then
  147.             arglist[#arglist+1]=decodenumber(token)
  148.             pattern=pattern..({[true]="r",[false]="i"})[REGISTERS[token]~=nil]
  149.             token=""
  150.         end    
  151.     end
  152.    
  153.     for i=1,#args do
  154.         local char=args:sub(i,i)
  155.         --characters in strings
  156.         if instring then
  157.             --escaped char
  158.             if escape then
  159.                 token=token..char
  160.                 escape=false
  161.             --string end
  162.             elseif char==instring then
  163.                 --char
  164.                 if instring=="'" then
  165.                     arglist[#arglist+1]=token:byte()
  166.                     pattern=pattern.."i"
  167.                     token=""
  168.                 --string
  169.                 elseif instring=="\"" then
  170.                     arglist[#arglist+1]="\""..token.."\""
  171.                     pattern=pattern.."s"
  172.                     token=""
  173.                 end
  174.                 instring=false
  175.             --escape sequence
  176.             elseif char=="\\" then
  177.                 escape=i
  178.             --normal character
  179.             else
  180.                 token=token..char
  181.             end
  182.         -- [ ] + - ,
  183.         elseif char:find("^[%[%]+%-,]$") then
  184.             sub_pushtoken""
  185.             pattern=pattern..char
  186.         -- string start
  187.         elseif char=="\"" or char=="'" then
  188.             sub_pushtoken""
  189.             instring=char
  190.             escape=false
  191.         --whitespace
  192.         elseif char==" " or char=="\t" then
  193.             sub_pushtoken""
  194.         --normal characters
  195.         else
  196.             token=token..char
  197.         end
  198.     end
  199.     sub_pushtoken""
  200.     --check for unclosed string
  201.     if instring then return end
  202.  
  203.     return arglist,pattern
  204. end
  205. --do things with comma separated lists
  206. local function datalist(arglist,pattern,callback)
  207.     --check for commas
  208.     if not(pattern:find(string.rep("^".."[is],",#arglist-1).."[is]$")) then return end
  209.     for _,arg in ipairs(arglist) do
  210.         --string
  211.         if type(arg)=="string" and arg:find("^\".*\"$") then
  212.             for i=2,#arg-1 do
  213.                 callback(arg:byte(i))
  214.             end
  215.         --number
  216.         else
  217.             callback(arg)
  218.         end
  219.     end
  220.     return true
  221. end
  222. --convert code into instructions
  223. local function Parsecode(code)
  224.     local linenumber=1
  225.     local index=0
  226.     local instructions={}
  227.     local labels={}
  228.     local globallabel
  229.     --functions for writing stuff
  230.     local function addinstruction(instruction)
  231.         index=index+1
  232.         check(index<=COMPUTER.ramsize,"out of memory",linenumber)
  233.         instruction.linenumber=linenumber
  234.         instruction.globallabel=globallabel
  235.         instructions[index]=instruction
  236.     end
  237.     local function addword(value)
  238.         addinstruction{
  239.             arglist={value},
  240.             mode={{offset=0,size=29,arg=1}}
  241.         }
  242.     end
  243.     local lowbyte
  244.     local function addbyte(byte)
  245.         if lowbyte then
  246.             addword(byte*256+lowbyte)
  247.             lowbyte=false
  248.         else
  249.             lowbyte=byte
  250.         end
  251.     end
  252.    
  253.     for line in string.gmatch(code.."\n","([^\r\n]*)\r?\n") do
  254.        
  255.         line=removecomment(line,";")
  256.         --skip empty lines
  257.         if not(line:match("^%s*$")) then
  258.             --get opcode and arguments
  259.             opname,args=line:match("^%s*([^%s]*)%s*(.-)%s*$")
  260.             opname=opname:lower()
  261.             local opcode=OPCODES[opname]
  262.             --parse arguments
  263.             local arglist,pattern=parseargs(args)
  264.             check(pattern,"Error parsing arguments",linenumber)
  265.             --instruction
  266.             if opcode then
  267.                 if opcode.skip==1 then
  268.                     table.insert(arglist,1,0)
  269.                     pattern="r,"..pattern
  270.                 elseif opcode.skip==2 then
  271.                     arglist[#arglist]=0
  272.                     pattern=pattern..",r"
  273.                 elseif opcode.skip==3 then
  274.                     arglist[#arglist]=0
  275.                     arglist[#arglist]=0
  276.                     pattern=pattern.."r,r"
  277.                 end
  278.                
  279.                 local mode=MODES[pattern]
  280.                 check(mode,"Invalid arguments",linenumber)
  281.                
  282.                 if opcode.value==OPCODES["jmp"].value then
  283.                     arglist[1]=JUMPS[opname]
  284.                 end
  285.                 arglist[0]=opcode.value
  286.                 addinstruction{
  287.                     mode=mode,
  288.                     arglist=arglist
  289.                 }
  290.             elseif opname=="nop" then
  291.                 check(pattern=="","Invalid arguments",linenumber)
  292.                 addword(0)
  293.             --data (words)
  294.             elseif opname=="dw" then
  295.                 check(datalist(arglist,pattern,addword),"Invalid arguments",linenumber)
  296.             --data (bytes)
  297.             elseif opname=="db" then
  298.                 check(datalist(arglist,pattern,addbyte),"Invalid arguments",linenumber)
  299.                 if lowbyte then addbyte(0) end
  300.             --this is a special thing for my text game engine
  301.             elseif opname=="dbs" then
  302.                 check(datalist(arglist,pattern,addbyte),"Invalid arguments",linenumber)
  303.                 if lowbyte then addbyte(32) end
  304.                 addword(0)
  305.             --change address
  306.             elseif opname=="@org" then
  307.                 check(pattern=="i","Invalid arguments",linenumber)
  308.                 local newaddress=tonumber2(arglist[1])
  309.                 check(newaddress,"Invalid address",linenumber)
  310.                 index=newaddress-1
  311.             --label
  312.             elseif opname:sub(-1)==":" then
  313.                 check(pattern=="","Invalid arguments",linenumber)
  314.                 opname=opname:sub(1,-2)
  315.                 check(islabel(opname),"Illegal label name '"..opname.."'",linenumber)
  316.                 opname,globallabel=makelabel(opname,globallabel)
  317.                 check(opname,"Need global label",linenumber)
  318.                 check(not(labels[opname]),"Duplicate label '"..opname.."'",linenumber)
  319.                 labels[opname]=index
  320.             --error
  321.             else
  322.                 check(false,"Unknown instruction '"..opname.."'",linenumber)
  323.             end
  324.         end
  325.         linenumber=linenumber+1
  326.     end
  327.    
  328.     return instructions,labels
  329. end
  330. --convert instructions to binary
  331. local function Makeops(instructions,labels)
  332.     for i,instruction in pairs(instructions) do
  333.         local linenumber=instruction.linenumber
  334.         local mem=0
  335.         for j,field in ipairs(instruction.mode) do
  336.             local part
  337.             --constant
  338.             if field.value then
  339.                 part=decodenumber(field.value)
  340.             --argument reference
  341.             else
  342.                 part=decodenumber(instruction.arglist[field.arg],labels,instruction.globallabel)
  343.             end
  344.             check(part,"Undefined label",linenumber)
  345.             --convert to signed/unsigned
  346.             check(part<bit.lshift(1,field.size) and part>=-bit.lshift(1,field.size),"Overflow",linenumber,field.arg)
  347.             if part<0 then part=bit.bnot(-part-1) end
  348.             --insert part
  349.             mem=mem+bit.lshift(bit.band(part,bit.lshift(1,field.size)-1),field.offset)--mem=mem|(part&(1<<field.size)-1)<<field.offset
  350.         end
  351.         instructions[i].binary=mem
  352.     end
  353.     return instructions
  354. end
  355. --write instructions into RAM
  356. local function Writemem(instructions)
  357.     --find ram location
  358.     local sx,sy
  359.     for part=0,sim.NUM_PARTS do
  360.         if sim.partProperty(part,"type")==elem.DEFAULT_PT_QRTZ and sim.partProperty(part,"ctype")==COMPUTER.qrtzctype then
  361.             sx,sy=sim.partProperty(part,"x"),sim.partProperty(part,"y")
  362.         end
  363.     end
  364.     check(sx and sy,"Couldn't find computer")
  365.     sx=sx+COMPUTER.ramx
  366.     sy=sy+COMPUTER.ramy
  367.     --write code into RAM
  368.     local i=1
  369.     local wrotecells=0
  370.     for y=0,COMPUTER.ramheight-1 do
  371.         local beforegap=1 --"before" = lower address, not lower X coordinate
  372.         for x=0,COMPUTER.ramwidth-1 do
  373.             --try to find filt
  374.             local partID = sim.partID(sx-x+beforegap,sy+y)
  375.             --check past the gap
  376.             if beforegap==1 and not(partID) then
  377.                 beforegap=0
  378.                 partID = sim.partID(sx-x+beforegap,sy+y)
  379.             end
  380.             check(partID and sim.partProperty(partID,"type")==elem.DEFAULT_PT_FILT,"Write failed (missing filt)")
  381.             --write instructions
  382.             if instructions[i] then
  383.                 sim.partProperty(partID,"ctype",0x20000000+instructions[i].binary)
  384.                 wrotecells=wrotecells+1
  385.             --fill empty space
  386.             else
  387.                 sim.partProperty(partID,"ctype",0x20000000)
  388.             end
  389.             i=i+1
  390.         end
  391.     end
  392.     print("Success: Wrote "..tostring(wrotecells).." cells.")
  393. end
  394. --load file
  395. local file = io.open(...)
  396. local code = file:read("*a")
  397. file:close()
  398. check(code,"Load failed")
  399. --do the thing
  400. Writemem(Makeops(Parsecode(code)))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement