Compare commits

..
60 Commits
Author SHA1 Message Date
Irmen de Jong 4030a1b9e5 cleanup, animals example removed because it's superseded by the pointers/animalgame example now 2025-12-16 16:48:34 +01:00
Irmen de Jong 0217d05c68 IR: loadfield/storefield now accept offsets up to 65535 2025-12-16 14:56:11 +01:00
Irmen de Jong 8553f3c9f1 IR: loadfield is a better name 2025-12-16 12:57:45 +01:00
Irmen de Jong d157d03ea0 IR: optimize load+add+load into single loadm with offset (also store)
update gradle wrapper to 9.2.0
2025-12-16 12:17:52 +01:00
Irmen de Jong 1799b5f00c updated bundled zsmkit version to 2.8 2025-12-15 18:41:28 +01:00
Irmen de Jong 2ddb5e11b7 updated symboldumps 2025-12-13 02:12:00 +01:00
Irmen de Jong 3aa8b9fd12 turn prefix - numbers into negative numbers asap to avoid type problems later 2025-12-13 01:13:52 +01:00
Irmen de Jong 51300196e8 fix more long comparisons with arrays, fixes #196 2025-12-12 21:01:51 +01:00
Irmen de Jong f7b2f19cba remove restriction about long comparison with expression 2025-12-11 21:43:53 +01:00
Irmen de Jong 014a82a1ee always put all struct types as .struct in asm code to make them all accessible for size and offsets 2025-12-10 21:39:44 +01:00
Irmen de Jong 745cf3d958 IR: removed loadix/storeix instructions 2025-12-09 22:55:53 +01:00
Irmen de Jong c4aa681d58 IR: better name for loadfield/storefield instructions 2025-12-09 00:40:26 +01:00
Irmen de Jong feb0774bbd in benchmark-c routines, use same CIA timer as the original C code did so the program size is better comparable 2025-12-08 01:08:33 +01:00
Irmen de Jong 2ccfb2ac90 fix double evaluation (sign extension) of word to long assignment. Fixes #201 2025-12-07 22:23:59 +01:00
Irmen de Jong 2191c4b7d8 remove redundant expression assignment before multiplying certain words 2025-12-07 14:06:59 +01:00
Irmen de Jong 39c1cf0c3c fix diskio lf_start_list_dirs() and lf_start_list_files() not processing the pattern argument 2025-12-07 05:11:08 +01:00
Irmen de Jong c693be0dd6 fix byte subtract from long
fix IR register check error for %ir segments
2025-12-06 20:40:42 +01:00
Irmen de Jong 0f433055a7 fix pointer dereference division and modulus , fixes #199 2025-12-05 22:19:54 +01:00
Irmen de Jong 76c95ba6fa some more obscure inplace modifications implemented 2025-12-05 21:00:14 +01:00
Irmen de Jong bd272a8567 optimize some specialized cases of long << 2025-12-05 01:08:19 +01:00
Irmen de Jong b38fff76e9 hack to work around struct name prefix inconsistency
see github issue 198
2025-12-03 21:07:08 +01:00
Irmen de Jong 07b7639ff2 better error msg 2025-12-02 22:56:40 +01:00
Irmen de Jong 6871561c09 fix parent node error in sizeof() 2025-12-01 23:21:14 +01:00
Irmen de Jong 77a71138ad fix typo r8r0sl -> r8r9sl 2025-11-30 22:05:57 +01:00
Irmen de Jong 165f94bc93 fix long expression <0 / >0 comparison 2025-11-30 16:57:05 +01:00
Irmen de Jong b75090f928 fix some register clobbering with long operations 2025-11-30 16:44:36 +01:00
Irmen de Jong 62b0c82f93 implement long <=0 and long >=0 comparisons
make sure the adpcm benchmark doesn't use random memory contents
2025-11-30 16:25:58 +01:00
Irmen de Jong b1ef863c7f make $8000000 a valid long integer (-2147483648) 2025-11-26 23:10:59 +01:00
Irmen de Jong e78345410f fix long-- high word wrap bug 2025-11-26 20:13:17 +01:00
Irmen de Jong 8b86f97aaa Implement struct field long and signed word bitshifts. Fixes #194 2025-11-25 22:28:28 +01:00
Irmen de Jong 93135774e6 fix IR signed bitshift right 2025-11-25 22:16:28 +01:00
Irmen de Jong a64f27c6b0 add cx16.r0r1sl, cx16.r2r3sl, ... that memory-map signed longs on the virtual registers 2025-11-25 21:13:03 +01:00
Irmen de Jong 69ef63c96d tweaks 2025-11-24 22:23:40 +01:00
Irmen de Jong df1a2a1611 also optimize BRA+RTS into just BRA
release 12.0
2025-11-23 15:18:05 +01:00
Irmen de Jong d19a3af9ed change some single use float global constants to their asm proc 2025-11-21 21:35:51 +01:00
Irmen de Jong 352c11ad9f optimize float<>0 into sgn(float)<>0 2025-11-21 00:57:43 +01:00
Irmen de Jong 9504711fc7 IR/VM: SGN sets status bits 2025-11-19 21:37:04 +01:00
Irmen de Jong 590feda903 IR: fix sgn(float) register type error 2025-11-19 21:11:31 +01:00
Irmen de Jong 6e7e2922bf moved the float <> long cast routines to float.asm where the other cast routines are too 2025-11-19 18:45:32 +01:00
Irmen de Jong 01df1f0083 way more efficient implementation of internal_cast_as_long now using QINT 2025-11-19 18:32:40 +01:00
Irmen de Jong 397299bd1d implement 6502 codegen for casting float to long (super slow...) 2025-11-19 00:05:18 +01:00
Irmen de Jong c275aacd38 implement 6502 codegen for casting long to float 2025-11-18 23:26:12 +01:00
Irmen de Jong 6a6e18773e C64: key repeat is now enabled at program startup, to fall in line with the default key repeat behavior on the C128 and X16. 2025-11-18 21:09:13 +01:00
Irmen de Jong 99e037489b IR: added float<>long casts, cx16: blink_cursor extapi tested in test.p8 2025-11-17 23:13:56 +01:00
Irmen de Jong fb5290e17b cx16: added diskio.f_fatlba() 2025-11-15 20:17:26 +01:00
Irmen de Jong 818774ab84 added conv.hex2long(), added more new X16 extapi calls (rom 49+) 2025-11-15 17:52:34 +01:00
Irmen de Jong d667312acc Merge branch 'refs/heads/12.1-SNAPSHOT'
# Conflicts:
#	docs/source/todo.rst
#	examples/test.p8
2025-11-15 15:15:06 +01:00
Irmen de Jong 64d0cd87a8 diskio.f_seek() and f_tell() now use longs instead of separate words
also fix bug when returning multiple long values
2025-11-15 15:13:26 +01:00
Irmen de Jong 2fffbdde89 todo 2025-11-14 20:05:46 +01:00
Irmen de Jong dc8ea31c49 fix codegen error for a word array equality test 2025-11-13 21:18:04 +01:00
Irmen de Jong f4ead66e91 fix param name breakage in PET txt.setcc for ignored charcolor parameter 2025-11-12 22:00:30 +01:00
Irmen de Jong 314e7f5691 doc updates 2025-11-10 23:42:29 +01:00
Irmen de Jong da31465b7f added the missing smallstack to buffers module 2025-11-10 00:21:13 +01:00
Irmen de Jong 397a907088 fix handling and errors of using long addresses in memread/memwrite 2025-11-09 18:17:57 +01:00
Irmen de Jong 50c6962e1f fix symbol lookup crash 2025-11-09 15:10:10 +01:00
Irmen de Jong b7d1fb1342 translate address-of into + expression for non-const address ptrs too,
also IR optimization to use immediate arithmetic opcodes if possible
2025-11-09 01:36:34 +01:00
Irmen de Jong afb458a7da fix crash in long address-of 2025-11-08 22:24:37 +01:00
Irmen de Jong da3c7f267f improved identifier const value handling if types differ 2025-11-08 20:34:51 +01:00
Irmen de Jong b7ba7c50b1 handle &ptr[offset] for non-uword ptr 2025-11-08 20:31:28 +01:00
Irmen de Jong bdbfe7048b updated library symboldumps 2025-11-08 00:21:53 +01:00
106 changed files with 2306 additions and 929 deletions

