Compare commits

...
33 Commits
Author SHA1 Message Date
Irmen de Jong 4030a1b9e5 cleanup, animals example removed because it's superseded by the pointers/animalgame example now 2025-12-16 16:48:34 +01:00
Irmen de Jong 0217d05c68 IR: loadfield/storefield now accept offsets up to 65535 2025-12-16 14:56:11 +01:00
Irmen de Jong 8553f3c9f1 IR: loadfield is a better name 2025-12-16 12:57:45 +01:00
Irmen de Jong d157d03ea0 IR: optimize load+add+load into single loadm with offset (also store)
update gradle wrapper to 9.2.0
2025-12-16 12:17:52 +01:00
Irmen de Jong 1799b5f00c updated bundled zsmkit version to 2.8 2025-12-15 18:41:28 +01:00
Irmen de Jong 2ddb5e11b7 updated symboldumps 2025-12-13 02:12:00 +01:00
Irmen de Jong 3aa8b9fd12 turn prefix - numbers into negative numbers asap to avoid type problems later 2025-12-13 01:13:52 +01:00
Irmen de Jong 51300196e8 fix more long comparisons with arrays, fixes #196 2025-12-12 21:01:51 +01:00
Irmen de Jong f7b2f19cba remove restriction about long comparison with expression 2025-12-11 21:43:53 +01:00
Irmen de Jong 014a82a1ee always put all struct types as .struct in asm code to make them all accessible for size and offsets 2025-12-10 21:39:44 +01:00
Irmen de Jong 745cf3d958 IR: removed loadix/storeix instructions 2025-12-09 22:55:53 +01:00
Irmen de Jong c4aa681d58 IR: better name for loadfield/storefield instructions 2025-12-09 00:40:26 +01:00
Irmen de Jong feb0774bbd in benchmark-c routines, use same CIA timer as the original C code did so the program size is better comparable 2025-12-08 01:08:33 +01:00
Irmen de Jong 2ccfb2ac90 fix double evaluation (sign extension) of word to long assignment. Fixes #201 2025-12-07 22:23:59 +01:00
Irmen de Jong 2191c4b7d8 remove redundant expression assignment before multiplying certain words 2025-12-07 14:06:59 +01:00
Irmen de Jong 39c1cf0c3c fix diskio lf_start_list_dirs() and lf_start_list_files() not processing the pattern argument 2025-12-07 05:11:08 +01:00
Irmen de Jong c693be0dd6 fix byte subtract from long
fix IR register check error for %ir segments
2025-12-06 20:40:42 +01:00
Irmen de Jong 0f433055a7 fix pointer dereference division and modulus , fixes #199 2025-12-05 22:19:54 +01:00
Irmen de Jong 76c95ba6fa some more obscure inplace modifications implemented 2025-12-05 21:00:14 +01:00
Irmen de Jong bd272a8567 optimize some specialized cases of long << 2025-12-05 01:08:19 +01:00
Irmen de Jong b38fff76e9 hack to work around struct name prefix inconsistency
see github issue 198
2025-12-03 21:07:08 +01:00
Irmen de Jong 07b7639ff2 better error msg 2025-12-02 22:56:40 +01:00
Irmen de Jong 6871561c09 fix parent node error in sizeof() 2025-12-01 23:21:14 +01:00
Irmen de Jong 77a71138ad fix typo r8r0sl -> r8r9sl 2025-11-30 22:05:57 +01:00
Irmen de Jong 165f94bc93 fix long expression <0 / >0 comparison 2025-11-30 16:57:05 +01:00
Irmen de Jong b75090f928 fix some register clobbering with long operations 2025-11-30 16:44:36 +01:00
Irmen de Jong 62b0c82f93 implement long <=0 and long >=0 comparisons
make sure the adpcm benchmark doesn't use random memory contents
2025-11-30 16:25:58 +01:00
Irmen de Jong b1ef863c7f make $8000000 a valid long integer (-2147483648) 2025-11-26 23:10:59 +01:00
Irmen de Jong e78345410f fix long-- high word wrap bug 2025-11-26 20:13:17 +01:00
Irmen de Jong 8b86f97aaa Implement struct field long and signed word bitshifts. Fixes #194 2025-11-25 22:28:28 +01:00
Irmen de Jong 93135774e6 fix IR signed bitshift right 2025-11-25 22:16:28 +01:00
Irmen de Jong a64f27c6b0 add cx16.r0r1sl, cx16.r2r3sl, ... that memory-map signed longs on the virtual registers 2025-11-25 21:13:03 +01:00
Irmen de Jong 69ef63c96d tweaks 2025-11-24 22:23:40 +01:00
79 changed files with 1420 additions and 772 deletions

No files matched your search

+14 -1
View File
@@ -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 |
![crc8](result-crc8.png)
+98
View File
@@ -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
}
}
+5 -5
View File
@@ -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
}
+5 -5
View File
@@ -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
}
+5 -5
View File
@@ -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
}
+3 -3
View File
@@ -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 {}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 65 KiB

+5 -5
View File
@@ -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
}
+5 -5
View File
@@ -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 -1
View File
@@ -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)
+4 -1
View File
@@ -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)
}
}
+10
View File
@@ -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
+10
View File
@@ -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
+2
View File
@@ -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
}
+10
View File
@@ -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
+10
View File
@@ -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
+28
View File
@@ -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
}
+10
View File
@@ -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>()
+6 -6
View File
@@ -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)
-1
View File
@@ -203,7 +203,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
val bothCx16AndC64 = cartesianProduct(
listOf(
"animals",
"balls",
"cube3d",
"cube3d-float",
+34
View File
@@ -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
}
})
+44
View File
@@ -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
+1
View File
@@ -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)
+37
View File
@@ -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
+16 -17
View File
@@ -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
+1 -1
View File
@@ -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`.
-141
View File
@@ -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
+2
View File
@@ -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.
+2
View File
@@ -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 {
+1 -1
View File
@@ -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...
+5 -5
View File
@@ -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
+1 -1
View File
@@ -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
Binary file not shown.
-2
View File
@@ -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
+41 -79
View File
@@ -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()
}
*/
}
+1 -1
View File
@@ -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
Binary file not shown.
+1 -1
View File
@@ -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}")
}
}
+29 -18
View File
@@ -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 -> {}
}
}
+38 -7
View File
@@ -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" }
}
}
+2 -54
View File
@@ -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)