Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4030a1b9e5 | ||
|
|
0217d05c68 | ||
|
|
8553f3c9f1 | ||
|
|
d157d03ea0 | ||
|
|
1799b5f00c | ||
|
|
2ddb5e11b7 | ||
|
|
3aa8b9fd12 | ||
|
|
51300196e8 | ||
|
|
f7b2f19cba | ||
|
|
014a82a1ee | ||
|
|
745cf3d958 | ||
|
|
c4aa681d58 | ||
|
|
feb0774bbd | ||
|
|
2ccfb2ac90 | ||
|
|
2191c4b7d8 | ||
|
|
39c1cf0c3c | ||
|
|
c693be0dd6 | ||
|
|
0f433055a7 | ||
|
|
76c95ba6fa | ||
|
|
bd272a8567 | ||
|
|
b38fff76e9 | ||
|
|
07b7639ff2 | ||
|
|
6871561c09 | ||
|
|
77a71138ad | ||
|
|
165f94bc93 | ||
|
|
b75090f928 | ||
|
|
62b0c82f93 | ||
|
|
b1ef863c7f | ||
|
|
e78345410f | ||
|
|
8b86f97aaa | ||
|
|
93135774e6 | ||
|
|
a64f27c6b0 | ||
|
|
69ef63c96d |
No files matched your search
@@ -2,15 +2,28 @@
|
||||
|
||||
Several benchmarks of https://thred.github.io/c-bench-64/
|
||||
have been ported to equivalent Prog8 code and the benchmark results have been penciled in in the graphs below.
|
||||
Because they use the CIA timer for measuring the duration of the runs, the code only works on a C64.
|
||||
|
||||
Maybe one day I'll try to integrate the prog8 data properly but their benchmark site is comparing C compilers, which Prog8 clearly is not.
|
||||
|
||||
However conclusions so far (note: these are micro benchmarks so interpret the results as you will!)
|
||||
|
||||
* Prog8 program size is consistently the smallest by a fair bit.
|
||||
* Prog8 program size is consistently one of the smallest.
|
||||
* Prog8 execution speed places it more or less in the middle of the stack.
|
||||
|
||||
Measured with Prog8 V12.0 .
|
||||
Benchmarks ran on a PAL C64 emulated through VICE.
|
||||
|
||||
textual results:
|
||||
|
||||
| benchmark | size | time |
|
||||
|-----------|------|----------|
|
||||
| crc8 | 1568 | 2.0 sec |
|
||||
| crc16 | 1608 | 2.5 sec |
|
||||
| crc32 | 1812 | 4.2 sec |
|
||||
| pow | 2152 | 11.7 sec |
|
||||
| sieve | 1772 | 22.1 sec |
|
||||
| sieve_bit | 1918 | 40.1 sec |
|
||||
|
||||
|
||||