No files matched your search

+14 -1
View File
@@ -2,15 +2,28 @@
Several benchmarks of https://thred.github.io/c-bench-64/
have been ported to equivalent Prog8 code and the benchmark results have been penciled in in the graphs below.
Because they use the CIA timer for measuring the duration of the runs, the code only works on a C64.
Maybe one day I'll try to integrate the prog8 data properly but their benchmark site is comparing C compilers, which Prog8 clearly is not.
However conclusions so far (note: these are micro benchmarks so interpret the results as you will!)
* Prog8 program size is consistently the smallest by a fair bit.
* Prog8 program size is consistently one of the smallest.
* Prog8 execution speed places it more or less in the middle of the stack.
Measured with Prog8 V12.0 .
Benchmarks ran on a PAL C64 emulated through VICE.
textual results:
| benchmark | size | time |
|-----------|------|----------|
| crc8 | 1568 | 2.0 sec |
| crc16 | 1608 | 2.5 sec |
| crc32 | 1812 | 4.2 sec |
| pow | 2152 | 11.7 sec |
| sieve | 1772 | 22.1 sec |
| sieve_bit | 1918 | 40.1 sec |
![crc8](result-crc8.png)
+98
View File
@@ -0,0 +1,98 @@
%import textio
cia {
ubyte freq
const ubyte CNT = 200
sub calibrate() {
txt.print("calibrating frequency: ")
tod_init(0)
txt.print_ub(tod_freq())
txt.print(" hz\n")
tod_reset()
}
sub print_time() {
uword t = tod_get10()
txt.print("(cia) time: ")
txt.print_uw(t / 10)
txt.chrout('.')
txt.print_uw(t % 10)
txt.print(" sec.\n")
}
sub tod_reset() {
; set the tod to 0
c64.CIA2TODHR = 0
c64.CIA2TODMIN = 0
c64.CIA2TODSEC = 0
c64.CIA2TOD10 = 0
}
sub tod_get10() -> uword {
ubyte h, m, s, t
uword time
h = c64.CIA2TODHR
m = c64.CIA2TODMIN
s = c64.CIA2TODSEC
t = c64.CIA2TOD10
time = t
time += (s & $0f) * (10 as uword)
time += (s >> 4) * (100 as uword)
time += (m & $0f) * 600
time += (m >> 4) * 6000
return time
}
sub tod_init(ubyte f) {
if (f == 0)
freq = tod_detect_freq()
else
freq = f
if (freq == 50)
c64.CIA2CRA |= $80
else
c64.CIA2CRA &= $7f
}
sub tod_freq() -> ubyte {
return freq
}
sub tod_detect_freq() -> ubyte {
uword cbl
c64.CIA2CRB = $40 ; stop timer
c64.CIA2CRA = $80 ; stop timer
; set ta to overflow every 10000 count (~= 10ms)
c64.CIA2TAL = $10
c64.CIA2TAH = $27
c64.CIA2TBL = CNT
c64.CIA2TBH = 0
tod_reset()
c64.CIA2CRB = $41 ; input from tim1 overflow, continuous, start timer
c64.CIA2CRA = $81 ; start timer, continuous tod 50HZ
while c64.CIA2TODSEC == 0 {
; wait for tod to count 1s
}
; cal=CIA2.ta_lo;
; cah=CIA2.ta_hi;
cbl = c64.CIA2TBL
; cbh=CIA2.tb_hi;
; printf("count2 = %d %d %d %d\n",cah, cal, cbh, cbl);
; printf("elapsed ~= %d0ms\n",CNT-cbl);
if CNT - cbl > 90
return 50
else
return 60
}
}
+5 -5
View File
@@ -1,5 +1,5 @@
%import textio
%import floats
%import ciatimer
; note: Prog8 has a complete CRC16 routine in its library: math.crc16
@@ -7,12 +7,11 @@
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
@@ -40,12 +39,13 @@ test {
if crc_result == EXPECTED
{
txt.print(" [OK]")
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uwhex(EXPECTED, true)
txt.nl()
return true
}
+5 -5
View File
@@ -1,5 +1,5 @@
%import textio
%import floats
%import ciatimer
; note: Prog8 has a complete CRC32 routine in its library: math.crc32
@@ -7,12 +7,11 @@
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
@@ -40,12 +39,13 @@ test {
if crc_result == EXPECTED
{
txt.print(" [OK]")
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_ulhex(EXPECTED, true)
txt.nl()
return true
}
+5 -5
View File
@@ -1,16 +1,15 @@
%import textio
%import floats
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
@@ -36,12 +35,13 @@ test {
if crc_result == EXPECTED
{
txt.print(" [OK]")
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_ubhex(EXPECTED, true)
txt.nl()
return true
}
+3 -3
View File
@@ -1,16 +1,16 @@
%import textio
%import floats
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 65 KiB

+5 -5
View File
@@ -1,16 +1,15 @@
%import textio
%import floats
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
@@ -46,12 +45,13 @@ test {
if prime_count == EXPECTED
{
txt.print(" [OK]")
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uw(EXPECTED)
txt.nl()
return true
}
+5 -5
View File
@@ -1,16 +1,15 @@
%import textio
%import floats
%import ciatimer
main {
sub start() {
txt.lowercase()
cia.calibrate()
test.benchmark_name()
cbm.SETTIM(0,0,0)
test.benchmark()
txt.print_f(floats.time() / 60)
txt.print(" seconds\n")
void test.benchmark_check()
cia.print_time()
repeat {}
}
}
@@ -46,12 +45,13 @@ test {
if prime_count == EXPECTED
{
txt.print(" [OK]")
txt.print(" [OK]\n")
return false
}
txt.print(" [FAIL] - expected ")
txt.print_uw(EXPECTED)
txt.nl()
return true
}
+1 -1
View File
@@ -7,7 +7,7 @@ adpcm {
while cbm.RDTIM16()<max_time {
adpcm.init(0,0)
uword @requirezp nibbles_ptr = $a000 ; for benchmark purposes, the exact nibbles don't really matter, so we just take the basic ROM as input
uword @requirezp nibbles_ptr = $0800 ; for benchmark purposes, the exact nibbles don't really matter, so we just take this benchmark program itself as input
repeat 252/2 {
unroll 2 {
ubyte @zp nibble = @(nibbles_ptr)
+4 -1
View File
@@ -14,8 +14,11 @@ fun Number.toHex(): String {
// larger -> "$12345678"
// negative values are prefixed with '-'.
val integer = this.toLong()
if(integer<0)
if(integer<0) {
if(integer==-2147483648L)
return "$80000000" // the exception to the rule, because -$80000000 is not a valid hex number
return '-' + abs(integer).toHex()
}
return when (integer) {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
@@ -962,14 +962,14 @@ class AsmGen6502Internal (
}
} else throw AssemblyError("weird casted type")
} else {
TODO("assign typecasted expression $value to a long target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
TODO("assign typecasted expression $value to a long target ${target.kind} at ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
}
} else if(target.kind == TargetStorageKind.REGISTER) {
assignExpressionToRegister(value, target.register!!, true)
} else if(target.kind == TargetStorageKind.VARIABLE) {
assignExpressionToVariable(value, target.asmVarname, target.datatype)
} else {
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - please report this issue. Use simple expressions and temporary variables for now")
}
}
target.datatype.isFloat -> {
@@ -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)
}
}
+59 -1
View File
@@ -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.
+11 -1
View File
@@ -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
+71 -1
View File
@@ -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.
+1
View File
@@ -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.
+4 -1
View File
@@ -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
+14
View File
@@ -1150,6 +1150,16 @@ cx16 {
&word r14s = $cffc
&word r15s = $cffe
; signed long versions
&long r0r1sl = $cfe0
&long r2r3sl = $cfe4
&long r4r5sl = $cfe8
&long r6r7sl = $cfec
&long r8r9sl = $cff0
&long r10r11sl = $cff4
&long r12r13sl = $cff8
&long r14r15sl = $cffc
; ubyte versions (low and high bytes)
&ubyte r0L = $cfe0
&ubyte r1L = $cfe2
@@ -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
+79
View File
@@ -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 %)
+61 -3
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
; **experimental** data compression/decompression routines, API subject to change!!
; data compression/decompression routines
%import shared_compression
+60 -55
View File
@@ -244,6 +244,7 @@ io_error:
; this version it only returns directory entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=d")
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -252,6 +253,7 @@ io_error:
; this version only returns actual file entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=p")
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -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)
}
}
+2
View File
@@ -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
+68 -19
View File
@@ -217,6 +217,16 @@ cx16 {
&word r14s = $001e
&word r15s = $0020
; signed long versions
&long r0r1sl = $0002
&long r2r3sl = $0006
&long r4r5sl = $000a
&long r6r7sl = $000e
&long r8r9sl = $0012
&long r10r11sl = $0016
&long r12r13sl = $001a
&long r14r15sl = $001e
; ubyte versions (low and high bytes)
&ubyte r0L = $0002
&ubyte r1L = $0004
@@ -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 ----
+10
View File
@@ -605,6 +605,16 @@ cx16 {
&word r14s = $7ffc
&word r15s = $7ffe
; signed long versions
&long r0r1sl = $7fe0
&long r2r3sl = $7fe4
&long r4r5sl = $7fe8
&long r6r7sl = $7fec
&long r8r9sl = $7fe0
&long r10r11sl = $7fe4
&long r12r13sl = $7fe8
&long r14r15sl = $7fec
; ubyte versions (low and high bytes)
&ubyte r0L = $7fe0
&ubyte r1L = $7fe2
+2 -2
View File
@@ -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
+28
View File
@@ -660,3 +660,31 @@ long_shiftrightX_inplace .proc
bne -
_end rts
.pend
compare_long_0_and_sign .proc
; pointer to 32 bits signed long in P8ZP_SCRATCH_W1, carry Set=check NEGATIVE (<=0), carry Clear=check POSITIVE (>=0)
; returns carry Set if comparison succeeded otherwise carry Clear
ldy #0
lda (P8ZP_SCRATCH_W1),y
iny
ora (P8ZP_SCRATCH_W1),y
iny
ora (P8ZP_SCRATCH_W1),y
iny
ora (P8ZP_SCRATCH_W1),y
beq _success
lda (P8ZP_SCRATCH_W1),y
bcc _checkpositive
bmi _success
bpl _fail
_checkpositive bpl _success
_fail clc
rts
_success sec
rts
.pend
@@ -222,6 +222,7 @@ io_error:
; this version it only returns directory entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=c") ; on C64 (1581 diskdrive) the type for directories is CBM
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
@@ -230,6 +231,7 @@ io_error:
; this version only returns actual file entries!
; note: only a single iteration loop can be active at a time!
cbm.SETNAM(5, "$:*=p")
diskio.lf_start_list.pattern_ptr = pattern_ptr
goto diskio.lf_start_list.start_list_internal
}
+1 -1
View File
@@ -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!
}}
}
+20
View File
@@ -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 %)
+10
View File
@@ -307,6 +307,16 @@ cx16 {
&word r14s = $ff1e
&word r15s = $ff20
; signed long versions
&long r0r1sl = $ff02
&long r2r3sl = $ff06
&long r4r5sl = $ff0a
&long r6r7sl = $ff0e
&long r8r9sl = $ff12
&long r10r11sl = $ff16
&long r12r13sl = $ff1a
&long r14r15sl = $ff1e
; ubyte versions (low and high bytes)
&ubyte r0L = $ff02
&ubyte r1L = $ff04
+3 -3
View File
@@ -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
+6 -6
View File
@@ -38,9 +38,9 @@ class TestCompilerOnCharLit: FunSpec({
test("testCharLitAsExtsubArg") {
val platform = Cx16Target()
val result = compileText(platform, false, """
val result = compileText(platform, false, $$"""
main {
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
extsub $FFD2 = chrout(ubyte ch @ A)
sub start() {
chrout('\n')
}
@@ -61,9 +61,9 @@ class TestCompilerOnCharLit: FunSpec({
test("testCharVarAsExtsubArg") {
val platform = Cx16Target()
val result = compileText(platform, false, """
val result = compileText(platform, false, $$"""
main {
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
extsub $FFD2 = chrout(ubyte ch @ A)
sub start() {
ubyte ch = '\n'
chrout(ch)
@@ -96,9 +96,9 @@ class TestCompilerOnCharLit: FunSpec({
test("testCharConstAsExtsubArg") {
val platform = Cx16Target()
val result = compileText(platform, false, """
val result = compileText(platform, false, $$"""
main {
extsub ${"$"}FFD2 = chrout(ubyte ch @ A)
extsub $FFD2 = chrout(ubyte ch @ A)
sub start() {
const ubyte ch = '\n'
chrout(ch)
-1
View File
@@ -203,7 +203,6 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
val bothCx16AndC64 = cartesianProduct(
listOf(
"animals",
"balls",
"cube3d",
"cube3d-float",
+21 -4
View File
@@ -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"
}
})
+34
View File
@@ -7,10 +7,13 @@ import io.kotest.matchers.doubles.plusOrMinus
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.code.ast.PtAssignment
import prog8.code.ast.PtNumber
import prog8.code.core.InternalCompilerException
import prog8.code.core.toHex
import prog8.code.target.C64Target
import prog8.code.target.Mflpt5
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
@@ -225,4 +228,35 @@ class TestNumbers: FunSpec({
errors.errors[4] shouldContain "out of range"
errors.errors[5] shouldContain "byte doesn't match"
}
test("biggest long numbers") {
val src= $$"""
main {
sub start() {
long @shared l1, l2
l1 = $7fffffff
l2 = $80000000
l1 = -2147483648
l2 = -2147483649 ; will be truncated
l1 = $abcd1234
l2 = -$7fffffff
l1 = $80000001
l2 = -$80
}
}"""
compileText(C64Target(), false, src, outputDir) shouldNotBe null
val result = compileText(VMTarget(), false, src, outputDir)!!
val st = result.codegenAst!!.entrypoint()!!
st.children.size shouldBe 13
((st.children[4] as PtAssignment).value as PtNumber).number shouldBe 2147483647
((st.children[5] as PtAssignment).value as PtNumber).number shouldBe -2147483648
((st.children[6] as PtAssignment).value as PtNumber).number shouldBe -2147483648
((st.children[7] as PtAssignment).value as PtNumber).number shouldBe 2147483647
((st.children[8] as PtAssignment).value as PtNumber).number shouldBe -1412623820
((st.children[9] as PtAssignment).value as PtNumber).number shouldBe -2147483647
((st.children[10] as PtAssignment).value as PtNumber).number shouldBe -2147483647
((st.children[11] as PtAssignment).value as PtNumber).number shouldBe -128
}
})
+63 -11
View File
@@ -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
+1 -1
View File
@@ -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.
+2 -2
View File
@@ -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
+4
View File
@@ -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.
+53 -45
View File
@@ -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)
+3 -2
View File
@@ -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)
+37
View File
@@ -261,3 +261,40 @@ Address-Of: untyped vs typed
``&`` still returns an untyped (uword) pointer, as it did in older Prog8 versions. This is for backward compatibility reasons so existing programs don't break.
The new *double ampersand* operator ``&&`` returns a *typed* pointer to the value. The semantics are slightly different from the old untyped address-of operator, because adding or subtracting
a number from a typed pointer uses *pointer arithmetic* that takes the size of the value that it points to into account.
Accessing struct definitions in Assembly code
---------------------------------------------
Prog8 lets you query the size of a struct type, and the offsets of a field.
You can do the same in assembly code if needed: the struct definition gets written as `.struct` into the assembly file::
; prog8 struct declaration:
struct Node {
ubyte type
uword value
bool flag
}
; generates this assembly code:
; (the symbol prefixes are explained in the 'Technical details' chapter)
p8b_main.p8t_Node .struct f0,f1,f2
p8v_type .byte \f0
p8v_value .word \f1
p8v_flag .byte \f2
.endstruct
64tass then lets you query that information::
; prog8 code:
ubyte size = sizeof(Node)
ubyte offset1 = offsetof(Node.type)
ubyte offset2 = offsetof(Node.value)
ubyte offset3 = offsetof(Node.flag)
; assembly equivalents (64tass syntax):
lda #size(p8t_Node)
lda #p8t_Node.p8v_type
lda #p8t_Node.p8v_value
lda #p8t_Node.p8v_flag
+9 -1
View File
@@ -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
+32 -19
View File
@@ -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
+1 -1
View File
@@ -225,7 +225,7 @@ value datatype
0 .. 255 ubyte
-32768 .. 32767 word
0 .. 65535 uword
-2147483647 .. 2147483647 long (there is no unsigned long right now)
-2147483648 .. 2147483647 long (there is no unsigned long right now)
========================= =================
Numeric expressions usually 'stay within their type' unless a cast is used, see :ref:`arithmetic`.
-141
View File
@@ -1,141 +0,0 @@
%import textio
%import strings
; Animal guessing game where the computer gets smarter every time.
; Note: this program can be compiled for multiple target systems.
main {
const ubyte database_size = 100
uword animal_names_buf = memory("animalnames", 500, 0) ; area to store all animal names in, in sequence
uword questions_buf = memory("questions", 2000, 0) ; area to store all question texts in, in sequence
uword animal_names_ptr
uword questions_ptr
uword[database_size] animals ; pointers to the animal names
uword[database_size] questions ; pointers to the question texts
uword[database_size] answers_questions ; tree entries for question choices, indexed by question id, pair of (msb=yes, lsb=no) follow up question id (or 0 if it's an animal leaf node)
uword[database_size] answers_animals ; tree entries for animal leafs, indexed by question id, pair of (msb=yes, lsb=no) animal id
ubyte new_animal_number
ubyte new_question_number
str userinput = "x"*80
sub start() {
; initialize the database
animal_names_ptr = animal_names_buf
questions_ptr = questions_buf
animals[0] = 0
animals[1] = "dolphin"
animals[2] = "eagle"
animals[3] = "horse"
new_animal_number = 4
questions[0] = 0
questions[1] = "does it swim"
questions[2] = "can it fly"
new_question_number = 3
answers_questions[0] = mkword(0, 0)
answers_questions[1] = mkword(0, 2)
answers_questions[2] = mkword(0, 0)
answers_animals[0] = mkword(0, 0)
answers_animals[1] = mkword(1, 0)
answers_animals[2] = mkword(2, 3)
; play the game
game()
}
sub game() {
repeat {
ubyte current_question = 1
txt.print("\n\nanimal guessing game!\nthink of an animal.\n")
bool guessed = false
while not guessed {
txt.print(questions[current_question])
txt.print("? ")
if txt.input_chars(userinput)!=0 {
txt.nl()
ubyte animal_number
if userinput[0]=='y' {
animal_number = msb(answers_animals[current_question])
if animal_number!=0 {
guess(current_question, true, animal_number)
guessed = true
} else {
current_question = msb(answers_questions[current_question])
}
}
else if userinput[0]=='n' {
animal_number = lsb(answers_animals[current_question])
if animal_number!=0 {
guess(current_question, false, animal_number)
guessed = true
} else {
current_question = lsb(answers_questions[current_question])
}
}
else {
txt.print("answer (y)es or (n)o please.\n")
}
} else
txt.nl()
}
}
}
sub guess(ubyte question_number, bool given_answer_yesno, ubyte animal_number) {
txt.print("is it a ")
txt.print(animals[animal_number])
txt.print("? ")
void txt.input_chars(userinput)
if userinput[0] == 'y' {
txt.print("\n\nsee, i knew it!\n")
return
}
str name = "x"*30
txt.print("\n\ni give up. what is it? ")
void txt.input_chars(name)
txt.print("\nwhat yes-no question would best articulate the difference\nbetween a ")
txt.print(animals[animal_number])
txt.print(" and a ")
txt.print(name)
txt.print("? ")
void txt.input_chars(userinput)
txt.print("\nin case of the ")
txt.print(name)
txt.print(", what is the answer to that question? ")
str answer = "x"*10
void txt.input_chars(answer)
animals[new_animal_number] = animal_names_ptr
questions[new_question_number] = questions_ptr
animal_names_ptr += strings.copy(name, animal_names_ptr)+1 ; store animal name in buffer
questions_ptr += strings.copy(userinput, questions_ptr)+1 ; store question in buffer
answers_questions[new_question_number] = mkword(0, 0)
if answer[0]=='y'
answers_animals[new_question_number] = mkword(new_animal_number, animal_number)
else
answers_animals[new_question_number] = mkword(animal_number, new_animal_number)
uword previous_animals = answers_animals[question_number]
uword previous_questions = answers_questions[question_number]
if given_answer_yesno {
answers_animals[question_number] = mkword(0, lsb(previous_animals))
answers_questions[question_number] = mkword(new_question_number, lsb(previous_questions))
} else {
answers_animals[question_number] = mkword(msb(previous_animals), 0)
answers_questions[question_number] = mkword(msb(previous_questions), new_question_number)
}
new_animal_number++
new_question_number++
txt.print("\n\nthanks, i know more animals now! let's try again.\n")
}
}
@@ -482,6 +482,16 @@ cx16 {
&word r14s = $1b1c
&word r15s = $1b1e
; signed long versions
&long r0r1sl = $1b00
&long r2r3sl = $1b04
&long r4r5sl = $1b08
&long r6r7sl = $1b0c
&long r8r9sl = $1b00
&long r10r11sl = $1b04
&long r12r13sl = $1b08
&long r14r15sl = $1b0c
; ubyte versions (low and high bytes)
&ubyte r0L = $1b00
&ubyte r1L = $1b02
@@ -741,6 +741,16 @@ cx16 {
&word r14s = $002c
&word r15s = $002e
; signed long versions
&long r0r1sl = $0010
&long r2r3sl = $0014
&long r4r5sl = $0018
&long r6r7sl = $001c
&long r8r9sl = $0020
&long r10r11sl = $0024
&long r12r13sl = $0028
&long r14r15sl = $002c
; ubyte versions (low and high bytes)
&ubyte r0L = $0010
&ubyte r1L = $0012
@@ -414,6 +414,16 @@ cx16 {
&word r14s = $001e
&word r15s = $0020
; signed long versions
&long r0r1sl = $0002
&long r2r3sl = $0006
&long r4r5sl = $000a
&long r6r7sl = $000e
&long r8r9sl = $0012
&long r10r11sl = $0016
&long r12r13sl = $001a
&long r14r15sl = $001e
; ubyte versions (low and high bytes)
&ubyte r0L = $0002
&ubyte r1L = $0004
@@ -203,6 +203,16 @@ cx16 {
&word r14s = $cffc
&word r15s = $cffe
; signed long versions
&long r0r1sl = $cfe0
&long r2r3sl = $cfe4
&long r4r5sl = $cfe8
&long r6r7sl = $cfec
&long r8r9sl = $cff0
&long r10r11sl = $cff4
&long r12r13sl = $cff8
&long r14r15sl = $cffc
; ubyte versions (low and high bytes)
&ubyte r0L = $cfe0
&ubyte r1L = $cfe2
@@ -203,6 +203,16 @@ cx16 {
&word r14s = $001e
&word r15s = $0020
; signed long versions
&long r0r1sl = $0002
&long r2r3sl = $0006
&long r4r5sl = $000a
&long r6r7sl = $000e
&long r8r9sl = $0012
&long r10r11sl = $0016
&long r12r13sl = $001a
&long r14r15sl = $001e
; ubyte versions (low and high bytes)
&ubyte r0L = $0002
&ubyte r1L = $0004
@@ -199,6 +199,16 @@ cx16 {
&word r14s = $7ffc
&word r15s = $7ffe
; signed long versions
&long r0r1sl = $7fe0
&long r2r3sl = $7fe4
&long r4r5sl = $7fe8
&long r6r7sl = $7fec
&long r8r9sl = $7fe0
&long r10r11sl = $7fe4
&long r12r13sl = $7fe8
&long r14r15sl = $7fec
; ubyte versions (low and high bytes)
&ubyte r0L = $7fe0
&ubyte r1L = $7fe2
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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)
+2
View File
@@ -13,6 +13,8 @@ adpcm {
; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav
; And/or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set the correct block size, -b8)
;
; NOTE: sox may generate IMA-ADPCM files with a block size different than 256 bytes, which is not supported by this decoder. Use ffmpeg instead.
;
; NOTE: for speed reasons this implementation doesn't guard against clipping errors.
; if the output sounds distorted, lower the volume of the source waveform to 80% and try again etc.
+2
View File
@@ -14,6 +14,8 @@
; $ ffmpeg -i source.mp3 -ss 00:01:27.50 -to 00:01:36.50 -ar 8000 -ac 1 -c:a adpcm_ima_wav -block_size 256 -map_metadata -1 -bitexact out.wav
; Or use a tool such as https://github.com/dbry/adpcm-xq (make sure to set correct block size)
;
; NOTE: sox may generate IMA-ADPCM files with a block size different than 256 bytes, which is not supported by this decoder. Use ffmpeg instead.
;
main {
+1 -1
View File
@@ -29,7 +29,7 @@ main {
ubyte vera_rate
sub start() {
;; diskio.fastmode(1)
diskio.fastmode(1)
txt.print("name of .wav file to play on drive 8: ")
while 0==txt.input_chars(MUSIC_FILENAME) {
; until user types a name...
+5 -5
View File
@@ -2,17 +2,17 @@
%import sprites
%option no_sysinit
; play a raw pcm stereo 16 bit audio file at 16021 hz,
; play a raw pcm stereo 16 bit audio file at 14877 hz,
; with real-time VU meters and sample waveform displays.
; you can make an appropriate pcm file with one of the following commands:
; ffmpeg -i input_audio_file -ac 2 -ar 16021 -f s16le -acodec pcm_s16le music.pcm
; sox input_audio_file -e signed-integer -L -b 16 -c 2 -r 16021 -t raw music.pcm
; ffmpeg -i input_audio_file -ac 2 -ar 14877 -f s16le -acodec pcm_s16le music.pcm
; sox input_audio_file -e signed-integer -L -b 16 -c 2 -r 14877 -t raw music.pcm
main {
const ubyte vera_rate = 42 ; 16021 hz
const ubyte vera_rate = 39 ; 14877 hz
str pcmfile = "music.pcm" ; see format specs mentioned above
sub start() {
@@ -34,7 +34,7 @@ main {
cx16.GRAPH_put_next_char(cx16.r9L)
cx16.r0 = 250
cx16.r1 = 20
for cx16.r9L in iso:"16kHz stereo"
for cx16.r9L in iso:"15kHz stereo"
cx16.GRAPH_put_next_char(cx16.r9L)
cx16.rombank(0) ; activate kernal bank for faster calls
+2 -2
View File
@@ -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.
Binary file not shown.
-2
View File
@@ -3,8 +3,6 @@
;
; Exercise for the reader to store the memory containing the nodes and strings and reload it on new runs...?
; TODO: remove the examples/animals.p8 once this one compiles for 6502 too
%import textio
%import strings
+3
View File
@@ -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()
}
+43 -23
View File
@@ -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
}
}
}
+1 -1
View File
@@ -4,4 +4,4 @@ org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.configuration-cache=false
kotlin.code.style=official
version=12.0-BETA6
version=12.0.1
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -272,7 +272,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
dt.isBool -> constant.value.toInt().toString()
dt.isFloat -> constant.value.toString()
dt.isPointer -> TODO("constant pointer $constant")
dt.isInteger -> constant.value.toInt().toHex()
dt.isInteger -> constant.value.toLong().toHex()
else -> throw InternalCompilerException("weird dt")
}
xml.writeCharacters("${constant.typeString} ${constant.name}=$value\n")
@@ -44,9 +44,8 @@ load reg1, value - load immediate value into register. If y
loadm reg1, address - load reg1 with value at memory address
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (0-255, a byte)
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
loadr reg1, reg2 - load reg1 with value in register reg2, "reg1 = reg2"
loadfield reg1, reg2, value - load reg1 with value in memory pointed to by reg2 + value 0-255 (gets a field from a pointer to a struct, like LOADI, but with additional field offset 0-255)
loadfield reg1, reg2, value - load reg1 with value in memory indirect, pointed to by reg2 + value 0-65535 (gets a field from a pointer to a struct, like LOADI with additional field offset 0-65535)
loadha reg1 - load cpu hardware register A into reg1.b
loadhx reg1 - load cpu hardware register X into reg1.b
loadhy reg1 - load cpu hardware register Y into reg1.b
@@ -58,11 +57,10 @@ loadhfacone fpreg1 - load "cpu hardware register" fac1 into f
storem reg1, address - store reg1 at memory address
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (0-255, a byte)
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
storezm address - store zero at memory address
storezi reg1 - store zero at memory pointed to by reg1
storezx reg1, address - store zero at memory address, indexed by value in reg1 (0-255, a byte)
storefield reg1, reg2, value - store reg1 in memory pointed to by reg2 + value 0-255 (set a field from a pointer to a struct, like STOREI, but with additional field offset 0-255)
storefield reg1, reg2, value - store reg1 in memory indirect, pointed to by reg2 + value 0-65535 (set a field from a pointer to a struct, like STOREI with additional field offset 0-65535)
storeha reg1 - store reg1.b into cpu hardware register A
storehx reg1 - store reg1.b into cpu hardware register X
storehy reg1 - store reg1.b into cpu hardware register Y
@@ -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}")
}
}
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff. Show more