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

|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
%import textio
|
||||
|
||||
cia {
|
||||
ubyte freq
|
||||
const ubyte CNT = 200
|
||||
|
||||
sub calibrate() {
|
||||
txt.print("calibrating frequency: ")
|
||||
tod_init(0)
|
||||
txt.print_ub(tod_freq())
|
||||
txt.print(" hz\n")
|
||||
tod_reset()
|
||||
}
|
||||
|
||||
sub print_time() {
|
||||
uword t = tod_get10()
|
||||
txt.print("(cia) time: ")
|
||||
txt.print_uw(t / 10)
|
||||
txt.chrout('.')
|
||||
txt.print_uw(t % 10)
|
||||
txt.print(" sec.\n")
|
||||
}
|
||||
|
||||
sub tod_reset() {
|
||||
; set the tod to 0
|
||||
c64.CIA2TODHR = 0
|
||||
c64.CIA2TODMIN = 0
|
||||
c64.CIA2TODSEC = 0
|
||||
c64.CIA2TOD10 = 0
|
||||
}
|
||||
|
||||
sub tod_get10() -> uword {
|
||||
ubyte h, m, s, t
|
||||
uword time
|
||||
h = c64.CIA2TODHR
|
||||
m = c64.CIA2TODMIN
|
||||
s = c64.CIA2TODSEC
|
||||
t = c64.CIA2TOD10
|
||||
time = t
|
||||
time += (s & $0f) * (10 as uword)
|
||||
time += (s >> 4) * (100 as uword)
|
||||
time += (m & $0f) * 600
|
||||
time += (m >> 4) * 6000
|
||||
return time
|
||||
}
|
||||
|
||||
sub tod_init(ubyte f) {
|
||||
if (f == 0)
|
||||
freq = tod_detect_freq()
|
||||
else
|
||||
freq = f
|
||||
|
||||
if (freq == 50)
|
||||
c64.CIA2CRA |= $80
|
||||
else
|
||||
c64.CIA2CRA &= $7f
|
||||
}
|
||||
|
||||
sub tod_freq() -> ubyte {
|
||||
return freq
|
||||
}
|
||||
|
||||
sub tod_detect_freq() -> ubyte {
|
||||
uword cbl
|
||||
|
||||
c64.CIA2CRB = $40 ; stop timer
|
||||
c64.CIA2CRA = $80 ; stop timer
|
||||
|
||||
; set ta to overflow every 10000 count (~= 10ms)
|
||||
c64.CIA2TAL = $10
|
||||
c64.CIA2TAH = $27
|
||||
c64.CIA2TBL = CNT
|
||||
c64.CIA2TBH = 0
|
||||
|
||||
tod_reset()
|
||||
|
||||
c64.CIA2CRB = $41 ; input from tim1 overflow, continuous, start timer
|
||||
c64.CIA2CRA = $81 ; start timer, continuous tod 50HZ
|
||||
|
||||
|
||||
while c64.CIA2TODSEC == 0 {
|
||||
; wait for tod to count 1s
|
||||
}
|
||||
|
||||
; cal=CIA2.ta_lo;
|
||||
; cah=CIA2.ta_hi;
|
||||
cbl = c64.CIA2TBL
|
||||
; cbh=CIA2.tb_hi;
|
||||
|
||||
; printf("count2 = %d %d %d %d\n",cah, cal, cbh, cbl);
|
||||
; printf("elapsed ~= %d0ms\n",CNT-cbl);
|
||||
|
||||
if CNT - cbl > 90
|
||||
return 50
|
||||
else
|
||||
return 60
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
; note: Prog8 has a complete CRC16 routine in its library: math.crc16
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
@@ -40,12 +39,13 @@ test {
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uwhex(EXPECTED, true)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
; note: Prog8 has a complete CRC32 routine in its library: math.crc32
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
@@ -40,12 +39,13 @@ test {
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_ulhex(EXPECTED, true)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
@@ -36,12 +35,13 @@ test {
|
||||
|
||||
if crc_result == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_ubhex(EXPECTED, true)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 65 KiB |
@@ -1,16 +1,15 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
@@ -46,12 +45,13 @@ test {
|
||||
|
||||
if prime_count == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uw(EXPECTED)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import ciatimer
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
cia.calibrate()
|
||||
test.benchmark_name()
|
||||
cbm.SETTIM(0,0,0)
|
||||
test.benchmark()
|
||||
txt.print_f(floats.time() / 60)
|
||||
txt.print(" seconds\n")
|
||||
void test.benchmark_check()
|
||||
cia.print_time()
|
||||
repeat {}
|
||||
}
|
||||
}
|
||||
@@ -46,12 +45,13 @@ test {
|
||||
|
||||
if prime_count == EXPECTED
|
||||
{
|
||||
txt.print(" [OK]")
|
||||
txt.print(" [OK]\n")
|
||||
return false
|
||||
}
|
||||
|
||||
txt.print(" [FAIL] - expected ")
|
||||
txt.print_uw(EXPECTED)
|
||||
txt.nl()
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ adpcm {
|
||||
|
||||
while cbm.RDTIM16()<max_time {
|
||||
adpcm.init(0,0)
|
||||
uword @requirezp nibbles_ptr = $a000 ; for benchmark purposes, the exact nibbles don't really matter, so we just take the basic ROM as input
|
||||
uword @requirezp nibbles_ptr = $0800 ; for benchmark purposes, the exact nibbles don't really matter, so we just take this benchmark program itself as input
|
||||
repeat 252/2 {
|
||||
unroll 2 {
|
||||
ubyte @zp nibble = @(nibbles_ptr)
|
||||
|
||||
@@ -14,8 +14,11 @@ fun Number.toHex(): String {
|
||||
// larger -> "$12345678"
|
||||
// negative values are prefixed with '-'.
|
||||
val integer = this.toLong()
|
||||
if(integer<0)
|
||||
if(integer<0) {
|
||||
if(integer==-2147483648L)
|
||||
return "$80000000" // the exception to the rule, because -$80000000 is not a valid hex number
|
||||
return '-' + abs(integer).toHex()
|
||||
}
|
||||
return when (integer) {
|
||||
in 0 until 16 -> integer.toString()
|
||||
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
|
||||
|
||||
@@ -962,14 +962,14 @@ class AsmGen6502Internal (
|
||||
}
|
||||
} else throw AssemblyError("weird casted type")
|
||||
} else {
|
||||
TODO("assign typecasted expression $value to a long target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
|
||||
TODO("assign typecasted expression $value to a long target ${target.kind} at ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
|
||||
}
|
||||
} else if(target.kind == TargetStorageKind.REGISTER) {
|
||||
assignExpressionToRegister(value, target.register!!, true)
|
||||
} else if(target.kind == TargetStorageKind.VARIABLE) {
|
||||
assignExpressionToVariable(value, target.asmVarname, target.datatype)
|
||||
} else {
|
||||
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
|
||||
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
|
||||
}
|
||||
}
|
||||
target.datatype.isFloat -> {
|
||||
@@ -2003,9 +2003,9 @@ $repeatLabel""")
|
||||
lda #<$leftName
|
||||
ldy #>$leftName""")
|
||||
if(lessOrEquals)
|
||||
out("jsr floats.vars_lesseq_f")
|
||||
out(" jsr floats.vars_lesseq_f")
|
||||
else
|
||||
out("jsr floats.vars_less_f")
|
||||
out(" jsr floats.vars_less_f")
|
||||
}
|
||||
fun lessf(expr: PtExpression, rightName: String) {
|
||||
assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
|
||||
@@ -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""")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
||||
val third = lines[2].value
|
||||
|
||||
if(!haslabel(second)) {
|
||||
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
if ((" jmp" in first || "\tjmp" in first || " bra" in first || "\tbra" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
|
||||
@@ -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)
|
||||
@@ -1938,7 +1969,7 @@ _jump jmp (${target.asmLabel})
|
||||
if(notEquals)
|
||||
translateAYNotEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
|
||||
else
|
||||
translateAYNotEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
|
||||
translateAYEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 -> {}
|
||||
}
|
||||
@@ -3077,7 +3055,14 @@ $endLabel""")
|
||||
BaseDataType.BYTE -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName")
|
||||
BaseDataType.UWORD -> asmgen.out(" jsr floats.cast_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
|
||||
BaseDataType.WORD -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
|
||||
BaseDataType.LONG -> TODO("cast float to long")
|
||||
BaseDataType.LONG -> {
|
||||
asmgen.out("""
|
||||
sta cx16.r0L
|
||||
sty cx16.r0H
|
||||
lda #<$targetAsmVarName
|
||||
ldy #>$targetAsmVarName
|
||||
jsr floats.cast_as_long""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
@@ -3087,6 +3072,15 @@ $endLabel""")
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
} else if(targetDt.isWord || targetDt.isPointer) {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
||||
} else if(targetDt.isFloat) {
|
||||
asmgen.out("""
|
||||
lda #<$targetAsmVarName
|
||||
ldy #>$targetAsmVarName
|
||||
sta cx16.r0L
|
||||
sty cx16.r0H
|
||||
lda #<$sourceAsmVarName
|
||||
ldy #>$sourceAsmVarName
|
||||
jsr floats.cast_from_long""")
|
||||
} else
|
||||
throw AssemblyError("weird type")
|
||||
}
|
||||
@@ -3517,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()
|
||||
@@ -3568,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 */ }
|
||||
}
|
||||
}
|
||||
@@ -5326,7 +5372,7 @@ $endLabel""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceByteNegate(PtrTarget(target), ignoreDatatype, scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceByteNegate(PtrTarget(target), scope)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -5387,7 +5433,7 @@ $endLabel""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceWordNegate(PtrTarget(target), ignoreDatatype, scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceWordNegate(PtrTarget(target), scope)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -5412,7 +5458,7 @@ $endLabel""")
|
||||
TargetStorageKind.ARRAY -> TODO(" - long array ${target.position}")
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
|
||||
TargetStorageKind.REGISTER -> TODO("32 bits register negate ${target.position}")
|
||||
TargetStorageKind.POINTER -> TODO("long negate via pointer ${target.position}")
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceLongNegate(PtrTarget(target), scope)
|
||||
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()) {
|
||||
|
||||
@@ -117,14 +117,18 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
TODO("inplace word invert pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceByteNegate(target: PtrTarget, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
|
||||
internal fun inplaceByteNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
TODO("inplace byte negate to pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceWordNegate(target: PtrTarget, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
|
||||
internal fun inplaceWordNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
TODO("inplace word negate pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceLongNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
TODO("inplace long negate pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceFloatNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
TODO("inplace float negate pointer deref ${target.position}")
|
||||
}
|
||||
@@ -284,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")
|
||||
}
|
||||
@@ -396,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
|
||||
@@ -497,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
|
||||
@@ -752,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
|
||||
@@ -774,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 -")
|
||||
}
|
||||
}
|
||||
@@ -787,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 -> {
|
||||
@@ -802,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)
|
||||
|
||||
@@ -874,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)
|
||||
|
||||
@@ -1036,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
|
||||
@@ -1106,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
|
||||
@@ -1196,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
|
||||
@@ -1270,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
|
||||
@@ -1321,12 +1717,13 @@ 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
|
||||
if(signed) asmgen.out("jsr prog8_math.divmod_w_asm")
|
||||
else asmgen.out("jsr prog8_math.divmod_uw_asm")
|
||||
// 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("""
|
||||
tax
|
||||
tya
|
||||
@@ -1344,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)
|
||||
@@ -1353,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)
|
||||
@@ -819,6 +807,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
addInstr(result, IRInstruction(Opcode.FTOSL, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast $valueDt to long ${cast.position}")
|
||||
}
|
||||
}
|
||||
@@ -837,6 +829,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
BaseDataType.WORD -> {
|
||||
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
addInstr(result, IRInstruction(Opcode.FFROMSL, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -440,6 +442,54 @@ jump p8_label_gen_2
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
fun optimizeImmediateLoadAssociative(replacementOpcode: Opcode) {
|
||||
|
||||
fun getImmediateLoad(reg: Int): Pair<Int, Int>? {
|
||||
// look if the given register gets an immediate value 1 or 2 istructions back
|
||||
// returns (index of load instruction, immediate value) or null.
|
||||
if(idx>=1) {
|
||||
val previous = indexedInstructions[idx-1].value
|
||||
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
|
||||
return idx-1 to previous.immediate!!
|
||||
}
|
||||
if(idx>=2) {
|
||||
val previous = indexedInstructions[idx-2].value
|
||||
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
|
||||
return idx-2 to previous.immediate!!
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
if(ins.reg1!=null) {
|
||||
val immediate1 = getImmediateLoad(ins.reg1!!)
|
||||
if(immediate1!=null) {
|
||||
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg2, immediate = immediate1.second)
|
||||
chunk.instructions.removeAt(immediate1.first)
|
||||
changed=true
|
||||
} else {
|
||||
val immediate2 = getImmediateLoad(ins.reg2!!)
|
||||
if (immediate2 != null) {
|
||||
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg1, immediate = immediate2.second)
|
||||
chunk.instructions.removeAt(immediate2.first)
|
||||
changed=true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to use immediate arithmetic instruction if possible
|
||||
when(ins.opcode) {
|
||||
Opcode.ADDR -> optimizeImmediateLoadAssociative(Opcode.ADD)
|
||||
Opcode.MULR -> optimizeImmediateLoadAssociative(Opcode.MUL)
|
||||
Opcode.MULSR -> optimizeImmediateLoadAssociative(Opcode.MULS)
|
||||
// Opcode.SUBR -> TODO("ir peephole Subr")
|
||||
// Opcode.DIVR -> TODO("ir peephole Divr")
|
||||
// Opcode.DIVSR -> TODO("ir peephole Divsr")
|
||||
// Opcode.MODR -> TODO("ir peephole Modr")
|
||||
// Opcode.DIVMODR -> TODO("ir peephole DivModr")
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
@@ -505,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,65 @@ smallringbuffer {
|
||||
}
|
||||
}
|
||||
|
||||
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
|
||||
|
||||
smallstack {
|
||||
; -- A small stack (LIFO) that uses just 256 bytes and is independent of the CPU stack. Stack is growing downward from the top of the buffer.
|
||||
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
|
||||
; note: for a "small stack" (256 bytes size) you might also perhaps just use the CPU stack via sys.push[w] / sys.pop[w].
|
||||
|
||||
ubyte[256] buffer
|
||||
ubyte sp = 255
|
||||
|
||||
sub init() {
|
||||
sp = 255
|
||||
}
|
||||
|
||||
sub size() -> ubyte {
|
||||
return 255-sp
|
||||
}
|
||||
|
||||
sub free() -> ubyte {
|
||||
return sp
|
||||
}
|
||||
|
||||
sub isfull() -> bool {
|
||||
return sp==0
|
||||
}
|
||||
|
||||
sub isempty() -> bool {
|
||||
return sp==255
|
||||
}
|
||||
|
||||
sub push(ubyte value) {
|
||||
; -- put a byte on the stack
|
||||
buffer[sp] = value
|
||||
sp--
|
||||
}
|
||||
|
||||
sub pushw(uword value) {
|
||||
; -- put a word on the stack (lsb first then msb)
|
||||
buffer[sp] = lsb(value)
|
||||
sp--
|
||||
buffer[sp] = msb(value)
|
||||
sp--
|
||||
}
|
||||
|
||||
sub pop() -> ubyte {
|
||||
; -- pops a byte off the stack
|
||||
sp++
|
||||
return buffer[sp]
|
||||
}
|
||||
|
||||
sub popw() -> uword {
|
||||
; -- pops a word off the stack.
|
||||
sp++
|
||||
cx16.r0H = buffer[sp]
|
||||
sp++
|
||||
cx16.r0L = buffer[sp]
|
||||
return cx16.r0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stack {
|
||||
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
|
||||
|
||||
@@ -358,7 +358,7 @@ extsub $FF71 = JMPFAR() clobbers(A,X) ; jump without r
|
||||
extsub $FF74 = INDFET(ubyte zpaddr @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> ubyte @A ; fetch byte from another bank (address in zp, ptr in A)
|
||||
extsub $FF77 = INDSTA(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) ; store byte to another bank (address in zp, ptr in $02b9)
|
||||
extsub $FF7A = INDCMP(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> bool @Pz, bool @Pc, bool @Pv; compare byte in another bank (address in zp, ptr in $02c8)
|
||||
extsub $FF7D = PRIMM() ; print immediate string
|
||||
extsub $FF7D = PRIMM() ; print immediate string (the 0-terminated string immediately following the JSR PRIMM instruction)
|
||||
|
||||
|
||||
; ---- C128 specific system utility routines: ----
|
||||
@@ -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
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
FL_ONE_const .byte 129 ; 1.0
|
||||
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
||||
; note: don't add too many constants here because they all end up in the resulting program
|
||||
|
||||
|
||||
.section BSS
|
||||
floats_temp_var .byte ?,?,?,?,? ; temporary storage for a float
|
||||
@@ -126,6 +127,75 @@ cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
|
||||
.pend
|
||||
|
||||
|
||||
cast_from_long .proc
|
||||
; convert long pointed to by AY into a float pointed to by R0
|
||||
; a bit slow algorithm implemented below: (msw(l) as word) as float * 65536.0 + (lsw(l) as float)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #3
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ZP_SCRATCH_REG
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
jsr GIVAYFAY
|
||||
lda #<FL_65536_const
|
||||
ldy #>FL_65536_const
|
||||
jsr CONUPK
|
||||
jsr FMULTT
|
||||
jsr pushFAC1
|
||||
ldy #1
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ZP_SCRATCH_REG
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
jsr GIVUAYFAY
|
||||
jsr MOVEF
|
||||
clc
|
||||
jsr popFAC
|
||||
jsr FADDT
|
||||
ldx cx16.r0L
|
||||
ldy cx16.r0H
|
||||
jmp MOVMF
|
||||
|
||||
FL_65536_const .byte $91, $00, $00, $00, $00 ; 65536.0
|
||||
; !notreached!
|
||||
.pend
|
||||
|
||||
cast_as_long .proc
|
||||
; convert float pointed to by R0 into a long pointed to by AY
|
||||
|
||||
FACHO = FAC_ADDR + 1
|
||||
|
||||
; save the target variable pointer on the stack
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
|
||||
lda cx16.r0L
|
||||
ldy cx16.r0H
|
||||
jsr MOVFM
|
||||
jsr QINT
|
||||
|
||||
; restore the target variable pointer from the stack, and put the result in it
|
||||
pla
|
||||
sta P8ZP_SCRATCH_PTR+1
|
||||
pla
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
|
||||
ldx #3
|
||||
ldy #0
|
||||
- lda FACHO,x
|
||||
sta (P8ZP_SCRATCH_PTR),y
|
||||
iny
|
||||
dex
|
||||
bpl -
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
copy_float .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W1,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
|
||||
@@ -11,6 +11,7 @@ floats {
|
||||
; note: the fac1 and fac2 are working registers and take 6 bytes each,
|
||||
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
|
||||
|
||||
const uword FAC_ADDR = $61
|
||||
|
||||
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
|
||||
func_sign_f_into_A .proc
|
||||
; sign in A, also sets status flags
|
||||
jsr MOVFM
|
||||
jmp SIGN
|
||||
jsr SIGN
|
||||
cmp #0
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -1313,6 +1323,8 @@ asmsub init_system() {
|
||||
lda #0
|
||||
sta c64.BGCOL0
|
||||
jsr disable_runstop_and_charsetswitch
|
||||
lda #$80
|
||||
sta 650 ; enable key repeat for normal keys too
|
||||
lda #PROG8_C64_BANK_CONFIG ; apply bank config
|
||||
sta $01
|
||||
and #1
|
||||
@@ -1349,6 +1361,8 @@ asmsub cleanup_at_exit() {
|
||||
jsr cbm.MEMTOP ; adjust MEMTOP down again
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr enable_runstop_and_charsetswitch
|
||||
lda #0
|
||||
sta 650 ; disable keyrepeats
|
||||
lda _exitcarry
|
||||
lsr a
|
||||
lda _exitcode
|
||||
|
||||
@@ -572,6 +572,85 @@ _try_iso
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub hex2long(str string @AY) -> long @R0R1_32 {
|
||||
; -- hexadecimal string (with or without '$') to long.
|
||||
; string may be in petscii or c64-screencode encoding.
|
||||
; stops parsing at the first character that's not a hex digit (except leading $)
|
||||
; result in R0:R1, number of characters processed also remains in cx16.r15L if you want to use it!! (0 = error)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
sty cx16.r0
|
||||
sty cx16.r0+1
|
||||
sty cx16.r0+2
|
||||
sty cx16.r0+3
|
||||
sty cx16.r15H
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
beq _stop
|
||||
cmp #'$'
|
||||
bne _loop
|
||||
iny
|
||||
_loop
|
||||
lda #0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
beq _stop
|
||||
cmp #7 ; screencode letters A-F are 1-6
|
||||
bcc _add_letter
|
||||
and #127
|
||||
cmp #97
|
||||
bcs _try_iso ; maybe letter is iso:'a'-iso:'f' (97-102)
|
||||
cmp #'g'
|
||||
bcs _stop
|
||||
cmp #'a'
|
||||
bcs _add_letter
|
||||
cmp #'0'
|
||||
bcc _stop
|
||||
cmp #'9'+1
|
||||
bcs _stop
|
||||
_calc
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
rol cx16.r0+2
|
||||
rol cx16.r0+3
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
rol cx16.r0+2
|
||||
rol cx16.r0+3
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
rol cx16.r0+2
|
||||
rol cx16.r0+3
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
rol cx16.r0+2
|
||||
rol cx16.r0+3
|
||||
and #$0f
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
ora cx16.r0
|
||||
sta cx16.r0
|
||||
iny
|
||||
bne _loop
|
||||
_stop
|
||||
sty cx16.r15L
|
||||
rts
|
||||
_add_letter
|
||||
pha
|
||||
lda #9
|
||||
sta P8ZP_SCRATCH_B1
|
||||
pla
|
||||
jmp _calc
|
||||
_try_iso
|
||||
cmp #103
|
||||
bcs _stop
|
||||
and #63
|
||||
bne _add_letter
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub bin2uword(str string @AY) -> uword @AY {
|
||||
; -- binary string (with or without '%') to uword.
|
||||
; stops parsing at the first character that's not a 0 or 1. (except leading %)
|
||||
|
||||
@@ -68,7 +68,65 @@ smallringbuffer {
|
||||
}
|
||||
}
|
||||
|
||||
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
|
||||
|
||||
smallstack {
|
||||
; -- A small stack (LIFO) that uses just 256 bytes and is independent of the CPU stack. Stack is growing downward from the top of the buffer.
|
||||
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
|
||||
; note: for a "small stack" (256 bytes size) you might also perhaps just use the CPU stack via sys.push[w] / sys.pop[w].
|
||||
|
||||
ubyte[256] buffer
|
||||
ubyte sp = 255
|
||||
|
||||
sub init() {
|
||||
sp = 255
|
||||
}
|
||||
|
||||
sub size() -> ubyte {
|
||||
return 255-sp
|
||||
}
|
||||
|
||||
sub free() -> ubyte {
|
||||
return sp
|
||||
}
|
||||
|
||||
sub isfull() -> bool {
|
||||
return sp==0
|
||||
}
|
||||
|
||||
sub isempty() -> bool {
|
||||
return sp==255
|
||||
}
|
||||
|
||||
sub push(ubyte value) {
|
||||
; -- put a byte on the stack
|
||||
buffer[sp] = value
|
||||
sp--
|
||||
}
|
||||
|
||||
sub pushw(uword value) {
|
||||
; -- put a word on the stack (lsb first then msb)
|
||||
buffer[sp] = lsb(value)
|
||||
sp--
|
||||
buffer[sp] = msb(value)
|
||||
sp--
|
||||
}
|
||||
|
||||
sub pop() -> ubyte {
|
||||
; -- pops a byte off the stack
|
||||
sp++
|
||||
return buffer[sp]
|
||||
}
|
||||
|
||||
sub popw() -> uword {
|
||||
; -- pops a word off the stack.
|
||||
sp++
|
||||
cx16.r0H = buffer[sp]
|
||||
sp++
|
||||
cx16.r0L = buffer[sp]
|
||||
return cx16.r0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stack {
|
||||
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
|
||||
@@ -78,7 +136,7 @@ stack {
|
||||
ubyte bank
|
||||
|
||||
sub init(ubyte rambank) {
|
||||
; -- initialize the stack, must be called before use. Supply the HiRAM bank to use as buffer space.
|
||||
; -- initialize the stack, must be called before use. Supply the HIRAM bank to use as buffer space.
|
||||
sp = $bfff
|
||||
bank = rambank
|
||||
}
|
||||
@@ -157,7 +215,7 @@ ringbuffer {
|
||||
ubyte bank = 255 ; set via init()
|
||||
|
||||
sub init(ubyte rambank) {
|
||||
; -- initialize the ringbuffer, must be called before use. Supply the HiRAM bank to use as buffer space.
|
||||
; -- initialize the ringbuffer, must be called before use. Supply the HIRAM bank to use as buffer space.
|
||||
head = $a000
|
||||
tail = $bfff
|
||||
fill = 0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; **experimental** data compression/decompression routines, API subject to change!!
|
||||
; data compression/decompression routines
|
||||
|
||||
%import shared_compression
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -999,15 +1001,12 @@ io_error:
|
||||
return false
|
||||
}
|
||||
|
||||
sub f_seek(uword pos_hiword, uword pos_loword) {
|
||||
sub f_seek(long position) {
|
||||
; -- seek in the reading file opened with f_open, to the given 32-bits position
|
||||
; Note: this will not work if you have already read the last byte of the file! Then you must close and reopen the file first.
|
||||
ubyte[6] command = ['p',0,0,0,0,0]
|
||||
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
|
||||
command[2] = lsb(pos_loword)
|
||||
command[3] = msb(pos_loword)
|
||||
command[4] = lsb(pos_hiword)
|
||||
command[5] = msb(pos_hiword)
|
||||
pokel(&command+2, position)
|
||||
cbm.SETNAM(sizeof(command), &command)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
@@ -1016,13 +1015,10 @@ io_error:
|
||||
}
|
||||
|
||||
|
||||
sub f_seek_w(uword pos_hiword, uword pos_loword) {
|
||||
sub f_seek_w(long position) {
|
||||
; -- seek in the output file opened with f_open_w_seek, to the given 32-bits position
|
||||
diskio.f_seek.command[1] = WRITE_IO_CHANNEL ; f_open_w uses this secondary address
|
||||
diskio.f_seek.command[2] = lsb(pos_loword)
|
||||
diskio.f_seek.command[3] = msb(pos_loword)
|
||||
diskio.f_seek.command[4] = lsb(pos_hiword)
|
||||
diskio.f_seek.command[5] = msb(pos_hiword)
|
||||
pokel(&diskio.f_seek.command+2, position)
|
||||
cbm.SETNAM(sizeof(diskio.f_seek.command), &diskio.f_seek.command)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
@@ -1030,54 +1026,63 @@ io_error:
|
||||
reset_write_channel() ; back to the write io channel
|
||||
}
|
||||
|
||||
asmsub f_tell() -> uword @R0, uword @R1, uword @R2, uword @R3 {
|
||||
; -- Returns the current read position of the opened read file,
|
||||
; in R0 and R1 (low + high words) and the file size in R2 and R3 (low + high words).
|
||||
; Returns 0 as size if the command is not supported by the DOS implementation/version.
|
||||
%asm {{
|
||||
jmp internal_f_tell
|
||||
}}
|
||||
}
|
||||
|
||||
sub internal_f_tell() {
|
||||
; gets the (32 bits) position + file size of the opened read file channel
|
||||
ubyte[2] command = ['t',0]
|
||||
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
|
||||
cbm.SETNAM(sizeof(command), &command)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
void cbm.CHKIN(15) ; use #15 as input channel
|
||||
sub f_tell() -> long, long {
|
||||
; -- Returns the current read position of the opened read file, and the file size.
|
||||
; Returns 0,0 if the command is not supported by the DOS implementation/version.
|
||||
ubyte[3] command = ['?','?',0]
|
||||
bool success=false
|
||||
; valid response starts with "07," followed by hex notations of the position and filesize
|
||||
if cbm.CHRIN()=='0' and cbm.CHRIN()=='7' and cbm.CHRIN()==',' {
|
||||
cx16.r1 = read4hex()
|
||||
cx16.r0 = read4hex() ; position in R1:R0
|
||||
void cbm.CHRIN() ; separator space
|
||||
cx16.r3 = read4hex()
|
||||
cx16.r2 = read4hex() ; filesize in R3:R2
|
||||
success = true
|
||||
long filesize, filepos
|
||||
ubyte commandoffset
|
||||
|
||||
command[1] = 't'
|
||||
commandoffset = 1
|
||||
f_tell_internal()
|
||||
return filepos, filesize
|
||||
|
||||
sub f_tell_internal() {
|
||||
; this code is used for both the T (tell) and FL (fatlba) commands
|
||||
command[2] = READ_IO_CHANNEL ; f_open uses this secondary address
|
||||
cbm.SETNAM(sizeof(command)-commandoffset, &command+commandoffset)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
void cbm.CHKIN(15) ; use #15 as input channel
|
||||
|
||||
; valid response starts with "07," followed by hex notations of the position and filesize
|
||||
if cbm.CHRIN()=='0' and cbm.CHRIN()=='7' and cbm.CHRIN()==',' {
|
||||
filepos = read8hex()
|
||||
void cbm.CHRIN() ; separator space
|
||||
filesize = read8hex()
|
||||
success = true
|
||||
} else {
|
||||
filesize = filepos = 0
|
||||
}
|
||||
|
||||
while cbm.READST()==0 {
|
||||
cx16.r5L = cbm.CHRIN()
|
||||
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
||||
break
|
||||
}
|
||||
|
||||
cbm.CLOSE(15)
|
||||
reset_read_channel() ; back to the read io channel
|
||||
}
|
||||
|
||||
while cbm.READST()==0 {
|
||||
cx16.r5L = cbm.CHRIN()
|
||||
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
||||
break
|
||||
}
|
||||
|
||||
cbm.CLOSE(15)
|
||||
reset_read_channel() ; back to the read io channel
|
||||
if success
|
||||
return
|
||||
|
||||
cx16.r0 = cx16.r1 = cx16.r2 = cx16.r3 = 0
|
||||
}
|
||||
|
||||
sub read4hex() -> uword {
|
||||
str hex = "0000"
|
||||
hex[0] = cbm.CHRIN()
|
||||
hex[1] = cbm.CHRIN()
|
||||
hex[2] = cbm.CHRIN()
|
||||
hex[3] = cbm.CHRIN()
|
||||
return conv.hex2uword(hex)
|
||||
sub f_fatlba() -> long, long {
|
||||
; -- Return the current LBA, cluster number (sector index and shift value are ignored for now)
|
||||
diskio.f_tell.command[0] = 'f'
|
||||
diskio.f_tell.command[1] = 'l'
|
||||
diskio.f_tell.commandoffset = 0
|
||||
diskio.f_tell.f_tell_internal()
|
||||
return diskio.f_tell.filepos, diskio.f_tell.filesize
|
||||
}
|
||||
|
||||
|
||||
sub read8hex() -> long {
|
||||
str hex = "00000000"
|
||||
for cx16.r0L in 0 to 7 {
|
||||
hex[cx16.r0L] = cbm.CHRIN()
|
||||
}
|
||||
return conv.hex2long(hex)
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ floats {
|
||||
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||
|
||||
const uword FAC_ADDR = $c3
|
||||
|
||||
extsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 'facmo' and 'faclo', MSB FIRST. DON'T USE THIS, USE WRAPPER 'AYINT2' INSTEAD. (might throw ILLEGAL QUANTITY)
|
||||
|
||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||
|
||||
@@ -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
|
||||
@@ -463,7 +473,7 @@ extsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobb
|
||||
extsub $ff6e = JSRFAR() ; following word = address to call, byte after that=rom/ram bank it is in
|
||||
extsub $ff74 = fetch(ubyte zp_startaddr @A, ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
|
||||
extsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X) ; note: The the zero page address containing the base address is passed in stavec ($03B2)
|
||||
extsub $ff7d = PRIMM()
|
||||
extsub $ff7d = PRIMM() ; print immediate string (the 0-terminated string immediately following the JSR PRIMM instruction)
|
||||
|
||||
; high level graphics & fonts
|
||||
extsub $ff20 = GRAPH_init(uword vectors @R0) clobbers(A,X,Y)
|
||||
@@ -620,6 +630,10 @@ const ubyte EXTAPI_scnsiz = $0D
|
||||
const ubyte EXTAPI_kbd_leds = $0E
|
||||
const ubyte EXTAPI_memory_decompress_from_func = $0F
|
||||
const ubyte EXTAPI_default_palette = $10
|
||||
const ubyte EXTAPI_has_machine_property = $11
|
||||
const ubyte EXTAPI_kbdbuf_get = $12
|
||||
const ubyte EXTAPI_kbdbuf_clear = $13
|
||||
const ubyte EXTAPI_blink_enable = $14
|
||||
|
||||
; extapi16 call numbers
|
||||
const ubyte EXTAPI16_test = $00
|
||||
@@ -676,6 +690,30 @@ sub mouse_present() -> bool {
|
||||
return cx16.r0L != $fc ; $fc = BAT_FAIL
|
||||
}
|
||||
|
||||
asmsub get_charset() -> ubyte @A {
|
||||
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
|
||||
%asm {{
|
||||
|
||||
KERNAL_MODE = $0372 ; internal kernal variable, risky to read it, but it's ben stable for many releases.
|
||||
lda KERNAL_MODE
|
||||
beq _end
|
||||
|
||||
bit #$40 ;ISO mode flag
|
||||
beq + ;usually KERNAL_MODE 1 or 6 (| $40)
|
||||
lda #1
|
||||
bra _end
|
||||
|
||||
+ bit #1 ;PETSCII upper case/graphics
|
||||
bne + ;usually KERNAL_MODE 2 or 4
|
||||
lda #2
|
||||
bra _end
|
||||
|
||||
+ lda #3 ;PETSCII upper/lower case
|
||||
_end:
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
; shims for the kernal routines called via the extapi call:
|
||||
|
||||
asmsub mouse_set_pos(uword xpos @R0, uword ypos @R1) clobbers(X) {
|
||||
@@ -758,31 +796,42 @@ asmsub set_default_palette() {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_charset() -> ubyte @A {
|
||||
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
|
||||
asmsub has_machine_property(ubyte property @X) clobbers(A,X) -> bool @Pc {
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
lda #EXTAPI_has_machine_property
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
KERNAL_MODE = $0372 ; internal kernal variable, risky to read it, but it's ben stable for many releases.
|
||||
lda KERNAL_MODE
|
||||
beq _end
|
||||
asmsub kbdbuf_get() clobbers(X,Y) -> ubyte @A {
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
lda #EXTAPI_kbdbuf_get
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
bit #$40 ;ISO mode flag
|
||||
beq + ;usually KERNAL_MODE 1 or 6 (| $40)
|
||||
lda #1
|
||||
bra _end
|
||||
asmsub kbdbuf_clear() clobbers(A) {
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
lda #EXTAPI_kbdbuf_clear
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
+ bit #1 ;PETSCII upper case/graphics
|
||||
bne + ;usually KERNAL_MODE 2 or 4
|
||||
lda #2
|
||||
bra _end
|
||||
|
||||
+ lda #3 ;PETSCII upper/lower case
|
||||
_end:
|
||||
rts
|
||||
asmsub blink_enable(bool enable @X) clobbers(A,X) {
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
txa
|
||||
and #1
|
||||
eor #1
|
||||
tax
|
||||
lda #EXTAPI_blink_enable
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
; TODO : implement shims for the remaining extapi calls.
|
||||
|
||||
|
||||
; ---- end of kernal routines ----
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -182,8 +182,8 @@ sub setclr (ubyte col, ubyte row, ubyte color) {
|
||||
}
|
||||
|
||||
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored) {
|
||||
; ---- set char at the given position on the screen. charcolor is ignored on PET
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char at the given position on the screen. NOTE: charcolor is ignored on PET
|
||||
%asm {{
|
||||
lda row
|
||||
asl a
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; **experimental** data compression/decompression routines, API subject to change!!
|
||||
; data compression/decompression routines
|
||||
; This file contains the shared routines that work on all targets.
|
||||
|
||||
compression {
|
||||
|
||||
@@ -160,6 +160,9 @@ sub log2(float value) -> float {
|
||||
ldy #>FL_LOG2_const
|
||||
jsr MOVFM
|
||||
jmp FDIVT
|
||||
|
||||
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -279,6 +279,26 @@ sub hex2uword(str string) -> uword {
|
||||
}
|
||||
}
|
||||
|
||||
sub hex2long(str string) -> long {
|
||||
; -- hexadecimal string (with or without '$') to long.
|
||||
; stops parsing at the first character that's not a hex digit (except leading $)
|
||||
long result
|
||||
ubyte char
|
||||
if @(string)=='$'
|
||||
string++
|
||||
repeat {
|
||||
char = @(string)
|
||||
if char==0
|
||||
return result
|
||||
result <<= 4
|
||||
if char>='0' and char<='9'
|
||||
result |= char-'0'
|
||||
else
|
||||
result |= char-'a'+10
|
||||
string++
|
||||
}
|
||||
}
|
||||
|
||||
sub bin2uword(str string) -> uword {
|
||||
; -- binary string (with or without '%') to uword.
|
||||
; stops parsing at the first character that's not a 0 or 1. (except leading %)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -60,13 +60,13 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages, except warnings and errors")
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler messages")
|
||||
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.")
|
||||
val dontIncludeSourcelines by cli.option(ArgType.Boolean, fullName = "nosourcelines", description = "do not include original Prog8 source lines in generated asm code")
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")} or a custom target properties file) (required)")
|
||||
val showTimings by cli.option(ArgType.Boolean, fullName = "timings", description = "show internal compiler timings (for performance analysis)")
|
||||
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.")
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM")
|
||||
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
@@ -145,7 +145,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
}
|
||||
|
||||
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
|
||||
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
|
||||
System.err.println("On the Commander X16, HIRAM bank 0 is used by the kernal and can't be used.")
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -497,8 +497,8 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("parameter '${param.first.name}' should be (u)byte or bool", param.first.position)
|
||||
}
|
||||
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||
if (!param.first.type.isWord && !param.first.type.isString && !param.first.type.isArray && param.first.type!=DataType.pointer(BaseDataType.UBYTE)) {
|
||||
err("parameter '${param.first.name}' should be (u)word, str or ^^ubyte")
|
||||
if (!param.first.type.isWord && !param.first.type.isString && !param.first.type.isArray && !param.first.type.isPointer) {
|
||||
err("parameter '${param.first.name}' should be (u)word, str or a pointer")
|
||||
}
|
||||
}
|
||||
else if(param.second.statusflag!=null) {
|
||||
@@ -2093,7 +2093,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(memread: DirectMemoryRead) {
|
||||
if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
|
||||
if(memread.addressExpression.inferType(program).isLong)
|
||||
errors.err("long address not yet supported", memread.addressExpression.position)
|
||||
else if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
|
||||
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memread.position)
|
||||
|
||||
val pointervar = memread.addressExpression as? IdentifierReference
|
||||
@@ -2126,7 +2128,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(memwrite: DirectMemoryWrite) {
|
||||
if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
|
||||
if(memwrite.addressExpression.inferType(program).isLong)
|
||||
errors.err("long address not yet supported", memwrite.addressExpression.position)
|
||||
else if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
|
||||
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memwrite.position)
|
||||
|
||||
val pointervar = memwrite.addressExpression as? IdentifierReference
|
||||
@@ -2371,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>()
|
||||
|
||||
@@ -339,8 +339,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
|
||||
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
|
||||
val dt = memread.addressExpression.inferType(program).getOr(DataType.UWORD)
|
||||
if(dt.isLong) {
|
||||
val constAddr = memread.addressExpression.constValue(program)?.number
|
||||
if(constAddr==null || constAddr>=65536)
|
||||
return noModifications
|
||||
}
|
||||
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
|
||||
return noModifications
|
||||
|
||||
@@ -354,8 +359,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
|
||||
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
|
||||
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
|
||||
val dt = memwrite.addressExpression.inferType(program).getOr(DataType.UWORD)
|
||||
if(dt.isLong) {
|
||||
val constAddr = memwrite.addressExpression.constValue(program)?.number
|
||||
if(constAddr==null || constAddr>=65536)
|
||||
return noModifications
|
||||
}
|
||||
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
|
||||
return noModifications
|
||||
|
||||
|
||||
@@ -480,18 +480,31 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
|
||||
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
|
||||
if(addressOf.arrayIndex!=null) {
|
||||
val tgt = addressOf.identifier?.constValue(program)
|
||||
if (tgt != null && tgt.type.isWord) {
|
||||
// &constant[idx] --> constant + idx
|
||||
val indexExpr = addressOf.arrayIndex!!.indexExpr
|
||||
val right = if(indexExpr.inferType(program) issimpletype tgt.type)
|
||||
indexExpr
|
||||
else
|
||||
TypecastExpression(indexExpr, DataType.forDt(tgt.type), true, indexExpr.position)
|
||||
val add = BinaryExpression(tgt, "+", right, addressOf.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(addressOf, add, parent)
|
||||
)
|
||||
val tgt = addressOf.identifier
|
||||
if(tgt!=null) {
|
||||
val constAddress = tgt.constValue(program)
|
||||
if (constAddress != null && constAddress.type.isInteger) {
|
||||
// &constant[idx] --> constant + idx
|
||||
val indexExpr = addressOf.arrayIndex!!.indexExpr
|
||||
val right = if (indexExpr.inferType(program) issimpletype constAddress.type)
|
||||
indexExpr
|
||||
else
|
||||
TypecastExpression(indexExpr, DataType.forDt(constAddress.type), true, indexExpr.position)
|
||||
val add = BinaryExpression(constAddress, "+", right, addressOf.position)
|
||||
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
|
||||
} else {
|
||||
val decl = tgt.targetVarDecl()
|
||||
if(decl!=null && decl.datatype.isInteger) {
|
||||
// &addressvar[idx] --> addressvar + idx
|
||||
val indexExpr = addressOf.arrayIndex!!.indexExpr
|
||||
val right = if (indexExpr.inferType(program) istype decl.datatype)
|
||||
indexExpr
|
||||
else
|
||||
TypecastExpression(indexExpr, decl.datatype, true, indexExpr.position)
|
||||
val add = BinaryExpression(tgt, "+", right, addressOf.position)
|
||||
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
|
||||
@@ -38,9 +38,9 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
|
||||
test("testCharLitAsExtsubArg") {
|
||||
val platform = Cx16Target()
|
||||
val result = compileText(platform, false, """
|
||||
val result = compileText(platform, false, $$"""
|
||||
main {
|
||||
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
|
||||
extsub $FFD2 = chrout(ubyte ch @ A)
|
||||
sub start() {
|
||||
chrout('\n')
|
||||
}
|
||||
@@ -61,9 +61,9 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
|
||||
test("testCharVarAsExtsubArg") {
|
||||
val platform = Cx16Target()
|
||||
val result = compileText(platform, false, """
|
||||
val result = compileText(platform, false, $$"""
|
||||
main {
|
||||
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
|
||||
extsub $FFD2 = chrout(ubyte ch @ A)
|
||||
sub start() {
|
||||
ubyte ch = '\n'
|
||||
chrout(ch)
|
||||
@@ -96,9 +96,9 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
|
||||
test("testCharConstAsExtsubArg") {
|
||||
val platform = Cx16Target()
|
||||
val result = compileText(platform, false, """
|
||||
val result = compileText(platform, false, $$"""
|
||||
main {
|
||||
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
|
||||
extsub $FFD2 = chrout(ubyte ch @ A)
|
||||
sub start() {
|
||||
const ubyte ch = '\n'
|
||||
chrout(ch)
|
||||
|
||||
@@ -203,7 +203,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
|
||||
val bothCx16AndC64 = cartesianProduct(
|
||||
listOf(
|
||||
"animals",
|
||||
"balls",
|
||||
"cube3d",
|
||||
"cube3d-float",
|
||||
|
||||
@@ -16,10 +16,7 @@ import prog8.ast.expressions.PrefixExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.code.target.C128Target
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.PETTarget
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.code.target.*
|
||||
import prog8tests.helpers.*
|
||||
|
||||
|
||||
@@ -438,4 +435,24 @@ main {
|
||||
errors.errors[0] shouldContain "8:9: cannot assign to an array or string that is located in ROM"
|
||||
errors.errors[1] shouldContain "9:9: cannot assign to an array or string that is located in ROM"
|
||||
}
|
||||
|
||||
test("long addresses not yet supported in memread and memwrite") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
const long bufferll = 888888
|
||||
long @shared addrlv = 777777
|
||||
|
||||
cx16.r0L = bufferll[2]
|
||||
cx16.r1L = @(999999)
|
||||
cx16.r2L = @(addrlv)
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(Cx16Target(), true, src, outputDir, errors = errors, writeAssembly = false) shouldBe null
|
||||
errors.errors.size shouldBe 3
|
||||
errors.errors[0] shouldContain "long address not yet supported"
|
||||
errors.errors[1] shouldContain "long address not yet supported"
|
||||
errors.errors[2] shouldContain "long address not yet supported"
|
||||
}
|
||||
})
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
@@ -306,26 +306,34 @@ main {
|
||||
(waDeclV as NumericLiteral).number shouldBe 40960.0
|
||||
}
|
||||
|
||||
test("address of a const uword pointer array expression") {
|
||||
test("address of a const integer pointer array expression") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
const uword buffer = 2000
|
||||
const long bufferl = 999999
|
||||
uword @shared addr = &buffer[2]
|
||||
|
||||
const ubyte width = 100
|
||||
ubyte @shared i
|
||||
ubyte @shared j
|
||||
uword @shared addr2 = &buffer[i * width + j]
|
||||
long @shared addr2 = &bufferl[2]
|
||||
const uword width = 100
|
||||
uword @shared addr3 = &buffer[width]
|
||||
long @shared addr4 = &bufferl[width]
|
||||
}
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = true)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 11
|
||||
val assignAddr = (st[2] as Assignment).value
|
||||
(assignAddr as NumericLiteral).number shouldBe 2002.0
|
||||
val assignAddr2 = (st[9] as Assignment).value as BinaryExpression
|
||||
assignAddr2.operator shouldBe "+"
|
||||
st.size shouldBe 12
|
||||
val assignAddr = (st[3] as Assignment).value as NumericLiteral
|
||||
assignAddr.number shouldBe 2002.0
|
||||
assignAddr.type shouldBe BaseDataType.UWORD
|
||||
val assignAddr2 = (st[5] as Assignment).value as NumericLiteral
|
||||
assignAddr2.number shouldBe 1000001.0
|
||||
assignAddr2.type shouldBe BaseDataType.LONG
|
||||
val assignAddr3 = (st[8] as Assignment).value as NumericLiteral
|
||||
assignAddr3.number shouldBe 2100
|
||||
assignAddr3.type shouldBe BaseDataType.UWORD
|
||||
val assignAddr4 = (st[10] as Assignment).value as NumericLiteral
|
||||
assignAddr4.number shouldBe 1000099.0
|
||||
assignAddr4.type shouldBe BaseDataType.LONG
|
||||
}
|
||||
|
||||
test("out of range const byte and word give correct error") {
|
||||
@@ -494,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())
|
||||
}
|
||||
|
||||
@@ -572,14 +572,15 @@ data class AddressOf(var identifier: IdentifierReference?, var arrayIndex: Array
|
||||
val index = arrayIndex?.constIndex()
|
||||
if (index != null) {
|
||||
address += when {
|
||||
target.datatype.isUnsignedWord -> index
|
||||
target.datatype.isInteger -> index
|
||||
target.datatype.isArray -> program.memsizer.memorySize(targetVar.datatype, index)
|
||||
else -> throw FatalAstException("need array or uword ptr")
|
||||
else -> throw FatalAstException("need array or ptr")
|
||||
}
|
||||
} else
|
||||
return null
|
||||
}
|
||||
return NumericLiteral(BaseDataType.UWORD, address, position)
|
||||
val addressType = if(targetVar.datatype.isLong) BaseDataType.LONG else BaseDataType.UWORD
|
||||
return NumericLiteral(addressType, address, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -686,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1416,14 +1421,14 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
}
|
||||
|
||||
// the value of a variable can (temporarily) be a different type as the vardecl itself.
|
||||
// don't return the value if the types don't match yet!
|
||||
val value = vardecl.value?.constValue(program)
|
||||
if(value==null || value.type==vardecl.datatype.base)
|
||||
return value
|
||||
val optimal = NumericLiteral.optimalNumeric(value.number, value.position)
|
||||
if(optimal.type==vardecl.datatype.base)
|
||||
return optimal
|
||||
return null
|
||||
val casted = value.cast(vardecl.datatype.base, true)
|
||||
return if(casted.isValid)
|
||||
casted.valueOrZero()
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -1537,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
|
||||
@@ -45,6 +45,20 @@ smallringbuffer {
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
smallstack {
|
||||
ubyte[] buffer
|
||||
ubyte sp
|
||||
free () -> ubyte
|
||||
init ()
|
||||
isempty () -> bool
|
||||
isfull () -> bool
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte value)
|
||||
pushw (uword value)
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
stack {
|
||||
uword buffer_ptr
|
||||
uword sp
|
||||
@@ -99,6 +113,7 @@ conv {
|
||||
ubyte[] @shared string_out
|
||||
any2uword (str string @AY) -> uword @AY, ubyte @X
|
||||
bin2uword (str string @AY) -> uword @AY
|
||||
hex2long (str string @AY) -> long @R0R1_32
|
||||
hex2uword (str string @AY) -> uword @AY
|
||||
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
|
||||
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
|
||||
@@ -293,7 +308,9 @@ strings {
|
||||
lstripped (str s) -> str
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
|
||||
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
|
||||
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
|
||||
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
|
||||
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
|
||||
@@ -629,6 +646,7 @@ cx16 {
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&long r0r1sl
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@@ -638,6 +656,7 @@ cx16 {
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&long r10r11sl
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
@@ -654,6 +673,7 @@ cx16 {
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&long r12r13sl
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
@@ -670,6 +690,7 @@ cx16 {
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&long r14r15sl
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
@@ -693,6 +714,7 @@ cx16 {
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&long r2r3sl
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
@@ -709,6 +731,7 @@ cx16 {
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&long r4r5sl
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
@@ -725,6 +748,7 @@ cx16 {
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&long r6r7sl
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
@@ -741,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
|
||||
@@ -45,6 +45,20 @@ smallringbuffer {
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
smallstack {
|
||||
ubyte[] buffer
|
||||
ubyte sp
|
||||
free () -> ubyte
|
||||
init ()
|
||||
isempty () -> bool
|
||||
isfull () -> bool
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte value)
|
||||
pushw (uword value)
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
stack {
|
||||
uword buffer_ptr
|
||||
uword sp
|
||||
@@ -99,6 +113,7 @@ conv {
|
||||
ubyte[] @shared string_out
|
||||
any2uword (str string @AY) -> uword @AY, ubyte @X
|
||||
bin2uword (str string @AY) -> uword @AY
|
||||
hex2long (str string @AY) -> long @R0R1_32
|
||||
hex2uword (str string @AY) -> uword @AY
|
||||
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
|
||||
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
|
||||
@@ -208,6 +223,7 @@ LIBRARY MODULE NAME: floats
|
||||
floats {
|
||||
const float E
|
||||
const float EPSILON
|
||||
const uword FAC_ADDR
|
||||
const float PI
|
||||
const float TWOPI
|
||||
const float π
|
||||
@@ -420,7 +436,9 @@ strings {
|
||||
lstripped (str s) -> str
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
|
||||
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
|
||||
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
|
||||
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
|
||||
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
|
||||
@@ -741,6 +759,7 @@ cx16 {
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&long r0r1sl
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@@ -750,6 +769,7 @@ cx16 {
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&long r10r11sl
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
@@ -766,6 +786,7 @@ cx16 {
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&long r12r13sl
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
@@ -782,6 +803,7 @@ cx16 {
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&long r14r15sl
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
@@ -805,6 +827,7 @@ cx16 {
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&long r2r3sl
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
@@ -821,6 +844,7 @@ cx16 {
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&long r4r5sl
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
@@ -837,6 +861,7 @@ cx16 {
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&long r6r7sl
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
@@ -853,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
|
||||
@@ -84,6 +84,20 @@ smallringbuffer {
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
smallstack {
|
||||
ubyte[] buffer
|
||||
ubyte sp
|
||||
free () -> ubyte
|
||||
init ()
|
||||
isempty () -> bool
|
||||
isfull () -> bool
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte value)
|
||||
pushw (uword value)
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
stack {
|
||||
ubyte bank
|
||||
uword sp
|
||||
@@ -137,6 +151,7 @@ conv {
|
||||
ubyte[] @shared string_out
|
||||
any2uword (str string @AY) -> uword @AY, ubyte @X
|
||||
bin2uword (str string @AY) -> uword @AY
|
||||
hex2long (str string @AY) -> long @R0R1_32
|
||||
hex2uword (str string @AY) -> uword @AY
|
||||
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
|
||||
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
|
||||
@@ -216,20 +231,20 @@ diskio {
|
||||
exists (str filename) -> bool
|
||||
f_close ()
|
||||
f_close_w ()
|
||||
f_fatlba () -> long, long
|
||||
f_open (str filenameptr) -> bool
|
||||
f_open_w (str filename) -> bool
|
||||
f_open_w_seek (str filename) -> bool
|
||||
f_read (uword bufferpointer, uword num_bytes) -> uword
|
||||
f_read_all (uword bufferpointer) -> uword
|
||||
f_readline (^^ubyte bufptr @AY) clobbers (X) -> ubyte @Y, ubyte @A
|
||||
f_seek (uword pos_hiword, uword pos_loword)
|
||||
f_seek_w (uword pos_hiword, uword pos_loword)
|
||||
f_tell () -> uword @R0, uword @R1, uword @R2, uword @R3
|
||||
f_seek (long position)
|
||||
f_seek_w (long position)
|
||||
f_tell () -> long, long
|
||||
f_write (uword bufferpointer, uword num_bytes) -> bool
|
||||
fastmode (ubyte mode) -> bool
|
||||
get_loadaddress (str filename) -> uword
|
||||
internal_f_open_w (str filename, bool open_for_seeks) -> bool
|
||||
internal_f_tell ()
|
||||
internal_load_routine (str filenameptr, uword address_override, bool headerless) -> uword
|
||||
internal_save_routine (str filenameptr, uword startaddress, uword savesize, bool headerless) -> bool
|
||||
lf_end_list ()
|
||||
@@ -243,7 +258,7 @@ diskio {
|
||||
load_size (ubyte startbank, uword startaddress, uword endaddress) -> long
|
||||
loadlib (str libnameptr, uword libaddress) -> uword
|
||||
mkdir (str name)
|
||||
read4hex () -> uword
|
||||
read8hex () -> long
|
||||
relabel (str name)
|
||||
rename (str oldfileptr, str newfileptr)
|
||||
reset_read_channel ()
|
||||
@@ -300,6 +315,7 @@ LIBRARY MODULE NAME: floats
|
||||
floats {
|
||||
const float E
|
||||
const float EPSILON
|
||||
const uword FAC_ADDR
|
||||
const float PI
|
||||
const float TWOPI
|
||||
const float π
|
||||
@@ -732,7 +748,9 @@ strings {
|
||||
lstripped (str s) -> str
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
|
||||
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
|
||||
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
|
||||
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
|
||||
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
|
||||
@@ -835,13 +853,17 @@ cx16 {
|
||||
const ubyte EXTAPI16_stack_pop
|
||||
const ubyte EXTAPI16_stack_push
|
||||
const ubyte EXTAPI16_test
|
||||
const ubyte EXTAPI_blink_enable
|
||||
const ubyte EXTAPI_clear_status
|
||||
const ubyte EXTAPI_cursor_blink
|
||||
const ubyte EXTAPI_default_palette
|
||||
const ubyte EXTAPI_getlfs
|
||||
const ubyte EXTAPI_has_machine_property
|
||||
const ubyte EXTAPI_iso_cursor_char
|
||||
const ubyte EXTAPI_joystick_ps2_keycodes
|
||||
const ubyte EXTAPI_kbd_leds
|
||||
const ubyte EXTAPI_kbdbuf_clear
|
||||
const ubyte EXTAPI_kbdbuf_get
|
||||
const ubyte EXTAPI_led_update
|
||||
const ubyte EXTAPI_memory_decompress_from_func
|
||||
const ubyte EXTAPI_mouse_set_position
|
||||
@@ -938,6 +960,7 @@ cx16 {
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&long r0r1sl
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@@ -947,6 +970,7 @@ cx16 {
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&long r10r11sl
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
@@ -963,6 +987,7 @@ cx16 {
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&long r12r13sl
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
@@ -979,6 +1004,7 @@ cx16 {
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&long r14r15sl
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
@@ -1002,6 +1028,7 @@ cx16 {
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&long r2r3sl
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
@@ -1018,6 +1045,7 @@ cx16 {
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&long r4r5sl
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
@@ -1034,6 +1062,7 @@ cx16 {
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&long r6r7sl
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
@@ -1050,6 +1079,7 @@ cx16 {
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&long r8r9sl
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
@@ -1141,6 +1171,7 @@ cx16 {
|
||||
bas_psgnote (ubyte voice @A, ubyte note @X, ubyte fracsemitone @Y) clobbers (A,X,Y) -> bool @Pc @bank 10 = $c012
|
||||
bas_psgplaystring (ubyte length @A, str string @XY) clobbers (A,X,Y) @bank 10 = $c018
|
||||
bas_psgwav (ubyte voice @A, ubyte waveform @X) clobbers (A,X,Y) -> bool @Pc @bank 10 = $c015
|
||||
blink_enable (bool enable @X) clobbers (A,X)
|
||||
clock_get_date_time () clobbers (A,X,Y) -> uword @R0, uword @R1, uword @R2, uword @R3 = $ff50
|
||||
clock_set_date_time (uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, uword jiffiesweekday @R3) clobbers (A,X,Y) = $ff4d
|
||||
console_get_char () clobbers (X,Y) -> ubyte @A = $fee1
|
||||
@@ -1163,6 +1194,7 @@ cx16 {
|
||||
getlfs () -> ubyte @X, ubyte @A, ubyte @Y
|
||||
getrambank () -> ubyte @A
|
||||
getrombank () -> ubyte @A
|
||||
has_machine_property (ubyte property @X) clobbers (A,X) -> bool @Pc
|
||||
i2c_batch_read (ubyte device @X, uword buffer @R0, uword length @R1, bool advance @Pc) clobbers (A,Y) -> bool @Pc = $feb4
|
||||
i2c_batch_write (ubyte device @X, uword buffer @R0, uword length @R1, bool advance @Pc) clobbers (A,Y) -> bool @Pc = $feb7
|
||||
i2c_read_byte (ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, bool @Pc = $fec6
|
||||
@@ -1172,6 +1204,8 @@ cx16 {
|
||||
joystick_scan () clobbers (A,X,Y) = $ff53
|
||||
joysticks_detect () -> ubyte
|
||||
joysticks_getall (bool also_keyboard_js) -> uword
|
||||
kbdbuf_clear () clobbers (A)
|
||||
kbdbuf_get () clobbers (X,Y) -> ubyte @A
|
||||
kbdbuf_get_modifiers () -> ubyte @A = $fec0
|
||||
kbdbuf_peek () -> ubyte @A, ubyte @X = $febd
|
||||
kbdbuf_put (ubyte key @A) clobbers (X) = $fec3
|
||||
|
||||
@@ -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
|
||||
@@ -45,6 +45,20 @@ smallringbuffer {
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
smallstack {
|
||||
ubyte[] buffer
|
||||
ubyte sp
|
||||
free () -> ubyte
|
||||
init ()
|
||||
isempty () -> bool
|
||||
isfull () -> bool
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte value)
|
||||
pushw (uword value)
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
stack {
|
||||
uword buffer_ptr
|
||||
uword sp
|
||||
@@ -99,6 +113,7 @@ conv {
|
||||
ubyte[] @shared string_out
|
||||
any2uword (str string @AY) -> uword @AY, ubyte @X
|
||||
bin2uword (str string @AY) -> uword @AY
|
||||
hex2long (str string @AY) -> long @R0R1_32
|
||||
hex2uword (str string @AY) -> uword @AY
|
||||
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
|
||||
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
|
||||
@@ -244,7 +259,9 @@ strings {
|
||||
lstripped (str s) -> str
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
|
||||
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
|
||||
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
|
||||
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
|
||||
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
|
||||
@@ -352,6 +369,7 @@ cx16 {
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&long r0r1sl
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@@ -361,6 +379,7 @@ cx16 {
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&long r10r11sl
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
@@ -377,6 +396,7 @@ cx16 {
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&long r12r13sl
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
@@ -393,6 +413,7 @@ cx16 {
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&long r14r15sl
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
@@ -416,6 +437,7 @@ cx16 {
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&long r2r3sl
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
@@ -432,6 +454,7 @@ cx16 {
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&long r4r5sl
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
@@ -448,6 +471,7 @@ cx16 {
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&long r6r7sl
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
@@ -464,6 +488,7 @@ cx16 {
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&long r8r9sl
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
@@ -537,7 +562,7 @@ txt {
|
||||
scroll_left () clobbers (A,X,Y)
|
||||
scroll_right () clobbers (A,X)
|
||||
scroll_up () clobbers (A,X)
|
||||
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored)
|
||||
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor)
|
||||
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers (A,Y)
|
||||
setclr (ubyte col, ubyte row, ubyte color)
|
||||
size () clobbers (A) -> ubyte @X, ubyte @Y
|
||||
|
||||
@@ -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
|
||||
@@ -25,6 +25,20 @@ smallringbuffer {
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
smallstack {
|
||||
ubyte[] buffer
|
||||
ubyte sp
|
||||
free () -> ubyte
|
||||
init ()
|
||||
isempty () -> bool
|
||||
isfull () -> bool
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte value)
|
||||
pushw (uword value)
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
stack {
|
||||
uword buffer_ptr
|
||||
uword sp
|
||||
@@ -75,6 +89,7 @@ conv {
|
||||
str string_out
|
||||
any2uword (str string) -> uword, ubyte
|
||||
bin2uword (str string) -> uword
|
||||
hex2long (str string) -> long
|
||||
hex2uword (str string) -> uword
|
||||
internal_str_ub (ubyte value, str out_ptr)
|
||||
internal_str_uw (uword value, str out_ptr)
|
||||
@@ -311,7 +326,9 @@ strings {
|
||||
lstripped (str s) -> str
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
nappend (str target, str suffix, ubyte maxlength) -> ubyte
|
||||
ncompare (str st1, str st2, ubyte length) -> byte
|
||||
ncopy (str source, str target, ubyte maxlength) -> ubyte
|
||||
rfind (str stringptr, ubyte character) -> ubyte, bool
|
||||
right (str source, ubyte slen, str target)
|
||||
rstrip (str s)
|
||||
@@ -387,6 +404,7 @@ cx16 {
|
||||
&ubyte r0L
|
||||
&bool r0bH
|
||||
&bool r0bL
|
||||
&long r0r1sl
|
||||
&word r0s
|
||||
&byte r0sH
|
||||
&byte r0sL
|
||||
@@ -396,6 +414,7 @@ cx16 {
|
||||
&ubyte r10L
|
||||
&bool r10bH
|
||||
&bool r10bL
|
||||
&long r10r11sl
|
||||
&word r10s
|
||||
&byte r10sH
|
||||
&byte r10sL
|
||||
@@ -412,6 +431,7 @@ cx16 {
|
||||
&ubyte r12L
|
||||
&bool r12bH
|
||||
&bool r12bL
|
||||
&long r12r13sl
|
||||
&word r12s
|
||||
&byte r12sH
|
||||
&byte r12sL
|
||||
@@ -428,6 +448,7 @@ cx16 {
|
||||
&ubyte r14L
|
||||
&bool r14bH
|
||||
&bool r14bL
|
||||
&long r14r15sl
|
||||
&word r14s
|
||||
&byte r14sH
|
||||
&byte r14sL
|
||||
@@ -451,6 +472,7 @@ cx16 {
|
||||
&ubyte r2L
|
||||
&bool r2bH
|
||||
&bool r2bL
|
||||
&long r2r3sl
|
||||
&word r2s
|
||||
&byte r2sH
|
||||
&byte r2sL
|
||||
@@ -467,6 +489,7 @@ cx16 {
|
||||
&ubyte r4L
|
||||
&bool r4bH
|
||||
&bool r4bL
|
||||
&long r4r5sl
|
||||
&word r4s
|
||||
&byte r4sH
|
||||
&byte r4sL
|
||||
@@ -483,6 +506,7 @@ cx16 {
|
||||
&ubyte r6L
|
||||
&bool r6bH
|
||||
&bool r6bL
|
||||
&long r6r7sl
|
||||
&word r6s
|
||||
&byte r6sH
|
||||
&byte r6sL
|
||||
@@ -499,6 +523,7 @@ cx16 {
|
||||
&ubyte r8L
|
||||
&bool r8bH
|
||||
&bool r8bL
|
||||
&long r8r9sl
|
||||
&word r8s
|
||||
&byte r8sH
|
||||
&byte r8sL
|
||||
|
||||
@@ -18,7 +18,7 @@ The language
|
||||
- Identifiers and string literals can contain non-ASCII characters so for example ``knäckebröd`` and ``見せしめ`` are valid identifiers.
|
||||
- There's usually a single statement per line. There is no statement separator.
|
||||
- Semicolon ``;`` is used to start a line comment. Multi-line comments are also possible by enclosing it all in ``/*`` and ``*/``.
|
||||
- Ternary operator ``x ? value1 : value2`` is available in the form of an *if-expression*: ``if x value1 else value2``
|
||||
- Ternary operator ``x ? value1 : value2`` is available in the form of an *if-expression*: ``if x [then] value1 else value2``
|
||||
- There's a Swift/Zig/Go style ``defer`` statement for delayed cleanup is available in the subroutine scope.
|
||||
- Qualified names are searched from within the top level namespace (so you have to provide the full qualified name). Unqualified names are locally scoped.
|
||||
- A trailing comma is allowed optionally in array literals: [1,2,3,] is a valid array of values 1, 2 and 3.
|
||||
|
||||
@@ -228,7 +228,7 @@ One or more .p8 module files
|
||||
|
||||
``-slabshigh``
|
||||
put memory() slabs in high memory area instead of at the end of the program.
|
||||
On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.
|
||||
On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.
|
||||
|
||||
``-nosourcelines``
|
||||
Do not include the original prog8 source code lines as comments in the generated assembly code file,
|
||||
@@ -263,7 +263,7 @@ One or more .p8 module files
|
||||
|
||||
c64: $C000 - $CFFF ; 4 kB, and the specified rambank number is ignored
|
||||
|
||||
cx16: $A000 - $BFFF ; 8 kB in the specified HiRAM bank (note: no auto bank switching is done, you must make sure yourself that this HiRAM bank is active when accessing these variables!)
|
||||
cx16: $A000 - $BFFF ; 8 kB in the specified HIRAM bank (note: no auto bank switching is done, you must make sure yourself that this HIRAM bank is active when accessing these variables!)
|
||||
|
||||
If you use this option, you can no longer use the part of the above memory area that is
|
||||
alotted to the variables, for your own purposes. The output of the 64tass assembler step at the
|
||||
|
||||
@@ -200,3 +200,7 @@ If multiple options are listed above, you can select which one you want to launc
|
||||
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
|
||||
Look in the :source:`syntax-files <syntax-files>` directory in the github repository to find them.
|
||||
|
||||
**CTags:** for editors that support them, such as Vim, there is an effort by a member of the cummunity to provide "ctags" files for Prog8.
|
||||
The basic purpose of ctags is to index the definitions from your source code and let you find them easily.
|
||||
It is not really to find each use of print for example, but to find where print is defined.
|
||||
Visit `the Github repository <https://github.com/gillham/prog8ctags>`_ for the files and detailed usage information.
|
||||
@@ -312,7 +312,7 @@ Routines to load and save "BMX" files, the CommanderX16 bitmap file format:
|
||||
`BMX file format specification <https://cx16forum.com/forum/viewtopic.php?t=6945>`_
|
||||
Only the *uncompressed* bitmaps variant is supported in this library for now.
|
||||
|
||||
The routines are designed to be fast and bulk load/save the data directly into or from vram,
|
||||
The routines are designed to be fast and they bulk load/save the data directly into or from vram,
|
||||
without the need to buffer something in main memory.
|
||||
|
||||
For details about what routines are available, have a look at
|
||||
@@ -320,25 +320,44 @@ the :source:`bmx source code <compiler/res/prog8lib/cx16/bmx.p8>` .
|
||||
There's also the "showbmx" example to look at.
|
||||
|
||||
|
||||
buffers (experimental)
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
buffers
|
||||
^^^^^^^
|
||||
|
||||
A small library providing a 8 KB stack, an 8 KB ringbuffer, and a fast 256 bytes ringbuffer.
|
||||
API is experimental and may change or disappear in a future version.
|
||||
Stack is a LIFO container, ringbuffers are FIFO containers.
|
||||
On the Commander X16 the stack and ringbuffer will use a HiRAM bank instead of system ram,
|
||||
you have to initialize that via the init(bank) routine.
|
||||
Provides few data buffer routines. These are available:
|
||||
|
||||
Read the :source:`buffers source code <compiler/res/prog8lib/diskio.p8>`
|
||||
to see what's in there. Note that the init() routines have that extra bank parameter on the X16.
|
||||
- ``smallstack`` a fast 256 byte stack (LIFO) that is independent of the CPU stack.
|
||||
- ``smallringbuffer`` a fast 256 byte ringbuffer (FIFO).
|
||||
- ``stack`` a stack (LIFO) with up to 8Kb of data.
|
||||
- ``ringbuffer`` a ringbuffer (FIFO) with up to 8Kb of data.
|
||||
|
||||
On the Commander X16 the 8Kb stack and ringbuffer implementations use a HIRAM bank instead of regular system memory.
|
||||
You tell it which bank to use by calling ``init(bank)`` with the bank number as argument.
|
||||
|
||||
Read the :source:`buffers source code <compiler/res/prog8lib/buffers.p8>`
|
||||
to see what's in there. Note that on the X16, the init() routines have that extra bank parameter.
|
||||
|
||||
|
||||
compression (slightly experimental)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
cbm
|
||||
^^^
|
||||
Commodore (CBM) common variables, vectors and kernal routines. This 'library' is part of syslib and as such is always available.
|
||||
There's too much in this library to list in the docs here, but you can find things like:
|
||||
|
||||
Routines for data compression and decompression. Currently only the 'ByteRun1' aka 'PackBits' RLE encoding
|
||||
is available. This is the compression that was also used in Amiga IFF images and in old MacPaint images.
|
||||
API is slightly experimental and may change in a future version.
|
||||
- memory mapped variables such as ``TIME_HI``, ``TIME_MID`` and ``TIME_LO`` (the 3 bytes making up the jiffy clock)
|
||||
- cbm kernal routine extsubs such as ``CHROUT`` and ``PLOT``
|
||||
- helper routines such as ``GETIN2`` and ``RDTIML`` (convenience wrappers around existing kernal routines)
|
||||
|
||||
It has way too much to include here, you have to study the
|
||||
:source:`syslib source code <compiler/res/prog8lib/c64/syslib.p8>` to see what is there.
|
||||
The version linked here is the C64 version itself - on other targets in the CBM family (PET, C128 etc)
|
||||
it will contain different things based on what is available there. But the stuff common to the CBM family of compiler targets is there.
|
||||
|
||||
|
||||
compression
|
||||
^^^^^^^^^^^
|
||||
|
||||
Routines for data compression and decompression.
|
||||
For compression the 'ByteRun1' aka 'PackBits' RLE encoding is available, this is the compression that was used in old MacPaint and Amiga IFF images.
|
||||
Decompressors are available for RLE, TSCrunch and ZX0 (Salvador).
|
||||
|
||||
``encode_rle (uword data, uword size, uword target, bool is_last_block) -> uword``
|
||||
Compress the given data block using ByteRun1 aka PackBits RLE encoding.
|
||||
@@ -383,10 +402,10 @@ API is slightly experimental and may change in a future version.
|
||||
Decompress a block of data compressed in the TSCrunch format *inplace*.
|
||||
This can save an extra memory buffer if you are reading crunched data from a file into a buffer.
|
||||
It has extremely fast decompression (approaching RLE speeds),
|
||||
better compression as RLE, but slightly worse compression ration than LZSA.
|
||||
better compression than RLE, but slightly worse compression ratio than LZSA.
|
||||
See https://github.com/tonysavon/TSCrunch for the compression format and compressor tool.
|
||||
**NOTE:** for speed reasons this decompressor is *not* bank-aware and *not* I/O register aware;
|
||||
it only outputs to a memory buffer somewhere in the active 64 Kb address range.
|
||||
it only outputs to a memory buffer somewhere in the 64 Kb main memory address range.
|
||||
|
||||
.. note::
|
||||
The TSCrunch in-place format is a bit different than regular memory decompression.
|
||||
@@ -410,7 +429,7 @@ API is slightly experimental and may change in a future version.
|
||||
See https://github.com/emmanuel-marty/salvador for the compressor tool.
|
||||
**NOTE:** You have to use it with the "-classic" option to produce a data format that this decoder can handle!
|
||||
**NOTE:** for speed reasons this decompressor is *not* bank-aware and *not* I/O register aware;
|
||||
it only outputs to a memory buffer somewhere in the active 64 Kb address range.
|
||||
it only outputs to a memory buffer somewhere in the 64 Kb main memory address range.
|
||||
|
||||
|
||||
conv
|
||||
@@ -422,7 +441,6 @@ Routines to convert strings to numbers or vice versa.
|
||||
- strings in decimal, hex and binary format into numbers (bytes, words)
|
||||
|
||||
Read the :source:`conv source code <compiler/res/prog8lib/conv.p8>`
|
||||
|
||||
to see what's in there.
|
||||
|
||||
|
||||
@@ -430,13 +448,15 @@ coroutines
|
||||
^^^^^^^^^^
|
||||
|
||||
Provides a system to make cooperative multitasking programs via coroutines.
|
||||
A 'coroutine' is a subroutine whose execution you can pause and resume.
|
||||
This library handles the voodoo for you to switch between such coroutines transparently,
|
||||
so it can seem that your program is executing many subroutines at the same time.
|
||||
A 'coroutine' is a subroutine whose execution can be paused and resumed. This is done
|
||||
in a cooperative way where the coroutine calls ``coroutines.yield`` to pause and yield control back to the
|
||||
system (which then selects the next coroutine to run).
|
||||
This way it can seem that your program is executing many subroutines at the same time (without the use of an interrupt routine).
|
||||
This library handles the voodoo required to switch between such coroutines.
|
||||
|
||||
Read the :source:`coroutines source code <compiler/res/prog8lib/coroutines.p8>`
|
||||
to see what's in there. And look at the ``multitasking`` example to see how it can be used.
|
||||
Here is a minimal example (if the library gets more stable, better docs will be written here)::
|
||||
Better docs will be written here in the manual, at some point, but until then: here is a minimal example::
|
||||
|
||||
%import coroutine
|
||||
|
||||
@@ -460,12 +480,8 @@ Here is a minimal example (if the library gets more stable, better docs will be
|
||||
cx16
|
||||
^^^^
|
||||
|
||||
This is available on *all targets*, it is always imported as part of syslib.
|
||||
On the Commander X16 this module contains a *whole bunch* of things specific to that machine.
|
||||
It's way too much to include here, you have to study the
|
||||
:source:`syslib source code <compiler/res/prog8lib/cx16/syslib.p8>`
|
||||
to see what is there.
|
||||
|
||||
Despite its name, this 'module' is available on *all targets*; it also is always available because it is a part of syslib.
|
||||
On the Commander X16 this module contains a *whole bunch* of things specific to that machine
|
||||
On the other targets, it only contains the definition of the 16 memory-mapped virtual registers
|
||||
(cx16.r0 - cx16.r15) and the following utility routines:
|
||||
|
||||
@@ -475,15 +491,9 @@ On the other targets, it only contains the definition of the 16 memory-mapped vi
|
||||
``restore_virtual_registers()``
|
||||
restore the values of all 16 virtual registers r0 - r15 from the buffer. Might be useful in an IRQ handler to avoid clobbering them.
|
||||
|
||||
``reset_system ()``
|
||||
Soft-reset the system back to initial power-on BASIC prompt. (same as the routine in sys)
|
||||
|
||||
``poweroff_system ()``
|
||||
Powers down the computer.
|
||||
|
||||
``set_led_brightness (ubyte brightness)``
|
||||
Sets the brightness of the activity led on the computer.
|
||||
|
||||
For the Commander X16 version it has way too much to include here, you have to study the
|
||||
:source:`syslib source code <compiler/res/prog8lib/cx16/syslib.p8>` to see what is there.
|
||||
|
||||
cx16logo
|
||||
^^^^^^^^
|
||||
@@ -565,8 +575,7 @@ You have to explicitly output a character 10 on the console to see a newline the
|
||||
emudbg.console_write(iso:"hello\x0a")
|
||||
|
||||
|
||||
Read the :source:`emudbg source code <compiler/res/prog8lib/cx16/emudbg.p8>`
|
||||
to see what's in there.
|
||||
Read the :source:`emudbg source code <compiler/res/prog8lib/cx16/emudbg.p8>` to see what's in there.
|
||||
Information about the exposed debug registers is in the `emulator's documentation <https://github.com/X16Community/x16-emulator#debug-io-registers>`_.
|
||||
|
||||
|
||||
@@ -715,8 +724,7 @@ math
|
||||
Low-level integer math routines (which you usually don't have to bother with directly, but they are used by the compiler internally).
|
||||
Pseudo-Random number generators (byte and word).
|
||||
Various 8-bit integer trig functions that use lookup tables to quickly calculate sine and cosines.
|
||||
Usually a custom lookup table is the way to go if your application needs these,
|
||||
but perhaps the provided ones can be of service too.
|
||||
|
||||
|
||||
checksumming
|
||||
''''''''''''
|
||||
@@ -1318,16 +1326,16 @@ textio (txt.*)
|
||||
This will probably be the most used library module. It contains a whole lot of routines
|
||||
dealing with text-based input and output (to the screen). Such as
|
||||
|
||||
- printing strings, numbers and booleans
|
||||
- reading text input from the user via the keyboard
|
||||
- printing strings, numbers and booleans: ``print``, ``chrout``, ``nl``, ``print_ub`` etc.
|
||||
- reading text input from the user via the keyboard: ``input_chars``
|
||||
- filling or clearing the screen and colors
|
||||
- scrolling the text on the screen
|
||||
- placing individual characters on the screen
|
||||
- convert petscii to screencode characters
|
||||
|
||||
All routines work with Screencode character encoding, except `print`, `chrout` and `input_chars`,
|
||||
these work with PETSCII encoding instead.
|
||||
All routines work with Screencode character encoding, except ``print``, ``chrout`` and ``input_chars``, these work with PETSCII encoding instead.
|
||||
|
||||
There are *a lot of routines* in this module, many more than mentioned above.
|
||||
Read the :source:`textio source code <compiler/res/prog8lib/cx16/textio.p8>`
|
||||
to see what's in there. (Note: slight variations for different compiler targets)
|
||||
|
||||
|
||||
@@ -735,8 +735,8 @@ if expression
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Similar to the if statement, but this time selects one of two possible values as the outcome of the expression,
|
||||
depending on the condition. You write it as ``if <condition> <value1> else <value2>`` and it can be
|
||||
used anywhere an expression is used to assign or pass a value.
|
||||
depending on the condition. You write it as ``if <condition> [then] <value1> else <value2>`` (the then keyword is optional)
|
||||
and it can be used anywhere an expression is used to assign or pass a value.
|
||||
The first value will be used if the condition is true, otherwise the second value is used.
|
||||
Sometimes it may be more legible if you surround the condition expression with parentheses so it is better
|
||||
separated visually from the first value following it.
|
||||
@@ -1155,6 +1155,7 @@ so pay attention to any jumps and rts instructions in the inlined code!
|
||||
|
||||
- ``cx16.r0`` - ``cx16.r15`` (memory-mapped **uword** values, most often these are used)
|
||||
- ``cx16.r0s`` - ``cx16.r15s`` (memory-mapped **word** values, used when you need a signed word)
|
||||
- ``cx16.r0r1sl`` - ``cx16.r14r15sl`` (memory-mapped **long** values in 2 consecutive virtual registers, used when you need a signed long)
|
||||
- ``cx16.r0H``, ``cx16.r0L`` (for each r0..r15; memory-mapped **ubyte** values, both bytes of the register)
|
||||
- ``cx16.r0sH``, ``cx16.r0sL`` (for each r0..r15; memory-mapped **byte** values, both bytes of the register)
|
||||
- ``cx16.r0bH``, ``cx16.r0bL`` (for each r0..r15; memory-mapped **bool** values, both bytes of the register)
|
||||
|
||||
@@ -261,3 +261,40 @@ Address-Of: untyped vs typed
|
||||
``&`` still returns an untyped (uword) pointer, as it did in older Prog8 versions. This is for backward compatibility reasons so existing programs don't break.
|
||||
The new *double ampersand* operator ``&&`` returns a *typed* pointer to the value. The semantics are slightly different from the old untyped address-of operator, because adding or subtracting
|
||||
a number from a typed pointer uses *pointer arithmetic* that takes the size of the value that it points to into account.
|
||||
|
||||
|
||||
Accessing struct definitions in Assembly code
|
||||
---------------------------------------------
|
||||
|
||||
Prog8 lets you query the size of a struct type, and the offsets of a field.
|
||||
You can do the same in assembly code if needed: the struct definition gets written as `.struct` into the assembly file::
|
||||
|
||||
; prog8 struct declaration:
|
||||
struct Node {
|
||||
ubyte type
|
||||
uword value
|
||||
bool flag
|
||||
}
|
||||
|
||||
; generates this assembly code:
|
||||
; (the symbol prefixes are explained in the 'Technical details' chapter)
|
||||
p8b_main.p8t_Node .struct f0,f1,f2
|
||||
p8v_type .byte \f0
|
||||
p8v_value .word \f1
|
||||
p8v_flag .byte \f2
|
||||
.endstruct
|
||||
|
||||
|
||||
64tass then lets you query that information::
|
||||
|
||||
; prog8 code:
|
||||
ubyte size = sizeof(Node)
|
||||
ubyte offset1 = offsetof(Node.type)
|
||||
ubyte offset2 = offsetof(Node.value)
|
||||
ubyte offset3 = offsetof(Node.flag)
|
||||
|
||||
; assembly equivalents (64tass syntax):
|
||||
lda #size(p8t_Node)
|
||||
lda #p8t_Node.p8v_type
|
||||
lda #p8t_Node.p8v_value
|
||||
lda #p8t_Node.p8v_flag
|
||||
@@ -357,4 +357,12 @@ There are a few things to note:
|
||||
|
||||
.. note::
|
||||
The ROMable code generation is still quite experimental. Problems may still surface, and perhaps
|
||||
stuff will change a bit in a future compiler version.
|
||||
stuff will change a bit in a future compiler version.
|
||||
|
||||
|
||||
|
||||
Formal ANTLR4 syntax and grammar definition
|
||||
-------------------------------------------
|
||||
|
||||
.. literalinclude :: ../../parser/src/main/antlr/Prog8ANTLR.g4
|
||||
:language: antlr
|
||||
@@ -1,41 +1,45 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
Weird Heisenbug
|
||||
^^^^^^^^^^^^^^^
|
||||
- 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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- 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)
|
||||
- make $8000000 a valid long integer (-2147483648) this is more involved than you think. To make this work: long \|= $80000000
|
||||
- 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 (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
|
||||
- allow memory() to occur in array initializer (maybe needed for 2 dimensional arrays?) i.e. make it a constant (see github issue #192)
|
||||
- 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" ?
|
||||
- enums?
|
||||
- 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)
|
||||
- add float support to the configurable compiler targets
|
||||
- add float support to the configurable compiler targets. Restrictions: just have "cbm-style floats" as an option (to that it can slot into the current float codegen), where all you have to specify is the addresses of AYINT and GIVAYF and FADDT and all their friends.
|
||||
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
|
||||
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first), maybe only when qualified symbol starts with '.' such as: .local.value = 33
|
||||
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
|
||||
@@ -50,15 +54,20 @@ Future Things and Ideas
|
||||
(this is already done hardcoded for several of the builtin functions)
|
||||
- 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
|
||||
-----
|
||||
- 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?)
|
||||
- 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!)
|
||||
- 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
|
||||
@@ -80,15 +89,15 @@ 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)
|
||||
- implement fast code paths for TODO("inplace split....
|
||||
- implement more TODOs in AssignmentGen
|
||||
- do something with the 'split' tag on split word arrays
|
||||
- add more optimizations in IRPeepholeOptimizer
|
||||
- add more optimizations in IRPeepholeOptimizer, implement the TODOs in there at least
|
||||
- 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.
|
||||
@@ -108,19 +117,23 @@ 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
|
||||
- more optimized operator handling of different types, for example uword a ^ byte b now does a type cast of b to word first
|
||||
- optimize longEqualsValue() for const and variable operands to not assign needlessly to R0-R3.
|
||||
- optimize optimizedBitwiseExpr() for const and variable operands to not assign needlessly to R0-R3.
|
||||
- optimize longEqualsValue() for long const and variable operands to not assign needlessly to R0-R3.
|
||||
- optimize optimizedBitwiseExpr() for long const and variable operands to not assign needlessly to R0-R3.
|
||||
- optimize inplacemodificationLongWithLiteralval() for more shift values such as 8, 16, 24 etc but take sign bit into account!
|
||||
- optimize simple cases in funcPeekL and funcPokeL
|
||||
- bind types in the Ast much sooner than the simplifiedAst creation, so that we maybe could get rid of InferredType ?
|
||||
- longvar = lptr^^ now goes via temporary registers, optimize this to avoid using temps. Also check lptr^^ = lvar.
|
||||
- Port more benchmarks from https://thred.github.io/c-bench-64/ to prog8 and see how it stacks up. (see benchmark-c/ directory)
|
||||
- Since fixing the missing zp-var initialization, programs grew in size again because STZ's reappeared. Can we add more intelligent (and correct!) optimizations to remove those STZs that might be redundant again?
|
||||
- in Identifier: use typedarray of strings instead of listOf? Other places?
|
||||
- Compilation speed regression: test/comparisons/test_word_lte.p8 compilation takes twice as long as with prog8 10.5
|
||||
- Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time
|
||||
- Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression"
|
||||
- Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression" ... but maybe postpone until codegen from IR, where it seems solved?
|
||||
- optimize floats.cast_from_long and floats.cast_as_long by directly accessing FAC bits?
|
||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
|
||||
This will probably need the register categorization from the IR explained there, for the old 6502 codegen there is not enough information to act on
|
||||
|
||||
@@ -225,7 +225,7 @@ value datatype
|
||||
0 .. 255 ubyte
|
||||
-32768 .. 32767 word
|
||||
0 .. 65535 uword
|
||||
-2147483647 .. 2147483647 long (there is no unsigned long right now)
|
||||
-2147483648 .. 2147483647 long (there is no unsigned long right now)
|
||||
========================= =================
|
||||
|
||||
Numeric expressions usually 'stay within their type' unless a cast is used, see :ref:`arithmetic`.
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
%import textio
|
||||
%import strings
|
||||
|
||||
; Animal guessing game where the computer gets smarter every time.
|
||||
; Note: this program can be compiled for multiple target systems.
|
||||
|
||||
main {
|
||||
const ubyte database_size = 100
|
||||
|
||||
uword animal_names_buf = memory("animalnames", 500, 0) ; area to store all animal names in, in sequence
|
||||
uword questions_buf = memory("questions", 2000, 0) ; area to store all question texts in, in sequence
|
||||
uword animal_names_ptr
|
||||
uword questions_ptr
|
||||
|
||||
uword[database_size] animals ; pointers to the animal names
|
||||
uword[database_size] questions ; pointers to the question texts
|
||||
uword[database_size] answers_questions ; tree entries for question choices, indexed by question id, pair of (msb=yes, lsb=no) follow up question id (or 0 if it's an animal leaf node)
|
||||
uword[database_size] answers_animals ; tree entries for animal leafs, indexed by question id, pair of (msb=yes, lsb=no) animal id
|
||||
|
||||
ubyte new_animal_number
|
||||
ubyte new_question_number
|
||||
str userinput = "x"*80
|
||||
|
||||
sub start() {
|
||||
; initialize the database
|
||||
animal_names_ptr = animal_names_buf
|
||||
questions_ptr = questions_buf
|
||||
|
||||
animals[0] = 0
|
||||
animals[1] = "dolphin"
|
||||
animals[2] = "eagle"
|
||||
animals[3] = "horse"
|
||||
new_animal_number = 4
|
||||
|
||||
questions[0] = 0
|
||||
questions[1] = "does it swim"
|
||||
questions[2] = "can it fly"
|
||||
new_question_number = 3
|
||||
|
||||
answers_questions[0] = mkword(0, 0)
|
||||
answers_questions[1] = mkword(0, 2)
|
||||
answers_questions[2] = mkword(0, 0)
|
||||
|
||||
answers_animals[0] = mkword(0, 0)
|
||||
answers_animals[1] = mkword(1, 0)
|
||||
answers_animals[2] = mkword(2, 3)
|
||||
|
||||
; play the game
|
||||
game()
|
||||
}
|
||||
|
||||
sub game() {
|
||||
repeat {
|
||||
ubyte current_question = 1
|
||||
txt.print("\n\nanimal guessing game!\nthink of an animal.\n")
|
||||
bool guessed = false
|
||||
while not guessed {
|
||||
txt.print(questions[current_question])
|
||||
txt.print("? ")
|
||||
if txt.input_chars(userinput)!=0 {
|
||||
txt.nl()
|
||||
ubyte animal_number
|
||||
if userinput[0]=='y' {
|
||||
animal_number = msb(answers_animals[current_question])
|
||||
if animal_number!=0 {
|
||||
guess(current_question, true, animal_number)
|
||||
guessed = true
|
||||
} else {
|
||||
current_question = msb(answers_questions[current_question])
|
||||
}
|
||||
}
|
||||
else if userinput[0]=='n' {
|
||||
animal_number = lsb(answers_animals[current_question])
|
||||
if animal_number!=0 {
|
||||
guess(current_question, false, animal_number)
|
||||
guessed = true
|
||||
} else {
|
||||
current_question = lsb(answers_questions[current_question])
|
||||
}
|
||||
}
|
||||
else {
|
||||
txt.print("answer (y)es or (n)o please.\n")
|
||||
}
|
||||
} else
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub guess(ubyte question_number, bool given_answer_yesno, ubyte animal_number) {
|
||||
txt.print("is it a ")
|
||||
txt.print(animals[animal_number])
|
||||
txt.print("? ")
|
||||
void txt.input_chars(userinput)
|
||||
if userinput[0] == 'y' {
|
||||
txt.print("\n\nsee, i knew it!\n")
|
||||
return
|
||||
}
|
||||
|
||||
str name = "x"*30
|
||||
txt.print("\n\ni give up. what is it? ")
|
||||
void txt.input_chars(name)
|
||||
txt.print("\nwhat yes-no question would best articulate the difference\nbetween a ")
|
||||
txt.print(animals[animal_number])
|
||||
txt.print(" and a ")
|
||||
txt.print(name)
|
||||
txt.print("? ")
|
||||
void txt.input_chars(userinput)
|
||||
txt.print("\nin case of the ")
|
||||
txt.print(name)
|
||||
txt.print(", what is the answer to that question? ")
|
||||
str answer = "x"*10
|
||||
void txt.input_chars(answer)
|
||||
|
||||
animals[new_animal_number] = animal_names_ptr
|
||||
questions[new_question_number] = questions_ptr
|
||||
animal_names_ptr += strings.copy(name, animal_names_ptr)+1 ; store animal name in buffer
|
||||
questions_ptr += strings.copy(userinput, questions_ptr)+1 ; store question in buffer
|
||||
|
||||
answers_questions[new_question_number] = mkword(0, 0)
|
||||
if answer[0]=='y'
|
||||
answers_animals[new_question_number] = mkword(new_animal_number, animal_number)
|
||||
else
|
||||
answers_animals[new_question_number] = mkword(animal_number, new_animal_number)
|
||||
|
||||
uword previous_animals = answers_animals[question_number]
|
||||
uword previous_questions = answers_questions[question_number]
|
||||
if given_answer_yesno {
|
||||
answers_animals[question_number] = mkword(0, lsb(previous_animals))
|
||||
answers_questions[question_number] = mkword(new_question_number, lsb(previous_questions))
|
||||
} else {
|
||||
answers_animals[question_number] = mkword(msb(previous_animals), 0)
|
||||
answers_questions[question_number] = mkword(msb(previous_questions), new_question_number)
|
||||
}
|
||||
|
||||
new_animal_number++
|
||||
new_question_number++
|
||||
|
||||
txt.print("\n\nthanks, i know more animals now! let's try again.\n")
|
||||
}
|
||||
}
|
||||
@@ -482,6 +482,16 @@ cx16 {
|
||||
&word r14s = $1b1c
|
||||
&word r15s = $1b1e
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $1b00
|
||||
&long r2r3sl = $1b04
|
||||
&long r4r5sl = $1b08
|
||||
&long r6r7sl = $1b0c
|
||||
&long r8r9sl = $1b00
|
||||
&long r10r11sl = $1b04
|
||||
&long r12r13sl = $1b08
|
||||
&long r14r15sl = $1b0c
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $1b00
|
||||
&ubyte r1L = $1b02
|
||||
|
||||
@@ -741,6 +741,16 @@ cx16 {
|
||||
&word r14s = $002c
|
||||
&word r15s = $002e
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $0010
|
||||
&long r2r3sl = $0014
|
||||
&long r4r5sl = $0018
|
||||
&long r6r7sl = $001c
|
||||
&long r8r9sl = $0020
|
||||
&long r10r11sl = $0024
|
||||
&long r12r13sl = $0028
|
||||
&long r14r15sl = $002c
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0010
|
||||
&ubyte r1L = $0012
|
||||
|
||||
@@ -414,6 +414,16 @@ cx16 {
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $0002
|
||||
&long r2r3sl = $0006
|
||||
&long r4r5sl = $000a
|
||||
&long r6r7sl = $000e
|
||||
&long r8r9sl = $0012
|
||||
&long r10r11sl = $0016
|
||||
&long r12r13sl = $001a
|
||||
&long r14r15sl = $001e
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
|
||||
@@ -203,6 +203,16 @@ cx16 {
|
||||
&word r14s = $cffc
|
||||
&word r15s = $cffe
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $cfe0
|
||||
&long r2r3sl = $cfe4
|
||||
&long r4r5sl = $cfe8
|
||||
&long r6r7sl = $cfec
|
||||
&long r8r9sl = $cff0
|
||||
&long r10r11sl = $cff4
|
||||
&long r12r13sl = $cff8
|
||||
&long r14r15sl = $cffc
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $cfe0
|
||||
&ubyte r1L = $cfe2
|
||||
|
||||
@@ -203,6 +203,16 @@ cx16 {
|
||||
&word r14s = $001e
|
||||
&word r15s = $0020
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $0002
|
||||
&long r2r3sl = $0006
|
||||
&long r4r5sl = $000a
|
||||
&long r6r7sl = $000e
|
||||
&long r8r9sl = $0012
|
||||
&long r10r11sl = $0016
|
||||
&long r12r13sl = $001a
|
||||
&long r14r15sl = $001e
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
|
||||
@@ -199,6 +199,16 @@ cx16 {
|
||||
&word r14s = $7ffc
|
||||
&word r15s = $7ffe
|
||||
|
||||
; signed long versions
|
||||
&long r0r1sl = $7fe0
|
||||
&long r2r3sl = $7fe4
|
||||
&long r4r5sl = $7fe8
|
||||
&long r6r7sl = $7fec
|
||||
&long r8r9sl = $7fe0
|
||||
&long r10r11sl = $7fe4
|
||||
&long r12r13sl = $7fe8
|
||||
&long r14r15sl = $7fec
|
||||
|
||||
; ubyte versions (low and high bytes)
|
||||
&ubyte r0L = $7fe0
|
||||
&ubyte r1L = $7fe2
|
||||
|
||||
@@ -8,4 +8,4 @@ Look in the Makefile to see how to build or run the various programs.
|
||||
and example program, you can find those efforts here on GitHub: https://github.com/adiee5/prog8-nes-target
|
||||
|
||||
*gillham* has been working on a few other compilation targets, such as VIC-20 (various editions), Foenix, and CX16OS.
|
||||
you can find them here on GitHub: https://github.com/gillham/prog8targets
|
||||
These will be much more complete than the examples here. You can find them on GitHub: https://github.com/gillham/prog8targets
|
||||
@@ -33,7 +33,7 @@ main {
|
||||
|
||||
txt.print("\nseeking to 1292 and writing a few bytes...\n")
|
||||
if diskio.f_open_w_seek("seektestfile.bin") {
|
||||
diskio.f_seek_w(0, 1292)
|
||||
diskio.f_seek_w(1292)
|
||||
void diskio.f_write("123", 3)
|
||||
diskio.f_close_w()
|
||||
} else {
|
||||
@@ -66,7 +66,7 @@ main {
|
||||
; now the actual seek and read of the last few bytes
|
||||
txt.print("\nseeking to 1290 and reading...\n")
|
||||
if diskio.f_open("seektestfile.bin") {
|
||||
diskio.f_seek(0, 1290)
|
||||
diskio.f_seek(1290)
|
||||
uword ptr = megabuffer
|
||||
do {
|
||||
size = diskio.f_read(ptr, 255)
|
||||
|
||||
@@ -13,6 +13,8 @@ adpcm {
|
||||
; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav
|
||||
; And/or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set the correct block size, -b8)
|
||||
;
|
||||
; NOTE: sox may generate IMA-ADPCM files with a block size different than 256 bytes, which is not supported by this decoder. Use ffmpeg instead.
|
||||
;
|
||||
; NOTE: for speed reasons this implementation doesn't guard against clipping errors.
|
||||
; if the output sounds distorted, lower the volume of the source waveform to 80% and try again etc.
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav
|
||||
; Or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set correct block size)
|
||||
;
|
||||
; NOTE: sox may generate IMA-ADPCM files with a block size different than 256 bytes, which is not supported by this decoder. Use ffmpeg instead.
|
||||
;
|
||||
|
||||
main {
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ main {
|
||||
ubyte vera_rate
|
||||
|
||||
sub start() {
|
||||
;; diskio.fastmode(1)
|
||||
diskio.fastmode(1)
|
||||
txt.print("name of .wav file to play on drive 8: ")
|
||||
while 0==txt.input_chars(MUSIC_FILENAME) {
|
||||
; until user types a name...
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
%import sprites
|
||||
%option no_sysinit
|
||||
|
||||
; play a raw pcm stereo 16 bit audio file at 16021 hz,
|
||||
; play a raw pcm stereo 16 bit audio file at 14877 hz,
|
||||
; with real-time VU meters and sample waveform displays.
|
||||
|
||||
; you can make an appropriate pcm file with one of the following commands:
|
||||
; ffmpeg -i input_audio_file -ac 2 -ar 16021 -f s16le -acodec pcm_s16le music.pcm
|
||||
; sox input_audio_file -e signed-integer -L -b 16 -c 2 -r 16021 -t raw music.pcm
|
||||
; ffmpeg -i input_audio_file -ac 2 -ar 14877 -f s16le -acodec pcm_s16le music.pcm
|
||||
; sox input_audio_file -e signed-integer -L -b 16 -c 2 -r 14877 -t raw music.pcm
|
||||
|
||||
|
||||
main {
|
||||
|
||||
const ubyte vera_rate = 42 ; 16021 hz
|
||||
const ubyte vera_rate = 39 ; 14877 hz
|
||||
str pcmfile = "music.pcm" ; see format specs mentioned above
|
||||
|
||||
sub start() {
|
||||
@@ -34,7 +34,7 @@ main {
|
||||
cx16.GRAPH_put_next_char(cx16.r9L)
|
||||
cx16.r0 = 250
|
||||
cx16.r1 = 20
|
||||
for cx16.r9L in iso:"16kHz stereo"
|
||||
for cx16.r9L in iso:"15kHz stereo"
|
||||
cx16.GRAPH_put_next_char(cx16.r9L)
|
||||
|
||||
cx16.rombank(0) ; activate kernal bank for faster calls
|
||||
|
||||
@@ -8,12 +8,12 @@ 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
|
||||
------------
|
||||
The demo program loads the zsmkit player program in HiRAM bank 1.
|
||||
The demo program loads the zsmkit player program in HIRAM bank 1.
|
||||
The ZSM music file is read into memory too starting from bank 2.
|
||||
The zmskit library routines are defined in zsmkit.p8 as extsubs,
|
||||
using prog8's automatic ram banking mechanism.
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
; PRG size by a lot because they embed a large multiplication lookup table.
|
||||
|
||||
|
||||
%import textio
|
||||
%import math
|
||||
%import syslib
|
||||
|
||||
@@ -38,6 +39,8 @@ main {
|
||||
repeat {
|
||||
clear_particles()
|
||||
update_particles()
|
||||
txt.home()
|
||||
txt.print_uw(active_particles)
|
||||
sys.waitvsync()
|
||||
sys.waitvsync()
|
||||
}
|
||||
|
||||
@@ -1,33 +1,53 @@
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
ubyte[] array = ['h', 'e', 'l', 'l', 'o', 0]
|
||||
str name = "hello"
|
||||
^^ubyte @shared ubptr = 2000
|
||||
uword @shared ubptr2 = 3000
|
||||
|
||||
name[5] = '!' ; don't do this in real code...
|
||||
name[5] = 0
|
||||
name[6] = 99 ; out of bounds
|
||||
name[-1] = 99 ; ok
|
||||
name[-5] = 99 ; ok
|
||||
init()
|
||||
reads()
|
||||
prints()
|
||||
writes()
|
||||
reads()
|
||||
prints()
|
||||
|
||||
cx16.r0L = name[5]
|
||||
cx16.r1L = name[6] ; out of bounds
|
||||
cx16.r1L = name[-1] ; ok
|
||||
cx16.r1L = name[-5] ; ok
|
||||
sub init() {
|
||||
@(2000) = 100
|
||||
@(2001) = 101
|
||||
@(2002) = 102
|
||||
@(2500) = 109
|
||||
@(3000) = 200
|
||||
@(3001) = 201
|
||||
@(3002) = 202
|
||||
@(3500) = 209
|
||||
}
|
||||
|
||||
array[5] = '!'
|
||||
array[5] = 0
|
||||
array[6] = 99 ; out of bounds
|
||||
array[-1] = 99 ; ok
|
||||
array[-5] = 99 ; ok
|
||||
array[-6] = 99 ; ok
|
||||
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()
|
||||
}
|
||||
|
||||
cx16.r0L = array[5]
|
||||
cx16.r1L = array[6] ; out of bounds
|
||||
cx16.r1L = array[-1] ; ok
|
||||
cx16.r1L = array[-5] ; ok
|
||||
cx16.r1L = array[-6] ; ok
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,4 @@ org.gradle.parallel=true
|
||||
org.gradle.daemon=true
|
||||
org.gradle.configuration-cache=false
|
||||
kotlin.code.style=official
|
||||
version=12.0-BETA6
|
||||
version=12.0.1
|
||||
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -272,7 +272,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
dt.isBool -> constant.value.toInt().toString()
|
||||
dt.isFloat -> constant.value.toString()
|
||||
dt.isPointer -> TODO("constant pointer $constant")
|
||||
dt.isInteger -> constant.value.toInt().toHex()
|
||||
dt.isInteger -> constant.value.toLong().toHex()
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
xml.writeCharacters("${constant.typeString} ${constant.name}=$value\n")
|
||||
|
||||
@@ -44,9 +44,8 @@ load reg1, value - load immediate value into register. If y
|
||||
loadm reg1, address - load reg1 with value at memory address
|
||||
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
|
||||
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (0-255, a byte)
|
||||
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
|
||||
loadr reg1, reg2 - load reg1 with value in register reg2, "reg1 = reg2"
|
||||
loadfield reg1, reg2, value - load reg1 with value in memory pointed to by reg2 + value 0-255 (gets a field from a pointer to a struct, like LOADI, but with additional field offset 0-255)
|
||||
loadfield reg1, reg2, value - load reg1 with value in memory indirect, pointed to by reg2 + value 0-65535 (gets a field from a pointer to a struct, like LOADI with additional field offset 0-65535)
|
||||
loadha reg1 - load cpu hardware register A into reg1.b
|
||||
loadhx reg1 - load cpu hardware register X into reg1.b
|
||||
loadhy reg1 - load cpu hardware register Y into reg1.b
|
||||
@@ -58,11 +57,10 @@ loadhfacone fpreg1 - load "cpu hardware register" fac1 into f
|
||||
storem reg1, address - store reg1 at memory address
|
||||
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
|
||||
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (0-255, a byte)
|
||||
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
|
||||
storezm address - store zero at memory address
|
||||
storezi reg1 - store zero at memory pointed to by reg1
|
||||
storezx reg1, address - store zero at memory address, indexed by value in reg1 (0-255, a byte)
|
||||
storefield reg1, reg2, value - store reg1 in memory pointed to by reg2 + value 0-255 (set a field from a pointer to a struct, like STOREI, but with additional field offset 0-255)
|
||||
storefield reg1, reg2, value - store reg1 in memory indirect, pointed to by reg2 + value 0-65535 (set a field from a pointer to a struct, like STOREI with additional field offset 0-65535)
|
||||
storeha reg1 - store reg1.b into cpu hardware register A
|
||||
storehx reg1 - store reg1.b into cpu hardware register X
|
||||
storehy reg1 - store reg1.b into cpu hardware register Y
|
||||
@@ -220,10 +218,12 @@ ffromub fpreg1, reg1 - fpreg1 = reg1 from usigned byte
|
||||
ffromsb fpreg1, reg1 - fpreg1 = reg1 from signed byte
|
||||
ffromuw fpreg1, reg1 - fpreg1 = reg1 from unsigned word
|
||||
ffromsw fpreg1, reg1 - fpreg1 = reg1 from signed word
|
||||
ffromsl fpreg1, reg1 - fpreg1 = reg1 from signed long
|
||||
ftoub reg1, fpreg1 - reg1 = fpreg1 as unsigned byte
|
||||
ftosb reg1, fpreg1 - reg1 = fpreg1 as signed byte
|
||||
ftouw reg1, fpreg1 - reg1 = fpreg1 as unsigned word
|
||||
ftosw reg1, fpreg1 - reg1 = fpreg1 as signed word
|
||||
ftosl reg1, fpreg1 - reg1 = fpreg1 as signed long
|
||||
fpow fpreg1, fpreg2 - fpreg1 = fpreg1 to the power of fpreg2
|
||||
fabs fpreg1, fpreg2 - fpreg1 = abs(fpreg2)
|
||||
fcomp reg1, fpreg1, fpreg2 - reg1 = result of comparison of fpreg1 and fpreg2: 0.b=equal, 1.b=fpreg1 is greater, -1.b=fpreg1 is smaller
|
||||
@@ -265,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,
|
||||
@@ -279,7 +278,6 @@ enum class Opcode {
|
||||
STOREM,
|
||||
STOREI,
|
||||
STOREX,
|
||||
STOREIX,
|
||||
STOREZM,
|
||||
STOREZI,
|
||||
STOREZX,
|
||||
@@ -398,10 +396,12 @@ enum class Opcode {
|
||||
FFROMSB,
|
||||
FFROMUW,
|
||||
FFROMSW,
|
||||
FFROMSL,
|
||||
FTOUB,
|
||||
FTOSB,
|
||||
FTOUW,
|
||||
FTOSW,
|
||||
FTOSL,
|
||||
FPOW,
|
||||
FABS,
|
||||
FSIN,
|
||||
@@ -494,14 +494,14 @@ val OpcodesThatEndSSAblock = OpcodesThatBranchUnconditionally + arrayOf(
|
||||
val OpcodesThatSetStatusbitsIncludingCarry = arrayOf(
|
||||
Opcode.BIT,
|
||||
Opcode.CMP,
|
||||
Opcode.CMPI
|
||||
Opcode.CMPI,
|
||||
Opcode.SGN
|
||||
)
|
||||
val OpcodesThatSetStatusbitsButNotCarry = arrayOf(
|
||||
Opcode.LOAD,
|
||||
Opcode.LOADM,
|
||||
Opcode.LOADI,
|
||||
Opcode.LOADX,
|
||||
Opcode.LOADIX,
|
||||
Opcode.LOADR,
|
||||
Opcode.LOADHA,
|
||||
Opcode.LOADHX,
|
||||
@@ -548,7 +548,6 @@ val OpcodesThatLoad = arrayOf(
|
||||
Opcode.LOADM,
|
||||
Opcode.LOADI,
|
||||
Opcode.LOADX,
|
||||
Opcode.LOADIX,
|
||||
Opcode.LOADR,
|
||||
Opcode.LOADHA,
|
||||
Opcode.LOADHX,
|
||||
@@ -656,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"),
|
||||
@@ -671,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"),
|
||||
@@ -787,10 +784,12 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.FFROMSB to InstructionFormat.from("F,>fr1,<r1"),
|
||||
Opcode.FFROMUW to InstructionFormat.from("F,>fr1,<r1"),
|
||||
Opcode.FFROMSW to InstructionFormat.from("F,>fr1,<r1"),
|
||||
Opcode.FFROMSL to InstructionFormat.from("F,>fr1,<r1"),
|
||||
Opcode.FTOUB to InstructionFormat.from("F,>r1,<fr1"),
|
||||
Opcode.FTOSB to InstructionFormat.from("F,>r1,<fr1"),
|
||||
Opcode.FTOUW to InstructionFormat.from("F,>r1,<fr1"),
|
||||
Opcode.FTOSW to InstructionFormat.from("F,>r1,<fr1"),
|
||||
Opcode.FTOSL to InstructionFormat.from("F,>r1,<fr1"),
|
||||
Opcode.FPOW to InstructionFormat.from("F,<>fr1,<fr2"),
|
||||
Opcode.FABS to InstructionFormat.from("F,>fr1,<fr2"),
|
||||
Opcode.FCOMP to InstructionFormat.from("F,>r1,<fr1,<fr2"),
|
||||
@@ -910,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 -> {}
|
||||
}
|
||||
}
|
||||
@@ -1086,10 +1087,19 @@ data class IRInstruction(
|
||||
private fun determineReg1Type(): IRDataType? {
|
||||
if(type==IRDataType.FLOAT) {
|
||||
// some float instructions have an integer (byte or word) register as well in reg1
|
||||
return if(opcode in arrayOf(Opcode.FFROMUB, Opcode.FFROMSB, Opcode.FTOUB, Opcode.FTOSB, Opcode.FCOMP, Opcode.LOADIX, Opcode.LOADX, Opcode.STOREX, Opcode.STOREIX, Opcode.STOREZX))
|
||||
IRDataType.BYTE
|
||||
else
|
||||
IRDataType.WORD
|
||||
return when (opcode) {
|
||||
Opcode.FFROMUB,
|
||||
Opcode.FFROMSB,
|
||||
Opcode.FTOUB,
|
||||
Opcode.FTOSB,
|
||||
Opcode.FCOMP,
|
||||
Opcode.LOADX,
|
||||
Opcode.STOREX,
|
||||
Opcode.STOREZX,
|
||||
Opcode.SGN -> IRDataType.BYTE
|
||||
Opcode.FFROMSL, Opcode.FTOSL -> IRDataType.LONG
|
||||
else -> IRDataType.WORD
|
||||
}
|
||||
}
|
||||
if(type==IRDataType.WORD) {
|
||||
// some word instructions have byte reg1
|
||||
@@ -1117,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}")
|
||||
}
|
||||
}
|
||||
|
||||