Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --#########
- --Constants
- --#########
- --opcodes
- local JUMP={value=17,skip=1}
- local OPCODES={
- mov={value=0},["and"]={value=1},["or"]={value=2},xor={value=3},
- add={value=4},adc={value=5},sub={value=6},sbb={value=7},
- swm={value=8,skip=1},test={value=9},cmp={value=14},hlt={value=16,skip=3};
- jmp=JUMP,jn=JUMP,jb=JUMP,jnae=JUMP,
- jc=JUMP,jnb=JUMP,jae=JUMP,jnc=JUMP,
- jo=JUMP,jno=JUMP,js=JUMP,jns=JUMP,
- je=JUMP,jz=JUMP,jne=JUMP,jnz=JUMP,
- jle=JUMP,jng=JUMP,jnle=JUMP,jg=JUMP,
- jl=JUMP,jnge=JUMP,jnl=JUMP,jge=JUMP,
- jbe=JUMP,jna=JUMP,jnbe=JUMP,ja=JUMP;
- rol={value=18},ror={value=19},shl={value=20},shr={value=21},shld={value=22},shrd={value=23},
- bump={value=24,skip=2},wait={value=25,skip=2},send={value=26},recv={value=27},
- push={value=28,skip=1},pop={value=28,skip=2},call={value=28,skip=1},ret={value=28,skip=3}
- }
- --jump modes
- local JUMPS={
- jmp=0,jn=1,jb=2,jnae=2,
- jc=2,jnb=3,jae=3,jnc=3,
- jo=4,jno=5,js=6,jns=7,
- je=8,jz=8,jne=9,jnz=9,
- jle=10,jng=10,jnle=11,jg=11,
- jl=12,jnge=12,jnl=13,jge=13,
- jbe=14,jna=14,jnbe=15,ja=15
- }
- --registers
- local 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,
- sp=14,ip=15
- }
- --addressing modes
- local MODES={
- ["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}},
- ["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}},
- ["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}},
- ["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}},
- ["[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}},
- ["[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}},
- ["[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}},
- ["[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}},
- ["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}},
- ["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}},
- ["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}},
- ["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}},
- ["[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}},
- ["[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}},
- ["[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}},
- ["[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}},
- ["[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}},
- ["[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}},
- ["[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}},
- ["[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}}
- }
- --information about computer
- local COMPUTER={
- ramx=69,
- ramy=-99,
- qrtzctype=409248261,
- ramwidth=128,
- ramheight=16
- }
- COMPUTER.ramsize=COMPUTER.ramwidth*COMPUTER.ramheight
- --display an error message and line number(optional) if condition is falsey
- local function check(condition,message,linenumber,argnumber)
- if not(condition) then
- if linenumber then
- if argnumber then
- error("Error: "..message.." on line "..tostring(linenumber)..":"..tostring(argnumber),0)
- else
- error("Error: "..message.." on line "..tostring(linenumber),0)
- end
- else
- error("Error: "..message,0)
- end
- end
- end
- --tonumber with support for binary literals
- local function tonumber2(value)
- if type(value)=="string" and value:sub(1,3)=="0b" then
- return tonumber(value:sub(3),2)
- else
- return tonumber(value)
- end
- end
- --magic label function
- local function makelabel(label,globallabel)
- if label:sub(1,1)=="." then
- if not(globallabel) then return nil,globallabel end
- return globallabel..label,globallabel
- else
- return label,label:match("^[^%.]*")
- end
- end
- --check if valid labelname
- local function islabel(label)
- return not(tonumber2(label) or REGISTERS[label] or label:find("[\"+%-%[%],\"]"))
- end
- --remove comments from a line
- local function removecomment(line,commentchar)
- local instring,escape
- for i=1,#line do
- local char=line:sub(i,i)
- if escape then
- escape=false
- elseif char=="\\" then
- escape=true
- elseif not(instring) and (char=="\"" or char=="'") then
- instring=char
- elseif instring==char then
- instring=false
- elseif not(instring) and char==commentchar then
- return line:sub(1,i-1)
- end
- end
- return line
- end
- --get value of number/label/register
- local function decodenumber(value,labels,globallabel)
- if type(value)=="number" then
- return value
- elseif REGISTERS[value] then
- return REGISTERS[value]
- elseif tonumber2(value) then
- return tonumber2(value)
- elseif labels and globallabel then
- value=makelabel(value,globallabel)
- if labels[value] then return labels[value] end
- else
- return value
- end
- end
- --parse arguments
- local function parseargs(args)
- local pattern=""
- local arglist={}
- local instring,escape
- local token=""
- --push token
- local function sub_pushtoken()
- if token~="" then
- arglist[#arglist+1]=decodenumber(token)
- pattern=pattern..({[true]="r",[false]="i"})[REGISTERS[token]~=nil]
- token=""
- end
- end
- for i=1,#args do
- local char=args:sub(i,i)
- --characters in strings
- if instring then
- --escaped char
- if escape then
- token=token..char
- escape=false
- --string end
- elseif char==instring then
- --char
- if instring=="'" then
- arglist[#arglist+1]=token:byte()
- pattern=pattern.."i"
- token=""
- --string
- elseif instring=="\"" then
- arglist[#arglist+1]="\""..token.."\""
- pattern=pattern.."s"
- token=""
- end
- instring=false
- --escape sequence
- elseif char=="\\" then
- escape=i
- --normal character
- else
- token=token..char
- end
- -- [ ] + - ,
- elseif char:find("^[%[%]+%-,]$") then
- sub_pushtoken""
- pattern=pattern..char
- -- string start
- elseif char=="\"" or char=="'" then
- sub_pushtoken""
- instring=char
- escape=false
- --whitespace
- elseif char==" " or char=="\t" then
- sub_pushtoken""
- --normal characters
- else
- token=token..char
- end
- end
- sub_pushtoken""
- --check for unclosed string
- if instring then return end
- return arglist,pattern
- end
- --do things with comma separated lists
- local function datalist(arglist,pattern,callback)
- --check for commas
- if not(pattern:find(string.rep("^".."[is],",#arglist-1).."[is]$")) then return end
- for _,arg in ipairs(arglist) do
- --string
- if type(arg)=="string" and arg:find("^\".*\"$") then
- for i=2,#arg-1 do
- callback(arg:byte(i))
- end
- --number
- else
- callback(arg)
- end
- end
- return true
- end
- --convert code into instructions
- local function Parsecode(code)
- local linenumber=1
- local index=0
- local instructions={}
- local labels={}
- local globallabel
- --functions for writing stuff
- local function addinstruction(instruction)
- index=index+1
- check(index<=COMPUTER.ramsize,"out of memory",linenumber)
- instruction.linenumber=linenumber
- instruction.globallabel=globallabel
- instructions[index]=instruction
- end
- local function addword(value)
- addinstruction{
- arglist={value},
- mode={{offset=0,size=29,arg=1}}
- }
- end
- local lowbyte
- local function addbyte(byte)
- if lowbyte then
- addword(byte*256+lowbyte)
- lowbyte=false
- else
- lowbyte=byte
- end
- end
- for line in string.gmatch(code.."\n","([^\r\n]*)\r?\n") do
- line=removecomment(line,";")
- --skip empty lines
- if not(line:match("^%s*$")) then
- --get opcode and arguments
- opname,args=line:match("^%s*([^%s]*)%s*(.-)%s*$")
- opname=opname:lower()
- local opcode=OPCODES[opname]
- --parse arguments
- local arglist,pattern=parseargs(args)
- check(pattern,"Error parsing arguments",linenumber)
- --instruction
- if opcode then
- if opcode.skip==1 then
- table.insert(arglist,1,0)
- pattern="r,"..pattern
- elseif opcode.skip==2 then
- arglist[#arglist]=0
- pattern=pattern..",r"
- elseif opcode.skip==3 then
- arglist[#arglist]=0
- arglist[#arglist]=0
- pattern=pattern.."r,r"
- end
- local mode=MODES[pattern]
- check(mode,"Invalid arguments",linenumber)
- if opcode.value==OPCODES["jmp"].value then
- arglist[1]=JUMPS[opname]
- end
- arglist[0]=opcode.value
- addinstruction{
- mode=mode,
- arglist=arglist
- }
- elseif opname=="nop" then
- check(pattern=="","Invalid arguments",linenumber)
- addword(0)
- --data (words)
- elseif opname=="dw" then
- check(datalist(arglist,pattern,addword),"Invalid arguments",linenumber)
- --data (bytes)
- elseif opname=="db" then
- check(datalist(arglist,pattern,addbyte),"Invalid arguments",linenumber)
- if lowbyte then addbyte(0) end
- --this is a special thing for my text game engine
- elseif opname=="dbs" then
- check(datalist(arglist,pattern,addbyte),"Invalid arguments",linenumber)
- if lowbyte then addbyte(32) end
- addword(0)
- --change address
- elseif opname=="@org" then
- check(pattern=="i","Invalid arguments",linenumber)
- local newaddress=tonumber2(arglist[1])
- check(newaddress,"Invalid address",linenumber)
- index=newaddress-1
- --label
- elseif opname:sub(-1)==":" then
- check(pattern=="","Invalid arguments",linenumber)
- opname=opname:sub(1,-2)
- check(islabel(opname),"Illegal label name '"..opname.."'",linenumber)
- opname,globallabel=makelabel(opname,globallabel)
- check(opname,"Need global label",linenumber)
- check(not(labels[opname]),"Duplicate label '"..opname.."'",linenumber)
- labels[opname]=index
- --error
- else
- check(false,"Unknown instruction '"..opname.."'",linenumber)
- end
- end
- linenumber=linenumber+1
- end
- return instructions,labels
- end
- --convert instructions to binary
- local function Makeops(instructions,labels)
- for i,instruction in pairs(instructions) do
- local linenumber=instruction.linenumber
- local mem=0
- for j,field in ipairs(instruction.mode) do
- local part
- --constant
- if field.value then
- part=decodenumber(field.value)
- --argument reference
- else
- part=decodenumber(instruction.arglist[field.arg],labels,instruction.globallabel)
- end
- check(part,"Undefined label",linenumber)
- --convert to signed/unsigned
- check(part<bit.lshift(1,field.size) and part>=-bit.lshift(1,field.size),"Overflow",linenumber,field.arg)
- if part<0 then part=bit.bnot(-part-1) end
- --insert part
- mem=mem+bit.lshift(bit.band(part,bit.lshift(1,field.size)-1),field.offset)--mem=mem|(part&(1<<field.size)-1)<<field.offset
- end
- instructions[i].binary=mem
- end
- return instructions
- end
- --write instructions into RAM
- local function Writemem(instructions)
- --find ram location
- local sx,sy
- for part=0,sim.NUM_PARTS do
- if sim.partProperty(part,"type")==elem.DEFAULT_PT_QRTZ and sim.partProperty(part,"ctype")==COMPUTER.qrtzctype then
- sx,sy=sim.partProperty(part,"x"),sim.partProperty(part,"y")
- end
- end
- check(sx and sy,"Couldn't find computer")
- sx=sx+COMPUTER.ramx
- sy=sy+COMPUTER.ramy
- --write code into RAM
- local i=1
- local wrotecells=0
- for y=0,COMPUTER.ramheight-1 do
- local beforegap=1 --"before" = lower address, not lower X coordinate
- for x=0,COMPUTER.ramwidth-1 do
- --try to find filt
- local partID = sim.partID(sx-x+beforegap,sy+y)
- --check past the gap
- if beforegap==1 and not(partID) then
- beforegap=0
- partID = sim.partID(sx-x+beforegap,sy+y)
- end
- check(partID and sim.partProperty(partID,"type")==elem.DEFAULT_PT_FILT,"Write failed (missing filt)")
- --write instructions
- if instructions[i] then
- sim.partProperty(partID,"ctype",0x20000000+instructions[i].binary)
- wrotecells=wrotecells+1
- --fill empty space
- else
- sim.partProperty(partID,"ctype",0x20000000)
- end
- i=i+1
- end
- end
- print("Success: Wrote "..tostring(wrotecells).." cells.")
- end
- --load file
- local file = io.open(...)
- local code = file:read("*a")
- file:close()
- check(code,"Load failed")
- --do the thing
- Writemem(Makeops(Parsecode(code)))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement