TriOS-alt/zubehör/sphinx/hive-port/codegen/codegen.spin

1072 lines
38 KiB
Plaintext

' 2010-02-25 new version number
con
_clkmode = xtal1 + pll8x
_xinfreq = 10_000_000
tvPin = 24
sdPin = 16
obj
term: "isxtv"
kw: "kwdefs"
st: "symbols"
bt: "bintree"
fs: "sxfile"
str: "stringx"
token: "tokenrdr"
eval: "eval"
methods: "methods"
pub Main | err, p
err := \Try
if err
if err > 0
term.str( err )
else
term.str( string("Error ") )
term.dec( err )
term.out( 13 )
if stackset and long[pSymbolTableSpace] <> $6666_6666
term.str( string("Stack corrupt",13) )
term.dec( token.LineNumber )
term.out( "," )
term.dec( token.Column + 1 )
term.out( " " )
term.out( "'" )
term.str( token.Text )
term.out( "'" )
term.out( 13 )
fs.Close
fs.Open( string("sphinx.bin"), "R" )
else
fs.Close
if link
fs.Open( string("link.bin"), "R" )
else
fs.Open( string("sphinx.bin"), "R" )
bt.Stop
fs.Execute( 0 )
pri Try
ProcessCommandLine
if verbosity => 1
term.str( string("codegen 100225", 13) )
Start
pri ProcessCommandLine | nArgs, l
fs.Open( string("args.d8a"), "R" )
nArgs := fs.ReadByte
ifnot nArgs--
abort string("usage: compile spinfile [options]")
fs.ReadStringUpperCase( @tokenFilename, MAXFILENAMELENGTH )
l := strsize( @tokenFilename )
if tokenFilename[l-4] == "." and tokenFilename[l-3] == "S" and tokenFilename[l-2] == "P" and tokenFilename[l-1] == "N"
tokenFilename[l-4]~
if strsize( @tokenFilename ) > 8
term.str( @tokenFilename )
abort string(" -- filename too long")
str.Copy( @outputFilename, @tokenFilename )
str.Copy( @binFilename, @tokenFilename )
str.Append( @tokenFilename, string(".TOK") )
str.Append( @outputFilename, string(".SOB") )
str.Append( @binFilename, string(".BIN") )
' Process command line arguments
repeat while nArgs--
fs.ReadStringUpperCase( @stringBuffer, MAXSTRINGBUFFERLENGTH )
if stringBuffer[0] == "-"
stringBuffer[0] := "/"
if strcomp( @stringBuffer, string("/L") )
link~~
elseif strcomp( @stringBuffer, string("/V") )
ifnot nArgs--
abort string("/V must be followed by a number")
verbosity := fs.ReadNumber
elseif strcomp( @stringBuffer, string("/S") )
ifnot nArgs--
abort string("/S must be followed by a number")
STACKSPACE := fs.ReadNumber
if STACKSPACE & 3
abort string("/S argument must be a multiple of 4")
elseif strcomp( @stringBuffer, string("/T") )
ifnot nArgs--
abort string("/T must be followed by a number")
TABLESPACE := fs.ReadNumber
if TABLESPACE & 3
abort string("/T argument must be a multiple of 4")
'else
' ignore
fs.Close
con
MAXFILENAMELENGTH = 8 + 1 + 3 ' 8.3
MAXSTRINGBUFFERLENGTH = 32
SXTVRENDEZVOUS = $8000 - 4
RESERVED = SXTVRENDEZVOUS - 4
SDSPIRENDEZVOUS = RESERVED - 3 * 4
SXFS2RENDEZVOUS = SDSPIRENDEZVOUS - 4 * 4 ' four rendezvous variables
SXFSRENDEZVOUS = SXFS2RENDEZVOUS - 4 * 4 ' four rendezvous variables
METADATABUFFER = SXFSRENDEZVOUS - 512
_free = ($8000 - METADATABUFFER) / 4
var
byte tokenFilename[MAXFILENAMELENGTH+1] ' input .tok file
byte outputFilename[MAXFILENAMELENGTH+1] ' output .sob file
byte binFilename[MAXFILENAMELENGTH+1] ' output .bin file (just used to empty the .bin)
byte sobFilename[MAXFILENAMELENGTH+1] ' used for child .sob file(s)
byte stringBuffer[MAXSTRINGBUFFERLENGTH+1] ' temp string buffer
dat
STACKSPACE long 575<<2
TABLESPACE long 1500<<2
verbosity byte 0 ' set by /V option
stackset byte 0
link byte 0 ' set by /L; if non-zero, run link.bin automatically.
{
|<---STACKSPACE--->|<--------TABLESPACE---------->|<-----------OBJECTSPACE------------>|<-I/O RENDEZVOUS etc.
+-----+------------+-----------+------------------+------+----------------+------------+--+
|stack| |symboltable| |header|dat/PASM|methods| | |
+-----+------------+-----------+------------------+------+----------------+------------+--+
|tempstorage| ^ ^ |stringtemp| ^
| | ^ ^ |
pObjSpace-+ pObjWork-+ | | +--$8000
pStringWork-+ pObjTop-+
}
var
' These next 3 vars actually live in methods.spin now.
' word pObjSpace ' points to area where the compiled object's header and bytecode will live (this pointer doesn't change)
' word pObjWork ' points to next available byte (this pointer changes as code is compiled)
' word pObjTop ' points just beyond available object workspace
word pDatWork ' like pObjWork but for the DAT segment
word pSymbolTableSpace
pri Start | i
pSymbolTableSpace := word[ $0a ] + STACKSPACE
methods.set_pObjSpace( pSymbolTableSpace + TABLESPACE )
methods.set_pObjTop( METADATABUFFER )
bt.Init( pSymbolTableSpace, methods.get_pObjSpace )
st.Init
long[ bt.Alloc(4) ] := $6666_6666
stackset~~
ReadTimestamp
if verbosity => 2
term.str( string("stack space: ") )
term.dec( STACKSPACE )
term.str( string(13,"table space: ") )
term.dec( TABLESPACE )
term.str( string(13,"work space: ") )
term.dec( methods.get_pObjTop - methods.get_pObjSpace )
term.out( 13 )
term.str( string("Opening ") )
term.str( @tokenFilename )
term.out( 13 )
token.Open( @tokenFilename )
bvarSize~
wvarSize~
lvarSize~
nPubs := 1 ' pub indexes start at 1; 0th entry in object header is not a pub
nPris~ ' pri indexes have to be compensated later once final nPubs is known
nObjs~
pObjList~
pObjListEnd~
ParsePass1
token.Close
CheckStack
varSize := (lvarSize + wvarSize + bvarSize + 3) & !3
pDatWork := methods.get_pObjSpace + (nPubs + nPris + nObjs) << 2 ' each PUB/PRI/OBJ takes up 4 bytes in the object header
methods.set_pObjWork( pDatWork + datSize )
bytefill( methods.get_pObjSpace, 0, pDatWork - methods.get_pObjSpace ) ' zero the header
FixupOffsets( bt.PeekW( st.GetPSymbolTable ) )
token.Open( @tokenFilename )
ParsePass2
token.Close
methods.set_pObjWork( (methods.get_pObjWork + 3) & !3 ) ' Round up to multiple of 4
WriteSobFile
pri CheckStack
if long[pSymbolTableSpace] <> $6666_6666
abort string("Stack overflow")
pri ReadTimestamp
if verbosity => 3
term.str( string("reading timestamp: ") )
if fs.Open( string("timestmp.d8a"), "R" ) == 0
timestamp := fs.ReadLong + 1
fs.Close
else
timestamp := 1
if verbosity => 3
term.dec( timestamp )
term.out( 13 )
fs.Open( string("timestmp.d8a"), "W" )
fs.WriteLong( timestamp )
fs.Close
pri WriteSobFile | p
if nPubs == 1 ' recall that nPubs starts at 1 because of the 0th entry in the object header.
abort string("No PUB routines defined")
ifnot link ' If we're not running the linker immediately after codegen,
if verbosity => 3 ' empty the .bin file if it exists(would be better to delete,
term.str( string("Zeroing ") ) ' but sxfs doesn't have that ability). If we forget to link,
term.str( @binFilename ) ' we'll get an error when we try to run the .bin.
term.out( 13 )
if fs.Open( @binFilename, "R" ) == 0 ' Check for existence
fs.Close
fs.Open( @binFilename, "W" )
fs.Close
objBinSize := methods.get_pObjWork - methods.get_pObjSpace ' This gets stored in .sob header
word[methods.get_pObjSpace][0] := objBinSize ' This gets stored in object header
word[methods.get_pObjSpace][1] := nPubs + nPris + nObjs << 8
' Compute checksum and hash
checksum~
hash~
p := methods.get_pObjSpace
repeat objBinSize
checksum += byte[p]
hash := (hash <- 1) ^ byte[p++]
if verbosity => 2
term.str( string("Writing ") )
term.str( @outputFilename )
term.out( 13 )
wordfill( @numExports, 0, 4 )
ComputeSobInfo( bt.PeekW( st.GetPSymbolTable ) )
fs.Open( @outputFilename, "W" )
fs.Write( @SobFileHeader, SIZEOFSOBHEADER )
WriteExports( bt.PeekW( st.GetPSymbolTable ) )
WriteImports
fs.Write( methods.get_pObjSpace, objBinSize )
fs.Close
pri FixupOffsets( p ) | s, t, v, size, headerSize, q
{{
Goes through the symbol table and adjusts offsets of word VARs to put them after long VARs
and puts byte VARs after word VARs.
Also adjusts PRI method indexes so they come after PUBs, and OBJ indexes to put them after PRIs.
And now also adjusts DAT labels upward by the size of the object header.
}}
ifnot p
return
FixupOffsets( bt.PeekW( p ) )
s := p + 4
s += strsize(s) + 1
if byte[s] == st#kVAR_SYMBOL ' var is followed by
++s
size := byte[s++] ' 1-byte size (1, 2, 4) and 2-byte offset
t~
if size == 2
t := lvarSize ' word vars come after long vars
elseif size == 1
t := lvarSize + wvarSize ' byte vars come after word vars
bt.PokeW( s, bt.PeekW(s) + t ) ' adjust var symbol's offset
elseif byte[s] == st#kPRI_SYMBOL
++s
byte[s] += nPubs ' the PUBs are 1..nPubs-1, the PRIs are nPubs..nPubs+nPris-1
elseif byte[s] == st#kOBJ_SYMBOL
++s
byte[s][2] += nPubs + nPris ' the OBJs are nPubs+nPris..
elseif byte[s] == st#kDAT_SYMBOL
headerSize := (nPubs + nPris + nObjs) << 2
q := s ' start at global label
repeat ' and follow the linked list of local labels
bt.PokeW( q+2, bt.PeekW( q+2 ) + headerSize ) ' adjust label's dp
ifnot q := bt.PeekW( q+6 )
quit
q += strsize(q) + 1 ' next in the list
FixupOffsets( bt.PeekW( p + 2 ) )
{
pri TraverseTable( p ) | s, t, v, q
{
Symbol table dump. Call thusly:
TraverseTable( bt.PeekW( st.GetPSymbolTable ) )
}
ifnot p
return
TraverseTable( bt.PeekW( p ) )
s := p + 4
term.str( s )
s += strsize(s) + 1
case t := byte[s++]
st#kINT_CON_SYMBOL:
v := bt.PeekL( s )
term.out( "=" )
term.dec( v )
st#kPRI_SYMBOL, st#kPUB_SYMBOL:
if t == st#kPRI_SYMBOL
term.str( string(" pri: ") )
else
term.str( string(" pub: ") )
term.dec( byte[s++] ) ' method index
term.out( " " )
term.dec( byte[s++] ) ' #params
st#kDAT_SYMBOL:
q := s-1
repeat while q := bt.PeekW( q+6 )
term.out(":")
term.str( q )
q += strsize( q ) + 1
term.str( string(" dat ") )
case byte[s++]
1: term.str( string("byte@") )
2: term.str( string("word@") )
4: term.str( string("long@") )
term.hex( bt.PeekW(s), 4 )
term.out(",")
term.hex( bt.PeekW(s+2), 4 )
st#kVAR_SYMBOL:
term.str( string(" var ") )
case byte[s++]
1: term.str( string("byte@") )
2: term.str( string("word@") )
4: term.str( string("long@") )
term.hex( bt.PeekW(s), 4 )
st#kOBJ_SYMBOL:
term.out( ":" )
term.str( bt.PeekW(s) )
st#kSOB_SYMBOL:
term.out( ">" )
term.hex( bt.PeekW(s), 4)
other:
term.out( "?" )
term.hex( t, 2 )
term.out( ";" )
term.out( " " )
TraverseTable( bt.PeekW( p + 2 ) )
}
con
SIZEOFSOBHEADER = 26
dat
SobFileHeader long
byte "SOB1"
timestamp long 0
hash long 0
numExports word 0
exportSize word 0
numImports word 0
importSize word 0
objBinSize word 0
checksum byte 0
{padding} byte 0
varSize word 0 ' total size in bytes of vars (rounded up to multiple of 4)
pri ComputeSobInfo( p ) | pName, type
{{
Traverses the symbol table and computes exportSize, numExports, importSize, numImports.
Initialize those variables to 0 before entering this routine.
}}
ifnot p
return
pName := p + 4
type := byte[pName + strsize(pName) + 1]
if type == st#kINT_CON_SYMBOL or type == st#kFLOAT_CON_SYMBOL
++numExports
exportSize += strsize(pName) + 6 ' name + null + type + 4-byte value
elseif type == st#kPUB_SYMBOL
++numExports
exportSize += strsize(pName) + 4 ' name + null + type + index + #args
elseif type == st#kOBJ_SYMBOL
pName := bt.PeekW( pName + strsize(pName) + 2 )
++numImports
importSize += strsize(pName) - 4 + 4 ' name (excluding ".SOB") + null + 2-byte count + reserved
ComputeSobInfo( bt.PeekW( p ) )
ComputeSobInfo( bt.PeekW( p + 2 ) )
pri WriteExports( p ) | pName, pData, type
{{
Traverses the symbol table and writes exports to output file.
}}
ifnot p
return
pName := p + 4
pData := pName + strsize(pName) + 1
type := byte[pData]
if type == st#kINT_CON_SYMBOL or type == st#kFLOAT_CON_SYMBOL ' name + null + type + 4-byte value
fs.WriteString( pName )
fs.WriteByte( type )
fs.WriteLong( bt.PeekL( pData+1 ) )
elseif type == st#kPUB_SYMBOL ' name + null + type + index + #args
fs.WriteString( pName )
fs.WriteByte( type )
fs.Write( pData+1, 2 ) ' index + #args
WriteExports( bt.PeekW( p ) )
WriteExports( bt.PeekW( p + 2 ) )
pri WriteImports | pName, pData, type
{{
Traverses the OBJ list and writes imports to output file.
We use a list so that the imports are written in the order they appear in the source.
This is important for OBJ symbols because we want the SOB imports to line up properly.
}}
pName := pObjList
repeat while pName
pData := pName + strsize(pName) + 1 ' pName is name of symbol (e.g. "DEBUG")
' pData points to symbol's data: 1-byte type + 2-byte pointer to SOB + 1-byte index + 2-byte count + 2-byte link
pName := bt.PeekW( pData + 1 ) ' pName is name of .sob file (e.g. "TV_TEXT.SOB")
fs.Write( pName, strsize(pName)-4 ) ' Write name but drop .SOB suffix.
fs.WriteByte( 0 ) ' Write null terminator
fs.WriteWord( bt.PeekW( pData + 4 ) ) ' Write count
fs.WriteByte( 0 ) ' Write reserved byte
pName := bt.PeekW( pData + 6 ) ' link to next
pri ParsePass1
Eval.set_pass( 1 )
InitDat
if verbosity => 2
term.str( string("Pass 1") )
term.out( 13 )
ParseCon1
repeat while token.Type <> kw#kEOF
ifnot token.IsBlockDesignator
abort string("Syntax error")
if token.Column
abort string("Block designator not in column 1")
if token.AdvanceIf( kw#kCON )
ParseCon1
elseif token.AdvanceIf( kw#kDAT )
ParseDat1
elseif token.AdvanceIf( kw#kOBJ )
ParseObj1
elseif token.AdvanceIf( kw#kPRI )
ParseMethod1( st#kPRI_SYMBOL )
elseif token.AdvanceIf( kw#kPUB )
ParseMethod1( st#kPUB_SYMBOL )
elseif token.AdvanceIf( kw#kVAR )
ParseVar1
else
abort string("Syntax error")
pri ParsePass2
Eval.set_pass( 2 )
InitDat
if verbosity => 2
term.str( string("Pass 2") )
term.out( 13 )
ParseCon2
repeat while token.Type <> kw#kEOF
CheckStack
ifnot token.IsBlockDesignator
abort string("Syntax error")
if token.AdvanceIf( kw#kCON )
ParseCon2
elseif token.AdvanceIf( kw#kDAT )
ParseDat2
elseif token.AdvanceIf( kw#kOBJ )
SkipBlock
elseif token.AdvanceIf( kw#kPRI )
methods.Parse2( st#kPRI_SYMBOL )
elseif token.AdvanceIf( kw#kPUB )
methods.Parse2( st#kPUB_SYMBOL )
elseif token.AdvanceIf( kw#kVAR )
SkipBlock
else
abort string("Syntax error")
pri SkipBlock
repeat until token.IsBlockDesignator or token.Type == kw#kEOF
token.Advance
pri ParseCon1 | o, p, inc, v
o~
token.AdvanceIf( kw#kEOL )
repeat
inc := 1
if token.AdvanceIf( kw#kOCTOTHORP )
o := Eval.EvaluateExpression( 0 )
elseif token.IsId ' Id +?
st.AddToTable( token.Text )
byte[ p := bt.Alloc(1) ] := st#kINT_CON_SYMBOL ' p points to symbol type byte
' in case we have to patch the type retroactively
token.Advance
if token.AdvanceIf( kw#kEQUAL ) ' Id =
v := Eval.TryToEvaluateExpression( 0 )
if Eval.Succeeded
bt.PokeL( bt.Alloc(4), v )
else ' it is undefined
byte[p] := st#kUNDEFINED_CON_SYMBOL
bt.Alloc(4) ' but we should allocate space for the eventual value.
elseif token.AdvanceIf( kw#kLBRACKET ) ' Id [
inc := Eval.EvaluateExpression( 0 )
token.Eat( kw#kRBRACKET )
bt.PokeL( bt.Alloc(4), o )
o += inc
else ' Plain Id
bt.PokeL( bt.Alloc(4), o )
o += inc
else
quit
if token.Type == kw#kCOMMA or token.Type == kw#kEOL
token.Advance
else
abort string("Syntax error")
pri ParseCon2 | o, p, inc
o~
token.AdvanceIf( kw#kEOL )
repeat
inc := 1
if token.AdvanceIf( kw#kOCTOTHORP )
o := Eval.EvaluateExpression( 0 )
elseif token.IsId ' Id +?
p := st.SymbolLookup( token.Text )
token.Advance
if token.AdvanceIf( kw#kEQUAL ) ' Id =
bt.PokeL( p+1, Eval.EvaluateExpression( 0 ) )
byte[p] := st#kINT_CON_SYMBOL
elseif token.AdvanceIf( kw#kLBRACKET ) ' Id [
inc := Eval.EvaluateExpression( 0 )
token.Eat( kw#kRBRACKET )
o += inc
else ' Plain Id
o += inc
else
quit
if token.Type == kw#kCOMMA or token.Type == kw#kEOL
token.Advance
else
abort string("Syntax error")
var
word dp ' dp is the address in DAT space of current source line.
' dp = pObjWork - pObjSpace
word ooo ' dp + ooo = cogx4; cogx4 / 4 = cog address of current source line.
word pGlobalLabel ' points to most-recently defined global label; used as the head of a linked list of local labels
byte alignment ' 1, 2, or 4
{
a DAT line consists of (in rough pseudo-bnf) one of the following:
1) [label | :label] ORG|RES [ <expr> ]
2) [label | :label] <size> <expr>* (with commas between <expr> -- yeah, yeah, I said it was rough)
3) [label | :label] [ <cond> ] <pasm op> <dst> , <src> <effect>*
As we process DAT statements and emit bytes, pDatWork increases, always pointing where the next byte will go.
dp = pDatWork - pObjSpace (i.e., dp 0 is the beginning of DAT space)
cogx4 = dp + ooo, where ooo is updated each time an ORG directive is encountered:
ooo = org_expression * 4 - dp
Example:
Let us say pObjSpace is 1000. The object header starts there. Say it takes up 100 bytes, so pDatWork is 1100.
In terms of DAT space, dp = 1100 - 1000 = 100.
Say there's an ORG 100, so ooo = 100 * 4 - 100 = 300.
A label defined at this point would have dp = 100, cogx4 = 100 + 300 = 400 (divide this by 4 to get cog address 100).
Suppose we assemble a PASM instruction and emit the four bytes.
Now pDatWork = 1104, dp = 104, and cogx4 = 104 + 300 = 404 (cog address 101).
}
pri InitDat
if Eval.get_pass == 1
dp~
ooo~
else
ooo := methods.get_pObjSpace - pDatWork '= -dp
pGlobalLabel~
alignment := 1
pri ParseDat1 | pLabelData, p, v, size, count, dpInc, temp, cogx4
Eval.EnteringDat
cogx4~
repeat
pLabelData~
if token.AdvanceIf( kw#kEOL )
next
if token.IsId ' label?
if byte[token.Text] == ":" ' local label?
' Traverse the list of locals to check for duplicates
p := pGlobalLabel
repeat while p := bt.PeekW( p + 6 ) ' p points to string part of local label
if strcomp( p, token.Text )
abort string("Duplicate local label")
p += strsize(p) + 1 ' Increment p past string part to data part
p := bt.Alloc( strsize( token.Text ) + 9 ) ' p points to
str.Copy( p, token.Text ) ' local label's text followed by
pLabelData := p + strsize( token.Text ) + 1 ' 8 bytes of label data (to be filled in later)
byte[ pLabelData ] := st#kDAT_SYMBOL ' type
bt.PokeW( pLabelData+6, bt.PeekW(pGlobalLabel+6) ) ' pointer to local labels
ifnot pGlobalLabel
abort string("No preceding global label")
bt.PokeW( pGlobalLabel+6, p ) ' Insert this local label at head of list
else ' plain old label
st.AddToTable( token.Text )
pLabelData := bt.Alloc( 8 ) ' local label data
byte[ pLabelData ] := st#kDAT_SYMBOL ' type
bt.PokeW( pLabelData+6, 0 ) ' pointer to local labels
pGlobalLabel := pLabelData
token.Advance
dpInc~
if token.AdvanceIf( kw#kORG ) ' Case 1a: ORG [ <expr> ]
alignment := 4
PadToAlignment
temp~ ' default ORG is 0
if token.Type <> kw#kEOL
temp := Eval.EvaluateExpression( pGlobalLabel ) << 2
ooo := temp - dp
cogx4 := dp + ooo
elseif token.AdvanceIf( kw#kRES ) ' Case 1b: RES [ <expr> ]
alignment := 4
PadToAlignment
temp := 4 ' default RES is 1 long
if token.Type <> kw#kEOL
temp := Eval.EvaluateExpression( pGlobalLabel ) << 2
cogx4 := dp + ooo
ooo += temp
elseif token.AdvanceIf( kw#kFIT )
temp := 496
if token.Type <> kw#kEOL
temp := Eval.EvaluateExpression( pGlobalLabel )
if cogx4 > temp << 2
abort string("Origin exceeds FIT limit")
elseif token.IsSize ' Case 2. BYTE/WORD/LONG <expr>*
alignment := |< (token.Type - kw#kBYTE) ' 1/2/4
PadToAlignment
cogx4 := dp + ooo
token.Advance
if token.Type <> kw#kEOL
repeat
size := alignment
if token.IsSize
size := |< (token.Type - kw#kBYTE) ' 1/2/4 -- just size
token.Advance
v := Eval.TryToEvaluateExpression( pGlobalLabel )
count := 1
if token.AdvanceIf( kw#kLBRACKET )
count := Eval.EvaluateExpression( pGlobalLabel )
token.Eat( kw#kRBRACKET )
dpInc += count * size
while token.AdvanceIf( kw#kCOMMA )
elseif token.IsCond or token.IsPasm ' Case 3. [ <cond> ] <pasm op>
alignment := 4
PadToAlignment
cogx4 := dp + ooo
dpInc := 4
repeat
token.Advance
until token.Type == kw#kEOL
elseif token.Type <> kw#kEOL
abort string("Syntax error")
if pLabelData ' Was a label defined on the current source line?
byte[pLabelData+1] := alignment ' data size
bt.PokeW( pLabelData+2, dp ) ' Address in DAT space
bt.PokeW( pLabelData+4, cogx4 ) ' Cog address x 4
' The local label linked list pointer was filled in previously
dp += dpInc
cogx4 += dpInc
until token.IsBlockDesignator or token.Type == kw#kEOF
datSize := dp
Eval.LeavingDat
pri ParseDat2 | pLabelData, p, v, size, count, temp, cond, pasmOp, ds, effect, oooInc
Eval.EnteringDat
oooInc~
repeat
pLabelData~
if token.AdvanceIf( kw#kEOL )
next
if token.IsId ' label?
if byte[token.Text] == ":" ' local label?
' Traverse the list of locals to find this one.
p := pGlobalLabel
repeat while p := bt.PeekW( p + 6 ) ' p points to string part of local label
if strcomp( p, token.Text )
quit
p += strsize(p) + 1 ' Increment p past string part to data part
pLabelData := p + strsize(p) + 1
else ' plain old label
pLabelData := st.SymbolLookup( token.Text )
pGlobalLabel := pLabelData
token.Advance
dp := pDatWork - methods.get_pObjSpace
if token.AdvanceIf( kw#kORG ) ' Case 1: ORG <expr>
temp~ ' default ORG is 0
alignment := 4
PadToAlignment
if token.Type <> kw#kEOL
temp := Eval.EvaluateExpression( pGlobalLabel ) << 2
ooo := temp - dp
elseif token.AdvanceIf( kw#kRES ) ' Case 1b: RES [ <expr> ]
alignment := 4
PadToAlignment
oooInc := 4 ' default RES is 1 long
if token.Type <> kw#kEOL
oooInc := Eval.EvaluateExpression( pGlobalLabel ) << 2
elseif token.AdvanceIf( kw#kFIT ) ' ignore FIT in pass 2
if token.Type <> kw#kEOL
Eval.EvaluateExpression( pGlobalLabel )
elseif token.IsSize ' Case 2. BYTE/WORD/LONG <expr>*
alignment := |< (token.Type - kw#kBYTE) ' 1/2/4
PadToAlignment
token.Advance
if token.Type == kw#kEOL
next
repeat
size := alignment
if token.IsSize
size := |< (token.Type - kw#kBYTE) ' 1/2/4 -- just size
token.Advance
v := Eval.EvaluateExpression( pGlobalLabel )
count := 1
if token.AdvanceIf( kw#kLBRACKET )
count := Eval.EvaluateExpression( pGlobalLabel )
token.Eat( kw#kRBRACKET )
repeat count
temp := v
repeat size
EmitDat( temp )
temp >>= 8
while token.AdvanceIf( kw#kCOMMA )
elseif token.IsCond or token.IsPasm
alignment := 4
PadToAlignment
cond~~
if token.IsCond
cond := token.GetCond
token.Advance
ifnot token.IsPasm
abort string("Expected PASM instruction")
ds := %11
case token.Type
kw#kAND: pasmOp := $60bc0000 ' Special handling for double-duty PASM mnemonics
kw#kOR: pasmOp := $68bc0000
kw#kWAITCNT: pasmOp := $f8bc0000
kw#kWAITPEQ: pasmOp := $f03c0000
kw#kWAITPNE: pasmOp := $f43c0000
kw#kWAITVID: pasmOp := $fc3c0000
kw#kCOGID: pasmOp := $0cfc0001
ds := %10
kw#kCOGINIT: pasmOp := $0c7c0002
ds := %10
kw#kCOGSTOP: pasmOp := $0cfc0003
ds := %10
kw#kCLKSET: pasmOp := $0c7c0000
ds := %10
other:
pasmOp := token.GetPasmOp
ds := token.GetPasmDS
if token.Type == kw#kCALL ' special handling for CALL instruction
token.Advance
token.Eat( kw#kOCTOTHORP )
ifnot token.IsId
abort string("Expected return label")
str.Copy( @stringBuffer, token.Text )
str.Append( @stringBuffer, string("_RET") )
ifnot p := st.SymbolLookup( @stringBuffer )
abort string("Return label not found")
if byte[p] <> st#kDAT_SYMBOL
abort string("Non-DAT return label")
temp := bt.PeekW( p+4 )
if temp & 3
abort string("Return label is not long")
pasmOp |= ((temp >> 2 & $1ff) << 9) | (Eval.EvaluateExpression( pGlobalLabel ) & $1ff) ' combine destination and source
else ' regular PASM instruction
token.Advance
if ds & %10 ' destination operand
pasmOp |= (Eval.EvaluateExpression( pGlobalLabel ) & $1ff) << 9
if ds == %11
token.Eat( kw#kCOMMA )
if ds & %01
if token.AdvanceIf( kw#kOCTOTHORP ) ' immediate?
pasmOp |= |< 22 ' set I
pasmOp |= Eval.EvaluateExpression( pGlobalLabel ) & $1ff ' source operand
effect~
if token.Type <> kw#kEOL
repeat
ifnot token.IsEffect
abort string("Expected PASM effect")
if token.GetEffect == 8 ' NR?
effect &= %110 ' clear R
else
effect |= token.GetEffect
token.Advance
while token.AdvanceIf( kw#kCOMMA )
if pasmOp == 0 and (cond <> -1 or effect )
abort string("Conditions and effects not allowed on NOP")
pasmOp |= (effect << 23)
if cond <> -1
pasmOp := pasmOp & %111111_1111_0000_111111111_111111111 | (cond << 18)
repeat 4
EmitDat( pasmOp )
pasmOp >>= 8
if pLabelData ' Was a label defined on the current source line?
if byte[pLabelData+1] <> alignment { data size
} or bt.PeekW( pLabelData+2 ) <> dp { Address in DAT space
} or bt.PeekW( pLabelData+4 ) <> dp + ~~ooo ' Cog address x 4
term.dec(byte[pLabelData+1])
term.out(" ")
term.dec(alignment)
term.out(13)
term.dec(bt.PeekW( pLabelData+2 ))
term.out(" ")
term.dec(dp)
term.out(13)
term.dec(bt.PeekW( pLabelData+4 ))
term.out(" ")
term.dec(dp+~~ooo)
abort string("Phase error")
ooo += oooInc~
until token.IsBlockDesignator or token.Type == kw#kEOF
Eval.LeavingDat
pri PadToAlignment
repeat (alignment - dp) & (alignment - 1) ' number of bytes needed to pad to new alignment
++dp
if Eval.get_pass == 2
EmitDat( 0 ) ' padding
pri EmitDat( b )
byte[pDatWork++] := b
var
word pObjList ' OBJ symbols are linked together in a list.
word pObjListEnd ' Pointer to the last OBJ; append new OBJs at the end.
pri ParseObj1 | i, count, pObj, pSob, pObjName
{{
<objId> { [ <const expr> ] } : <sobId>
}}
token.AdvanceIf( kw#kEOL )
repeat while token.IsId
pObjName := bt.Alloc( 0 ) + 4 ' Hacky way to get pointer to the name field of the symbol we're about to add.
st.AddToTable( token.Text )
pObj := bt.Alloc( 8 ) ' type (1), ptr to sym table (2), index(1), count (2), ptr to next (2)
byte[pObj] := st#kOBJ_SYMBOL
token.Advance
count := 1
if token.AdvanceIf( kw#kLBRACKET )
count := Eval.EvaluateExpression( 0 )
token.Eat( kw#kRBRACKET )
token.Eat( kw#kCOLON )
byte[pObj][3] := nObjs
nObjs += count
bt.PokeW( pObj+4, count )
' Link this OBJ into the list of OBJs
if pObjListEnd ' this points to last OBJ name field
pObjListEnd += strsize(pObjListEnd)+1 ' now this points to OBJ data
bt.PokeW( pObjListEnd+6, pObjName ) ' make a link to new OBJ name field
pObjListEnd := pObjName
bt.PokeW( pObj+6, 0 )
ifnot pObjList
pObjList := pObjName
i~
repeat
ifnot token.IsIntLiteral
abort string("Syntax error")
sobFilename[i++] := token.Value
if i > MAXFILENAMELENGTH-4 ' have to leave room for ".sob" suffix
abort string("SOB filename too long")
token.Advance
while token.AdvanceIf( kw#kCOMMA )
sobFilename[i]~
str.ToUpper( @sobFilename )
str.Append( @sobFilename, string(".SOB") )
ifnot pSob := st.SymbolLookup( @sobFilename )
st.AddToTable( @sobFilename )
byte[ pSob := bt.Alloc(3) ] := st#kSOB_SYMBOL ' type (1), ptr to sym table (2)
bt.PokeW( pSob+1, 0 )
ReadSobFile( pSob+1 )
bt.PokeW( pObj+1, pSob - strsize(@sobFilename) - 1 )
token.Eat( kw#kEOL )
con
SOBFILEFORMATVERSION = "S" + "O" << 8 + "B" << 16 + "1" << 24 ' "SOB1"
pri ReadSobFile( pSobTable ) | p, t
if verbosity => 3
term.str( string("Reading ") )
term.str( @sobFilename )
term.out( 13 )
if fs.Open( @sobFilename, "R" ) <> 0
term.str( @sobFilename )
abort string(" -- couldn't open")
if fs.Readlong <> SOBFILEFORMATVERSION ' SOB file format version
abort string("Unrecognized SOB file format")
fs.Readlong ' timestamp
fs.ReadLong ' hash
numExports := fs.ReadWord ' number of exports
exportSize := fs.ReadWord ' size of exports segment
fs.ReadWord ' number of imports
importSize := fs.ReadWord ' size of imports segment
fs.ReadWord ' size of bytecode segment
fs.ReadByte ' checksum
fs.ReadByte ' padding
fs.ReadWord ' size of sob's VAR space
repeat numExports
fs.ReadStringUpperCase( @stringBuffer, MAXSTRINGBUFFERLENGTH )
bt.AddToTable( @stringBuffer, pSobTable )
case t := fs.ReadByte
st#kINT_CON_SYMBOL, st#kFLOAT_CON_SYMBOL:
p := bt.Alloc( 5 )
byte[p++] := t
bt.PokeL( p, fs.ReadLong )
st#kPUB_SYMBOL:
p := bt.Alloc( 3 )
byte[p++] := t
byte[p++] := fs.ReadByte ' method index
byte[p++] := fs.ReadByte ' #params
fs.Close
pri ParseMethod1( type ) | nargs
' type = st#kPRI_SYMBOL or st#kPUB_SYMBOL
ifnot token.IsId
abort string("Expected ID")
st.AddToTable( token.Text )
token.Advance
nargs~
if token.AdvanceIf( kw#kLPAREN )
repeat
++nargs
ifnot token.IsId
abort string("Expected ID")
token.Advance
while token.AdvanceIf( kw#kCOMMA )
token.Eat( kw#kRPAREN )
if nargs > 255
abort string("Too many parameters")
byte[ bt.Alloc(1) ] := type
if type == st#kPUB_SYMBOL
byte[ bt.Alloc(1) ] := nPubs++
else
byte[ bt.Alloc(1) ] := nPris++
byte[ bt.Alloc(1) ] := nargs
SkipBlock
var
word bvarSize ' size in bytes of byte var area
word wvarSize ' size in bytes of word var area
word lvarSize ' size in bytes of long var area
byte nPubs, nPris
word nObjs
word datSize ' size in bytes of DAT area
pri ParseVar1 | s, t, dim
token.AdvanceIf( kw#kEOL )
repeat while token.IsSize
if token.Type == kw#kBYTE
s~
elseif token.Type == kw#kWORD
s := 1
elseif token.Type == kw#kLONG
s := 2
token.Advance
repeat
ifnot token.IsId
abort string("Expected ID")
st.AddToTable( token.Text )
token.Advance
byte[ bt.Alloc(1) ] := st#kVAR_SYMBOL
byte[ bt.Alloc(1) ] := |< s
if token.AdvanceIf( kw#kLBRACKET )
dim := Eval.EvaluateExpression( 0 )
token.Eat( kw#kRBRACKET )
else
dim := 1
bt.PokeW( bt.Alloc(2), bvarSize[s] )
bvarSize[s] += dim << s
while token.AdvanceIf( kw#kCOMMA )
token.Eat( kw#kEOL )
{{
Copyright (c) 2009 Michael Park
+------------------------------------------------------------------------------------------------------------------------------+
| TERMS OF USE: MIT License |
+------------------------------------------------------------------------------------------------------------------------------+
|Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation |
|files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, |
|modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software|
|is furnished to do so, subject to the following conditions: |
| |
|The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.|
| |
|THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
|WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
|COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
|ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
+------------------------------------------------------------------------------------------------------------------------------+
}}