1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-29 04:41:30 +00:00

Preliminary LUnix support

This commit is contained in:
Karol Stasiak 2018-06-09 00:18:21 +02:00
parent 5dc1bba8ed
commit 8931ced6fc
14 changed files with 444 additions and 51 deletions

3
.gitignore vendored
View File

@ -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

View File

@ -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).

View File

@ -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)

View File

@ -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
View 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
View 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
}

View File

@ -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,

View File

@ -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)

View File

@ -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)
}

View File

@ -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")) {

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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]]]()