mirror of
https://github.com/KarolS/millfork.git
synced 2025-02-03 11:33:33 +00:00
Preliminary LUnix support
This commit is contained in:
parent
5dc1bba8ed
commit
8931ced6fc
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,6 +6,8 @@ project/project/target/
|
||||
stuff
|
||||
releases
|
||||
src/test/scala/experiments/
|
||||
# doesn't work yet
|
||||
examples/lunix/
|
||||
|
||||
# hidden files
|
||||
*.~
|
||||
@ -18,6 +20,7 @@ src/test/scala/experiments/
|
||||
|
||||
# compiled Millfork files
|
||||
*.prg
|
||||
*.seq
|
||||
*.asm
|
||||
*.lbl
|
||||
*.xex
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
## Current version
|
||||
|
||||
* Preliminary Atari 2600 and BBC Micro support.
|
||||
* Preliminary Atari 2600, BBC Micro and LUnix support.
|
||||
|
||||
* Added array initialization syntax with `for` (not yet finalized).
|
||||
|
||||
|
@ -22,6 +22,8 @@ For binary releases, see: https://github.com/KarolS/millfork/releases (latest: 0
|
||||
|
||||
* Commodore 128
|
||||
|
||||
* Commodore 64/128 running LUnix/LNG 0.21 (experimental)
|
||||
|
||||
* Commodore PET
|
||||
|
||||
* Commodore Vic-20 (stock or with RAM extensions)
|
||||
|
@ -15,6 +15,8 @@ The following platforms are currently supported:
|
||||
|
||||
* `c64_scpu16` – Commodore 64 with SuperCPU in native, 16-bit mode (very buggy)
|
||||
|
||||
* `lunix` – Commodore 64 or 128 running LUnix/LNG 0.21
|
||||
|
||||
* `c16` – Commodore 16
|
||||
|
||||
* `plus4` – Commodore Plus/4
|
||||
@ -121,7 +123,9 @@ Every platform is defined in an `.ini` file with an appropriate name.
|
||||
default is `false` on 65C02-compatible processors and `true` elsewhere
|
||||
|
||||
* `compact_dispatch_params` – whether parameter values in return dispatch statements may overlap other objects, default is `true`
|
||||
This may cause problems if the parameter table is stored next to a hardware register that has side effects when reading.
|
||||
This may cause problems if the parameter table is stored next to a hardware register that has side effects when reading.
|
||||
|
||||
* `lunix` – generate relocatable code for LUnix/LNG, default is `false`
|
||||
|
||||
|
||||
#### `[allocation]` section
|
||||
@ -155,6 +159,8 @@ Default: `after_code`.
|
||||
|
||||
* `single` – output a single file, based mostly, but not necessarily only on data in the `default` segment (the default)
|
||||
|
||||
* `lunix` – like `single`, but add data necessary for relocation between code and data (requires `lunix` option in the `compilation` section)
|
||||
|
||||
* `per_segment` – generate a separate file with each segment
|
||||
|
||||
* `format` – output file format; a comma-separated list of tokens:
|
||||
@ -163,10 +169,14 @@ Default: `after_code`.
|
||||
|
||||
* `startaddr` – little-endian 16-bit address of the first used byte of the compiled output (not necessarily the segment start)
|
||||
|
||||
* `startpage` – the high byte of `startaddr`
|
||||
|
||||
* `endaddr` – little-endian 16-bit address of the last used byte of the compiled output (usually not the segment end)
|
||||
|
||||
* `allocated` – all used bytes
|
||||
|
||||
* `pagecount` – the number of pages used by all used bytes (including partially filled pages)
|
||||
|
||||
* `<addr>:<addr>` - inclusive range of bytes
|
||||
|
||||
* `<segment>:<addr>:<addr>` - inclusive range of bytes in a given segment
|
||||
|
24
include/lunix.ini
Normal file
24
include/lunix.ini
Normal file
@ -0,0 +1,24 @@
|
||||
; Commodore 64/128 running LUnix 0.21
|
||||
|
||||
[compilation]
|
||||
arch=nmos
|
||||
modules=lunix
|
||||
lunix=true
|
||||
|
||||
|
||||
[allocation]
|
||||
zp_pointers=$80-$bf
|
||||
segments=default
|
||||
default_code_segment=default
|
||||
segment_default_start=$1006
|
||||
segment_default_codeend=$8fff
|
||||
segment_default_datastart=after_code
|
||||
segment_default_end=$8fff
|
||||
|
||||
|
||||
[output]
|
||||
style=lunix
|
||||
format=$ff,$fe,0,21,pagecount,startpage,allocated
|
||||
extension=prg
|
||||
|
||||
|
225
include/lunix.mfk
Normal file
225
include/lunix.mfk
Normal file
@ -0,0 +1,225 @@
|
||||
const word lkf_jumptab = $200
|
||||
|
||||
inline asm byte get_ipid() {
|
||||
? lda 2
|
||||
? rts
|
||||
}
|
||||
|
||||
pointer tsp @4
|
||||
|
||||
pointer argv @$80
|
||||
byte argc
|
||||
|
||||
inline asm byte get_pdmajor() {
|
||||
? ldy #$25
|
||||
? lda (tsp),y
|
||||
? rts
|
||||
}
|
||||
|
||||
inline asm byte get_pdminor() {
|
||||
? ldy #$26
|
||||
? lda (tsp),y
|
||||
? rts
|
||||
}
|
||||
|
||||
inline asm word get_pid() {
|
||||
? ldy #$28
|
||||
? lda (tsp),y
|
||||
? tax
|
||||
? dey
|
||||
? lda (tsp),y
|
||||
? rts
|
||||
}
|
||||
|
||||
inline asm byte get_parent_ipid() {
|
||||
? ldy #$29
|
||||
? lda (tsp),y
|
||||
? rts
|
||||
}
|
||||
|
||||
byte errno
|
||||
|
||||
inline asm void set_errno_on_carry_return_a() {
|
||||
? tax
|
||||
? jmp set_errno_on_carry_return_x()
|
||||
}
|
||||
|
||||
inline asm void set_errno_on_carry_return_x() {
|
||||
? bcs _no_error
|
||||
? lda #0
|
||||
_no_error:
|
||||
? sta errno
|
||||
? txa
|
||||
? rts
|
||||
}
|
||||
|
||||
inline asm void set_errno_on_carry_return_nothing() {
|
||||
? bcs _no_error
|
||||
? lda #0
|
||||
_no_error:
|
||||
? sta errno
|
||||
? rts
|
||||
}
|
||||
|
||||
asm void set_zpsize(byte a) @lkf_jumptab + $0000 extern
|
||||
|
||||
asm void get_moduleif() @lkf_jumptab + $0002 extern // TODO
|
||||
|
||||
asm void _fopen(word ay, byte x) @lkf_jumptab + $0004 extern
|
||||
asm byte fopen(word ay, byte x) {
|
||||
? jsr _fopen
|
||||
? jmp set_errno_on_carry_return_x
|
||||
}
|
||||
|
||||
asm void _fopendir(word ay) @lkf_jumptab + $0006 extern
|
||||
asm byte fopendir(word ay) {
|
||||
? jsr _fopendir
|
||||
? jmp set_errno_on_carry_return_x
|
||||
}
|
||||
|
||||
asm void _fclose(byte x) @lkf_jumptab + $0008 extern
|
||||
asm byte fclose(byte x) {
|
||||
? jsr _fclose
|
||||
? jmp set_errno_on_carry_return_nothing
|
||||
}
|
||||
|
||||
asm byte _fgetc(byte x) @lkf_jumptab + $000a extern
|
||||
asm byte fgetc(byte x) {
|
||||
? jsr _fgetc
|
||||
? jmp set_errno_on_carry_return_a
|
||||
}
|
||||
|
||||
asm void _fputc(byte x, byte a) @lkf_jumptab + $000c extern
|
||||
inline asm void fputc_blocking(byte x, byte a) {
|
||||
? sec
|
||||
? jmp _fputc
|
||||
}
|
||||
inline asm void fputc_nonblocking(byte x, byte a) {
|
||||
? clc
|
||||
? jmp _fputc
|
||||
}
|
||||
|
||||
asm void _fcmd(word ay, byte x) @lkf_jumptab + $000e extern
|
||||
asm byte fcmd(word ay, byte x) {
|
||||
? jsr _fcmd
|
||||
? jmp set_errno_on_carry_return_x
|
||||
}
|
||||
asm void _freaddir(word ay, byte x) @lkf_jumptab + $0010 extern
|
||||
asm byte freaddir(word ay, byte x) {
|
||||
? sec
|
||||
? jsr _freaddir
|
||||
? jmp set_errno_on_carry_return_a
|
||||
}
|
||||
|
||||
asm word _fgetdevice(byte x) @lkf_jumptab + $0012 extern
|
||||
asm word fgetdevice(byte x) {
|
||||
? jsr _fgetdevice
|
||||
? txa
|
||||
? pha
|
||||
? tya
|
||||
? tax
|
||||
? pla
|
||||
? rts
|
||||
}
|
||||
asm void _strout(byte x) @lkf_jumptab + $0014 extern
|
||||
macro asm void strout(word const string, byte x) {
|
||||
? jsr _strout
|
||||
bit string
|
||||
? jsr set_errno_on_carry_return_nothing
|
||||
}
|
||||
asm void popen() @lkf_jumptab + $0016 extern // TODO
|
||||
asm void ufd_open() @lkf_jumptab + $0018 extern // TODO
|
||||
asm void _fdup(byte x) @lkf_jumptab + $001a extern
|
||||
inline asm byte fdup(byte x) {
|
||||
? jsr _fdup
|
||||
? txa
|
||||
? rts
|
||||
}
|
||||
asm void print_error() @lkf_jumptab + $001c extern // TODO
|
||||
|
||||
asm void suicerrout(byte a) @lkf_jumptab + $001e extern
|
||||
asm void suicide(byte a) @lkf_jumptab + $0020 extern
|
||||
|
||||
asm void _palloc(byte a) @lkf_jumptab + $0022 extern
|
||||
asm byte palloc(byte a) {
|
||||
? jsr _palloc
|
||||
? jmp set_errno_on_carry_return_x
|
||||
}
|
||||
asm void free(byte x) @lkf_jumptab + $0024 extern
|
||||
asm void force_taskswitch() @lkf_jumptab + $0026 extern
|
||||
|
||||
asm void forkto() @lkf_jumptab + $0028 extern // TODO
|
||||
asm void getipid() @lkf_jumptab + $002a extern // TODO
|
||||
asm void signal() @lkf_jumptab + $002c extern // TODO
|
||||
asm void sendsignal() @lkf_jumptab + $002e extern // TODO
|
||||
asm void wait() @lkf_jumptab + $0030 extern // TODO
|
||||
|
||||
asm void sleep(word xy) @lkf_jumptab + $0032 extern
|
||||
asm void _lock(byte x) @lkf_jumptab + $0034 extern
|
||||
inline asm void lock(byte x) {
|
||||
? clc
|
||||
? jmp _lock
|
||||
}
|
||||
asm void unlock(byte x) @lkf_jumptab + $0036 extern
|
||||
asm void suspend(word ax) @lkf_jumptab + $0038 extern
|
||||
|
||||
asm void hook_alert() @lkf_jumptab + $003a extern // TODO
|
||||
asm void hook_irq() @lkf_jumptab + $003c extern // TODO
|
||||
asm void hook_nmi() @lkf_jumptab + $003e extern // TODO
|
||||
|
||||
asm void panic() @lkf_jumptab + $0040 extern
|
||||
asm void locktsw() @lkf_jumptab + $0042 extern
|
||||
asm void unlocktsw() @lkf_jumptab + $0044 extern
|
||||
|
||||
asm void add_module() @lkf_jumptab + $0046 extern // TODO
|
||||
asm void fix_module() @lkf_jumptab + $0048 extern // TODO
|
||||
asm void mpalloc() @lkf_jumptab + $004a extern // TODO
|
||||
asm void spalloc() @lkf_jumptab + $004c extern // TODO
|
||||
asm void pfree() @lkf_jumptab + $004e extern // TODO
|
||||
asm void mun_block() @lkf_jumptab + $0050 extern // TODO
|
||||
asm void catcherr() @lkf_jumptab + $0052 extern // TODO
|
||||
|
||||
asm void printk(byte a) @lkf_jumptab + $0054 extern
|
||||
asm void putchar(byte a) @lkf_jumptab + $0054 extern // alias
|
||||
asm void hexout(byte a) @lkf_jumptab + $0056 extern
|
||||
asm void disable_nmi() @lkf_jumptab + $0058 extern
|
||||
asm void enable_nmi() @lkf_jumptab + $005a extern
|
||||
|
||||
asm void get_bitadr() @lkf_jumptab + $005c extern // TODO
|
||||
asm void addtask() @lkf_jumptab + $005e extern // TODO
|
||||
asm void get_smbptr() @lkf_jumptab + $0060 extern // TODO
|
||||
asm void smb_alloc() @lkf_jumptab + $0062 extern // TODO
|
||||
asm void smb_free() @lkf_jumptab + $0064 extern // TODO
|
||||
asm void alloc_pfd() @lkf_jumptab + $0066 extern // TODO
|
||||
asm void io_return() @lkf_jumptab + $0068 extern // TODO
|
||||
asm void io_return_error() @lkf_jumptab + $006a extern // TODO
|
||||
asm void ref_increment() @lkf_jumptab + $006c extern // TODO
|
||||
asm void p_insert() @lkf_jumptab + $006e extern // TODO
|
||||
asm void p_remove() @lkf_jumptab + $0070 extern // TODO
|
||||
asm void _raw_alloc() @lkf_jumptab + $0072 extern // TODO
|
||||
asm void exe_reloc() @lkf_jumptab + $0074 extern // TODO
|
||||
asm void exe_test() @lkf_jumptab + $0076 extern // TODO
|
||||
asm void init() @lkf_jumptab + $0078 extern // TODO
|
||||
asm void keyb_joy0() @lkf_jumptab + $007a extern // TODO
|
||||
asm void keyb_joy1() @lkf_jumptab + $007c extern // TODO
|
||||
asm void keyb_scan() @lkf_jumptab + $007e extern // TODO
|
||||
asm void keyb_stat() @lkf_jumptab + $0080 extern // TODO
|
||||
|
||||
asm byte random() @lkf_jumptab + $0082 extern
|
||||
asm void srandom(word ay) @lkf_jumptab + $0084 extern
|
||||
|
||||
asm void getenv() @lkf_jumptab + $0086 extern // TODO
|
||||
asm void setenv() @lkf_jumptab + $0088 extern // TODO
|
||||
asm void loado65() @lkf_jumptab + $008a extern // TODO
|
||||
|
||||
asm byte __start() @$1006 {
|
||||
? lda argv
|
||||
? sta argc
|
||||
? lda #0
|
||||
? sta argv
|
||||
lda #__zeropage_usage // TODO
|
||||
jsr set_zpsize
|
||||
jsr main
|
||||
jsr suicide
|
||||
}
|
||||
|
@ -104,6 +104,9 @@ object Cpu extends Enumeration {
|
||||
case "6502" => Mos
|
||||
case "6510" => Mos
|
||||
case "strict" => StrictMos
|
||||
case "strictnmos" => StrictMos
|
||||
case "strict6502" => StrictMos
|
||||
case "strict6510" => StrictMos
|
||||
case "cmos" => Cmos
|
||||
case "65sc02" => Cmos
|
||||
case "sc02" => Cmos
|
||||
@ -135,7 +138,7 @@ object CompilationFlag extends Enumeration {
|
||||
// optimization options:
|
||||
DangerousOptimizations, InlineFunctions, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed,
|
||||
// memory allocation options
|
||||
VariableOverlap, CompactReturnDispatchParams,
|
||||
VariableOverlap, CompactReturnDispatchParams, LUnixRelocatableCode,
|
||||
// runtime check options
|
||||
CheckIndexOutOfBounds,
|
||||
// special options
|
||||
@ -150,6 +153,7 @@ object CompilationFlag extends Enumeration {
|
||||
val allWarnings: Set[CompilationFlag.Value] = Set(ExtraComparisonWarnings)
|
||||
|
||||
val fromString = Map(
|
||||
"lunix" -> LUnixRelocatableCode,
|
||||
"emit_illegals" -> EmitIllegals,
|
||||
"emit_cmos" -> EmitCmosOpcodes,
|
||||
"emit_65ce02" -> Emit65CE02Opcodes,
|
||||
|
@ -13,7 +13,7 @@ import org.apache.commons.configuration2.INIConfiguration
|
||||
*/
|
||||
|
||||
object OutputStyle extends Enumeration {
|
||||
val Single, PerBank = Value
|
||||
val Single, PerBank, LUnix = Value
|
||||
}
|
||||
|
||||
class Platform(
|
||||
@ -135,7 +135,7 @@ object Platform {
|
||||
|
||||
val freePointers = as.get(classOf[String], "zp_pointers", "all") match {
|
||||
case "all" => List.tabulate(128)(_ * 2)
|
||||
case xs => xs.split("[, ]+").map(parseNumber).toList
|
||||
case xs => xs.split("[, ]+").flatMap(parseNumberOrRange).toList
|
||||
}
|
||||
|
||||
val codeAllocators = banks.map(b => b -> new UpwardByteAllocator(bankStarts(b), bankCodeEnds(b)))
|
||||
@ -148,7 +148,9 @@ object Platform {
|
||||
val os = conf.getSection("output")
|
||||
val outputPackager = SequenceOutput(os.get(classOf[String], "format", "").split("[, ]+").filter(_.nonEmpty).map {
|
||||
case "startaddr" => StartAddressOutput
|
||||
case "startpage" => StartPageOutput
|
||||
case "endaddr" => EndAddressOutput
|
||||
case "pagecount" => PageCountOutput
|
||||
case "allocated" => AllocatedDataOutput
|
||||
case n => n.split(":").filter(_.nonEmpty) match {
|
||||
case Array(b, s, e) => BankFragmentOutput(b, parseNumber(s), parseNumber(e))
|
||||
@ -162,6 +164,7 @@ object Platform {
|
||||
val outputStyle = os.get(classOf[String], "style", "single") match {
|
||||
case "" | "single" => OutputStyle.Single
|
||||
case "per_bank" | "per_segment" => OutputStyle.PerBank
|
||||
case "lunix" => OutputStyle.LUnix
|
||||
case x => ErrorReporting.fatal(s"Invalid output style: `$x`")
|
||||
}
|
||||
|
||||
@ -175,6 +178,18 @@ object Platform {
|
||||
outputStyle)
|
||||
}
|
||||
|
||||
def parseNumberOrRange(s:String): Seq[Int] = {
|
||||
if (s.contains("-")) {
|
||||
var segments = s.split("-")
|
||||
if (segments.length != 2) {
|
||||
ErrorReporting.fatal(s"Invalid range: `$s`")
|
||||
}
|
||||
Range(parseNumber(segments(0)), parseNumber(segments(1)), 2)
|
||||
} else {
|
||||
Seq(parseNumber(s))
|
||||
}
|
||||
}
|
||||
|
||||
def parseNumber(s: String): Int = {
|
||||
if (s.startsWith("$")) {
|
||||
Integer.parseInt(s.substring(1), 16)
|
||||
|
16
src/main/scala/millfork/env/Constant.scala
vendored
16
src/main/scala/millfork/env/Constant.scala
vendored
@ -70,7 +70,7 @@ sealed trait Constant {
|
||||
|
||||
def quickSimplify: Constant = this
|
||||
|
||||
def isRelatedTo(v: Variable): Boolean
|
||||
def isRelatedTo(v: Thing): Boolean
|
||||
}
|
||||
|
||||
case class AssertByte(c: Constant) extends Constant {
|
||||
@ -78,13 +78,15 @@ case class AssertByte(c: Constant) extends Constant {
|
||||
|
||||
override def requiredSize: Int = 1
|
||||
|
||||
override def isRelatedTo(v: Variable): Boolean = c.isRelatedTo(v)
|
||||
override def isRelatedTo(v: Thing): Boolean = c.isRelatedTo(v)
|
||||
|
||||
override def quickSimplify: Constant = AssertByte(c.quickSimplify)
|
||||
}
|
||||
|
||||
case class UnexpandedConstant(name: String, requiredSize: Int) extends Constant {
|
||||
override def isRelatedTo(v: Variable): Boolean = false
|
||||
override def isRelatedTo(v: Thing): Boolean = false
|
||||
|
||||
override def toString: String = name
|
||||
}
|
||||
|
||||
case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
|
||||
@ -111,7 +113,7 @@ case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
|
||||
|
||||
override def toString: String = if (value > 9) value.formatted("$%X") else value.toString
|
||||
|
||||
override def isRelatedTo(v: Variable): Boolean = false
|
||||
override def isRelatedTo(v: Thing): Boolean = false
|
||||
}
|
||||
|
||||
case class MemoryAddressConstant(var thing: ThingInMemory) extends Constant {
|
||||
@ -119,7 +121,7 @@ case class MemoryAddressConstant(var thing: ThingInMemory) extends Constant {
|
||||
|
||||
override def toString: String = thing.name
|
||||
|
||||
override def isRelatedTo(v: Variable): Boolean = thing.name == v.name
|
||||
override def isRelatedTo(v: Thing): Boolean = thing.name == v.name
|
||||
}
|
||||
|
||||
case class SubbyteConstant(base: Constant, index: Int) extends Constant {
|
||||
@ -144,7 +146,7 @@ case class SubbyteConstant(base: Constant, index: Int) extends Constant {
|
||||
case 3 => ".b3"
|
||||
})
|
||||
|
||||
override def isRelatedTo(v: Variable): Boolean = base.isRelatedTo(v)
|
||||
override def isRelatedTo(v: Thing): Boolean = base.isRelatedTo(v)
|
||||
}
|
||||
|
||||
object MathOperator extends Enumeration {
|
||||
@ -304,5 +306,5 @@ case class CompoundConstant(operator: MathOperator.Value, lhs: Constant, rhs: Co
|
||||
|
||||
override def requiredSize: Int = lhs.requiredSize max rhs.requiredSize
|
||||
|
||||
override def isRelatedTo(v: Variable): Boolean = lhs.isRelatedTo(v) || rhs.isRelatedTo(v)
|
||||
override def isRelatedTo(v: Thing): Boolean = lhs.isRelatedTo(v) || rhs.isRelatedTo(v)
|
||||
}
|
101
src/main/scala/millfork/env/Environment.scala
vendored
101
src/main/scala/millfork/env/Environment.scala
vendored
@ -100,7 +100,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
}
|
||||
}.toSet
|
||||
val toAdd = things.values.flatMap {
|
||||
case m: UninitializedMemory if nf.isDefined == isLocalVariableName(m.name) =>
|
||||
case m: UninitializedMemory if nf.isDefined == isLocalVariableName(m.name) && !m.name.endsWith(".addr") && maybeGet[Thing](m.name + ".array").isEmpty =>
|
||||
val vertex = if (options.flag(CompilationFlag.VariableOverlap)) {
|
||||
nf.fold[VariableVertex](GlobalVertex) { f =>
|
||||
if (m.alloc == VariableAllocationMethod.Static) {
|
||||
@ -240,20 +240,23 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
if (parent.isEmpty) {
|
||||
addThing(VoidType, None)
|
||||
addThing(BuiltInBooleanType, None)
|
||||
addThing(BasicPlainType("byte", 1), None)
|
||||
addThing(BasicPlainType("word", 2), None)
|
||||
val b = BasicPlainType("byte", 1)
|
||||
val w = BasicPlainType("word", 2)
|
||||
addThing(b, None)
|
||||
addThing(w, None)
|
||||
addThing(BasicPlainType("farword", 3), None)
|
||||
addThing(BasicPlainType("long", 4), None)
|
||||
addThing(DerivedPlainType("pointer", get[PlainType]("word"), isSigned = false), None)
|
||||
// addThing(DerivedPlainType("farpointer", get[PlainType]("farword"), isSigned = false), None)
|
||||
addThing(DerivedPlainType("ubyte", get[PlainType]("byte"), isSigned = false), None)
|
||||
addThing(DerivedPlainType("sbyte", get[PlainType]("byte"), isSigned = true), None)
|
||||
addThing(DerivedPlainType("pointer", w, isSigned = false), None)
|
||||
// addThing(DerivedPlainType("farpointer", get[PlainType]("farword"), isSigned = false), None)
|
||||
addThing(DerivedPlainType("ubyte", b, isSigned = false), None)
|
||||
addThing(DerivedPlainType("sbyte", b, isSigned = true), None)
|
||||
val trueType = ConstantBooleanType("true$", value = true)
|
||||
val falseType = ConstantBooleanType("false$", value = false)
|
||||
addThing(trueType, None)
|
||||
addThing(falseType, None)
|
||||
addThing(ConstantThing("true", NumericConstant(0, 0), trueType), None)
|
||||
addThing(ConstantThing("false", NumericConstant(0, 0), falseType), None)
|
||||
addThing(ConstantThing("__zeropage_usage", UnexpandedConstant("__zeropage_usage", 1), b), None)
|
||||
addThing(FlagBooleanType("set_carry", Opcode.BCS, Opcode.BCC), None)
|
||||
addThing(FlagBooleanType("clear_carry", Opcode.BCC, Opcode.BCS), None)
|
||||
addThing(FlagBooleanType("set_overflow", Opcode.BVS, Opcode.BVC), None)
|
||||
@ -502,7 +505,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
}
|
||||
|
||||
val env = new Environment(Some(this), name + "$")
|
||||
stmt.params.foreach(p => env.registerParameter(p))
|
||||
stmt.params.foreach(p => env.registerParameter(p, options))
|
||||
val params = if (stmt.assembly) {
|
||||
AssemblyParamSignature(stmt.params.map {
|
||||
pd =>
|
||||
@ -539,7 +542,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
stmt.bank
|
||||
)
|
||||
addThing(mangled, stmt.position)
|
||||
registerAddressConstant(mangled, stmt.position)
|
||||
registerAddressConstant(mangled, stmt.position, options)
|
||||
addThing(ConstantThing(name + '`', addr, w), stmt.position)
|
||||
}
|
||||
|
||||
@ -585,19 +588,29 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
declaredBank = stmt.bank
|
||||
)
|
||||
addThing(mangled, stmt.position)
|
||||
registerAddressConstant(mangled, stmt.position)
|
||||
registerAddressConstant(mangled, stmt.position, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def registerAddressConstant(thing: ThingInMemory, position: Option[Position]): Unit = {
|
||||
val addr = thing.toAddress
|
||||
addThing(ConstantThing(thing.name + ".addr", addr, get[Type]("pointer")), position)
|
||||
addThing(ConstantThing(thing.name + ".addr.hi", addr.hiByte, get[Type]("byte")), position)
|
||||
addThing(ConstantThing(thing.name + ".addr.lo", addr.loByte, get[Type]("byte")), position)
|
||||
private def registerAddressConstant(thing: ThingInMemory, position: Option[Position], options: CompilationOptions): Unit = {
|
||||
if (!thing.zeropage && options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
val b = get[Type]("byte")
|
||||
val w = get[Type]("word")
|
||||
val relocatable = UninitializedMemoryVariable(thing.name + ".addr", w, VariableAllocationMethod.Static, None)
|
||||
val addr = relocatable.toAddress
|
||||
addThing(relocatable, position)
|
||||
addThing(RelativeVariable(thing.name + ".addr.hi", addr + 1, b, zeropage = false, None), position)
|
||||
addThing(RelativeVariable(thing.name + ".addr.lo", addr, b, zeropage = false, None), position)
|
||||
} else {
|
||||
val addr = thing.toAddress
|
||||
addThing(ConstantThing(thing.name + ".addr", addr, get[Type]("pointer")), position)
|
||||
addThing(ConstantThing(thing.name + ".addr.hi", addr.hiByte, get[Type]("byte")), position)
|
||||
addThing(ConstantThing(thing.name + ".addr.lo", addr.loByte, get[Type]("byte")), position)
|
||||
}
|
||||
}
|
||||
|
||||
def registerParameter(stmt: ParameterDeclaration): Unit = {
|
||||
def registerParameter(stmt: ParameterDeclaration, options: CompilationOptions): Unit = {
|
||||
val typ = get[Type](stmt.typ)
|
||||
val b = get[Type]("byte")
|
||||
val w = get[Type]("word")
|
||||
@ -607,7 +620,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
val zp = typ.name == "pointer" // TODO
|
||||
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto, None)
|
||||
addThing(v, stmt.position)
|
||||
registerAddressConstant(v, stmt.position)
|
||||
registerAddressConstant(v, stmt.position, options)
|
||||
val addr = v.toAddress
|
||||
typ.size match {
|
||||
case 2 =>
|
||||
@ -674,7 +687,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
}
|
||||
}
|
||||
|
||||
def registerArray(stmt: ArrayDeclarationStatement): Unit = {
|
||||
def registerArray(stmt: ArrayDeclarationStatement, options: CompilationOptions): Unit = {
|
||||
val b = get[Type]("byte")
|
||||
val p = get[Type]("pointer")
|
||||
stmt.elements match {
|
||||
@ -694,18 +707,30 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
declaredBank = stmt.bank)
|
||||
}
|
||||
addThing(array, stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank), stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank), stmt.position, options)
|
||||
val a = address match {
|
||||
case None => array.toAddress
|
||||
case Some(aa) => aa
|
||||
}
|
||||
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
|
||||
declaredBank = stmt.bank), stmt.position)
|
||||
addThing(ConstantThing(stmt.name, a, p), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
val b = get[Type]("byte")
|
||||
val w = get[Type]("word")
|
||||
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None)
|
||||
val addr = relocatable.toAddress
|
||||
addThing(relocatable, stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".addr.hi", addr + 1, b, zeropage = false, None), stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".addr.lo", addr, b, zeropage = false, None), stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None), stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".array.lo", addr, b, zeropage = false, None), stmt.position)
|
||||
} else {
|
||||
addThing(ConstantThing(stmt.name, a, p), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
}
|
||||
if (length < 256) {
|
||||
addThing(ConstantThing(stmt.name + ".length", lengthConst, b), stmt.position)
|
||||
}
|
||||
@ -730,18 +755,28 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank)
|
||||
addThing(array, stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None,
|
||||
declaredBank = stmt.bank), stmt.position)
|
||||
declaredBank = stmt.bank), stmt.position, options)
|
||||
val a = address match {
|
||||
case None => array.toAddress
|
||||
case Some(aa) => aa
|
||||
}
|
||||
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
|
||||
declaredBank = stmt.bank), stmt.position)
|
||||
addThing(ConstantThing(stmt.name, a, p), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
val b = get[Type]("byte")
|
||||
val w = get[Type]("word")
|
||||
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None)
|
||||
val addr = relocatable.toAddress
|
||||
addThing(relocatable, stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None), stmt.position)
|
||||
addThing(RelativeVariable(stmt.name + ".array.lo", addr, b, zeropage = false, None), stmt.position)
|
||||
} else {
|
||||
addThing(ConstantThing(stmt.name, a, p), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.quickSimplify, b), stmt.position)
|
||||
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||
}
|
||||
if (length < 256) {
|
||||
addThing(ConstantThing(stmt.name + ".length", NumericConstant(length, 1), b), stmt.position)
|
||||
}
|
||||
@ -846,7 +881,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
}
|
||||
InitializedMemoryVariable(name, None, typ, ive, declaredBank = stmt.bank)
|
||||
}
|
||||
registerAddressConstant(v, stmt.position)
|
||||
registerAddressConstant(v, stmt.position, options)
|
||||
(v, v.toAddress)
|
||||
})(a => {
|
||||
val addr = eval(a).getOrElse(Constant.error(s"Address of `$name` has a non-constant value", position))
|
||||
@ -856,7 +891,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
}
|
||||
val v = RelativeVariable(prefix + name, addr, typ, zeropage = zp,
|
||||
declaredBank = stmt.bank)
|
||||
registerAddressConstant(v, stmt.position)
|
||||
registerAddressConstant(v, stmt.position, options)
|
||||
(v, addr)
|
||||
})
|
||||
addThing(v, stmt.position)
|
||||
@ -927,7 +962,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
program.declarations.foreach {
|
||||
case f: FunctionDeclarationStatement => registerFunction(f, options)
|
||||
case v: VariableDeclarationStatement => registerVariable(v, options)
|
||||
case a: ArrayDeclarationStatement => registerArray(a)
|
||||
case a: ArrayDeclarationStatement => registerArray(a, options)
|
||||
case i: ImportStatement => ()
|
||||
}
|
||||
if (options.flag(CompilationFlag.ZeropagePseudoregister) && !things.contains("__reg")) {
|
||||
|
15
src/main/scala/millfork/env/Thing.scala
vendored
15
src/main/scala/millfork/env/Thing.scala
vendored
@ -78,6 +78,8 @@ sealed trait TypedThing extends Thing {
|
||||
|
||||
|
||||
sealed trait ThingInMemory extends Thing {
|
||||
def zeropage: Boolean
|
||||
|
||||
def toAddress: Constant
|
||||
|
||||
var farFlag: Option[Boolean] = None
|
||||
@ -105,12 +107,13 @@ case class Label(name: String) extends ThingInMemory {
|
||||
declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
|
||||
|
||||
override val declaredBank: Option[String] = None
|
||||
|
||||
override def zeropage: Boolean = false
|
||||
}
|
||||
|
||||
sealed trait Variable extends TypedThing with VariableLikeThing
|
||||
|
||||
sealed trait VariableInMemory extends Variable with ThingInMemory with IndexableThing {
|
||||
def zeropage: Boolean
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean =
|
||||
!zeropage && farFlag.getOrElse(false)
|
||||
@ -175,6 +178,8 @@ case class UninitializedArray(name: String, sizeInBytes: Int, declaredBank: Opti
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||
|
||||
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse("default")
|
||||
|
||||
override def zeropage: Boolean = false
|
||||
}
|
||||
|
||||
case class RelativeArray(name: String, address: Constant, sizeInBytes: Int, declaredBank: Option[String]) extends MfArray {
|
||||
@ -183,6 +188,8 @@ case class RelativeArray(name: String, address: Constant, sizeInBytes: Int, decl
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||
|
||||
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse("default")
|
||||
|
||||
override def zeropage: Boolean = false
|
||||
}
|
||||
|
||||
case class InitializedArray(name: String, address: Option[Constant], contents: List[Expression], declaredBank: Option[String]) extends MfArray with PreallocableThing {
|
||||
@ -191,6 +198,8 @@ case class InitializedArray(name: String, address: Option[Constant], contents: L
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||
|
||||
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
|
||||
|
||||
override def zeropage: Boolean = false
|
||||
}
|
||||
|
||||
case class RelativeVariable(name: String, address: Constant, typ: Type, zeropage: Boolean, declaredBank: Option[String]) extends VariableInMemory {
|
||||
@ -242,6 +251,8 @@ case class ExternFunction(name: String,
|
||||
override def toAddress: Constant = address
|
||||
|
||||
override def interrupt = false
|
||||
|
||||
override def zeropage: Boolean = false
|
||||
}
|
||||
|
||||
case class NormalFunction(name: String,
|
||||
@ -257,6 +268,8 @@ case class NormalFunction(name: String,
|
||||
position: Option[Position],
|
||||
declaredBank: Option[String]) extends FunctionInMemory with PreallocableThing {
|
||||
override def shouldGenerate = true
|
||||
|
||||
override def zeropage: Boolean = false
|
||||
}
|
||||
|
||||
case class ConstantThing(name: String, value: Constant, typ: Type) extends TypedThing with VariableLikeThing with IndexableThing {
|
||||
|
@ -78,7 +78,7 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
|
||||
try {
|
||||
if (labelMap.contains(th.name)) return labelMap(th.name)
|
||||
if (labelMap.contains(th.name + "`")) return labelMap(th.name)
|
||||
if (labelMap.contains(th.name + ".addr")) return labelMap(th.name)
|
||||
if (labelMap.contains(th.name + ".addr")) return labelMap.getOrElse[Int](th.name, labelMap(th.name + ".array"))
|
||||
val x1 = env.maybeGet[ConstantThing](th.name).map(_.value)
|
||||
val x2 = env.maybeGet[ConstantThing](th.name + "`").map(_.value)
|
||||
val x3 = env.maybeGet[NormalFunction](th.name).flatMap(_.address)
|
||||
@ -97,6 +97,9 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
|
||||
case e: StackOverflowError =>
|
||||
ErrorReporting.fatal("Stack overflow " + c)
|
||||
}
|
||||
case UnexpandedConstant(name, _) =>
|
||||
if (labelMap.contains(name)) labelMap(name)
|
||||
else ???
|
||||
case SubbyteConstant(cc, i) => deepConstResolve(cc).>>>(i * 8).&(0xff)
|
||||
case CompoundConstant(operator, lc, rc) =>
|
||||
val l = deepConstResolve(lc)
|
||||
@ -282,6 +285,42 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
|
||||
env.allThings.things.foreach {
|
||||
case (_, m@UninitializedMemoryVariable(name, typ, _, _)) if name.endsWith(".addr") || env.maybeGet[Thing](name + ".array").isDefined =>
|
||||
val isUsed = compiledFunctions.values.exists(_.exists(_.parameter.isRelatedTo(m)))
|
||||
// println(m.name -> isUsed)
|
||||
if (isUsed) {
|
||||
val bank = m.bank(options)
|
||||
if (bank != "default") ???
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size + 1, initialized = true, writeable = false)
|
||||
labelMap(name) = index + 1
|
||||
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||
val thing = if (name.endsWith(".addr")) env.get[ThingInMemory](name.stripSuffix(".addr")) else env.get[ThingInMemory](name + ".array")
|
||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(" !byte $2c")
|
||||
assembly.append(name)
|
||||
val c = thing.toAddress
|
||||
writeByte(bank, index, 0x2c.toByte) // BIT abs
|
||||
index += 1
|
||||
for (i <- 0 until typ.size) {
|
||||
writeByte(bank, index, c.subbyte(i))
|
||||
assembly.append(" !byte " + c.subbyte(i).quickSimplify)
|
||||
index += 1
|
||||
}
|
||||
initializedVariablesSize += typ.size
|
||||
justAfterCode += bank -> index
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
val index = codeAllocators("default").allocateBytes(mem.banks("default"), options, 1, initialized = true, writeable = false)
|
||||
writeByte("default", index, 2.toByte) // BIT abs
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(" !byte 2 ;; end of LUnix relocatable segment")
|
||||
justAfterCode += "default" -> (index + 1)
|
||||
}
|
||||
env.allPreallocatables.foreach {
|
||||
case thing@InitializedArray(name, None, items, _) =>
|
||||
val bank = thing.bank(options)
|
||||
@ -305,7 +344,7 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
|
||||
}
|
||||
initializedVariablesSize += items.length
|
||||
justAfterCode += bank -> index
|
||||
case m@InitializedMemoryVariable(name, None, typ, value, _) =>
|
||||
case m@InitializedMemoryVariable(name, None, typ, value, _) =>
|
||||
val bank = m.bank(options)
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size, initialized = true, writeable = true)
|
||||
@ -335,9 +374,15 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
|
||||
for(i <- 0 until size) bank0.occupied(addr + i) = true
|
||||
}
|
||||
val variableAllocators = platform.variableAllocators
|
||||
variableAllocators.foreach{case (b,a) => a.notifyAboutEndOfCode(justAfterCode(b))}
|
||||
variableAllocators.foreach { case (b, a) => a.notifyAboutEndOfCode(justAfterCode(b)) }
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put)
|
||||
|
||||
val zeropageOccupation = mem.banks("default").occupied.slice(variableAllocators("default").pointers.head, variableAllocators("default").pointers.last + 2)
|
||||
labelMap += "__zeropage_usage" -> (zeropageOccupation.lastIndexOf(true) - zeropageOccupation.indexOf(true) + 1)
|
||||
labelMap += "__zeropage_first" -> (zeropageOccupation.indexOf(true) max 0)
|
||||
labelMap += "__zeropage_last" -> (zeropageOccupation.lastIndexOf(true) max 0)
|
||||
labelMap += "__zeropage_end" -> (zeropageOccupation.lastIndexOf(true) + 1)
|
||||
|
||||
env = rootEnv.allThings
|
||||
|
||||
for ((bank, addr, b) <- bytesToWriteLater) {
|
||||
@ -367,7 +412,7 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
|
||||
|
||||
// TODO:
|
||||
val code = (platform.outputStyle match {
|
||||
case OutputStyle.Single => List("default")
|
||||
case OutputStyle.Single | OutputStyle.LUnix => List("default")
|
||||
case OutputStyle.PerBank => platform.bankNumbers.keys.toList
|
||||
}).map(b => b -> platform.outputPackager.packageOutput(mem, b)).toMap
|
||||
AssemblerOutput(code, assembly.toArray, labelMap.toList)
|
||||
|
@ -45,6 +45,13 @@ object StartAddressOutput extends OutputPackager {
|
||||
}
|
||||
}
|
||||
|
||||
object StartPageOutput extends OutputPackager {
|
||||
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
|
||||
val b = mem.banks(bank)
|
||||
Array(b.start.>>(8).toByte)
|
||||
}
|
||||
}
|
||||
|
||||
object EndAddressOutput extends OutputPackager {
|
||||
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
|
||||
val b = mem.banks(bank)
|
||||
@ -52,6 +59,14 @@ object EndAddressOutput extends OutputPackager {
|
||||
}
|
||||
}
|
||||
|
||||
object PageCountOutput extends OutputPackager {
|
||||
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
|
||||
val e = mem.banks(bank).end.>>(8)
|
||||
val s = mem.banks(bank).start.>>(8)
|
||||
Array((e - s + 1).toByte)
|
||||
}
|
||||
}
|
||||
|
||||
object AllocatedDataOutput extends OutputPackager {
|
||||
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
|
||||
val b = mem.banks(bank)
|
||||
|
@ -50,7 +50,7 @@ class AfterCodeByteAllocator(val endBefore: Int) extends ByteAllocator {
|
||||
def notifyAboutEndOfCode(org: Int): Unit = startAt = org
|
||||
}
|
||||
|
||||
class VariableAllocator(private val pointers: List[Int], private val bytes: ByteAllocator) {
|
||||
class VariableAllocator(val pointers: List[Int], private val bytes: ByteAllocator) {
|
||||
|
||||
private val pointerMap = mutable.Map[Int, Set[VariableVertex]]()
|
||||
private val variableMap = mutable.Map[Int, mutable.Map[Int, Set[VariableVertex]]]()
|
||||
|
Loading…
x
Reference in New Issue
Block a user