mirror of
https://github.com/KarolS/millfork.git
synced 2025-02-13 16:30:30 +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
|
stuff
|
||||||
releases
|
releases
|
||||||
src/test/scala/experiments/
|
src/test/scala/experiments/
|
||||||
|
# doesn't work yet
|
||||||
|
examples/lunix/
|
||||||
|
|
||||||
# hidden files
|
# hidden files
|
||||||
*.~
|
*.~
|
||||||
@ -18,6 +20,7 @@ src/test/scala/experiments/
|
|||||||
|
|
||||||
# compiled Millfork files
|
# compiled Millfork files
|
||||||
*.prg
|
*.prg
|
||||||
|
*.seq
|
||||||
*.asm
|
*.asm
|
||||||
*.lbl
|
*.lbl
|
||||||
*.xex
|
*.xex
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Current version
|
## 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).
|
* 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 128
|
||||||
|
|
||||||
|
* Commodore 64/128 running LUnix/LNG 0.21 (experimental)
|
||||||
|
|
||||||
* Commodore PET
|
* Commodore PET
|
||||||
|
|
||||||
* Commodore Vic-20 (stock or with RAM extensions)
|
* 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)
|
* `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
|
* `c16` – Commodore 16
|
||||||
|
|
||||||
* `plus4` – Commodore Plus/4
|
* `plus4` – Commodore Plus/4
|
||||||
@ -123,6 +125,8 @@ Every platform is defined in an `.ini` file with an appropriate name.
|
|||||||
* `compact_dispatch_params` – whether parameter values in return dispatch statements may overlap other objects, default is `true`
|
* `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
|
#### `[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)
|
* `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
|
* `per_segment` – generate a separate file with each segment
|
||||||
|
|
||||||
* `format` – output file format; a comma-separated list of tokens:
|
* `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)
|
* `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)
|
* `endaddr` – little-endian 16-bit address of the last used byte of the compiled output (usually not the segment end)
|
||||||
|
|
||||||
* `allocated` – all used bytes
|
* `allocated` – all used bytes
|
||||||
|
|
||||||
|
* `pagecount` – the number of pages used by all used bytes (including partially filled pages)
|
||||||
|
|
||||||
* `<addr>:<addr>` - inclusive range of bytes
|
* `<addr>:<addr>` - inclusive range of bytes
|
||||||
|
|
||||||
* `<segment>:<addr>:<addr>` - inclusive range of bytes in a given segment
|
* `<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 "6502" => Mos
|
||||||
case "6510" => Mos
|
case "6510" => Mos
|
||||||
case "strict" => StrictMos
|
case "strict" => StrictMos
|
||||||
|
case "strictnmos" => StrictMos
|
||||||
|
case "strict6502" => StrictMos
|
||||||
|
case "strict6510" => StrictMos
|
||||||
case "cmos" => Cmos
|
case "cmos" => Cmos
|
||||||
case "65sc02" => Cmos
|
case "65sc02" => Cmos
|
||||||
case "sc02" => Cmos
|
case "sc02" => Cmos
|
||||||
@ -135,7 +138,7 @@ object CompilationFlag extends Enumeration {
|
|||||||
// optimization options:
|
// optimization options:
|
||||||
DangerousOptimizations, InlineFunctions, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed,
|
DangerousOptimizations, InlineFunctions, OptimizeForSize, OptimizeForSpeed, OptimizeForSonicSpeed,
|
||||||
// memory allocation options
|
// memory allocation options
|
||||||
VariableOverlap, CompactReturnDispatchParams,
|
VariableOverlap, CompactReturnDispatchParams, LUnixRelocatableCode,
|
||||||
// runtime check options
|
// runtime check options
|
||||||
CheckIndexOutOfBounds,
|
CheckIndexOutOfBounds,
|
||||||
// special options
|
// special options
|
||||||
@ -150,6 +153,7 @@ object CompilationFlag extends Enumeration {
|
|||||||
val allWarnings: Set[CompilationFlag.Value] = Set(ExtraComparisonWarnings)
|
val allWarnings: Set[CompilationFlag.Value] = Set(ExtraComparisonWarnings)
|
||||||
|
|
||||||
val fromString = Map(
|
val fromString = Map(
|
||||||
|
"lunix" -> LUnixRelocatableCode,
|
||||||
"emit_illegals" -> EmitIllegals,
|
"emit_illegals" -> EmitIllegals,
|
||||||
"emit_cmos" -> EmitCmosOpcodes,
|
"emit_cmos" -> EmitCmosOpcodes,
|
||||||
"emit_65ce02" -> Emit65CE02Opcodes,
|
"emit_65ce02" -> Emit65CE02Opcodes,
|
||||||
|
@ -13,7 +13,7 @@ import org.apache.commons.configuration2.INIConfiguration
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
object OutputStyle extends Enumeration {
|
object OutputStyle extends Enumeration {
|
||||||
val Single, PerBank = Value
|
val Single, PerBank, LUnix = Value
|
||||||
}
|
}
|
||||||
|
|
||||||
class Platform(
|
class Platform(
|
||||||
@ -135,7 +135,7 @@ object Platform {
|
|||||||
|
|
||||||
val freePointers = as.get(classOf[String], "zp_pointers", "all") match {
|
val freePointers = as.get(classOf[String], "zp_pointers", "all") match {
|
||||||
case "all" => List.tabulate(128)(_ * 2)
|
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)))
|
val codeAllocators = banks.map(b => b -> new UpwardByteAllocator(bankStarts(b), bankCodeEnds(b)))
|
||||||
@ -148,7 +148,9 @@ object Platform {
|
|||||||
val os = conf.getSection("output")
|
val os = conf.getSection("output")
|
||||||
val outputPackager = SequenceOutput(os.get(classOf[String], "format", "").split("[, ]+").filter(_.nonEmpty).map {
|
val outputPackager = SequenceOutput(os.get(classOf[String], "format", "").split("[, ]+").filter(_.nonEmpty).map {
|
||||||
case "startaddr" => StartAddressOutput
|
case "startaddr" => StartAddressOutput
|
||||||
|
case "startpage" => StartPageOutput
|
||||||
case "endaddr" => EndAddressOutput
|
case "endaddr" => EndAddressOutput
|
||||||
|
case "pagecount" => PageCountOutput
|
||||||
case "allocated" => AllocatedDataOutput
|
case "allocated" => AllocatedDataOutput
|
||||||
case n => n.split(":").filter(_.nonEmpty) match {
|
case n => n.split(":").filter(_.nonEmpty) match {
|
||||||
case Array(b, s, e) => BankFragmentOutput(b, parseNumber(s), parseNumber(e))
|
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 {
|
val outputStyle = os.get(classOf[String], "style", "single") match {
|
||||||
case "" | "single" => OutputStyle.Single
|
case "" | "single" => OutputStyle.Single
|
||||||
case "per_bank" | "per_segment" => OutputStyle.PerBank
|
case "per_bank" | "per_segment" => OutputStyle.PerBank
|
||||||
|
case "lunix" => OutputStyle.LUnix
|
||||||
case x => ErrorReporting.fatal(s"Invalid output style: `$x`")
|
case x => ErrorReporting.fatal(s"Invalid output style: `$x`")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,6 +178,18 @@ object Platform {
|
|||||||
outputStyle)
|
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 = {
|
def parseNumber(s: String): Int = {
|
||||||
if (s.startsWith("$")) {
|
if (s.startsWith("$")) {
|
||||||
Integer.parseInt(s.substring(1), 16)
|
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 quickSimplify: Constant = this
|
||||||
|
|
||||||
def isRelatedTo(v: Variable): Boolean
|
def isRelatedTo(v: Thing): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
case class AssertByte(c: Constant) extends Constant {
|
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 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)
|
override def quickSimplify: Constant = AssertByte(c.quickSimplify)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class UnexpandedConstant(name: String, requiredSize: Int) extends Constant {
|
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 {
|
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 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 {
|
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 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 {
|
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"
|
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 {
|
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 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)
|
||||||
}
|
}
|
71
src/main/scala/millfork/env/Environment.scala
vendored
71
src/main/scala/millfork/env/Environment.scala
vendored
@ -100,7 +100,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
|||||||
}
|
}
|
||||||
}.toSet
|
}.toSet
|
||||||
val toAdd = things.values.flatMap {
|
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)) {
|
val vertex = if (options.flag(CompilationFlag.VariableOverlap)) {
|
||||||
nf.fold[VariableVertex](GlobalVertex) { f =>
|
nf.fold[VariableVertex](GlobalVertex) { f =>
|
||||||
if (m.alloc == VariableAllocationMethod.Static) {
|
if (m.alloc == VariableAllocationMethod.Static) {
|
||||||
@ -240,20 +240,23 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
|||||||
if (parent.isEmpty) {
|
if (parent.isEmpty) {
|
||||||
addThing(VoidType, None)
|
addThing(VoidType, None)
|
||||||
addThing(BuiltInBooleanType, None)
|
addThing(BuiltInBooleanType, None)
|
||||||
addThing(BasicPlainType("byte", 1), None)
|
val b = BasicPlainType("byte", 1)
|
||||||
addThing(BasicPlainType("word", 2), None)
|
val w = BasicPlainType("word", 2)
|
||||||
|
addThing(b, None)
|
||||||
|
addThing(w, None)
|
||||||
addThing(BasicPlainType("farword", 3), None)
|
addThing(BasicPlainType("farword", 3), None)
|
||||||
addThing(BasicPlainType("long", 4), None)
|
addThing(BasicPlainType("long", 4), None)
|
||||||
addThing(DerivedPlainType("pointer", get[PlainType]("word"), isSigned = false), None)
|
addThing(DerivedPlainType("pointer", w, isSigned = false), None)
|
||||||
// addThing(DerivedPlainType("farpointer", get[PlainType]("farword"), isSigned = false), None)
|
// addThing(DerivedPlainType("farpointer", get[PlainType]("farword"), isSigned = false), None)
|
||||||
addThing(DerivedPlainType("ubyte", get[PlainType]("byte"), isSigned = false), None)
|
addThing(DerivedPlainType("ubyte", b, isSigned = false), None)
|
||||||
addThing(DerivedPlainType("sbyte", get[PlainType]("byte"), isSigned = true), None)
|
addThing(DerivedPlainType("sbyte", b, isSigned = true), None)
|
||||||
val trueType = ConstantBooleanType("true$", value = true)
|
val trueType = ConstantBooleanType("true$", value = true)
|
||||||
val falseType = ConstantBooleanType("false$", value = false)
|
val falseType = ConstantBooleanType("false$", value = false)
|
||||||
addThing(trueType, None)
|
addThing(trueType, None)
|
||||||
addThing(falseType, None)
|
addThing(falseType, None)
|
||||||
addThing(ConstantThing("true", NumericConstant(0, 0), trueType), None)
|
addThing(ConstantThing("true", NumericConstant(0, 0), trueType), None)
|
||||||
addThing(ConstantThing("false", NumericConstant(0, 0), falseType), 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("set_carry", Opcode.BCS, Opcode.BCC), None)
|
||||||
addThing(FlagBooleanType("clear_carry", Opcode.BCC, Opcode.BCS), None)
|
addThing(FlagBooleanType("clear_carry", Opcode.BCC, Opcode.BCS), None)
|
||||||
addThing(FlagBooleanType("set_overflow", Opcode.BVS, Opcode.BVC), 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 + "$")
|
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) {
|
val params = if (stmt.assembly) {
|
||||||
AssemblyParamSignature(stmt.params.map {
|
AssemblyParamSignature(stmt.params.map {
|
||||||
pd =>
|
pd =>
|
||||||
@ -539,7 +542,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
|||||||
stmt.bank
|
stmt.bank
|
||||||
)
|
)
|
||||||
addThing(mangled, stmt.position)
|
addThing(mangled, stmt.position)
|
||||||
registerAddressConstant(mangled, stmt.position)
|
registerAddressConstant(mangled, stmt.position, options)
|
||||||
addThing(ConstantThing(name + '`', addr, w), stmt.position)
|
addThing(ConstantThing(name + '`', addr, w), stmt.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,19 +588,29 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
|||||||
declaredBank = stmt.bank
|
declaredBank = stmt.bank
|
||||||
)
|
)
|
||||||
addThing(mangled, stmt.position)
|
addThing(mangled, stmt.position)
|
||||||
registerAddressConstant(mangled, stmt.position)
|
registerAddressConstant(mangled, stmt.position, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def registerAddressConstant(thing: ThingInMemory, position: Option[Position]): Unit = {
|
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
|
val addr = thing.toAddress
|
||||||
addThing(ConstantThing(thing.name + ".addr", addr, get[Type]("pointer")), position)
|
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.hi", addr.hiByte, get[Type]("byte")), position)
|
||||||
addThing(ConstantThing(thing.name + ".addr.lo", addr.loByte, 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 typ = get[Type](stmt.typ)
|
||||||
val b = get[Type]("byte")
|
val b = get[Type]("byte")
|
||||||
val w = get[Type]("word")
|
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 zp = typ.name == "pointer" // TODO
|
||||||
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto, None)
|
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto, None)
|
||||||
addThing(v, stmt.position)
|
addThing(v, stmt.position)
|
||||||
registerAddressConstant(v, stmt.position)
|
registerAddressConstant(v, stmt.position, options)
|
||||||
val addr = v.toAddress
|
val addr = v.toAddress
|
||||||
typ.size match {
|
typ.size match {
|
||||||
case 2 =>
|
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 b = get[Type]("byte")
|
||||||
val p = get[Type]("pointer")
|
val p = get[Type]("pointer")
|
||||||
stmt.elements match {
|
stmt.elements match {
|
||||||
@ -694,18 +707,30 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
|||||||
declaredBank = stmt.bank)
|
declaredBank = stmt.bank)
|
||||||
}
|
}
|
||||||
addThing(array, stmt.position)
|
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 {
|
val a = address match {
|
||||||
case None => array.toAddress
|
case None => array.toAddress
|
||||||
case Some(aa) => aa
|
case Some(aa) => aa
|
||||||
}
|
}
|
||||||
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
|
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
|
||||||
declaredBank = stmt.bank), stmt.position)
|
declaredBank = stmt.bank), 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, a, p), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), 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 + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.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)
|
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||||
|
}
|
||||||
if (length < 256) {
|
if (length < 256) {
|
||||||
addThing(ConstantThing(stmt.name + ".length", lengthConst, b), stmt.position)
|
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)
|
val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank)
|
||||||
addThing(array, stmt.position)
|
addThing(array, stmt.position)
|
||||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None,
|
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None,
|
||||||
declaredBank = stmt.bank), stmt.position)
|
declaredBank = stmt.bank), stmt.position, options)
|
||||||
val a = address match {
|
val a = address match {
|
||||||
case None => array.toAddress
|
case None => array.toAddress
|
||||||
case Some(aa) => aa
|
case Some(aa) => aa
|
||||||
}
|
}
|
||||||
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
|
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
|
||||||
declaredBank = stmt.bank), stmt.position)
|
declaredBank = stmt.bank), 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, a, p), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), 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 + ".lo", a.loByte.quickSimplify, b), stmt.position)
|
||||||
addThing(ConstantThing(stmt.name + ".array.hi", a.hiByte.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)
|
addThing(ConstantThing(stmt.name + ".array.lo", a.loByte.quickSimplify, b), stmt.position)
|
||||||
|
}
|
||||||
if (length < 256) {
|
if (length < 256) {
|
||||||
addThing(ConstantThing(stmt.name + ".length", NumericConstant(length, 1), b), stmt.position)
|
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)
|
InitializedMemoryVariable(name, None, typ, ive, declaredBank = stmt.bank)
|
||||||
}
|
}
|
||||||
registerAddressConstant(v, stmt.position)
|
registerAddressConstant(v, stmt.position, options)
|
||||||
(v, v.toAddress)
|
(v, v.toAddress)
|
||||||
})(a => {
|
})(a => {
|
||||||
val addr = eval(a).getOrElse(Constant.error(s"Address of `$name` has a non-constant value", position))
|
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,
|
val v = RelativeVariable(prefix + name, addr, typ, zeropage = zp,
|
||||||
declaredBank = stmt.bank)
|
declaredBank = stmt.bank)
|
||||||
registerAddressConstant(v, stmt.position)
|
registerAddressConstant(v, stmt.position, options)
|
||||||
(v, addr)
|
(v, addr)
|
||||||
})
|
})
|
||||||
addThing(v, stmt.position)
|
addThing(v, stmt.position)
|
||||||
@ -927,7 +962,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
|||||||
program.declarations.foreach {
|
program.declarations.foreach {
|
||||||
case f: FunctionDeclarationStatement => registerFunction(f, options)
|
case f: FunctionDeclarationStatement => registerFunction(f, options)
|
||||||
case v: VariableDeclarationStatement => registerVariable(v, options)
|
case v: VariableDeclarationStatement => registerVariable(v, options)
|
||||||
case a: ArrayDeclarationStatement => registerArray(a)
|
case a: ArrayDeclarationStatement => registerArray(a, options)
|
||||||
case i: ImportStatement => ()
|
case i: ImportStatement => ()
|
||||||
}
|
}
|
||||||
if (options.flag(CompilationFlag.ZeropagePseudoregister) && !things.contains("__reg")) {
|
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 {
|
sealed trait ThingInMemory extends Thing {
|
||||||
|
def zeropage: Boolean
|
||||||
|
|
||||||
def toAddress: Constant
|
def toAddress: Constant
|
||||||
|
|
||||||
var farFlag: Option[Boolean] = None
|
var farFlag: Option[Boolean] = None
|
||||||
@ -105,12 +107,13 @@ case class Label(name: String) extends ThingInMemory {
|
|||||||
declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
|
declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
|
||||||
|
|
||||||
override val declaredBank: Option[String] = None
|
override val declaredBank: Option[String] = None
|
||||||
|
|
||||||
|
override def zeropage: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait Variable extends TypedThing with VariableLikeThing
|
sealed trait Variable extends TypedThing with VariableLikeThing
|
||||||
|
|
||||||
sealed trait VariableInMemory extends Variable with ThingInMemory with IndexableThing {
|
sealed trait VariableInMemory extends Variable with ThingInMemory with IndexableThing {
|
||||||
def zeropage: Boolean
|
|
||||||
|
|
||||||
override def isFar(compilationOptions: CompilationOptions): Boolean =
|
override def isFar(compilationOptions: CompilationOptions): Boolean =
|
||||||
!zeropage && farFlag.getOrElse(false)
|
!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 isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||||
|
|
||||||
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse("default")
|
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 {
|
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 isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||||
|
|
||||||
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse("default")
|
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 {
|
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 isFar(compilationOptions: CompilationOptions): Boolean = farFlag.getOrElse(false)
|
||||||
|
|
||||||
override def bank(compilationOptions: CompilationOptions): String = declaredBank.getOrElse(compilationOptions.platform.defaultCodeBank)
|
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 {
|
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 toAddress: Constant = address
|
||||||
|
|
||||||
override def interrupt = false
|
override def interrupt = false
|
||||||
|
|
||||||
|
override def zeropage: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class NormalFunction(name: String,
|
case class NormalFunction(name: String,
|
||||||
@ -257,6 +268,8 @@ case class NormalFunction(name: String,
|
|||||||
position: Option[Position],
|
position: Option[Position],
|
||||||
declaredBank: Option[String]) extends FunctionInMemory with PreallocableThing {
|
declaredBank: Option[String]) extends FunctionInMemory with PreallocableThing {
|
||||||
override def shouldGenerate = true
|
override def shouldGenerate = true
|
||||||
|
|
||||||
|
override def zeropage: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class ConstantThing(name: String, value: Constant, typ: Type) extends TypedThing with VariableLikeThing with IndexableThing {
|
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 {
|
try {
|
||||||
if (labelMap.contains(th.name)) return labelMap(th.name)
|
if (labelMap.contains(th.name)) return labelMap(th.name)
|
||||||
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 x1 = env.maybeGet[ConstantThing](th.name).map(_.value)
|
||||||
val x2 = 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)
|
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 =>
|
case e: StackOverflowError =>
|
||||||
ErrorReporting.fatal("Stack overflow " + c)
|
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 SubbyteConstant(cc, i) => deepConstResolve(cc).>>>(i * 8).&(0xff)
|
||||||
case CompoundConstant(operator, lc, rc) =>
|
case CompoundConstant(operator, lc, rc) =>
|
||||||
val l = deepConstResolve(lc)
|
val l = deepConstResolve(lc)
|
||||||
@ -282,6 +285,42 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
|
|||||||
}
|
}
|
||||||
case _ =>
|
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 {
|
env.allPreallocatables.foreach {
|
||||||
case thing@InitializedArray(name, None, items, _) =>
|
case thing@InitializedArray(name, None, items, _) =>
|
||||||
val bank = thing.bank(options)
|
val bank = thing.bank(options)
|
||||||
@ -338,6 +377,12 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
|
|||||||
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)
|
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
|
env = rootEnv.allThings
|
||||||
|
|
||||||
for ((bank, addr, b) <- bytesToWriteLater) {
|
for ((bank, addr, b) <- bytesToWriteLater) {
|
||||||
@ -367,7 +412,7 @@ class Assembler(private val program: Program, private val rootEnv: Environment,
|
|||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
val code = (platform.outputStyle match {
|
val code = (platform.outputStyle match {
|
||||||
case OutputStyle.Single => List("default")
|
case OutputStyle.Single | OutputStyle.LUnix => List("default")
|
||||||
case OutputStyle.PerBank => platform.bankNumbers.keys.toList
|
case OutputStyle.PerBank => platform.bankNumbers.keys.toList
|
||||||
}).map(b => b -> platform.outputPackager.packageOutput(mem, b)).toMap
|
}).map(b => b -> platform.outputPackager.packageOutput(mem, b)).toMap
|
||||||
AssemblerOutput(code, assembly.toArray, labelMap.toList)
|
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 {
|
object EndAddressOutput extends OutputPackager {
|
||||||
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
|
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
|
||||||
val b = mem.banks(bank)
|
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 {
|
object AllocatedDataOutput extends OutputPackager {
|
||||||
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
|
def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
|
||||||
val b = mem.banks(bank)
|
val b = mem.banks(bank)
|
||||||
|
@ -50,7 +50,7 @@ class AfterCodeByteAllocator(val endBefore: Int) extends ByteAllocator {
|
|||||||
def notifyAboutEndOfCode(org: Int): Unit = startAt = org
|
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 pointerMap = mutable.Map[Int, Set[VariableVertex]]()
|
||||||
private val variableMap = mutable.Map[Int, 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