|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
%import textio
|
||||
|
||||
cia {
|
||||
ubyte freq
|
||||
const ubyte CNT = 200
|
||||
|
||||
sub calibrate() {
|
||||
txt.print("calibrating frequency: ")
|
||||
tod_init(0)
|
||||
txt.print_ub(tod_freq())
|
||||
txt.print(" hz\n")
|
||||
tod_reset()
|
||||
}
|
||||
|
||||
sub print_time() {
|
||||
uword t = tod_get10()
|
||||
txt.print("(cia) time: ")
|
||||
txt.print_uw(t / 10)
|
||||
txt.chrout('.')
|
||||
txt.print_uw(t % 10)
|
||||
txt.print(" sec.\n")
|
||||
}
|
||||
|
||||
sub tod_reset() {
|
||||
; set the tod to 0
|
||||
c64.CIA2TODHR = 0
|
||||
c64.CIA2TODMIN = 0
|
||||
c64.CIA2TODSEC = 0
|
||||
c64.CIA2TOD10 = 0
|
||||
}
|
||||
|
||||
sub tod_get10() -> uword {
|
||||
ubyte h, m, s, t
|
||||
uword time
|
||||
h = c64.CIA2TODHR
|
||||
m = c64.CIA2TODMIN
|
||||
s = c64.CIA2TODSEC
|
||||
t = c64.CIA2TOD10
|
||||
time = t
|
||||
time += (s & $0f) * (10 as uword)
|
||||
time += (s >> 4) * (100 as uword)
|
||||
time += (m & $0f) * 600
|
||||
time += (m >> 4) * 6000
|
||||
return time
|
||||
}
|
||||
|
||||
sub tod_init(ubyte f) {
|
||||
if (f == 0)
|
||||
freq = tod_detect_freq()
|
||||
else
|
||||
freq = f
|
||||
|
||||
if (freq == 50)
|
||||
c64.CIA2CRA |= $80
|
||||
else
|
||||
c64.CIA2CRA &= $7f
|
||||
}
|
||||
|
||||
sub tod_freq() -> ubyte {
|
||||
return freq
|
||||
}
|
||||
|
||||
sub tod_detect_freq() -> ubyte {
|
||||
uword cbl
|
||||
|
||||
c64.CIA2CRB = $40 ; stop timer
|
||||
c64.CIA2CRA = $80 ; stop timer
|
||||
|
||||
; set ta to overflow every 10000 count (~= 10ms)
|
||||
c64.CIA2TAL = $10
|
||||
c64.CIA2TAH = $27
|
||||
c64.CIA2TBL = CNT
|
||||
c64.CIA2TBH = 0
|
||||
|
||||
tod_reset()
|
||||
|
||||
c64.CIA2CRB = $41 ; input from tim1 overflow, continuous, start timer
|
||||
c64.CIA2CRA = $81 ; start timer, continuous tod 50HZ
|
||||
|
||||
|
||||
while c64.CIA2TODSEC == 0 {
|
||||
; wait for tod to count 1s
|
||||
}
|
||||
|
||||
; cal=CIA2.ta_lo;
|
||||
; cah=CIA2.ta_hi;
|
||||
cbl = c64.CIA2TBL
|
||||
; cbh=CIA2.tb_hi;
|
||||
|
||||
; printf("count2 = %d %d %d %d\n",cah, cal, cbh, cbl);
|
||||
; printf("elapsed ~= %d0ms\n",CNT-cbl);
|
||||
|
||||
if CNT - cbl > 90
|
||||
return 50
|
||||
else
|
||||
return 60
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
; note: Prog8 has a complete CRC16 routine in its library: math.crc16
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
@@ -40,12 +39,13 @@ test {
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uwhex(EXPECTED, true)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
; note: Prog8 has a complete CRC32 routine in its library: math.crc32
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
@@ -40,12 +39,13 @@ test {
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_ulhex(EXPECTED, true)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
@@ -36,12 +35,13 @@ test {
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_ubhex(EXPECTED, true)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 65 KiB |
@@ -1,16 +1,15 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
@@ -46,12 +45,13 @@ test {
|
||||
|
||||
if prime_count == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uw(EXPECTED)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
@@ -46,12 +45,13 @@ test {
|
||||
|
||||
if prime_count == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uw(EXPECTED)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ adpcm {
|
||||
|
||||
while cbm.RDTIM16()<max_time {
|
||||
adpcm.init(0,0)
|
||||
uword @requirezp nibbles_ptr = $a000 ; for benchmark purposes, the exact nibbles don't really matter, so we just take the basic ROM as input
|
||||
uword @requirezp nibbles_ptr = $0800 ; for benchmark purposes, the exact nibbles don't really matter, so we just take this benchmark program itself as input
|
||||
repeat 252/2 {
|
||||
unroll 2 {
|
||||
ubyte @zp nibble = @(nibbles_ptr)
|
||||
|
||||
@@ -14,8 +14,11 @@ fun Number.toHex(): String {
|
||||
// larger -> "$12345678"
|
||||
// negative values are prefixed with '-'.
|
||||
val integer = this.toLong()
|
||||
if(integer<0)
|
||||
if(integer<0) {
|
||||
if(integer==-2147483648L)
|
||||
return "$80000000" // the exception to the rule, because -$80000000 is not a valid hex number
|
||||
return '-' + abs(integer).toHex()
|
||||
}
|
||||
return when (integer) {
|
||||
in 0 until 16 -> integer.toString()
|
||||
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
|
||||
|
||||
@@ -962,14 +962,14 @@ class AsmGen6502Internal (
|
||||
}
|
||||
} else throw AssemblyError("weird casted type")
|
||||
} else {
|
||||
TODO("assign typecasted expression $value to a long target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
|
||||
TODO("assign typecasted expression $value to a long target ${target.kind} at ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
|
||||
}
|
||||
} else if(target.kind == TargetStorageKind.REGISTER) {
|
||||
assignExpressionToRegister(value, target.register!!, true)
|
||||
} else if(target.kind == TargetStorageKind.VARIABLE) {
|
||||
assignExpressionToVariable(value, target.asmVarname, target.datatype)
|
||||
} else {
|
||||
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
|
||||
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
|
||||
}
|
||||
}
|
||||
target.datatype.isFloat -> {
|
||||
@@ -2454,12 +2454,13 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
internal fun pushLongRegisters(startreg: RegisterOrPair, numberOfLongs: Int) {
|
||||
// may clobber P8ZP_SCRATCH_B1, X, Y.
|
||||
if(numberOfLongs==0) return
|
||||
|
||||
val startregName = startreg.startregname()
|
||||
if(numberOfLongs==1) {
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tax
|
||||
lda cx16.$startregName
|
||||
pha
|
||||
lda cx16.$startregName+1
|
||||
@@ -2468,12 +2469,12 @@ $repeatLabel""")
|
||||
pha
|
||||
lda cx16.$startregName+3
|
||||
pha
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
txa""")
|
||||
return
|
||||
}
|
||||
val numbytes = numberOfLongs * options.compTarget.memorySize(BaseDataType.LONG)
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tax
|
||||
sty P8ZP_SCRATCH_B1
|
||||
ldy #0
|
||||
- lda cx16.$startregName,y
|
||||
@@ -2482,7 +2483,7 @@ $repeatLabel""")
|
||||
cpy #$numbytes
|
||||
bne -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
txa""")
|
||||
}
|
||||
|
||||
internal fun popLongRegisters(startreg: RegisterOrPair, numberOfLongs: Int) {
|
||||
@@ -2491,7 +2492,7 @@ $repeatLabel""")
|
||||
val startregName = startreg.startregname()
|
||||
if(numberOfLongs==1) {
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tax
|
||||
pla
|
||||
sta cx16.$startregName+3
|
||||
pla
|
||||
@@ -2500,12 +2501,12 @@ $repeatLabel""")
|
||||
sta cx16.$startregName+1
|
||||
pla
|
||||
sta cx16.$startregName
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
txa""")
|
||||
return
|
||||
}
|
||||
val numbytes = numberOfLongs * options.compTarget.memorySize(BaseDataType.LONG)
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tax
|
||||
sty P8ZP_SCRATCH_B1
|
||||
ldy #$numbytes-1
|
||||
- pla
|
||||
@@ -2513,7 +2514,7 @@ $repeatLabel""")
|
||||
dey
|
||||
bpl -
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
lda P8ZP_SCRATCH_REG""")
|
||||
txa""")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
private fun argumentViaRegister(sub: IPtSubroutine, parameter: IndexedValue<PtSubroutineParameter>, value: PtExpression, registerOverride: RegisterOrPair? = null): RegisterOrStatusflag {
|
||||
// pass argument via a register parameter
|
||||
if(!isArgumentTypeCompatible(value.type, parameter.value.type))
|
||||
throw AssemblyError("argument type incompatible")
|
||||
throw AssemblyError("argument type incompatible ${value.type} vs param ${parameter.value.type} at ${parameter.value.position}")
|
||||
|
||||
val paramRegister: RegisterOrStatusflag = when(sub) {
|
||||
is PtAsmSub -> if(registerOverride==null) sub.parameters[parameter.index].first else RegisterOrStatusflag(registerOverride, null)
|
||||
@@ -362,12 +362,17 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||
if(argType isAssignableTo paramType)
|
||||
return true
|
||||
if(argType.isBool && paramType.isBool)
|
||||
return true
|
||||
|
||||
// signed/unsigned args vs parameters are considered compatible
|
||||
if(argType.isByte && paramType.isByte)
|
||||
return true
|
||||
if(argType.isWord && paramType.isWord)
|
||||
return true
|
||||
// only signed longs and floats are available so these were already covered
|
||||
// if(argType.isLong && paramType.isLong)
|
||||
// return true
|
||||
// if(argType.isFloat && paramType.isFloat)
|
||||
// return true
|
||||
|
||||
// we have a special rule for some types.
|
||||
// strings are assignable to UWORD, for example, and vice versa
|
||||
|
||||
@@ -942,7 +942,7 @@ _jump jmp (${target.asmLabel})
|
||||
asmgen.out(" lda $varname+1")
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
require(!long)
|
||||
require(!long) {"addresses must still be words not longs"}
|
||||
if(value.isFromArrayElement) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
@@ -955,9 +955,14 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
require(!long)
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
if(long) {
|
||||
// note: clobbers R14+R15
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, true)
|
||||
asmgen.out(" lda cx16.r14+3")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY, true)
|
||||
asmgen.out(" cpy #0")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1294,7 +1299,11 @@ _jump jmp (${target.asmLabel})
|
||||
|
||||
private fun longLessZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
if(lessEquals) {
|
||||
TODO("long <= 0 ${value.position}")
|
||||
loadAndCheck0OrSignIntoCarry(value, true)
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bcs", "bcc", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
} else {
|
||||
loadAndCmp0MSB(value, true)
|
||||
if (jump != null)
|
||||
@@ -1306,7 +1315,11 @@ _jump jmp (${target.asmLabel})
|
||||
|
||||
private fun longGreaterZero(value: PtExpression, lessEquals: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
if(lessEquals) {
|
||||
TODO("long >= 0 ${value.position}")
|
||||
loadAndCheck0OrSignIntoCarry(value, false)
|
||||
if (jump != null)
|
||||
translateJumpElseBodies("bcs", "bcc", jump, stmt.elseScope)
|
||||
else
|
||||
translateIfElseBodies("bcc", stmt)
|
||||
} else {
|
||||
loadAndCmp0MSB(value, true)
|
||||
if (jump != null)
|
||||
@@ -1316,6 +1329,28 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadAndCheck0OrSignIntoCarry(value: PtExpression, negative: Boolean) {
|
||||
if(value is PtIdentifier) {
|
||||
val varname = asmgen.asmVariableName(value)
|
||||
asmgen.out("""
|
||||
lda #<$varname
|
||||
ldy #>$varname
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1""")
|
||||
asmgen.out(if(negative) " sec" else " clc")
|
||||
asmgen.out(" jsr prog8_lib.compare_long_0_and_sign")
|
||||
} else {
|
||||
assignmentAsmGen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, value.type.isSigned)
|
||||
asmgen.out("""
|
||||
lda #<cx16.r14
|
||||
ldy #>cx16.r14
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1""")
|
||||
asmgen.out(if(negative) " sec" else " clc")
|
||||
asmgen.out(" jsr prog8_lib.compare_long_0_and_sign")
|
||||
}
|
||||
}
|
||||
|
||||
private fun wordEqualsZero(value: PtExpression, notEquals: Boolean, signed: Boolean, jump: PtJump?, stmt: PtIfElse) {
|
||||
|
||||
// special case for (u)word == 0
|
||||
@@ -1493,10 +1528,6 @@ _jump jmp (${target.asmLabel})
|
||||
) {
|
||||
// this comparison is not part of an expression but part of an if statement, there's no need to save the previous values of the temp registers
|
||||
|
||||
if(left !is PtNumber && left !is PtIdentifier || right !is PtNumber && right !is PtIdentifier) {
|
||||
TODO("long comparison $operator with expressions - use temporary long variable instead to simplify for now ${left.position}")
|
||||
}
|
||||
|
||||
if(operator=="<" || operator ==">=") {
|
||||
assignmentAsmGen.assignExpressionToRegister(right, RegisterOrPair.R14R15_32, left.type.isSigned)
|
||||
assignmentAsmGen.assignExpressionToRegister(left, RegisterOrPair.R12R13_32, left.type.isSigned)
|
||||
|
||||
@@ -412,10 +412,9 @@ internal class ProgramAndVarsGen(
|
||||
|
||||
|
||||
asmgen.out("; struct types")
|
||||
symboltable.allStructInstances.distinctBy { it.structName }.forEach {
|
||||
val structtype: StStruct = symboltable.lookup(it.structName) as StStruct
|
||||
symboltable.allStructTypes().distinctBy { it.name }.forEach { structtype ->
|
||||
val structargs = structtype.fields.withIndex().joinToString(",") { field -> "f${field.index}" }
|
||||
asmgen.out("${it.structName} .struct $structargs\n")
|
||||
asmgen.out("${structtype.scopedNameString} .struct $structargs\n")
|
||||
structtype.fields.withIndex().forEach { (index, field) ->
|
||||
val dt = field.first
|
||||
val varname = "f${index}"
|
||||
@@ -425,7 +424,7 @@ internal class ProgramAndVarsGen(
|
||||
asmgen.out(" .endstruct\n")
|
||||
}
|
||||
|
||||
val (instancesNoInit, instances) = symboltable.allStructInstances.partition { it.initialValues.isEmpty() }
|
||||
val (instancesNoInit, instances) = symboltable.allStructInstances().partition { it.initialValues.isEmpty() }
|
||||
asmgen.out("; struct instances without initialization values, as BSS zeroed at startup\n")
|
||||
asmgen.out(" .section BSS\n")
|
||||
asmgen.out("${StStructInstanceBlockName}_bss .block\n")
|
||||
|
||||
@@ -296,17 +296,7 @@ internal class AssignmentAsmGen(
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
elementDt.isLong -> {
|
||||
// it's not an expression so no need to preserve R14-R15
|
||||
asmgen.out("""
|
||||
lda $arrayVarName+$indexValue
|
||||
sta cx16.r14
|
||||
lda $arrayVarName+$indexValue+1
|
||||
sta cx16.r14+1
|
||||
lda $arrayVarName+$indexValue+2
|
||||
sta cx16.r14+2
|
||||
lda $arrayVarName+$indexValue+3
|
||||
sta cx16.r14+3""")
|
||||
assignRegisterLong(assign.target, RegisterOrPair.R14R15_32)
|
||||
assignVariableLong(assign.target, "$arrayVarName+$indexValue", DataType.LONG)
|
||||
}
|
||||
elementDt.isFloat -> {
|
||||
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue)")
|
||||
@@ -328,18 +318,7 @@ internal class AssignmentAsmGen(
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AX)
|
||||
}
|
||||
elementDt.isLong -> {
|
||||
// it's not an expression so no need to preserve R14-R15
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
asmgen.out("""
|
||||
lda $arrayVarName,y
|
||||
sta cx16.r14
|
||||
lda $arrayVarName+1,y
|
||||
sta cx16.r14+1
|
||||
lda $arrayVarName+2,y
|
||||
sta cx16.r14+2
|
||||
lda $arrayVarName+3,y
|
||||
sta cx16.r14+3""")
|
||||
assignRegisterLong(assign.target, RegisterOrPair.R14R15_32)
|
||||
assignVariableLongIndexed(assign.target, arrayVarName, value)
|
||||
}
|
||||
elementDt.isFloat -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.A)
|
||||
@@ -1199,7 +1178,6 @@ internal class AssignmentAsmGen(
|
||||
}
|
||||
}
|
||||
else {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.type.isSigned)
|
||||
if(expr.definingBlock()!!.options.veraFxMuls){
|
||||
// cx16 verafx hardware mul
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "cx16.r1")
|
||||
@@ -1836,7 +1814,7 @@ internal class AssignmentAsmGen(
|
||||
pointergen.assignLongVar(target.pointer!!, "cx16.r14")
|
||||
return true
|
||||
} else {
|
||||
TODO("add/subtract long const into ${target.kind} ${target.position} - use simple expressions and temporary variables for now")
|
||||
TODO("add/subtract long const into ${target.kind} ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
@@ -2697,35 +2675,35 @@ $endLabel""")
|
||||
}
|
||||
RegisterOrPair.R0R1_32 -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.R0, false)
|
||||
asmgen.signExtendLongVariable("cx16.r0", valueDt.base)
|
||||
return asmgen.signExtendLongVariable("cx16.r0", valueDt.base)
|
||||
}
|
||||
RegisterOrPair.R2R3_32 -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.R2, false)
|
||||
asmgen.signExtendLongVariable("cx16.r2", valueDt.base)
|
||||
return asmgen.signExtendLongVariable("cx16.r2", valueDt.base)
|
||||
}
|
||||
RegisterOrPair.R4R5_32 -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.R4, false)
|
||||
asmgen.signExtendLongVariable("cx16.r4", valueDt.base)
|
||||
return asmgen.signExtendLongVariable("cx16.r4", valueDt.base)
|
||||
}
|
||||
RegisterOrPair.R6R7_32 -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.R6, false)
|
||||
asmgen.signExtendLongVariable("cx16.r6", valueDt.base)
|
||||
return asmgen.signExtendLongVariable("cx16.r6", valueDt.base)
|
||||
}
|
||||
RegisterOrPair.R8R9_32 -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.R8, false)
|
||||
asmgen.signExtendLongVariable("cx16.r8", valueDt.base)
|
||||
return asmgen.signExtendLongVariable("cx16.r8", valueDt.base)
|
||||
}
|
||||
RegisterOrPair.R10R11_32 -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.R10, false)
|
||||
asmgen.signExtendLongVariable("cx16.r10", valueDt.base)
|
||||
return asmgen.signExtendLongVariable("cx16.r10", valueDt.base)
|
||||
}
|
||||
RegisterOrPair.R12R13_32 -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.R12, false)
|
||||
asmgen.signExtendLongVariable("cx16.r12", valueDt.base)
|
||||
return asmgen.signExtendLongVariable("cx16.r12", valueDt.base)
|
||||
}
|
||||
RegisterOrPair.R14R15_32 -> {
|
||||
assignExpressionToRegister(value, RegisterOrPair.R14, false)
|
||||
asmgen.signExtendLongVariable("cx16.r14", valueDt.base)
|
||||
return asmgen.signExtendLongVariable("cx16.r14", valueDt.base)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
@@ -3533,7 +3511,6 @@ $endLabel""")
|
||||
lda $varName+3
|
||||
sta ${target.asmVarname}+3,y""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> {
|
||||
require(target.register in combinedLongRegisters)
|
||||
val regstart = target.register!!.startregname()
|
||||
@@ -3584,6 +3561,59 @@ $endLabel""")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignLongVar(target.pointer!!, varName)
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignVariableLongIndexed(target: AsmAssignTarget, arrayVarName: String, index: PtArrayIndexer) {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(index, CpuRegister.Y)
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda $arrayVarName,y
|
||||
sta ${target.asmVarname}
|
||||
lda $arrayVarName+1,y
|
||||
sta ${target.asmVarname}+1
|
||||
lda $arrayVarName+2,y
|
||||
sta ${target.asmVarname}+2
|
||||
lda $arrayVarName+3,y
|
||||
sta ${target.asmVarname}+3""")
|
||||
}
|
||||
TargetStorageKind.REGISTER -> {
|
||||
require(target.register in combinedLongRegisters)
|
||||
val regstart = target.register!!.startregname()
|
||||
asmgen.out("""
|
||||
lda $arrayVarName,y
|
||||
sta cx16.$regstart
|
||||
lda $arrayVarName+1,y
|
||||
sta cx16.$regstart+1
|
||||
lda $arrayVarName+2,y
|
||||
sta cx16.$regstart+2
|
||||
lda $arrayVarName+3,y
|
||||
sta cx16.$regstart+3""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val deref = target.array!!.pointerderef
|
||||
if(deref!=null) {
|
||||
pointergen.assignLongVar(IndexedPtrTarget(target), arrayVarName)
|
||||
return
|
||||
}
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.X)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out("""
|
||||
lda $arrayVarName,y
|
||||
sta ${target.asmVarname},x
|
||||
lda $arrayVarName+1,y
|
||||
sta ${target.asmVarname}+1,x
|
||||
lda $arrayVarName+2,y
|
||||
sta ${target.asmVarname}+2,x
|
||||
lda $arrayVarName+3,y
|
||||
sta ${target.asmVarname}+3,x""")
|
||||
}
|
||||
TargetStorageKind.POINTER -> pointergen.assignIndexedPointer(target, arrayVarName, index, DataType.arrayFor(BaseDataType.LONG))
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,9 +113,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(target.asmVarname, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(target.asmVarname, operator, value.asmVarname)
|
||||
SourceStorageKind.EXPRESSION -> inplacemodificationLongWithExpression(target.asmVarname, operator, value.expression!!)
|
||||
SourceStorageKind.REGISTER -> TODO("32 bits register inplace modification? ${target.position}")
|
||||
SourceStorageKind.ARRAY -> TODO("inplace modify long with array ${target.position}")
|
||||
SourceStorageKind.MEMORY -> TODO("memread into long ${target.position}")
|
||||
SourceStorageKind.REGISTER -> inplacemodificationLongWithVariable(target.asmVarname, operator, regName(value))
|
||||
SourceStorageKind.ARRAY -> TODO("inplace modify long with array value ${target.position}")
|
||||
SourceStorageKind.MEMORY -> TODO("inplace modify long with memread value ${target.position}")
|
||||
}
|
||||
}
|
||||
target.datatype.isFloat -> {
|
||||
@@ -124,7 +124,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(target.asmVarname, operator, value.number!!.number)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(target.asmVarname, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(target.asmVarname, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float ${target.position}")
|
||||
SourceStorageKind.MEMORY -> inplacemodificationFloatWithValue(target.asmVarname, operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(target.asmVarname, operator, value.array!!)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
@@ -164,18 +164,18 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
is PtIdentifier -> {
|
||||
val pointer = memory.address as PtIdentifier
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> inplacemodificationBytePointerWithLiteralval(pointer, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationBytePointerWithLiteralval(pointer, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationBytePointerWithVariable(pointer, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationBytePointerWithVariable(pointer, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> TODO("memread into pointer ${target.position}")
|
||||
SourceStorageKind.ARRAY -> inplacemodificationBytePointerWithValue(pointer, operator, value.array!!)
|
||||
SourceStorageKind.LITERALBOOLEAN -> ptrgen.inplacemodificationBytePointerWithLiteralval(pointer, operator, value.boolean!!.asInt())
|
||||
SourceStorageKind.LITERALNUMBER -> ptrgen.inplacemodificationBytePointerWithLiteralval(pointer, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> ptrgen.inplacemodificationBytePointerWithVariable(pointer, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> ptrgen.inplacemodificationBytePointerWithVariable(pointer, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> ptrgen. inplacemodificationBytePointerWithValue(pointer, operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> ptrgen.inplacemodificationBytePointerWithValue(pointer, operator, value.array!!)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
inplacemodificationBytePointerWithValue(pointer, operator, value.expression)
|
||||
ptrgen.inplacemodificationBytePointerWithValue(pointer, operator, value.expression)
|
||||
} else {
|
||||
inplacemodificationBytePointerWithValue(pointer, operator, value.expression!!)
|
||||
ptrgen.inplacemodificationBytePointerWithValue(pointer, operator, value.expression!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -309,14 +309,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationLongWithLiteralval(targetVarName, operator, value.number!!.number.toInt())
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationLongWithVariable(targetVarName, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationLongWithVariable(targetVarName, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> TODO("inplace long modifiication ${target.position}")
|
||||
SourceStorageKind.ARRAY -> TODO("inplace long modifiication ${target.position}")
|
||||
SourceStorageKind.MEMORY -> TODO("inplace long modification with memread value ${target.position}")
|
||||
SourceStorageKind.ARRAY -> TODO("inplace long modification with array value ${target.position}")
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
if (tryInplaceModifyWithRemovedRedundantCast(value.expression, target, operator)) return
|
||||
TODO("inplace long modifiication ${target.position}")
|
||||
TODO("inplace long modification ${target.position}")
|
||||
} else {
|
||||
TODO("inplace long modifiication ${target.position}")
|
||||
TODO("inplace long modification ${target.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,7 +328,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(targetVarName, operator, value.number!!.number)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(targetVarName, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(targetVarName, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float array ${target.position}")
|
||||
SourceStorageKind.MEMORY -> inplacemodificationFloatWithValue(targetVarName, operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(targetVarName, operator, value.array!!)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
@@ -519,7 +519,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationFloatWithLiteralval(tempvar, operator, value.number!!.number)
|
||||
SourceStorageKind.VARIABLE -> inplacemodificationFloatWithVariable(tempvar, operator, value.asmVarname)
|
||||
SourceStorageKind.REGISTER -> inplacemodificationFloatWithVariable(tempvar, operator, regName(value))
|
||||
SourceStorageKind.MEMORY -> TODO("memread into float ${target.position}")
|
||||
SourceStorageKind.MEMORY -> inplacemodificationFloatWithValue(tempvar, operator, value.memory!!)
|
||||
SourceStorageKind.ARRAY -> inplacemodificationFloatWithValue(tempvar, operator, value.array!!)
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
if(value.expression is PtTypeCast) {
|
||||
@@ -555,7 +555,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
internal fun inplacemodificationLongWithExpression(targetVar: String, operator: String, value: PtExpression) {
|
||||
// it's not an expression so no need to preserve R14:R15
|
||||
// it's not an expression so no need to preserve R14-R15
|
||||
assignmentAsmGen.assignExpressionToRegister(value, RegisterOrPair.R14R15_32, value.type.isSigned)
|
||||
inplacemodificationLongWithVariable(targetVar, operator, "cx16.r14")
|
||||
}
|
||||
@@ -711,13 +711,21 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
lda $variable
|
||||
sta $variable+1
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable""")
|
||||
if(shift==1) {
|
||||
asmgen.out("""
|
||||
asl $variable+1
|
||||
rol $variable+2
|
||||
rol $variable+3""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #$shift
|
||||
- asl $variable+1
|
||||
rol $variable+2
|
||||
rol $variable+3
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
}
|
||||
value == 16 -> {
|
||||
asmgen.out("""
|
||||
@@ -738,12 +746,17 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
sta $variable+2
|
||||
lda #0
|
||||
sta $variable
|
||||
sta $variable+1
|
||||
sta $variable+1""")
|
||||
if(shift==1) {
|
||||
asmgen.out(" asl $variable+2 | rol $variable+3")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy #$shift
|
||||
- asl $variable+2
|
||||
rol $variable+3
|
||||
dey
|
||||
bne -""")
|
||||
}
|
||||
}
|
||||
value == 24 -> {
|
||||
asmgen.out("""
|
||||
@@ -756,12 +769,17 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
value <= 31 -> {
|
||||
val shift = value-24
|
||||
asmgen.out("""
|
||||
if(shift==1) {
|
||||
asmgen.out(" lda $variable | asl a")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
ldy #$shift
|
||||
- asl a
|
||||
dey
|
||||
bne -
|
||||
bne -""")
|
||||
}
|
||||
asmgen.out("""
|
||||
sta $variable+3
|
||||
lda #0
|
||||
sta $variable
|
||||
@@ -885,41 +903,31 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
1 -> {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
bne +
|
||||
dec $variable+1
|
||||
bne +
|
||||
dec $variable+2
|
||||
bne +++
|
||||
lda $variable+1
|
||||
bne ++
|
||||
lda $variable+2
|
||||
bne +
|
||||
dec $variable+3
|
||||
+ dec $variable+2
|
||||
+ dec $variable+1
|
||||
+ dec $variable""")
|
||||
}
|
||||
else -> {
|
||||
if(value in 1..255) {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
sec
|
||||
lda $variable
|
||||
sbc #$value
|
||||
sta $variable
|
||||
bcs +
|
||||
dec $variable+1
|
||||
bne +
|
||||
dec $variable+2
|
||||
bne +
|
||||
dec $variable+3
|
||||
+""")
|
||||
} else if(value in 1..65535) {
|
||||
asmgen.out("""
|
||||
lda $variable
|
||||
sec
|
||||
sbc #<$value
|
||||
sta $variable
|
||||
bcs +++
|
||||
lda $variable+1
|
||||
sbc #>$value
|
||||
sta $variable+1
|
||||
bcs +
|
||||
dec $variable+2
|
||||
bne ++
|
||||
lda $variable+2
|
||||
bne +
|
||||
dec $variable+3
|
||||
+ dec $variable+2
|
||||
+ dec $variable+1
|
||||
+""")
|
||||
} else {
|
||||
val hex = value.toLongHex()
|
||||
@@ -1386,188 +1394,6 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
return false
|
||||
}
|
||||
|
||||
private fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) {
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
private fun inplacemodificationBytePointerWithVariable(pointervar: PtIdentifier, operator: String, otherName: String) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
|
||||
when (operator) {
|
||||
"+" -> asmgen.out(" clc | adc $otherName")
|
||||
"-" -> asmgen.out(" sec | sbc $otherName")
|
||||
"*" -> asmgen.out(" ldy $otherName | jsr prog8_math.multiply_bytes")
|
||||
"/" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm | tya")
|
||||
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.remainder_ub_asm")
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
ldy $otherName
|
||||
beq +
|
||||
- asl a
|
||||
dey
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
">>" -> {
|
||||
asmgen.out("""
|
||||
ldy $otherName
|
||||
beq +
|
||||
- lsr a
|
||||
dey
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
"&" -> asmgen.out(" and $otherName")
|
||||
"|" -> asmgen.out(" ora $otherName")
|
||||
"^" -> asmgen.out(" eor $otherName")
|
||||
"==" -> {
|
||||
asmgen.out("""
|
||||
cmp $otherName
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
"!=" -> {
|
||||
asmgen.out("""
|
||||
cmp $otherName
|
||||
bne +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?
|
||||
"<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons ${pointervar.position}")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
|
||||
private fun inplacemodificationBytePointerWithLiteralval(pointervar: PtIdentifier, operator: String, value: Int) {
|
||||
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
if(value==1) {
|
||||
if(asmgen.options.romable) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" inc a")
|
||||
else
|
||||
asmgen.out(" clc | adc #1")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out($$"+\tinc $ffff\t; modified")
|
||||
}
|
||||
} else {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" clc | adc #$value")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
if(value==1) {
|
||||
if(asmgen.options.romable) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" dec a")
|
||||
else
|
||||
asmgen.out(" sec | sbc #1")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out($$"+\tdec $ffff\t; modified")
|
||||
}
|
||||
} else {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" sec | sbc #$value")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
}
|
||||
"*" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(value in asmgen.optimizedByteMultiplications)
|
||||
asmgen.out(" jsr prog8_math.mul_byte_${value}")
|
||||
else
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.multiply_bytes")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"/" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(value==0)
|
||||
throw AssemblyError("division by zero")
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.divmod_ub_asm | tya")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"%" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(value==0)
|
||||
throw AssemblyError("division by zero")
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.remainder_ub_asm")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"<<" -> {
|
||||
if (value > 0) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
repeat(value) { asmgen.out(" asl a") }
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
}
|
||||
">>" -> {
|
||||
if (value > 0) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
repeat(value) { asmgen.out(" lsr a") }
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" and #$value")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"|"-> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" ora #$value")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"^" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" eor #$value")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"==" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out("""
|
||||
cmp #$value
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"!=" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out("""
|
||||
cmp #$value
|
||||
bne +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?:
|
||||
"<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons ${pointervar.position}")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplacemodificationByteWithValue(name: String, dt: DataType, operator: String, value: PtExpression) {
|
||||
require(dt.isByteOrBool)
|
||||
if(!value.isSimple()) {
|
||||
|
||||
@@ -288,52 +288,76 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
}
|
||||
"*" -> {
|
||||
if(target.dt.isByte) TODO("inplaceByteMul(target, value) ${target.position}")
|
||||
else if(target.dt.isWord) inplaceWordMul(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatAddOrMul(target, "FMULT", value)
|
||||
else if(target.dt.isLong) TODO("inplace long mul ${target.position}")
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
when {
|
||||
target.dt.isByte -> TODO("inplaceByteMul(target, value) ${target.position}")
|
||||
target.dt.isWord -> inplaceWordMul(target, value)
|
||||
target.dt.isFloat -> inplaceFloatAddOrMul(target, "FMULT", value)
|
||||
target.dt.isLong -> TODO("inplace long mul ${target.position}")
|
||||
else -> throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
}
|
||||
"/" -> {
|
||||
if(target.dt.isByte) TODO("inplaceByteDiv(target, value) ${target.position}")
|
||||
else if(target.dt.isWord) inplaceWordDiv(target, value)
|
||||
else if(target.dt.isFloat) inplaceFloatSubOrDiv(target, "FDIV", value)
|
||||
else if(target.dt.isLong) TODO("inplace long div ${target.position}")
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
when {
|
||||
target.dt.isByte -> TODO("inplaceByteDiv(target, value) ${target.position}")
|
||||
target.dt.isWord -> inplaceWordDiv(target, value)
|
||||
target.dt.isFloat -> inplaceFloatSubOrDiv(target, "FDIV", value)
|
||||
target.dt.isLong -> TODO("inplace long div ${target.position}")
|
||||
else -> throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
}
|
||||
"%" -> {
|
||||
if(target.dt.isSigned || value.datatype.isSigned)
|
||||
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
|
||||
when {
|
||||
target.dt.isByte -> TODO("inplace byte pointer mod should have been handled via MEMORY target type ${target.position}")
|
||||
target.dt.isWord -> inplaceUnsignedWordMod(target, value)
|
||||
//target.dt.isFloat -> TODO("inplace float pointer mod ${target.position}")
|
||||
//target.dt.isLong -> TODO("inplace long pointer mod ${target.position}")
|
||||
else -> throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
}
|
||||
"%" -> TODO("inplace ptr % ${target.position}")
|
||||
"<<" -> {
|
||||
if(target.dt.isByte) inplaceByteShiftLeft(target, value)
|
||||
else if(target.dt.isWord) inplaceWordShiftLeft(target, value)
|
||||
else if(target.dt.isLong) TODO("inplace long << ${target.position}")
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
when {
|
||||
target.dt.isByte -> inplaceByteShiftLeft(target, value)
|
||||
target.dt.isWord -> inplaceWordShiftLeft(target, value)
|
||||
target.dt.isLong -> inplaceLongShiftLeft(target, value)
|
||||
else -> throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
}
|
||||
">>" -> {
|
||||
if(target.dt.isByte) inplaceByteShiftRight(target, value)
|
||||
else if(target.dt.isWord) inplaceWordShiftRight(target, value)
|
||||
else if(target.dt.isLong) TODO("inplace long >> ${target.position}")
|
||||
else throw AssemblyError("weird dt ${target.position}")
|
||||
when {
|
||||
target.dt.isByte -> inplaceByteShiftRight(target, value)
|
||||
target.dt.isWord -> inplaceWordShiftRight(target, value)
|
||||
target.dt.isLong -> inplaceLongShiftRight(target, value)
|
||||
else -> throw AssemblyError("weird dt ${target.position}")
|
||||
}
|
||||
}
|
||||
"&", "and" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
if(target.dt.isByteOrBool) inplaceByteAnd(target, value)
|
||||
else if(target.dt.isWord) inplaceWordAnd(target, value)
|
||||
else if(target.dt.isLong) inplaceLongAnd(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
when {
|
||||
target.dt.isByteOrBool -> inplaceByteAnd(target, value)
|
||||
target.dt.isWord -> inplaceWordAnd(target, value)
|
||||
target.dt.isLong -> inplaceLongAnd(target, value)
|
||||
else -> throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
}
|
||||
"|", "or" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
if(target.dt.isByteOrBool) inplaceByteOr(target, value)
|
||||
else if(target.dt.isWord) inplaceWordOr(target, value)
|
||||
else if(target.dt.isLong) inplaceLongOr(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
when {
|
||||
target.dt.isByteOrBool -> inplaceByteOr(target, value)
|
||||
target.dt.isWord -> inplaceWordOr(target, value)
|
||||
target.dt.isLong -> inplaceLongOr(target, value)
|
||||
else -> throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
}
|
||||
"^", "xor" -> {
|
||||
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
|
||||
if(target.dt.isByteOrBool) inplaceByteXor(target, value)
|
||||
else if(target.dt.isWord) inplaceWordXor(target, value)
|
||||
else if(target.dt.isLong) inplaceLongXor(target, value)
|
||||
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
when {
|
||||
target.dt.isByteOrBool -> inplaceByteXor(target, value)
|
||||
target.dt.isWord -> inplaceWordXor(target, value)
|
||||
target.dt.isLong -> inplaceLongXor(target, value)
|
||||
else -> throw AssemblyError("weird dt ${target.dt} ${target.position}")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
@@ -400,13 +424,15 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
if(target.index.type.isByte) {
|
||||
asmgen.pushCpuStack(BaseDataType.UBYTE, target.index)
|
||||
val (zpPtrVar, _) = deref(target.pointer, addOffsetToPointer = true)
|
||||
val (zpPtrVar, offset) = deref(target.pointer, addOffsetToPointer = true)
|
||||
require(offset==0.toUByte())
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" lda #$byte | sta ($zpPtrVar),y")
|
||||
}
|
||||
else {
|
||||
asmgen.pushCpuStack(BaseDataType.UWORD, target.index)
|
||||
val (zpPtrVar, _) = deref(target.pointer, addOffsetToPointer = true)
|
||||
val (zpPtrVar, offset) = deref(target.pointer, addOffsetToPointer = true)
|
||||
require(offset==0.toUByte())
|
||||
asmgen.out("""
|
||||
pla
|
||||
clc
|
||||
@@ -501,6 +527,189 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
sta ($ptrVar),y""")
|
||||
}
|
||||
|
||||
internal fun inplacemodificationBytePointerWithValue(pointervar: PtIdentifier, operator: String, value: PtExpression) {
|
||||
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
inplacemodificationBytePointerWithVariable(pointervar, operator, "P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
internal fun inplacemodificationBytePointerWithVariable(pointervar: PtIdentifier, operator: String, otherName: String) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
|
||||
when (operator) {
|
||||
"+" -> asmgen.out(" clc | adc $otherName")
|
||||
"-" -> asmgen.out(" sec | sbc $otherName")
|
||||
"*" -> asmgen.out(" ldy $otherName | jsr prog8_math.multiply_bytes")
|
||||
"/" -> asmgen.out(" ldy $otherName | jsr prog8_math.divmod_ub_asm | tya")
|
||||
"%" -> asmgen.out(" ldy $otherName | jsr prog8_math.remainder_ub_asm")
|
||||
"<<" -> {
|
||||
asmgen.out("""
|
||||
ldy $otherName
|
||||
beq +
|
||||
- asl a
|
||||
dey
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
">>" -> {
|
||||
asmgen.out("""
|
||||
ldy $otherName
|
||||
beq +
|
||||
- lsr a
|
||||
dey
|
||||
bne -
|
||||
+""")
|
||||
}
|
||||
"&" -> asmgen.out(" and $otherName")
|
||||
"|" -> asmgen.out(" ora $otherName")
|
||||
"^" -> asmgen.out(" eor $otherName")
|
||||
"==" -> {
|
||||
asmgen.out("""
|
||||
cmp $otherName
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
"!=" -> {
|
||||
asmgen.out("""
|
||||
cmp $otherName
|
||||
bne +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?
|
||||
"<", "<=", ">", ">=" -> TODO("byte-var-to-pointer comparisons ${pointervar.position}")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
|
||||
internal fun inplacemodificationBytePointerWithLiteralval(pointervar: PtIdentifier, operator: String, value: Int) {
|
||||
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
if(value==1) {
|
||||
if(asmgen.options.romable) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" inc a")
|
||||
else
|
||||
asmgen.out(" clc | adc #1")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out($$"+\tinc $ffff\t; modified")
|
||||
}
|
||||
} else {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" clc | adc #$value")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
if(value==1) {
|
||||
if(asmgen.options.romable) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
asmgen.out(" dec a")
|
||||
else
|
||||
asmgen.out(" sec | sbc #1")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(pointervar, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.out($$"+\tdec $ffff\t; modified")
|
||||
}
|
||||
} else {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" sec | sbc #$value")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
}
|
||||
"*" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(value in asmgen.optimizedByteMultiplications)
|
||||
asmgen.out(" jsr prog8_math.mul_byte_${value}")
|
||||
else
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.multiply_bytes")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"/" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(value==0)
|
||||
throw AssemblyError("division by zero")
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.divmod_ub_asm | tya")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"%" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
if(value==0)
|
||||
throw AssemblyError("division by zero")
|
||||
asmgen.out(" ldy #$value | jsr prog8_math.remainder_ub_asm")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"<<" -> {
|
||||
if (value > 0) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
repeat(value) { asmgen.out(" asl a") }
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
}
|
||||
">>" -> {
|
||||
if (value > 0) {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
repeat(value) { asmgen.out(" lsr a") }
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
}
|
||||
"&" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" and #$value")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"|"-> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" ora #$value")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"^" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out(" eor #$value")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"==" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out("""
|
||||
cmp #$value
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
"!=" -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(pointervar)
|
||||
asmgen.out("""
|
||||
cmp #$value
|
||||
bne +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
asmgen.storeAIntoZpPointerVar(sourceName, false)
|
||||
}
|
||||
// pretty uncommon, who's going to assign a comparison boolean expression to a pointer?:
|
||||
"<", "<=", ">", ">=" -> TODO("byte-litval-to-pointer comparisons ${pointervar.position}")
|
||||
else -> throw AssemblyError("invalid operator for in-place modification $operator")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal fun operatorDereference(binExpr: PtBinaryExpression): Triple<String, UByte, DataType> {
|
||||
// the only case we support here is: a.b.c[i] . value
|
||||
@@ -756,17 +965,24 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
asmgen.assignRegister(RegisterOrPair.AY, target)
|
||||
}
|
||||
|
||||
private fun inplaceWordShiftRight(target: PtrTarget, value: AsmAssignSource) {
|
||||
private fun inplaceLongShiftRight(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
if(target.dt.isSigned)
|
||||
TODO("signed word shift right ${target.position} $value")
|
||||
|
||||
fun shift1unsigned() {
|
||||
fun shift1signed() {
|
||||
asmgen.out("""
|
||||
ldy #${offset+1u}
|
||||
ldy #${offset}+3
|
||||
lda ($zpPtrVar),y
|
||||
lsr a
|
||||
asl a ; save sign bit
|
||||
lda ($zpPtrVar),y
|
||||
ror a
|
||||
sta ($zpPtrVar),y
|
||||
dey
|
||||
lda ($zpPtrVar),y
|
||||
ror a
|
||||
sta ($zpPtrVar),y
|
||||
dey
|
||||
lda ($zpPtrVar),y
|
||||
ror a
|
||||
sta ($zpPtrVar),y
|
||||
dey
|
||||
lda ($zpPtrVar),y
|
||||
@@ -778,11 +994,11 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number==1) {
|
||||
shift1unsigned()
|
||||
shift1signed()
|
||||
} else if(number>1) {
|
||||
asmgen.out(" ldx #$number")
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
shift1signed()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
}
|
||||
@@ -791,14 +1007,14 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
val varname = value.asmVarname
|
||||
asmgen.out(" ldx $varname")
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
shift1signed()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
shift1signed()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
@@ -806,13 +1022,125 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
shift1signed()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordShiftRight(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
if(target.dt.isSigned) {
|
||||
// signed word shift right
|
||||
fun shift1signed() {
|
||||
asmgen.out("""
|
||||
ldy #${offset+1u}
|
||||
lda ($zpPtrVar),y
|
||||
asl a ; save sign bit
|
||||
lda ($zpPtrVar),y
|
||||
ror a
|
||||
sta ($zpPtrVar),y
|
||||
dey
|
||||
lda ($zpPtrVar),y
|
||||
ror a
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number==1) {
|
||||
shift1signed()
|
||||
} else if(number>1) {
|
||||
asmgen.out(" ldx #$number")
|
||||
asmgen.out("-")
|
||||
shift1signed()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out(" ldx $varname")
|
||||
asmgen.out("-")
|
||||
shift1signed()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("-")
|
||||
shift1signed()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("-")
|
||||
shift1signed()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
|
||||
} else {
|
||||
// unsigned word shift right
|
||||
|
||||
fun shift1unsigned() {
|
||||
asmgen.out("""
|
||||
ldy #${offset+1u}
|
||||
lda ($zpPtrVar),y
|
||||
lsr a
|
||||
sta ($zpPtrVar),y
|
||||
dey
|
||||
lda ($zpPtrVar),y
|
||||
ror a
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number==1) {
|
||||
shift1unsigned()
|
||||
} else if(number>1) {
|
||||
asmgen.out(" ldx #$number")
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out(" ldx $varname")
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("-")
|
||||
shift1unsigned()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceByteShiftRight(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
@@ -878,6 +1206,68 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceLongShiftLeft(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
fun shift1() {
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
lda ($zpPtrVar),y
|
||||
asl a
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
rol a
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
rol a
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda ($zpPtrVar),y
|
||||
rol a
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number==1) {
|
||||
shift1()
|
||||
} else if(number>1) {
|
||||
asmgen.out(" ldx #$number")
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isByte)
|
||||
val varname = value.asmVarname
|
||||
asmgen.out(" ldx $varname")
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
require(value.datatype.isByte)
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.X)
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
require(value.datatype.isByte)
|
||||
val register = value.register!!
|
||||
asmgen.assignRegister(register, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE, null, target.position, register = RegisterOrPair.X))
|
||||
asmgen.out("-")
|
||||
shift1()
|
||||
asmgen.out(" dex | bne -")
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceWordShiftLeft(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer)
|
||||
|
||||
@@ -1040,7 +1430,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
// it's not an expression so no need to preserve R14:R15
|
||||
// it's not an expression so no need to preserve R14-R15
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, true)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
@@ -1110,7 +1500,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
// it's not an expression so no need to preserve R14:R15
|
||||
// it's not an expression so no need to preserve R14-R15
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, true)
|
||||
asmgen.out("""
|
||||
ldy #$offset
|
||||
@@ -1200,7 +1590,8 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
|
||||
private fun inplaceFloatAddOrMul(target: PtrTarget, floatoperation: String, value: AsmAssignSource) {
|
||||
require(floatoperation=="FADD" || floatoperation=="FMULT")
|
||||
val (zpPtrVar, _) = deref(target.pointer, addOffsetToPointer = true)
|
||||
val (zpPtrVar, offset) = deref(target.pointer, addOffsetToPointer = true)
|
||||
require(offset==0.toUByte())
|
||||
asmgen.out("""
|
||||
lda $zpPtrVar
|
||||
ldy $zpPtrVar+1
|
||||
@@ -1274,7 +1665,8 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
|
||||
private fun inplaceWordMul(target: PtrTarget, value: AsmAssignSource) {
|
||||
|
||||
val (zpPtrVar, _) = deref(target.pointer, addOffsetToPointer = true)
|
||||
val (zpPtrVar, offset) = deref(target.pointer, addOffsetToPointer = true)
|
||||
require(offset==0.toUByte())
|
||||
|
||||
fun multiply() {
|
||||
// on entry here: number placed in routine argument variable
|
||||
@@ -1325,10 +1717,11 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
}
|
||||
|
||||
private fun inplaceWordDiv(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, _) = deref(target.pointer, addOffsetToPointer = true)
|
||||
val (zpPtrVar, offset) = deref(target.pointer, addOffsetToPointer = true)
|
||||
require(offset==0.toUByte())
|
||||
|
||||
fun divide(signed: Boolean) {
|
||||
// on entry here: number placed in P8ZP_SCRATCH_PTR, divisor placed in AY
|
||||
// on entry here: number placed in P8ZP_SCRATCH_W1, divisor placed in AY
|
||||
if(signed) asmgen.out(" jsr prog8_math.divmod_w_asm")
|
||||
else asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
||||
asmgen.out("""
|
||||
@@ -1348,8 +1741,8 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
throw AssemblyError("divide by power of two should have been a shift $value.position")
|
||||
asmgen.loadIndirectWord(zpPtrVar, 0u)
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
sty P8ZP_SCRATCH_PTR+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<$number
|
||||
ldy #>$number""")
|
||||
divide(target.dt.isSigned)
|
||||
@@ -1357,21 +1750,102 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
TODO("inplace variable word divide ${target.position}")
|
||||
asmgen.loadIndirectWord(zpPtrVar, 0u)
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda $varname
|
||||
ldy $varname+1""")
|
||||
divide(target.dt.isSigned)
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
val register = value.register!!
|
||||
require(register.isWord())
|
||||
TODO("inplace register word divide ${target.position}")
|
||||
val regname = asmgen.asmSymbolName(register)
|
||||
asmgen.loadIndirectWord(zpPtrVar, 0u)
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda $regname
|
||||
ldy $regname+1""")
|
||||
divide(target.dt.isSigned)
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.loadIndirectWord(zpPtrVar, 0u)
|
||||
asmgen.out(" sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AY)
|
||||
divide(target.dt.isSigned)
|
||||
}
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceUnsignedWordMod(target: PtrTarget, value: AsmAssignSource) {
|
||||
val (zpPtrVar, offset) = deref(target.pointer, addOffsetToPointer = true)
|
||||
require(offset==0.toUByte())
|
||||
|
||||
fun modulus() {
|
||||
// on entry here: number placed in P8ZP_SCRATCH_W1, divisor placed in AY
|
||||
asmgen.out("""
|
||||
jsr prog8_math.divmod_uw_asm
|
||||
lda P8ZP_SCRATCH_W2
|
||||
ldy #0
|
||||
sta ($zpPtrVar),y
|
||||
iny
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta ($zpPtrVar),y""")
|
||||
}
|
||||
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val number = value.number!!.number.toInt()
|
||||
if(number in powersOfTwoInt)
|
||||
throw AssemblyError("divide by power of two should have been a shift $value.position")
|
||||
asmgen.loadIndirectWord(zpPtrVar, 0u)
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #<$number
|
||||
ldy #>$number""")
|
||||
modulus()
|
||||
}
|
||||
SourceStorageKind.VARIABLE -> {
|
||||
require(value.datatype.isWord)
|
||||
val varname = value.asmVarname
|
||||
asmgen.loadIndirectWord(zpPtrVar, 0u)
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda $varname
|
||||
ldy $varname+1""")
|
||||
modulus()
|
||||
}
|
||||
SourceStorageKind.REGISTER -> {
|
||||
val register = value.register!!
|
||||
require(register.isWord())
|
||||
val regname = asmgen.asmSymbolName(register)
|
||||
asmgen.loadIndirectWord(zpPtrVar, 0u)
|
||||
asmgen.out("""
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda $regname
|
||||
ldy $regname+1""")
|
||||
modulus()
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.loadIndirectWord(zpPtrVar, 0u)
|
||||
asmgen.out(" sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
|
||||
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.AY)
|
||||
modulus()
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> TODO("ptr / expr (word) ${target.position}")
|
||||
else -> throw AssemblyError("weird source value $value")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplaceFloatSubOrDiv(target: PtrTarget, floatoperation: String, value: AsmAssignSource) {
|
||||
require(floatoperation=="FSUB" || floatoperation=="FDIV")
|
||||
val (zpPtrVar, _) = deref(target.pointer, addOffsetToPointer = true)
|
||||
val (zpPtrVar, offset) = deref(target.pointer, addOffsetToPointer = true)
|
||||
require(offset==0.toUByte())
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> {
|
||||
val floatConst = allocator.getFloatAsmConst(value.number!!.number)
|
||||
|
||||
@@ -191,7 +191,10 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
"&=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.ANDR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"^=", "xor=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.XORR, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
"<<=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSLN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
">>=" -> addInstr(inplaceInstrs, IRInstruction(Opcode.LSRN, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
">>=" -> {
|
||||
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
|
||||
addInstr(inplaceInstrs, IRInstruction(opc, targetDt, reg1 = oldvalueReg, reg2 = operandTr.resultReg), null)
|
||||
}
|
||||
"or=" -> {
|
||||
val shortcutLabel = codeGen.createLabelName()
|
||||
addInstr(inplaceInstrs, IRInstruction(Opcode.BSTNE, labelSymbol = shortcutLabel), null)
|
||||
@@ -557,7 +560,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
if(constOffset in 0..65535) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
@@ -567,18 +570,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return result
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// STOREIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
expressionEval.translateExpression(offsetTypecast.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
val tr = expressionEval.translateExpression(memory!!.address)
|
||||
|
||||
@@ -406,7 +406,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
if(ptrWithOffset!=null) {
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
val constOffset = (ptrWithOffset.right as? PtNumber)?.number?.toInt()
|
||||
if(constOffset in 0..255) {
|
||||
if(constOffset in 0..65535) {
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
@@ -416,18 +416,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset.right as? PtTypeCast
|
||||
if(ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier && (ptrWithOffset.right.type.isByte || offsetTypecast?.value?.type?.isByte==true)) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type?.isByte==true)
|
||||
translateExpression(offsetTypecast.value)
|
||||
else
|
||||
translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=resultRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1)
|
||||
}
|
||||
}
|
||||
|
||||
val tr = translateExpression(mem.address)
|
||||
|
||||
@@ -55,6 +55,8 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
|| cleanupPushPop(chunk1, indexedInstructions)
|
||||
|| simplifyConstantReturns(chunk1, indexedInstructions)
|
||||
|| removeNeedlessLoads(chunk1, indexedInstructions)
|
||||
|| useArrayIndexingInsteadOfAdds(chunk1, indexedInstructions)
|
||||
|| removeNops(chunk1, indexedInstructions) // last time, in case one of the optimizers replaced something with a nop
|
||||
} while (changed)
|
||||
}
|
||||
}
|
||||
@@ -553,4 +555,40 @@ jump p8_label_gen_2
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
private fun useArrayIndexingInsteadOfAdds(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
|
||||
var changed = false
|
||||
indexedInstructions.reversed().forEach { (idx, ins) ->
|
||||
if (ins.opcode == Opcode.ADD && ins.immediate!=null && idx>0) {
|
||||
val load = indexedInstructions[idx-1].value
|
||||
if((load.opcode==Opcode.LOAD) && load.labelSymbol!=null) {
|
||||
val lastInstruction = indexedInstructions[idx+1].value
|
||||
if(lastInstruction.opcode==Opcode.LOADI) {
|
||||
val targetRegister = lastInstruction.reg1!!
|
||||
if(ins.reg1==lastInstruction.reg2!! && load.reg1==lastInstruction.reg2!!) {
|
||||
val loadm = IRInstruction(Opcode.LOADM, lastInstruction.type, reg1=targetRegister, labelSymbol = load.labelSymbol, symbolOffset = ins.immediate)
|
||||
chunk.instructions[idx-1] = loadm
|
||||
chunk.instructions.removeAt(idx+1)
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
} else if(lastInstruction.opcode==Opcode.STOREI) {
|
||||
val targetRegister = lastInstruction.reg1!!
|
||||
if(ins.reg1==lastInstruction.reg2!! && load.reg1==lastInstruction.reg2!!) {
|
||||
val valueLoad = indexedInstructions[idx-2].value
|
||||
if(valueLoad.opcode==Opcode.LOAD && valueLoad.reg1==targetRegister) {
|
||||
val storem = IRInstruction(Opcode.STOREM, lastInstruction.type, reg1=valueLoad.reg1, labelSymbol = load.labelSymbol, symbolOffset = ins.immediate)
|
||||
chunk.instructions[idx-1] = storem
|
||||
chunk.instructions.removeAt(idx+1)
|
||||
chunk.instructions.removeAt(idx)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ class UnusedCodeRemover(private val program: Program,
|
||||
|
||||
private fun reportUnreachable(stmt: Statement) {
|
||||
when(val next = stmt.nextSibling()) {
|
||||
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine -> {}
|
||||
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine, is Break, is Jump -> {}
|
||||
else -> errors.warn("unreachable code", next.position)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1140,6 +1140,16 @@ cx16 {
|
||||
&word r14s = $1bfc
|
||||
&word r15s = $1bfe
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $1be0
|
||||
&long r2r3sl = $1be4
|
||||
&long r4r5sl = $1be8
|
||||
&long r6r7sl = $1bec
|
||||
&long r8r9sl = $1bf0
|
||||
&long r10r11sl = $1bf4
|
||||
&long r12r13sl = $1bf8
|
||||
&long r14r15sl = $1bfc
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $1be0
|
||||
&ubyte r1L = $1be2
|
||||
|
||||
@@ -1150,6 +1150,16 @@ cx16 {
|
||||
&word r14s = $cffc
|
||||
&word r15s = $cffe
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $cfe0
|
||||
&long r2r3sl = $cfe4
|
||||
&long r4r5sl = $cfe8
|
||||
&long r6r7sl = $cfec
|
||||
&long r8r9sl = $cff0
|
||||
&long r10r11sl = $cff4
|
||||
&long r12r13sl = $cff8
|
||||
&long r14r15sl = $cffc
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $cfe0
|
||||
&ubyte r1L = $cfe2
|
||||
|
||||
@@ -244,6 +244,7 @@ io_error:
|
||||
; this version it only returns directory entries!
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
cbm.SETNAM(5, "$:*=d")
|
||||
diskio.lf_start_list.pattern_ptr = pattern_ptr
|
||||
goto diskio.lf_start_list.start_list_internal
|
||||
}
|
||||
|
||||
@@ -252,6 +253,7 @@ io_error:
|
||||
; this version only returns actual file entries!
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
cbm.SETNAM(5, "$:*=p")
|
||||
diskio.lf_start_list.pattern_ptr = pattern_ptr
|
||||
goto diskio.lf_start_list.start_list_internal
|
||||
}
|
||||
|
||||
|
||||
@@ -217,6 +217,16 @@ cx16 {
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $0002
|
||||
&long r2r3sl = $0006
|
||||
&long r4r5sl = $000a
|
||||
&long r6r7sl = $000e
|
||||
&long r8r9sl = $0012
|
||||
&long r10r11sl = $0016
|
||||
&long r12r13sl = $001a
|
||||
&long r14r15sl = $001e
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
|
||||
@@ -605,6 +605,16 @@ cx16 {
|
||||
&word r14s = $7ffc
|
||||
&word r15s = $7ffe
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $7fe0
|
||||
&long r2r3sl = $7fe4
|
||||
&long r4r5sl = $7fe8
|
||||
&long r6r7sl = $7fec
|
||||
&long r8r9sl = $7fe0
|
||||
&long r10r11sl = $7fe4
|
||||
&long r12r13sl = $7fe8
|
||||
&long r14r15sl = $7fec
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $7fe0
|
||||
&ubyte r1L = $7fe2
|
||||
|
||||
@@ -660,3 +660,31 @@ long_shiftrightX_inplace .proc
|
||||
bne -
|
||||
_end rts
|
||||
.pend
|
||||
|
||||
|
||||
compare_long_0_and_sign .proc
|
||||
; pointer to 32 bits signed long in P8ZP_SCRATCH_W1, carry Set=check NEGATIVE (<=0), carry Clear=check POSITIVE (>=0)
|
||||
; returns carry Set if comparison succeeded otherwise carry Clear
|
||||
ldy #0
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
ora (P8ZP_SCRATCH_W1),y
|
||||
beq _success
|
||||
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
bcc _checkpositive
|
||||
bmi _success
|
||||
bpl _fail
|
||||
|
||||
_checkpositive bpl _success
|
||||
|
||||
_fail clc
|
||||
rts
|
||||
|
||||
_success sec
|
||||
rts
|
||||
.pend
|
||||
@@ -222,6 +222,7 @@ io_error:
|
||||
; this version it only returns directory entries!
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
cbm.SETNAM(5, "$:*=c") ; on C64 (1581 diskdrive) the type for directories is CBM
|
||||
diskio.lf_start_list.pattern_ptr = pattern_ptr
|
||||
goto diskio.lf_start_list.start_list_internal
|
||||
}
|
||||
|
||||
@@ -230,6 +231,7 @@ io_error:
|
||||
; this version only returns actual file entries!
|
||||
; note: only a single iteration loop can be active at a time!
|
||||
cbm.SETNAM(5, "$:*=p")
|
||||
diskio.lf_start_list.pattern_ptr = pattern_ptr
|
||||
goto diskio.lf_start_list.start_list_internal
|
||||
}
|
||||
|
||||
|
||||
@@ -307,6 +307,16 @@ cx16 {
|
||||
&word r14s = $ff1e
|
||||
&word r15s = $ff20
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $ff02
|
||||
&long r2r3sl = $ff06
|
||||
&long r4r5sl = $ff0a
|
||||
&long r6r7sl = $ff0e
|
||||
&long r8r9sl = $ff12
|
||||
&long r10r11sl = $ff16
|
||||
&long r12r13sl = $ff1a
|
||||
&long r14r15sl = $ff1e
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $ff02
|
||||
&ubyte r1L = $ff04
|
||||
|
||||
@@ -2375,7 +2375,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(value.type==BaseDataType.FLOAT)
|
||||
err("integer value expected instead of float; possible loss of precision")
|
||||
val number=value.number
|
||||
if (number < -2147483647.0 || number > 2147483647.0)
|
||||
if (number < -2147483648.0 || number > 2147483647.0)
|
||||
return err("value '$number' out of range for long")
|
||||
}
|
||||
targetDt.isArray -> {
|
||||
|
||||
@@ -69,6 +69,13 @@ class AstPreprocessor(val program: Program,
|
||||
return super.before(string, parent)
|
||||
}
|
||||
|
||||
override fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||
if (parent is RangeExpression)
|
||||
return noModifications
|
||||
val constValue = expr.constValue(program) ?: return noModifications
|
||||
return listOf(IAstModification.ReplaceNode(expr, constValue, parent))
|
||||
}
|
||||
|
||||
override fun after(range: RangeExpression, parent: Node): Iterable<IAstModification> {
|
||||
// has to be done before the constant folding, otherwise certain checks there will fail on invalid range sizes
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
|
||||
@@ -38,9 +38,9 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
|
||||
test("testCharLitAsExtsubArg") {
|
||||
val platform = Cx16Target()
|
||||
val result = compileText(platform, false, """
|
||||
val result = compileText(platform, false, $$"""
|
||||
main {
|
||||
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
|
||||
extsub $FFD2 = chrout(ubyte ch @ A)
|
||||
sub start() {
|
||||
chrout('\n')
|
||||
}
|
||||
@@ -61,9 +61,9 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
|
||||
test("testCharVarAsExtsubArg") {
|
||||
val platform = Cx16Target()
|
||||
val result = compileText(platform, false, """
|
||||
val result = compileText(platform, false, $$"""
|
||||
main {
|
||||
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
|
||||
extsub $FFD2 = chrout(ubyte ch @ A)
|
||||
sub start() {
|
||||
ubyte ch = '\n'
|
||||
chrout(ch)
|
||||
@@ -96,9 +96,9 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
|
||||
test("testCharConstAsExtsubArg") {
|
||||
val platform = Cx16Target()
|
||||
val result = compileText(platform, false, """
|
||||
val result = compileText(platform, false, $$"""
|
||||
main {
|
||||
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
|
||||
extsub $FFD2 = chrout(ubyte ch @ A)
|
||||
sub start() {
|
||||
const ubyte ch = '\n'
|
||||
chrout(ch)
|
||||
|
||||
@@ -203,7 +203,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
|
||||
val bothCx16AndC64 = cartesianProduct(
|
||||
listOf(
|
||||
"animals",
|
||||
"balls",
|
||||
"cube3d",
|
||||
"cube3d-float",
|
||||
|
||||
@@ -7,10 +7,13 @@ import io.kotest.matchers.doubles.plusOrMinus
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import prog8.code.ast.PtAssignment
|
||||
import prog8.code.ast.PtNumber
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.core.toHex
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Mflpt5
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
@@ -225,4 +228,35 @@ class TestNumbers: FunSpec({
|
||||
errors.errors[4] shouldContain "out of range"
|
||||
errors.errors[5] shouldContain "byte doesn't match"
|
||||
}
|
||||
|
||||
|
||||
test("biggest long numbers") {
|
||||
val src= $$"""
|
||||
main {
|
||||
sub start() {
|
||||
long @shared l1, l2
|
||||
|
||||
l1 = $7fffffff
|
||||
l2 = $80000000
|
||||
l1 = -2147483648
|
||||
l2 = -2147483649 ; will be truncated
|
||||
l1 = $abcd1234
|
||||
l2 = -$7fffffff
|
||||
l1 = $80000001
|
||||
l2 = -$80
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
val result = compileText(VMTarget(), false, src, outputDir)!!
|
||||
val st = result.codegenAst!!.entrypoint()!!
|
||||
st.children.size shouldBe 13
|
||||
((st.children[4] as PtAssignment).value as PtNumber).number shouldBe 2147483647
|
||||
((st.children[5] as PtAssignment).value as PtNumber).number shouldBe -2147483648
|
||||
((st.children[6] as PtAssignment).value as PtNumber).number shouldBe -2147483648
|
||||
((st.children[7] as PtAssignment).value as PtNumber).number shouldBe 2147483647
|
||||
((st.children[8] as PtAssignment).value as PtNumber).number shouldBe -1412623820
|
||||
((st.children[9] as PtAssignment).value as PtNumber).number shouldBe -2147483647
|
||||
((st.children[10] as PtAssignment).value as PtNumber).number shouldBe -2147483647
|
||||
((st.children[11] as PtAssignment).value as PtNumber).number shouldBe -128
|
||||
}
|
||||
})
|
||||
@@ -502,5 +502,49 @@ main {
|
||||
((a[6].value as BinaryExpression).right as NumericLiteral).number shouldBe 0x7fffffffL.toDouble()
|
||||
}
|
||||
|
||||
test("test const sizeof type casting") {
|
||||
val src = """
|
||||
main {
|
||||
const uword LineSize = sizeof(uword)
|
||||
const uword BufferSize = LineSize ; or 400*LineSize
|
||||
|
||||
sub start () {
|
||||
}
|
||||
}"""
|
||||
compileText(Cx16Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
}
|
||||
|
||||
|
||||
test("const struct sizes and offsets, also available in assembly code") {
|
||||
val src = """
|
||||
main {
|
||||
|
||||
struct Node {
|
||||
ubyte type
|
||||
uword value
|
||||
bool flag
|
||||
}
|
||||
|
||||
sub start() {
|
||||
const ubyte size = sizeof(Node)
|
||||
const ubyte offset1 = offsetof(Node.type)
|
||||
const ubyte offset2 = offsetof(Node.value)
|
||||
const ubyte offset3 = offsetof(Node.flag)
|
||||
|
||||
%asm {{
|
||||
lda p8c_size
|
||||
lda p8c_offset1
|
||||
lda p8c_offset2
|
||||
lda p8c_offset3
|
||||
lda #size(p8t_Node)
|
||||
lda #p8t_Node.p8v_type
|
||||
lda #p8t_Node.p8v_value
|
||||
lda #p8t_Node.p8v_flag
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
compileText(Cx16Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
@@ -637,4 +637,40 @@ main{
|
||||
compileText(C64Target(), true, src, outputDir) shouldNotBe null
|
||||
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
test("symbol prefixing inconsistency with struct types and merged blocks") {
|
||||
// see https://github.com/irmen/prog8/issues/198
|
||||
val src="""
|
||||
plane {
|
||||
struct Point {
|
||||
ubyte x
|
||||
ubyte y
|
||||
}
|
||||
}
|
||||
|
||||
mytxt {
|
||||
%option no_symbol_prefixing
|
||||
|
||||
sub print_ub(ubyte value) {
|
||||
cx16.r0L++
|
||||
}
|
||||
}
|
||||
|
||||
mytxt {
|
||||
%option merge
|
||||
sub print_pt(^^plane.Point p) {
|
||||
mytxt.print_ub(p.x)
|
||||
mytxt.print_ub(p.y)
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
^^plane.Point origin = ^^plane.Point:[0,0]
|
||||
mytxt.print_pt(origin)
|
||||
}
|
||||
}"""
|
||||
|
||||
compileText(C64Target(), false, src, outputDir) shouldNotBe null
|
||||
}
|
||||
})
|
||||
@@ -347,7 +347,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
|
||||
in -128..127 -> BaseDataType.BYTE
|
||||
in 0..65535 -> BaseDataType.UWORD
|
||||
in -32768..32767 -> BaseDataType.WORD
|
||||
in -2147483647..2147483647 -> BaseDataType.LONG
|
||||
in -2147483648L..0xffffffffL -> BaseDataType.LONG // TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast (max value)
|
||||
else -> BaseDataType.FLOAT
|
||||
}
|
||||
}
|
||||
@@ -388,7 +388,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
|
||||
}
|
||||
|
||||
// TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast
|
||||
if(integer.second.isLong && integer.first > Integer.MAX_VALUE) {
|
||||
if(integer.second.isLong && integer.first > Integer.MAX_VALUE && integer.first <= 0xffffffff) {
|
||||
val signedLong = integer.first.toLong().toInt()
|
||||
return NumericLiteral(integer.second, signedLong.toDouble(), ctx.toPosition())
|
||||
}
|
||||
|
||||
@@ -687,8 +687,12 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
|
||||
in -128..127 -> NumericLiteral(BaseDataType.BYTE, dvalue, position)
|
||||
in 0..65535 -> NumericLiteral(BaseDataType.UWORD, dvalue, position)
|
||||
in -32768..32767 -> NumericLiteral(BaseDataType.WORD, dvalue, position)
|
||||
in -2147483647..2147483647 -> NumericLiteral(BaseDataType.LONG, dvalue, position)
|
||||
else -> NumericLiteral(BaseDataType.FLOAT, dvalue, position)
|
||||
else -> {
|
||||
if(dvalue in -2147483648.0 .. 2147483647.0)
|
||||
NumericLiteral(BaseDataType.LONG, dvalue, position)
|
||||
else
|
||||
NumericLiteral(BaseDataType.FLOAT, dvalue, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1538,6 +1542,7 @@ class FunctionCallExpression(override var target: IdentifierReference,
|
||||
return null
|
||||
|
||||
val resultValue: NumericLiteral? = program.builtinFunctions.constValue(target.nameInSource[0], args, position)
|
||||
resultValue?.parent=this.parent
|
||||
if(withDatatypeCheck) {
|
||||
val resultDt = this.inferType(program)
|
||||
if(resultValue==null || resultDt istype DataType.forDt(resultValue.type))
|
||||
|
||||
@@ -1030,9 +1030,9 @@ class Subroutine(override val name: String,
|
||||
replacement.parent = this
|
||||
}
|
||||
is NumericLiteral -> {
|
||||
if(node===asmAddress?.address) {
|
||||
if(node===asmAddress?.address || node==asmAddress?.address) {
|
||||
asmAddress.address = replacement
|
||||
} else if(node===asmAddress?.varbank) {
|
||||
} else if(node==asmAddress?.varbank) {
|
||||
asmAddress.constbank = replacement.number.toInt().toUByte()
|
||||
asmAddress.varbank = null
|
||||
} else throw FatalAstException("can't replace")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v12.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v12.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c128.p8
|
||||
@@ -646,6 +646,7 @@ cx16 {
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&long r0r1sl
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@@ -655,6 +656,7 @@ cx16 {
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&long r10r11sl
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
@@ -671,6 +673,7 @@ cx16 {
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&long r12r13sl
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
@@ -687,6 +690,7 @@ cx16 {
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&long r14r15sl
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
@@ -710,6 +714,7 @@ cx16 {
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&long r2r3sl
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
@@ -726,6 +731,7 @@ cx16 {
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&long r4r5sl
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
@@ -742,6 +748,7 @@ cx16 {
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&long r6r7sl
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
@@ -758,6 +765,7 @@ cx16 {
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&long r8r9sl
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v12.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v12.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c64.p8
|
||||
@@ -759,6 +759,7 @@ cx16 {
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&long r0r1sl
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@@ -768,6 +769,7 @@ cx16 {
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&long r10r11sl
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
@@ -784,6 +786,7 @@ cx16 {
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&long r12r13sl
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
@@ -800,6 +803,7 @@ cx16 {
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&long r14r15sl
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
@@ -823,6 +827,7 @@ cx16 {
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&long r2r3sl
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
@@ -839,6 +844,7 @@ cx16 {
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&long r4r5sl
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
@@ -855,6 +861,7 @@ cx16 {
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&long r6r7sl
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
@@ -871,6 +878,7 @@ cx16 {
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&long r8r9sl
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v12.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v12.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-cx16.p8
|
||||
@@ -960,6 +960,7 @@ cx16 {
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&long r0r1sl
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@@ -969,6 +970,7 @@ cx16 {
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&long r10r11sl
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
@@ -985,6 +987,7 @@ cx16 {
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&long r12r13sl
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
@@ -1001,6 +1004,7 @@ cx16 {
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&long r14r15sl
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
@@ -1024,6 +1028,7 @@ cx16 {
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&long r2r3sl
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
@@ -1040,6 +1045,7 @@ cx16 {
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&long r4r5sl
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
@@ -1056,6 +1062,7 @@ cx16 {
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&long r6r7sl
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
@@ -1072,6 +1079,7 @@ cx16 {
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&long r8r9sl
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v12.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v12.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-pet32.p8
|
||||
@@ -369,6 +369,7 @@ cx16 {
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&long r0r1sl
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@@ -378,6 +379,7 @@ cx16 {
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&long r10r11sl
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
@@ -394,6 +396,7 @@ cx16 {
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&long r12r13sl
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
@@ -410,6 +413,7 @@ cx16 {
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&long r14r15sl
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
@@ -433,6 +437,7 @@ cx16 {
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&long r2r3sl
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
@@ -449,6 +454,7 @@ cx16 {
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&long r4r5sl
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
@@ -465,6 +471,7 @@ cx16 {
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&long r6r7sl
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
@@ -481,6 +488,7 @@ cx16 {
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&long r8r9sl
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v12.0 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v12.0.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-virtual.p8
|
||||
@@ -404,6 +404,7 @@ cx16 {
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&long r0r1sl
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@@ -413,6 +414,7 @@ cx16 {
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&long r10r11sl
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
@@ -429,6 +431,7 @@ cx16 {
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&long r12r13sl
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
@@ -445,6 +448,7 @@ cx16 {
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&long r14r15sl
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
@@ -468,6 +472,7 @@ cx16 {
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&long r2r3sl
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
@@ -484,6 +489,7 @@ cx16 {
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&long r4r5sl
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
@@ -500,6 +506,7 @@ cx16 {
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&long r6r7sl
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
@@ -516,6 +523,7 @@ cx16 {
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&long r8r9sl
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
|
||||
@@ -1155,6 +1155,7 @@ so pay attention to any jumps and rts instructions in the inlined code!
|
||||
|
||||
- ``cx16.r0`` - ``cx16.r15`` (memory-mapped **uword** values, most often these are used)
|
||||
- ``cx16.r0s`` - ``cx16.r15s`` (memory-mapped **word** values, used when you need a signed word)
|
||||
- ``cx16.r0r1sl`` - ``cx16.r14r15sl`` (memory-mapped **long** values in 2 consecutive virtual registers, used when you need a signed long)
|
||||
- ``cx16.r0H``, ``cx16.r0L`` (for each r0..r15; memory-mapped **ubyte** values, both bytes of the register)
|
||||
- ``cx16.r0sH``, ``cx16.r0sL`` (for each r0..r15; memory-mapped **byte** values, both bytes of the register)
|
||||
- ``cx16.r0bH``, ``cx16.r0bL`` (for each r0..r15; memory-mapped **bool** values, both bytes of the register)
|
||||
|
||||
@@ -261,3 +261,40 @@ Address-Of: untyped vs typed
|
||||
``&`` still returns an untyped (uword) pointer, as it did in older Prog8 versions. This is for backward compatibility reasons so existing programs don't break.
|
||||
The new *double ampersand* operator ``&&`` returns a *typed* pointer to the value. The semantics are slightly different from the old untyped address-of operator, because adding or subtracting
|
||||
a number from a typed pointer uses *pointer arithmetic* that takes the size of the value that it points to into account.
|
||||
|
||||
|
||||
Accessing struct definitions in Assembly code
|
||||
---------------------------------------------
|
||||
|
||||
Prog8 lets you query the size of a struct type, and the offsets of a field.
|
||||
You can do the same in assembly code if needed: the struct definition gets written as `.struct` into the assembly file::
|
||||
|
||||
; prog8 struct declaration:
|
||||
struct Node {
|
||||
ubyte type
|
||||
uword value
|
||||
bool flag
|
||||
}
|
||||
|
||||
; generates this assembly code:
|
||||
; (the symbol prefixes are explained in the 'Technical details' chapter)
|
||||
p8b_main.p8t_Node .struct f0,f1,f2
|
||||
p8v_type .byte \f0
|
||||
p8v_value .word \f1
|
||||
p8v_flag .byte \f2
|
||||
.endstruct
|
||||
|
||||
|
||||
64tass then lets you query that information::
|
||||
|
||||
; prog8 code:
|
||||
ubyte size = sizeof(Node)
|
||||
ubyte offset1 = offsetof(Node.type)
|
||||
ubyte offset2 = offsetof(Node.value)
|
||||
ubyte offset3 = offsetof(Node.flag)
|
||||
|
||||
; assembly equivalents (64tass syntax):
|
||||
lda #size(p8t_Node)
|
||||
lda #p8t_Node.p8v_type
|
||||
lda #p8t_Node.p8v_value
|
||||
lda #p8t_Node.p8v_flag
|
||||
@@ -1,45 +1,41 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
|
||||
|
||||
Weird Heisenbug
|
||||
^^^^^^^^^^^^^^^
|
||||
- examples/cube3d-float crashes with div by zero error on C64 (works on cx16. ALready broken in v11, v10 still worked)
|
||||
- BUG: examples/cube3d-float crashes with div by zero error on C64 (works on cx16. ALready broken in v11, v10 still worked)
|
||||
caused by the RTS after JMP removal in optimizeJsrRtsAndOtherCombinations (replacing it with a NOP makes the problem disappear !??!?)
|
||||
|
||||
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- make $8000000 a valid long integer (-2147483648) this is more involved than you think. To make this work: long \|= $80000000
|
||||
- add cx16.r0r1sL, r2r3sL, ... etc to map signed longs on the virtual registers?
|
||||
- implement rest of long comparisons in IfElseAsmGen compareLongValues(): expressions operands that might clobber the R14-R15 registers...
|
||||
- BUG: structs: properly fix the symbol name prefix hack in StStruct.sameas(), see github issue 198
|
||||
- when implementing unsigned longs: remove the (multiple) "TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast"
|
||||
- struct/ptr: support const pointers (simple and struct types) (make sure to change codegen properly in all cases, change remark about this limitation in docs too)
|
||||
- struct/ptr: implement the remaining TODOs in PointerAssignmentsGen.
|
||||
- struct/ptr: optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
|
||||
- struct/ptr: optimize the float copying in assignIndexedPointer() (also word and long?)
|
||||
- struct/ptr: optimize augmented assignments to indexed pointer targets like sprptr[2]^^.y++ (these are now not performend in-place but as a regular assignment)
|
||||
- struct/ptr: implement even more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs')
|
||||
- struct/ptr: support const pointers (simple and struct types) (make sure to change codegen properly in all cases, change remark about this limitation in docs too)
|
||||
- struct/ptr: support @nosplit pointer arrays?
|
||||
- struct/ptr: support pointer to pointer?
|
||||
- struct/ptr: support for typed function pointers? (&routine could be typed by default as well then)
|
||||
- struct/ptr: really fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator"
|
||||
- struct/ptr: (later, nasty parser problem:) support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
|
||||
- array-as-param bug: printf([1111,2,3,-4444]) gives argument type mismatch (param is of type uword), while printf([1111,2,3,4444]) just works fine (passes address of @nosplit array)
|
||||
- should we have a SourceStorageKind.POINTER? (there is one for TargetStorageKind...)
|
||||
- make memory mapped variables support more constant expressions such as: &uword MyHigh = &mylong1+2
|
||||
- allow memory() to occur in array initializer (maybe needed for 2 dimensional arrays?) i.e. make it a constant (see github issue #192)
|
||||
- handle Alias in a general way in LiteralsToAutoVarsAndRecombineIdentifiers instead of replacing it scattered over multiple functions
|
||||
- After long variable type is completed: make all constants long by default? or not? (remove type name altogether), reduce to target type implictly if the actual value fits.
|
||||
- Make all constants long by default? or not? (remove type name altogether), reduce to target type implictly if the actual value fits. Experiment is in branch 'long-consts'
|
||||
This will break some existing programs that depend on value wrap arounds, but gives more intuitive constant number handling.
|
||||
Can give descriptive error message for old syntax that still includes the type name?
|
||||
- improve ANTLR grammar with better error handling (as suggested by Qwen AI)
|
||||
- add documentation for more library modules instead of just linking to the source code
|
||||
- add an Index to the documentation
|
||||
- sizeof(pointer) is now always 2 (an uword), make this a variable in the ICompilationTarget so that it could be 4 at the time we might ad a 32-bits 68000 target for example.
|
||||
- sizeof(pointer) is now always 2 (an uword), make this a variable in the ICompilationTarget so that it could be 4 at the time we might ad a 32-bits 68000 target for example. Much code assumes word size addresses though.
|
||||
- Two- or even multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays?
|
||||
- when a complete block is removed because unused, suppress all info messages about everything in the block being removed
|
||||
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
|
||||
- fix the c64 multiplexer example
|
||||
- romable: should we have a way to explicitly set the memory address for the BSS area (add a -varsaddress and -slabsaddress options?)
|
||||
- romable: fix remaining codegens (some for loops, see ForLoopsAsmGen)
|
||||
- Kotlin: can we use inline value classes in certain spots? (domain types instead of primitives)
|
||||
@@ -59,16 +55,19 @@ Future Things and Ideas
|
||||
- more support for (64tass) SEGMENTS in the prog8 syntax itself? maybe %segment blah in blocks?
|
||||
- ability to use a sub instead of only a var for @bank ? what for though? dynamic bank/overlay loading?
|
||||
- enums?
|
||||
- BUG: fix the c64 multiplexer example
|
||||
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that) But the V flag is also set on certain normal instructions
|
||||
|
||||
|
||||
IR/VM
|
||||
-----
|
||||
- optimize float<0 float==0 float>0 to use SGN instruction? Check what code is generated for other data types.
|
||||
- extend the index register datatype in the LOADX, STOREX, STOREZX instructions from byte to word (0-255 to 0-65535) (this not compatible with 8 bit 6502, but the 68000 can use that)
|
||||
- get rid of LOADX/STOREX/STOREZX, LOADFIELD/STOREFIELD just use add + loadi / storei?
|
||||
- if float<0 / if word<0 uses sgn or load, but still use a bgt etc instruction after that with a #0 operand even though the sgn and load instructions sets the status bits already, so just use bstneg etc
|
||||
- add and sub instructions should modify the status flags so an explicit compare to zero can be avoided for example: if cx16.r0sL + cx16.r1sL <= 0 now compiles into: addr.b r10,r11 / bgts.b r10,#0,label
|
||||
- getting it in shape for code generation: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
|
||||
- fix call() return value handling (... what's wrong with it again?)
|
||||
- BUG: fix call() return value handling (... what's wrong with it again?)
|
||||
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
|
||||
- is it possible to use LOADFIELD/STOREFIELD instructions even more?
|
||||
- make multiple classes of registers and maybe also categorize by life time , to prepare for better register allocation in the future
|
||||
SYSCALL_ARGS, // Reserved for syscall arguments (r99000-99099, r99100-99199)
|
||||
FUNCTION_PARAMS, // For passing function parameters
|
||||
@@ -90,8 +89,8 @@ IR/VM
|
||||
- byte, word, long, float registers
|
||||
|
||||
- pointer dt's are all reduced to just an uword (in the irTypeString method) - is this okay or could it be beneficial to reintroduce the actual pointer type information? See commit 88b074c208450c58aa32469745afa03e4c5f564a
|
||||
- change the instruction format so an indirect register (a pointer) can be used more often, at least for the inplace assignment operators that operate on pointer
|
||||
- register reuse to reduce the number of required variables in memory eventually. But can only re-use a register if a) it's the same type and b) if the second occurrence is not called from the first occurrence (otherwise the value gets overwritten!)
|
||||
- change the instruction format so an indirect register (a pointer) can be used more often, at least for the inplace assignment operators that operate on pointer. Breaks SSA form though?
|
||||
- register reuse to reduce the number of required variables in memory eventually. But can only re-use a register if a) it's the same type and b) if the second occurrence is not called from the first occurrence (otherwise the value gets overwritten!) Breaks SSA form though?
|
||||
- reduce register usage via linear-scan algorithm (based on live intervals) https://anoopsarkar.github.io/compilers-class/assets/lectures/opt3-regalloc-linearscan.pdf
|
||||
don't forget to take into account the data type of the register when it's going to be reused!
|
||||
- encode asmsub/extsub clobber info in the call , or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction)
|
||||
@@ -99,7 +98,6 @@ IR/VM
|
||||
- implement more TODOs in AssignmentGen
|
||||
- do something with the 'split' tag on split word arrays
|
||||
- add more optimizations in IRPeepholeOptimizer, implement the TODOs in there at least
|
||||
- extend the index range from 0-255 to 0-32767 in the LOADX, STOREX, LOADFIELD, STOREFIELD etc instructions (not compatible with 8 bit 6502, but the 68000 can use that)
|
||||
- idea: replace all scalar variables that are not @shared by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||
global initialization values are simply a list of LOAD instructions.
|
||||
Variables replaced include all subroutine parameters? Or not? So the only variables that remain as variables are arrays and strings.
|
||||
@@ -119,6 +117,7 @@ Libraries
|
||||
Optimizations
|
||||
-------------
|
||||
|
||||
- peek(address + offset) and poke(address + offset, value) generate quite large asm segments. Optimize into subroutines for "indexed memory peek/pokes"?
|
||||
- (6502) optimize if sgn(value)<0: still does a compare with 0 even though SGN sets all status bits.
|
||||
- longvar = lptr^^ , lptr2^^=lptr^^ now go via temporary registers, optimize this to avoid using temps. (seems like it is dereferencing the pointer first and then assigning the intermediate value)
|
||||
- optimize inplaceLongShiftRight() for byte aligned cases
|
||||
|
||||
@@ -225,7 +225,7 @@ value datatype
|
||||
0 .. 255 ubyte
|
||||
-32768 .. 32767 word
|
||||
0 .. 65535 uword
|
||||
-2147483647 .. 2147483647 long (there is no unsigned long right now)
|
||||
-2147483648 .. 2147483647 long (there is no unsigned long right now)
|
||||
========================= =================
|
||||
|
||||
Numeric expressions usually 'stay within their type' unless a cast is used, see :ref:`arithmetic`.
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
%import textio
|
||||
%import strings
|
||||
|
||||
; Animal guessing game where the computer gets smarter every time.
|
||||
; Note: this program can be compiled for multiple target systems.
|
||||
|
||||
main {
|
||||
const ubyte database_size = 100
|
||||
|
||||
uword animal_names_buf = memory("animalnames", 500, 0) ; area to store all animal names in, in sequence
|
||||
uword questions_buf = memory("questions", 2000, 0) ; area to store all question texts in, in sequence
|
||||
uword animal_names_ptr
|
||||
uword questions_ptr
|
||||
|
||||
uword[database_size] animals ; pointers to the animal names
|
||||
uword[database_size] questions ; pointers to the question texts
|
||||
uword[database_size] answers_questions ; tree entries for question choices, indexed by question id, pair of (msb=yes, lsb=no) follow up question id (or 0 if it's an animal leaf node)
|
||||
uword[database_size] answers_animals ; tree entries for animal leafs, indexed by question id, pair of (msb=yes, lsb=no) animal id
|
||||
|
||||
ubyte new_animal_number
|
||||
ubyte new_question_number
|
||||
str userinput = "x"*80
|
||||
|
||||
sub start() {
|
||||
; initialize the database
|
||||
animal_names_ptr = animal_names_buf
|
||||
questions_ptr = questions_buf
|
||||
|
||||
animals[0] = 0
|
||||
animals[1] = "dolphin"
|
||||
animals[2] = "eagle"
|
||||
animals[3] = "horse"
|
||||
new_animal_number = 4
|
||||
|
||||
questions[0] = 0
|
||||
questions[1] = "does it swim"
|
||||
questions[2] = "can it fly"
|
||||
new_question_number = 3
|
||||
|
||||
answers_questions[0] = mkword(0, 0)
|
||||
answers_questions[1] = mkword(0, 2)
|
||||
answers_questions[2] = mkword(0, 0)
|
||||
|
||||
answers_animals[0] = mkword(0, 0)
|
||||
answers_animals[1] = mkword(1, 0)
|
||||
answers_animals[2] = mkword(2, 3)
|
||||
|
||||
; play the game
|
||||
game()
|
||||
}
|
||||
|
||||
sub game() {
|
||||
repeat {
|
||||
ubyte current_question = 1
|
||||
txt.print("\n\nanimal guessing game!\nthink of an animal.\n")
|
||||
bool guessed = false
|
||||
while not guessed {
|
||||
txt.print(questions[current_question])
|
||||
txt.print("? ")
|
||||
if txt.input_chars(userinput)!=0 {
|
||||
txt.nl()
|
||||
ubyte animal_number
|
||||
if userinput[0]=='y' {
|
||||
animal_number = msb(answers_animals[current_question])
|
||||
if animal_number!=0 {
|
||||
guess(current_question, true, animal_number)
|
||||
guessed = true
|
||||
} else {
|
||||
current_question = msb(answers_questions[current_question])
|
||||
}
|
||||
}
|
||||
else if userinput[0]=='n' {
|
||||
animal_number = lsb(answers_animals[current_question])
|
||||
if animal_number!=0 {
|
||||
guess(current_question, false, animal_number)
|
||||
guessed = true
|
||||
} else {
|
||||
current_question = lsb(answers_questions[current_question])
|
||||
}
|
||||
}
|
||||
else {
|
||||
txt.print("answer (y)es or (n)o please.\n")
|
||||
}
|
||||
} else
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub guess(ubyte question_number, bool given_answer_yesno, ubyte animal_number) {
|
||||
txt.print("is it a ")
|
||||
txt.print(animals[animal_number])
|
||||
txt.print("? ")
|
||||
void txt.input_chars(userinput)
|
||||
if userinput[0] == 'y' {
|
||||
txt.print("\n\nsee, i knew it!\n")
|
||||
return
|
||||
}
|
||||
|
||||
str name = "x"*30
|
||||
txt.print("\n\ni give up. what is it? ")
|
||||
void txt.input_chars(name)
|
||||
txt.print("\nwhat yes-no question would best articulate the difference\nbetween a ")
|
||||
txt.print(animals[animal_number])
|
||||
txt.print(" and a ")
|
||||
txt.print(name)
|
||||
txt.print("? ")
|
||||
void txt.input_chars(userinput)
|
||||
txt.print("\nin case of the ")
|
||||
txt.print(name)
|
||||
txt.print(", what is the answer to that question? ")
|
||||
str answer = "x"*10
|
||||
void txt.input_chars(answer)
|
||||
|
||||
animals[new_animal_number] = animal_names_ptr
|
||||
questions[new_question_number] = questions_ptr
|
||||
animal_names_ptr += strings.copy(name, animal_names_ptr)+1 ; store animal name in buffer
|
||||
questions_ptr += strings.copy(userinput, questions_ptr)+1 ; store question in buffer
|
||||
|
||||
answers_questions[new_question_number] = mkword(0, 0)
|
||||
if answer[0]=='y'
|
||||
answers_animals[new_question_number] = mkword(new_animal_number, animal_number)
|
||||
else
|
||||
answers_animals[new_question_number] = mkword(animal_number, new_animal_number)
|
||||
|
||||
uword previous_animals = answers_animals[question_number]
|
||||
uword previous_questions = answers_questions[question_number]
|
||||
if given_answer_yesno {
|
||||
answers_animals[question_number] = mkword(0, lsb(previous_animals))
|
||||
answers_questions[question_number] = mkword(new_question_number, lsb(previous_questions))
|
||||
} else {
|
||||
answers_animals[question_number] = mkword(msb(previous_animals), 0)
|
||||
answers_questions[question_number] = mkword(msb(previous_questions), new_question_number)
|
||||
}
|
||||
|
||||
new_animal_number++
|
||||
new_question_number++
|
||||
|
||||
txt.print("\n\nthanks, i know more animals now! let's try again.\n")
|
||||
}
|
||||
}
|
||||
@@ -482,6 +482,16 @@ cx16 {
|
||||
&word r14s = $1b1c
|
||||
&word r15s = $1b1e
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $1b00
|
||||
&long r2r3sl = $1b04
|
||||
&long r4r5sl = $1b08
|
||||
&long r6r7sl = $1b0c
|
||||
&long r8r9sl = $1b00
|
||||
&long r10r11sl = $1b04
|
||||
&long r12r13sl = $1b08
|
||||
&long r14r15sl = $1b0c
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $1b00
|
||||
&ubyte r1L = $1b02
|
||||
|
||||
@@ -741,6 +741,16 @@ cx16 {
|
||||
&word r14s = $002c
|
||||
&word r15s = $002e
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $0010
|
||||
&long r2r3sl = $0014
|
||||
&long r4r5sl = $0018
|
||||
&long r6r7sl = $001c
|
||||
&long r8r9sl = $0020
|
||||
&long r10r11sl = $0024
|
||||
&long r12r13sl = $0028
|
||||
&long r14r15sl = $002c
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0010
|
||||
&ubyte r1L = $0012
|
||||
|
||||
@@ -414,6 +414,16 @@ cx16 {
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $0002
|
||||
&long r2r3sl = $0006
|
||||
&long r4r5sl = $000a
|
||||
&long r6r7sl = $000e
|
||||
&long r8r9sl = $0012
|
||||
&long r10r11sl = $0016
|
||||
&long r12r13sl = $001a
|
||||
&long r14r15sl = $001e
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
|
||||
@@ -203,6 +203,16 @@ cx16 {
|
||||
&word r14s = $cffc
|
||||
&word r15s = $cffe
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $cfe0
|
||||
&long r2r3sl = $cfe4
|
||||
&long r4r5sl = $cfe8
|
||||
&long r6r7sl = $cfec
|
||||
&long r8r9sl = $cff0
|
||||
&long r10r11sl = $cff4
|
||||
&long r12r13sl = $cff8
|
||||
&long r14r15sl = $cffc
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $cfe0
|
||||
&ubyte r1L = $cfe2
|
||||
|
||||
@@ -203,6 +203,16 @@ cx16 {
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $0002
|
||||
&long r2r3sl = $0006
|
||||
&long r4r5sl = $000a
|
||||
&long r6r7sl = $000e
|
||||
&long r8r9sl = $0012
|
||||
&long r10r11sl = $0016
|
||||
&long r12r13sl = $001a
|
||||
&long r14r15sl = $001e
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
|
||||
@@ -199,6 +199,16 @@ cx16 {
|
||||
&word r14s = $7ffc
|
||||
&word r15s = $7ffe
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $7fe0
|
||||
&long r2r3sl = $7fe4
|
||||
&long r4r5sl = $7fe8
|
||||
&long r6r7sl = $7fec
|
||||
&long r8r9sl = $7fe0
|
||||
&long r10r11sl = $7fe4
|
||||
&long r12r13sl = $7fe8
|
||||
&long r14r15sl = $7fec
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $7fe0
|
||||
&ubyte r1L = $7fe2
|
||||
|
||||
@@ -13,6 +13,8 @@ adpcm {
|
||||
; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav
|
||||
; And/or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set the correct block size, -b8)
|
||||
;
|
||||
; NOTE: sox may generate IMA-ADPCM files with a block size different than 256 bytes, which is not supported by this decoder. Use ffmpeg instead.
|
||||
;
|
||||
; NOTE: for speed reasons this implementation doesn't guard against clipping errors.
|
||||
; if the output sounds distorted, lower the volume of the source waveform to 80% and try again etc.
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav
|
||||
; Or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set correct block size)
|
||||
;
|
||||
; NOTE: sox may generate IMA-ADPCM files with a block size different than 256 bytes, which is not supported by this decoder. Use ffmpeg instead.
|
||||
;
|
||||
|
||||
main {
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ main {
|
||||
ubyte vera_rate
|
||||
|
||||
sub start() {
|
||||
;; diskio.fastmode(1)
|
||||
diskio.fastmode(1)
|
||||
txt.print("name of .wav file to play on drive 8: ")
|
||||
while 0==txt.input_chars(MUSIC_FILENAME) {
|
||||
; until user types a name...
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
%import sprites
|
||||
%option no_sysinit
|
||||
|
||||
; play a raw pcm stereo 16 bit audio file at 16021 hz,
|
||||
; play a raw pcm stereo 16 bit audio file at 14877 hz,
|
||||
; with real-time VU meters and sample waveform displays.
|
||||
|
||||
; you can make an appropriate pcm file with one of the following commands:
|
||||
; ffmpeg -i input_audio_file -ac 2 -ar 16021 -f s16le -acodec pcm_s16le music.pcm
|
||||
; sox input_audio_file -e signed-integer -L -b 16 -c 2 -r 16021 -t raw music.pcm
|
||||
; ffmpeg -i input_audio_file -ac 2 -ar 14877 -f s16le -acodec pcm_s16le music.pcm
|
||||
; sox input_audio_file -e signed-integer -L -b 16 -c 2 -r 14877 -t raw music.pcm
|
||||
|
||||
|
||||
main {
|
||||
|
||||
const ubyte vera_rate = 42 ; 16021 hz
|
||||
const ubyte vera_rate = 39 ; 14877 hz
|
||||
str pcmfile = "music.pcm" ; see format specs mentioned above
|
||||
|
||||
sub start() {
|
||||
@@ -34,7 +34,7 @@ main {
|
||||
cx16.GRAPH_put_next_char(cx16.r9L)
|
||||
cx16.r0 = 250
|
||||
cx16.r1 = 20
|
||||
for cx16.r9L in iso:"16kHz stereo"
|
||||
for cx16.r9L in iso:"15kHz stereo"
|
||||
cx16.GRAPH_put_next_char(cx16.r9L)
|
||||
|
||||
cx16.rombank(0) ; activate kernal bank for faster calls
|
||||
|
||||
@@ -8,7 +8,7 @@ Evolution of Zerobyte's ZSOUND, by MooingLemur. Read the README there!
|
||||
|
||||
INCLUDED ZSMKIT BIN
|
||||
-------------------
|
||||
The ZSMKIT-A000.BIN included here is release version 2.6 of zsmkit.
|
||||
The ZSMKIT-A000.BIN included here is release version 2.8 of zsmkit.
|
||||
|
||||
|
||||
DEMO PROGRAM
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
;
|
||||
; Exercise for the reader to store the memory containing the nodes and strings and reload it on new runs...?
|
||||
|
||||
; TODO: remove the examples/animals.p8 once this one compiles for 6502 too
|
||||
|
||||
%import textio
|
||||
%import strings
|
||||
|
||||
|
||||
@@ -1,91 +1,53 @@
|
||||
%import floats
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
float @shared fv
|
||||
^^ubyte @shared ubptr = 2000
|
||||
uword @shared ubptr2 = 3000
|
||||
|
||||
fv = -22
|
||||
compares()
|
||||
fv=0
|
||||
compares()
|
||||
fv=33
|
||||
compares()
|
||||
txt.nl()
|
||||
init()
|
||||
reads()
|
||||
prints()
|
||||
writes()
|
||||
reads()
|
||||
prints()
|
||||
|
||||
fv = -22
|
||||
signs()
|
||||
fv=0
|
||||
signs()
|
||||
fv=33
|
||||
signs()
|
||||
|
||||
sub compares() {
|
||||
txt.print("compares\n")
|
||||
if fv==0
|
||||
txt.print(" ==0\n")
|
||||
if fv<=0
|
||||
txt.print(" <=0\n")
|
||||
if fv<0
|
||||
txt.print(" <0\n")
|
||||
if fv>=0
|
||||
txt.print(" >=0\n")
|
||||
if fv>0
|
||||
txt.print(" >0\n")
|
||||
sub init() {
|
||||
@(2000) = 100
|
||||
@(2001) = 101
|
||||
@(2002) = 102
|
||||
@(2500) = 109
|
||||
@(3000) = 200
|
||||
@(3001) = 201
|
||||
@(3002) = 202
|
||||
@(3500) = 209
|
||||
}
|
||||
|
||||
sub signs() {
|
||||
txt.print("signs\n")
|
||||
if sgn(fv)==0
|
||||
txt.print(" ==0\n")
|
||||
if sgn(fv)<=0
|
||||
txt.print(" <=0\n")
|
||||
if sgn(fv)<0
|
||||
txt.print(" <0\n")
|
||||
if sgn(fv)>=0
|
||||
txt.print(" >=0\n")
|
||||
if sgn(fv)>0
|
||||
txt.print(" >0\n")
|
||||
sub prints() {
|
||||
txt.print_ub(cx16.r0L)
|
||||
txt.spc()
|
||||
txt.print_ub(cx16.r1L)
|
||||
txt.spc()
|
||||
txt.print_ub(cx16.r2L)
|
||||
txt.spc()
|
||||
txt.print_ub(cx16.r3L)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub reads() {
|
||||
cx16.r0L = ubptr^^
|
||||
cx16.r1L = @(ubptr2)
|
||||
cx16.r2L = @(ubptr2+1)
|
||||
cx16.r3L = @(ubptr2+500)
|
||||
}
|
||||
|
||||
sub writes() {
|
||||
ubptr^^ = 55
|
||||
@(ubptr2) = 66
|
||||
@(ubptr2+1) = 77
|
||||
@(ubptr2+500) = 88
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
sub start2() {
|
||||
uword uw
|
||||
word sw
|
||||
long lv
|
||||
float fl
|
||||
|
||||
|
||||
uw = 44555
|
||||
fl = uw as float
|
||||
txt.print_f(fl)
|
||||
txt.nl()
|
||||
fl /= 2
|
||||
uw = fl as uword
|
||||
txt.print_uw(uw)
|
||||
txt.nl()
|
||||
|
||||
sw = -8888
|
||||
fl = sw as float
|
||||
txt.print_f(fl)
|
||||
txt.nl()
|
||||
fl /= 2
|
||||
sw = fl as word
|
||||
txt.print_w(sw)
|
||||
txt.nl()
|
||||
|
||||
lv = -99886666
|
||||
fl = lv as float
|
||||
txt.print_f(fl)
|
||||
txt.nl()
|
||||
fl /= 2
|
||||
lv = fl as long
|
||||
txt.print_l(lv)
|
||||
txt.nl()
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -4,4 +4,4 @@ org.gradle.parallel=true
|
||||
org.gradle.daemon=true
|
||||
org.gradle.configuration-cache=false
|
||||
kotlin.code.style=official
|
||||
version=12.0
|
||||
version=12.0.1
|
||||
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -272,7 +272,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
dt.isBool -> constant.value.toInt().toString()
|
||||
dt.isFloat -> constant.value.toString()
|
||||
dt.isPointer -> TODO("constant pointer $constant")
|
||||
dt.isInteger -> constant.value.toInt().toHex()
|
||||
dt.isInteger -> constant.value.toLong().toHex()
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
xml.writeCharacters("${constant.typeString} ${constant.name}=$value\n")
|
||||
|
||||
@@ -44,9 +44,8 @@ load reg1, value - load immediate value into register. If y
|
||||
loadm reg1, address - load reg1 with value at memory address
|
||||
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
|
||||
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (0-255, a byte)
|
||||
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
|
||||
loadr reg1, reg2 - load reg1 with value in register reg2, "reg1 = reg2"
|
||||
loadfield reg1, reg2, value - load reg1 with value in memory pointed to by reg2 + value 0-255 (gets a field from a pointer to a struct, like LOADI, but with additional field offset 0-255)
|
||||
loadfield reg1, reg2, value - load reg1 with value in memory indirect, pointed to by reg2 + value 0-65535 (gets a field from a pointer to a struct, like LOADI with additional field offset 0-65535)
|
||||
loadha reg1 - load cpu hardware register A into reg1.b
|
||||
loadhx reg1 - load cpu hardware register X into reg1.b
|
||||
loadhy reg1 - load cpu hardware register Y into reg1.b
|
||||
@@ -58,11 +57,10 @@ loadhfacone fpreg1 - load "cpu hardware register" fac1 into f
|
||||
storem reg1, address - store reg1 at memory address
|
||||
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
|
||||
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (0-255, a byte)
|
||||
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
|
||||
storezm address - store zero at memory address
|
||||
storezi reg1 - store zero at memory pointed to by reg1
|
||||
storezx reg1, address - store zero at memory address, indexed by value in reg1 (0-255, a byte)
|
||||
storefield reg1, reg2, value - store reg1 in memory pointed to by reg2 + value 0-255 (set a field from a pointer to a struct, like STOREI, but with additional field offset 0-255)
|
||||
storefield reg1, reg2, value - store reg1 in memory indirect, pointed to by reg2 + value 0-65535 (set a field from a pointer to a struct, like STOREI with additional field offset 0-65535)
|
||||
storeha reg1 - store reg1.b into cpu hardware register A
|
||||
storehx reg1 - store reg1.b into cpu hardware register X
|
||||
storehy reg1 - store reg1.b into cpu hardware register Y
|
||||
@@ -267,7 +265,6 @@ enum class Opcode {
|
||||
LOADM,
|
||||
LOADI, // the only opcode that allows r1 and r2 to be the same; because this saves a lot of intermediary registers and loads to dereference a pointer chain
|
||||
LOADX,
|
||||
LOADIX,
|
||||
LOADR,
|
||||
LOADHA,
|
||||
LOADHX,
|
||||
@@ -281,7 +278,6 @@ enum class Opcode {
|
||||
STOREM,
|
||||
STOREI,
|
||||
STOREX,
|
||||
STOREIX,
|
||||
STOREZM,
|
||||
STOREZI,
|
||||
STOREZX,
|
||||
@@ -506,7 +502,6 @@ val OpcodesThatSetStatusbitsButNotCarry = arrayOf(
|
||||
Opcode.LOADM,
|
||||
Opcode.LOADI,
|
||||
Opcode.LOADX,
|
||||
Opcode.LOADIX,
|
||||
Opcode.LOADR,
|
||||
Opcode.LOADHA,
|
||||
Opcode.LOADHX,
|
||||
@@ -553,7 +548,6 @@ val OpcodesThatLoad = arrayOf(
|
||||
Opcode.LOADM,
|
||||
Opcode.LOADI,
|
||||
Opcode.LOADX,
|
||||
Opcode.LOADIX,
|
||||
Opcode.LOADR,
|
||||
Opcode.LOADHA,
|
||||
Opcode.LOADHX,
|
||||
@@ -661,7 +655,6 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.LOADM to InstructionFormat.from("BWL,>r1,<a | F,>fr1,<a"),
|
||||
Opcode.LOADI to InstructionFormat.from("BWL,>r1,<r2 | F,>fr1,<r1"),
|
||||
Opcode.LOADX to InstructionFormat.from("BWL,>r1,<r2,<a | F,>fr1,<r1,<a"),
|
||||
Opcode.LOADIX to InstructionFormat.from("BWL,>r1,<r2,<a | F,>fr1,<r1,<a"),
|
||||
Opcode.LOADR to InstructionFormat.from("BWL,>r1,<r2 | F,>fr1,<fr2"),
|
||||
Opcode.LOADHA to InstructionFormat.from("B,>r1"),
|
||||
Opcode.LOADHA to InstructionFormat.from("B,>r1"),
|
||||
@@ -676,7 +669,6 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.STOREM to InstructionFormat.from("BWL,<r1,>a | F,<fr1,>a"),
|
||||
Opcode.STOREI to InstructionFormat.from("BWL,<r1,<r2 | F,<fr1,<r1"),
|
||||
Opcode.STOREX to InstructionFormat.from("BWL,<r1,<r2,>a | F,<fr1,<r1,>a"),
|
||||
Opcode.STOREIX to InstructionFormat.from("BWL,<r1,<r2,>a | F,<fr1,<r1,>a"),
|
||||
Opcode.STOREZM to InstructionFormat.from("BWL,>a | F,>a"),
|
||||
Opcode.STOREZI to InstructionFormat.from("BWL,<r1 | F,<r1"),
|
||||
Opcode.STOREZX to InstructionFormat.from("BWL,<r1,>a | F,<r1,>a"),
|
||||
@@ -917,11 +909,13 @@ data class IRInstruction(
|
||||
require(address!=null || labelSymbol!=null) {
|
||||
"missing an address or labelsymbol"}
|
||||
if(format.immediate && (immediate!=null || immediateFp!=null)) {
|
||||
if(opcode!=Opcode.SYSCALL) {
|
||||
if(opcode==Opcode.LOADFIELD || opcode==Opcode.STOREFIELD) {
|
||||
require(immediate in 0..65535) { "immediate value out of range for loadfield/storefield: $immediate" }
|
||||
} else if(opcode!=Opcode.SYSCALL) {
|
||||
when (type) {
|
||||
IRDataType.BYTE -> require(immediate in -128..255) { "immediate value out of range for byte: $immediate" }
|
||||
IRDataType.WORD -> require(immediate in -32768..65535) { "immediate value out of range for word: $immediate" }
|
||||
IRDataType.LONG -> require(immediate in -2147483647..2147483647) { "immediate value out of range for long: $immediate" }
|
||||
IRDataType.LONG -> require(immediate in -2147483648..2147483647) { "immediate value out of range for long: $immediate" }
|
||||
IRDataType.FLOAT, null -> {}
|
||||
}
|
||||
}
|
||||
@@ -1099,10 +1093,8 @@ data class IRInstruction(
|
||||
Opcode.FTOUB,
|
||||
Opcode.FTOSB,
|
||||
Opcode.FCOMP,
|
||||
Opcode.LOADIX,
|
||||
Opcode.LOADX,
|
||||
Opcode.STOREX,
|
||||
Opcode.STOREIX,
|
||||
Opcode.STOREZX,
|
||||
Opcode.SGN -> IRDataType.BYTE
|
||||
Opcode.FFROMSL, Opcode.FTOSL -> IRDataType.LONG
|
||||
@@ -1135,7 +1127,7 @@ data class IRInstruction(
|
||||
}
|
||||
|
||||
private fun determineReg2Type(): IRDataType? {
|
||||
if(opcode==Opcode.LOADX || opcode==Opcode.LOADIX || opcode==Opcode.STOREX || opcode==Opcode.STOREIX)
|
||||
if(opcode==Opcode.LOADX || opcode==Opcode.STOREX)
|
||||
return IRDataType.BYTE
|
||||
if(opcode==Opcode.LOADI || opcode==Opcode.STOREI || opcode==Opcode.LOADFIELD || opcode==Opcode.STOREFIELD)
|
||||
return IRDataType.WORD
|
||||
|
||||
@@ -306,7 +306,7 @@ class IRProgram(val name: String,
|
||||
fun convert(asmChunk: IRInlineAsmChunk): IRCodeChunks {
|
||||
val chunks = mutableListOf<IRCodeChunkBase>()
|
||||
var chunk = IRCodeChunk(asmChunk.label, null)
|
||||
asmChunk.assembly.lineSequence().forEach {
|
||||
asmChunk.assembly.lineSequence().filter{it.isNotBlank()}.forEach {
|
||||
val parsed = parseIRCodeLine(it.trim())
|
||||
parsed.fold(
|
||||
ifLeft = { instruction -> chunk += instruction },
|
||||
@@ -642,9 +642,11 @@ class RegistersUsed(
|
||||
|
||||
fun validate(allowed: Map<Int, IRDataType>, chunk: IRCodeChunkBase?) {
|
||||
for((reg, type) in regsTypes) {
|
||||
if(allowed[reg]==null)
|
||||
throw IllegalArgumentException("Reg type mismatch for register $reg type $type: no type known. CodeChunk=$chunk label ${chunk?.label}")
|
||||
if(allowed[reg]!=type)
|
||||
val allowedType = allowed[reg]
|
||||
// can't do this check because %ir {{ .. }} segments may contain registers that the compiler doesn't know about yet.
|
||||
// if(allowedType==null)
|
||||
// throw IllegalArgumentException("Reg type mismatch for register $reg type $type: no type known. CodeChunk=$chunk label ${chunk?.label}")
|
||||
if(allowedType!=null && allowedType!=type)
|
||||
throw IllegalArgumentException("Reg type mismatch for register $reg type $type: expected ${allowed[reg]}. CodeChunk=$chunk label ${chunk?.label}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +64,11 @@ fun parseIRValue(value: String): Double {
|
||||
return if(value.startsWith("-"))
|
||||
-parseIRValue(value.substring(1))
|
||||
else if(value.startsWith('$'))
|
||||
value.substring(1).toInt(16).toDouble()
|
||||
value.substring(1).toLong(16).toDouble()
|
||||
else if(value.startsWith('%'))
|
||||
value.substring(1).toInt(2).toDouble()
|
||||
value.substring(1).toLong(2).toDouble()
|
||||
else if(value.startsWith("0x"))
|
||||
value.substring(2).toInt(16).toDouble()
|
||||
value.substring(2).toLong(16).toDouble()
|
||||
else if(value.startsWith('_'))
|
||||
throw IRParseException("attempt to parse a label as numeric value")
|
||||
else if(value.startsWith('&'))
|
||||
@@ -154,7 +154,13 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
|
||||
if (immediateInt == null && immediateFp == null) {
|
||||
if (type == IRDataType.FLOAT && opcode != Opcode.LOADFIELD && opcode != Opcode.STOREFIELD)
|
||||
immediateFp = value
|
||||
else
|
||||
else if(type == IRDataType.LONG) {
|
||||
val immediateLong = value.toLong()
|
||||
if(immediateLong == 0x80000000L) {
|
||||
immediateInt = -2147483648
|
||||
} else
|
||||
immediateInt = immediateLong.toInt()
|
||||
} else
|
||||
immediateInt = value.toInt()
|
||||
} else {
|
||||
address = value.toInt()
|
||||
@@ -200,21 +206,26 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
|
||||
if(format.immediate && opcode!=Opcode.SYSCALL) {
|
||||
if(immediateInt==null && immediateFp==null && labelSymbol==null)
|
||||
throw IRParseException("needs value or symbol for $line")
|
||||
when (type) {
|
||||
IRDataType.BYTE -> {
|
||||
if (immediateInt!=null && (immediateInt < -128 || immediateInt > 255))
|
||||
throw IRParseException("immediate value out of range for byte: $immediateInt")
|
||||
if(opcode==Opcode.LOADFIELD || opcode==Opcode.STOREFIELD) {
|
||||
if(immediateInt !in 0..65535)
|
||||
throw IRParseException("immediate value out of range for loadfield/storefield: $immediateInt")
|
||||
} else {
|
||||
when (type) {
|
||||
IRDataType.BYTE -> {
|
||||
if (immediateInt!=null && (immediateInt < -128 || immediateInt > 255))
|
||||
throw IRParseException("immediate value out of range for byte: $immediateInt")
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
if (immediateInt!=null && (immediateInt < -32768 || immediateInt > 65535))
|
||||
throw IRParseException("immediate value out of range for word: $immediateInt")
|
||||
}
|
||||
IRDataType.LONG -> {
|
||||
if (immediateInt!=null && (immediateInt.toLong() < -2147483648L || immediateInt.toLong() > 0x7fffffffL))
|
||||
throw IRParseException("immediate value out of range for long: $immediateInt")
|
||||
}
|
||||
IRDataType.FLOAT -> {}
|
||||
null -> {}
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
if (immediateInt!=null && (immediateInt < -32768 || immediateInt > 65535))
|
||||
throw IRParseException("immediate value out of range for word: $immediateInt")
|
||||
}
|
||||
IRDataType.LONG -> {
|
||||
if (immediateInt!=null && immediateInt < -2147483647)
|
||||
throw IRParseException("immediate value out of range for long: $immediateInt")
|
||||
}
|
||||
IRDataType.FLOAT -> {}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,18 +81,32 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
||||
vars
|
||||
}
|
||||
|
||||
val allStructInstances: Collection<StStructInstance> by lazy {
|
||||
fun allStructInstances(): Collection<StStructInstance> {
|
||||
val vars = mutableListOf<StStructInstance>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
if(child.value.type== StNodeType.STRUCTINSTANCE)
|
||||
if(child.value.type == StNodeType.STRUCTINSTANCE)
|
||||
vars.add(child.value as StStructInstance)
|
||||
else
|
||||
collect(child.value)
|
||||
}
|
||||
}
|
||||
collect(this)
|
||||
vars
|
||||
return vars
|
||||
}
|
||||
|
||||
fun allStructTypes(): Collection<StStruct> {
|
||||
val vars = mutableListOf<StStruct>()
|
||||
fun collect(node: StNode) {
|
||||
for(child in node.children) {
|
||||
if(child.value.type == StNodeType.STRUCT)
|
||||
vars.add(child.value as StStruct)
|
||||
else
|
||||
collect(child.value)
|
||||
}
|
||||
}
|
||||
collect(this)
|
||||
return vars
|
||||
}
|
||||
|
||||
override fun lookup(scopedName: String) = flat[scopedName]
|
||||
@@ -309,10 +323,27 @@ class StStruct(
|
||||
override fun getFieldType(name: String): DataType? = fields.firstOrNull { it.second == name }?.first
|
||||
|
||||
override fun memsize(sizer: IMemSizer): Int = size.toInt()
|
||||
override fun sameas(other: ISubType): Boolean = other is StStruct &&
|
||||
scopedNameString == other.scopedNameString &&
|
||||
fields == other.fields &&
|
||||
size == other.size
|
||||
override fun sameas(other: ISubType): Boolean {
|
||||
if(other is StStruct && size == other.size && fields == other.fields) {
|
||||
if(scopedNameString == other.scopedNameString)
|
||||
return true
|
||||
|
||||
// TODO this is a HACK: work around difference in struct names caused by symbol prefixing. See https://github.com/irmen/prog8/issues/198
|
||||
fun nameWithoutSymbolPrefixes(name: String): List<String> {
|
||||
return name.split('.')
|
||||
.map { name ->
|
||||
if(name.length>4 && name[0]=='p' && name[1]=='8' && name[3]=='_') {
|
||||
// strip prefix (3 characters followed by underscore, see Callgraph.prefixes)
|
||||
name.drop(4)
|
||||
} else name
|
||||
}
|
||||
}
|
||||
|
||||
if(nameWithoutSymbolPrefixes(scopedNameString) == (nameWithoutSymbolPrefixes(other.scopedNameString)))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -348,7 +348,7 @@ class PtNumber(type: BaseDataType, val number: Double, position: Position) : PtE
|
||||
BaseDataType.BYTE -> require(number in -128.0..127.0)
|
||||
BaseDataType.UWORD -> require(number in 0.0..65535.0)
|
||||
BaseDataType.WORD -> require(number in -32768.0..32767.0)
|
||||
BaseDataType.LONG -> require(number in -2147483647.0..2147483647.0)
|
||||
BaseDataType.LONG -> require(number in -2147483648.0..2147483647.0)
|
||||
else -> require(type.isNumeric) { "numeric literal type should be numeric: $type" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,6 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.LOADM -> InsLOADM(ins)
|
||||
Opcode.LOADX -> InsLOADX(ins)
|
||||
Opcode.LOADI -> InsLOADI(ins)
|
||||
Opcode.LOADIX -> InsLOADIX(ins)
|
||||
Opcode.LOADR -> InsLOADR(ins)
|
||||
Opcode.LOADHA -> InsLOADHA(ins)
|
||||
Opcode.LOADHX -> InsLOADHX(ins)
|
||||
@@ -202,7 +201,6 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.LOADFIELD -> InsLOADFIELD(ins)
|
||||
Opcode.STOREM -> InsSTOREM(ins)
|
||||
Opcode.STOREX -> InsSTOREX(ins)
|
||||
Opcode.STOREIX -> InsSTOREIX(ins)
|
||||
Opcode.STOREI -> InsSTOREI(ins)
|
||||
Opcode.STOREZM -> InsSTOREZM(ins)
|
||||
Opcode.STOREZX -> InsSTOREZX(ins)
|
||||
@@ -521,7 +519,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
|
||||
private fun InsLOADFIELD(i: IRInstruction) {
|
||||
val offset = i.immediate!!
|
||||
require(offset in 0..255)
|
||||
require(offset in 0..65535)
|
||||
when(i.type!!) {
|
||||
IRDataType.BYTE -> {
|
||||
val value = memory.getUB(registers.getUW(i.reg2!!).toInt() + offset)
|
||||
@@ -567,34 +565,6 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsLOADIX(i: IRInstruction) {
|
||||
when (i.type!!) {
|
||||
IRDataType.BYTE -> {
|
||||
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!)
|
||||
val value = memory.getUB(pointer.toInt())
|
||||
registers.setUB(i.reg1!!, value)
|
||||
statusbitsNZ(value.toInt(), i.type!!)
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!)
|
||||
val value = memory.getUW(pointer.toInt())
|
||||
registers.setUW(i.reg1!!, value)
|
||||
statusbitsNZ(value.toInt(), i.type!!)
|
||||
}
|
||||
IRDataType.LONG -> {
|
||||
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!)
|
||||
val value = memory.getSL(pointer.toInt())
|
||||
registers.setSL(i.reg1!!, value)
|
||||
statusbitsNZ(value, i.type!!)
|
||||
}
|
||||
IRDataType.FLOAT -> {
|
||||
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg1!!)
|
||||
registers.setFloat(i.fpReg1!!, memory.getFloat(pointer.toInt()))
|
||||
}
|
||||
}
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsLOADR(i: IRInstruction) {
|
||||
when(i.type!!) {
|
||||
IRDataType.BYTE -> {
|
||||
@@ -639,7 +609,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
|
||||
private fun InsSTOREFIELD(i: IRInstruction) {
|
||||
val offset = i.immediate!!
|
||||
require(offset in 0..255)
|
||||
require(offset in 0..65535)
|
||||
when (i.type!!) {
|
||||
IRDataType.BYTE -> memory.setUB(registers.getUW(i.reg2!!).toInt() + offset, registers.getUB(i.reg1!!))
|
||||
IRDataType.WORD -> memory.setUW(registers.getUW(i.reg2!!).toInt() + offset, registers.getUW(i.reg1!!))
|
||||
@@ -659,28 +629,6 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsSTOREIX(i: IRInstruction) {
|
||||
when (i.type!!) {
|
||||
IRDataType.BYTE -> {
|
||||
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!)
|
||||
memory.setUB(pointer.toInt(), registers.getUB(i.reg1!!))
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!)
|
||||
memory.setUW(pointer.toInt(), registers.getUW(i.reg1!!))
|
||||
}
|
||||
IRDataType.LONG -> {
|
||||
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!)
|
||||
memory.setSL(pointer.toInt(), registers.getSL(i.reg1!!))
|
||||
}
|
||||
IRDataType.FLOAT -> {
|
||||
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg1!!)
|
||||
memory.setFloat(pointer.toInt(), registers.getFloat(i.fpReg1!!))
|
||||
}
|
||||
}
|
||||
nextPc()
|
||||
}
|
||||
|
||||
private fun InsSTOREZM(i: IRInstruction) {
|
||||
when(i.type!!) {
|
||||
IRDataType.BYTE -> memory.setUB(i.address!!, 0u)
|
||||
|
||||