mirror of
https://github.com/irmen/prog8.git
synced 2025-06-14 11:23:37 +00:00
Compare commits
169 Commits
Author | SHA1 | Date | |
---|---|---|---|
f1ee3b4e60 | |||
6395e39d63 | |||
2a6d9d7e31 | |||
32a7cd31da | |||
dd4a56cb5f | |||
d110d1cb5f | |||
48858019b7 | |||
aff6b1fca5 | |||
d260182ef3 | |||
e39a38b0d9 | |||
82d7179c92 | |||
f42746ba06 | |||
1f69deaccd | |||
ea8b7ab193 | |||
9938959026 | |||
d5e5485d2e | |||
97b9c8f320 | |||
35aebbc209 | |||
81f7419f70 | |||
2f951bd54d | |||
18f5963b09 | |||
836509c1d1 | |||
949d536e42 | |||
f69b17e165 | |||
49a0584c54 | |||
e21aa2c8f0 | |||
40071b1431 | |||
02e29e6990 | |||
e19de0901e | |||
137d506e42 | |||
90c4a26d52 | |||
f378a8997b | |||
1377bed988 | |||
8f9f947c42 | |||
37f6c2858f | |||
13d7f239ab | |||
a6f3c84e28 | |||
fe4e0e9835 | |||
809917f13b | |||
2b35498370 | |||
f45eabdd9e | |||
438f3ee8d2 | |||
4bea31f051 | |||
5eae7a2b93 | |||
364ef3e55c | |||
e61818f194 | |||
0f9ce319d4 | |||
5d90871789 | |||
88a9e09918 | |||
c50ecf6055 | |||
a18de75da9 | |||
e112dfd910 | |||
9154d8bd37 | |||
0b55372b3b | |||
3ad7fb010f | |||
3f64d1bb5a | |||
a6f564ad88 | |||
d97da3bb7b | |||
a77d3c92ad | |||
6d17e5307c | |||
c2205e473a | |||
4ffb194847 | |||
744cd6ec42 | |||
f08fc18ab5 | |||
462af76770 | |||
9cec554f7c | |||
08b25e610d | |||
e896d5a1a6 | |||
b939562062 | |||
256781bba5 | |||
19705196d6 | |||
3ce692bb10 | |||
78bdbde3ae | |||
8d8c066447 | |||
5da9379c37 | |||
032d20ff37 | |||
d19b17cbfe | |||
4a4f8ff5db | |||
60a9209a14 | |||
0f9e167df3 | |||
2e2b8c498e | |||
144199730f | |||
4bb4eab3b2 | |||
cf9151f669 | |||
aef4598cec | |||
3ada0fdf84 | |||
a5d97b326e | |||
2640015fb1 | |||
6cd42ddafe | |||
1f17c22132 | |||
5c62f612cc | |||
b9ca1c2e2c | |||
93b2ff2e52 | |||
3991d23a69 | |||
1be139759c | |||
d0674ad688 | |||
ffb47458ff | |||
84ec1be8a4 | |||
f4dafec645 | |||
97ce72521d | |||
d2f0e74879 | |||
d9e3895c45 | |||
5075901830 | |||
f1193bb5a0 | |||
d3dc279105 | |||
acc942f690 | |||
e947067dcf | |||
bd9ebf4603 | |||
f41192a52a | |||
ff54d6abd7 | |||
f40bcc219f | |||
679965410a | |||
c6e13ae2a3 | |||
20cdcc673b | |||
89f46222d9 | |||
b27cbfac5e | |||
31c946aeeb | |||
bfc8a26381 | |||
9d98746501 | |||
63b03ba70c | |||
70bab76b36 | |||
15d24d4308 | |||
9ec62eb045 | |||
12f841e30d | |||
335599ed22 | |||
0b717f9e76 | |||
e941f6ecca | |||
ef7744dbda | |||
c83a61c460 | |||
335684caf7 | |||
8d6220ce51 | |||
39ea5c5f99 | |||
b03597ac13 | |||
58f323c087 | |||
513a68584c | |||
88d5c68b32 | |||
14f9382cf9 | |||
cffb582568 | |||
e1812ce16c | |||
7a3163f59a | |||
6f3b2749b0 | |||
c144d4e501 | |||
edfd9d55ba | |||
774897260e | |||
65ba91411d | |||
9cbb8e1a64 | |||
53e9ad5088 | |||
cf6ea63fa6 | |||
1de0ebb7bc | |||
77c1376d6d | |||
353f1954a5 | |||
8bf3406cf8 | |||
936bf9a05c | |||
4487499663 | |||
3976cc26a2 | |||
e6ff87ecd0 | |||
c0887b5f08 | |||
f14dda4eca | |||
bd7f75c130 | |||
fbe3ce008b | |||
7ac6c8f2d1 | |||
fdfbb7bdf0 | |||
1c16bbb742 | |||
9735527062 | |||
402827497e | |||
f81aa0d867 | |||
d32a970101 | |||
cd651aa416 | |||
8a3189123a |
@ -4,8 +4,8 @@ sudo: false
|
||||
# dist: xenial
|
||||
|
||||
before_install:
|
||||
- chmod +x gradlew
|
||||
- chmod +x ./gradlew
|
||||
|
||||
script:
|
||||
- gradle test
|
||||
- ./gradlew test
|
||||
|
||||
|
65
README.md
65
README.md
@ -2,19 +2,28 @@
|
||||
[](https://travis-ci.org/irmen/prog8)
|
||||
[](https://prog8.readthedocs.io/)
|
||||
|
||||
Prog8 - Structured Programming Language for 8-bit 6502/6510 microprocessors
|
||||
===========================================================================
|
||||
Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||
============================================================================
|
||||
|
||||
*Written by Irmen de Jong (irmen@razorvine.net)*
|
||||
|
||||
*Software license: GNU GPL 3.0, see file LICENSE*
|
||||
|
||||
|
||||
This is a structured programming language for the 8-bit 6502/6510 microprocessor from the late 1970's and 1980's
|
||||
This is a structured programming language for the 8-bit 6502/6510/65c02 microprocessor from the late 1970's and 1980's
|
||||
as used in many home computers from that era. It is a medium to low level programming language,
|
||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler):
|
||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
||||
|
||||
- reduction of source code length
|
||||
Documentation
|
||||
-------------
|
||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||
https://prog8.readthedocs.io/
|
||||
|
||||
|
||||
What use Prog8 provide?
|
||||
-----------------------
|
||||
|
||||
- reduction of source code length over raw assembly
|
||||
- modularity, symbol scoping, subroutines
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- automatic variable allocations, automatic string and array variables and string sharing
|
||||
@ -29,7 +38,7 @@ which aims to provide many conveniences over raw assembly code (even when using
|
||||
- inline assembly allows you to have full control when every cycle or byte matters
|
||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
|
||||
|
||||
Rapid edit-compile-run-debug cycle:
|
||||
*Rapid edit-compile-run-debug cycle:*
|
||||
|
||||
- use a modern PC to do the work on
|
||||
- very quick compilation times
|
||||
@ -37,15 +46,16 @@ Rapid edit-compile-run-debug cycle:
|
||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||
|
||||
Prog8 is mainly targeted at the Commodore-64 machine at this time.
|
||||
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
||||
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||
|
||||
Documentation/manual
|
||||
--------------------
|
||||
https://prog8.readthedocs.io/
|
||||
- "c64": Commodore-64 (6510 CPU = almost a 6502) premium support.
|
||||
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) experimental support.
|
||||
|
||||
Required tools
|
||||
--------------
|
||||
|
||||
|
||||
|
||||
Additional required tools
|
||||
-------------------------
|
||||
|
||||
[64tass](https://sourceforge.net/projects/tass64/) - cross assembler. Install this on your shell path.
|
||||
A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project.
|
||||
@ -55,8 +65,9 @@ A **Java runtime (jre or jdk), version 8 or newer** is required to run a prepac
|
||||
If you want to build it from source, you'll need a Java SDK + Kotlin 1.3.x SDK (or for instance,
|
||||
IntelliJ IDEA with the Kotlin plugin).
|
||||
|
||||
It's handy to have a C-64 emulator or a real C-64 to run the programs on. The compiler assumes the presence
|
||||
of the [Vice emulator](http://vice-emu.sourceforge.net/)
|
||||
It's handy to have an emulator (or a real machine perhaps!) to run the programs on. The compiler assumes the presence
|
||||
of the [Vice emulator](http://vice-emu.sourceforge.net/) for the C64 target,
|
||||
and the [x16emu emulator](https://github.com/commanderx16/x16-emulator) for the CommanderX16 target.
|
||||
|
||||
|
||||
Example code
|
||||
@ -64,7 +75,7 @@ Example code
|
||||
|
||||
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||
|
||||
%import c64utils
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -73,35 +84,33 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||
ubyte candidate_prime = 2
|
||||
|
||||
sub start() {
|
||||
memset(sieve, 256, false)
|
||||
|
||||
c64scr.print("prime numbers up to 255:\n\n")
|
||||
memset(sieve, 256, false) ; clear the sieve
|
||||
txt.print("prime numbers up to 255:\n\n")
|
||||
ubyte amount=0
|
||||
while true {
|
||||
repeat {
|
||||
ubyte prime = find_next_prime()
|
||||
if prime==0
|
||||
break
|
||||
c64scr.print_ub(prime)
|
||||
c64scr.print(", ")
|
||||
txt.print_ub(prime)
|
||||
txt.print(", ")
|
||||
amount++
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("number of primes (expected 54): ")
|
||||
c64scr.print_ub(amount)
|
||||
txt.print("number of primes (expected 54): ")
|
||||
txt.print_ub(amount)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
|
||||
sub find_next_prime() -> ubyte {
|
||||
|
||||
while sieve[candidate_prime] {
|
||||
candidate_prime++
|
||||
if candidate_prime==0
|
||||
return 0
|
||||
return 0 ; we wrapped; no more primes available
|
||||
}
|
||||
|
||||
; found next one, mark the multiples and return it.
|
||||
sieve[candidate_prime] = true
|
||||
uword multiple = candidate_prime
|
||||
|
||||
while multiple < len(sieve) {
|
||||
sieve[lsb(multiple)] = true
|
||||
multiple += candidate_prime
|
||||
|
@ -1,11 +1,11 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10"
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
// id "org.jetbrains.kotlin.jvm" version "1.3.72"
|
||||
// id "org.jetbrains.kotlin.jvm" version "1.4.10"
|
||||
id 'application'
|
||||
id 'org.jetbrains.dokka' version "0.9.18"
|
||||
id 'com.github.johnrengelman.shadow' version '5.2.0'
|
||||
@ -110,3 +110,7 @@ dokka {
|
||||
outputFormat = 'html'
|
||||
outputDirectory = "$buildDir/kdoc"
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '6.1.1'
|
||||
}
|
||||
|
@ -1,49 +1,56 @@
|
||||
; --- low level floating point assembly routines for the C64
|
||||
|
||||
FL_ONE_const .byte 129 ; 1.0
|
||||
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||
|
||||
floats_store_reg .byte 0 ; temp storage
|
||||
|
||||
|
||||
ub2float .proc
|
||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
ldy c64.SCRATCH_ZPB1
|
||||
jsr FREADUY
|
||||
_fac_to_mem ldx c64.SCRATCH_ZPWORD2
|
||||
ldy c64.SCRATCH_ZPWORD2+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
lda #0
|
||||
jsr GIVAYF
|
||||
_fac_to_mem ldx P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
jsr MOVMF
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
b2float .proc
|
||||
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
||||
; clobbers A, Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
lda c64.SCRATCH_ZPB1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_B1
|
||||
jsr FREADSA
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
uw2float .proc
|
||||
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr GIVUAYFAY
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
|
||||
w2float .proc
|
||||
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
ldy c64.SCRATCH_ZPWORD1
|
||||
lda c64.SCRATCH_ZPWORD1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy P8ZP_SCRATCH_W1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
jsr GIVAYF
|
||||
jmp ub2float._fac_to_mem
|
||||
.pend
|
||||
@ -51,8 +58,8 @@ w2float .proc
|
||||
stack_b2float .proc
|
||||
; -- b2float operating on the stack
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda P8ESTACK_LO,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FREADSA
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -60,9 +67,9 @@ stack_b2float .proc
|
||||
stack_w2float .proc
|
||||
; -- w2float operating on the stack
|
||||
inx
|
||||
ldy c64.ESTACK_LO,x
|
||||
lda c64.ESTACK_HI,x
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
ldy P8ESTACK_LO,x
|
||||
lda P8ESTACK_HI,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVAYF
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -70,44 +77,45 @@ stack_w2float .proc
|
||||
stack_ub2float .proc
|
||||
; -- ub2float operating on the stack
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda P8ESTACK_LO,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tay
|
||||
jsr FREADUY
|
||||
lda #0
|
||||
jsr GIVAYF
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
stack_uw2float .proc
|
||||
; -- uw2float operating on the stack
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
ldy c64.ESTACK_HI,x
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda P8ESTACK_LO,x
|
||||
ldy P8ESTACK_HI,x
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GIVUAYFAY
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
stack_float2w .proc ; also used for float2b
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr AYINT
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
lda $64
|
||||
sta c64.ESTACK_HI,x
|
||||
sta P8ESTACK_HI,x
|
||||
lda $65
|
||||
sta c64.ESTACK_LO,x
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_float2uw .proc ; also used for float2ub
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr GETADR
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
sta c64.ESTACK_HI,x
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
sta P8ESTACK_HI,x
|
||||
tya
|
||||
sta c64.ESTACK_LO,x
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
@ -115,79 +123,68 @@ stack_float2uw .proc ; also used for float2ub
|
||||
push_float .proc
|
||||
; ---- push mflpt5 in A/Y onto stack
|
||||
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.ESTACK_LO,x
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.ESTACK_HI,x
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.ESTACK_LO,x
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.ESTACK_HI,x
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta c64.ESTACK_LO,x
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
|
||||
func_rndf .proc
|
||||
; -- put a random floating point value on the stack
|
||||
stx c64.SCRATCH_ZPREG
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #1
|
||||
jsr FREADSA
|
||||
jsr RND ; rng into fac1
|
||||
ldx #<_rndf_rnum5
|
||||
ldy #>_rndf_rnum5
|
||||
jsr MOVMF ; fac1 to mem X/Y
|
||||
ldx c64.SCRATCH_ZPREG
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
lda #<_rndf_rnum5
|
||||
ldy #>_rndf_rnum5
|
||||
jmp push_float
|
||||
_rndf_rnum5 .byte 0,0,0,0,0
|
||||
.pend
|
||||
|
||||
push_float_from_indexed_var .proc
|
||||
; -- push the float from the array at A/Y with index on stack, onto the stack.
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
jsr prog8_lib.pop_index_times_5
|
||||
jsr prog8_lib.add_a_to_zpword
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
jmp push_float
|
||||
.pend
|
||||
|
||||
pop_float .proc
|
||||
; ---- pops mflpt5 from stack to memory A/Y
|
||||
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #4
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
inx
|
||||
lda c64.ESTACK_HI,x
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
lda P8ESTACK_HI,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda c64.ESTACK_LO,x
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
inx
|
||||
lda c64.ESTACK_HI,x
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
lda P8ESTACK_HI,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
lda c64.ESTACK_LO,x
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
lda P8ESTACK_LO,x
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
@ -201,110 +198,74 @@ pop_float_fac1 .proc
|
||||
jmp MOVFM
|
||||
.pend
|
||||
|
||||
pop_float_fac2 .proc
|
||||
; -- pops float from stack into FAC2
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jmp CONUPK
|
||||
.pend
|
||||
|
||||
pop_float_to_indexed_var .proc
|
||||
; -- pop the float on the stack, to the memory in the array at A/Y indexed by the byte on stack
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
jsr prog8_lib.pop_index_times_5
|
||||
jsr prog8_lib.add_a_to_zpword
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jmp pop_float
|
||||
.pend
|
||||
|
||||
copy_float .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
ldy #0
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
iny
|
||||
lda (c64.SCRATCH_ZPWORD1),y
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
sta _target+1
|
||||
sty _target+2
|
||||
ldy #4
|
||||
_loop lda (P8ZP_SCRATCH_W1),y
|
||||
_target sta $ffff,y ; modified
|
||||
dey
|
||||
bpl _loop
|
||||
rts
|
||||
.pend
|
||||
|
||||
inc_var_f .proc
|
||||
; -- add 1 to float pointed to by A/Y
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr MOVFM
|
||||
lda #<FL_FONE
|
||||
ldy #>FL_FONE
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr FADD
|
||||
ldx c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVMF
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
dec_var_f .proc
|
||||
; -- subtract 1 from float pointed to by A/Y
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sty c64.SCRATCH_ZPWORD1+1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<FL_FONE
|
||||
ldy #>FL_FONE
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr MOVFM
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr FSUB
|
||||
ldx c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
ldx P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVMF
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
.pend
|
||||
|
||||
inc_indexed_var_f .proc
|
||||
; -- add 1 to float in array pointed to by A/Y, at index X
|
||||
pha
|
||||
txa
|
||||
sta c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
sta c64.SCRATCH_ZPB1
|
||||
pla
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp inc_var_f
|
||||
.pend
|
||||
|
||||
dec_indexed_var_f .proc
|
||||
; -- subtract 1 to float in array pointed to by A/Y, at index X
|
||||
pha
|
||||
txa
|
||||
sta c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
sta c64.SCRATCH_ZPB1
|
||||
pla
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp dec_var_f
|
||||
.pend
|
||||
|
||||
|
||||
pop_2_floats_f2_in_fac1 .proc
|
||||
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
||||
@ -330,7 +291,7 @@ push_fac1_as_result .proc
|
||||
jsr MOVMF
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
jmp push_float
|
||||
.pend
|
||||
|
||||
@ -342,21 +303,21 @@ pow_f .proc
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr CONUPK ; fac2 = float1
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
jsr FPWR
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
div_f .proc
|
||||
; -- push f1/f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FDIV
|
||||
@ -366,7 +327,7 @@ div_f .proc
|
||||
add_f .proc
|
||||
; -- push f1+f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FADD
|
||||
@ -376,7 +337,7 @@ add_f .proc
|
||||
sub_f .proc
|
||||
; -- push f1-f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FSUB
|
||||
@ -386,7 +347,7 @@ sub_f .proc
|
||||
mul_f .proc
|
||||
; -- push f1*f2 on stack
|
||||
jsr pop_2_floats_f2_in_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr FMULT
|
||||
@ -396,7 +357,7 @@ mul_f .proc
|
||||
neg_f .proc
|
||||
; -- push -flt back on stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr NEGOP
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -404,7 +365,7 @@ neg_f .proc
|
||||
abs_f .proc
|
||||
; -- push abs(float) on stack (as float)
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr ABS
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -415,24 +376,24 @@ equal_f .proc
|
||||
inx
|
||||
inx
|
||||
inx
|
||||
lda c64.ESTACK_LO-3,x
|
||||
cmp c64.ESTACK_LO,x
|
||||
lda P8ESTACK_LO-3,x
|
||||
cmp P8ESTACK_LO,x
|
||||
bne _equals_false
|
||||
lda c64.ESTACK_LO-2,x
|
||||
cmp c64.ESTACK_LO+1,x
|
||||
lda P8ESTACK_LO-2,x
|
||||
cmp P8ESTACK_LO+1,x
|
||||
bne _equals_false
|
||||
lda c64.ESTACK_LO-1,x
|
||||
cmp c64.ESTACK_LO+2,x
|
||||
lda P8ESTACK_LO-1,x
|
||||
cmp P8ESTACK_LO+2,x
|
||||
bne _equals_false
|
||||
lda c64.ESTACK_HI-2,x
|
||||
cmp c64.ESTACK_HI+1,x
|
||||
lda P8ESTACK_HI-2,x
|
||||
cmp P8ESTACK_HI+1,x
|
||||
bne _equals_false
|
||||
lda c64.ESTACK_HI-1,x
|
||||
cmp c64.ESTACK_HI+2,x
|
||||
lda P8ESTACK_HI-1,x
|
||||
cmp P8ESTACK_HI+2,x
|
||||
bne _equals_false
|
||||
_equals_true lda #1
|
||||
_equals_store inx
|
||||
sta c64.ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
_equals_false lda #0
|
||||
beq _equals_store
|
||||
@ -442,7 +403,7 @@ notequal_f .proc
|
||||
; -- are the two mflpt5 numbers on the stack different?
|
||||
jsr equal_f
|
||||
eor #1 ; invert the result
|
||||
sta c64.ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
@ -495,12 +456,12 @@ compare_floats .proc
|
||||
jsr MOVFM ; fac1 = flt1
|
||||
lda #<fmath_float2
|
||||
ldy #>fmath_float2
|
||||
stx c64.SCRATCH_ZPREG
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
||||
ldx c64.SCRATCH_ZPREG
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
_return_false lda #0
|
||||
_return_result sta c64.ESTACK_LO,x
|
||||
_return_result sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
_return_true lda #1
|
||||
@ -510,7 +471,7 @@ _return_true lda #1
|
||||
func_sin .proc
|
||||
; -- push sin(f) back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr SIN
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -518,7 +479,7 @@ func_sin .proc
|
||||
func_cos .proc
|
||||
; -- push cos(f) back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr COS
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -526,7 +487,7 @@ func_cos .proc
|
||||
func_tan .proc
|
||||
; -- push tan(f) back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr TAN
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -534,7 +495,7 @@ func_tan .proc
|
||||
func_atan .proc
|
||||
; -- push atan(f) back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr ATN
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -542,7 +503,7 @@ func_atan .proc
|
||||
func_ln .proc
|
||||
; -- push ln(f) back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr LOG
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -550,7 +511,7 @@ func_ln .proc
|
||||
func_log2 .proc
|
||||
; -- push log base 2, ln(f)/ln(2), back onto stack
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr LOG
|
||||
jsr MOVEF
|
||||
lda #<c64.FL_LOG2
|
||||
@ -562,7 +523,7 @@ func_log2 .proc
|
||||
|
||||
func_sqrt .proc
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr SQR
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -570,7 +531,7 @@ func_sqrt .proc
|
||||
func_rad .proc
|
||||
; -- convert degrees to radians (d * pi / 180)
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<_pi_div_180
|
||||
ldy #>_pi_div_180
|
||||
jsr FMULT
|
||||
@ -581,7 +542,7 @@ _pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
|
||||
func_deg .proc
|
||||
; -- convert radians to degrees (d * (1/ pi * 180))
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda #<_one_over_pi_div_180
|
||||
ldy #>_one_over_pi_div_180
|
||||
jsr FMULT
|
||||
@ -591,7 +552,7 @@ _one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
|
||||
|
||||
func_round .proc
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr FADDH
|
||||
jsr INT
|
||||
jmp push_fac1_as_result
|
||||
@ -599,7 +560,7 @@ func_round .proc
|
||||
|
||||
func_floor .proc
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr INT
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
@ -607,7 +568,7 @@ func_floor .proc
|
||||
func_ceil .proc
|
||||
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
|
||||
jsr pop_float_fac1
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldx #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr MOVMF
|
||||
@ -617,53 +578,53 @@ func_ceil .proc
|
||||
jsr FCOMP
|
||||
cmp #0
|
||||
beq +
|
||||
lda #<FL_FONE
|
||||
ldy #>FL_FONE
|
||||
lda #<FL_ONE_const
|
||||
ldy #>FL_ONE_const
|
||||
jsr FADD
|
||||
+ jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
func_any_f .proc
|
||||
inx
|
||||
lda c64.ESTACK_LO,x ; array size
|
||||
sta c64.SCRATCH_ZPB1
|
||||
lda P8ESTACK_LO,x ; array size
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
||||
adc P8ZP_SCRATCH_B1 ; times 5 because of float
|
||||
jmp prog8_lib.func_any_b._entry
|
||||
.pend
|
||||
|
||||
func_all_f .proc
|
||||
inx
|
||||
jsr prog8_lib.peek_address
|
||||
lda c64.ESTACK_LO,x ; array size
|
||||
sta c64.SCRATCH_ZPB1
|
||||
lda P8ESTACK_LO,x ; array size
|
||||
sta P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
||||
adc P8ZP_SCRATCH_B1 ; times 5 because of float
|
||||
tay
|
||||
dey
|
||||
- lda (c64.SCRATCH_ZPWORD1),y
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
clc
|
||||
dey
|
||||
adc (c64.SCRATCH_ZPWORD1),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
adc (c64.SCRATCH_ZPWORD1),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
adc (c64.SCRATCH_ZPWORD1),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
adc (c64.SCRATCH_ZPWORD1),y
|
||||
adc (P8ZP_SCRATCH_W1),y
|
||||
dey
|
||||
cmp #0
|
||||
beq +
|
||||
cpy #255
|
||||
bne -
|
||||
lda #1
|
||||
sta c64.ESTACK_LO+1,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
+ sta c64.ESTACK_LO+1,x
|
||||
+ sta P8ESTACK_LO+1,x
|
||||
rts
|
||||
.pend
|
||||
|
||||
@ -674,26 +635,28 @@ func_max_f .proc
|
||||
ldy #>_largest_neg_float
|
||||
_minmax_entry jsr MOVFM
|
||||
jsr prog8_lib.pop_array_and_lengthmin1Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
- sty c64.SCRATCH_ZPREG
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
stx floats_store_reg
|
||||
- sty P8ZP_SCRATCH_REG
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr FCOMP
|
||||
_minmax_cmp cmp #255 ; modified
|
||||
bne +
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr MOVFM
|
||||
+ lda c64.SCRATCH_ZPWORD1
|
||||
+ lda P8ZP_SCRATCH_W1
|
||||
clc
|
||||
adc #5
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc c64.SCRATCH_ZPWORD1+1
|
||||
+ ldy c64.SCRATCH_ZPREG
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ ldy P8ZP_SCRATCH_REG
|
||||
dey
|
||||
cpy #255
|
||||
bne -
|
||||
ldx floats_store_reg
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jmp push_fac1_as_result
|
||||
_largest_neg_float .byte 255,255,255,255,255 ; largest negative float -1.7014118345e+38
|
||||
.pend
|
||||
@ -709,33 +672,35 @@ _largest_pos_float .byte 255,127,255,255,255 ; largest positive float
|
||||
.pend
|
||||
|
||||
func_sum_f .proc
|
||||
lda #<FL_ZERO
|
||||
ldy #>FL_ZERO
|
||||
lda #<FL_ZERO_const
|
||||
ldy #>FL_ZERO_const
|
||||
jsr MOVFM
|
||||
jsr prog8_lib.pop_array_and_lengthmin1Y
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
- sty c64.SCRATCH_ZPREG
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
ldy c64.SCRATCH_ZPWORD1+1
|
||||
stx floats_store_reg
|
||||
- sty P8ZP_SCRATCH_REG
|
||||
lda P8ZP_SCRATCH_W1
|
||||
ldy P8ZP_SCRATCH_W1+1
|
||||
jsr FADD
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
dey
|
||||
cpy #255
|
||||
beq +
|
||||
lda c64.SCRATCH_ZPWORD1
|
||||
lda P8ZP_SCRATCH_W1
|
||||
clc
|
||||
adc #5
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc -
|
||||
inc c64.SCRATCH_ZPWORD1+1
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
bne -
|
||||
+ jmp push_fac1_as_result
|
||||
+ ldx floats_store_reg
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jmp push_fac1_as_result
|
||||
.pend
|
||||
|
||||
sign_f .proc
|
||||
jsr pop_float_fac1
|
||||
jsr SIGN
|
||||
sta c64.ESTACK_LO,x
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
rts
|
||||
.pend
|
||||
@ -744,22 +709,22 @@ sign_f .proc
|
||||
set_0_array_float .proc
|
||||
; -- set a float in an array to zero (index on stack, array in SCRATCH_ZPWORD1)
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
lda P8ESTACK_LO,x
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.ESTACK_LO,x
|
||||
adc P8ESTACK_LO,x
|
||||
tay
|
||||
lda #0
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
iny
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
@ -767,14 +732,13 @@ set_0_array_float .proc
|
||||
set_array_float .proc
|
||||
; -- set a float in an array to a value (index on stack, float in SCRATCH_ZPWORD1, array in SCRATCH_ZPWORD2)
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
lda P8ESTACK_LO,x
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.ESTACK_LO,x
|
||||
clc
|
||||
adc c64.SCRATCH_ZPWORD2
|
||||
ldy c64.SCRATCH_ZPWORD2+1
|
||||
adc P8ESTACK_LO,x
|
||||
adc P8ZP_SCRATCH_W2
|
||||
ldy P8ZP_SCRATCH_W2+1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp copy_float
|
||||
@ -786,12 +750,12 @@ set_array_float .proc
|
||||
swap_floats .proc
|
||||
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
|
||||
ldy #4
|
||||
- lda (c64.SCRATCH_ZPWORD1),y
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
lda (c64.SCRATCH_ZPWORD2),y
|
||||
sta (c64.SCRATCH_ZPWORD1),y
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
sta (P8ZP_SCRATCH_W1),y
|
||||
pla
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
dey
|
||||
bpl -
|
||||
rts
|
||||
|
@ -4,14 +4,14 @@
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%target c64
|
||||
%option enable_floats
|
||||
|
||||
|
||||
c64flt {
|
||||
; ---- this block contains C-64 floating point related functions ----
|
||||
|
||||
const float PI = 3.141592653589793
|
||||
const float TWOPI = 6.283185307179586
|
||||
const float PI = 3.141592653589793
|
||||
const float TWOPI = 6.283185307179586
|
||||
|
||||
|
||||
; ---- C64 basic and kernal ROM float constants and functions ----
|
||||
@ -35,13 +35,11 @@ c64flt {
|
||||
&float FL_TWOPI = $e2e5 ; 2 * PI
|
||||
&float FL_FR4 = $e2ea ; .25
|
||||
; oddly enough, 0.0 isn't available in the kernel.
|
||||
float FL_ZERO = 0.0 ; oddly enough 0.0 isn't available in the kernel
|
||||
|
||||
|
||||
; 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.
|
||||
|
||||
; checked functions below:
|
||||
romsub $bba2 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1
|
||||
romsub $bba6 = FREADMEM() clobbers(A,Y) ; load mflpt value from memory in $22/$23 into fac1
|
||||
romsub $ba8c = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
|
||||
@ -91,6 +89,7 @@ romsub $bb12 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1
|
||||
romsub $bb0f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||
romsub $bf7b = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||
romsub $bf78 = FPWR(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = fac2 ** mflpt from A/Y
|
||||
romsub $bd7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||
|
||||
romsub $aed4 = NOTOP() clobbers(A,X,Y) ; fac1 = NOT(fac1)
|
||||
romsub $bccc = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||
@ -163,9 +162,9 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||
%asm {{
|
||||
sta c64.SCRATCH_ZPREG
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tya
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
jmp GIVAYF ; this uses the inverse order, Y/A
|
||||
}}
|
||||
}
|
||||
@ -174,9 +173,9 @@ asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to signed word in A/Y
|
||||
%asm {{
|
||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
||||
sta c64.SCRATCH_ZPREG
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tya
|
||||
ldy c64.SCRATCH_ZPREG
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
@ -185,41 +184,34 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to unsigned word in A/Y
|
||||
%asm {{
|
||||
jsr GETADR ; this uses the inverse order, Y/A
|
||||
sta c64.SCRATCH_ZPB1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
tya
|
||||
ldy c64.SCRATCH_ZPB1
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_f (float value) {
|
||||
; ---- prints the floating point value (without a newline) using basic rom routines.
|
||||
; ---- prints the floating point value (without a newline).
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
stx floats_store_reg
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM ; load float into fac1
|
||||
jsr FOUT ; fac1 to string in A/Y
|
||||
jsr c64.STROUT ; print string in A/Y
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
rts
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
ldx floats_store_reg
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_fln (float value) {
|
||||
; ---- prints the floating point value (with a newline at the end) using basic rom routines
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM ; load float into fac1
|
||||
jsr FPRINTLN ; print fac1 with newline
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
rts
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
%asminclude "library:c64floats.asm", ""
|
||||
|
||||
} ; ------ end of block c64flt
|
||||
}
|
||||
|
242
compiler/res/prog8lib/c64graphics.p8
Normal file
242
compiler/res/prog8lib/c64graphics.p8
Normal file
@ -0,0 +1,242 @@
|
||||
%target c64
|
||||
%import c64textio
|
||||
|
||||
; bitmap pixel graphics module for the C64
|
||||
; only black/white monchrome for now
|
||||
; assumes bitmap screen memory is $2000-$3fff
|
||||
|
||||
graphics {
|
||||
const uword bitmap_address = $2000
|
||||
|
||||
sub enable_bitmap_mode() {
|
||||
; enable bitmap screen, erase it and set colors to black/white.
|
||||
c64.SCROLY |= %00100000
|
||||
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
|
||||
clear_screen(1, 0)
|
||||
}
|
||||
|
||||
sub clear_screen(ubyte pixelcolor, ubyte bgcolor) {
|
||||
memset(bitmap_address, 320*200/8, 0)
|
||||
txt.fill_screen(pixelcolor << 4 | bgcolor, 0)
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
|
||||
; Bresenham algorithm.
|
||||
; This code special cases various quadrant loops to allow simple ++ and -- operations.
|
||||
; TODO rewrite this in optimized assembly
|
||||
if y1>y2 {
|
||||
; make sure dy is always positive to avoid 8 instead of just 4 special cases
|
||||
swap(x1, x2)
|
||||
swap(y1, y2)
|
||||
}
|
||||
word @zp d = 0
|
||||
ubyte positive_ix = true
|
||||
word @zp dx = x2 - x1 as word
|
||||
word @zp dy = y2 as word - y1 as word
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
positive_ix = false
|
||||
}
|
||||
dx *= 2
|
||||
dy *= 2
|
||||
internal_plotx = x1
|
||||
|
||||
if dx >= dy {
|
||||
if positive_ix {
|
||||
repeat {
|
||||
internal_plot(y1)
|
||||
if internal_plotx==x2
|
||||
return
|
||||
internal_plotx++
|
||||
d += dy
|
||||
if d > dx {
|
||||
y1++
|
||||
d -= dx
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
internal_plot(y1)
|
||||
if internal_plotx==x2
|
||||
return
|
||||
internal_plotx--
|
||||
d += dy
|
||||
if d > dx {
|
||||
y1++
|
||||
d -= dx
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if positive_ix {
|
||||
repeat {
|
||||
internal_plot(y1)
|
||||
if y1 == y2
|
||||
return
|
||||
y1++
|
||||
d += dx
|
||||
if d > dy {
|
||||
internal_plotx++
|
||||
d -= dy
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
internal_plot(y1)
|
||||
if y1 == y2
|
||||
return
|
||||
y1++
|
||||
d += dx
|
||||
if d > dy {
|
||||
internal_plotx--
|
||||
d -= dy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
; Midpoint algorithm
|
||||
ubyte @zp ploty
|
||||
ubyte @zp xx = radius
|
||||
ubyte @zp yy = 0
|
||||
byte @zp decisionOver2 = 1-xx as byte
|
||||
|
||||
while xx>=yy {
|
||||
internal_plotx = xcenter + xx
|
||||
ploty = ycenter + yy
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter - xx
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter + xx
|
||||
ploty = ycenter - yy
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter - xx
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter + yy
|
||||
ploty = ycenter + xx
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter - yy
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter + yy
|
||||
ploty = ycenter - xx
|
||||
internal_plot(ploty)
|
||||
internal_plotx = xcenter - yy
|
||||
internal_plot(ploty)
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += 2*yy+1
|
||||
else {
|
||||
xx--
|
||||
decisionOver2 += 2*(yy-xx)+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub disc(uword cx, ubyte cy, ubyte radius) {
|
||||
; Midpoint algorithm, filled
|
||||
ubyte xx = radius
|
||||
ubyte yy = 0
|
||||
byte decisionOver2 = 1-xx as byte
|
||||
|
||||
while xx>=yy {
|
||||
ubyte cy_plus_yy = cy + yy
|
||||
ubyte cy_min_yy = cy - yy
|
||||
ubyte cy_plus_xx = cy + xx
|
||||
ubyte cy_min_xx = cy - xx
|
||||
|
||||
for internal_plotx in cx to cx+xx {
|
||||
internal_plot(cy_plus_yy)
|
||||
internal_plot(cy_min_yy)
|
||||
}
|
||||
for internal_plotx in cx-xx to cx-1 {
|
||||
internal_plot(cy_plus_yy)
|
||||
internal_plot(cy_min_yy)
|
||||
}
|
||||
for internal_plotx in cx to cx+yy {
|
||||
internal_plot(cy_plus_xx)
|
||||
internal_plot(cy_min_xx)
|
||||
}
|
||||
for internal_plotx in cx-yy to cx {
|
||||
internal_plot(cy_plus_xx)
|
||||
internal_plot(cy_min_xx)
|
||||
}
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += 2*yy+1
|
||||
else {
|
||||
xx--
|
||||
decisionOver2 += 2*(yy-xx)+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; here is the non-asm code for the plot routine below:
|
||||
; sub plot_nonasm(uword px, ubyte py) {
|
||||
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
; uword addr = bitmap_address + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
||||
; @(addr) |= ormask[lsb(px) & 7]
|
||||
; }
|
||||
|
||||
asmsub plot(uword plotx @XY, ubyte ploty @A) clobbers (A, X, Y) {
|
||||
%asm {{
|
||||
stx internal_plotx
|
||||
sty internal_plotx+1
|
||||
jmp internal_plot
|
||||
}}
|
||||
}
|
||||
|
||||
; for efficiency of internal algorithms here is the internal plot routine
|
||||
; that takes the plotx coordinate in a separate variable instead of the XY register pair:
|
||||
|
||||
uword internal_plotx ; 0..319 ; separate 'parameter' for internal_plot()
|
||||
|
||||
asmsub internal_plot(ubyte ploty @A) clobbers (A, X, Y) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register
|
||||
%asm {{
|
||||
tay
|
||||
lda internal_plotx+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lsr a ; 0
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda internal_plotx
|
||||
pha
|
||||
and #7
|
||||
tax
|
||||
|
||||
lda _y_lookup_lo,y
|
||||
clc
|
||||
adc P8ZP_SCRATCH_W2
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda _y_lookup_hi,y
|
||||
adc P8ZP_SCRATCH_W2+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
|
||||
pla ; internal_plotx
|
||||
and #%11111000
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
ora _ormask,x
|
||||
sta (P8ZP_SCRATCH_W2),y
|
||||
rts
|
||||
|
||||
_ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
||||
|
||||
; note: this can be even faster if we also have a 256 byte x-lookup table, but hey.
|
||||
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
||||
; the y lookup tables encodes this formula: bitmap_address + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||
; We use the 64tass syntax for range expressions to calculate this table on assembly time.
|
||||
|
||||
_plot_y_values := $2000 + 320*(range(200)>>3) + (range(200) & 7)
|
||||
|
||||
_y_lookup_lo .byte <_plot_y_values
|
||||
_y_lookup_hi .byte >_plot_y_values
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -5,17 +5,9 @@
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%target c64
|
||||
|
||||
c64 {
|
||||
const uword ESTACK_LO = $ce00 ; evaluation stack (lsb)
|
||||
const uword ESTACK_HI = $cf00 ; evaluation stack (msb)
|
||||
&ubyte SCRATCH_ZPB1 = $02 ; scratch byte 1 in ZP
|
||||
&ubyte SCRATCH_ZPREG = $03 ; scratch register in ZP
|
||||
&ubyte SCRATCH_ZPREGX = $fa ; temp storage for X register (stack pointer)
|
||||
&uword SCRATCH_ZPWORD1 = $fb ; scratch word in ZP ($fb/$fc)
|
||||
&uword SCRATCH_ZPWORD2 = $fd ; scratch word in ZP ($fd/$fe)
|
||||
|
||||
|
||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||
@ -183,19 +175,11 @@ c64 {
|
||||
; ---- end of SID registers ----
|
||||
|
||||
|
||||
|
||||
; ---- C64 basic routines ----
|
||||
|
||||
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||
|
||||
|
||||
; ---- end of C64 basic routines ----
|
||||
|
||||
|
||||
; ---- C64 kernal routines ----
|
||||
; ---- C64 ROM kernal routines ----
|
||||
|
||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use c64scr.print instead)
|
||||
romsub $E544 = CLEARSCR() clobbers(A,X,Y) ; clear the screen
|
||||
romsub $E566 = HOMECRSR() clobbers(A,X,Y) ; cursor to top left of screen
|
||||
romsub $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||
@ -238,6 +222,207 @@ romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
; ---- end of C64 kernal routines ----
|
||||
; ---- end of C64 ROM kernal routines ----
|
||||
|
||||
|
||||
; ---- C64 specific system utility routines: ----
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in,
|
||||
; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set.
|
||||
; Also a different color scheme is chosen to identify ourselves a little.
|
||||
; Uppercase charset is activated, and all three registers set to 0, status flags cleared.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
lda #%00101111
|
||||
sta $00
|
||||
lda #%00100111
|
||||
sta $01
|
||||
jsr c64.IOINIT
|
||||
jsr c64.RESTOR
|
||||
jsr c64.CINT
|
||||
lda #6
|
||||
sta c64.EXTCOL
|
||||
lda #7
|
||||
sta c64.COLOR
|
||||
lda #0
|
||||
sta c64.BGCOL0
|
||||
tax
|
||||
tay
|
||||
clc
|
||||
clv
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irqvec_excl() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
sta c64.CINV
|
||||
lda #>_irq_handler
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
rts
|
||||
_irq_handler jsr set_irqvec._irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr set_irqvec._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
lda c64.CIA1ICR ; acknowledge CIA1 interrupt
|
||||
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irqvec() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
sta c64.CINV
|
||||
lda #>_irq_handler
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
rts
|
||||
_irq_handler jsr _irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr _irq_handler_end
|
||||
jmp c64.IRQDFRT ; continue with normal kernel irq routine
|
||||
|
||||
_irq_handler_init
|
||||
; save all zp scratch registers and the X register as these might be clobbered by the irq routine
|
||||
stx IRQ_X_REG
|
||||
lda P8ZP_SCRATCH_B1
|
||||
sta IRQ_SCRATCH_ZPB1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
sta IRQ_SCRATCH_ZPREG
|
||||
lda P8ZP_SCRATCH_W1
|
||||
sta IRQ_SCRATCH_ZPWORD1
|
||||
lda P8ZP_SCRATCH_W1+1
|
||||
sta IRQ_SCRATCH_ZPWORD1+1
|
||||
lda P8ZP_SCRATCH_W2
|
||||
sta IRQ_SCRATCH_ZPWORD2
|
||||
lda P8ZP_SCRATCH_W2+1
|
||||
sta IRQ_SCRATCH_ZPWORD2+1
|
||||
; stack protector; make sure we don't clobber the top of the evaluation stack
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
dex
|
||||
cld
|
||||
rts
|
||||
|
||||
_irq_handler_end
|
||||
; restore all zp scratch registers and the X register
|
||||
lda IRQ_SCRATCH_ZPB1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda IRQ_SCRATCH_ZPREG
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda IRQ_SCRATCH_ZPWORD1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda IRQ_SCRATCH_ZPWORD1+1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda IRQ_SCRATCH_ZPWORD2
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda IRQ_SCRATCH_ZPWORD2+1
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
ldx IRQ_X_REG
|
||||
rts
|
||||
|
||||
IRQ_X_REG .byte 0
|
||||
IRQ_SCRATCH_ZPB1 .byte 0
|
||||
IRQ_SCRATCH_ZPREG .byte 0
|
||||
IRQ_SCRATCH_ZPWORD1 .word 0
|
||||
IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub restore_irqvec() {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<c64.IRQDFRT
|
||||
sta c64.CINV
|
||||
lda #>c64.IRQDFRT
|
||||
sta c64.CINV+1
|
||||
lda #0
|
||||
sta c64.IREQMASK ; disable raster irq
|
||||
lda #%10000001
|
||||
sta c64.CIA1ICR ; restore CIA1 irq
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
jsr _setup_raster_irq
|
||||
lda #<_raster_irq_handler
|
||||
sta c64.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
jsr set_irqvec._irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr set_irqvec._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
jmp c64.IRQDFRT
|
||||
|
||||
_setup_raster_irq
|
||||
pha
|
||||
lda #%01111111
|
||||
sta c64.CIA1ICR ; "switch off" interrupts signals from cia-1
|
||||
sta c64.CIA2ICR ; "switch off" interrupts signals from cia-2
|
||||
and c64.SCROLY
|
||||
sta c64.SCROLY ; clear most significant bit of raster position
|
||||
lda c64.CIA1ICR ; ack previous irq
|
||||
lda c64.CIA2ICR ; ack previous irq
|
||||
pla
|
||||
sta c64.RASTER ; set the raster line number where interrupt should occur
|
||||
cpy #0
|
||||
beq +
|
||||
lda c64.SCROLY
|
||||
ora #%10000000
|
||||
sta c64.SCROLY ; set most significant bit of raster position
|
||||
+ lda #%00000001
|
||||
sta c64.IREQMASK ;enable raster interrupt signals from vic
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq_excl(uword rasterpos @ AY) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
jsr set_rasterirq._setup_raster_irq
|
||||
lda #<_raster_irq_handler
|
||||
sta c64.CINV
|
||||
lda #>_raster_irq_handler
|
||||
sta c64.CINV+1
|
||||
cli
|
||||
rts
|
||||
|
||||
_raster_irq_handler
|
||||
jsr set_irqvec._irq_handler_init
|
||||
jsr irq.irq
|
||||
jsr set_irqvec._irq_handler_end
|
||||
lda #$ff
|
||||
sta c64.VICIRQ ; acknowledge raster irq
|
||||
jmp c64.IRQDFEND ; end irq processing - don't call kernel
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
; ---- end of C64 specific system utility routines ----
|
||||
|
||||
|
||||
}
|
||||
|
556
compiler/res/prog8lib/c64textio.p8
Normal file
556
compiler/res/prog8lib/c64textio.p8
Normal file
@ -0,0 +1,556 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
|
||||
%target c64
|
||||
%import c64lib
|
||||
%import conv
|
||||
|
||||
|
||||
txt {
|
||||
|
||||
asmsub clear_screen() {
|
||||
%asm {{
|
||||
lda #' '
|
||||
jmp clear_screenchars
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte charcolor @ Y) clobbers(A) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
; (assumes screen and color matrix are at their default addresses)
|
||||
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr clear_screencolors
|
||||
pla
|
||||
jsr clear_screenchars
|
||||
rts
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #0
|
||||
_loop sta c64.Screen,y
|
||||
sta c64.Screen+$0100,y
|
||||
sta c64.Screen+$0200,y
|
||||
sta c64.Screen+$02e8,y
|
||||
iny
|
||||
bne _loop
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screencolors (ubyte scrcolor @ A) clobbers(Y) {
|
||||
; ---- clear the character screen colors with the given color (leaves characters).
|
||||
; (assumes color matrix is at the default address)
|
||||
%asm {{
|
||||
ldy #0
|
||||
_loop sta c64.Colors,y
|
||||
sta c64.Colors+$0100,y
|
||||
sta c64.Colors+$0200,y
|
||||
sta c64.Colors+$02e8,y
|
||||
iny
|
||||
bne _loop
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub color (ubyte txtcol) {
|
||||
c64.COLOR = txtcol
|
||||
}
|
||||
|
||||
asmsub scroll_left_full (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
|
||||
+ ; scroll the color memory
|
||||
ldx #0
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Colors + 40*row + 1,x
|
||||
sta c64.Colors + 40*row,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
bpl -
|
||||
|
||||
_scroll_screen ; scroll the screen memory
|
||||
ldx #0
|
||||
ldy #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 1,x
|
||||
sta c64.Screen + 40*row,x
|
||||
.next
|
||||
inx
|
||||
dey
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_right_full (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
|
||||
+ ; scroll the color memory
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Colors + 40*row + 0,x
|
||||
sta c64.Colors + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
_scroll_screen ; scroll the screen memory
|
||||
ldx #38
|
||||
-
|
||||
.for row=0, row<=24, row+=1
|
||||
lda c64.Screen + 40*row + 0,x
|
||||
sta c64.Screen + 40*row + 1,x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_up_full (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
|
||||
+ ; scroll the color memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
_scroll_screen ; scroll the screen memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=1, row<=24, row+=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row-1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub scroll_down_full (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcs +
|
||||
jmp _scroll_screen
|
||||
|
||||
+ ; scroll the color memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Colors + 40*row,x
|
||||
sta c64.Colors + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
_scroll_screen ; scroll the screen memory
|
||||
ldx #39
|
||||
-
|
||||
.for row=23, row>=0, row-=1
|
||||
lda c64.Screen + 40*row,x
|
||||
sta c64.Screen + 40*(row+1),x
|
||||
.next
|
||||
dex
|
||||
bpl -
|
||||
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to c64.CHROUT of that single char.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_B1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
txa
|
||||
jsr c64.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
cpy #'0'
|
||||
beq +
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
jmp _ones
|
||||
+ pla
|
||||
cmp #'0'
|
||||
beq _ones
|
||||
jsr c64.CHROUT
|
||||
_ones txa
|
||||
jsr c64.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
+ pla
|
||||
jsr conv.byte2decimal
|
||||
jmp print_ub._print_byte_digits
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr c64.CHROUT
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
jsr c64.CHROUT
|
||||
+ ldy #8
|
||||
- lda #'0'
|
||||
asl P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'1'
|
||||
+ jsr c64.CHROUT
|
||||
dey
|
||||
bne -
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubbin
|
||||
pla
|
||||
clc
|
||||
jmp print_ubbin
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubhex
|
||||
pla
|
||||
clc
|
||||
jmp print_ubhex
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
jsr conv.uword2decimal
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
cmp #'0'
|
||||
bne _gotdigit
|
||||
iny
|
||||
bne -
|
||||
|
||||
_gotdigit
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
rts
|
||||
_allzero
|
||||
lda #'0'
|
||||
jmp c64.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
bpl +
|
||||
pha
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
tya
|
||||
eor #255
|
||||
tay
|
||||
pla
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp print_uw
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0 ; char counter = 0
|
||||
- jsr c64.CHRIN
|
||||
cmp #$0d ; return (ascii 13) pressed?
|
||||
beq + ; yes, end.
|
||||
sta (P8ZP_SCRATCH_W1),y ; else store char in buffer
|
||||
iny
|
||||
bne -
|
||||
+ lda #0
|
||||
sta (P8ZP_SCRATCH_W1),y ; finish string with 0 byte
|
||||
rts
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setchr (ubyte col @Y, ubyte row @A) clobbers(A) {
|
||||
; ---- set the character in SCRATCH_ZPB1 on the screen matrix at the given position
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda _screenrows+1,y
|
||||
sta _mod+2
|
||||
lda _screenrows,y
|
||||
clc
|
||||
adc P8ZP_SCRATCH_REG
|
||||
sta _mod+1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
+ lda P8ZP_SCRATCH_B1
|
||||
_mod sta $ffff ; modified
|
||||
rts
|
||||
|
||||
_screenrows .word $0400 + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getchr (ubyte col @Y, ubyte row @A) clobbers(Y) -> ubyte @ A {
|
||||
; ---- get the character in the screen matrix at the given location
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _mod+2
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setclr (ubyte col @Y, ubyte row @A) clobbers(A) {
|
||||
; ---- set the color in SCRATCH_ZPB1 on the screen matrix at the given position
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda _colorrows+1,y
|
||||
sta _mod+2
|
||||
lda _colorrows,y
|
||||
clc
|
||||
adc P8ZP_SCRATCH_REG
|
||||
sta _mod+1
|
||||
bcc +
|
||||
inc _mod+2
|
||||
+ lda P8ZP_SCRATCH_B1
|
||||
_mod sta $ffff ; modified
|
||||
rts
|
||||
|
||||
_colorrows .word $d800 + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getclr (ubyte col @Y, ubyte row @A) clobbers(Y) -> ubyte @ A {
|
||||
; ---- get the color in the screen color matrix at the given location
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_B1
|
||||
asl a
|
||||
tay
|
||||
lda setclr._colorrows+1,y
|
||||
sta _mod+2
|
||||
lda setclr._colorrows,y
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
sta _mod+1
|
||||
bcc _mod
|
||||
inc _mod+2
|
||||
_mod lda $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
lda row
|
||||
asl a
|
||||
tay
|
||||
lda setchr._screenrows+1,y
|
||||
sta _charmod+2
|
||||
adc #$d4
|
||||
sta _colormod+2
|
||||
lda setchr._screenrows,y
|
||||
clc
|
||||
adc column
|
||||
sta _charmod+1
|
||||
sta _colormod+1
|
||||
bcc +
|
||||
inc _charmod+2
|
||||
inc _colormod+2
|
||||
+ lda char
|
||||
_charmod sta $ffff ; modified
|
||||
lda charcolor
|
||||
_colormod sta $ffff ; modified
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
tax
|
||||
clc
|
||||
jsr c64.PLOT
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
361
compiler/res/prog8lib/conv.p8
Normal file
361
compiler/res/prog8lib/conv.p8
Normal file
@ -0,0 +1,361 @@
|
||||
; Prog8 definitions for number conversions routines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
|
||||
conv {
|
||||
|
||||
; ----- number conversions to decimal strings
|
||||
|
||||
asmsub ubyte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ A, ubyte @ X {
|
||||
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
|
||||
%asm {{
|
||||
ldy #uword2decimal.ASCII_0_OFFSET
|
||||
bne uword2decimal.hex_try200
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub uword2decimal (uword value @ AY) -> ubyte @Y, ubyte @A, ubyte @X {
|
||||
; ---- convert 16 bit uword in A/Y to decimal
|
||||
; output in uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
|
||||
; (these are terminated by a zero byte so they can be easily printed)
|
||||
; also returns Y = 100's, A = 10's, X = 1's
|
||||
|
||||
%asm {{
|
||||
|
||||
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
|
||||
;By Omegamatrix Further optimizations by tepples
|
||||
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15
|
||||
|
||||
;HexToDec99
|
||||
; start in A
|
||||
; end with A = 10's, decOnes (also in X)
|
||||
|
||||
;HexToDec255
|
||||
; start in A
|
||||
; end with Y = 100's, A = 10's, decOnes (also in X)
|
||||
|
||||
;HexToDec999
|
||||
; start with A = high byte, Y = low byte
|
||||
; end with Y = 100's, A = 10's, decOnes (also in X)
|
||||
; requires 1 extra temp register on top of decOnes, could combine
|
||||
; these two if HexToDec65535 was eliminated...
|
||||
|
||||
;HexToDec65535
|
||||
; start with A/Y (low/high) as 16 bit value
|
||||
; end with decTenThousand, decThousand, Y = 100's, A = 10's, decOnes (also in X)
|
||||
; (irmen: I store Y and A in decHundreds and decTens too, so all of it can be easily printed)
|
||||
|
||||
|
||||
ASCII_0_OFFSET = $30
|
||||
temp = P8ZP_SCRATCH_B1 ; byte in zeropage
|
||||
hexHigh = P8ZP_SCRATCH_W1 ; byte in zeropage
|
||||
hexLow = P8ZP_SCRATCH_W1+1 ; byte in zeropage
|
||||
|
||||
|
||||
HexToDec65535; SUBROUTINE
|
||||
sty hexHigh ;3 @9
|
||||
sta hexLow ;3 @12
|
||||
tya
|
||||
tax ;2 @14
|
||||
lsr a ;2 @16
|
||||
lsr a ;2 @18 integer divide 1024 (result 0-63)
|
||||
|
||||
cpx #$A7 ;2 @20 account for overflow of multiplying 24 from 43,000 ($A7F8) onward,
|
||||
adc #1 ;2 @22 we can just round it to $A700, and the divide by 1024 is fine...
|
||||
|
||||
;at this point we have a number 1-65 that we have to times by 24,
|
||||
;add to original sum, and Mod 1024 to get a remainder 0-999
|
||||
|
||||
|
||||
sta temp ;3 @25
|
||||
asl a ;2 @27
|
||||
adc temp ;3 @30 x3
|
||||
tay ;2 @32
|
||||
lsr a ;2 @34
|
||||
lsr a ;2 @36
|
||||
lsr a ;2 @38
|
||||
lsr a ;2 @40
|
||||
lsr a ;2 @42
|
||||
tax ;2 @44
|
||||
tya ;2 @46
|
||||
asl a ;2 @48
|
||||
asl a ;2 @50
|
||||
asl a ;2 @52
|
||||
clc ;2 @54
|
||||
adc hexLow ;3 @57
|
||||
sta hexLow ;3 @60
|
||||
txa ;2 @62
|
||||
adc hexHigh ;3 @65
|
||||
sta hexHigh ;3 @68
|
||||
ror a ;2 @70
|
||||
lsr a ;2 @72
|
||||
tay ;2 @74 integer divide 1,000 (result 0-65)
|
||||
|
||||
lsr a ;2 @76 split the 1,000 and 10,000 digit
|
||||
tax ;2 @78
|
||||
lda ShiftedBcdTab,x ;4 @82
|
||||
tax ;2 @84
|
||||
rol a ;2 @86
|
||||
and #$0F ;2 @88
|
||||
ora #ASCII_0_OFFSET
|
||||
sta decThousands ;3 @91
|
||||
txa ;2 @93
|
||||
lsr a ;2 @95
|
||||
lsr a ;2 @97
|
||||
lsr a ;2 @99
|
||||
ora #ASCII_0_OFFSET
|
||||
sta decTenThousands ;3 @102
|
||||
|
||||
lda hexLow ;3 @105
|
||||
cpy temp ;3 @108
|
||||
bmi _doSubtract ;2³ @110/111
|
||||
beq _useZero ;2³ @112/113
|
||||
adc #23 + 24 ;2 @114
|
||||
_doSubtract
|
||||
sbc #23 ;2 @116
|
||||
sta hexLow ;3 @119
|
||||
_useZero
|
||||
lda hexHigh ;3 @122
|
||||
sbc #0 ;2 @124
|
||||
|
||||
Start100s
|
||||
and #$03 ;2 @126
|
||||
tax ;2 @128 0,1,2,3
|
||||
cmp #2 ;2 @130
|
||||
rol a ;2 @132 0,2,5,7
|
||||
ora #ASCII_0_OFFSET
|
||||
tay ;2 @134 Y = Hundreds digit
|
||||
|
||||
lda hexLow ;3 @137
|
||||
adc Mod100Tab,x ;4 @141 adding remainder of 256, 512, and 256+512 (all mod 100)
|
||||
bcs hex_doSub200 ;2³ @143/144
|
||||
|
||||
hex_try200
|
||||
cmp #200 ;2 @145
|
||||
bcc hex_try100 ;2³ @147/148
|
||||
hex_doSub200
|
||||
iny ;2 @149
|
||||
iny ;2 @151
|
||||
sbc #200 ;2 @153
|
||||
hex_try100
|
||||
cmp #100 ;2 @155
|
||||
bcc HexToDec99 ;2³ @157/158
|
||||
iny ;2 @159
|
||||
sbc #100 ;2 @161
|
||||
|
||||
HexToDec99; SUBROUTINE
|
||||
lsr a ;2 @163
|
||||
tax ;2 @165
|
||||
lda ShiftedBcdTab,x ;4 @169
|
||||
tax ;2 @171
|
||||
rol a ;2 @173
|
||||
and #$0F ;2 @175
|
||||
ora #ASCII_0_OFFSET
|
||||
sta decOnes ;3 @178
|
||||
txa ;2 @180
|
||||
lsr a ;2 @182
|
||||
lsr a ;2 @184
|
||||
lsr a ;2 @186
|
||||
ora #ASCII_0_OFFSET
|
||||
|
||||
; irmen: load X with ones, and store Y and A too, for easy printing afterwards
|
||||
sty decHundreds
|
||||
sta decTens
|
||||
ldx decOnes
|
||||
rts ;6 @192 Y=hundreds, A = tens digit, X=ones digit
|
||||
|
||||
|
||||
HexToDec999; SUBROUTINE
|
||||
sty hexLow ;3 @9
|
||||
jmp Start100s ;3 @12
|
||||
|
||||
Mod100Tab
|
||||
.byte 0,56,12,56+12
|
||||
|
||||
ShiftedBcdTab
|
||||
.byte $00,$01,$02,$03,$04,$08,$09,$0A,$0B,$0C
|
||||
.byte $10,$11,$12,$13,$14,$18,$19,$1A,$1B,$1C
|
||||
.byte $20,$21,$22,$23,$24,$28,$29,$2A,$2B,$2C
|
||||
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
|
||||
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
|
||||
|
||||
decTenThousands .byte 0
|
||||
decThousands .byte 0
|
||||
decHundreds .byte 0
|
||||
decTens .byte 0
|
||||
decOnes .byte 0
|
||||
.byte 0 ; zero-terminate the decimal output string
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
; ----- utility functions ----
|
||||
|
||||
|
||||
asmsub byte2decimal (byte value @ A) -> ubyte @ Y, ubyte @ A, ubyte @ X {
|
||||
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
|
||||
; note: if the number is negative, you have to deal with the '-' yourself!
|
||||
%asm {{
|
||||
cmp #0
|
||||
bpl +
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
+ jmp ubyte2decimal
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub ubyte2hex (ubyte value @ A) -> ubyte @ A, ubyte @ Y {
|
||||
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
|
||||
%asm {{
|
||||
stx P8ZP_SCRATCH_REG
|
||||
pha
|
||||
and #$0f
|
||||
tax
|
||||
ldy _hex_digits,x
|
||||
pla
|
||||
lsr a
|
||||
lsr a
|
||||
lsr a
|
||||
lsr a
|
||||
tax
|
||||
lda _hex_digits,x
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
rts
|
||||
|
||||
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub uword2hex (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tya
|
||||
jsr ubyte2hex
|
||||
sta output
|
||||
sty output+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
jsr ubyte2hex
|
||||
sta output+2
|
||||
sty output+3
|
||||
rts
|
||||
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str2uword(str string @ AY) -> uword @ AY {
|
||||
; -- returns the unsigned word value of the string number argument in AY
|
||||
; the number may NOT be preceded by a + sign and may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
%asm {{
|
||||
_result = P8ZP_SCRATCH_W2
|
||||
sta _mod+1
|
||||
sty _mod+2
|
||||
ldy #0
|
||||
sty _result
|
||||
sty _result+1
|
||||
_mod lda $ffff,y ; modified
|
||||
sec
|
||||
sbc #48
|
||||
bpl +
|
||||
_done ; return result
|
||||
lda _result
|
||||
ldy _result+1
|
||||
rts
|
||||
+ cmp #10
|
||||
bcs _done
|
||||
; add digit to result
|
||||
pha
|
||||
jsr _result_times_10
|
||||
pla
|
||||
clc
|
||||
adc _result
|
||||
sta _result
|
||||
bcc +
|
||||
inc _result+1
|
||||
+ iny
|
||||
bne _mod
|
||||
; never reached
|
||||
|
||||
_result_times_10 ; (W*4 + W)*2
|
||||
lda _result+1
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda _result
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
rol P8ZP_SCRATCH_REG
|
||||
clc
|
||||
adc _result
|
||||
sta _result
|
||||
lda P8ZP_SCRATCH_REG
|
||||
adc _result+1
|
||||
asl _result
|
||||
rol a
|
||||
sta _result+1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub str2word(str string @ AY) -> word @ AY {
|
||||
; -- returns the signed word value of the string number argument in AY
|
||||
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
%asm {{
|
||||
_result = P8ZP_SCRATCH_W2
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
sty _result
|
||||
sty _result+1
|
||||
sty _negative
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
cmp #'+'
|
||||
bne +
|
||||
iny
|
||||
+ cmp #'-'
|
||||
bne _parse
|
||||
inc _negative
|
||||
iny
|
||||
_parse lda (P8ZP_SCRATCH_W1),y
|
||||
sec
|
||||
sbc #48
|
||||
bpl _digit
|
||||
_done ; return result
|
||||
lda _negative
|
||||
beq +
|
||||
sec
|
||||
lda #0
|
||||
sbc _result
|
||||
sta _result
|
||||
lda #0
|
||||
sbc _result+1
|
||||
sta _result+1
|
||||
+ lda _result
|
||||
ldy _result+1
|
||||
rts
|
||||
_digit cmp #10
|
||||
bcs _done
|
||||
; add digit to result
|
||||
pha
|
||||
jsr str2uword._result_times_10
|
||||
pla
|
||||
clc
|
||||
adc _result
|
||||
sta _result
|
||||
bcc +
|
||||
inc _result+1
|
||||
+ iny
|
||||
bne _parse
|
||||
; never reached
|
||||
_negative .byte 0
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
153
compiler/res/prog8lib/cx16flt.p8
Normal file
153
compiler/res/prog8lib/cx16flt.p8
Normal file
@ -0,0 +1,153 @@
|
||||
; Prog8 definitions for floating point handling on the CommanderX16
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%target cx16
|
||||
%option enable_floats
|
||||
|
||||
c64flt {
|
||||
; ---- this block contains C-64 floating point related functions ----
|
||||
|
||||
const float PI = 3.141592653589793
|
||||
const float TWOPI = 6.283185307179586
|
||||
|
||||
|
||||
; ---- ROM float functions ----
|
||||
|
||||
; 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.
|
||||
|
||||
; 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.
|
||||
|
||||
romsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||
|
||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||
; there is also c64flt.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
||||
; (tip: use GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||
romsub $fe03 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
romsub $fe06 = GETADR() clobbers(X) -> ubyte @ Y, ubyte @ A
|
||||
|
||||
romsub $fe09 = FADDH() clobbers(A,X,Y) ; fac1 += 0.5, for rounding- call this before INT
|
||||
romsub $fe0c = FSUB(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt from A/Y - fac1
|
||||
romsub $fe0f = FSUBT() clobbers(A,X,Y) ; fac1 = fac2-fac1 mind the order of the operands
|
||||
romsub $fe12 = FADD(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 += mflpt value from A/Y
|
||||
romsub $fe15 = FADDT() clobbers(A,X,Y) ; fac1 += fac2
|
||||
romsub $fe1b = ZEROFC() clobbers(A,X,Y) ; fac1 = 0
|
||||
romsub $fe1e = NORMAL() clobbers(A,X,Y) ; normalize fac1 (?)
|
||||
romsub $fe24 = LOG() clobbers(A,X,Y) ; fac1 = LN(fac1) (natural log)
|
||||
romsub $fe27 = FMULT(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 *= mflpt value from A/Y
|
||||
romsub $fe2a = FMULTT() clobbers(A,X,Y) ; fac1 *= fac2
|
||||
romsub $fe33 = CONUPK(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac2
|
||||
romsub $fe36 = MUL10() clobbers(A,X,Y) ; fac1 *= 10
|
||||
romsub $fe3c = DIV10() clobbers(A,X,Y) ; fac1 /= 10 , CAUTION: result is always positive!
|
||||
romsub $fe3f = FDIV(uword mflpt @ AY) clobbers(A,X,Y) ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||
romsub $fe42 = FDIVT() clobbers(A,X,Y) ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
||||
|
||||
romsub $fe48 = MOVFM(uword mflpt @ AY) clobbers(A,Y) ; load mflpt value from memory in A/Y into fac1
|
||||
romsub $fe4b = MOVMF(uword mflpt @ XY) clobbers(A,Y) ; store fac1 to memory X/Y as 5-byte mflpt
|
||||
romsub $fe4e = MOVFA() clobbers(A,X) ; copy fac2 to fac1
|
||||
romsub $fe51 = MOVAF() clobbers(A,X) ; copy fac1 to fac2 (rounded)
|
||||
romsub $fe54 = MOVEF() clobbers(A,X) ; copy fac1 to fac2
|
||||
romsub $fe5a = SIGN() -> ubyte @ A ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||
romsub $fe5d = SGN() clobbers(A,X,Y) ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||
romsub $fe60 = FREADSA(byte value @ A) clobbers(A,X,Y) ; 8 bit signed A -> float in fac1
|
||||
romsub $fe6c = ABS() ; fac1 = ABS(fac1)
|
||||
romsub $fe6f = FCOMP(uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
||||
romsub $fe78 = INT() clobbers(A,X,Y) ; INT() truncates, use FADDH first to round instead of trunc
|
||||
romsub $fe7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += signed byte in A
|
||||
romsub $fe81 = FOUT() clobbers(X) -> uword @ AY ; fac1 -> string, address returned in AY
|
||||
romsub $fe8a = SQR() clobbers(A,X,Y) ; fac1 = SQRT(fac1)
|
||||
romsub $fe8d = FPWRT() clobbers(A,X,Y) ; fac1 = fac2 ** fac1
|
||||
romsub $fe93 = NEGOP() clobbers(A) ; switch the sign of fac1
|
||||
romsub $fe96 = EXP() clobbers(A,X,Y) ; fac1 = EXP(fac1) (e ** fac1)
|
||||
romsub $fe9f = RND2(byte value @A) clobbers(A,X,Y) ; fac1 = RND(A) float random number generator
|
||||
romsub $fea2 = RND() clobbers(A,X,Y) ; fac1 = RND(fac1) float random number generator
|
||||
romsub $fea5 = COS() clobbers(A,X,Y) ; fac1 = COS(fac1)
|
||||
romsub $fea8 = SIN() clobbers(A,X,Y) ; fac1 = SIN(fac1)
|
||||
romsub $feab = TAN() clobbers(A,X,Y) ; fac1 = TAN(fac1)
|
||||
romsub $feae = ATN() clobbers(A,X,Y) ; fac1 = ATN(fac1)
|
||||
|
||||
|
||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
%asm {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_REG
|
||||
sty P8ZP_SCRATCH_B1
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
jsr GIVAYF ; load it as signed... correct afterwards
|
||||
lda P8ZP_SCRATCH_B1
|
||||
bpl +
|
||||
lda #<_flt65536
|
||||
ldy #>_flt65536
|
||||
jsr FADD
|
||||
+ plx
|
||||
rts
|
||||
_flt65536 .byte 145,0,0,0,0 ; 65536.0
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
jmp GIVAYF ; this uses the inverse order, Y/A
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to signed word in A/Y
|
||||
%asm {{
|
||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
||||
sta P8ZP_SCRATCH_REG
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to unsigned word in A/Y
|
||||
%asm {{
|
||||
jsr GETADR ; this uses the inverse order, Y/A
|
||||
sta P8ZP_SCRATCH_B1
|
||||
tya
|
||||
ldy P8ZP_SCRATCH_B1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
sub print_f (float value) {
|
||||
; ---- prints the floating point value (without a newline).
|
||||
%asm {{
|
||||
phx
|
||||
lda #<value
|
||||
ldy #>value
|
||||
jsr MOVFM ; load float into fac1
|
||||
jsr FOUT ; fac1 to string in A/Y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
plx
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
%asminclude "library:c64floats.asm", ""
|
||||
|
||||
}
|
272
compiler/res/prog8lib/cx16lib.p8
Normal file
272
compiler/res/prog8lib/cx16lib.p8
Normal file
@ -0,0 +1,272 @@
|
||||
; Prog8 definitions for the CommanderX16
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%target cx16
|
||||
|
||||
|
||||
c64 {
|
||||
|
||||
; ---- kernal routines, these are the same as on the Commodore-64 (hence the same block name) ----
|
||||
|
||||
; STROUT --> use screen.print
|
||||
; CLEARSCR -> use screen.clear_screen
|
||||
; HOMECRSR -> use screen.plot
|
||||
|
||||
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||
romsub $FF84 = IOINIT() clobbers(A, X) ; initialize I/O devices (CIA, SID, IRQ)
|
||||
romsub $FF87 = RAMTAS() clobbers(A,X,Y) ; initialize RAM, tape buffer, screen
|
||||
romsub $FF8A = RESTOR() clobbers(A,X,Y) ; restore default I/O vectors
|
||||
romsub $FF8D = VECTOR(uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) ; read/set I/O vector table
|
||||
romsub $FF90 = SETMSG(ubyte value @ A) ; set Kernal message control flag
|
||||
romsub $FF93 = SECOND(ubyte address @ A) clobbers(A) ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
romsub $FF96 = TKSA(ubyte address @ A) clobbers(A) ; (alias: TALKSA) send secondary address after TALK
|
||||
romsub $FF99 = MEMTOP(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set top of memory pointer
|
||||
romsub $FF9C = MEMBOT(uword address @ XY, ubyte dir @ Pc) -> uword @ XY ; read/set bottom of memory pointer
|
||||
romsub $FF9F = SCNKEY() clobbers(A,X,Y) ; scan the keyboard
|
||||
romsub $FFA2 = SETTMO(ubyte timeout @ A) ; set time-out flag for IEEE bus
|
||||
romsub $FFA5 = ACPTR() -> ubyte @ A ; (alias: IECIN) input byte from serial bus
|
||||
romsub $FFA8 = CIOUT(ubyte databyte @ A) ; (alias: IECOUT) output byte to serial bus
|
||||
romsub $FFAB = UNTLK() clobbers(A) ; command serial bus device to UNTALK
|
||||
romsub $FFAE = UNLSN() clobbers(A) ; command serial bus device to UNLISTEN
|
||||
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
|
||||
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
|
||||
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
|
||||
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
|
||||
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
|
||||
romsub $FFC0 = OPEN() clobbers(A,X,Y) ; (via 794 ($31A)) open a logical file
|
||||
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
|
||||
romsub $FFC6 = CHKIN(ubyte logical @ X) clobbers(A,X) ; (via 798 ($31E)) define an input channel
|
||||
romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320)) define an output channel
|
||||
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
|
||||
romsub $FFCF = CHRIN() clobbers(Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
|
||||
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
|
||||
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
|
||||
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
|
||||
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
|
||||
romsub $FFE1 = STOP() clobbers(A,X) -> ubyte @ Pz, ubyte @ Pc ; (via 808 ($328)) check the STOP key
|
||||
romsub $FFE4 = GETIN() clobbers(X,Y) -> ubyte @ A ; (via 810 ($32A)) get a character
|
||||
romsub $FFE7 = CLALL() clobbers(A,X) ; (via 812 ($32C)) close all files
|
||||
romsub $FFEA = UDTIM() clobbers(A,X) ; update the software clock
|
||||
romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of screen rows and columns
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use screen.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
|
||||
; 65c02 hardware vectors:
|
||||
&uword NMI_VEC = $FFFA ; 6502 nmi vector, determined by the kernal if banked in
|
||||
&uword RESET_VEC = $FFFC ; 6502 reset vector, determined by the kernal if banked in
|
||||
&uword IRQ_VEC = $FFFE ; 6502 interrupt vector, determined by the kernal if banked in
|
||||
|
||||
|
||||
; the sixteen virtual 16-bit registers
|
||||
&uword r0 = $02
|
||||
&uword r1 = $04
|
||||
&uword r2 = $06
|
||||
&uword r3 = $08
|
||||
&uword r4 = $0a
|
||||
&uword r5 = $0c
|
||||
&uword r6 = $0e
|
||||
&uword r7 = $10
|
||||
&uword r8 = $12
|
||||
&uword r9 = $14
|
||||
&uword r10 = $16
|
||||
&uword r11 = $18
|
||||
&uword r12 = $1a
|
||||
&uword r13 = $1c
|
||||
&uword r14 = $1e
|
||||
&uword r15 = $20
|
||||
|
||||
; VERA registers
|
||||
|
||||
const uword VERA_BASE = $9F20
|
||||
&ubyte VERA_ADDR_L = VERA_BASE + $00
|
||||
&ubyte VERA_ADDR_M = VERA_BASE + $01
|
||||
&ubyte VERA_ADDR_H = VERA_BASE + $02
|
||||
&ubyte VERA_DATA0 = VERA_BASE + $03
|
||||
&ubyte VERA_DATA1 = VERA_BASE + $04
|
||||
&ubyte VERA_CTRL = VERA_BASE + $05
|
||||
&ubyte VERA_IEN = VERA_BASE + $06
|
||||
&ubyte VERA_ISR = VERA_BASE + $07
|
||||
&ubyte VERA_IRQ_LINE_L = VERA_BASE + $08
|
||||
&ubyte VERA_DC_VIDEO = VERA_BASE + $09
|
||||
&ubyte VERA_DC_HSCALE = VERA_BASE + $0A
|
||||
&ubyte VERA_DC_VSCALE = VERA_BASE + $0B
|
||||
&ubyte VERA_DC_BORDER = VERA_BASE + $0C
|
||||
&ubyte VERA_DC_HSTART = VERA_BASE + $09
|
||||
&ubyte VERA_DC_HSTOP = VERA_BASE + $0A
|
||||
&ubyte VERA_DC_VSTART = VERA_BASE + $0B
|
||||
&ubyte VERA_DC_VSTOP = VERA_BASE + $0C
|
||||
&ubyte VERA_L0_CONFIG = VERA_BASE + $0D
|
||||
&ubyte VERA_L0_MAPBASE = VERA_BASE + $0E
|
||||
&ubyte VERA_L0_TILEBASE = VERA_BASE + $0F
|
||||
&ubyte VERA_L0_HSCROLL_L = VERA_BASE + $10
|
||||
&ubyte VERA_L0_HSCROLL_H = VERA_BASE + $11
|
||||
&ubyte VERA_L0_VSCROLL_L = VERA_BASE + $12
|
||||
&ubyte VERA_L0_VSCROLL_H = VERA_BASE + $13
|
||||
&ubyte VERA_L1_CONFIG = VERA_BASE + $14
|
||||
&ubyte VERA_L1_MAPBASE = VERA_BASE + $15
|
||||
&ubyte VERA_L1_TILEBASE = VERA_BASE + $16
|
||||
&ubyte VERA_L1_HSCROLL_L = VERA_BASE + $17
|
||||
&ubyte VERA_L1_HSCROLL_H = VERA_BASE + $18
|
||||
&ubyte VERA_L1_VSCROLL_L = VERA_BASE + $19
|
||||
&ubyte VERA_L1_VSCROLL_H = VERA_BASE + $1A
|
||||
&ubyte VERA_AUDIO_CTRL = VERA_BASE + $1B
|
||||
&ubyte VERA_AUDIO_RATE = VERA_BASE + $1C
|
||||
&ubyte VERA_AUDIO_DATA = VERA_BASE + $1D
|
||||
&ubyte VERA_SPI_DATA = VERA_BASE + $1E
|
||||
&ubyte VERA_SPI_CTRL = VERA_BASE + $1F
|
||||
; VERA_PSG_BASE = $1F9C0
|
||||
; VERA_PALETTE_BASE = $1FA00
|
||||
; VERA_SPRITES_BASE = $1FC00
|
||||
|
||||
; I/O
|
||||
|
||||
const uword via1 = $9f60 ;VIA 6522 #1
|
||||
&ubyte d1prb = via1+0
|
||||
&ubyte d1pra = via1+1
|
||||
&ubyte d1ddrb = via1+2
|
||||
&ubyte d1ddra = via1+3
|
||||
&ubyte d1t1l = via1+4
|
||||
&ubyte d1t1h = via1+5
|
||||
&ubyte d1t1ll = via1+6
|
||||
&ubyte d1t1lh = via1+7
|
||||
&ubyte d1t2l = via1+8
|
||||
&ubyte d1t2h = via1+9
|
||||
&ubyte d1sr = via1+10
|
||||
&ubyte d1acr = via1+11
|
||||
&ubyte d1pcr = via1+12
|
||||
&ubyte d1ifr = via1+13
|
||||
&ubyte d1ier = via1+14
|
||||
&ubyte d1ora = via1+15
|
||||
|
||||
const uword via2 = $9f70 ;VIA 6522 #2
|
||||
&ubyte d2prb =via2+0
|
||||
&ubyte d2pra =via2+1
|
||||
&ubyte d2ddrb =via2+2
|
||||
&ubyte d2ddra =via2+3
|
||||
&ubyte d2t1l =via2+4
|
||||
&ubyte d2t1h =via2+5
|
||||
&ubyte d2t1ll =via2+6
|
||||
&ubyte d2t1lh =via2+7
|
||||
&ubyte d2t2l =via2+8
|
||||
&ubyte d2t2h =via2+9
|
||||
&ubyte d2sr =via2+10
|
||||
&ubyte d2acr =via2+11
|
||||
&ubyte d2pcr =via2+12
|
||||
&ubyte d2ifr =via2+13
|
||||
&ubyte d2ier =via2+14
|
||||
&ubyte d2ora =via2+15
|
||||
|
||||
|
||||
; ---- Commander X-16 additions on top of C64 kernal routines ----
|
||||
; spelling of the names is taken from the Commander X-16 rom sources
|
||||
|
||||
; TODO specify the correct clobbers for alle these functions, for simplicity all 3 regs are marked clobbered atm
|
||||
|
||||
; supported C128 additions
|
||||
romsub $ff4a = close_all() clobbers(A,X,Y)
|
||||
romsub $ff59 = lkupla() clobbers(A,X,Y)
|
||||
romsub $ff5c = lkupsa() clobbers(A,X,Y)
|
||||
romsub $ff5f = screen_set_mode(ubyte mode @A) clobbers(A, X, Y) -> ubyte @Pc
|
||||
romsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) ; incompatible with C128 dlchr()
|
||||
romsub $ff65 = pfkey() clobbers(A,X,Y)
|
||||
romsub $ff6e = jsrfar() clobbers(A,X,Y)
|
||||
romsub $ff74 = fetch() clobbers(A,X,Y)
|
||||
romsub $ff77 = stash() clobbers(A,X,Y)
|
||||
romsub $ff7a = cmpare() clobbers(A,X,Y)
|
||||
romsub $ff7d = primm() clobbers(A,X,Y)
|
||||
|
||||
; X16 additions
|
||||
romsub $ff44 = macptr() clobbers(A,X,Y)
|
||||
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
|
||||
romsub $ff68 = mouse_config(ubyte shape @A, ubyte scale @X) clobbers (A, X, Y)
|
||||
romsub $ff6b = mouse_get(ubyte zpdataptr @X) clobbers(A)
|
||||
romsub $ff71 = mouse_scan() clobbers(A, X, Y)
|
||||
romsub $ff53 = joystick_scan() clobbers(A, X, Y)
|
||||
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
|
||||
romsub $ff4d = clock_set_date_time() clobbers(A, X, Y) ; args: r0, r1, r2, r3L
|
||||
romsub $ff50 = clock_get_date_time() clobbers(A) ; outout args: r0, r1, r2, r3L
|
||||
|
||||
; high level graphics & fonts
|
||||
romsub $ff20 = GRAPH_init() clobbers(A,X,Y) ; uses vectors=r0
|
||||
romsub $ff23 = GRAPH_clear() clobbers(A,X,Y)
|
||||
romsub $ff26 = GRAPH_set_window() clobbers(A,X,Y) ; uses x=r0, y=r1, width=r2, height=r3
|
||||
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y) clobbers (A,X,Y)
|
||||
romsub $ff2c = GRAPH_draw_line() clobbers(A,X,Y) ; uses x1=r0, y1=r1, x2=r2, y2=r3
|
||||
romsub $ff2f = GRAPH_draw_rect(ubyte fill @Pc) clobbers(A,X,Y) ; uses x=r0, y=r1, width=r2, height=r3, cornerradius=r4
|
||||
romsub $ff32 = GRAPH_move_rect() clobbers(A,X,Y) ; uses sx=r0, sy=r1, tx=r2, ty=r3, width=r4, height=r5
|
||||
romsub $ff35 = GRAPH_draw_oval(ubyte fill @Pc) clobbers(A,X,Y) ; uses x=r0, y=r1, width=r2, height=r3
|
||||
romsub $ff38 = GRAPH_draw_image() clobbers(A,X,Y) ; uses x=r0, y=r1, ptr=r2, width=r3, height=r4
|
||||
romsub $ff3b = GRAPH_set_font() clobbers(A,X,Y) ; uses ptr=r0
|
||||
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, ubyte is_control @Pc) clobbers(A,X,Y)
|
||||
romsub $ff41 = GRAPH_put_char(ubyte char @A) clobbers(A,X,Y) ; uses x=r0, y=r1
|
||||
|
||||
; framebuffer
|
||||
romsub $fef6 = FB_init() clobbers(A,X,Y)
|
||||
romsub $fef9 = FB_get_info() clobbers(X,Y) -> byte @A ; also outputs width=r0, height=r1
|
||||
romsub $fefc = FB_set_palette(ubyte index @A, ubyte bytecount @X) clobbers(A,X,Y) ; also uses pointer=r0
|
||||
romsub $feff = FB_cursor_position() clobbers(A,X,Y) ; uses x=r0, y=r1
|
||||
romsub $ff02 = FB_cursor_next_line() clobbers(A,X,Y) ; uses x=r0
|
||||
romsub $ff05 = FB_get_pixel() clobbers(X,Y) -> ubyte @A
|
||||
romsub $ff08 = FB_get_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
|
||||
romsub $ff0b = FB_set_pixel(ubyte color @A) clobbers(A,X,Y)
|
||||
romsub $ff0e = FB_set_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
|
||||
romsub $ff11 = FB_set_8_pixels(ubyte pattern @A, ubyte color @X) clobbers(A,X,Y)
|
||||
romsub $ff14 = FB_set_8_pixels_opaque(ubyte pattern @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y) ; also uses mask=r0L
|
||||
romsub $ff17 = FB_fill_pixels(ubyte color @A) clobbers(A,X,Y) ; also uses count=r0, step=r1
|
||||
romsub $ff1a = FB_filter_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
|
||||
romsub $ff1d = FB_move_pixels() clobbers(A,X,Y) ; uses sx=r0, sy=r1, tx=r2, ty=r3, count=r4
|
||||
|
||||
; misc
|
||||
romsub $fef0 = sprite_set_image(ubyte number @A, ubyte width @X, ubyte height @Y, ubyte apply_mask @Pc) clobbers(A,X,Y) -> ubyte @Pc ; also uses pixels=r0, mask=r1, bpp=r2L
|
||||
romsub $fef3 = sprite_set_position(ubyte number @A) clobbers(A,X,Y) ; also uses x=r0 and y=r1
|
||||
romsub $fee4 = memory_fill(ubyte value @A) clobbers(A,X,Y) ; uses address=r0, num_bytes=r1
|
||||
romsub $fee7 = memory_copy() clobbers(A,X,Y) ; uses source=r0, target=r1, num_bytes=r2
|
||||
romsub $feea = memory_crc() clobbers(A,X,Y) ; uses address=r0, num_bytes=r1 result->r2
|
||||
romsub $feed = memory_decompress() clobbers(A,X,Y) ; uses input=r0, output=r1 result->r1
|
||||
romsub $fedb = console_init() clobbers(A,X,Y) ; uses x=r0, y=r1, width=r2, height=r3
|
||||
romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc) clobbers(A,X,Y)
|
||||
romsub $fee1 = console_get_char() clobbers(X,Y) -> ubyte @A
|
||||
romsub $fed8 = console_put_image() clobbers(A,X,Y) ; uses ptr=r0, width=r1, height=r2
|
||||
romsub $fed5 = console_set_paging_message() clobbers(A,X,Y) ; uses messageptr=r0
|
||||
romsub $fed2 = kbdbuf_put(ubyte key @A) clobbers(A,X,Y)
|
||||
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
|
||||
romsub $fecc = monitor() clobbers(A,X,Y)
|
||||
|
||||
|
||||
|
||||
; ---- end of kernal routines ----
|
||||
|
||||
asmsub init_system() {
|
||||
; Initializes the machine to a sane starting state.
|
||||
; Called automatically by the loader program logic.
|
||||
%asm {{
|
||||
sei
|
||||
cld
|
||||
;stz $00
|
||||
;stz $01
|
||||
;stz d1prb ; select rom bank 0
|
||||
lda #$80
|
||||
sta VERA_CTRL
|
||||
jsr c64.IOINIT
|
||||
jsr c64.RESTOR
|
||||
jsr c64.CINT
|
||||
lda #0
|
||||
tax
|
||||
tay
|
||||
clc
|
||||
clv
|
||||
cli
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
344
compiler/res/prog8lib/cx16textio.p8
Normal file
344
compiler/res/prog8lib/cx16textio.p8
Normal file
@ -0,0 +1,344 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the CommanderX16
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%target cx16
|
||||
%import cx16lib
|
||||
%import conv
|
||||
|
||||
|
||||
txt {
|
||||
|
||||
sub clear_screen() {
|
||||
c64.CHROUT(147) ; clear screen (spaces)
|
||||
}
|
||||
|
||||
|
||||
asmsub fill_screen (ubyte char @ A, ubyte txtcolor @ Y) clobbers(A) {
|
||||
; ---- fill the character screen with the given fill character and character color.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1 ; fillchar
|
||||
sty P8ZP_SCRATCH_W1+1 ; textcolor
|
||||
phx
|
||||
jsr c64.SCREEN ; get dimensions in X/Y
|
||||
dex
|
||||
dey
|
||||
txa
|
||||
asl a
|
||||
adc #1
|
||||
sta P8ZP_SCRATCH_B1
|
||||
- ldx P8ZP_SCRATCH_B1
|
||||
- stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda cx16.VERA_DATA0
|
||||
and #$f0
|
||||
ora P8ZP_SCRATCH_W1+1
|
||||
sta cx16.VERA_DATA0
|
||||
dex
|
||||
stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda P8ZP_SCRATCH_W1
|
||||
sta cx16.VERA_DATA0
|
||||
dex
|
||||
cpx #255
|
||||
bne -
|
||||
dey
|
||||
bpl --
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
|
||||
}
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
pha
|
||||
phx
|
||||
jsr c64.SCREEN ; get dimensions in X/Y
|
||||
dex
|
||||
dey
|
||||
txa
|
||||
asl a
|
||||
sta P8ZP_SCRATCH_B1
|
||||
pla
|
||||
- ldx P8ZP_SCRATCH_B1
|
||||
- stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
sta cx16.VERA_DATA0
|
||||
dex
|
||||
dex
|
||||
cpx #254
|
||||
bne -
|
||||
dey
|
||||
bpl --
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
ubyte[16] color_to_charcode = [$90,$05,$1c,$9f,$9c,$1e,$1f,$9e,$81,$95,$96,$97,$98,$99,$9a,$9b]
|
||||
|
||||
sub color (ubyte txtcol) {
|
||||
c64.CHROUT(color_to_charcode[txtcol & 15])
|
||||
}
|
||||
|
||||
sub color2 (ubyte txtcol, ubyte bgcol) {
|
||||
c64.CHROUT(color_to_charcode[bgcol & 15])
|
||||
c64.CHROUT(1) ; switch fg and bg colors
|
||||
c64.CHROUT(color_to_charcode[txtcol & 15])
|
||||
}
|
||||
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
; by just one call to c64.CHROUT of that single char.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_B1
|
||||
sty P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_B1),y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.ubyte2decimal
|
||||
pha
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
txa
|
||||
jsr c64.CHROUT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.ubyte2decimal
|
||||
_print_byte_digits
|
||||
pha
|
||||
cpy #'0'
|
||||
beq +
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
jsr c64.CHROUT
|
||||
jmp _ones
|
||||
+ pla
|
||||
cmp #'0'
|
||||
beq _ones
|
||||
jsr c64.CHROUT
|
||||
_ones txa
|
||||
jsr c64.CHROUT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
pha
|
||||
cmp #0
|
||||
bpl +
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
+ pla
|
||||
jsr conv.byte2decimal
|
||||
jmp print_ub._print_byte_digits
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
phx
|
||||
bcc +
|
||||
pha
|
||||
lda #'$'
|
||||
jsr c64.CHROUT
|
||||
pla
|
||||
+ jsr conv.ubyte2hex
|
||||
jsr c64.CHROUT
|
||||
tya
|
||||
jsr c64.CHROUT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'%'
|
||||
jsr c64.CHROUT
|
||||
+ ldy #8
|
||||
- lda #'0'
|
||||
asl P8ZP_SCRATCH_B1
|
||||
bcc +
|
||||
lda #'1'
|
||||
+ jsr c64.CHROUT
|
||||
dey
|
||||
bne -
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubbin
|
||||
pla
|
||||
clc
|
||||
jmp print_ubbin
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
tya
|
||||
jsr print_ubhex
|
||||
pla
|
||||
clc
|
||||
jmp print_ubhex
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.uword2decimal
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
bne -
|
||||
+ plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
phx
|
||||
jsr conv.uword2decimal
|
||||
plx
|
||||
ldy #0
|
||||
- lda conv.uword2decimal.decTenThousands,y
|
||||
beq _allzero
|
||||
cmp #'0'
|
||||
bne _gotdigit
|
||||
iny
|
||||
bne -
|
||||
|
||||
_gotdigit
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
lda conv.uword2decimal.decTenThousands,y
|
||||
bne _gotdigit
|
||||
rts
|
||||
_allzero
|
||||
lda #'0'
|
||||
jmp c64.CHROUT
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
bpl +
|
||||
pha
|
||||
lda #'-'
|
||||
jsr c64.CHROUT
|
||||
tya
|
||||
eor #255
|
||||
tay
|
||||
pla
|
||||
eor #255
|
||||
clc
|
||||
adc #1
|
||||
bcc +
|
||||
iny
|
||||
+ jmp print_uw
|
||||
}}
|
||||
}
|
||||
|
||||
; TODO implement the "missing" txtio subroutines
|
||||
|
||||
sub setcc (ubyte column, ubyte row, ubyte char, ubyte charcolor) {
|
||||
; ---- set char+color at the given position on the screen
|
||||
%asm {{
|
||||
phx
|
||||
lda column
|
||||
asl a
|
||||
tax
|
||||
ldy row
|
||||
lda charcolor
|
||||
and #$0f
|
||||
sta P8ZP_SCRATCH_B1
|
||||
stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda char
|
||||
sta cx16.VERA_DATA0
|
||||
inx
|
||||
stz cx16.VERA_ADDR_H
|
||||
stx cx16.VERA_ADDR_L
|
||||
sty cx16.VERA_ADDR_M
|
||||
lda cx16.VERA_DATA0
|
||||
and #$f0
|
||||
ora P8ZP_SCRATCH_B1
|
||||
sta cx16.VERA_DATA0
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
||||
%asm {{
|
||||
phx
|
||||
tax
|
||||
clc
|
||||
jsr c64.PLOT
|
||||
plx
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -4,8 +4,6 @@
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%import c64lib
|
||||
|
||||
math {
|
||||
%asminclude "library:math.asm", ""
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,8 +4,6 @@
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%import c64lib
|
||||
|
||||
prog8_lib {
|
||||
%asminclude "library:prog8lib.asm", ""
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
3.0
|
||||
4.2
|
||||
|
@ -4,12 +4,10 @@ import kotlinx.cli.*
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.compiler.CompilationResult
|
||||
import prog8.compiler.compileProgram
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.compiler.target.CompilationTarget
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import prog8.compiler.target.c64.codegen.AsmGen
|
||||
import prog8.parser.ParsingFailedError
|
||||
import java.io.IOException
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardWatchEventKinds
|
||||
@ -35,12 +33,13 @@ fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDe
|
||||
|
||||
private fun compileMain(args: Array<String>) {
|
||||
val cli = CommandLineInterface("prog8compiler")
|
||||
val startEmulator by cli.flagArgument("-emu", "auto-start the Vice C-64 emulator after successful compilation")
|
||||
val startEmulator by cli.flagArgument("-emu", "auto-start emulator after successful compilation")
|
||||
val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
|
||||
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
|
||||
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
|
||||
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
||||
val compilationTarget by cli.flagValueArgument("-target", "compilertarget", "target output of the compiler, currently only 'c64' (C64 6502 assembly) available", "c64")
|
||||
val compilationTarget by cli.flagValueArgument("-target", "compilertarget",
|
||||
"target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available", C64Target.name)
|
||||
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
||||
|
||||
try {
|
||||
@ -49,26 +48,6 @@ private fun compileMain(args: Array<String>) {
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
when(compilationTarget) {
|
||||
"c64" -> {
|
||||
with(CompilationTarget) {
|
||||
name = "c64"
|
||||
machine = C64MachineDefinition
|
||||
encodeString = { str, altEncoding ->
|
||||
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||
}
|
||||
decodeString = { bytes, altEncoding ->
|
||||
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||
}
|
||||
asmGenerator = ::AsmGen
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
System.err.println("invalid compilation target")
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
val outputPath = pathFrom(outputDir)
|
||||
if(!outputPath.toFile().isDirectory) {
|
||||
System.err.println("Output path doesn't exist")
|
||||
@ -83,7 +62,7 @@ private fun compileMain(args: Array<String>) {
|
||||
println("Continuous watch mode active. Main module: $filepath")
|
||||
|
||||
try {
|
||||
val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, outputDir=outputPath)
|
||||
val compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, compilationTarget, outputPath)
|
||||
println("Imported files (now watching:)")
|
||||
for (importedFile in compilationResult.importedFiles) {
|
||||
print(" ")
|
||||
@ -108,7 +87,7 @@ private fun compileMain(args: Array<String>) {
|
||||
val filepath = pathFrom(filepathRaw).normalize()
|
||||
val compilationResult: CompilationResult
|
||||
try {
|
||||
compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, outputDir=outputPath)
|
||||
compilationResult = compileProgram(filepath, !dontOptimize, !dontWriteAssembly, compilationTarget, outputPath)
|
||||
if(!compilationResult.success)
|
||||
exitProcess(1)
|
||||
} catch (x: ParsingFailedError) {
|
||||
@ -121,20 +100,7 @@ private fun compileMain(args: Array<String>) {
|
||||
if (compilationResult.programName.isEmpty())
|
||||
println("\nCan't start emulator because no program was assembled.")
|
||||
else if(startEmulator) {
|
||||
for(emulator in listOf("x64sc", "x64")) {
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-silent", "-moncommands", "${compilationResult.programName}.vice-mon-list",
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", compilationResult.programName + ".prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
} catch(x: IOException) {
|
||||
continue // try the next emulator executable
|
||||
}
|
||||
process.waitFor()
|
||||
break
|
||||
}
|
||||
CompilationTarget.instance.machine.launchEmulator(compilationResult.programName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
param.second.stack -> "stack"
|
||||
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
|
||||
param.second.statusflag!=null -> param.second.statusflag.toString()
|
||||
else -> "?????1"
|
||||
else -> "?????"
|
||||
}
|
||||
output("${datatypeString(param.first.type)} ${param.first.name} @$reg")
|
||||
if(param.first!==subroutine.parameters.last())
|
||||
@ -287,12 +287,19 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
}
|
||||
|
||||
override fun visit(assignment: Assignment) {
|
||||
assignment.target.accept(this)
|
||||
if (assignment.aug_op != null && assignment.aug_op != "setvalue")
|
||||
output(" ${assignment.aug_op} ")
|
||||
else
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if(binExpr!=null && binExpr.left isSameAs assignment.target
|
||||
&& binExpr.operator !in setOf("and", "or", "xor")
|
||||
&& binExpr.operator !in comparisonOperators) {
|
||||
// we only support the inplace assignments of the form A = A <operator> <value>
|
||||
assignment.target.accept(this)
|
||||
output(" ${binExpr.operator}= ")
|
||||
binExpr.right.accept(this)
|
||||
} else {
|
||||
assignment.target.accept(this)
|
||||
output(" = ")
|
||||
assignment.value.accept(this)
|
||||
assignment.value.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(postIncrDecr: PostIncrDecr) {
|
||||
@ -300,10 +307,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
output(postIncrDecr.operator)
|
||||
}
|
||||
|
||||
override fun visit(contStmt: Continue) {
|
||||
output("continue")
|
||||
}
|
||||
|
||||
override fun visit(breakStmt: Break) {
|
||||
output("break")
|
||||
}
|
||||
@ -426,8 +429,4 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
whenChoice.statements.accept(this)
|
||||
outputln("")
|
||||
}
|
||||
|
||||
override fun visit(nopStatement: NopStatement) {
|
||||
output("; NOP @ ${nopStatement.position} $nopStatement")
|
||||
}
|
||||
}
|
||||
|
@ -314,9 +314,9 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
||||
}
|
||||
// lookup something from the module.
|
||||
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
||||
is Label, is VarDecl, is Block, is Subroutine, is StructDecl -> stmt
|
||||
null -> null
|
||||
else -> throw SyntaxError("wrong identifier target for $scopedName: $stmt", stmt.position)
|
||||
else -> throw SyntaxError("invalid identifier target type", stmt.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,14 +161,15 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
||||
if(vardecl!=null) return vardecl
|
||||
|
||||
assignment()?.let {
|
||||
return Assignment(it.assign_target().toAst(), null, it.expression().toAst(), it.toPosition())
|
||||
return Assignment(it.assign_target().toAst(), it.expression().toAst(), it.toPosition())
|
||||
}
|
||||
|
||||
augassignment()?.let {
|
||||
return Assignment(it.assign_target().toAst(),
|
||||
it.operator.text,
|
||||
it.expression().toAst(),
|
||||
it.toPosition())
|
||||
// replace A += X with A = A + X
|
||||
val target = it.assign_target().toAst()
|
||||
val oper = it.operator.text.substringBefore('=')
|
||||
val expression = BinaryExpression(target.toExpression(), oper, it.expression().toAst(), it.expression().toPosition())
|
||||
return Assignment(it.assign_target().toAst(), expression, it.toPosition())
|
||||
}
|
||||
|
||||
postincrdecr()?.let {
|
||||
@ -217,9 +218,6 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
||||
val breakstmt = breakstmt()?.toAst()
|
||||
if(breakstmt!=null) return breakstmt
|
||||
|
||||
val continuestmt = continuestmt()?.toAst()
|
||||
if(continuestmt!=null) return continuestmt
|
||||
|
||||
val whenstmt = whenstmt()?.toAst()
|
||||
if(whenstmt!=null) return whenstmt
|
||||
|
||||
@ -256,7 +254,7 @@ private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
|
||||
val clobbers = asmsub_clobbers()?.clobber()?.toAst() ?: emptySet()
|
||||
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) }
|
||||
val normalReturntypes = returns.map { it.type }
|
||||
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
||||
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, false) }
|
||||
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
||||
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers)
|
||||
}
|
||||
@ -265,7 +263,7 @@ private class AsmSubroutineParameter(name: String,
|
||||
type: DataType,
|
||||
val registerOrPair: RegisterOrPair?,
|
||||
val statusflag: Statusflag?,
|
||||
val stack: Boolean,
|
||||
// TODO implement: val stack: Boolean,
|
||||
position: Position) : SubroutineParameter(name, type, position)
|
||||
|
||||
private class AsmSubroutineReturn(val type: DataType,
|
||||
@ -304,11 +302,10 @@ private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParamete
|
||||
when (val name = register.nameInSource.single()) {
|
||||
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name)
|
||||
in Statusflag.names -> statusregister = Statusflag.valueOf(name)
|
||||
else -> throw FatalAstException("invalid register or status flag in $it")
|
||||
else -> throw FatalAstException("invalid register or status flag '$name'")
|
||||
}
|
||||
}
|
||||
AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister,
|
||||
!it.stack?.text.isNullOrEmpty(), toPosition())
|
||||
AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
||||
@ -474,7 +471,7 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
||||
litval.charliteral()!=null -> {
|
||||
try {
|
||||
val cc=litval.charliteral()
|
||||
NumericLiteralValue(DataType.UBYTE, CompilationTarget.encodeString(
|
||||
NumericLiteralValue(DataType.UBYTE, CompilationTarget.instance.encodeString(
|
||||
unescape(litval.charliteral().SINGLECHAR().text, litval.toPosition()),
|
||||
litval.charliteral().ALT_STRING_ENCODING()!=null)[0], litval.toPosition())
|
||||
} catch (ce: CharConversionException) {
|
||||
@ -593,8 +590,6 @@ private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||
return ForLoop(loopvar, iterable, scope, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
|
||||
|
||||
private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
|
||||
|
||||
private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
|
||||
|
@ -21,10 +21,9 @@ enum class DataType {
|
||||
STRUCT; // pass by reference
|
||||
|
||||
/**
|
||||
* is the type assignable to the given other type?
|
||||
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
||||
*/
|
||||
infix fun isAssignableTo(targetType: DataType) =
|
||||
// what types are assignable to others, perhaps via a typecast, without loss of precision?
|
||||
when(this) {
|
||||
UBYTE -> targetType in setOf(UBYTE, WORD, UWORD, FLOAT)
|
||||
BYTE -> targetType in setOf(BYTE, WORD, FLOAT)
|
||||
@ -57,8 +56,8 @@ enum class DataType {
|
||||
return when(this) {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes -> 2
|
||||
FLOAT -> CompilationTarget.machine.FLOAT_MEM_SIZE
|
||||
in PassByReferenceDatatypes -> 2
|
||||
FLOAT -> CompilationTarget.instance.machine.FLOAT_MEM_SIZE
|
||||
in PassByReferenceDatatypes -> CompilationTarget.instance.machine.POINTER_MEM_SIZE
|
||||
else -> -9999999
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import prog8.ast.Program
|
||||
import prog8.ast.processing.*
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||
import prog8.optimizer.AssignmentTransformer
|
||||
|
||||
|
||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
||||
@ -36,17 +35,6 @@ internal fun Program.verifyFunctionArgTypes() {
|
||||
fixer.visit(this)
|
||||
}
|
||||
|
||||
internal fun Program.transformAssignments(errors: ErrorReporter) {
|
||||
val transform = AssignmentTransformer(this, errors)
|
||||
transform.visit(this)
|
||||
while(transform.optimizationsDone>0 && errors.isEmpty()) {
|
||||
transform.applyModifications()
|
||||
transform.optimizationsDone = 0
|
||||
transform.visit(this)
|
||||
}
|
||||
transform.applyModifications()
|
||||
}
|
||||
|
||||
internal fun Module.checkImportedValid() {
|
||||
val imr = ImportedModuleDirectiveRemover()
|
||||
imr.visit(this, this.parent)
|
||||
|
@ -16,6 +16,7 @@ import kotlin.math.abs
|
||||
|
||||
|
||||
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||
val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||
|
||||
|
||||
sealed class Expression: Node {
|
||||
@ -25,6 +26,8 @@ sealed class Expression: Node {
|
||||
abstract fun referencesIdentifiers(vararg name: String): Boolean
|
||||
abstract fun inferType(program: Program): InferredTypes.InferredType
|
||||
|
||||
infix fun isSameAs(assigntarget: AssignTarget) = assigntarget.isSameAs(this)
|
||||
|
||||
infix fun isSameAs(other: Expression): Boolean {
|
||||
if(this===other)
|
||||
return true
|
||||
@ -41,6 +44,23 @@ sealed class Expression: Node {
|
||||
(other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
||||
&& other.arrayspec.index isSameAs arrayspec.index)
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
(other is DirectMemoryRead && other.addressExpression isSameAs addressExpression)
|
||||
}
|
||||
is TypecastExpression -> {
|
||||
(other is TypecastExpression && other.implicit==implicit && other.type==type && other.expression isSameAs expression)
|
||||
}
|
||||
is AddressOf -> {
|
||||
(other is AddressOf && other.identifier.nameInSource == identifier.nameInSource)
|
||||
}
|
||||
is RangeExpr -> {
|
||||
(other is RangeExpr && other.from==from && other.to==to && other.step==step)
|
||||
}
|
||||
is FunctionCall -> {
|
||||
(other is FunctionCall && other.target.nameInSource == target.nameInSource
|
||||
&& other.args.size == args.size
|
||||
&& other.args.zip(args).all { it.first isSameAs it.second } )
|
||||
}
|
||||
else -> other==this
|
||||
}
|
||||
}
|
||||
@ -275,9 +295,11 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
|
||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
||||
override fun constValue(program: Program): NumericLiteralValue? {
|
||||
val cv = expression.constValue(program) ?: return null
|
||||
return cv.cast(type)
|
||||
// val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
|
||||
// return LiteralValue.fromNumber(value.numericValue(), value.type, position).cast(type)
|
||||
val cast = cv.cast(type)
|
||||
return if(cast.isValid)
|
||||
cast.valueOrZero()
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@ -396,62 +418,66 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
||||
|
||||
operator fun compareTo(other: NumericLiteralValue): Int = number.toDouble().compareTo(other.number.toDouble())
|
||||
|
||||
fun cast(targettype: DataType): NumericLiteralValue {
|
||||
class CastValue(val isValid: Boolean, private val value: NumericLiteralValue?) {
|
||||
fun valueOrZero() = if(isValid) value!! else NumericLiteralValue(DataType.UBYTE, 0, Position.DUMMY)
|
||||
}
|
||||
|
||||
fun cast(targettype: DataType): CastValue {
|
||||
if(type==targettype)
|
||||
return this
|
||||
return CastValue(true, this)
|
||||
val numval = number.toDouble()
|
||||
when(type) {
|
||||
DataType.UBYTE -> {
|
||||
if(targettype== DataType.BYTE && numval <= 127)
|
||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||
if(targettype== DataType.WORD || targettype== DataType.UWORD)
|
||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||
if(targettype== DataType.FLOAT)
|
||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(targettype== DataType.UBYTE && numval >= 0)
|
||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||
if(targettype== DataType.UWORD && numval >= 0)
|
||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||
if(targettype== DataType.WORD)
|
||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||
if(targettype== DataType.FLOAT)
|
||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(targettype== DataType.BYTE && numval <= 127)
|
||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||
if(targettype== DataType.UBYTE && numval <= 255)
|
||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||
if(targettype== DataType.WORD && numval <= 32767)
|
||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||
if(targettype== DataType.FLOAT)
|
||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(targettype== DataType.BYTE && numval >= -128 && numval <=127)
|
||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||
if(targettype== DataType.UBYTE && numval >= 0 && numval <= 255)
|
||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||
if(targettype== DataType.UWORD && numval >=0)
|
||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||
if(targettype== DataType.FLOAT)
|
||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if (targettype == DataType.BYTE && numval >= -128 && numval <=127)
|
||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||
if (targettype == DataType.UBYTE && numval >=0 && numval <= 255)
|
||||
return NumericLiteralValue(targettype, number.toShort(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toShort(), position))
|
||||
if (targettype == DataType.WORD && numval >= -32768 && numval <= 32767)
|
||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||
if (targettype == DataType.UWORD && numval >=0 && numval <= 65535)
|
||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
||||
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
throw ExpressionError("can't cast $type into $targettype", position)
|
||||
return CastValue(false, null)
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,14 +587,14 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
||||
if(num==null) {
|
||||
// an array of UWORDs could possibly also contain AddressOfs, other stuff can't be casted
|
||||
if (elementType != DataType.UWORD || it !is AddressOf)
|
||||
return null
|
||||
return null // can't cast a value of the array, abort
|
||||
it
|
||||
} else {
|
||||
try {
|
||||
num.cast(elementType)
|
||||
} catch(x: ExpressionError) {
|
||||
return null
|
||||
}
|
||||
val cast = num.cast(elementType)
|
||||
if(cast.isValid)
|
||||
cast.valueOrZero()
|
||||
else
|
||||
return null // can't cast a value of the array, abort
|
||||
}
|
||||
}.toTypedArray()
|
||||
return ArrayLiteralValue(InferredTypes.InferredType.known(targettype), castArray, position = position)
|
||||
@ -616,7 +642,14 @@ class RangeExpr(var from: Expression,
|
||||
fromDt istype DataType.STR && toDt istype DataType.STR -> InferredTypes.knownFor(DataType.STR)
|
||||
fromDt istype DataType.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
|
||||
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
|
||||
else -> InferredTypes.knownFor(DataType.ARRAY_UB)
|
||||
else -> {
|
||||
val fdt = fromDt.typeOrElse(DataType.STRUCT)
|
||||
val tdt = toDt.typeOrElse(DataType.STRUCT)
|
||||
if(fdt largerThan tdt)
|
||||
InferredTypes.knownFor(ElementArrayTypes.getValue(fdt))
|
||||
else
|
||||
InferredTypes.knownFor(ElementArrayTypes.getValue(tdt))
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun toString(): String {
|
||||
@ -638,8 +671,8 @@ class RangeExpr(var from: Expression,
|
||||
val toString = to as? StringLiteralValue
|
||||
if(fromString!=null && toString!=null ) {
|
||||
// string range -> int range over character values
|
||||
fromVal = CompilationTarget.encodeString(fromString.value, fromString.altEncoding)[0].toInt()
|
||||
toVal = CompilationTarget.encodeString(toString.value, fromString.altEncoding)[0].toInt()
|
||||
fromVal = CompilationTarget.instance.encodeString(fromString.value, fromString.altEncoding)[0].toInt()
|
||||
toVal = CompilationTarget.instance.encodeString(toString.value, fromString.altEncoding)[0].toInt()
|
||||
} else {
|
||||
val fromLv = from as? NumericLiteralValue
|
||||
val toLv = to as? NumericLiteralValue
|
||||
@ -714,11 +747,10 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
override fun referencesIdentifiers(vararg name: String): Boolean = nameInSource.last() in name
|
||||
|
||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||
val targetStmt = targetStatement(program.namespace)
|
||||
return if(targetStmt is VarDecl) {
|
||||
InferredTypes.knownFor(targetStmt.datatype)
|
||||
} else {
|
||||
InferredTypes.InferredType.unknown()
|
||||
return when (val targetStmt = targetStatement(program.namespace)) {
|
||||
is VarDecl -> InferredTypes.knownFor(targetStmt.datatype)
|
||||
is StructDecl -> InferredTypes.knownFor(DataType.STRUCT)
|
||||
else -> InferredTypes.InferredType.unknown()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,9 @@ import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8.compiler.target.CompilationTarget
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import java.io.File
|
||||
|
||||
@ -118,6 +120,11 @@ internal class AstChecker(private val program: Program,
|
||||
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
||||
errors.err("for loop requires a variable to loop with", forLoop.position)
|
||||
} else {
|
||||
|
||||
fun checkLoopRangeValues() {
|
||||
|
||||
}
|
||||
|
||||
when (loopvar.datatype) {
|
||||
DataType.UBYTE -> {
|
||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
||||
@ -142,6 +149,22 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
else -> errors.err("loop variable must be numeric type", forLoop.position)
|
||||
}
|
||||
if(errors.isEmpty()) {
|
||||
// check loop range values
|
||||
val range = forLoop.iterable as? RangeExpr
|
||||
if(range!=null) {
|
||||
val from = range.from as? NumericLiteralValue
|
||||
val to = range.to as? NumericLiteralValue
|
||||
if(from != null)
|
||||
checkValueTypeAndRange(loopvar.datatype, from)
|
||||
else if(!range.from.inferType(program).istype(loopvar.datatype))
|
||||
errors.err("range start value is incompatible with loop variable type", range.position)
|
||||
if(to != null)
|
||||
checkValueTypeAndRange(loopvar.datatype, to)
|
||||
else if(!range.to.inferType(program).istype(loopvar.datatype))
|
||||
errors.err("range end value is incompatible with loop variable type", range.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,13 +327,10 @@ internal class AstChecker(private val program: Program,
|
||||
} else {
|
||||
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||
// Instead, their reference (address) should be passed (as an UWORD).
|
||||
// The language has no typed pointers at this time.
|
||||
if(subroutine.parameters.any{it.type in PassByReferenceDatatypes }) {
|
||||
err("Pass-by-reference types (str, array) cannot occur as a parameter type directly. Instead, use an uword for their address, or access the variable from the outer scope directly.")
|
||||
err("Pass-by-reference types (str, array) cannot occur as a parameter type directly. Instead, use an uword to receive their address, or access the variable from the outer scope directly.")
|
||||
}
|
||||
}
|
||||
|
||||
visitStatements(subroutine.statements)
|
||||
}
|
||||
|
||||
override fun visit(untilLoop: UntilLoop) {
|
||||
@ -362,8 +382,18 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(assignment.value.inferType(program) != assignment.target.inferType(program, assignment))
|
||||
errors.err("assignment value is of different type as the target", assignment.value.position)
|
||||
val targetDt = assignment.target.inferType(program, assignment)
|
||||
if(assignment.value.inferType(program) != targetDt) {
|
||||
if(targetDt.typeOrElse(DataType.STRUCT) in IterableDatatypes)
|
||||
errors.err("cannot assign value to string or array", assignment.value.position)
|
||||
else
|
||||
errors.err("value's type doesn't match target", assignment.value.position)
|
||||
}
|
||||
|
||||
if(assignment.value is TypecastExpression) {
|
||||
if(assignment.isAugmentable && targetDt.istype(DataType.FLOAT))
|
||||
errors.err("typecasting a float value in-place makes no sense", assignment.value.position)
|
||||
}
|
||||
|
||||
super.visit(assignment)
|
||||
}
|
||||
@ -434,21 +464,20 @@ internal class AstChecker(private val program: Program,
|
||||
if(variable==null)
|
||||
errors.err("pointer-of operand must be the name of a heap variable", addressOf.position)
|
||||
else {
|
||||
if(variable.datatype !in ArrayDatatypes && variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
||||
if(variable.datatype !in ArrayDatatypes
|
||||
&& variable.type!=VarDeclType.MEMORY
|
||||
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||
}
|
||||
super.visit(addressOf)
|
||||
}
|
||||
|
||||
override fun visit(decl: VarDecl) {
|
||||
fun err(msg: String, position: Position?=null) {
|
||||
errors.err(msg, position ?: decl.position)
|
||||
}
|
||||
fun err(msg: String, position: Position?=null) = errors.err(msg, position ?: decl.position)
|
||||
|
||||
// the initializer value can't refer to the variable itself (recursive definition)
|
||||
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
||||
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true)
|
||||
err("recursive var declaration")
|
||||
}
|
||||
|
||||
// CONST can only occur on simple types (byte, word, float)
|
||||
if(decl.type== VarDeclType.CONST) {
|
||||
@ -456,10 +485,12 @@ internal class AstChecker(private val program: Program,
|
||||
err("const modifier can only be used on numeric types (byte, word, float)")
|
||||
}
|
||||
|
||||
// FLOATS
|
||||
if(!compilerOptions.floats && decl.datatype in setOf(DataType.FLOAT, DataType.ARRAY_F) && decl.type!= VarDeclType.MEMORY) {
|
||||
// FLOATS enabled?
|
||||
if(!compilerOptions.floats && decl.datatype in setOf(DataType.FLOAT, DataType.ARRAY_F) && decl.type!= VarDeclType.MEMORY)
|
||||
err("floating point used, but that is not enabled via options")
|
||||
}
|
||||
|
||||
if(decl.datatype == DataType.FLOAT && (decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE))
|
||||
errors.warn("floating point values won't be placed in Zeropage due to size constraints", decl.position)
|
||||
|
||||
// ARRAY without size specifier MUST have an iterable initializer value
|
||||
if(decl.isArray && decl.arraysize==null) {
|
||||
@ -492,8 +523,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
when(decl.value) {
|
||||
null -> {
|
||||
// a vardecl without an initial value, don't bother with the rest
|
||||
return super.visit(decl)
|
||||
// a vardecl without an initial value, don't bother with it
|
||||
}
|
||||
is RangeExpr -> throw FatalAstException("range expression should have been converted to a true array value")
|
||||
is StringLiteralValue -> {
|
||||
@ -529,15 +559,17 @@ internal class AstChecker(private val program: Program,
|
||||
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
|
||||
}
|
||||
else -> {
|
||||
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
|
||||
super.visit(decl)
|
||||
return
|
||||
if(decl.type==VarDeclType.CONST) {
|
||||
err("const declaration needs a compile-time constant initializer value, or range")
|
||||
super.visit(decl)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VarDeclType.MEMORY -> {
|
||||
if(decl.arraysize!=null) {
|
||||
val arraySize = decl.arraysize!!.size() ?: 1
|
||||
val arraySize = decl.arraysize!!.constIndex() ?: 1
|
||||
when(decl.datatype) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB ->
|
||||
if(arraySize > 256)
|
||||
@ -577,6 +609,26 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
// array length limits
|
||||
if(decl.isArray) {
|
||||
val length = decl.arraysize!!.constIndex() ?: 1
|
||||
when (decl.datatype) {
|
||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
if(length==0 || length>256)
|
||||
err("string and byte array length must be 1-256")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
if(length==0 || length>128)
|
||||
err("word array length must be 1-128")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
if(length==0 || length>51)
|
||||
err("float array length must be 1-51")
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
super.visit(decl)
|
||||
}
|
||||
|
||||
@ -660,6 +712,14 @@ internal class AstChecker(private val program: Program,
|
||||
else if(directive.args.map{it.name in setOf("enable_floats", "force_output")}.any { !it })
|
||||
err("invalid option directive argument(s)")
|
||||
}
|
||||
"%target" -> {
|
||||
if(directive.parent !is Block && directive.parent !is Module)
|
||||
err("this directive may only occur in a block or at module level")
|
||||
if(directive.args.size != 1)
|
||||
err("directive requires one argument")
|
||||
if(directive.args.single().name !in setOf(C64Target.name, Cx16Target.name))
|
||||
err("invalid compilation target")
|
||||
}
|
||||
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
|
||||
}
|
||||
super.visit(directive)
|
||||
@ -691,12 +751,20 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(expr: PrefixExpression) {
|
||||
val dt = expr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if(expr.operator=="-") {
|
||||
val dt = expr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
||||
errors.err("can only take negative of a signed number type", expr.position)
|
||||
}
|
||||
}
|
||||
else if(expr.operator == "not") {
|
||||
if(dt !in IntegerDatatypes)
|
||||
errors.err("can only use boolean not on integer types", expr.position)
|
||||
}
|
||||
else if(expr.operator == "~") {
|
||||
if(dt !in IntegerDatatypes)
|
||||
errors.err("can only use bitwise invert on integer types", expr.position)
|
||||
}
|
||||
super.visit(expr)
|
||||
}
|
||||
|
||||
@ -738,12 +806,6 @@ internal class AstChecker(private val program: Program,
|
||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||
errors.err("bitwise operator can only be used on integer operands", expr.right.position)
|
||||
}
|
||||
"<<", ">>" -> {
|
||||
// for now, bit-shifts can only shift by a constant number
|
||||
val constRight = expr.right.constValue(program)
|
||||
if(constRight==null)
|
||||
errors.err("bit-shift can only be done by a constant number (for now)", expr.right.position)
|
||||
}
|
||||
}
|
||||
|
||||
if(leftDt !in NumericDatatypes)
|
||||
@ -810,6 +872,10 @@ internal class AstChecker(private val program: Program,
|
||||
errors.warn("sgn() of unsigned type is always 0 or 1, this is perhaps not what was intended", functionCall.args.first().position)
|
||||
}
|
||||
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCall, functionCall.definingScope(), program)
|
||||
if(error!=null)
|
||||
errors.err(error, functionCall.args.first().position)
|
||||
|
||||
super.visit(functionCall)
|
||||
}
|
||||
|
||||
@ -832,12 +898,18 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
|
||||
if(functionCallStatement.target.nameInSource.last() in setOf("rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
|
||||
// in-place modification, can't be done on literals
|
||||
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
|
||||
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
|
||||
}
|
||||
}
|
||||
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCallStatement, functionCallStatement.definingScope(), program)
|
||||
if(error!=null) {
|
||||
errors.err(error, functionCallStatement.args.firstOrNull()?.position ?: functionCallStatement.position)
|
||||
}
|
||||
|
||||
super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
@ -846,79 +918,35 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("cannot use arguments when calling a label", position)
|
||||
|
||||
if(target is BuiltinFunctionStatementPlaceholder) {
|
||||
// it's a call to a builtin function.
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
if(args.size!=func.parameters.size)
|
||||
errors.err("invalid number of arguments", position)
|
||||
else {
|
||||
val paramTypesForAddressOf = PassByReferenceDatatypes + DataType.UWORD
|
||||
for (arg in args.withIndex().zip(func.parameters)) {
|
||||
val argDt=arg.first.value.inferType(program)
|
||||
if (argDt.isKnown
|
||||
&& !(argDt.typeOrElse(DataType.STRUCT) isAssignableTo arg.second.possibleDatatypes)
|
||||
&& (argDt.typeOrElse(DataType.STRUCT) != DataType.UWORD || arg.second.possibleDatatypes.intersect(paramTypesForAddressOf).isEmpty())) {
|
||||
errors.err("builtin function '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position)
|
||||
}
|
||||
if(target.name=="swap") {
|
||||
// swap() is a bit weird because this one is translated into a operations directly, instead of being a function call
|
||||
val dt1 = args[0].inferType(program)
|
||||
val dt2 = args[1].inferType(program)
|
||||
if (dt1 != dt2)
|
||||
errors.err("swap requires 2 args of identical type", position)
|
||||
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||
errors.err("swap requires 2 variables, not constant value(s)", position)
|
||||
else if(args[0] isSameAs args[1])
|
||||
errors.err("swap should have 2 different args", position)
|
||||
else if(dt1.typeOrElse(DataType.STRUCT) !in NumericDatatypes)
|
||||
errors.err("swap requires args of numerical type", position)
|
||||
}
|
||||
else if(target.name=="all" || target.name=="any") {
|
||||
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program.namespace)?.datatype == DataType.STR) {
|
||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||
}
|
||||
if(target.name=="swap") {
|
||||
// swap() is a bit weird because this one is translated into a operations directly, instead of being a function call
|
||||
val dt1 = args[0].inferType(program)
|
||||
val dt2 = args[1].inferType(program)
|
||||
if (dt1 != dt2)
|
||||
errors.err("swap requires 2 args of identical type", position)
|
||||
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||
errors.err("swap requires 2 variables, not constant value(s)", position)
|
||||
else if(args[0] isSameAs args[1])
|
||||
errors.err("swap should have 2 different args", position)
|
||||
else if(dt1.typeOrElse(DataType.STRUCT) !in NumericDatatypes)
|
||||
errors.err("swap requires args of numerical type", position)
|
||||
}
|
||||
else if(target.name=="all" || target.name=="any") {
|
||||
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program.namespace)?.datatype == DataType.STR) {
|
||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||
}
|
||||
if(args[0].inferType(program).typeOrElse(DataType.STR) == DataType.STR) {
|
||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||
}
|
||||
if(args[0].inferType(program).typeOrElse(DataType.STR) == DataType.STR) {
|
||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||
}
|
||||
}
|
||||
} else if(target is Subroutine) {
|
||||
if(target.regXasResult())
|
||||
errors.warn("subroutine call return value in X register is discarded and replaced by 0", position)
|
||||
if(args.size!=target.parameters.size)
|
||||
errors.err("invalid number of arguments", position)
|
||||
else {
|
||||
if(target.isAsmSubroutine) {
|
||||
for (arg in args.withIndex().zip(target.parameters)) {
|
||||
val argIDt = arg.first.value.inferType(program)
|
||||
if(!argIDt.isKnown) {
|
||||
if (!argIDt.isKnown)
|
||||
return
|
||||
}
|
||||
val argDt=argIDt.typeOrElse(DataType.STRUCT)
|
||||
if(!(argDt isAssignableTo arg.second.type)) {
|
||||
// for asm subroutines having STR param it's okay to provide a UWORD (address value)
|
||||
if(!(target.isAsmSubroutine && arg.second.type == DataType.STR && argDt == DataType.UWORD))
|
||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position)
|
||||
}
|
||||
|
||||
if(target.isAsmSubroutine) {
|
||||
if (target.asmParameterRegisters[arg.first.index].registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.XY, RegisterOrPair.X)) {
|
||||
if (arg.first.value !is NumericLiteralValue && arg.first.value !is IdentifierReference)
|
||||
errors.warn("calling a subroutine that expects X as a parameter is problematic. If you see a compiler error/crash about this later, try to change this call", position)
|
||||
}
|
||||
|
||||
// check if the argument types match the register(pairs)
|
||||
val asmParamReg = target.asmParameterRegisters[arg.first.index]
|
||||
if(asmParamReg.statusflag!=null) {
|
||||
if(argDt !in ByteDatatypes)
|
||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} must be byte type for statusflag", position)
|
||||
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
||||
if(argDt !in ByteDatatypes)
|
||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} must be byte type for single register", position)
|
||||
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||
if(argDt !in WordDatatypes + IterableDatatypes)
|
||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} must be word type for register pair", position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -958,7 +986,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(target is VarDecl) {
|
||||
if(target.datatype !in IterableDatatypes)
|
||||
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
|
||||
val arraysize = target.arraysize?.size()
|
||||
val arraysize = target.arraysize?.constIndex()
|
||||
if(arraysize!=null) {
|
||||
// check out of bounds
|
||||
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
||||
@ -1039,35 +1067,14 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(scope: AnonymousScope) {
|
||||
visitStatements(scope.statements)
|
||||
}
|
||||
|
||||
private fun visitStatements(statements: List<Statement>) {
|
||||
for((index, stmt) in statements.withIndex()) {
|
||||
if(index < statements.lastIndex && statements[index+1] !is Subroutine) {
|
||||
when {
|
||||
stmt is FunctionCallStatement && stmt.target.nameInSource.last() == "exit" -> {
|
||||
errors.warn("unreachable code, preceding exit call will never return", statements[index + 1].position)
|
||||
}
|
||||
stmt is Return && statements[index + 1] !is Subroutine -> {
|
||||
errors.warn("unreachable code, preceding return statement", statements[index + 1].position)
|
||||
}
|
||||
stmt is Jump && statements[index + 1] !is Subroutine -> {
|
||||
errors.warn("unreachable code, preceding jump statement", statements[index + 1].position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stmt.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
||||
val targetStatement = target.targetStatement(program.namespace)
|
||||
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
||||
return targetStatement
|
||||
errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position)
|
||||
else if(targetStatement==null)
|
||||
errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position)
|
||||
else
|
||||
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", statement.position)
|
||||
return null
|
||||
}
|
||||
|
||||
@ -1100,7 +1107,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(value.type.istype(targetDt)) {
|
||||
if(!checkArrayValues(value, targetDt))
|
||||
return false
|
||||
val arraySpecSize = arrayspec.size()
|
||||
val arraySpecSize = arrayspec.constIndex()
|
||||
val arraySize = value.value.size
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
if(arraySpecSize<1 || arraySpecSize>256)
|
||||
@ -1122,7 +1129,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(value.type.istype(targetDt)) {
|
||||
if(!checkArrayValues(value, targetDt))
|
||||
return false
|
||||
val arraySpecSize = arrayspec.size()
|
||||
val arraySpecSize = arrayspec.constIndex()
|
||||
val arraySize = value.value.size
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
if(arraySpecSize<1 || arraySpecSize>128)
|
||||
@ -1145,7 +1152,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(!checkArrayValues(value, targetDt))
|
||||
return false
|
||||
val arraySize = value.value.size
|
||||
val arraySpecSize = arrayspec.size()
|
||||
val arraySpecSize = arrayspec.constIndex()
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
if(arraySpecSize < 1 || arraySpecSize>51)
|
||||
return err("float array length must be 1-51")
|
||||
@ -1160,7 +1167,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
// check if the floating point values are all within range
|
||||
val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
|
||||
if(doubles.any { it < CompilationTarget.machine.FLOAT_MAX_NEGATIVE || it > CompilationTarget.machine.FLOAT_MAX_POSITIVE })
|
||||
if(doubles.any { it < CompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || it > CompilationTarget.instance.machine.FLOAT_MAX_POSITIVE })
|
||||
return err("floating point value overflow")
|
||||
return true
|
||||
}
|
||||
@ -1237,7 +1244,11 @@ internal class AstChecker(private val program: Program,
|
||||
is AddressOf -> it.identifier.heapId(program.namespace)
|
||||
is TypecastExpression -> {
|
||||
val constVal = it.expression.constValue(program)
|
||||
constVal?.cast(it.type)?.number?.toInt() ?: -9999999
|
||||
val cast = constVal?.cast(it.type)
|
||||
if(cast==null || !cast.isValid)
|
||||
-9999999
|
||||
else
|
||||
cast.valueOrZero().number.toInt()
|
||||
}
|
||||
else -> -9999999
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
||||
}
|
||||
|
||||
override fun visit(block: Block) {
|
||||
if(block.name in CompilationTarget.instance.machine.opcodeNames)
|
||||
errors.err("can't use a cpu opcode name as a symbol: '${block.name}'", block.position)
|
||||
|
||||
val existing = blocks[block.name]
|
||||
if(existing!=null)
|
||||
nameError(block.name, block.position, existing)
|
||||
@ -37,7 +40,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
||||
if(decl.name in BuiltinFunctions)
|
||||
errors.err("builtin function cannot be redefined", decl.position)
|
||||
|
||||
if(decl.name in CompilationTarget.machine.opcodeNames)
|
||||
if(decl.name in CompilationTarget.instance.machine.opcodeNames)
|
||||
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
||||
|
||||
if(decl.datatype==DataType.STRUCT) {
|
||||
@ -71,7 +74,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
||||
}
|
||||
|
||||
override fun visit(subroutine: Subroutine) {
|
||||
if(subroutine.name in CompilationTarget.machine.opcodeNames) {
|
||||
if(subroutine.name in CompilationTarget.instance.machine.opcodeNames) {
|
||||
errors.err("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position)
|
||||
} else if(subroutine.name in BuiltinFunctions) {
|
||||
// the builtin functions can't be redefined
|
||||
@ -116,7 +119,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
||||
}
|
||||
|
||||
override fun visit(label: Label) {
|
||||
if(label.name in CompilationTarget.machine.opcodeNames)
|
||||
if(label.name in CompilationTarget.instance.machine.opcodeNames)
|
||||
errors.err("can't use a cpu opcode name as a symbol: '${label.name}'", label.position)
|
||||
|
||||
if(label.name in BuiltinFunctions) {
|
||||
|
@ -12,6 +12,7 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallStatement.target.nameInSource == listOf("swap")) {
|
||||
// TODO don't replace swap(), let the code generator figure this all out
|
||||
// if x and y are both just identifiers, do not rewrite (there should be asm generation for that)
|
||||
// otherwise:
|
||||
// rewrite swap(x,y) as follows:
|
||||
@ -26,19 +27,16 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
||||
val tempvar = IdentifierReference(listOf(tempname), first.position)
|
||||
val assignTemp = Assignment(
|
||||
AssignTarget(tempvar, null, null, first.position),
|
||||
null,
|
||||
first,
|
||||
first.position
|
||||
)
|
||||
val assignFirst = Assignment(
|
||||
AssignTarget.fromExpr(first),
|
||||
null,
|
||||
second,
|
||||
first.position
|
||||
)
|
||||
val assignSecond = Assignment(
|
||||
AssignTarget.fromExpr(second),
|
||||
null,
|
||||
tempvar,
|
||||
first.position
|
||||
)
|
||||
@ -50,18 +48,6 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
||||
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
||||
val typecast = TypecastExpression(functionCall.args.single(), DataType.UBYTE, false, functionCall.position)
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
functionCall, typecast, parent
|
||||
))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
// is it a struct variable? then define all its struct members as mangled names,
|
||||
// and include the original decl as well.
|
||||
|
@ -61,6 +61,18 @@ interface IAstModification {
|
||||
}
|
||||
}
|
||||
|
||||
class InsertBefore(val before: Statement, val stmt: Statement, val parent: Node) : IAstModification {
|
||||
override fun perform() {
|
||||
if(parent is INameScope) {
|
||||
val idx = parent.statements.indexOfFirst { it===before }
|
||||
parent.statements.add(idx, stmt)
|
||||
stmt.linkParents(parent)
|
||||
} else {
|
||||
throw FatalAstException("parent of an insert modification is not an INameScope")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReplaceNode(val node: Node, val replacement: Node, val parent: Node) : IAstModification {
|
||||
override fun perform() {
|
||||
parent.replaceChildNode(node, replacement)
|
||||
@ -88,7 +100,6 @@ abstract class AstWalker {
|
||||
open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun before(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun before(contStmt: Continue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
@ -130,7 +141,6 @@ abstract class AstWalker {
|
||||
open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun after(contStmt: Continue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||
@ -309,11 +319,6 @@ abstract class AstWalker {
|
||||
track(after(postIncrDecr, parent), postIncrDecr, parent)
|
||||
}
|
||||
|
||||
fun visit(contStmt: Continue, parent: Node) {
|
||||
track(before(contStmt, parent), contStmt, parent)
|
||||
track(after(contStmt, parent), contStmt, parent)
|
||||
}
|
||||
|
||||
fun visit(breakStmt: Break, parent: Node) {
|
||||
track(before(breakStmt, parent), breakStmt, parent)
|
||||
track(after(breakStmt, parent), breakStmt, parent)
|
||||
|
@ -95,9 +95,6 @@ interface IAstVisitor {
|
||||
postIncrDecr.target.accept(this)
|
||||
}
|
||||
|
||||
fun visit(contStmt: Continue) {
|
||||
}
|
||||
|
||||
fun visit(breakStmt: Break) {
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,8 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
||||
// - in every scope, most directives and vardecls are moved to the top.
|
||||
// - the 'start' subroutine is moved to the top.
|
||||
// - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
|
||||
// - (syntax desugaring) augmented assignment is turned into regular assignment.
|
||||
// - (syntax desugaring) struct value assignment is expanded into several struct member assignments.
|
||||
// - in-place assignments are reordered a bit so that they are mostly of the form A = A <operator> <rest>
|
||||
// - sorts the choices in when statement.
|
||||
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||
|
||||
@ -71,23 +71,6 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
val declValue = decl.value
|
||||
if(declValue!=null && decl.type== VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||
val declConstValue = declValue.constValue(program)
|
||||
if(declConstValue==null) {
|
||||
// move the vardecl (without value) to the scope and replace this with a regular assignment
|
||||
decl.value = null
|
||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||
val assign = Assignment(target, null, declValue, decl.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, assign, parent),
|
||||
IAstModification.InsertFirst(decl, decl.definingScope() as Node)
|
||||
)
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
|
||||
val choices = whenStatement.choiceValues(program).sortedBy {
|
||||
@ -99,10 +82,6 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
||||
}
|
||||
|
||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
if(assignment.aug_op!=null) {
|
||||
return listOf(IAstModification.ReplaceNode(assignment, assignment.asDesugaredNonaugmented(), parent))
|
||||
}
|
||||
|
||||
val valueType = assignment.value.inferType(program)
|
||||
val targetType = assignment.target.inferType(program, assignment)
|
||||
if(valueType.istype(DataType.STRUCT) && targetType.istype(DataType.STRUCT)) {
|
||||
@ -122,6 +101,55 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
// rewrite in-place assignment expressions a bit so that the assignment target usually is the leftmost operand
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if(binExpr!=null) {
|
||||
if(binExpr.left isSameAs assignment.target) {
|
||||
// A = A <operator> 5, unchanged
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(binExpr.operator in associativeOperators) {
|
||||
if (binExpr.right isSameAs assignment.target) {
|
||||
// A = v <associative-operator> A ==> A = A <associative-operator> v
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
|
||||
val leftBinExpr = binExpr.left as? BinaryExpression
|
||||
if(leftBinExpr?.operator == binExpr.operator) {
|
||||
return if(leftBinExpr.left isSameAs assignment.target) {
|
||||
// A = (A <associative-operator> x) <same-operator> y ==> A = A <associative-operator> (x <same-operator> y)
|
||||
val newRight = BinaryExpression(leftBinExpr.right, binExpr.operator, binExpr.right, binExpr.position)
|
||||
val newValue = BinaryExpression(leftBinExpr.left, binExpr.operator, newRight, binExpr.position)
|
||||
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
||||
} else {
|
||||
// A = (x <associative-operator> A) <same-operator> y ==> A = A <associative-operator> (x <same-operator> y)
|
||||
val newRight = BinaryExpression(leftBinExpr.left, binExpr.operator, binExpr.right, binExpr.position)
|
||||
val newValue = BinaryExpression(leftBinExpr.right, binExpr.operator, newRight, binExpr.position)
|
||||
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
||||
}
|
||||
}
|
||||
val rightBinExpr = binExpr.right as? BinaryExpression
|
||||
if(rightBinExpr?.operator == binExpr.operator) {
|
||||
return if(rightBinExpr.left isSameAs assignment.target) {
|
||||
// A = x <associative-operator> (A <same-operator> y) ==> A = A <associative-operator> (x <same-operator> y)
|
||||
val newRight = BinaryExpression(binExpr.left, binExpr.operator, rightBinExpr.right, binExpr.position)
|
||||
val newValue = BinaryExpression(rightBinExpr.left, binExpr.operator, newRight, binExpr.position)
|
||||
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
||||
} else {
|
||||
// A = x <associative-operator> (y <same-operator> A) ==> A = A <associative-operator> (x <same-operator> y)
|
||||
val newRight = BinaryExpression(binExpr.left, binExpr.operator, rightBinExpr.left, binExpr.position)
|
||||
val newValue = BinaryExpression(rightBinExpr.right, binExpr.operator, newRight, binExpr.position)
|
||||
listOf(IAstModification.ReplaceNode(binExpr, newValue, assignment))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment, program: Program): List<Assignment> {
|
||||
val identifier = structAssignment.target.identifier!!
|
||||
val identifierName = identifier.nameInSource.single()
|
||||
@ -137,7 +165,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
||||
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
||||
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
|
||||
null, sourceValue, sourceValue.position)
|
||||
sourceValue, sourceValue.position)
|
||||
assign.linkParents(structAssignment)
|
||||
assign
|
||||
}
|
||||
@ -168,8 +196,7 @@ internal class StatementReorderer(val program: Program) : AstWalker() {
|
||||
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
||||
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
|
||||
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
|
||||
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
|
||||
null, sourceIdref, member.second.position)
|
||||
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position)
|
||||
assign.linkParents(structAssignment)
|
||||
assign
|
||||
}
|
||||
|
@ -18,6 +18,21 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
val declValue = decl.value
|
||||
if(decl.type==VarDeclType.VAR && declValue!=null && decl.struct==null) {
|
||||
val valueDt = declValue.inferType(program)
|
||||
if(!valueDt.istype(decl.datatype)) {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
declValue,
|
||||
TypecastExpression(declValue, decl.datatype, true, declValue.position),
|
||||
decl
|
||||
))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
val leftDt = expr.left.inferType(program)
|
||||
val rightDt = expr.right.inferType(program)
|
||||
@ -51,8 +66,13 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
|
||||
assignment))
|
||||
} else {
|
||||
fun castLiteral(cvalue: NumericLiteralValue): List<IAstModification.ReplaceNode> =
|
||||
listOf(IAstModification.ReplaceNode(cvalue, cvalue.cast(targettype), cvalue.parent))
|
||||
fun castLiteral(cvalue: NumericLiteralValue): List<IAstModification.ReplaceNode> {
|
||||
val cast = cvalue.cast(targettype)
|
||||
return if(cast.isValid)
|
||||
listOf(IAstModification.ReplaceNode(cvalue, cast.valueOrZero(), cvalue.parent))
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
val cvalue = assignment.value.constValue(program)
|
||||
if(cvalue!=null) {
|
||||
val number = cvalue.number.toDouble()
|
||||
@ -109,15 +129,12 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
||||
call as Node)
|
||||
} else if(arg.second.value is NumericLiteralValue) {
|
||||
try {
|
||||
val castedValue = (arg.second.value as NumericLiteralValue).cast(requiredType)
|
||||
val cast = (arg.second.value as NumericLiteralValue).cast(requiredType)
|
||||
if(cast.isValid)
|
||||
modifications += IAstModification.ReplaceNode(
|
||||
call.args[arg.second.index],
|
||||
castedValue,
|
||||
cast.valueOrZero(),
|
||||
call as Node)
|
||||
} catch (x: ExpressionError) {
|
||||
// no cast possible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -137,13 +154,13 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
call.args[arg.second.index],
|
||||
TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position),
|
||||
call as Node)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
null -> { }
|
||||
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
||||
else -> { }
|
||||
}
|
||||
|
||||
return modifications
|
||||
@ -152,7 +169,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
// warn about any implicit type casts to Float, because that may not be intended
|
||||
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||
errors.warn("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position)
|
||||
errors.warn("integer implicitly converted to float. Suggestion: use float literals, add an explicit cast, or revert to integer arithmetic", typecast.position)
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
@ -161,7 +178,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
// make sure the memory address is an uword
|
||||
val dt = memread.addressExpression.inferType(program)
|
||||
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||
val typecast = (memread.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)
|
||||
val typecast = (memread.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)?.valueOrZero()
|
||||
?: TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
||||
return listOf(IAstModification.ReplaceNode(memread.addressExpression, typecast, memread))
|
||||
}
|
||||
@ -172,7 +189,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
// make sure the memory address is an uword
|
||||
val dt = memwrite.addressExpression.inferType(program)
|
||||
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||
val typecast = (memwrite.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)
|
||||
val typecast = (memwrite.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)?.valueOrZero()
|
||||
?: TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
||||
return listOf(IAstModification.ReplaceNode(memwrite.addressExpression, typecast, memwrite))
|
||||
}
|
||||
@ -189,7 +206,9 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
if (returnValue.inferType(program).istype(subReturnType))
|
||||
return noModifications
|
||||
if (returnValue is NumericLiteralValue) {
|
||||
returnStmt.value = returnValue.cast(subroutine.returntypes.single())
|
||||
val cast = returnValue.cast(subroutine.returntypes.single())
|
||||
if(cast.isValid)
|
||||
returnStmt.value = cast.valueOrZero()
|
||||
} else {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
returnValue,
|
||||
|
@ -35,7 +35,8 @@ internal class VariousCleanups: AstWalker() {
|
||||
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(typecast.expression is NumericLiteralValue) {
|
||||
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value, parent))
|
||||
if(value.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
|
@ -13,30 +13,49 @@ import prog8.functions.BuiltinFunctions
|
||||
|
||||
class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
||||
|
||||
override fun visit(functionCall: FunctionCall)
|
||||
= checkTypes(functionCall as IFunctionCall, functionCall.definingScope())
|
||||
override fun visit(functionCall: FunctionCall) {
|
||||
val error = checkTypes(functionCall as IFunctionCall, functionCall.definingScope(), program)
|
||||
if(error!=null)
|
||||
throw CompilerException(error)
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement)
|
||||
= checkTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope())
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
val error = checkTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope(), program)
|
||||
if (error!=null)
|
||||
throw CompilerException(error)
|
||||
}
|
||||
|
||||
private fun checkTypes(call: IFunctionCall, scope: INameScope) {
|
||||
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
|
||||
val target = call.target.targetStatement(scope)
|
||||
when(target) {
|
||||
is Subroutine -> {
|
||||
companion object {
|
||||
fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? {
|
||||
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
|
||||
val target = call.target.targetStatement(scope)
|
||||
if (target is Subroutine) {
|
||||
// asmsub types are not checked specifically at this time
|
||||
if(call.args.size != target.parameters.size)
|
||||
return "invalid number of arguments"
|
||||
val paramtypes = target.parameters.map { it.type }
|
||||
if(argtypes!=paramtypes)
|
||||
throw CompilerException("parameter type mismatch $call")
|
||||
}
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
val paramtypes = func.parameters.map { it.possibleDatatypes }
|
||||
for(x in argtypes.zip(paramtypes)) {
|
||||
if(x.first !in x.second)
|
||||
throw CompilerException("parameter type mismatch $call")
|
||||
val mismatch = argtypes.zip(paramtypes).indexOfFirst { it.first != it.second}
|
||||
if(mismatch>=0) {
|
||||
val actual = argtypes[mismatch].toString()
|
||||
val expected = paramtypes[mismatch].toString()
|
||||
return "argument ${mismatch + 1} type mismatch, was: $actual expected: $expected"
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
else if (target is BuiltinFunctionStatementPlaceholder) {
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
if(call.args.size != func.parameters.size)
|
||||
return "invalid number of arguments"
|
||||
val paramtypes = func.parameters.map { it.possibleDatatypes }
|
||||
for (x in argtypes.zip(paramtypes).withIndex()) {
|
||||
if (x.value.first !in x.value.second) {
|
||||
val actual = x.value.first.toString()
|
||||
val expected = x.value.second.toString()
|
||||
return "argument ${x.index + 1} type mismatch, was: $actual expected: $expected"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,27 +139,6 @@ open class Return(var value: Expression?, override val position: Position) : Sta
|
||||
}
|
||||
}
|
||||
|
||||
class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "ReturnFromIrq(pos=$position)"
|
||||
}
|
||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||
}
|
||||
|
||||
class Continue(override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent=parent
|
||||
}
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
}
|
||||
|
||||
class Break(override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
@ -282,8 +261,8 @@ open class VarDecl(val type: VarDeclType,
|
||||
member.isArray,
|
||||
true,
|
||||
member.position
|
||||
) as Statement
|
||||
}.toMutableList()
|
||||
)
|
||||
}.toMutableList<Statement>()
|
||||
structHasBeenFlattened = true
|
||||
return result
|
||||
}
|
||||
@ -291,7 +270,7 @@ open class VarDecl(val type: VarDeclType,
|
||||
|
||||
// a vardecl used only for subroutine parameters
|
||||
class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Position)
|
||||
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.NOT_IN_ZEROPAGE, null, name, null, null, false, true, position)
|
||||
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position)
|
||||
|
||||
|
||||
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
||||
@ -321,10 +300,10 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
|
||||
return("ArrayIndex($index, pos=$position)")
|
||||
}
|
||||
|
||||
fun size() = (index as? NumericLiteralValue)?.number?.toInt()
|
||||
fun constIndex() = (index as? NumericLiteralValue)?.number?.toInt()
|
||||
}
|
||||
|
||||
open class Assignment(var target: AssignTarget, var aug_op : String?, var value: Expression, override val position: Position) : Statement() {
|
||||
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@ -346,31 +325,55 @@ open class Assignment(var target: AssignTarget, var aug_op : String?, var value:
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
|
||||
override fun toString(): String {
|
||||
return("Assignment(augop: $aug_op, target: $target, value: $value, pos=$position)")
|
||||
return("Assignment(target: $target, value: $value, pos=$position)")
|
||||
}
|
||||
|
||||
fun asDesugaredNonaugmented(): Assignment {
|
||||
val augmented = aug_op ?: return this
|
||||
/**
|
||||
* Is the assigment value an expression that references the assignment target itself?
|
||||
* The expression can be a BinaryExpression, PrefixExpression or TypecastExpression (possibly with one sub-cast).
|
||||
*/
|
||||
val isAugmentable: Boolean
|
||||
get() {
|
||||
val binExpr = value as? BinaryExpression
|
||||
if(binExpr!=null) {
|
||||
if(binExpr.left isSameAs target)
|
||||
return true // A = A <operator> Something
|
||||
|
||||
val leftOperand: Expression =
|
||||
when {
|
||||
target.identifier != null -> target.identifier!!
|
||||
target.arrayindexed != null -> target.arrayindexed!!
|
||||
target.memoryAddress != null -> DirectMemoryRead(target.memoryAddress!!.addressExpression, value.position)
|
||||
else -> throw FatalAstException("strange this")
|
||||
if(binExpr.operator in associativeOperators) {
|
||||
if (binExpr.left !is BinaryExpression && binExpr.right isSameAs target)
|
||||
return true // A = v <associative-operator> A
|
||||
|
||||
val leftBinExpr = binExpr.left as? BinaryExpression
|
||||
if(leftBinExpr?.operator == binExpr.operator) {
|
||||
// one of these?
|
||||
// A = (A <associative-operator> x) <same-operator> y
|
||||
// A = (x <associative-operator> A) <same-operator> y
|
||||
// A = (x <associative-operator> y) <same-operator> A
|
||||
return leftBinExpr.left isSameAs target || leftBinExpr.right isSameAs target || binExpr.right isSameAs target
|
||||
}
|
||||
val rightBinExpr = binExpr.right as? BinaryExpression
|
||||
if(rightBinExpr?.operator == binExpr.operator) {
|
||||
// one of these?
|
||||
// A = y <associative-operator> (A <same-operator> x)
|
||||
// A = y <associative-operator> (x <same-operator> y)
|
||||
// A = A <associative-operator> (x <same-operator> y)
|
||||
return rightBinExpr.left isSameAs target || rightBinExpr.right isSameAs target || binExpr.left isSameAs target
|
||||
}
|
||||
}
|
||||
|
||||
val assignment =
|
||||
if(augmented=="setvalue") {
|
||||
Assignment(target, null, value, position)
|
||||
} else {
|
||||
val expression = BinaryExpression(leftOperand, augmented.substringBeforeLast('='), value, position)
|
||||
Assignment(target, null, expression, position)
|
||||
}
|
||||
assignment.linkParents(parent)
|
||||
|
||||
return assignment
|
||||
}
|
||||
val prefixExpr = value as? PrefixExpression
|
||||
if(prefixExpr!=null)
|
||||
return prefixExpr.expression isSameAs target
|
||||
|
||||
val castExpr = value as? TypecastExpression
|
||||
if(castExpr!=null) {
|
||||
val subCast = castExpr.expression as? TypecastExpression
|
||||
return if(subCast!=null) subCast.expression isSameAs target else castExpr.expression isSameAs target
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
data class AssignTarget(var identifier: IdentifierReference?,
|
||||
@ -425,6 +428,15 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
||||
return InferredTypes.unknown()
|
||||
}
|
||||
|
||||
fun toExpression(): Expression {
|
||||
return when {
|
||||
identifier!=null -> identifier!!
|
||||
arrayindexed!=null -> arrayindexed!!
|
||||
memoryAddress!=null -> DirectMemoryRead(memoryAddress.addressExpression, memoryAddress.position)
|
||||
else -> throw FatalAstException("invalid assignmenttarget $this")
|
||||
}
|
||||
}
|
||||
|
||||
infix fun isSameAs(value: Expression): Boolean {
|
||||
return when {
|
||||
this.memoryAddress!=null -> {
|
||||
@ -437,9 +449,9 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
||||
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
|
||||
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
||||
value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource &&
|
||||
value.arrayspec.size()!=null &&
|
||||
arrayindexed!!.arrayspec.size()!=null &&
|
||||
value.arrayspec.size()==arrayindexed!!.arrayspec.size()
|
||||
value.arrayspec.constIndex()!=null &&
|
||||
arrayindexed!!.arrayspec.constIndex()!=null &&
|
||||
value.arrayspec.constIndex()==arrayindexed!!.arrayspec.constIndex()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@ -621,7 +633,6 @@ class Subroutine(override val name: String,
|
||||
override var statements: MutableList<Statement>,
|
||||
override val position: Position) : Statement(), INameScope {
|
||||
|
||||
var keepAlways: Boolean = false
|
||||
override lateinit var parent: Node
|
||||
val scopedname: String by lazy { makeScopedName(name) }
|
||||
|
||||
@ -646,6 +657,7 @@ class Subroutine(override val name: String,
|
||||
}
|
||||
|
||||
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||
|
||||
fun amountOfRtsInAsm(): Int = statements
|
||||
.asSequence()
|
||||
|
@ -1,5 +1,6 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
@ -14,13 +15,32 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if (decl.value == null && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||
// a numeric vardecl without an initial value is initialized with zero.
|
||||
decl.value = decl.zeroElementValue()
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
// Try to replace A = B <operator> Something by A= B, A = A <operator> Something
|
||||
// this triggers the more efficent augmented assignment code generation more often.
|
||||
if(!assignment.isAugmentable
|
||||
&& assignment.target.identifier != null
|
||||
&& assignment.target.isNotMemory(program.namespace)) {
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if(binExpr!=null && binExpr.operator !in comparisonOperators) {
|
||||
if(binExpr.left !is BinaryExpression) {
|
||||
val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position)
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(assignment, assignLeft, parent),
|
||||
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||
val decls = scope.statements.filterIsInstance<VarDecl>()
|
||||
val sub = scope.definingSubroutine()
|
||||
@ -35,12 +55,13 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
||||
}
|
||||
}
|
||||
if (!conflicts) {
|
||||
val numericVarsWithValue = decls.filter { it.value != null && it.datatype in NumericDatatypes }
|
||||
// move vardecls of the scope into the upper scope. Make sure the order remains the same!
|
||||
val numericVarsWithValue = decls.filter { it.value != null && it.datatype in NumericDatatypes }.reversed()
|
||||
return numericVarsWithValue.map {
|
||||
val initValue = it.value!! // assume here that value has always been set by now
|
||||
it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous)
|
||||
val target = AssignTarget(IdentifierReference(listOf(it.name), it.position), null, null, it.position)
|
||||
val assign = Assignment(target, null, initValue, it.position)
|
||||
val assign = Assignment(target, initValue, it.position)
|
||||
initValue.parent = assign
|
||||
IAstModification.InsertFirst(assign, scope)
|
||||
} + decls.map { IAstModification.ReplaceNode(it, NopStatement(it.position), scope) } +
|
||||
@ -89,7 +110,26 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
}
|
||||
}
|
||||
else if(sourceDt in PassByReferenceDatatypes) {
|
||||
|
||||
|
||||
// Note: for various reasons (most importantly, code simplicity), the code generator assumes/requires
|
||||
// that the types of assignment values and their target are the same,
|
||||
// and that the types of both operands of a binaryexpression node are the same.
|
||||
// So, it is not easily possible to remove the typecasts that are there to make these conditions true.
|
||||
// The only place for now where we can do this is for:
|
||||
// asmsub register pair parameter.
|
||||
|
||||
if(typecast.type in WordDatatypes) {
|
||||
val fcall = typecast.parent as? IFunctionCall
|
||||
if (fcall != null) {
|
||||
val sub = fcall.target.targetStatement(program.namespace) as? Subroutine
|
||||
if (sub != null && sub.isAsmSubroutine) {
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sourceDt in PassByReferenceDatatypes) {
|
||||
if(typecast.type==DataType.UWORD) {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
|
@ -27,7 +27,8 @@ data class CompilationOptions(val output: OutputType,
|
||||
val launcher: LauncherType,
|
||||
val zeropage: ZeropageType,
|
||||
val zpReserved: List<IntRange>,
|
||||
val floats: Boolean)
|
||||
val floats: Boolean,
|
||||
val compilationTarget: String?)
|
||||
|
||||
|
||||
class CompilerException(message: String?) : Exception(message)
|
||||
|
@ -4,7 +4,9 @@ import prog8.ast.AstToSourceCode
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8.compiler.target.CompilationTarget
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.optimizer.UnusedCodeRemover
|
||||
import prog8.optimizer.constantFold
|
||||
import prog8.optimizer.optimizeStatements
|
||||
@ -13,6 +15,7 @@ import prog8.parser.ModuleImporter
|
||||
import prog8.parser.ParsingFailedError
|
||||
import prog8.parser.moduleName
|
||||
import java.nio.file.Path
|
||||
import kotlin.system.exitProcess
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
|
||||
@ -25,12 +28,22 @@ class CompilationResult(val success: Boolean,
|
||||
fun compileProgram(filepath: Path,
|
||||
optimize: Boolean,
|
||||
writeAssembly: Boolean,
|
||||
compilationTarget: String,
|
||||
outputDir: Path): CompilationResult {
|
||||
var programName = ""
|
||||
lateinit var programAst: Program
|
||||
lateinit var importedFiles: List<Path>
|
||||
val errors = ErrorReporter()
|
||||
|
||||
when(compilationTarget) {
|
||||
C64Target.name -> CompilationTarget.instance = C64Target
|
||||
Cx16Target.name -> CompilationTarget.instance = Cx16Target
|
||||
else -> {
|
||||
System.err.println("invalid compilation target")
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
val totalTime = measureTimeMillis {
|
||||
// import main module and everything it needs
|
||||
@ -79,7 +92,7 @@ fun compileProgram(filepath: Path,
|
||||
|
||||
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
|
||||
println("Parsing...")
|
||||
val importer = ModuleImporter(errors)
|
||||
val importer = ModuleImporter()
|
||||
val programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
||||
importer.importModule(programAst, filepath)
|
||||
errors.handle()
|
||||
@ -90,11 +103,8 @@ private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program,
|
||||
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
||||
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
|
||||
|
||||
// if we're producing a PRG or BASIC program, include the c64utils and c64lib libraries
|
||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) {
|
||||
importer.importLibraryModule(programAst, "c64lib")
|
||||
importer.importLibraryModule(programAst, "c64utils")
|
||||
}
|
||||
// depending on the machine and compiler options we may have to include some libraries
|
||||
CompilationTarget.instance.machine.importLibs(compilerOptions, importer, programAst)
|
||||
|
||||
// always import prog8lib and math
|
||||
importer.importLibraryModule(programAst, "math")
|
||||
@ -132,16 +142,26 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
||||
.map { it[0].int!!..it[1].int!! }
|
||||
.toList()
|
||||
|
||||
var target = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%target" }
|
||||
as? Directive)?.args?.single()?.name
|
||||
|
||||
when(target) {
|
||||
C64Target.name -> CompilationTarget.instance = C64Target
|
||||
Cx16Target.name -> CompilationTarget.instance = Cx16Target
|
||||
null -> target = CompilationTarget.instance.name
|
||||
else -> throw FatalAstException("invalid target")
|
||||
}
|
||||
|
||||
return CompilationOptions(
|
||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||
zpType, zpReserved, floatsEnabled
|
||||
zpType, zpReserved, floatsEnabled, target
|
||||
)
|
||||
}
|
||||
|
||||
private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
|
||||
// perform initial syntax checks and processings
|
||||
println("Processing...")
|
||||
println("Processing for target ${CompilationTarget.instance.name}...")
|
||||
programAst.checkIdentifiers(errors)
|
||||
errors.handle()
|
||||
programAst.constantFold(errors)
|
||||
@ -169,14 +189,13 @@ private fun optimizeAst(programAst: Program, errors: ErrorReporter) {
|
||||
break
|
||||
}
|
||||
|
||||
val remover = UnusedCodeRemover()
|
||||
val remover = UnusedCodeRemover(errors)
|
||||
remover.visit(programAst)
|
||||
remover.applyModifications()
|
||||
errors.handle()
|
||||
}
|
||||
|
||||
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
|
||||
programAst.transformAssignments(errors)
|
||||
errors.handle()
|
||||
programAst.addTypecasts(errors)
|
||||
errors.handle()
|
||||
programAst.variousCleanups()
|
||||
@ -190,16 +209,19 @@ private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerO
|
||||
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
||||
optimize: Boolean, compilerOptions: CompilationOptions): String {
|
||||
// asm generation directly from the Ast,
|
||||
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
|
||||
programAst.processAstBeforeAsmGeneration(errors)
|
||||
errors.handle()
|
||||
|
||||
// printAst(programAst)
|
||||
|
||||
val assembly = CompilationTarget.asmGenerator(
|
||||
if(compilerOptions.compilationTarget!=null && compilerOptions.compilationTarget != CompilationTarget.instance.name)
|
||||
throw AssemblyError("program's compilation target differs from configured target")
|
||||
|
||||
CompilationTarget.instance.machine.initializeZeropage(compilerOptions)
|
||||
val assembly = CompilationTarget.instance.asmGenerator(
|
||||
programAst,
|
||||
errors,
|
||||
zeropage,
|
||||
CompilationTarget.instance.machine.zeropage,
|
||||
compilerOptions,
|
||||
outputDir).compileToAssembly(optimize)
|
||||
assembly.assemble(compilerOptions)
|
||||
|
@ -8,6 +8,12 @@ class ZeropageDepletedError(message: String) : Exception(message)
|
||||
|
||||
abstract class Zeropage(protected val options: CompilationOptions) {
|
||||
|
||||
abstract val SCRATCH_B1 : Int // temp storage for a single byte
|
||||
abstract val SCRATCH_REG : Int // temp storage for a register
|
||||
abstract val SCRATCH_W1 : Int // temp storage 1 for a word $fb+$fc
|
||||
abstract val SCRATCH_W2 : Int // temp storage 2 for a word $fb+$fc
|
||||
|
||||
|
||||
private val allocations = mutableMapOf<Int, Pair<String, DataType>>()
|
||||
val free = mutableListOf<Int>() // subclasses must set this to the appropriate free locations.
|
||||
|
||||
@ -16,7 +22,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
||||
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
||||
|
||||
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: ErrorReporter): Int {
|
||||
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"isSameAs scopedname can't be allocated twice"}
|
||||
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"}
|
||||
|
||||
if(options.zeropage==ZeropageType.DONTUSE)
|
||||
throw CompilerException("zero page usage has been disabled")
|
||||
@ -39,13 +45,13 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
||||
|
||||
if(free.size > 0) {
|
||||
if(size==1) {
|
||||
for(candidate in free.min()!! .. free.max()!!+1) {
|
||||
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1) {
|
||||
if(loneByte(candidate))
|
||||
return makeAllocation(candidate, 1, datatype, scopedname)
|
||||
}
|
||||
return makeAllocation(free[0], 1, datatype, scopedname)
|
||||
}
|
||||
for(candidate in free.min()!! .. free.max()!!+1) {
|
||||
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1) {
|
||||
if (sequentialFree(candidate, size))
|
||||
return makeAllocation(candidate, size, datatype, scopedname)
|
||||
}
|
||||
|
@ -4,15 +4,50 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.ErrorReporter
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.Zeropage
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import prog8.compiler.target.c64.codegen.AsmGen
|
||||
import prog8.compiler.target.cx16.CX16MachineDefinition
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
internal interface CompilationTarget {
|
||||
val name: String
|
||||
val machine: IMachineDefinition
|
||||
fun encodeString(str: String, altEncoding: Boolean): List<Short>
|
||||
fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
|
||||
fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path): IAssemblyGenerator
|
||||
val asmForSystemReset: String
|
||||
val initProcName: String?
|
||||
|
||||
companion object {
|
||||
lateinit var name: String
|
||||
lateinit var machine: IMachineDefinition
|
||||
lateinit var encodeString: (str: String, altEncoding: Boolean) -> List<Short>
|
||||
lateinit var decodeString: (bytes: List<Short>, altEncoding: Boolean) -> String
|
||||
lateinit var asmGenerator: (Program, ErrorReporter, Zeropage, CompilationOptions, Path) -> IAssemblyGenerator
|
||||
lateinit var instance: CompilationTarget
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal object C64Target: CompilationTarget {
|
||||
override val name = "c64"
|
||||
override val machine = C64MachineDefinition
|
||||
override fun encodeString(str: String, altEncoding: Boolean) =
|
||||
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
|
||||
AsmGen(program, errors, zp, options, path)
|
||||
override val asmForSystemReset = " sei | lda #14 | sta $1 | jmp (c64.RESET_VEC)"
|
||||
override val initProcName = "c64.init_system"
|
||||
}
|
||||
|
||||
internal object Cx16Target: CompilationTarget {
|
||||
override val name = "cx16"
|
||||
override val machine = CX16MachineDefinition
|
||||
override fun encodeString(str: String, altEncoding: Boolean) =
|
||||
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||
if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||
override fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path) =
|
||||
AsmGen(program, errors, zp, options, path)
|
||||
override val asmForSystemReset = " sei | stz cx16.d1prb | jmp (cx16.RESET_VEC)"
|
||||
override val initProcName = "cx16.init_system"
|
||||
}
|
||||
|
@ -1,15 +1,38 @@
|
||||
package prog8.compiler.target
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.Zeropage
|
||||
import prog8.parser.ModuleImporter
|
||||
|
||||
|
||||
interface IMachineDefinition {
|
||||
internal interface IMachineFloat {
|
||||
fun toDouble(): Double
|
||||
fun makeFloatFillAsm(): String
|
||||
}
|
||||
|
||||
internal enum class CpuType {
|
||||
CPU6502,
|
||||
CPU65c02
|
||||
}
|
||||
|
||||
internal interface IMachineDefinition {
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
val POINTER_MEM_SIZE: Int
|
||||
val ESTACK_LO: Int
|
||||
val ESTACK_HI: Int
|
||||
val BASIC_LOAD_ADDRESS : Int
|
||||
val RAW_LOAD_ADDRESS : Int
|
||||
|
||||
val opcodeNames: Set<String>
|
||||
var zeropage: Zeropage
|
||||
val cpu: CpuType
|
||||
|
||||
fun getZeropage(compilerOptions: CompilationOptions): Zeropage
|
||||
fun initializeZeropage(compilerOptions: CompilationOptions)
|
||||
fun getFloat(num: Number): IMachineFloat
|
||||
fun getFloatRomConst(number: Double): String?
|
||||
fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program)
|
||||
fun launchEmulator(programName: String)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package prog8.compiler.target.c64
|
||||
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.OutputType
|
||||
import prog8.compiler.target.CompilationTarget
|
||||
import prog8.compiler.target.IAssemblyProgram
|
||||
import prog8.compiler.target.generatedLabelPrefix
|
||||
import java.nio.file.Path
|
||||
@ -14,20 +15,20 @@ class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyPro
|
||||
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
|
||||
|
||||
override fun assemble(options: CompilationOptions) {
|
||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps
|
||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", "-Werror", "-Wno-error=long-branch",
|
||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
||||
|
||||
val outFile = when (options.output) {
|
||||
OutputType.PRG -> {
|
||||
command.add("--cbm-prg")
|
||||
println("\nCreating C-64 prg.")
|
||||
println("\nCreating prg for target ${CompilationTarget.instance.name}.")
|
||||
prgFile
|
||||
}
|
||||
OutputType.RAW -> {
|
||||
command.add("--nostart")
|
||||
println("\nCreating raw binary.")
|
||||
println("\nCreating raw binary for target ${CompilationTarget.instance.name}.")
|
||||
binFile
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,96 @@
|
||||
package prog8.compiler.target.c64
|
||||
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.CompilerException
|
||||
import prog8.compiler.Zeropage
|
||||
import prog8.compiler.ZeropageType
|
||||
import prog8.ast.Program
|
||||
import prog8.compiler.*
|
||||
import prog8.compiler.target.CpuType
|
||||
import prog8.compiler.target.IMachineDefinition
|
||||
import prog8.compiler.target.IMachineFloat
|
||||
import prog8.parser.ModuleImporter
|
||||
import java.io.IOException
|
||||
import java.math.RoundingMode
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.pow
|
||||
|
||||
object C64MachineDefinition: IMachineDefinition {
|
||||
internal object C64MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU6502
|
||||
|
||||
// 5-byte cbm MFLPT format limitations:
|
||||
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||
override val FLOAT_MEM_SIZE = 5
|
||||
const val BASIC_LOAD_ADDRESS = 0x0801
|
||||
const val RAW_LOAD_ADDRESS = 0xc000
|
||||
override val POINTER_MEM_SIZE = 2
|
||||
override val BASIC_LOAD_ADDRESS = 0x0801
|
||||
override val RAW_LOAD_ADDRESS = 0xc000
|
||||
|
||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||
// and some heavily used string constants derived from the two values above
|
||||
const val ESTACK_LO_VALUE = 0xce00 // $ce00-$ceff inclusive
|
||||
const val ESTACK_HI_VALUE = 0xcf00 // $cf00-$cfff inclusive
|
||||
const val ESTACK_LO_HEX = "\$ce00"
|
||||
const val ESTACK_LO_PLUS1_HEX = "\$ce01"
|
||||
const val ESTACK_LO_PLUS2_HEX = "\$ce02"
|
||||
const val ESTACK_HI_HEX = "\$cf00"
|
||||
const val ESTACK_HI_PLUS1_HEX = "\$cf01"
|
||||
const val ESTACK_HI_PLUS2_HEX = "\$cf02"
|
||||
override val ESTACK_LO = 0xce00 // $ce00-$ceff inclusive
|
||||
override val ESTACK_HI = 0xcf00 // $ce00-$ceff inclusive
|
||||
|
||||
override fun getZeropage(compilerOptions: CompilationOptions) = C64Zeropage(compilerOptions)
|
||||
override lateinit var zeropage: Zeropage
|
||||
|
||||
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
||||
|
||||
override fun getFloatRomConst(number: Double): String? {
|
||||
// try to match the ROM float constants to save memory
|
||||
val mflpt5 = Mflpt5.fromNumber(number)
|
||||
val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
|
||||
when {
|
||||
floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.ZERO" // not a ROM const
|
||||
floatbytes.contentEquals(shortArrayOf(0x82, 0x49, 0x0f, 0xda, 0xa1)) -> return "c64flt.FL_PIVAL"
|
||||
floatbytes.contentEquals(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_N32768"
|
||||
floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FONE"
|
||||
floatbytes.contentEquals(shortArrayOf(0x80, 0x35, 0x04, 0xf3, 0x34)) -> return "c64flt.FL_SQRHLF"
|
||||
floatbytes.contentEquals(shortArrayOf(0x81, 0x35, 0x04, 0xf3, 0x34)) -> return "c64flt.FL_SQRTWO"
|
||||
floatbytes.contentEquals(shortArrayOf(0x80, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_NEGHLF"
|
||||
floatbytes.contentEquals(shortArrayOf(0x80, 0x31, 0x72, 0x17, 0xf8)) -> return "c64flt.FL_LOG2"
|
||||
floatbytes.contentEquals(shortArrayOf(0x84, 0x20, 0x00, 0x00, 0x00)) -> return "c64flt.FL_TENC"
|
||||
floatbytes.contentEquals(shortArrayOf(0x9e, 0x6e, 0x6b, 0x28, 0x00)) -> return "c64flt.FL_NZMIL"
|
||||
floatbytes.contentEquals(shortArrayOf(0x80, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FHALF"
|
||||
floatbytes.contentEquals(shortArrayOf(0x81, 0x38, 0xaa, 0x3b, 0x29)) -> return "c64flt.FL_LOGEB2"
|
||||
floatbytes.contentEquals(shortArrayOf(0x81, 0x49, 0x0f, 0xda, 0xa2)) -> return "c64flt.FL_PIHALF"
|
||||
floatbytes.contentEquals(shortArrayOf(0x83, 0x49, 0x0f, 0xda, 0xa2)) -> return "c64flt.FL_TWOPI"
|
||||
floatbytes.contentEquals(shortArrayOf(0x7f, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FR4"
|
||||
else -> {
|
||||
// attempt to correct for a few rounding issues
|
||||
when (number.toBigDecimal().setScale(10, RoundingMode.HALF_DOWN).toDouble()) {
|
||||
3.1415926536 -> return "c64flt.FL_PIVAL"
|
||||
1.4142135624 -> return "c64flt.FL_SQRTWO"
|
||||
0.7071067812 -> return "c64flt.FL_SQRHLF"
|
||||
0.6931471806 -> return "c64flt.FL_LOG2"
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
|
||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
importer.importLibraryModule(program, "c64lib")
|
||||
}
|
||||
|
||||
override fun launchEmulator(programName: String) {
|
||||
for(emulator in listOf("x64sc", "x64")) {
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-silent", "-moncommands", "$programName.vice-mon-list",
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", programName + ".prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
} catch(x: IOException) {
|
||||
continue // try the next emulator executable
|
||||
}
|
||||
process.waitFor()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||
zeropage = C64Zeropage(compilerOptions)
|
||||
}
|
||||
|
||||
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
||||
override val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
|
||||
@ -42,15 +104,13 @@ object C64MachineDefinition: IMachineDefinition {
|
||||
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
||||
|
||||
|
||||
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
internal class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0x02 // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0x03 // temp storage for a register
|
||||
override val SCRATCH_W1 = 0xfb // temp storage 1 for a word $fb+$fc
|
||||
override val SCRATCH_W2 = 0xfd // temp storage 2 for a word $fb+$fc
|
||||
|
||||
companion object {
|
||||
const val SCRATCH_B1 = 0x02
|
||||
const val SCRATCH_REG = 0x03 // temp storage for a register
|
||||
const val SCRATCH_REG_X = 0xfa // temp storage for register X (the evaluation stack pointer)
|
||||
const val SCRATCH_W1 = 0xfb // $fb+$fc
|
||||
const val SCRATCH_W2 = 0xfd // $fd+$fe
|
||||
}
|
||||
|
||||
override val exitProgramStrategy: ExitProgramStrategy = when (options.zeropage) {
|
||||
ZeropageType.BASICSAFE, ZeropageType.DONTUSE -> ExitProgramStrategy.CLEAN_EXIT
|
||||
@ -65,7 +125,7 @@ object C64MachineDefinition: IMachineDefinition {
|
||||
if (options.zeropage == ZeropageType.FULL) {
|
||||
free.addAll(0x04..0xf9)
|
||||
free.add(0xff)
|
||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_REG_X, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
||||
} else {
|
||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||
@ -106,19 +166,17 @@ object C64MachineDefinition: IMachineDefinition {
|
||||
free.clear()
|
||||
}
|
||||
}
|
||||
assert(SCRATCH_B1 !in free)
|
||||
assert(SCRATCH_REG !in free)
|
||||
assert(SCRATCH_REG_X !in free)
|
||||
assert(SCRATCH_W1 !in free)
|
||||
assert(SCRATCH_W2 !in free)
|
||||
require(SCRATCH_B1 !in free)
|
||||
require(SCRATCH_REG !in free)
|
||||
require(SCRATCH_W1 !in free)
|
||||
require(SCRATCH_W2 !in free)
|
||||
|
||||
for (reserved in options.zpReserved)
|
||||
reserve(reserved)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short) {
|
||||
internal data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short): IMachineFloat {
|
||||
|
||||
companion object {
|
||||
val zero = Mflpt5(0, 0, 0, 0, 0)
|
||||
@ -165,7 +223,7 @@ object C64MachineDefinition: IMachineDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
fun toDouble(): Double {
|
||||
override fun toDouble(): Double {
|
||||
if (this == zero) return 0.0
|
||||
val exp = b0 - 128
|
||||
val sign = (b1.toInt() and 0x80) > 0
|
||||
@ -173,5 +231,14 @@ object C64MachineDefinition: IMachineDefinition {
|
||||
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
||||
return if (sign) -result else result
|
||||
}
|
||||
|
||||
override fun makeFloatFillAsm(): String {
|
||||
val b0 = "$" + b0.toString(16).padStart(2, '0')
|
||||
val b1 = "$" + b1.toString(16).padStart(2, '0')
|
||||
val b2 = "$" + b2.toString(16).padStart(2, '0')
|
||||
val b3 = "$" + b3.toString(16).padStart(2, '0')
|
||||
val b4 = "$" + b4.toString(16).padStart(2, '0')
|
||||
return "$b0, $b1, $b2, $b3, $b4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,5 @@
|
||||
package prog8.compiler.target.c64.codegen
|
||||
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||
|
||||
|
||||
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations
|
||||
|
||||
@ -87,10 +84,10 @@ private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>):
|
||||
// the repeated lda can be removed
|
||||
val mods = mutableListOf<Modification>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="lda $ESTACK_LO_PLUS1_HEX,x" &&
|
||||
if(lines[0].value.trim()=="lda P8ESTACK_LO+1,x" &&
|
||||
lines[1].value.trim().startsWith("cmp ") &&
|
||||
lines[2].value.trim().startsWith("beq ") &&
|
||||
lines[3].value.trim()=="lda $ESTACK_LO_PLUS1_HEX,x") {
|
||||
lines[3].value.trim()=="lda P8ESTACK_LO+1,x") {
|
||||
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
||||
}
|
||||
}
|
||||
@ -102,10 +99,10 @@ private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<S
|
||||
// this is a lot harder for word values because the instruction sequence varies.
|
||||
val mods = mutableListOf<Modification>()
|
||||
for(lines in linesByFour) {
|
||||
if(lines[0].value.trim()=="sta $ESTACK_LO_HEX,x" &&
|
||||
if(lines[0].value.trim()=="sta P8ESTACK_LO,x" &&
|
||||
lines[1].value.trim()=="dex" &&
|
||||
lines[2].value.trim()=="inx" &&
|
||||
lines[3].value.trim()=="lda $ESTACK_LO_HEX,x") {
|
||||
lines[3].value.trim()=="lda P8ESTACK_LO,x") {
|
||||
mods.add(Modification(lines[1].index, true, null))
|
||||
mods.add(Modification(lines[2].index, true, null))
|
||||
mods.add(Modification(lines[3].index, true, null))
|
||||
@ -154,7 +151,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
||||
}
|
||||
|
||||
if(first.startsWith("lda") && second.startsWith("ldy") && third.startsWith("sta") && fourth.startsWith("sty") &&
|
||||
fifth.startsWith("lda") && sixth.startsWith("ldy") && seventh.startsWith("jsr c64flt.copy_float")) {
|
||||
fifth.startsWith("lda") && sixth.startsWith("ldy") &&
|
||||
(seventh.startsWith("jsr c64flt.copy_float") || seventh.startsWith("jsr cx16flt.copy_float"))) {
|
||||
|
||||
val nineth = pair[8].value.trimStart()
|
||||
val tenth = pair[9].value.trimStart()
|
||||
@ -164,7 +162,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
||||
val fourteenth = pair[13].value.trimStart()
|
||||
|
||||
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
||||
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") && fourteenth.startsWith("jsr c64flt.copy_float")) {
|
||||
twelveth.startsWith("lda") && thirteenth.startsWith("ldy") &&
|
||||
(fourteenth.startsWith("jsr c64flt.copy_float") || fourteenth.startsWith("jsr cx16flt.copy_float"))) {
|
||||
|
||||
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
||||
// identical float init
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,11 +6,6 @@ import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||
import prog8.compiler.toHex
|
||||
import prog8.functions.FSignature
|
||||
|
||||
@ -35,6 +30,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
|
||||
when (functionName) {
|
||||
"msb" -> funcMsb(fcall)
|
||||
"lsb" -> funcLsb(fcall)
|
||||
"mkword" -> funcMkword(fcall, func)
|
||||
"abs" -> funcAbs(fcall, func)
|
||||
"swap" -> funcSwap(fcall)
|
||||
@ -46,8 +42,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
"ln", "log2", "sqrt", "rad",
|
||||
"deg", "round", "floor", "ceil",
|
||||
"rdnf" -> funcVariousFloatFuncs(fcall, func, functionName)
|
||||
"lsl" -> funcLsl(fcall)
|
||||
"lsr" -> funcLsr(fcall)
|
||||
"rol" -> funcRol(fcall)
|
||||
"rol2" -> funcRol2(fcall)
|
||||
"ror" -> funcRor(fcall)
|
||||
@ -57,7 +51,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
"rsave" -> {
|
||||
// save cpu status flag and all registers A, X, Y.
|
||||
// see http://6502.org/tutorials/register_preservation.html
|
||||
asmgen.out(" php | sta ${C64Zeropage.SCRATCH_REG} | pha | txa | pha | tya | pha | lda ${C64Zeropage.SCRATCH_REG}")
|
||||
asmgen.out(" php | sta P8ZP_SCRATCH_REG | pha | txa | pha | tya | pha | lda P8ZP_SCRATCH_REG")
|
||||
}
|
||||
"rrestore" -> {
|
||||
// restore all registers and cpu status flag
|
||||
@ -78,15 +72,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
val variable = fcall.args.single()
|
||||
if (variable is IdentifierReference) {
|
||||
val decl = variable.targetVarDecl(program.namespace)!!
|
||||
val varName = asmgen.asmIdentifierName(variable)
|
||||
val numElements = decl.arraysize!!.size()
|
||||
val varName = asmgen.asmVariableName(variable)
|
||||
val numElements = decl.arraysize!!.constIndex()
|
||||
when (decl.datatype) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.reverse_b
|
||||
""")
|
||||
@ -95,8 +89,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.reverse_w
|
||||
""")
|
||||
@ -105,8 +99,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
jsr prog8_lib.reverse_f
|
||||
""")
|
||||
@ -120,17 +114,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
val variable = fcall.args.single()
|
||||
if (variable is IdentifierReference) {
|
||||
val decl = variable.targetVarDecl(program.namespace)!!
|
||||
val varName = asmgen.asmIdentifierName(variable)
|
||||
val numElements = decl.arraysize!!.size()
|
||||
val varName = asmgen.asmVariableName(variable)
|
||||
val numElements = decl.arraysize!!.constIndex()
|
||||
when (decl.datatype) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
sta ${C64Zeropage.SCRATCH_B1}
|
||||
sta P8ZP_SCRATCH_B1
|
||||
""")
|
||||
asmgen.out(if (decl.datatype == DataType.ARRAY_UB) " jsr prog8_lib.sort_ub" else " jsr prog8_lib.sort_b")
|
||||
}
|
||||
@ -138,10 +132,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.out("""
|
||||
lda #<$varName
|
||||
ldy #>$varName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #$numElements
|
||||
sta ${C64Zeropage.SCRATCH_B1}
|
||||
sta P8ZP_SCRATCH_B1
|
||||
""")
|
||||
asmgen.out(if (decl.datatype == DataType.ARRAY_UW) " jsr prog8_lib.sort_uw" else " jsr prog8_lib.sort_w")
|
||||
}
|
||||
@ -173,7 +167,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -187,7 +181,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -216,16 +210,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.translateExpression(what.addressExpression)
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda $ESTACK_LO_HEX,x
|
||||
lda P8ESTACK_LO,x
|
||||
sta (+) + 1
|
||||
lda $ESTACK_HI_HEX,x
|
||||
lda P8ESTACK_HI,x
|
||||
sta (+) + 2
|
||||
+ ror ${'$'}ffff ; modified
|
||||
""")
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" ror $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -239,7 +233,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" ror $variable+1 | ror $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -270,7 +264,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -284,7 +278,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -313,16 +307,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.translateExpression(what.addressExpression)
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda $ESTACK_LO_HEX,x
|
||||
lda P8ESTACK_LO,x
|
||||
sta (+) + 1
|
||||
lda $ESTACK_HI_HEX,x
|
||||
lda P8ESTACK_HI,x
|
||||
sta (+) + 2
|
||||
+ rol ${'$'}ffff ; modified
|
||||
""")
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" rol $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -336,7 +330,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
val variable = asmgen.asmVariableName(what)
|
||||
asmgen.out(" rol $variable | rol $variable+1")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -346,132 +340,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcLsr(fcall: IFunctionCall) {
|
||||
val what = fcall.args.single()
|
||||
val dt = what.inferType(program)
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
|
||||
is DirectMemoryRead -> {
|
||||
if (what.addressExpression is NumericLiteralValue) {
|
||||
val number = (what.addressExpression as NumericLiteralValue).number
|
||||
asmgen.out(" lsr ${number.toHex()}")
|
||||
} else {
|
||||
asmgen.translateExpression(what.addressExpression)
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda $ESTACK_LO_HEX,x
|
||||
sta (+) + 1
|
||||
lda $ESTACK_HI_HEX,x
|
||||
sta (+) + 2
|
||||
+ lsr ${'$'}ffff ; modified
|
||||
""")
|
||||
}
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
asmgen.translateExpression(what.identifier)
|
||||
asmgen.translateExpression(what.arrayspec.index)
|
||||
asmgen.out(" jsr prog8_lib.lsr_array_ub")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when (what) {
|
||||
is ArrayIndexedExpression -> {
|
||||
asmgen.translateExpression(what.identifier)
|
||||
asmgen.translateExpression(what.arrayspec.index)
|
||||
asmgen.out(" jsr prog8_lib.lsr_array_b")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
asmgen.out(" lda $variable | asl a | ror $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when (what) {
|
||||
is ArrayIndexedExpression -> {
|
||||
asmgen.translateExpression(what.identifier)
|
||||
asmgen.translateExpression(what.arrayspec.index)
|
||||
asmgen.out(" jsr prog8_lib.lsr_array_uw")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
asmgen.out(" lsr $variable+1 | ror $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when (what) {
|
||||
is ArrayIndexedExpression -> {
|
||||
asmgen.translateExpression(what.identifier)
|
||||
asmgen.translateExpression(what.arrayspec.index)
|
||||
asmgen.out(" jsr prog8_lib.lsr_array_w")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
asmgen.out(" lda $variable+1 | asl a | ror $variable+1 | ror $variable")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcLsl(fcall: IFunctionCall) {
|
||||
val what = fcall.args.single()
|
||||
val dt = what.inferType(program)
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
in ByteDatatypes -> {
|
||||
when (what) {
|
||||
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
|
||||
is DirectMemoryRead -> {
|
||||
if (what.addressExpression is NumericLiteralValue) {
|
||||
val number = (what.addressExpression as NumericLiteralValue).number
|
||||
asmgen.out(" asl ${number.toHex()}")
|
||||
} else {
|
||||
asmgen.translateExpression(what.addressExpression)
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda $ESTACK_LO_HEX,x
|
||||
sta (+) + 1
|
||||
lda $ESTACK_HI_HEX,x
|
||||
sta (+) + 2
|
||||
+ asl ${'$'}ffff ; modified
|
||||
""")
|
||||
}
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
asmgen.translateExpression(what.identifier)
|
||||
asmgen.translateExpression(what.arrayspec.index)
|
||||
asmgen.out(" jsr prog8_lib.lsl_array_b")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
when (what) {
|
||||
is ArrayIndexedExpression -> {
|
||||
asmgen.translateExpression(what.identifier)
|
||||
asmgen.translateExpression(what.arrayspec.index)
|
||||
asmgen.out(" jsr prog8_lib.lsl_array_w")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val variable = asmgen.asmIdentifierName(what)
|
||||
asmgen.out(" asl $variable | rol $variable+1")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, functionName: String) {
|
||||
translateFunctionArguments(fcall.args, func)
|
||||
asmgen.out(" jsr c64flt.func_$functionName")
|
||||
@ -515,16 +383,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
|
||||
private fun funcStrlen(fcall: IFunctionCall) {
|
||||
outputPushAddressOfIdentifier(fcall.args[0])
|
||||
asmgen.out(" jsr prog8_lib.func_strlen")
|
||||
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
||||
asmgen.out("""
|
||||
lda #<$name
|
||||
ldy #>$name
|
||||
jsr prog8_lib.strlen
|
||||
sta P8ESTACK_LO,x
|
||||
dex""")
|
||||
}
|
||||
|
||||
private fun funcSwap(fcall: IFunctionCall) {
|
||||
val first = fcall.args[0]
|
||||
val second = fcall.args[1]
|
||||
if(first is IdentifierReference && second is IdentifierReference) {
|
||||
val firstName = asmgen.asmIdentifierName(first)
|
||||
val secondName = asmgen.asmIdentifierName(second)
|
||||
val firstName = asmgen.asmVariableName(first)
|
||||
val secondName = asmgen.asmVariableName(second)
|
||||
val dt = first.inferType(program)
|
||||
if(dt.istype(DataType.BYTE) || dt.istype(DataType.UBYTE)) {
|
||||
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | tya | sta $secondName")
|
||||
@ -546,13 +419,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
if(dt.istype(DataType.FLOAT)) {
|
||||
asmgen.out("""
|
||||
lda #<$firstName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>$firstName
|
||||
sta ${C64Zeropage.SCRATCH_W1+1}
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda #<$secondName
|
||||
sta ${C64Zeropage.SCRATCH_W2}
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>$secondName
|
||||
sta ${C64Zeropage.SCRATCH_W2+1}
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
jsr c64flt.swap_floats
|
||||
""")
|
||||
return
|
||||
@ -575,8 +448,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
|
||||
private fun funcMkword(fcall: IFunctionCall, func: FSignature) {
|
||||
translateFunctionArguments(fcall.args, func)
|
||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x")
|
||||
// trick: push the args in reverse order (msb first, lsb second) this saves some instructions
|
||||
asmgen.translateExpression(fcall.args[1])
|
||||
asmgen.translateExpression(fcall.args[0])
|
||||
asmgen.out(" inx | lda P8ESTACK_LO,x | sta P8ESTACK_HI+1,x")
|
||||
}
|
||||
|
||||
private fun funcMsb(fcall: IFunctionCall) {
|
||||
@ -584,39 +459,43 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||
throw AssemblyError("msb required word argument")
|
||||
if (arg is NumericLiteralValue)
|
||||
throw AssemblyError("should have been const-folded")
|
||||
throw AssemblyError("msb(const) should have been const-folded away")
|
||||
if (arg is IdentifierReference) {
|
||||
val sourceName = asmgen.asmIdentifierName(arg)
|
||||
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
asmgen.translateExpression(arg)
|
||||
asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | sta $ESTACK_LO_PLUS1_HEX,x")
|
||||
asmgen.out(" lda P8ESTACK_HI+1,x | sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcLsb(fcall: IFunctionCall) {
|
||||
val arg = fcall.args.single()
|
||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||
throw AssemblyError("lsb required word argument")
|
||||
if (arg is NumericLiteralValue)
|
||||
throw AssemblyError("lsb(const) should have been const-folded away")
|
||||
if (arg is IdentifierReference) {
|
||||
val sourceName = asmgen.asmVariableName(arg)
|
||||
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
|
||||
} else {
|
||||
asmgen.translateExpression(arg)
|
||||
// just ignore any high-byte
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputPushAddressAndLenghtOfArray(arg: Expression) {
|
||||
arg as IdentifierReference
|
||||
val identifierName = asmgen.asmIdentifierName(arg)
|
||||
val size = arg.targetVarDecl(program.namespace)!!.arraysize!!.size()!!
|
||||
val identifierName = asmgen.asmVariableName(arg)
|
||||
val size = arg.targetVarDecl(program.namespace)!!.arraysize!!.constIndex()!!
|
||||
asmgen.out("""
|
||||
lda #<$identifierName
|
||||
sta $ESTACK_LO_HEX,x
|
||||
sta P8ESTACK_LO,x
|
||||
lda #>$identifierName
|
||||
sta $ESTACK_HI_HEX,x
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
lda #$size
|
||||
sta $ESTACK_LO_HEX,x
|
||||
dex
|
||||
""")
|
||||
}
|
||||
|
||||
private fun outputPushAddressOfIdentifier(arg: Expression) {
|
||||
val identifierName = asmgen.asmIdentifierName(arg as IdentifierReference)
|
||||
asmgen.out("""
|
||||
lda #<$identifierName
|
||||
sta $ESTACK_LO_HEX,x
|
||||
lda #>$identifierName
|
||||
sta $ESTACK_HI_HEX,x
|
||||
sta P8ESTACK_LO,x
|
||||
dex
|
||||
""")
|
||||
}
|
||||
|
@ -4,12 +4,8 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS2_HEX
|
||||
import prog8.compiler.target.CompilationTarget
|
||||
import prog8.compiler.target.CpuType
|
||||
import prog8.compiler.toHex
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import kotlin.math.absoluteValue
|
||||
@ -20,7 +16,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
when(expression) {
|
||||
is PrefixExpression -> translateExpression(expression)
|
||||
is BinaryExpression -> translateExpression(expression)
|
||||
is ArrayIndexedExpression -> translatePushFromArray(expression)
|
||||
is ArrayIndexedExpression -> translateExpression(expression)
|
||||
is TypecastExpression -> translateExpression(expression)
|
||||
is AddressOf -> translateExpression(expression)
|
||||
is DirectMemoryRead -> translateExpression(expression)
|
||||
@ -46,20 +42,33 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
// result value in cpu or status registers, put it on the stack
|
||||
if (reg.registerOrPair != null) {
|
||||
when (reg.registerOrPair) {
|
||||
RegisterOrPair.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
|
||||
RegisterOrPair.Y -> asmgen.out(" tya | sta $ESTACK_LO_HEX,x | dex")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta $ESTACK_LO_HEX,x | tya | sta $ESTACK_HI_HEX,x | dex")
|
||||
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
RegisterOrPair.X -> {
|
||||
// return value in X register has been discarded, just push a zero
|
||||
asmgen.out(" lda #0 | sta $ESTACK_LO_HEX,x | dex")
|
||||
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
|
||||
asmgen.out(" stz P8ESTACK_LO,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_LO,x")
|
||||
asmgen.out(" dex")
|
||||
}
|
||||
RegisterOrPair.AX -> {
|
||||
// return value in X register has been discarded, just push a zero in this place
|
||||
asmgen.out(" sta $ESTACK_LO_HEX,x | lda #0 | sta $ESTACK_HI_HEX,x | dex")
|
||||
asmgen.out(" sta P8ESTACK_LO,x")
|
||||
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
|
||||
asmgen.out(" stz P8ESTACK_HI,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI,x")
|
||||
asmgen.out(" dex")
|
||||
}
|
||||
RegisterOrPair.XY -> {
|
||||
// return value in X register has been discarded, just push a zero in this place
|
||||
asmgen.out(" lda #0 | sta $ESTACK_LO_HEX,x | tya | sta $ESTACK_HI_HEX,x | dex")
|
||||
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
|
||||
asmgen.out(" stz P8ESTACK_LO,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_LO,x")
|
||||
asmgen.out(" tya | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,7 +84,12 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
DataType.UBYTE -> {
|
||||
when(expr.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {}
|
||||
DataType.UWORD, DataType.WORD -> asmgen.out(" lda #0 | sta $ESTACK_HI_PLUS1_HEX,x")
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
|
||||
asmgen.out(" stz P8ESTACK_HI+1,x")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta P8ESTACK_HI+1,x")
|
||||
}
|
||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_ub2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -84,7 +98,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
DataType.BYTE -> {
|
||||
when(expr.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {}
|
||||
DataType.UWORD, DataType.WORD -> asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | ${asmgen.signExtendAtoMsb("$ESTACK_HI_PLUS1_HEX,x")}")
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
// sign extend
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+1,x
|
||||
ora #$7f
|
||||
bmi +
|
||||
lda #0
|
||||
+ sta P8ESTACK_HI+1,x""")
|
||||
}
|
||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_b2float")
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -125,48 +147,41 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: AddressOf) {
|
||||
val name = asmgen.asmIdentifierName(expr.identifier)
|
||||
asmgen.out(" lda #<$name | sta $ESTACK_LO_HEX,x | lda #>$name | sta $ESTACK_HI_HEX,x | dex")
|
||||
val name = asmgen.asmVariableName(expr.identifier)
|
||||
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: DirectMemoryRead) {
|
||||
when(expr.addressExpression) {
|
||||
is NumericLiteralValue -> {
|
||||
val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
|
||||
asmgen.out(" lda ${address.toHex()} | sta $ESTACK_LO_HEX,x | dex")
|
||||
asmgen.out(" lda ${address.toHex()} | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
// the identifier is a pointer variable, so read the value from the address in it
|
||||
val sourceName = asmgen.asmIdentifierName(expr.addressExpression as IdentifierReference)
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
sta (+) +1
|
||||
lda $sourceName+1
|
||||
sta (+) +2
|
||||
+ lda ${'$'}ffff ; modified
|
||||
sta $ESTACK_LO_HEX,x
|
||||
dex""")
|
||||
asmgen.loadByteFromPointerIntoA(expr.addressExpression as IdentifierReference)
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
else -> {
|
||||
translateExpression(expr.addressExpression)
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address")
|
||||
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack")
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: NumericLiteralValue) {
|
||||
when(expr.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta $ESTACK_LO_HEX,x | dex")
|
||||
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
|
||||
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
||||
lda #<${expr.number.toHex()}
|
||||
sta $ESTACK_LO_HEX,x
|
||||
sta P8ESTACK_LO,x
|
||||
lda #>${expr.number.toHex()}
|
||||
sta $ESTACK_HI_HEX,x
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
""")
|
||||
DataType.FLOAT -> {
|
||||
val floatConst = asmgen.getFloatConst(expr.number.toDouble())
|
||||
val floatConst = asmgen.getFloatAsmConst(expr.number.toDouble())
|
||||
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -174,28 +189,24 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
|
||||
private fun translateExpression(expr: IdentifierReference) {
|
||||
val varname = asmgen.asmIdentifierName(expr)
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" lda $varname | sta $ESTACK_LO_HEX,x | dex")
|
||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out(" lda $varname | sta $ESTACK_LO_HEX,x | lda $varname+1 | sta $ESTACK_HI_HEX,x | dex")
|
||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | lda $varname+1 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$varname | ldy #>$varname| jsr c64flt.push_float")
|
||||
}
|
||||
in IterableDatatypes -> {
|
||||
asmgen.out(" lda #<$varname | sta $ESTACK_LO_HEX,x | lda #>$varname | sta $ESTACK_HI_HEX,x | dex")
|
||||
asmgen.out(" lda #<$varname | sta P8ESTACK_LO,x | lda #>$varname | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
else -> throw AssemblyError("stack push weird variable type $expr")
|
||||
}
|
||||
}
|
||||
|
||||
private val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40)
|
||||
private val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40)
|
||||
private val powersOfTwo = setOf(0,1,2,4,8,16,32,64,128,256)
|
||||
|
||||
private fun translateExpression(expr: BinaryExpression) {
|
||||
val leftIDt = expr.left.inferType(program)
|
||||
val rightIDt = expr.right.inferType(program)
|
||||
@ -207,79 +218,80 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
// see if we can apply some optimized routines
|
||||
when(expr.operator) {
|
||||
">>" -> {
|
||||
// bit-shifts are always by a constant number (for now)
|
||||
translateExpression(expr.left)
|
||||
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||
when (leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
if(amount<=2)
|
||||
repeat(amount) { asmgen.out(" lsr $ESTACK_LO_PLUS1_HEX,x") }
|
||||
else {
|
||||
asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x")
|
||||
repeat(amount) { asmgen.out(" lsr a") }
|
||||
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||
if(amount!=null) {
|
||||
when (leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" lsr P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||
repeat(amount) { asmgen.out(" lsr a") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(amount<=2)
|
||||
repeat(amount) { asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | asl a | ror $ESTACK_LO_PLUS1_HEX,x") }
|
||||
else {
|
||||
asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | sta ${C64MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||
repeat(amount) { asmgen.out(" asl a | ror ${C64MachineDefinition.C64Zeropage.SCRATCH_B1} | lda ${C64MachineDefinition.C64Zeropage.SCRATCH_B1}") }
|
||||
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
||||
DataType.BYTE -> {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x | sta P8ZP_SCRATCH_B1")
|
||||
repeat(amount) { asmgen.out(" asl a | ror P8ZP_SCRATCH_B1 | lda P8ZP_SCRATCH_B1") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
var left = amount
|
||||
while(left>=7) {
|
||||
asmgen.out(" jsr math.shift_right_uw_7")
|
||||
left -= 7
|
||||
DataType.UWORD -> {
|
||||
var left = amount
|
||||
while (left >= 7) {
|
||||
asmgen.out(" jsr math.shift_right_uw_7")
|
||||
left -= 7
|
||||
}
|
||||
if (left in 0..2)
|
||||
repeat(left) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_uw_$left")
|
||||
}
|
||||
if (left in 0..2)
|
||||
repeat(left) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_uw_$left")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
var left = amount
|
||||
while(left>=7) {
|
||||
asmgen.out(" jsr math.shift_right_w_7")
|
||||
left -= 7
|
||||
DataType.WORD -> {
|
||||
var left = amount
|
||||
while (left >= 7) {
|
||||
asmgen.out(" jsr math.shift_right_w_7")
|
||||
left -= 7
|
||||
}
|
||||
if (left in 0..2)
|
||||
repeat(left) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_w_$left")
|
||||
}
|
||||
if (left in 0..2)
|
||||
repeat(left) { asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_right_w_$left")
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
"<<" -> {
|
||||
// bit-shifts are always by a constant number (for now)
|
||||
translateExpression(expr.left)
|
||||
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||
if (leftDt in ByteDatatypes) {
|
||||
if(amount<=2)
|
||||
repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x") }
|
||||
else {
|
||||
asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x")
|
||||
repeat(amount) { asmgen.out(" asl a") }
|
||||
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||
if(amount!=null) {
|
||||
if (leftDt in ByteDatatypes) {
|
||||
if (amount <= 2)
|
||||
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
||||
else {
|
||||
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||
repeat(amount) { asmgen.out(" asl a") }
|
||||
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||
}
|
||||
} else {
|
||||
var left = amount
|
||||
while (left >= 7) {
|
||||
asmgen.out(" jsr math.shift_left_w_7")
|
||||
left -= 7
|
||||
}
|
||||
if (left in 0..2)
|
||||
repeat(left) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_left_w_$left")
|
||||
}
|
||||
return
|
||||
}
|
||||
else {
|
||||
var left=amount
|
||||
while(left>=7) {
|
||||
asmgen.out(" jsr math.shift_left_w_7")
|
||||
left -= 7
|
||||
}
|
||||
if (left in 0..2)
|
||||
repeat(left) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") }
|
||||
else
|
||||
asmgen.out(" jsr math.shift_left_w_$left")
|
||||
}
|
||||
return
|
||||
}
|
||||
"*" -> {
|
||||
val value = expr.right.constValue(program)
|
||||
@ -288,40 +300,40 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
val amount = value.number.toInt()
|
||||
when(rightDt) {
|
||||
DataType.UBYTE -> {
|
||||
if(amount in optimizedByteMultiplications) {
|
||||
if(amount in asmgen.optimizedByteMultiplications) {
|
||||
translateExpression(expr.left)
|
||||
asmgen.out(" jsr math.mul_byte_$amount")
|
||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||
return
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(amount in optimizedByteMultiplications) {
|
||||
if(amount in asmgen.optimizedByteMultiplications) {
|
||||
translateExpression(expr.left)
|
||||
asmgen.out(" jsr math.mul_byte_$amount")
|
||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||
return
|
||||
}
|
||||
if(amount.absoluteValue in optimizedByteMultiplications) {
|
||||
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
||||
translateExpression(expr.left)
|
||||
asmgen.out(" jsr prog8_lib.neg_b | jsr math.mul_byte_${amount.absoluteValue}")
|
||||
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
||||
return
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(amount in optimizedWordMultiplications) {
|
||||
if(amount in asmgen.optimizedWordMultiplications) {
|
||||
translateExpression(expr.left)
|
||||
asmgen.out(" jsr math.mul_word_$amount")
|
||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||
return
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(amount in optimizedWordMultiplications) {
|
||||
if(amount in asmgen.optimizedWordMultiplications) {
|
||||
translateExpression(expr.left)
|
||||
asmgen.out(" jsr math.mul_word_$amount")
|
||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||
return
|
||||
}
|
||||
if(amount.absoluteValue in optimizedWordMultiplications) {
|
||||
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
||||
translateExpression(expr.left)
|
||||
asmgen.out(" jsr prog8_lib.neg_w | jsr math.mul_word_${amount.absoluteValue}")
|
||||
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -364,9 +376,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
when(type) {
|
||||
in ByteDatatypes ->
|
||||
asmgen.out("""
|
||||
lda $ESTACK_LO_PLUS1_HEX,x
|
||||
lda P8ESTACK_LO+1,x
|
||||
eor #255
|
||||
sta $ESTACK_LO_PLUS1_HEX,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
||||
else -> throw AssemblyError("weird type")
|
||||
@ -383,29 +395,47 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
}
|
||||
}
|
||||
|
||||
private fun translatePushFromArray(arrayExpr: ArrayIndexedExpression) {
|
||||
// assume *reading* from an array
|
||||
private fun translateExpression(arrayExpr: ArrayIndexedExpression) {
|
||||
val index = arrayExpr.arrayspec.index
|
||||
val arrayDt = arrayExpr.identifier.targetVarDecl(program.namespace)!!.datatype
|
||||
val arrayVarName = asmgen.asmIdentifierName(arrayExpr.identifier)
|
||||
val elementDt = arrayExpr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val arrayVarName = asmgen.asmVariableName(arrayExpr.identifier)
|
||||
if(index is NumericLiteralValue) {
|
||||
val elementDt = ArrayElementTypes.getValue(arrayDt)
|
||||
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | dex")
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | lda $arrayVarName+$indexValue+1 | sta $ESTACK_HI_HEX,x | dex")
|
||||
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | lda $arrayVarName+$indexValue+1 | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
else -> throw AssemblyError("weird element type")
|
||||
}
|
||||
} else {
|
||||
asmgen.translateArrayIndexIntoA(arrayExpr)
|
||||
asmgen.readAndPushArrayvalueWithIndexA(arrayDt, arrayExpr.identifier)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | lda $arrayVarName+1,y | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.A)
|
||||
asmgen.out("""
|
||||
ldy #>$arrayVarName
|
||||
clc
|
||||
adc #<$arrayVarName
|
||||
bcc +
|
||||
iny
|
||||
+ jsr c64flt.push_float""")
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -420,20 +450,21 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
asmgen.out(" jsr prog8_lib.remainder_ub")
|
||||
}
|
||||
"+" -> asmgen.out("""
|
||||
lda $ESTACK_LO_PLUS2_HEX,x
|
||||
lda P8ESTACK_LO+2,x
|
||||
clc
|
||||
adc $ESTACK_LO_PLUS1_HEX,x
|
||||
adc P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta $ESTACK_LO_PLUS1_HEX,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
"-" -> asmgen.out("""
|
||||
lda $ESTACK_LO_PLUS2_HEX,x
|
||||
lda P8ESTACK_LO+2,x
|
||||
sec
|
||||
sbc $ESTACK_LO_PLUS1_HEX,x
|
||||
sbc P8ESTACK_LO+1,x
|
||||
inx
|
||||
sta $ESTACK_LO_PLUS1_HEX,x
|
||||
sta P8ESTACK_LO+1,x
|
||||
""")
|
||||
"<<", ">>" -> throw AssemblyError("bit-shifts not via stack")
|
||||
"<<" -> asmgen.out(" jsr prog8_lib.shiftleft_b")
|
||||
">>" -> asmgen.out(" jsr prog8_lib.shiftright_b")
|
||||
"<" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.less_ub" else " jsr prog8_lib.less_b")
|
||||
">" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.greater_ub" else " jsr prog8_lib.greater_b")
|
||||
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
|
||||
|
@ -4,20 +4,15 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.RangeExpr
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.ForLoop
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
|
||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
|
||||
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
||||
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
|
||||
import prog8.compiler.toHex
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
// todo choose more efficient comparisons to avoid needless lda's
|
||||
// todo optimize common case when step == 2 or -2
|
||||
|
||||
|
||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||
|
||||
internal fun translate(stmt: ForLoop) {
|
||||
@ -43,57 +38,61 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
||||
private fun translateForOverNonconstRange(stmt: ForLoop, iterableDt: DataType, range: RangeExpr) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
val continueLabel = asmgen.makeLabel("for_continue")
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
asmgen.loopContinueLabels.push(continueLabel)
|
||||
val stepsize=range.step.constValue(program)!!.number.toInt()
|
||||
when(iterableDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
if (stepsize==1 || stepsize==-1) {
|
||||
|
||||
// bytes, step 1 or -1
|
||||
// bytes array, step 1 or -1
|
||||
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.translateExpression(range.to)
|
||||
asmgen.translateExpression(range.from)
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda ${ESTACK_LO_HEX},x
|
||||
lda P8ESTACK_LO,x
|
||||
sta $varname
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta $modifiedLabel+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel lda $varname
|
||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
||||
lda $varname
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
beq $endLabel
|
||||
$incdec $varname
|
||||
jmp $loopLabel
|
||||
$endLabel inx""")
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
|
||||
// bytes, step >= 2 or <= -2
|
||||
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.translateExpression(range.to)
|
||||
asmgen.translateExpression(range.from)
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda ${ESTACK_LO_HEX},x
|
||||
lda P8ESTACK_LO,x
|
||||
sta $varname
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta $modifiedLabel+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel lda $varname""")
|
||||
lda $varname""")
|
||||
if(stepsize>0) {
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc #$stepsize
|
||||
sta $varname
|
||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bcc $loopLabel
|
||||
beq $loopLabel""")
|
||||
} else {
|
||||
@ -101,7 +100,7 @@ $continueLabel lda $varname""")
|
||||
sec
|
||||
sbc #${stepsize.absoluteValue}
|
||||
sta $varname
|
||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bcs $loopLabel""")
|
||||
}
|
||||
asmgen.out("""
|
||||
@ -115,47 +114,52 @@ $endLabel inx""")
|
||||
|
||||
stepsize == 1 || stepsize == -1 -> {
|
||||
asmgen.translateExpression(range.to)
|
||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position),
|
||||
null, range.from, range.position)
|
||||
assignLoopvar.linkParents(stmt)
|
||||
asmgen.translate(assignLoopvar)
|
||||
asmgen.out(loopLabel)
|
||||
assignLoopvar(stmt, range)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta $modifiedLabel+1
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
lda $varname+1
|
||||
cmp $ESTACK_HI_PLUS1_HEX,x
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bne +
|
||||
lda $varname
|
||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
beq $endLabel""")
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne +
|
||||
inc $varname+1
|
||||
bne $loopLabel
|
||||
inc $varname+1
|
||||
jmp $loopLabel
|
||||
""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
+ dec $varname
|
||||
jmp $loopLabel""")
|
||||
}
|
||||
asmgen.out("""
|
||||
+ jmp $loopLabel
|
||||
$endLabel inx""")
|
||||
asmgen.out(endLabel)
|
||||
asmgen.out(" inx")
|
||||
}
|
||||
stepsize > 0 -> {
|
||||
|
||||
// (u)words, step >= 2
|
||||
|
||||
asmgen.translateExpression(range.to)
|
||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position),
|
||||
null, range.from, range.position)
|
||||
assignLoopvar.linkParents(stmt)
|
||||
asmgen.translate(assignLoopvar)
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta $modifiedLabel+1
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta $modifiedLabel2+1
|
||||
""")
|
||||
assignLoopvar(stmt, range)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.body)
|
||||
|
||||
@ -168,12 +172,11 @@ $endLabel inx""")
|
||||
lda $varname+1
|
||||
adc #>$stepsize
|
||||
sta $varname+1
|
||||
lda $ESTACK_HI_PLUS1_HEX,x
|
||||
cmp $varname+1
|
||||
bcc $endLabel
|
||||
bne $loopLabel
|
||||
lda $varname
|
||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bcc $loopLabel
|
||||
bne $endLabel
|
||||
$modifiedLabel2 lda #0 ; modified
|
||||
cmp $varname
|
||||
bcc $endLabel
|
||||
bcs $loopLabel
|
||||
$endLabel inx""")
|
||||
@ -186,9 +189,9 @@ $endLabel inx""")
|
||||
lda $varname+1
|
||||
adc #>$stepsize
|
||||
sta $varname+1
|
||||
lda $ESTACK_LO_PLUS1_HEX,x
|
||||
$modifiedLabel2 lda #0 ; modified
|
||||
cmp $varname
|
||||
lda $ESTACK_HI_PLUS1_HEX,x
|
||||
$modifiedLabel lda #0 ; modified
|
||||
sbc $varname+1
|
||||
bvc +
|
||||
eor #$80
|
||||
@ -200,11 +203,14 @@ $endLabel inx""")
|
||||
|
||||
// (u)words, step <= -2
|
||||
asmgen.translateExpression(range.to)
|
||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position),
|
||||
null, range.from, range.position)
|
||||
assignLoopvar.linkParents(stmt)
|
||||
asmgen.translate(assignLoopvar)
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_HI+1,x
|
||||
sta $modifiedLabel+1
|
||||
lda P8ESTACK_LO+1,x
|
||||
sta $modifiedLabel2+1
|
||||
""")
|
||||
assignLoopvar(stmt, range)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.body)
|
||||
|
||||
@ -217,11 +223,11 @@ $endLabel inx""")
|
||||
lda $varname+1
|
||||
sbc #>${stepsize.absoluteValue}
|
||||
sta $varname+1
|
||||
cmp $ESTACK_HI_PLUS1_HEX,x
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bcc $endLabel
|
||||
bne $loopLabel
|
||||
lda $varname
|
||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
bcs $loopLabel
|
||||
$endLabel inx""")
|
||||
} else {
|
||||
@ -235,9 +241,9 @@ $endLabel inx""")
|
||||
sbc #>${stepsize.absoluteValue}
|
||||
sta $varname+1
|
||||
pla
|
||||
cmp $ESTACK_LO_PLUS1_HEX,x
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
lda $varname+1
|
||||
sbc $ESTACK_HI_PLUS1_HEX,x
|
||||
$modifiedLabel sbc #0 ; modified
|
||||
bvc +
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
@ -250,16 +256,13 @@ $endLabel inx""")
|
||||
}
|
||||
|
||||
asmgen.loopEndLabels.pop()
|
||||
asmgen.loopContinueLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForOverIterableVar(stmt: ForLoop, iterableDt: DataType, ident: IdentifierReference) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
val continueLabel = asmgen.makeLabel("for_continue")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
asmgen.loopContinueLabels.push(continueLabel)
|
||||
val iterableName = asmgen.asmIdentifierName(ident)
|
||||
val iterableName = asmgen.asmVariableName(ident)
|
||||
val decl = ident.targetVarDecl(program.namespace)!!
|
||||
when(iterableDt) {
|
||||
DataType.STR -> {
|
||||
@ -269,72 +272,88 @@ $endLabel inx""")
|
||||
sta $loopLabel+1
|
||||
sty $loopLabel+2
|
||||
$loopLabel lda ${65535.toHex()} ; modified
|
||||
beq $endLabel""")
|
||||
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}")
|
||||
beq $endLabel
|
||||
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel inc $loopLabel+1
|
||||
inc $loopLabel+1
|
||||
bne $loopLabel
|
||||
inc $loopLabel+2
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases
|
||||
val length = decl.arraysize!!.size()!!
|
||||
val counterLabel = asmgen.makeLabel("for_counter")
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val length = decl.arraysize!!.constIndex()!!
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
asmgen.out("""
|
||||
lda #<$iterableName
|
||||
ldy #>$iterableName
|
||||
sta $modifiedLabel+1
|
||||
sty $modifiedLabel+2
|
||||
ldy #0
|
||||
$loopLabel sty $counterLabel
|
||||
$modifiedLabel lda ${65535.toHex()},y ; modified""")
|
||||
asmgen.out(" sta ${asmgen.asmIdentifierName(stmt.loopVar)}")
|
||||
$loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel ldy $counterLabel
|
||||
iny
|
||||
cpy #${length and 255}
|
||||
beq $endLabel
|
||||
jmp $loopLabel
|
||||
$counterLabel .byte 0
|
||||
$endLabel""")
|
||||
if(length<=255) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
cpy #$length
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 256
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16 && asmgen.zeropage.available() > 0) {
|
||||
// allocate index var on ZP
|
||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
$indexVar .byte 0""")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
// TODO: optimize loop code when the length of the array is < 256, don't need a separate counter var in such cases
|
||||
val length = decl.arraysize!!.size()!! * 2
|
||||
val counterLabel = asmgen.makeLabel("for_counter")
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modified2")
|
||||
val loopvarName = asmgen.asmIdentifierName(stmt.loopVar)
|
||||
val length = decl.arraysize!!.constIndex()!! * 2
|
||||
val indexVar = asmgen.makeLabel("for_index")
|
||||
val loopvarName = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #<$iterableName
|
||||
ldy #>$iterableName
|
||||
sta $modifiedLabel+1
|
||||
sty $modifiedLabel+2
|
||||
lda #<$iterableName+1
|
||||
ldy #>$iterableName+1
|
||||
sta $modifiedLabel2+1
|
||||
sty $modifiedLabel2+2
|
||||
ldy #0
|
||||
$loopLabel sty $counterLabel
|
||||
$modifiedLabel lda ${65535.toHex()},y ; modified
|
||||
$loopLabel sty $indexVar
|
||||
lda $iterableName,y
|
||||
sta $loopvarName
|
||||
$modifiedLabel2 lda ${65535.toHex()},y ; modified
|
||||
lda $iterableName+1,y
|
||||
sta $loopvarName+1""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel ldy $counterLabel
|
||||
iny
|
||||
iny
|
||||
cpy #${length and 255}
|
||||
beq $endLabel
|
||||
jmp $loopLabel
|
||||
$counterLabel .byte 0
|
||||
$endLabel""")
|
||||
if(length<=127) {
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
cpy #$length
|
||||
beq $endLabel
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
// length is 128 words, 256 bytes
|
||||
asmgen.out("""
|
||||
ldy $indexVar
|
||||
iny
|
||||
iny
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16 && asmgen.zeropage.available() > 0) {
|
||||
// allocate index var on ZP
|
||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
$indexVar .byte 0""")
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
throw AssemblyError("for loop with floating point variables is not supported")
|
||||
@ -342,205 +361,123 @@ $endLabel""")
|
||||
else -> throw AssemblyError("can't iterate over $iterableDt")
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
asmgen.loopContinueLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForOverConstRange(stmt: ForLoop, iterableDt: DataType, range: IntProgression) {
|
||||
// TODO: optimize loop code when the range is < 256 iterations, don't need a separate counter var in such cases
|
||||
if (range.isEmpty())
|
||||
throw AssemblyError("empty range")
|
||||
if (range.isEmpty() || range.step==0)
|
||||
throw AssemblyError("empty range or step 0")
|
||||
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
|
||||
if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
|
||||
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range)
|
||||
}
|
||||
else if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) {
|
||||
if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range)
|
||||
if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range)
|
||||
}
|
||||
|
||||
// not one of the easy cases, generate more complex code...
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
val continueLabel = asmgen.makeLabel("for_continue")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
asmgen.loopContinueLabels.push(continueLabel)
|
||||
when(iterableDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
val counterLabel = asmgen.makeLabel("for_counter")
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||
when {
|
||||
range.step==1 -> {
|
||||
// step = 1
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
lda #${range.last-range.first+1 and 255}
|
||||
sta $counterLabel
|
||||
// loop over byte range via loopvar, step >= 2 or <= -2
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.translate(stmt.body)
|
||||
when (range.step) {
|
||||
0, 1, -1 -> {
|
||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||
}
|
||||
2 -> {
|
||||
if(range.last==255) {
|
||||
asmgen.out("""
|
||||
$continueLabel dec $counterLabel
|
||||
beq $endLabel
|
||||
inc $varname
|
||||
jmp $loopLabel
|
||||
$counterLabel .byte 0
|
||||
$endLabel""")
|
||||
inc $varname
|
||||
beq $endLabel
|
||||
inc $varname
|
||||
bne $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #${range.last}
|
||||
beq $endLabel
|
||||
inc $varname
|
||||
inc $varname
|
||||
jmp $loopLabel""")
|
||||
}
|
||||
}
|
||||
range.step==-1 -> {
|
||||
// step = -1
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
lda #${range.first-range.last+1 and 255}
|
||||
sta $counterLabel
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel dec $counterLabel
|
||||
beq $endLabel
|
||||
dec $varname
|
||||
jmp $loopLabel
|
||||
$counterLabel .byte 0
|
||||
$endLabel""")
|
||||
}
|
||||
range.step >= 2 -> {
|
||||
// step >= 2
|
||||
asmgen.out("""
|
||||
lda #${(range.last-range.first) / range.step + 1}
|
||||
sta $counterLabel
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel dec $counterLabel
|
||||
beq $endLabel
|
||||
lda $varname
|
||||
clc
|
||||
adc #${range.step}
|
||||
sta $varname
|
||||
jmp $loopLabel
|
||||
$counterLabel .byte 0
|
||||
$endLabel""")
|
||||
-2 -> {
|
||||
when (range.last) {
|
||||
0 -> asmgen.out("""
|
||||
lda $varname
|
||||
beq $endLabel
|
||||
dec $varname
|
||||
dec $varname
|
||||
jmp $loopLabel""")
|
||||
1 -> asmgen.out("""
|
||||
dec $varname
|
||||
beq $endLabel
|
||||
dec $varname
|
||||
bne $loopLabel""")
|
||||
else -> asmgen.out("""
|
||||
lda $varname
|
||||
cmp #${range.last}
|
||||
beq $endLabel
|
||||
dec $varname
|
||||
dec $varname
|
||||
jmp $loopLabel""")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// step <= -2
|
||||
// step <= -3 or >= 3
|
||||
asmgen.out("""
|
||||
lda #${(range.first-range.last) / range.step.absoluteValue + 1}
|
||||
sta $counterLabel
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel dec $counterLabel
|
||||
beq $endLabel
|
||||
lda $varname
|
||||
sec
|
||||
sbc #${range.step.absoluteValue}
|
||||
sta $varname
|
||||
jmp $loopLabel
|
||||
$counterLabel .byte 0
|
||||
$endLabel""")
|
||||
lda $varname
|
||||
cmp #${range.last}
|
||||
beq $endLabel
|
||||
clc
|
||||
adc #${range.step}
|
||||
sta $varname
|
||||
jmp $loopLabel""")
|
||||
}
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
// loop over word range via loopvar
|
||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
||||
when {
|
||||
range.step == 1 -> {
|
||||
// word, step = 1
|
||||
val lastValue = range.last+1
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel inc $varname
|
||||
bne +
|
||||
inc $varname+1
|
||||
+ lda $varname
|
||||
cmp #<$lastValue
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>$lastValue
|
||||
beq $endLabel
|
||||
+ jmp $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
range.step == -1 -> {
|
||||
// word, step = 1
|
||||
val lastValue = range.last-1
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname
|
||||
lda $varname
|
||||
cmp #<$lastValue
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>$lastValue
|
||||
beq $endLabel
|
||||
+ jmp $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
range.step >= 2 -> {
|
||||
// word, step >= 2
|
||||
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
|
||||
val lastValue = range.last+range.step
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel clc
|
||||
lda $varname
|
||||
adc #<${range.step}
|
||||
sta $varname
|
||||
lda $varname+1
|
||||
adc #>${range.step}
|
||||
sta $varname+1
|
||||
lda $varname
|
||||
cmp #<$lastValue
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>$lastValue
|
||||
beq $endLabel
|
||||
+ jmp $loopLabel
|
||||
$endLabel""")
|
||||
// loop over word range via loopvar, step >= 2 or <= -2
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
when (range.step) {
|
||||
0, 1, -1 -> {
|
||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||
}
|
||||
else -> {
|
||||
// step <= -2
|
||||
// word, step >= 2 or <= -2
|
||||
// note: range.last has already been adjusted by kotlin itself to actually be the last value of the sequence
|
||||
val lastValue = range.last+range.step
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
$continueLabel sec
|
||||
lda $varname
|
||||
sbc #<${range.step.absoluteValue}
|
||||
sta $varname
|
||||
lda $varname+1
|
||||
sbc #>${range.step.absoluteValue}
|
||||
sta $varname+1
|
||||
lda $varname
|
||||
cmp #<$lastValue
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>$lastValue
|
||||
beq $endLabel
|
||||
+ jmp $loopLabel
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>${range.last}
|
||||
bne +
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
clc
|
||||
adc #<${range.step}
|
||||
sta $varname
|
||||
lda $varname+1
|
||||
adc #>${range.step}
|
||||
sta $varname+1
|
||||
jmp $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
@ -548,7 +485,134 @@ $endLabel""")
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
asmgen.loopContinueLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleByteRangeAsc(stmt: ForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
if (range.last == 255) {
|
||||
asmgen.out("""
|
||||
inc $varname
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #${range.last}
|
||||
beq $endLabel
|
||||
inc $varname
|
||||
jmp $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleByteRangeDesc(stmt: ForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
when (range.last) {
|
||||
0 -> {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
beq $endLabel
|
||||
dec $varname
|
||||
jmp $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
1 -> {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
jmp $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #${range.last}
|
||||
beq $endLabel
|
||||
dec $varname
|
||||
jmp $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleWordRangeAsc(stmt: ForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>${range.last}
|
||||
bne +
|
||||
beq $endLabel
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1
|
||||
jmp $loopLabel
|
||||
$endLabel""")
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleWordRangeDesc(stmt: ForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
ldy #>${range.first}
|
||||
sta $varname
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>${range.last}
|
||||
bne +
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname
|
||||
jmp $loopLabel
|
||||
$endLabel""")
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) {
|
||||
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), variable=stmt.loopVar)
|
||||
val src = AsmAssignSource.fromAstSource(range.from, program).adjustDataTypeToTarget(target)
|
||||
val assign = AsmAssignment(src, target, false, range.position)
|
||||
asmgen.translateNormalAssignment(assign)
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,11 @@ import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.RegisterOrStatusflag
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.SubroutineParameter
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||
import prog8.compiler.toHex
|
||||
import prog8.compiler.target.c64.codegen.assignment.*
|
||||
|
||||
|
||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||
@ -19,11 +17,11 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
// output the code to setup the parameters and perform the actual call
|
||||
// does NOT output the code to deal with the result values!
|
||||
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult()
|
||||
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
|
||||
if(saveX)
|
||||
asmgen.out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y...
|
||||
asmgen.saveRegister(CpuRegister.X)
|
||||
|
||||
val subName = asmgen.asmIdentifierName(stmt.target)
|
||||
val subName = asmgen.asmSymbolName(stmt.target)
|
||||
if(stmt.args.isNotEmpty()) {
|
||||
if(sub.asmParameterRegisters.isEmpty()) {
|
||||
// via variables
|
||||
@ -51,7 +49,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
}
|
||||
else -> {
|
||||
// Risk of clobbering due to complex expression args. Work via the stack.
|
||||
argsViaStackEvaluation(stmt, sub)
|
||||
registerArgsViaStackEvaluation(stmt, sub)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,39 +58,85 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
asmgen.out(" jsr $subName")
|
||||
|
||||
if(saveX)
|
||||
asmgen.out(" ldx c64.SCRATCH_ZPREGX") // restore X again
|
||||
asmgen.restoreRegister(CpuRegister.X)
|
||||
}
|
||||
|
||||
private fun argsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||
// this is called when one or more of the arguments are 'complex' and
|
||||
// cannot be assigned to a register easily or risk clobbering other registers.
|
||||
|
||||
if(sub.parameters.isEmpty())
|
||||
return
|
||||
|
||||
// 1. load all arguments reversed onto the stack: first arg goes last (is on top).
|
||||
for (arg in stmt.args.reversed())
|
||||
asmgen.translateExpression(arg)
|
||||
for (regparam in sub.asmParameterRegisters) {
|
||||
when (regparam.registerOrPair) {
|
||||
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
||||
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
||||
RegisterOrPair.AX -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||
RegisterOrPair.AY -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
||||
RegisterOrPair.XY -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||
null -> {
|
||||
|
||||
var argForCarry: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||
var argForXregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||
var argForAregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||
|
||||
asmgen.out(" inx") // align estack pointer
|
||||
|
||||
for(argi in stmt.args.zip(sub.asmParameterRegisters).withIndex()) {
|
||||
when {
|
||||
argi.value.second.stack -> TODO("asmsub @stack parameter")
|
||||
argi.value.second.statusflag == Statusflag.Pc -> {
|
||||
require(argForCarry == null)
|
||||
argForCarry = argi
|
||||
}
|
||||
}
|
||||
when (regparam.statusflag) {
|
||||
Statusflag.Pc -> asmgen.out("""
|
||||
inx
|
||||
pha
|
||||
lda $ESTACK_LO_HEX,x
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+ pla
|
||||
""")
|
||||
null -> {
|
||||
argi.value.second.statusflag != null -> throw AssemblyError("can only use Carry as status flag parameter")
|
||||
argi.value.second.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) -> {
|
||||
require(argForXregister==null)
|
||||
argForXregister = argi
|
||||
}
|
||||
else -> throw AssemblyError("can only use Carry as status flag parameter")
|
||||
argi.value.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.AY) -> {
|
||||
require(argForAregister == null)
|
||||
argForAregister = argi
|
||||
}
|
||||
argi.value.second.registerOrPair == RegisterOrPair.Y -> {
|
||||
asmgen.out(" ldy P8ESTACK_LO+${argi.index},x")
|
||||
}
|
||||
else -> throw AssemblyError("weird argument")
|
||||
}
|
||||
}
|
||||
|
||||
if(argForCarry!=null) {
|
||||
asmgen.out("""
|
||||
lda P8ESTACK_LO+${argForCarry.index},x
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+ php""") // push the status flags
|
||||
}
|
||||
|
||||
if(argForAregister!=null) {
|
||||
when(argForAregister.value.second.registerOrPair) {
|
||||
RegisterOrPair.A -> asmgen.out(" lda P8ESTACK_LO+${argForAregister.index},x")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda P8ESTACK_LO+${argForAregister.index},x | ldy P8ESTACK_HI+${argForAregister.index},x")
|
||||
else -> throw AssemblyError("weird arg")
|
||||
}
|
||||
}
|
||||
|
||||
if(argForXregister!=null) {
|
||||
|
||||
if(argForAregister!=null)
|
||||
asmgen.out(" pha")
|
||||
when(argForXregister.value.second.registerOrPair) {
|
||||
RegisterOrPair.X -> asmgen.out(" lda P8ESTACK_LO+${argForXregister.index},x | tax")
|
||||
RegisterOrPair.AX -> asmgen.out(" ldy P8ESTACK_LO+${argForXregister.index},x | lda P8ESTACK_HI+${argForXregister.index},x | tax | tya")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy P8ESTACK_HI+${argForXregister.index},x | lda P8ESTACK_LO+${argForXregister.index},x | tax")
|
||||
else -> throw AssemblyError("weird arg")
|
||||
}
|
||||
if(argForAregister!=null)
|
||||
asmgen.out(" pla")
|
||||
} else {
|
||||
repeat(sub.parameters.size - 1) { asmgen.out(" inx") } // unwind stack
|
||||
}
|
||||
|
||||
if(argForCarry!=null)
|
||||
asmgen.out(" plp") // set the carry flag back to correct value
|
||||
}
|
||||
|
||||
private fun argumentViaVariable(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||
@ -101,55 +145,16 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
if(!valueIDt.isKnown)
|
||||
throw AssemblyError("arg type unknown")
|
||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||
if(!argumentTypeCompatible(valueDt, parameter.value.type))
|
||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||
throw AssemblyError("argument type incompatible")
|
||||
|
||||
val paramVar = parameter.value
|
||||
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
||||
val target = AssignTarget(IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
||||
target.linkParents(value.parent)
|
||||
when (value) {
|
||||
is NumericLiteralValue -> {
|
||||
// optimize when the argument is a constant literal
|
||||
when(parameter.value.type) {
|
||||
in ByteDatatypes -> asmgen.assignFromByteConstant(target, value.number.toShort())
|
||||
in WordDatatypes -> asmgen.assignFromWordConstant(target, value.number.toInt())
|
||||
DataType.FLOAT -> asmgen.assignFromFloatConstant(target, value.number.toDouble())
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as argument via a variable?") // TODO huh
|
||||
else -> throw AssemblyError("weird parameter datatype")
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
// optimize when the argument is a variable
|
||||
when (parameter.value.type) {
|
||||
in ByteDatatypes -> asmgen.assignFromByteVariable(target, value)
|
||||
in WordDatatypes -> asmgen.assignFromWordVariable(target, value)
|
||||
DataType.FLOAT -> asmgen.assignFromFloatVariable(target, value)
|
||||
in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as argument via a variable?") // TODO huh
|
||||
else -> throw AssemblyError("weird parameter datatype")
|
||||
}
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
when(value.addressExpression) {
|
||||
is NumericLiteralValue -> {
|
||||
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
||||
asmgen.assignFromMemoryByte(target, address, null)
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
asmgen.assignFromMemoryByte(target, null, value.addressExpression as IdentifierReference)
|
||||
}
|
||||
else -> {
|
||||
asmgen.translateExpression(value.addressExpression)
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
|
||||
asmgen.assignFromRegister(target, CpuRegister.A)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
asmgen.translateExpression(value)
|
||||
asmgen.assignFromEvalResult(target)
|
||||
}
|
||||
}
|
||||
val scopedParamVar = (sub.scopedname+"."+parameter.value.name).split(".")
|
||||
val identifier = IdentifierReference(scopedParamVar, sub.position)
|
||||
identifier.linkParents(value.parent)
|
||||
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, variable = identifier)
|
||||
val source = AsmAssignSource.fromAstSource(value, program).adjustDataTypeToTarget(tgt)
|
||||
val asgn = AsmAssignment(source, tgt, false, Position.DUMMY)
|
||||
asmgen.translateNormalAssignment(asgn)
|
||||
}
|
||||
|
||||
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||
@ -158,7 +163,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
if(!valueIDt.isKnown)
|
||||
throw AssemblyError("arg type unknown")
|
||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||
if(!argumentTypeCompatible(valueDt, parameter.value.type))
|
||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||
throw AssemblyError("argument type incompatible")
|
||||
|
||||
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
||||
@ -181,14 +186,15 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
asmgen.out(if(carrySet) " sec" else " clc")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val sourceName = asmgen.asmIdentifierName(value)
|
||||
val sourceName = asmgen.asmVariableName(value)
|
||||
asmgen.out("""
|
||||
pha
|
||||
lda $sourceName
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
+ clc
|
||||
+
|
||||
+ pla
|
||||
""")
|
||||
}
|
||||
else -> {
|
||||
@ -196,7 +202,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
asmgen.out("""
|
||||
inx
|
||||
pha
|
||||
lda $ESTACK_LO_HEX,x
|
||||
lda P8ESTACK_LO,x
|
||||
beq +
|
||||
sec
|
||||
bcs ++
|
||||
@ -208,79 +214,22 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
}
|
||||
else throw AssemblyError("can only use Carry as status flag parameter")
|
||||
}
|
||||
register!=null && register.name.length==1 -> {
|
||||
when (value) {
|
||||
is NumericLiteralValue -> {
|
||||
asmgen.assignToRegister(CpuRegister.valueOf(register.name), value.number.toShort(), null)
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
asmgen.assignToRegister(CpuRegister.valueOf(register.name), null, value)
|
||||
}
|
||||
else -> {
|
||||
asmgen.translateExpression(value)
|
||||
when(register) {
|
||||
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
||||
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
||||
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
||||
else -> throw AssemblyError("cannot assign to register pair")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
register!=null && register.name.length==2 -> {
|
||||
// register pair as a 16-bit value (only possible for subroutine parameters)
|
||||
when (value) {
|
||||
is NumericLiteralValue -> {
|
||||
// optimize when the argument is a constant literal
|
||||
val hex = value.number.toHex()
|
||||
when (register) {
|
||||
RegisterOrPair.AX -> asmgen.out(" lda #<$hex | ldx #>$hex")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda #<$hex | ldy #>$hex")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx #<$hex | ldy #>$hex")
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
is AddressOf -> {
|
||||
// optimize when the argument is an address of something
|
||||
val sourceName = asmgen.asmIdentifierName(value.identifier)
|
||||
when (register) {
|
||||
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName")
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val sourceName = asmgen.asmIdentifierName(value)
|
||||
if(valueDt in PassByReferenceDatatypes) {
|
||||
when (register) {
|
||||
RegisterOrPair.AX -> asmgen.out(" lda #<$sourceName | ldx #>$sourceName")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx #<$sourceName | ldy #>$sourceName")
|
||||
else -> {}
|
||||
}
|
||||
} else {
|
||||
when (register) {
|
||||
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx $sourceName+1")
|
||||
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy $sourceName+1")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy $sourceName+1")
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
asmgen.translateExpression(value)
|
||||
if (register == RegisterOrPair.AX || register == RegisterOrPair.XY)
|
||||
throw AssemblyError("can't use X register here - use a variable")
|
||||
else if (register == RegisterOrPair.AY)
|
||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
||||
}
|
||||
else -> {
|
||||
// via register or register pair
|
||||
val target = AsmAssignTarget.fromRegisters(register!!, program, asmgen)
|
||||
val src = if(valueDt in PassByReferenceDatatypes) {
|
||||
val addr = AddressOf(value as IdentifierReference, Position.DUMMY)
|
||||
AsmAssignSource.fromAstSource(addr, program).adjustDataTypeToTarget(target)
|
||||
} else {
|
||||
AsmAssignSource.fromAstSource(value, program).adjustDataTypeToTarget(target)
|
||||
}
|
||||
|
||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, false, Position.DUMMY))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||
private fun isArgumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
|
||||
if(argType isAssignableTo paramType)
|
||||
return true
|
||||
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
||||
|
@ -6,7 +6,6 @@ import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.statements.PostIncrDecr
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||
import prog8.compiler.toHex
|
||||
|
||||
|
||||
@ -18,7 +17,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
||||
val targetArrayIdx = stmt.target.arrayindexed
|
||||
when {
|
||||
targetIdent!=null -> {
|
||||
val what = asmgen.asmIdentifierName(targetIdent)
|
||||
val what = asmgen.asmVariableName(targetIdent)
|
||||
when (stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)) {
|
||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||
in WordDatatypes -> {
|
||||
@ -46,83 +45,91 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
||||
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val what = asmgen.asmIdentifierName(addressExpr)
|
||||
val what = asmgen.asmVariableName(addressExpr)
|
||||
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
else -> throw AssemblyError("weird target type $targetMemory")
|
||||
else -> {
|
||||
asmgen.translateExpression(addressExpr)
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta (+) + 1
|
||||
lda P8ESTACK_HI,x
|
||||
sta (+) + 2
|
||||
""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
}
|
||||
}
|
||||
targetArrayIdx!=null -> {
|
||||
val index = targetArrayIdx.arrayspec.index
|
||||
val what = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val elementDt = ArrayElementTypes.getValue(arrayDt)
|
||||
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.identifier)
|
||||
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
when(index) {
|
||||
is NumericLiteralValue -> {
|
||||
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what+$indexValue" else " dec $what+$indexValue")
|
||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $what+$indexValue | bne + | inc $what+$indexValue+1 |+")
|
||||
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $what+$indexValue
|
||||
lda $asmArrayvarname+$indexValue
|
||||
bne +
|
||||
dec $what+$indexValue+1
|
||||
+ dec $what+$indexValue
|
||||
dec $asmArrayvarname+$indexValue+1
|
||||
+ dec $asmArrayvarname+$indexValue
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out(" lda #<$what+$indexValue | ldy #>$what+$indexValue")
|
||||
asmgen.out(" lda #<$asmArrayvarname+$indexValue | ldy #>$asmArrayvarname+$indexValue")
|
||||
asmgen.out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f")
|
||||
}
|
||||
else -> throw AssemblyError("need numeric type")
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||
}
|
||||
else -> {
|
||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
|
||||
asmgen.saveRegister(CpuRegister.X)
|
||||
asmgen.out(" tax")
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
asmgen.out(if(incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $asmArrayvarname,x | bne + | inc $asmArrayvarname+1,x |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname,x
|
||||
bne +
|
||||
dec $asmArrayvarname+1,x
|
||||
+ dec $asmArrayvarname
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
ldy #>$asmArrayvarname
|
||||
clc
|
||||
adc #<$asmArrayvarname
|
||||
bcc +
|
||||
iny
|
||||
+ jsr c64flt.inc_var_f""")
|
||||
}
|
||||
else -> throw AssemblyError("weird array elt dt")
|
||||
}
|
||||
asmgen.restoreRegister(CpuRegister.X)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird target type ${stmt.target}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun incrDecrArrayvalueWithIndexA(incr: Boolean, arrayDt: DataType, arrayVarName: String) {
|
||||
asmgen.out(" stx ${C64Zeropage.SCRATCH_REG_X} | tax")
|
||||
when(arrayDt) {
|
||||
DataType.STR,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
asmgen.out(if(incr) " inc $arrayVarName,x" else " dec $arrayVarName,x")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
if(incr)
|
||||
asmgen.out(" inc $arrayVarName,x | bne + | inc $arrayVarName+1,x |+")
|
||||
else
|
||||
asmgen.out("""
|
||||
lda $arrayVarName,x
|
||||
bne +
|
||||
dec $arrayVarName+1,x
|
||||
+ dec $arrayVarName
|
||||
""")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
asmgen.out(" lda #<$arrayVarName | ldy #>$arrayVarName")
|
||||
asmgen.out(if(incr) " jsr c64flt.inc_indexed_var_f" else " jsr c64flt.dec_indexed_var_f")
|
||||
}
|
||||
else -> throw AssemblyError("weird array dt")
|
||||
}
|
||||
asmgen.out(" ldx ${C64Zeropage.SCRATCH_REG_X}")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,165 @@
|
||||
package prog8.compiler.target.c64.codegen.assignment
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.c64.codegen.AsmGen
|
||||
|
||||
|
||||
internal enum class TargetStorageKind {
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
STACK
|
||||
}
|
||||
|
||||
internal enum class SourceStorageKind {
|
||||
LITERALNUMBER,
|
||||
VARIABLE,
|
||||
ARRAY,
|
||||
MEMORY,
|
||||
REGISTER,
|
||||
STACK, // value is already present on stack
|
||||
EXPRESSION, // expression in ast-form, still to be evaluated
|
||||
}
|
||||
|
||||
internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
program: Program,
|
||||
asmgen: AsmGen,
|
||||
val datatype: DataType,
|
||||
val variable: IdentifierReference? = null,
|
||||
val array: ArrayIndexedExpression? = null,
|
||||
val memory: DirectMemoryWrite? = null,
|
||||
val register: RegisterOrPair? = null,
|
||||
val origAstTarget: AssignTarget? = null
|
||||
)
|
||||
{
|
||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
||||
val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() }
|
||||
val vardecl by lazy { variable?.targetVarDecl(program.namespace)!! }
|
||||
val asmVarname by lazy {
|
||||
if(variable!=null)
|
||||
asmgen.asmVariableName(variable)
|
||||
else
|
||||
asmgen.asmVariableName(array!!.identifier)
|
||||
}
|
||||
|
||||
lateinit var origAssign: AsmAssignment
|
||||
|
||||
init {
|
||||
if(variable!=null && vardecl.type == VarDeclType.CONST)
|
||||
throw AssemblyError("can't assign to a constant")
|
||||
if(register!=null && datatype !in IntegerDatatypes)
|
||||
throw AssemblyError("register must be integer type")
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromAstAssignment(assign: Assignment, program: Program, asmgen: AsmGen): AsmAssignTarget = with(assign.target) {
|
||||
val dt = inferType(program, assign).typeOrElse(DataType.STRUCT)
|
||||
when {
|
||||
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, variable=identifier, origAstTarget = this)
|
||||
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, array = arrayindexed, origAstTarget = this)
|
||||
memoryAddress != null -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, dt, memory = memoryAddress, origAstTarget = this)
|
||||
else -> throw AssemblyError("weird target")
|
||||
}
|
||||
}
|
||||
|
||||
fun fromRegisters(registers: RegisterOrPair, program: Program, asmgen: AsmGen): AsmAssignTarget =
|
||||
when(registers) {
|
||||
RegisterOrPair.A,
|
||||
RegisterOrPair.X,
|
||||
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UBYTE, register = registers)
|
||||
RegisterOrPair.AX,
|
||||
RegisterOrPair.AY,
|
||||
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, register = registers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
private val program: Program,
|
||||
val datatype: DataType,
|
||||
val variable: IdentifierReference? = null,
|
||||
val array: ArrayIndexedExpression? = null,
|
||||
val memory: DirectMemoryRead? = null,
|
||||
val register: CpuRegister? = null,
|
||||
val number: NumericLiteralValue? = null,
|
||||
val expression: Expression? = null
|
||||
)
|
||||
{
|
||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
||||
val constArrayIndexValue by lazy { array?.arrayspec?.constIndex() }
|
||||
val vardecl by lazy { variable?.targetVarDecl(program.namespace)!! }
|
||||
|
||||
companion object {
|
||||
fun fromAstSource(value: Expression, program: Program): AsmAssignSource {
|
||||
val cv = value.constValue(program)
|
||||
if(cv!=null)
|
||||
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, cv.type, number = cv)
|
||||
|
||||
return when(value) {
|
||||
is NumericLiteralValue -> AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, value.type, number = cv)
|
||||
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
||||
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
||||
is IdentifierReference -> {
|
||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, dt, variable = value)
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
AsmAssignSource(SourceStorageKind.MEMORY, program, DataType.UBYTE, memory = value)
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
AsmAssignSource(SourceStorageKind.ARRAY, program, dt, array = value)
|
||||
}
|
||||
else -> {
|
||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, dt, expression = value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAstValue(): Expression = when(kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> number!!
|
||||
SourceStorageKind.VARIABLE -> variable!!
|
||||
SourceStorageKind.ARRAY -> array!!
|
||||
SourceStorageKind.MEMORY -> memory!!
|
||||
SourceStorageKind.EXPRESSION -> expression!!
|
||||
SourceStorageKind.REGISTER -> throw AssemblyError("cannot get a register source as Ast node")
|
||||
SourceStorageKind.STACK -> throw AssemblyError("cannot get a stack source as Ast node")
|
||||
}
|
||||
|
||||
fun withAdjustedDt(newType: DataType) =
|
||||
AsmAssignSource(kind, program, newType, variable, array, memory, register, number, expression)
|
||||
|
||||
fun adjustDataTypeToTarget(target: AsmAssignTarget): AsmAssignSource {
|
||||
// allow some signed/unsigned relaxations
|
||||
if(target.datatype!=datatype) {
|
||||
if(target.datatype in ByteDatatypes && datatype in ByteDatatypes) {
|
||||
return withAdjustedDt(target.datatype)
|
||||
} else if(target.datatype in WordDatatypes && datatype in WordDatatypes) {
|
||||
return withAdjustedDt(target.datatype)
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal class AsmAssignment(val source: AsmAssignSource,
|
||||
val target: AsmAssignTarget,
|
||||
val isAugmentable: Boolean,
|
||||
val position: Position) {
|
||||
|
||||
init {
|
||||
if(target.register !in setOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||
require(source.datatype.memorySize() == target.datatype.memorySize()) { "source and target datatype must be same storage class" }
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
123
compiler/src/prog8/compiler/target/cx16/CX16MachineDefinition.kt
Normal file
123
compiler/src/prog8/compiler/target/cx16/CX16MachineDefinition.kt
Normal file
@ -0,0 +1,123 @@
|
||||
package prog8.compiler.target.cx16
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.compiler.*
|
||||
import prog8.compiler.target.CpuType
|
||||
import prog8.compiler.target.IMachineDefinition
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.parser.ModuleImporter
|
||||
import java.io.IOException
|
||||
|
||||
internal object CX16MachineDefinition: IMachineDefinition {
|
||||
|
||||
override val cpu = CpuType.CPU65c02
|
||||
|
||||
// 5-byte cbm MFLPT format limitations:
|
||||
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||
override val FLOAT_MEM_SIZE = 5
|
||||
override val POINTER_MEM_SIZE = 2
|
||||
override val BASIC_LOAD_ADDRESS = 0x0801
|
||||
override val RAW_LOAD_ADDRESS = 0x8000
|
||||
|
||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
||||
// and some heavily used string constants derived from the two values above
|
||||
override val ESTACK_LO = 0x0400 // $0400-$04ff inclusive
|
||||
override val ESTACK_HI = 0x0500 // $0500-$05ff inclusive
|
||||
|
||||
override lateinit var zeropage: Zeropage
|
||||
|
||||
override fun getFloat(num: Number) = C64MachineDefinition.Mflpt5.fromNumber(num)
|
||||
|
||||
override fun getFloatRomConst(number: Double): String? = null // Cx16 has no pulblic ROM float locations
|
||||
override fun importLibs(compilerOptions: CompilationOptions, importer: ModuleImporter, program: Program) {
|
||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||
importer.importLibraryModule(program, "cx16lib")
|
||||
}
|
||||
|
||||
override fun launchEmulator(programName: String) {
|
||||
for(emulator in listOf("x16emu")) {
|
||||
println("\nStarting Commander X16 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-rom", "/usr/share/x16-rom/rom.bin", "-scale", "2",
|
||||
"-run", "-prg", programName + ".prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
process=processb.start()
|
||||
} catch(x: IOException) {
|
||||
continue // try the next emulator executable
|
||||
}
|
||||
process.waitFor()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
override fun initializeZeropage(compilerOptions: CompilationOptions) {
|
||||
zeropage = CX16Zeropage(compilerOptions)
|
||||
}
|
||||
|
||||
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
|
||||
override val opcodeNames = setOf("adc", "and", "asl", "bcc", "bcs",
|
||||
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
|
||||
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dec", "dex", "dey",
|
||||
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
|
||||
"inc", "inx", "iny", "jmp", "jsr",
|
||||
"lda", "ldx", "ldy", "lsr", "nop", "ora", "pha", "php",
|
||||
"pla", "plp", "rol", "ror", "rti", "rts", "sbc",
|
||||
"sec", "sed", "sei",
|
||||
"sta", "stx", "sty", "tax", "tay", "tsx", "txa", "txs", "tya",
|
||||
"bra", "phx", "phy", "plx", "ply", "stz", "trb", "tsb", "bbr", "bbs",
|
||||
"rmb", "smb", "stp", "wai")
|
||||
|
||||
|
||||
internal class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
|
||||
override val SCRATCH_B1 = 0x79 // temp storage for a single byte
|
||||
override val SCRATCH_REG = 0x7a // temp storage for a register
|
||||
override val SCRATCH_W1 = 0x7c // temp storage 1 for a word $7c+$7d
|
||||
override val SCRATCH_W2 = 0x7e // temp storage 2 for a word $7e+$7f
|
||||
|
||||
|
||||
override val exitProgramStrategy: ExitProgramStrategy = when (options.zeropage) {
|
||||
ZeropageType.BASICSAFE, ZeropageType.DONTUSE -> ExitProgramStrategy.CLEAN_EXIT
|
||||
ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET
|
||||
else -> ExitProgramStrategy.SYSTEM_RESET
|
||||
}
|
||||
|
||||
|
||||
init {
|
||||
if (options.floats && options.zeropage !in setOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||
throw CompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")
|
||||
|
||||
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
|
||||
|
||||
when (options.zeropage) {
|
||||
ZeropageType.FULL -> {
|
||||
free.addAll(0x22..0xff)
|
||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||
}
|
||||
ZeropageType.KERNALSAFE -> {
|
||||
free.addAll(0x22..0x7f)
|
||||
free.addAll(0xa9..0xff)
|
||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||
}
|
||||
ZeropageType.BASICSAFE -> {
|
||||
free.addAll(0x22..0x7f)
|
||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||
}
|
||||
ZeropageType.DONTUSE -> {
|
||||
free.clear() // don't use zeropage at all
|
||||
}
|
||||
else -> throw CompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||
}
|
||||
|
||||
require(SCRATCH_B1 !in free)
|
||||
require(SCRATCH_REG !in free)
|
||||
require(SCRATCH_W1 !in free)
|
||||
require(SCRATCH_W2 !in free)
|
||||
|
||||
for (reserved in options.zpReserved)
|
||||
reserve(reserved)
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,8 @@ package prog8.functions
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.StructDecl
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compiler.CompilerException
|
||||
import kotlin.math.*
|
||||
|
||||
@ -25,8 +27,6 @@ val BuiltinFunctions = mapOf(
|
||||
"ror" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"rol2" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"ror2" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"lsl" to FSignature(false, listOf(FParam("item", IntegerDatatypes)), null),
|
||||
"lsr" to FSignature(false, listOf(FParam("item", IntegerDatatypes)), null),
|
||||
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
// these few have a return value depending on the argument(s):
|
||||
@ -35,6 +35,7 @@ val BuiltinFunctions = mapOf(
|
||||
"sum" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
|
||||
// normal functions follow:
|
||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
||||
"sin" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||
@ -62,7 +63,7 @@ val BuiltinFunctions = mapOf(
|
||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||
"lsb" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
|
||||
"msb" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
|
||||
"mkword" to FSignature(true, listOf(FParam("lsb", setOf(DataType.UBYTE)), FParam("msb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
||||
"mkword" to FSignature(true, listOf(FParam("msb", setOf(DataType.UBYTE)), FParam("lsb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
||||
"rnd" to FSignature(true, emptyList(), DataType.UBYTE),
|
||||
"rndw" to FSignature(true, emptyList(), DataType.UWORD),
|
||||
"rndf" to FSignature(true, emptyList(), DataType.FLOAT),
|
||||
@ -103,9 +104,9 @@ val BuiltinFunctions = mapOf(
|
||||
FParam("length", setOf(DataType.UBYTE))), null)
|
||||
)
|
||||
|
||||
fun builtinMax(array: List<Number>): Number = array.maxBy { it.toDouble() }!!
|
||||
fun builtinMax(array: List<Number>): Number = array.maxByOrNull { it.toDouble() }!!
|
||||
|
||||
fun builtinMin(array: List<Number>): Number = array.minBy { it.toDouble() }!!
|
||||
fun builtinMin(array: List<Number>): Number = array.minByOrNull { it.toDouble() }!!
|
||||
|
||||
fun builtinSum(array: List<Number>): Number = array.sumByDouble { it.toDouble() }
|
||||
|
||||
@ -240,59 +241,88 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
// 1 arg, type = anything, result type = ubyte
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("sizeof requires one argument", position)
|
||||
if(args[0] !is IdentifierReference)
|
||||
throw SyntaxError("sizeof argument should be an identifier", position)
|
||||
|
||||
val dt = args[0].inferType(program)
|
||||
if(dt.isKnown) {
|
||||
val target = (args[0] as IdentifierReference).targetStatement(program.namespace)
|
||||
?: throw CannotEvaluateException("sizeof", "no target")
|
||||
|
||||
fun structSize(target: StructDecl) =
|
||||
NumericLiteralValue(DataType.UBYTE, target.statements.map { (it as VarDecl).datatype.memorySize() }.sum(), position)
|
||||
|
||||
return when {
|
||||
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
||||
val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size")
|
||||
val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT))
|
||||
numericLiteral(elementDt.memorySize() * length, position)
|
||||
}
|
||||
dt.istype(DataType.STRUCT) -> {
|
||||
when (target) {
|
||||
is VarDecl -> structSize(target.struct!!)
|
||||
is StructDecl -> structSize(target)
|
||||
else -> throw CompilerException("weird struct type $target")
|
||||
}
|
||||
}
|
||||
dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
|
||||
else -> NumericLiteralValue(DataType.UBYTE, dt.typeOrElse(DataType.STRUCT).memorySize(), position)
|
||||
}
|
||||
} else {
|
||||
throw SyntaxError("sizeof invalid argument type", position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinStrlen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("strlen requires one argument", position)
|
||||
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
if(argument.type != DataType.STR)
|
||||
throw SyntaxError("strlen must have string argument", position)
|
||||
|
||||
throw NotConstArgumentException() // this function is not considering the string argument a constant
|
||||
val argument=args[0]
|
||||
if(argument is StringLiteralValue)
|
||||
return NumericLiteralValue.optimalInteger(argument.value.length, argument.position)
|
||||
val vardecl = (argument as IdentifierReference).targetVarDecl(program.namespace)
|
||||
if(vardecl!=null) {
|
||||
if(vardecl.datatype!=DataType.STR)
|
||||
throw SyntaxError("strlen must have string argument", position)
|
||||
if(vardecl.autogeneratedDontRemove) {
|
||||
return NumericLiteralValue.optimalInteger((vardecl.value as StringLiteralValue).value.length, argument.position)
|
||||
}
|
||||
}
|
||||
throw NotConstArgumentException()
|
||||
}
|
||||
|
||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("len requires one argument", position)
|
||||
val constArg = args[0].constValue(program)
|
||||
if(constArg!=null)
|
||||
throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||
|
||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
|
||||
var arraySize = directMemVar?.arraysize?.size()
|
||||
var arraySize = directMemVar?.arraysize?.constIndex()
|
||||
if(arraySize != null)
|
||||
return NumericLiteralValue.optimalInteger(arraySize, position)
|
||||
if(args[0] is ArrayLiteralValue)
|
||||
return NumericLiteralValue.optimalInteger((args[0] as ArrayLiteralValue).value.size, position)
|
||||
if(args[0] !is IdentifierReference)
|
||||
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
||||
throw SyntaxError("len argument should be an identifier", position)
|
||||
val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace)
|
||||
?: throw CannotEvaluateException("len", "no target vardecl")
|
||||
|
||||
return when(target.datatype) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
arraySize = target.arraysize?.size()
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> {
|
||||
arraySize = target.arraysize?.constIndex()
|
||||
if(arraySize==null)
|
||||
throw CannotEvaluateException("len", "arraysize unknown")
|
||||
if(arraySize>256)
|
||||
throw CompilerException("array length exceeds byte limit ${target.position}")
|
||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
arraySize = target.arraysize?.size()
|
||||
if(arraySize==null)
|
||||
throw CannotEvaluateException("len", "arraysize unknown")
|
||||
if(arraySize>256)
|
||||
throw CompilerException("array length exceeds byte limit ${target.position}")
|
||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
}
|
||||
DataType.STR -> {
|
||||
val refLv = target.value as StringLiteralValue
|
||||
if(refLv.value.length>255)
|
||||
throw CompilerException("string length exceeds byte limit ${refLv.position}")
|
||||
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
|
||||
}
|
||||
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||
DataType.STRUCT -> throw SyntaxError("cannot use len on struct, did you mean sizeof?", args[0].position)
|
||||
in NumericDatatypes -> throw SyntaxError("cannot use len on numeric value, did you mean sizeof?", args[0].position)
|
||||
else -> throw CompilerException("weird datatype")
|
||||
}
|
||||
}
|
||||
@ -300,9 +330,9 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
||||
|
||||
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("mkword requires lsb and msb arguments", position)
|
||||
val constLsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constMsb = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
throw SyntaxError("mkword requires msb and lsb arguments", position)
|
||||
val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val constLsb = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||
val result = (constMsb.number.toInt() shl 8) or constLsb.number.toInt()
|
||||
return NumericLiteralValue(DataType.UWORD, result, position)
|
||||
}
|
||||
@ -312,7 +342,7 @@ private fun builtinSin8(args: List<Expression>, position: Position, program: Pro
|
||||
throw SyntaxError("sin8 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toShort(), position)
|
||||
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toInt().toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@ -320,7 +350,7 @@ private fun builtinSin8u(args: List<Expression>, position: Position, program: Pr
|
||||
throw SyntaxError("sin8u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort(), position)
|
||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toInt().toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinCos8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@ -328,7 +358,7 @@ private fun builtinCos8(args: List<Expression>, position: Position, program: Pro
|
||||
throw SyntaxError("cos8 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toShort(), position)
|
||||
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toInt().toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@ -336,7 +366,7 @@ private fun builtinCos8u(args: List<Expression>, position: Position, program: Pr
|
||||
throw SyntaxError("cos8u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.number.toDouble() /256.0 * 2.0 * PI
|
||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort(), position)
|
||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toInt().toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinSin16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
@ -375,7 +405,7 @@ private fun builtinSgn(args: List<Expression>, position: Position, program: Prog
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sgn requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
return NumericLiteralValue(DataType.BYTE, constval.number.toDouble().sign.toShort(), position)
|
||||
return NumericLiteralValue(DataType.BYTE, constval.number.toDouble().sign.toInt().toShort(), position)
|
||||
}
|
||||
|
||||
private fun numericLiteral(value: Number, position: Position): NumericLiteralValue {
|
||||
@ -387,8 +417,8 @@ private fun numericLiteral(value: Number, position: Position): NumericLiteralVal
|
||||
floatNum
|
||||
|
||||
return when(tweakedValue) {
|
||||
is Int -> NumericLiteralValue.optimalNumeric(value.toInt(), position)
|
||||
is Short -> NumericLiteralValue.optimalNumeric(value.toInt(), position)
|
||||
is Int -> NumericLiteralValue.optimalInteger(value.toInt(), position)
|
||||
is Short -> NumericLiteralValue.optimalInteger(value.toInt(), position)
|
||||
is Byte -> NumericLiteralValue(DataType.UBYTE, value.toShort(), position)
|
||||
is Double -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
|
||||
is Float -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
|
||||
|
@ -1,157 +0,0 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ErrorReporter
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.processing.AstWalker
|
||||
import prog8.ast.processing.IAstModification
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.PostIncrDecr
|
||||
|
||||
|
||||
|
||||
internal class AssignmentTransformer(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
||||
|
||||
var optimizationsDone: Int = 0
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
// modify A = A + 5 back into augmented form A += 5 for easier code generation for optimized in-place assignments
|
||||
// also to put code generation stuff together, single value assignment (A = 5) is converted to a special
|
||||
// augmented form as wel (with the operator "setvalue")
|
||||
if (assignment.aug_op == null) {
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if (binExpr != null) {
|
||||
if (assignment.target.isSameAs(binExpr.left)) {
|
||||
assignment.value = binExpr.right
|
||||
assignment.aug_op = binExpr.operator + "="
|
||||
assignment.value.parent = assignment
|
||||
optimizationsDone++
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
assignment.aug_op = "setvalue"
|
||||
optimizationsDone++
|
||||
} else if(assignment.aug_op == "+=") {
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if (binExpr != null) {
|
||||
val leftnum = binExpr.left.constValue(program)?.number?.toDouble()
|
||||
val rightnum = binExpr.right.constValue(program)?.number?.toDouble()
|
||||
if(binExpr.operator == "+") {
|
||||
when {
|
||||
leftnum == 1.0 -> {
|
||||
optimizationsDone++
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
leftnum == 2.0 -> {
|
||||
optimizationsDone++
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
rightnum == 1.0 -> {
|
||||
// x += y + 1 -> x += y , x++
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent)
|
||||
)
|
||||
}
|
||||
rightnum == 2.0 -> {
|
||||
// x += y + 2 -> x += y , x++, x++
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if(binExpr.operator == "-") {
|
||||
when {
|
||||
leftnum == 1.0 -> {
|
||||
optimizationsDone++
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
leftnum == 2.0 -> {
|
||||
optimizationsDone++
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
rightnum == 1.0 -> {
|
||||
// x += y - 1 -> x += y , x--
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent)
|
||||
)
|
||||
}
|
||||
rightnum == 2.0 -> {
|
||||
// x += y - 2 -> x += y , x--, x--
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(assignment.aug_op == "-=") {
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if (binExpr != null) {
|
||||
val leftnum = binExpr.left.constValue(program)?.number?.toDouble()
|
||||
val rightnum = binExpr.right.constValue(program)?.number?.toDouble()
|
||||
if(binExpr.operator == "+") {
|
||||
when {
|
||||
leftnum == 1.0 -> {
|
||||
optimizationsDone++
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
leftnum == 2.0 -> {
|
||||
optimizationsDone++
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
rightnum == 1.0 -> {
|
||||
// x -= y + 1 -> x -= y , x--
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent)
|
||||
)
|
||||
}
|
||||
rightnum == 2.0 -> {
|
||||
// x -= y + 2 -> x -= y , x--, x--
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "--", assignment.position), parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if(binExpr.operator == "-") {
|
||||
when {
|
||||
leftnum == 1.0 -> {
|
||||
optimizationsDone++
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
leftnum == 2.0 -> {
|
||||
optimizationsDone++
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
rightnum == 1.0 -> {
|
||||
// x -= y - 1 -> x -= y , x++
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent)
|
||||
)
|
||||
}
|
||||
rightnum == 2.0 -> {
|
||||
// x -= y - 2 -> x -= y , x++, x++
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(assignment.value, binExpr.left, assignment),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent),
|
||||
IAstModification.InsertAfter(assignment, PostIncrDecr(assignment.target, "++", assignment.position), parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
@ -14,8 +14,7 @@ import prog8.compiler.loadAsmIncludeFile
|
||||
|
||||
private val alwaysKeepSubroutines = setOf(
|
||||
Pair("main", "start"),
|
||||
Pair("irq", "irq"),
|
||||
Pair("prog8_lib", "init_system")
|
||||
Pair("irq", "irq")
|
||||
)
|
||||
|
||||
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||
|
@ -132,11 +132,11 @@ class ConstExprEvaluator {
|
||||
private fun bitwiseand(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toShort(), left.position)
|
||||
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toShort(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteralValue(DataType.UWORD, left.number.toInt() or right.number.toInt(), left.position)
|
||||
return NumericLiteralValue(DataType.UWORD, left.number.toInt() and right.number.toInt(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||
@ -163,7 +163,7 @@ class ConstExprEvaluator {
|
||||
val error = "cannot add $left and $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() + right.number.toInt(), left.position)
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() + right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
@ -180,7 +180,7 @@ class ConstExprEvaluator {
|
||||
val error = "cannot subtract $left and $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() - right.number.toInt(), left.position)
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() - right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
@ -197,7 +197,7 @@ class ConstExprEvaluator {
|
||||
val error = "cannot multiply ${left.type} and ${right.type}"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position)
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() * right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
@ -220,7 +220,7 @@ class ConstExprEvaluator {
|
||||
in IntegerDatatypes -> {
|
||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||
val result: Int = left.number.toInt() / right.number.toInt()
|
||||
NumericLiteralValue.optimalNumeric(result, left.position)
|
||||
NumericLiteralValue.optimalInteger(result, left.position)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||
|
@ -56,7 +56,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
))
|
||||
}
|
||||
}
|
||||
else if(decl.arraysize?.size()==null) {
|
||||
else if(decl.arraysize?.constIndex()==null) {
|
||||
val size = decl.arraysize!!.index.constValue(program)
|
||||
if(size!=null) {
|
||||
return listOf(IAstModification.SetExpression(
|
||||
@ -81,7 +81,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
val rangeExpr = decl.value as? RangeExpr
|
||||
if(rangeExpr!=null) {
|
||||
// convert the initializer range expression to an actual array
|
||||
val declArraySize = decl.arraysize?.size()
|
||||
val declArraySize = decl.arraysize?.constIndex()
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||
val constRange = rangeExpr.toConstantIntegerRange()
|
||||
@ -101,7 +101,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
}
|
||||
if(numericLv!=null && numericLv.type==DataType.FLOAT)
|
||||
errors.err("arraysize requires only integers here", numericLv.position)
|
||||
val size = decl.arraysize?.size() ?: return noModifications
|
||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||
if (rangeExpr==null && numericLv!=null) {
|
||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||
val fillvalue = numericLv.number.toInt()
|
||||
@ -125,18 +125,18 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
else -> {}
|
||||
}
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) as Expression}.toTypedArray()
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) }.toTypedArray<Expression>()
|
||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
val size = decl.arraysize?.size() ?: return noModifications
|
||||
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||
val litval = decl.value as? NumericLiteralValue
|
||||
val rangeExpr = decl.value as? RangeExpr
|
||||
if(rangeExpr!=null) {
|
||||
// convert the initializer range expression to an actual array of floats
|
||||
val declArraySize = decl.arraysize?.size()
|
||||
val declArraySize = decl.arraysize?.constIndex()
|
||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||
val constRange = rangeExpr.toConstantIntegerRange()
|
||||
@ -150,11 +150,11 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
if(rangeExpr==null && litval!=null) {
|
||||
// arraysize initializer is a single int, and we know the size.
|
||||
val fillvalue = litval.number.toDouble()
|
||||
if (fillvalue < CompilationTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.machine.FLOAT_MAX_POSITIVE)
|
||||
if (fillvalue < CompilationTarget.instance.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.instance.machine.FLOAT_MAX_POSITIVE)
|
||||
errors.err("float value overflow", litval.position)
|
||||
else {
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) }.toTypedArray<Expression>()
|
||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = litval.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
}
|
||||
@ -171,7 +171,9 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
if(declValue!=null && decl.type==VarDeclType.VAR
|
||||
&& declValue is NumericLiteralValue && !declValue.inferType(program).istype(decl.datatype)) {
|
||||
// cast the numeric literal to the appropriate datatype of the variable
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, declValue.cast(decl.datatype), decl))
|
||||
val cast = declValue.cast(decl.datatype)
|
||||
if(cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
@ -179,7 +181,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
}
|
||||
|
||||
|
||||
internal class ConstantFoldingOptimizer(private val program: Program, private val errors: ErrorReporter) : AstWalker() {
|
||||
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||
@ -203,7 +205,7 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
||||
"-" -> when (subexpr.type) {
|
||||
in IntegerDatatypes -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position),
|
||||
NumericLiteralValue.optimalInteger(-subexpr.number.toInt(), subexpr.position),
|
||||
parent))
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
@ -216,7 +218,7 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
||||
"~" -> when (subexpr.type) {
|
||||
in IntegerDatatypes -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position),
|
||||
NumericLiteralValue.optimalInteger(subexpr.number.toInt().inv(), subexpr.position),
|
||||
parent))
|
||||
}
|
||||
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
||||
@ -273,11 +275,8 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
||||
// const fold when both operands are a const
|
||||
if(leftconst != null && rightconst != null) {
|
||||
val evaluator = ConstExprEvaluator()
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
expr,
|
||||
evaluator.evaluate(leftconst, expr.operator, rightconst),
|
||||
parent
|
||||
))
|
||||
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
|
||||
return listOf(IAstModification.ReplaceNode(expr, result, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
@ -319,21 +318,24 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
||||
}
|
||||
|
||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr {
|
||||
val newFrom: NumericLiteralValue
|
||||
val newTo: NumericLiteralValue
|
||||
try {
|
||||
newFrom = rangeFrom.cast(targetDt)
|
||||
newTo = rangeTo.cast(targetDt)
|
||||
} catch (x: ExpressionError) {
|
||||
return range
|
||||
}
|
||||
val newStep: Expression = try {
|
||||
stepLiteral?.cast(targetDt)?: range.step
|
||||
} catch(ee: ExpressionError) {
|
||||
range.step
|
||||
}
|
||||
return RangeExpr(newFrom, newTo, newStep, range.position)
|
||||
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr? {
|
||||
val fromCast = rangeFrom.cast(targetDt)
|
||||
val toCast = rangeTo.cast(targetDt)
|
||||
if(!fromCast.isValid || !toCast.isValid)
|
||||
return null
|
||||
|
||||
val newStep =
|
||||
if(stepLiteral!=null) {
|
||||
val stepCast = stepLiteral.cast(targetDt)
|
||||
if(stepCast.isValid)
|
||||
stepCast.valueOrZero()
|
||||
else
|
||||
range.step
|
||||
} else {
|
||||
range.step
|
||||
}
|
||||
|
||||
return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, range.position)
|
||||
}
|
||||
|
||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||
@ -349,28 +351,32 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
||||
if(rangeFrom.type!= DataType.UBYTE) {
|
||||
// attempt to translate the iterable into ubyte values
|
||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
if(newIter!=null)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(rangeFrom.type!= DataType.BYTE) {
|
||||
// attempt to translate the iterable into byte values
|
||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
if(newIter!=null)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(rangeFrom.type!= DataType.UWORD) {
|
||||
// attempt to translate the iterable into uword values
|
||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
if(newIter!=null)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(rangeFrom.type!= DataType.WORD) {
|
||||
// attempt to translate the iterable into word values
|
||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
if(newIter!=null)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||
}
|
||||
}
|
||||
else -> throw FatalAstException("invalid loopvar datatype $loopvar")
|
||||
@ -379,6 +385,19 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
val numval = decl.value as? NumericLiteralValue
|
||||
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||
val valueDt = numval.inferType(program)
|
||||
if(!valueDt.istype(decl.datatype)) {
|
||||
val cast = numval.cast(decl.datatype)
|
||||
if(cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private class ShuffleOperands(val expr: BinaryExpression,
|
||||
val exprOperator: String?,
|
||||
val subExpr: BinaryExpression,
|
||||
@ -406,8 +425,9 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
||||
// todo: this implements only a small set of possible reorderings at this time
|
||||
if(expr.operator==subExpr.operator) {
|
||||
// both operators are the same.
|
||||
// If + or *, we can simply shuffle the const operands around to optimize.
|
||||
if(expr.operator=="+" || expr.operator=="*") {
|
||||
|
||||
// If associative, we can simply shuffle the const operands around to optimize.
|
||||
if(expr.operator in associativeOperators) {
|
||||
return if(leftIsConst) {
|
||||
if(subleftIsConst)
|
||||
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)
|
||||
|
@ -6,7 +6,6 @@ import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.AstWalker
|
||||
import prog8.ast.processing.IAstModification
|
||||
import prog8.ast.statements.Assignment
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.pow
|
||||
@ -24,12 +23,6 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
if (assignment.aug_op != null)
|
||||
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
|
||||
@ -37,19 +30,22 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
val literal = typecast.expression as? NumericLiteralValue
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type)
|
||||
if (newLiteral !== literal)
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral, typecast)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal)
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral.valueOrZero(), typecast)
|
||||
}
|
||||
|
||||
// remove redundant nested typecasts:
|
||||
// if the typecast casts a value to the same type, remove the cast.
|
||||
// if the typecast contains another typecast, remove the inner typecast.
|
||||
// remove redundant nested typecasts
|
||||
val subTypecast = typecast.expression as? TypecastExpression
|
||||
if (subTypecast != null) {
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||
// remove the sub-typecast if its datatype is larger than the outer typecast
|
||||
if(subTypecast.type largerThan typecast.type) {
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||
}
|
||||
} else {
|
||||
if (typecast.expression.inferType(program).istype(typecast.type))
|
||||
if (typecast.expression.inferType(program).istype(typecast.type)) {
|
||||
// remove duplicate cast
|
||||
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
@ -301,6 +297,49 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCall.target.nameInSource == listOf("lsb")) {
|
||||
val arg = functionCall.args[0]
|
||||
if(arg is TypecastExpression) {
|
||||
val valueDt = arg.expression.inferType(program)
|
||||
if (valueDt.istype(DataType.BYTE) || valueDt.istype(DataType.UBYTE)) {
|
||||
// useless lsb() of byte value that was casted to word
|
||||
return listOf(IAstModification.ReplaceNode(functionCall, arg.expression, parent))
|
||||
}
|
||||
} else {
|
||||
val argDt = arg.inferType(program)
|
||||
if (argDt.istype(DataType.BYTE) || argDt.istype(DataType.UBYTE)) {
|
||||
// useless lsb() of byte value
|
||||
return listOf(IAstModification.ReplaceNode(functionCall, arg, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(functionCall.target.nameInSource == listOf("msb")) {
|
||||
val arg = functionCall.args[0]
|
||||
if(arg is TypecastExpression) {
|
||||
val valueDt = arg.expression.inferType(program)
|
||||
if (valueDt.istype(DataType.BYTE) || valueDt.istype(DataType.UBYTE)) {
|
||||
// useless msb() of byte value that was casted to word, replace with 0
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
functionCall,
|
||||
NumericLiteralValue(valueDt.typeOrElse(DataType.UBYTE), 0, arg.expression.position),
|
||||
parent))
|
||||
}
|
||||
} else {
|
||||
val argDt = arg.inferType(program)
|
||||
if (argDt.istype(DataType.BYTE) || argDt.istype(DataType.UBYTE)) {
|
||||
// useless msb() of byte value, replace with 0
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
functionCall,
|
||||
NumericLiteralValue(argDt.typeOrElse(DataType.UBYTE), 0, arg.position),
|
||||
parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
||||
return when {
|
||||
subBinExpr.left isSameAs x -> subBinExpr.right
|
||||
@ -590,10 +629,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
} else if (amount >= 8) {
|
||||
val lsb = TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
|
||||
if (amount == 8) {
|
||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), lsb), expr.position)
|
||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
|
||||
}
|
||||
val shifted = BinaryExpression(lsb, "<<", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), shifted), expr.position)
|
||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
|
@ -10,7 +10,7 @@ internal fun Program.constantFold(errors: ErrorReporter) {
|
||||
if(errors.isEmpty()) {
|
||||
replacer.applyModifications()
|
||||
|
||||
val optimizer = ConstantFoldingOptimizer(this, errors)
|
||||
val optimizer = ConstantFoldingOptimizer(this)
|
||||
optimizer.visit(this)
|
||||
while (errors.isEmpty() && optimizer.applyModifications() > 0) {
|
||||
optimizer.visit(this)
|
||||
|
@ -14,11 +14,6 @@ import prog8.functions.BuiltinFunctions
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
/*
|
||||
TODO: remove unreachable code after return and exit()
|
||||
*/
|
||||
|
||||
|
||||
internal class StatementOptimizer(private val program: Program,
|
||||
private val errors: ErrorReporter) : AstWalker() {
|
||||
|
||||
@ -105,32 +100,34 @@ internal class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
if(stringVar!=null) {
|
||||
val vardecl = stringVar.targetVarDecl(program.namespace)!!
|
||||
val string = vardecl.value!! as StringLiteralValue
|
||||
val pos = functionCallStatement.position
|
||||
if(string.value.length==1) {
|
||||
val firstCharEncoded = CompilationTarget.encodeString(string.value, string.altEncoding)[0]
|
||||
val chrout = FunctionCallStatement(
|
||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||
} else if(string.value.length==2) {
|
||||
val firstTwoCharsEncoded = CompilationTarget.encodeString(string.value.take(2), string.altEncoding)
|
||||
val chrout1 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val chrout2 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val anonscope = AnonymousScope(mutableListOf(), pos)
|
||||
anonscope.statements.add(chrout1)
|
||||
anonscope.statements.add(chrout2)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent))
|
||||
val string = vardecl.value as? StringLiteralValue
|
||||
if(string!=null) {
|
||||
val pos = functionCallStatement.position
|
||||
if (string.value.length == 1) {
|
||||
val firstCharEncoded = CompilationTarget.instance.encodeString(string.value, string.altEncoding)[0]
|
||||
val chrout = FunctionCallStatement(
|
||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||
} else if (string.value.length == 2) {
|
||||
val firstTwoCharsEncoded = CompilationTarget.instance.encodeString(string.value.take(2), string.altEncoding)
|
||||
val chrout1 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val chrout2 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("c64", "CHROUT"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val anonscope = AnonymousScope(mutableListOf(), pos)
|
||||
anonscope.statements.add(chrout1)
|
||||
anonscope.statements.add(chrout2)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, anonscope, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,7 +136,7 @@ internal class StatementOptimizer(private val program: Program,
|
||||
val subroutine = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is ReturnFromIrq || first is Return)
|
||||
if(first is Return)
|
||||
return listOf(IAstModification.Remove(functionCallStatement, parent))
|
||||
}
|
||||
|
||||
@ -211,7 +208,7 @@ internal class StatementOptimizer(private val program: Program,
|
||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||
// loopvar/reg = range value , follow by block
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, range.from, forLoop.position))
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), range.from, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
@ -223,23 +220,23 @@ internal class StatementOptimizer(private val program: Program,
|
||||
val size = sv.value.length
|
||||
if(size==1) {
|
||||
// loop over string of length 1 -> just assign the single character
|
||||
val character = CompilationTarget.encodeString(sv.value, sv.altEncoding)[0]
|
||||
val character = CompilationTarget.instance.encodeString(sv.value, sv.altEncoding)[0]
|
||||
val byte = NumericLiteralValue(DataType.UBYTE, character, iterable.position)
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, byte, forLoop.position))
|
||||
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
}
|
||||
else if(iterable.datatype in ArrayDatatypes) {
|
||||
val size = iterable.arraysize!!.size()
|
||||
val size = iterable.arraysize!!.constIndex()
|
||||
if(size==1) {
|
||||
// loop over array of length 1 -> just assign the single value
|
||||
val av = (iterable.value as ArrayLiteralValue).value[0].constValue(program)?.number
|
||||
if(av!=null) {
|
||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||
scope.statements.add(Assignment(
|
||||
AssignTarget(forLoop.loopVar, null, null, forLoop.position), null, NumericLiteralValue.optimalInteger(av.toInt(), iterable.position),
|
||||
AssignTarget(forLoop.loopVar, null, null, forLoop.position), NumericLiteralValue.optimalInteger(av.toInt(), iterable.position),
|
||||
forLoop.position))
|
||||
scope.statements.addAll(forLoop.body.statements)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
@ -255,9 +252,9 @@ internal class StatementOptimizer(private val program: Program,
|
||||
val constvalue = untilLoop.untilCondition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
if(constvalue.asBooleanValue) {
|
||||
// always true -> keep only the statement block (if there are no continue and break statements)
|
||||
// always true -> keep only the statement block (if there are no break statements)
|
||||
errors.warn("condition is always true", untilLoop.untilCondition.position)
|
||||
if(!hasContinueOrBreak(untilLoop.body))
|
||||
if(!hasBreak(untilLoop.body))
|
||||
return listOf(IAstModification.ReplaceNode(untilLoop, untilLoop.body, parent))
|
||||
} else {
|
||||
// always false
|
||||
@ -296,8 +293,10 @@ internal class StatementOptimizer(private val program: Program,
|
||||
errors.warn("iterations is always 0, removed loop", iter.position)
|
||||
return listOf(IAstModification.Remove(repeatLoop, parent))
|
||||
}
|
||||
if (iterations == 1)
|
||||
if (iterations == 1) {
|
||||
errors.warn("iterations is always 1", iter.position)
|
||||
return listOf(IAstModification.ReplaceNode(repeatLoop, repeatLoop.body, parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
@ -324,21 +323,74 @@ internal class StatementOptimizer(private val program: Program,
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
if(assignment.aug_op!=null)
|
||||
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
// remove assignments to self
|
||||
val binExpr = assignment.value as? BinaryExpression
|
||||
if(binExpr!=null) {
|
||||
if(binExpr.left isSameAs assignment.target) {
|
||||
val rExpr = binExpr.right as? BinaryExpression
|
||||
if(rExpr!=null) {
|
||||
val op1 = binExpr.operator
|
||||
val op2 = rExpr.operator
|
||||
|
||||
if(rExpr.left is NumericLiteralValue && op2 in setOf("+", "*", "&", "|")) {
|
||||
// associative operator, make sure the constant numeric value is second (right)
|
||||
return listOf(IAstModification.SwapOperands(rExpr))
|
||||
}
|
||||
|
||||
val rNum = (rExpr.right as? NumericLiteralValue)?.number
|
||||
if(rNum!=null) {
|
||||
if (op1 == "+" || op1 == "-") {
|
||||
if (op2 == "+") {
|
||||
// A = A +/- B + N
|
||||
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
||||
val addConstant = Assignment(
|
||||
assignment.target,
|
||||
BinaryExpression(binExpr.left, "+", rExpr.right, rExpr.position),
|
||||
assignment.position
|
||||
)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||
IAstModification.InsertAfter(assignment, addConstant, parent))
|
||||
} else if (op2 == "-") {
|
||||
// A = A +/- B - N
|
||||
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
||||
val subConstant = Assignment(
|
||||
assignment.target,
|
||||
BinaryExpression(binExpr.left, "-", rExpr.right, rExpr.position),
|
||||
assignment.position
|
||||
)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||
IAstModification.InsertAfter(assignment, subConstant, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(binExpr.operator in associativeOperators && binExpr.right isSameAs assignment.target) {
|
||||
// associative operator, swap the operands so that the assignment target is first (left)
|
||||
// unless the other operand is the same in which case we don't swap (endless loop!)
|
||||
if (!(binExpr.left isSameAs binExpr.right))
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
if(assignment.target isSameAs assignment.value) {
|
||||
if(assignment.target.isNotMemory(program.namespace))
|
||||
return listOf(IAstModification.Remove(assignment, parent))
|
||||
// remove assignment to self
|
||||
return listOf(IAstModification.Remove(assignment, parent))
|
||||
}
|
||||
|
||||
val targetIDt = assignment.target.inferType(program, assignment)
|
||||
if(!targetIDt.isKnown)
|
||||
throw FatalAstException("can't infer type of assignment target")
|
||||
|
||||
|
||||
// optimize binary expressions a bit
|
||||
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
|
||||
val bexpr=assignment.value as? BinaryExpression
|
||||
@ -354,8 +406,8 @@ internal class StatementOptimizer(private val program: Program,
|
||||
if (cv == 0.0) {
|
||||
return listOf(IAstModification.Remove(assignment, parent))
|
||||
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
||||
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
||||
// replace by several INCs (a bit less when dealing with memory targets)
|
||||
if (vardeclDt != VarDeclType.MEMORY && cv in 1.0..4.0) {
|
||||
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
||||
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
||||
repeat(cv.toInt()) {
|
||||
incs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position))
|
||||
@ -368,8 +420,8 @@ internal class StatementOptimizer(private val program: Program,
|
||||
if (cv == 0.0) {
|
||||
return listOf(IAstModification.Remove(assignment, parent))
|
||||
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
||||
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
||||
// replace by several DECs (a bit less when dealing with memory targets)
|
||||
if (vardeclDt != VarDeclType.MEMORY && cv in 1.0..4.0) {
|
||||
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||
repeat(cv.toInt()) {
|
||||
decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position))
|
||||
@ -386,28 +438,10 @@ internal class StatementOptimizer(private val program: Program,
|
||||
"<<" -> {
|
||||
if (cv == 0.0)
|
||||
return listOf(IAstModification.Remove(assignment, parent))
|
||||
// replace by in-place lsl(...) call
|
||||
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
||||
var numshifts = cv.toInt()
|
||||
while (numshifts > 0) {
|
||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsl"), assignment.position),
|
||||
mutableListOf(bexpr.left), true, assignment.position))
|
||||
numshifts--
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(assignment, scope, parent))
|
||||
}
|
||||
">>" -> {
|
||||
if (cv == 0.0)
|
||||
return listOf(IAstModification.Remove(assignment, parent))
|
||||
// replace by in-place lsr(...) call
|
||||
val scope = AnonymousScope(mutableListOf(), assignment.position)
|
||||
var numshifts = cv.toInt()
|
||||
while (numshifts > 0) {
|
||||
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("lsr"), assignment.position),
|
||||
mutableListOf(bexpr.left), true, assignment.position))
|
||||
numshifts--
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(assignment, scope, parent))
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,7 +475,7 @@ internal class StatementOptimizer(private val program: Program,
|
||||
return linesToRemove
|
||||
}
|
||||
|
||||
private fun hasContinueOrBreak(scope: INameScope): Boolean {
|
||||
private fun hasBreak(scope: INameScope): Boolean {
|
||||
|
||||
class Searcher: IAstVisitor
|
||||
{
|
||||
@ -450,10 +484,6 @@ internal class StatementOptimizer(private val program: Program,
|
||||
override fun visit(breakStmt: Break) {
|
||||
count++
|
||||
}
|
||||
|
||||
override fun visit(contStmt: Continue) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
val s=Searcher()
|
||||
|
@ -1,13 +1,14 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ErrorReporter
|
||||
import prog8.ast.processing.AstWalker
|
||||
import prog8.ast.processing.IAstModification
|
||||
import prog8.ast.statements.Block
|
||||
import prog8.ast.statements.*
|
||||
|
||||
|
||||
internal class UnusedCodeRemover: AstWalker() {
|
||||
internal class UnusedCodeRemover(private val errors: ErrorReporter): AstWalker() {
|
||||
|
||||
override fun before(program: Program, parent: Node): Iterable<IAstModification> {
|
||||
val callgraph = CallGraph(program)
|
||||
@ -17,8 +18,9 @@ internal class UnusedCodeRemover: AstWalker() {
|
||||
val entrypoint = program.entrypoint()
|
||||
program.modules.forEach {
|
||||
callgraph.forAllSubroutines(it) { sub ->
|
||||
if (sub !== entrypoint && !sub.keepAlways && (callgraph.calledBy[sub].isNullOrEmpty() || (sub.containsNoCodeNorVars() && !sub.isAsmSubroutine)))
|
||||
if (sub !== entrypoint && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
||||
removals.add(IAstModification.Remove(sub, sub.definingScope() as Node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,4 +37,33 @@ internal class UnusedCodeRemover: AstWalker() {
|
||||
|
||||
return removals
|
||||
}
|
||||
|
||||
|
||||
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(breakStmt, parent as INameScope)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun before(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(jump, parent as INameScope)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(returnStmt, parent as INameScope)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallStatement.target.nameInSource.last() == "exit")
|
||||
reportUnreachable(functionCallStatement, parent as INameScope)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private fun reportUnreachable(stmt: Statement, parent: INameScope) {
|
||||
when(val next = parent.nextSibling(stmt)) {
|
||||
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine, is StructDecl -> {}
|
||||
else -> errors.warn("unreachable code", next.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import org.antlr.v4.runtime.*
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.antlr.toAst
|
||||
import prog8.ast.base.ErrorReporter
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.base.checkImportedValid
|
||||
@ -34,7 +33,7 @@ internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexe
|
||||
internal fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
||||
|
||||
|
||||
internal class ModuleImporter(private val errors: ErrorReporter) {
|
||||
internal class ModuleImporter {
|
||||
|
||||
internal fun importModule(program: Program, filePath: Path): Module {
|
||||
print("importing '${moduleName(filePath.fileName)}'")
|
||||
@ -95,7 +94,7 @@ internal class ModuleImporter(private val errors: ErrorReporter) {
|
||||
|
||||
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
|
||||
val fileName = "$name.p8"
|
||||
val locations = mutableListOf(source.parent)
|
||||
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent)
|
||||
|
||||
val propPath = System.getProperty("prog8.libdir")
|
||||
if(propPath!=null)
|
||||
@ -110,7 +109,7 @@ internal class ModuleImporter(private val errors: ErrorReporter) {
|
||||
if (Files.isReadable(file)) return file
|
||||
}
|
||||
|
||||
throw ParsingFailedError("$position Import: no module source file '$fileName' found (I've looked in: $locations)")
|
||||
throw ParsingFailedError("$position Import: no module source file '$fileName' found (I've looked in: embedded libs and $locations)")
|
||||
}
|
||||
|
||||
private fun executeImportDirective(program: Program, import: Directive, source: Path): Module? {
|
||||
@ -129,10 +128,7 @@ internal class ModuleImporter(private val errors: ErrorReporter) {
|
||||
if(resource!=null) {
|
||||
// load the module from the embedded resource
|
||||
resource.use {
|
||||
if(import.args[0].int==42)
|
||||
println("importing '$moduleName' (library, auto)")
|
||||
else
|
||||
println("importing '$moduleName' (library)")
|
||||
println("importing '$moduleName' (library)")
|
||||
importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$moduleName"), true)
|
||||
}
|
||||
} else {
|
||||
|
@ -123,13 +123,13 @@ class TestCompiler {
|
||||
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class TestZeropage {
|
||||
class TestC64Zeropage {
|
||||
|
||||
private val errors = ErrorReporter()
|
||||
|
||||
@Test
|
||||
fun testNames() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, "c64"))
|
||||
|
||||
zp.allocate("", DataType.UBYTE, null, errors)
|
||||
zp.allocate("", DataType.UBYTE, null, errors)
|
||||
@ -142,37 +142,37 @@ class TestZeropage {
|
||||
|
||||
@Test
|
||||
fun testZpFloatEnable() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, "c64"))
|
||||
assertFailsWith<CompilerException> {
|
||||
zp.allocate("", DataType.FLOAT, null, errors)
|
||||
}
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, "c64"))
|
||||
assertFailsWith<CompilerException> {
|
||||
zp2.allocate("", DataType.FLOAT, null, errors)
|
||||
}
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, "c64"))
|
||||
zp3.allocate("", DataType.FLOAT, null, errors)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testZpModesWithFloats() {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, "c64"))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, "c64"))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, "c64"))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, "c64"))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, "c64"))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, "c64"))
|
||||
assertFailsWith<CompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, "c64"))
|
||||
}
|
||||
assertFailsWith<CompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, "c64"))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testZpDontuse() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, "c64"))
|
||||
println(zp.free)
|
||||
assertEquals(0, zp.available())
|
||||
assertFailsWith<CompilerException> {
|
||||
@ -182,19 +182,19 @@ class TestZeropage {
|
||||
|
||||
@Test
|
||||
fun testFreeSpaces() {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, "c64"))
|
||||
assertEquals(16, zp1.available())
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, "c64"))
|
||||
assertEquals(91, zp2.available())
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false))
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, "c64"))
|
||||
assertEquals(125, zp3.available())
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, "c64"))
|
||||
assertEquals(238, zp4.available())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReservedSpace() {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, "c64"))
|
||||
assertEquals(238, zp1.available())
|
||||
assertTrue(50 in zp1.free)
|
||||
assertTrue(100 in zp1.free)
|
||||
@ -203,7 +203,7 @@ class TestZeropage {
|
||||
assertTrue(200 in zp1.free)
|
||||
assertTrue(255 in zp1.free)
|
||||
assertTrue(199 in zp1.free)
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false))
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, "c64"))
|
||||
assertEquals(139, zp2.available())
|
||||
assertFalse(50 in zp2.free)
|
||||
assertFalse(100 in zp2.free)
|
||||
@ -216,7 +216,7 @@ class TestZeropage {
|
||||
|
||||
@Test
|
||||
fun testBasicsafeAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, "c64"))
|
||||
assertEquals(16, zp.available())
|
||||
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
@ -239,7 +239,7 @@ class TestZeropage {
|
||||
|
||||
@Test
|
||||
fun testFullAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, "c64"))
|
||||
assertEquals(238, zp.available())
|
||||
val loc = zp.allocate("", DataType.UWORD, null, errors)
|
||||
assertTrue(loc > 3)
|
||||
@ -269,7 +269,7 @@ class TestZeropage {
|
||||
|
||||
@Test
|
||||
fun testEfficientAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, "c64"))
|
||||
assertEquals(16, zp.available())
|
||||
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
||||
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
||||
|
@ -83,7 +83,7 @@ For normal use the compiler is invoked with the command:
|
||||
By default, assembly code is generated and written to ``sourcefile.asm``.
|
||||
It is then (automatically) fed to the `64tass <https://sourceforge.net/projects/tass64/>`_ cross assembler tool
|
||||
that assembles it into the final program.
|
||||
If you use the option to let the compiler auto-start a C-64 emulator, it will do so after
|
||||
If you use the option to let the compiler auto-start an emulator, it will do so after
|
||||
a successful compilation. This will load your program and the symbol and breakpoint lists
|
||||
(for the machine code monitor) into the emulator.
|
||||
|
||||
@ -109,8 +109,8 @@ A module source file is a text file with the ``.p8`` suffix, containing the prog
|
||||
It consists of compilation options and other directives, imports of other modules,
|
||||
and source code for one or more code blocks.
|
||||
|
||||
Prog8 has a couple of *LIBRARY* modules that are defined in special internal files provided by the compiler:
|
||||
``c64lib``, ``c64utils``, ``c64flt`` and ``prog8lib``. You should not overwrite these or reuse their names.
|
||||
Prog8 has various *LIBRARY* modules that are defined in special internal files provided by the compiler.
|
||||
You should not overwrite these or reuse their names.
|
||||
They are embedded into the packaged release version of the compiler so you don't have to worry about
|
||||
where they are, but their names are still reserved.
|
||||
|
||||
|
@ -12,6 +12,7 @@ What is Prog8?
|
||||
|
||||
This is an experimental compiled programming language targeting the 8-bit
|
||||
`6502 <https://en.wikipedia.org/wiki/MOS_Technology_6502>`_ /
|
||||
`65c02 <https://en.wikipedia.org/wiki/MOS_Technology_65C02>`_ /
|
||||
`6510 <https://en.wikipedia.org/wiki/MOS_Technology_6510>`_ microprocessor.
|
||||
This CPU is from the late 1970's and early 1980's and was used in many home computers from that era,
|
||||
such as the `Commodore-64 <https://en.wikipedia.org/wiki/Commodore_64>`_.
|
||||
@ -42,7 +43,7 @@ Code examples
|
||||
|
||||
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||
|
||||
%import c64utils
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -51,35 +52,33 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||
ubyte candidate_prime = 2
|
||||
|
||||
sub start() {
|
||||
memset(sieve, 256, false)
|
||||
|
||||
c64scr.print("prime numbers up to 255:\n\n")
|
||||
memset(sieve, 256, false) ; clear the sieve
|
||||
txt.print("prime numbers up to 255:\n\n")
|
||||
ubyte amount=0
|
||||
while true {
|
||||
repeat {
|
||||
ubyte prime = find_next_prime()
|
||||
if prime==0
|
||||
break
|
||||
c64scr.print_ub(prime)
|
||||
c64scr.print(", ")
|
||||
txt.print_ub(prime)
|
||||
txt.print(", ")
|
||||
amount++
|
||||
}
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("number of primes (expected 54): ")
|
||||
c64scr.print_ub(amount)
|
||||
txt.print("number of primes (expected 54): ")
|
||||
txt.print_ub(amount)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
|
||||
sub find_next_prime() -> ubyte {
|
||||
|
||||
while sieve[candidate_prime] {
|
||||
candidate_prime++
|
||||
if candidate_prime==0
|
||||
return 0
|
||||
return 0 ; we wrapped; no more primes available
|
||||
}
|
||||
|
||||
; found next one, mark the multiples and return it.
|
||||
sieve[candidate_prime] = true
|
||||
uword multiple = candidate_prime
|
||||
|
||||
while multiple < len(sieve) {
|
||||
sieve[lsb(multiple)] = true
|
||||
multiple += candidate_prime
|
||||
@ -89,6 +88,7 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||
}
|
||||
|
||||
|
||||
|
||||
when compiled an ran on a C-64 you get this:
|
||||
|
||||
.. image:: _static/primes_example.png
|
||||
@ -98,7 +98,8 @@ when compiled an ran on a C-64 you get this:
|
||||
|
||||
The following programs shows a use of the high level ``struct`` type::
|
||||
|
||||
%import c64utils
|
||||
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -111,22 +112,25 @@ The following programs shows a use of the high level ``struct`` type::
|
||||
|
||||
sub start() {
|
||||
|
||||
Color purple = {255, 0, 255}
|
||||
Color purple = [255, 0, 255]
|
||||
|
||||
Color other
|
||||
|
||||
other = purple
|
||||
other.red /= 2
|
||||
other.green = 10 + other.green / 2
|
||||
other.blue = 99
|
||||
|
||||
c64scr.print_ub(other.red)
|
||||
txt.print_ub(other.red)
|
||||
c64.CHROUT(',')
|
||||
c64scr.print_ub(other.green)
|
||||
txt.print_ub(other.green)
|
||||
c64.CHROUT(',')
|
||||
c64scr.print_ub(other.blue)
|
||||
txt.print_ub(other.blue)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
when compiled and ran, it prints ``127,10,99`` on the screen.
|
||||
|
||||
|
||||
@ -175,6 +179,7 @@ Fnd for Windows it's possible to get that as well. Check out `AdoptOpenJDK <http
|
||||
|
||||
Finally: a **C-64 emulator** (or a real C-64 ofcourse) can be nice to test and run your programs on.
|
||||
The compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
||||
If you're targeting the CommanderX16, there's the `x16emu <https://github.com/commanderx16/x16-emulator>`_.
|
||||
|
||||
.. important::
|
||||
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)
|
||||
|
@ -257,6 +257,16 @@ Note that the various keywords for the data type and variable type (``byte``, ``
|
||||
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
|
||||
for instance.
|
||||
|
||||
**Arrays at a specific memory location:**
|
||||
Using the memory-mapped syntax it is possible to define an array to be located at a specific memory location.
|
||||
For instance to reference the first 5 rows of the Commodore 64's screen matrix as an array, you can define::
|
||||
|
||||
&ubyte[5*40] top5screenrows = $0400
|
||||
|
||||
This way you can set the second character on the second row from the top like this::
|
||||
|
||||
top5screenrows[41] = '!'
|
||||
|
||||
|
||||
Strings
|
||||
^^^^^^^
|
||||
@ -397,6 +407,8 @@ The *repeat* loop is used as a short notation of a for loop where the loop varia
|
||||
|
||||
You can also create loops by using the ``goto`` statement, but this should usually be avoided.
|
||||
|
||||
Breaking out of a loop prematurely is possible with the ``break`` statement.
|
||||
|
||||
.. attention::
|
||||
The value of the loop variable after executing the loop *is undefined*. Don't use it immediately
|
||||
after the loop without first assigning a new value to it!
|
||||
@ -712,11 +724,17 @@ reverse(array)
|
||||
|
||||
len(x)
|
||||
Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte).
|
||||
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte.
|
||||
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. See sizeof().
|
||||
Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual
|
||||
length of the string during execution, the value of len(string) may no longer be correct!
|
||||
(use strlen function if you want to dynamically determine the length)
|
||||
|
||||
sizeof(name)
|
||||
Number of bytes that the object 'name' occupies in memory. This is a constant determined by the data type of
|
||||
the object. For instance, for a variable of type uword, the sizeof is 2.
|
||||
For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes).
|
||||
Note: usually you will be interested in the number of elements in an array, use len() for that.
|
||||
|
||||
strlen(str)
|
||||
Number of bytes in the string. This value is determined during runtime and counts upto
|
||||
the first terminating 0 byte in the string, regardless of the size of the string during compilation time.
|
||||
@ -730,8 +748,9 @@ msb(x)
|
||||
sgn(x)
|
||||
Get the sign of the value. Result is -1, 0 or 1 (negative, zero, positive).
|
||||
|
||||
mkword(lsb, msb)
|
||||
Efficiently create a word value from two bytes (the lsb and the msb). Avoids multiplication and shifting.
|
||||
mkword(msb, lsb)
|
||||
Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting.
|
||||
So mkword($80, $22) results in $8022.
|
||||
|
||||
any(x)
|
||||
1 ('true') if any of the values in the array value x is 'true' (not zero), else 0 ('false')
|
||||
@ -748,16 +767,6 @@ rndw()
|
||||
rndf()
|
||||
returns a pseudo-random float between 0.0 and 1.0
|
||||
|
||||
lsl(x)
|
||||
Shift the bits in x (byte or word) one position to the left.
|
||||
Bit 0 is set to 0 (and the highest bit is shifted into the status register's Carry flag)
|
||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||
|
||||
lsr(x)
|
||||
Shift the bits in x (byte or word) one position to the right.
|
||||
The highest bit is set to 0 (and bit 0 is shifted into the status register's Carry flag)
|
||||
Modifies in-place, doesn't return a value (so can't be used in an expression).
|
||||
|
||||
rol(x)
|
||||
Rotate the bits in x (byte or word) one position to the left.
|
||||
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,
|
||||
@ -794,7 +803,7 @@ memset(address, numbytes, bytevalue)
|
||||
Efficiently set a part of memory to the given (u)byte value.
|
||||
But the most efficient will always be to write a specialized fill routine in assembly yourself!
|
||||
Note that for clearing the character screen, very fast specialized subroutines are
|
||||
available in the ``c64scr`` block (part of the ``c64utils`` module)
|
||||
available in the ``screen`` block (part of the ``c64textio`` or ``cx16textio`` modules)
|
||||
|
||||
memsetw(address, numwords, wordvalue)
|
||||
Efficiently set a part of memory to the given (u)word value.
|
||||
|
@ -33,6 +33,14 @@ This makes it easier to understand and relate the generated code. Examples::
|
||||
Directives
|
||||
-----------
|
||||
|
||||
.. data:: %target <target>
|
||||
|
||||
Level: module.
|
||||
Global setting, selects a compilation target from within the source file.
|
||||
The default compilation target is "c64" which targets the Commodore-64 machine.
|
||||
You can also omit this and use the ``-target`` command line option.
|
||||
|
||||
|
||||
.. data:: %output <type>
|
||||
|
||||
Level: module.
|
||||
@ -157,7 +165,7 @@ Directives
|
||||
Identifiers
|
||||
-----------
|
||||
|
||||
Naming things in Prog8 is done via valid *identifiers*. They start with a letter or underscore,
|
||||
Naming things in Prog8 is done via valid *identifiers*. They start with a letter,
|
||||
and after that, a combination of letters, numbers, or underscores. Examples of valid identifiers::
|
||||
|
||||
a
|
||||
@ -165,7 +173,7 @@ and after that, a combination of letters, numbers, or underscores. Examples of v
|
||||
monkey
|
||||
COUNTER
|
||||
Better_Name_2
|
||||
_something_strange_
|
||||
something_strange__
|
||||
|
||||
|
||||
Code blocks
|
||||
@ -306,6 +314,7 @@ should be allocated by the compiler. Instead, the (mandatory) value assigned to
|
||||
should be the *memory address* where the value is located::
|
||||
|
||||
&byte BORDERCOLOR = $d020
|
||||
&ubyte[5*40] top5screenrows = $0400 ; works for array as well
|
||||
|
||||
|
||||
Direct access to memory locations
|
||||
@ -404,7 +413,7 @@ assignment: ``=``
|
||||
Note that an assignment sometimes is not possible or supported.
|
||||
|
||||
augmented assignment: ``+=`` ``-=`` ``*=`` ``/=`` ``**=`` ``&=`` ``|=`` ``^=`` ``<<=`` ``>>=``
|
||||
Syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx``
|
||||
This is syntactic sugar; ``aa += xx`` is equivalent to ``aa = aa + xx``
|
||||
|
||||
postfix increment and decrement: ``++`` ``--``
|
||||
Syntactic sugar; ``aa++`` is equivalent to ``aa = aa + 1``, and ``aa--`` is equivalent to ``aa = aa - 1``.
|
||||
@ -513,18 +522,20 @@ and returning stuff in several registers as well. The ``clobbers`` clause is use
|
||||
what CPU registers are clobbered by the call instead of being unchanged or returning a meaningful result value.
|
||||
|
||||
|
||||
Subroutines that are implemented purely in assembly code and which have an assembly calling convention (i.e.
|
||||
the parameters are strictly passed via cpu registers), are defined like this::
|
||||
User subroutines in the program source code that are implemented purely in assembly and which have an assembly calling convention (i.e.
|
||||
the parameters are strictly passed via cpu registers), are defined with ``asmsub`` like this::
|
||||
|
||||
asmsub FREADS32() clobbers(A,X,Y) {
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
%asm {{
|
||||
lda $62
|
||||
eor #$ff
|
||||
asl a
|
||||
lda #0
|
||||
ldx #$a0
|
||||
jmp $bc4f
|
||||
}}
|
||||
ldy #0
|
||||
_loop sta c64.Screen,y
|
||||
sta c64.Screen+$0100,y
|
||||
sta c64.Screen+$0200,y
|
||||
sta c64.Screen+$02e8,y
|
||||
iny
|
||||
bne _loop
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
the statement body of such a subroutine should consist of just an inline assembly block.
|
||||
@ -559,7 +570,6 @@ You can use a single statement, or a statement block like in the example below::
|
||||
for <loopvar> in <expression> [ step <amount> ] {
|
||||
; do something...
|
||||
break ; break out of the loop
|
||||
continue ; immediately enter next iteration
|
||||
}
|
||||
|
||||
For example, this is a for loop using a byte variable ``i``, defined before, to loop over a certain range of numbers::
|
||||
@ -592,7 +602,6 @@ You can use a single statement, or a statement block like in the example below::
|
||||
while <condition> {
|
||||
; do something...
|
||||
break ; break out of the loop
|
||||
continue ; immediately enter next iteration
|
||||
}
|
||||
|
||||
|
||||
@ -605,7 +614,6 @@ You can use a single statement, or a statement block like in the example below::
|
||||
do {
|
||||
; do something...
|
||||
break ; break out of the loop
|
||||
continue ; immediately enter next iteration
|
||||
} until <condition>
|
||||
|
||||
|
||||
|
@ -4,12 +4,17 @@ Target system specification
|
||||
|
||||
Prog8 targets the following hardware:
|
||||
|
||||
- 8 bit MOS 6502/6510 CPU
|
||||
- 8 bit MOS 6502/65c02/6510 CPU
|
||||
- 64 Kb addressable memory (RAM or ROM)
|
||||
- memory mapped I/O registers
|
||||
- optional use of memory mapped I/O registers
|
||||
- optional use of system ROM routines
|
||||
|
||||
The main target machine is the Commodore-64, which is an example of this.
|
||||
This chapter explains the relevant system details of such a machine.
|
||||
Currently there are two machines that are supported as compiler target (via the ``-target`` compiler argument):
|
||||
|
||||
- 'c64': the well-known Commodore-64, premium support
|
||||
- 'cx16': the `CommanderX16 <https://www.commanderx16.com/>`_ a project from the 8-Bit Guy. Support for this is still experimental.
|
||||
|
||||
This chapter explains the relevant system details of these machines.
|
||||
|
||||
|
||||
Memory Model
|
||||
@ -147,15 +152,15 @@ You can however install your own IRQ handler.
|
||||
This is possible ofcourse by doing it all using customized inline assembly,
|
||||
but there are a few library routines available to make setting up C-64 IRQs and raster IRQs a lot easier (no assembly code required).
|
||||
|
||||
These routines are::
|
||||
For the C64 these routines are::
|
||||
|
||||
c64utils.set_irqvec()
|
||||
c64utils.set_irqvec_excl()
|
||||
c64.set_irqvec()
|
||||
c64.set_irqvec_excl()
|
||||
|
||||
c64utils.set_rasterirq( <raster line> )
|
||||
c64utils.set_rasterirq_excl( <raster line> )
|
||||
c64.set_rasterirq( <raster line> )
|
||||
c64.set_rasterirq_excl( <raster line> )
|
||||
|
||||
c64utils.restore_irqvec() ; set it back to the systems default irq handler
|
||||
c64.restore_irqvec() ; set it back to the systems default irq handler
|
||||
|
||||
If you activate an IRQ handler with one of these, it expects the handler to be defined
|
||||
as a subroutine ``irq`` in the module ``irq`` so like this::
|
||||
|
@ -2,10 +2,15 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- finalize (most) of the still missing "new" assignment asm code generation
|
||||
- aliases for imported symbols for example perhaps '%alias print = c64scr.print'
|
||||
- option to load library files from a directory instead of the embedded ones (easier library development/debugging)
|
||||
- investigate support for 8bitguy's Commander X16 platform https://www.commanderx16.com and https://github.com/commanderx16/x16-docs
|
||||
- get rid of all other TODO's in the code ;-)
|
||||
- line-circle-gfx examples are now a few hundred bytes larger than before. Why is that, can it be fixed?
|
||||
- compiler errors and warnings in standard format so the IDE shows them as clickable links; ./test.asm:2578:3: blablabla
|
||||
- further optimize assignment codegeneration
|
||||
- auto select correct library to import based on target, instead of having c64- and cx16- prefix variants
|
||||
- implement @stack for asmsub parameters
|
||||
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as '_'
|
||||
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
|
||||
- aliases for imported symbols for example perhaps '%alias print = c64scr.print' ?
|
||||
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
|
||||
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%zeropage dontuse
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
@ -20,90 +19,90 @@ main {
|
||||
|
||||
; LEN/STRLEN
|
||||
ubyte length = len(name)
|
||||
if length!=5 c64scr.print("error len1\n")
|
||||
if length!=5 txt.print("error len1\n")
|
||||
length = len(uwarr)
|
||||
if length!=5 c64scr.print("error len2\n")
|
||||
if length!=5 txt.print("error len2\n")
|
||||
length=strlen(name)
|
||||
if length!=5 c64scr.print("error strlen1\n")
|
||||
if length!=5 txt.print("error strlen1\n")
|
||||
name[3] = 0
|
||||
length=strlen(name)
|
||||
if length!=3 c64scr.print("error strlen2\n")
|
||||
if length!=3 txt.print("error strlen2\n")
|
||||
|
||||
; MAX
|
||||
ub = max(ubarr)
|
||||
if ub!=199 c64scr.print("error max1\n")
|
||||
if ub!=199 txt.print("error max1\n")
|
||||
bb = max(barr)
|
||||
if bb!=99 c64scr.print("error max2\n")
|
||||
if bb!=99 txt.print("error max2\n")
|
||||
uw = max(uwarr)
|
||||
if uw!=4444 c64scr.print("error max3\n")
|
||||
if uw!=4444 txt.print("error max3\n")
|
||||
ww = max(warr)
|
||||
if ww!=999 c64scr.print("error max4\n")
|
||||
if ww!=999 txt.print("error max4\n")
|
||||
ff = max(farr)
|
||||
if ff!=999.9 c64scr.print("error max5\n")
|
||||
if ff!=999.9 txt.print("error max5\n")
|
||||
|
||||
; MIN
|
||||
ub = min(ubarr)
|
||||
if ub!=0 c64scr.print("error min1\n")
|
||||
if ub!=0 txt.print("error min1\n")
|
||||
bb = min(barr)
|
||||
if bb!=-122 c64scr.print("error min2\n")
|
||||
if bb!=-122 txt.print("error min2\n")
|
||||
uw = min(uwarr)
|
||||
if uw!=0 c64scr.print("error min3\n")
|
||||
if uw!=0 txt.print("error min3\n")
|
||||
ww = min(warr)
|
||||
if ww!=-4444 c64scr.print("error min4\n")
|
||||
if ww!=-4444 txt.print("error min4\n")
|
||||
ff = min(farr)
|
||||
if ff!=-4444.4 c64scr.print("error min5\n")
|
||||
if ff!=-4444.4 txt.print("error min5\n")
|
||||
|
||||
; SUM
|
||||
uw = sum(ubarr)
|
||||
if uw!=420 c64scr.print("error sum1\n")
|
||||
if uw!=420 txt.print("error sum1\n")
|
||||
ww = sum(barr)
|
||||
if ww!=-101 c64scr.print("error sum2\n")
|
||||
if ww!=-101 txt.print("error sum2\n")
|
||||
uw = sum(uwarr)
|
||||
if uw!=6665 c64scr.print("error sum3\n")
|
||||
if uw!=6665 txt.print("error sum3\n")
|
||||
ww = sum(warr)
|
||||
if ww!=-4223 c64scr.print("error sum4\n")
|
||||
if ww!=-4223 txt.print("error sum4\n")
|
||||
ff = sum(farr)
|
||||
if ff!=-4222.4 c64scr.print("error sum5\n")
|
||||
if ff!=-4222.4 txt.print("error sum5\n")
|
||||
|
||||
; ANY
|
||||
ub = any(ubarr)
|
||||
if ub==0 c64scr.print("error any1\n")
|
||||
if ub==0 txt.print("error any1\n")
|
||||
ub = any(barr)
|
||||
if ub==0 c64scr.print("error any2\n")
|
||||
if ub==0 txt.print("error any2\n")
|
||||
ub = any(uwarr)
|
||||
if ub==0 c64scr.print("error any3\n")
|
||||
if ub==0 txt.print("error any3\n")
|
||||
ub = any(warr)
|
||||
if ub==0 c64scr.print("error any4\n")
|
||||
if ub==0 txt.print("error any4\n")
|
||||
ub = any(farr)
|
||||
if ub==0 c64scr.print("error any5\n")
|
||||
if ub==0 txt.print("error any5\n")
|
||||
|
||||
; ALL
|
||||
ub = all(ubarr)
|
||||
if ub==1 c64scr.print("error all1\n")
|
||||
if ub==1 txt.print("error all1\n")
|
||||
ub = all(barr)
|
||||
if ub==1 c64scr.print("error all2\n")
|
||||
if ub==1 txt.print("error all2\n")
|
||||
ub = all(uwarr)
|
||||
if ub==1 c64scr.print("error all3\n")
|
||||
if ub==1 txt.print("error all3\n")
|
||||
ub = all(warr)
|
||||
if ub==1 c64scr.print("error all4\n")
|
||||
if ub==1 txt.print("error all4\n")
|
||||
ub = all(farr)
|
||||
if ub==1 c64scr.print("error all5\n")
|
||||
if ub==1 txt.print("error all5\n")
|
||||
ubarr[1]=$40
|
||||
barr[1]=$40
|
||||
uwarr[1]=$4000
|
||||
warr[1]=$4000
|
||||
farr[1]=1.1
|
||||
ub = all(ubarr)
|
||||
if ub==0 c64scr.print("error all6\n")
|
||||
if ub==0 txt.print("error all6\n")
|
||||
ub = all(barr)
|
||||
if ub==0 c64scr.print("error all7\n")
|
||||
if ub==0 txt.print("error all7\n")
|
||||
ub = all(uwarr)
|
||||
if ub==0 c64scr.print("error all8\n")
|
||||
if ub==0 txt.print("error all8\n")
|
||||
ub = all(warr)
|
||||
if ub==0 c64scr.print("error all9\n")
|
||||
if ub==0 txt.print("error all9\n")
|
||||
ub = all(farr)
|
||||
if ub==0 c64scr.print("error all10\n")
|
||||
if ub==0 txt.print("error all10\n")
|
||||
|
||||
c64scr.print("\nyou should see no errors printed above (only at first run).")
|
||||
txt.print("\nyou should see no errors printed above (only at first run).")
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
%import c64utils
|
||||
%zeropage dontuse
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
@ -7,379 +7,379 @@ main {
|
||||
sub start() {
|
||||
ubyte A
|
||||
|
||||
c64scr.print("ubyte shift left\n")
|
||||
txt.print("ubyte shift left\n")
|
||||
A = shiftlb0()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftlb1()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftlb2()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftlb3()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftlb4()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftlb5()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftlb6()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftlb7()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftlb8()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftlb9()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("enter to continue:\n")
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
|
||||
c64scr.print("ubyte shift right\n")
|
||||
txt.print("ubyte shift right\n")
|
||||
A = shiftrb0()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftrb1()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftrb2()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftrb3()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftrb4()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftrb5()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftrb6()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftrb7()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftrb8()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
A = shiftrb9()
|
||||
c64scr.print_ubbin(A, true)
|
||||
txt.print_ubbin(A, true)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("enter to continue:\n")
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
|
||||
|
||||
|
||||
|
||||
c64scr.print("signed byte shift left\n")
|
||||
txt.print("signed byte shift left\n")
|
||||
byte signedb
|
||||
signedb = shiftlsb0()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftlsb1()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftlsb2()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftlsb3()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftlsb4()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftlsb5()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftlsb6()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftlsb7()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftlsb8()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftlsb9()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("enter to continue:\n")
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
|
||||
c64scr.print("signed byte shift right\n")
|
||||
txt.print("signed byte shift right\n")
|
||||
signedb = shiftrsb0()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftrsb1()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftrsb2()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftrsb3()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftrsb4()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftrsb5()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftrsb6()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftrsb7()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftrsb8()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
signedb = shiftrsb9()
|
||||
c64scr.print_ubbin(signedb as ubyte, true)
|
||||
txt.print_ubbin(signedb as ubyte, true)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("enter to continue:\n")
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
|
||||
|
||||
|
||||
|
||||
c64scr.print("uword shift left\n")
|
||||
txt.print("uword shift left\n")
|
||||
uword uw
|
||||
uw = shiftluw0()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw1()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw2()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw3()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw4()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw5()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw6()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw7()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw8()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw9()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw10()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw11()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw12()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw13()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw14()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw15()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw16()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftluw17()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("enter to continue:\n")
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
|
||||
c64scr.print("uword shift right\n")
|
||||
txt.print("uword shift right\n")
|
||||
uw = shiftruw0()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw1()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw2()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw3()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw4()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw5()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw6()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw7()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw8()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw9()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw10()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw11()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw12()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw13()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw14()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw15()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw16()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
uw = shiftruw17()
|
||||
c64scr.print_uwbin(uw, true)
|
||||
txt.print_uwbin(uw, true)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("enter to continue:\n")
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
|
||||
c64scr.print("signed word shift left\n")
|
||||
txt.print("signed word shift left\n")
|
||||
word sw
|
||||
sw = shiftlsw0()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw1()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw2()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw3()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw4()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw5()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw6()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw7()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw8()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw9()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw10()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw11()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw12()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw13()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw14()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw15()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw16()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftlsw17()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print("enter to continue:\n")
|
||||
txt.print("enter to continue:\n")
|
||||
void c64.CHRIN()
|
||||
|
||||
c64scr.print("signed word shift right\n")
|
||||
txt.print("signed word shift right\n")
|
||||
sw = shiftrsw0()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw1()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw2()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw3()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw4()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw5()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw6()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw7()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw8()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw9()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw10()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw11()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw12()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw13()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw14()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw15()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw16()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
sw = shiftrsw17()
|
||||
c64scr.print_uwbin(sw as uword, true)
|
||||
txt.print_uwbin(sw as uword, true)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -29,75 +28,75 @@ main {
|
||||
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||
ubyte r = a1/a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("ubyte ")
|
||||
c64scr.print_ub(a1)
|
||||
c64scr.print(" / ")
|
||||
c64scr.print_ub(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_ub(r)
|
||||
txt.print("err! ")
|
||||
txt.print("ubyte ")
|
||||
txt.print_ub(a1)
|
||||
txt.print(" / ")
|
||||
txt.print_ub(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_ub(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub div_byte(byte a1, byte a2, byte c) {
|
||||
byte r = a1/a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("byte ")
|
||||
c64scr.print_b(a1)
|
||||
c64scr.print(" / ")
|
||||
c64scr.print_b(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_b(r)
|
||||
txt.print("err! ")
|
||||
txt.print("byte ")
|
||||
txt.print_b(a1)
|
||||
txt.print(" / ")
|
||||
txt.print_b(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_b(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub div_uword(uword a1, uword a2, uword c) {
|
||||
uword r = a1/a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("uword ")
|
||||
c64scr.print_uw(a1)
|
||||
c64scr.print(" / ")
|
||||
c64scr.print_uw(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_uw(r)
|
||||
txt.print("err! ")
|
||||
txt.print("uword ")
|
||||
txt.print_uw(a1)
|
||||
txt.print(" / ")
|
||||
txt.print_uw(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_uw(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub div_word(word a1, word a2, word c) {
|
||||
word r = a1/a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("word ")
|
||||
c64scr.print_w(a1)
|
||||
c64scr.print(" / ")
|
||||
c64scr.print_w(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_w(r)
|
||||
txt.print("err! ")
|
||||
txt.print("word ")
|
||||
txt.print_w(a1)
|
||||
txt.print(" / ")
|
||||
txt.print_w(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_w(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub div_float(float a1, float a2, float c) {
|
||||
float r = a1/a2
|
||||
if abs(r-c)<0.00001
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
txt.print("err! ")
|
||||
|
||||
c64scr.print("float ")
|
||||
txt.print("float ")
|
||||
c64flt.print_f(a1)
|
||||
c64scr.print(" / ")
|
||||
txt.print(" / ")
|
||||
c64flt.print_f(a2)
|
||||
c64scr.print(" = ")
|
||||
txt.print(" = ")
|
||||
c64flt.print_f(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -37,75 +36,75 @@ main {
|
||||
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||
ubyte r = a1-a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("ubyte ")
|
||||
c64scr.print_ub(a1)
|
||||
c64scr.print(" - ")
|
||||
c64scr.print_ub(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_ub(r)
|
||||
txt.print("err! ")
|
||||
txt.print("ubyte ")
|
||||
txt.print_ub(a1)
|
||||
txt.print(" - ")
|
||||
txt.print_ub(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_ub(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub minus_byte(byte a1, byte a2, byte c) {
|
||||
byte r = a1-a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("byte ")
|
||||
c64scr.print_b(a1)
|
||||
c64scr.print(" - ")
|
||||
c64scr.print_b(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_b(r)
|
||||
txt.print("err! ")
|
||||
txt.print("byte ")
|
||||
txt.print_b(a1)
|
||||
txt.print(" - ")
|
||||
txt.print_b(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_b(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub minus_uword(uword a1, uword a2, uword c) {
|
||||
uword r = a1-a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("uword ")
|
||||
c64scr.print_uw(a1)
|
||||
c64scr.print(" - ")
|
||||
c64scr.print_uw(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_uw(r)
|
||||
txt.print("err! ")
|
||||
txt.print("uword ")
|
||||
txt.print_uw(a1)
|
||||
txt.print(" - ")
|
||||
txt.print_uw(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_uw(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub minus_word(word a1, word a2, word c) {
|
||||
word r = a1-a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("word ")
|
||||
c64scr.print_w(a1)
|
||||
c64scr.print(" - ")
|
||||
c64scr.print_w(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_w(r)
|
||||
txt.print("err! ")
|
||||
txt.print("word ")
|
||||
txt.print_w(a1)
|
||||
txt.print(" - ")
|
||||
txt.print_w(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_w(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub minus_float(float a1, float a2, float c) {
|
||||
float r = a1-a2
|
||||
if abs(r-c)<0.00001
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
txt.print("err! ")
|
||||
|
||||
c64scr.print("float ")
|
||||
txt.print("float ")
|
||||
c64flt.print_f(a1)
|
||||
c64scr.print(" - ")
|
||||
txt.print(" - ")
|
||||
c64flt.print_f(a2)
|
||||
c64scr.print(" = ")
|
||||
txt.print(" = ")
|
||||
c64flt.print_f(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -31,75 +30,75 @@ main {
|
||||
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||
ubyte r = a1*a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("ubyte ")
|
||||
c64scr.print_ub(a1)
|
||||
c64scr.print(" * ")
|
||||
c64scr.print_ub(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_ub(r)
|
||||
txt.print("err! ")
|
||||
txt.print("ubyte ")
|
||||
txt.print_ub(a1)
|
||||
txt.print(" * ")
|
||||
txt.print_ub(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_ub(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub mul_byte(byte a1, byte a2, byte c) {
|
||||
byte r = a1*a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("byte ")
|
||||
c64scr.print_b(a1)
|
||||
c64scr.print(" * ")
|
||||
c64scr.print_b(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_b(r)
|
||||
txt.print("err! ")
|
||||
txt.print("byte ")
|
||||
txt.print_b(a1)
|
||||
txt.print(" * ")
|
||||
txt.print_b(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_b(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub mul_uword(uword a1, uword a2, uword c) {
|
||||
uword r = a1*a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("uword ")
|
||||
c64scr.print_uw(a1)
|
||||
c64scr.print(" * ")
|
||||
c64scr.print_uw(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_uw(r)
|
||||
txt.print("err! ")
|
||||
txt.print("uword ")
|
||||
txt.print_uw(a1)
|
||||
txt.print(" * ")
|
||||
txt.print_uw(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_uw(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub mul_word(word a1, word a2, word c) {
|
||||
word r = a1*a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("word ")
|
||||
c64scr.print_w(a1)
|
||||
c64scr.print(" * ")
|
||||
c64scr.print_w(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_w(r)
|
||||
txt.print("err! ")
|
||||
txt.print("word ")
|
||||
txt.print_w(a1)
|
||||
txt.print(" * ")
|
||||
txt.print_w(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_w(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub mul_float(float a1, float a2, float c) {
|
||||
float r = a1*a2
|
||||
if abs(r-c)<0.00001
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
txt.print("err! ")
|
||||
|
||||
c64scr.print("float ")
|
||||
txt.print("float ")
|
||||
c64flt.print_f(a1)
|
||||
c64scr.print(" * ")
|
||||
txt.print(" * ")
|
||||
c64flt.print_f(a2)
|
||||
c64scr.print(" = ")
|
||||
txt.print(" = ")
|
||||
c64flt.print_f(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -35,75 +34,75 @@ main {
|
||||
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||
ubyte r = a1+a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("ubyte ")
|
||||
c64scr.print_ub(a1)
|
||||
c64scr.print(" + ")
|
||||
c64scr.print_ub(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_ub(r)
|
||||
txt.print("err! ")
|
||||
txt.print("ubyte ")
|
||||
txt.print_ub(a1)
|
||||
txt.print(" + ")
|
||||
txt.print_ub(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_ub(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub plus_byte(byte a1, byte a2, byte c) {
|
||||
byte r = a1+a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("byte ")
|
||||
c64scr.print_b(a1)
|
||||
c64scr.print(" + ")
|
||||
c64scr.print_b(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_b(r)
|
||||
txt.print("err! ")
|
||||
txt.print("byte ")
|
||||
txt.print_b(a1)
|
||||
txt.print(" + ")
|
||||
txt.print_b(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_b(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub plus_uword(uword a1, uword a2, uword c) {
|
||||
uword r = a1+a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("uword ")
|
||||
c64scr.print_uw(a1)
|
||||
c64scr.print(" + ")
|
||||
c64scr.print_uw(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_uw(r)
|
||||
txt.print("err! ")
|
||||
txt.print("uword ")
|
||||
txt.print_uw(a1)
|
||||
txt.print(" + ")
|
||||
txt.print_uw(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_uw(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub plus_word(word a1, word a2, word c) {
|
||||
word r = a1+a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("word ")
|
||||
c64scr.print_w(a1)
|
||||
c64scr.print(" + ")
|
||||
c64scr.print_w(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_w(r)
|
||||
txt.print("err! ")
|
||||
txt.print("word ")
|
||||
txt.print_w(a1)
|
||||
txt.print(" + ")
|
||||
txt.print_w(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_w(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub plus_float(float a1, float a2, float c) {
|
||||
float r = a1+a2
|
||||
if abs(r-c)<0.00001
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
txt.print("err! ")
|
||||
|
||||
c64scr.print("float ")
|
||||
txt.print("float ")
|
||||
c64flt.print_f(a1)
|
||||
c64scr.print(" + ")
|
||||
txt.print(" + ")
|
||||
c64flt.print_f(a2)
|
||||
c64scr.print(" = ")
|
||||
txt.print(" = ")
|
||||
c64flt.print_f(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%option enable_floats
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
c64scr.plot(0,24)
|
||||
txt.plot(0,24)
|
||||
|
||||
ubyte Y
|
||||
ubyte ub=200
|
||||
@ -21,7 +20,7 @@ main {
|
||||
word[3] warr = -1000
|
||||
float[3] flarr = 999.99
|
||||
|
||||
c64scr.print("++\n")
|
||||
txt.print("++\n")
|
||||
ub++
|
||||
bb++
|
||||
uw++
|
||||
@ -52,7 +51,7 @@ main {
|
||||
check_uw(uwarr[1], 2001)
|
||||
check_w(warr[1], -999)
|
||||
|
||||
c64scr.print("--\n")
|
||||
txt.print("--\n")
|
||||
ub--
|
||||
bb--
|
||||
uw--
|
||||
@ -81,58 +80,58 @@ main {
|
||||
|
||||
sub check_ub(ubyte value, ubyte expected) {
|
||||
if value==expected
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print(" ubyte ")
|
||||
c64scr.print_ub(value)
|
||||
txt.print("err! ")
|
||||
txt.print(" ubyte ")
|
||||
txt.print_ub(value)
|
||||
c64.CHROUT(',')
|
||||
c64scr.print_ub(expected)
|
||||
txt.print_ub(expected)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub check_b(byte value, byte expected) {
|
||||
if value==expected
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print(" byte ")
|
||||
c64scr.print_b(value)
|
||||
txt.print("err! ")
|
||||
txt.print(" byte ")
|
||||
txt.print_b(value)
|
||||
c64.CHROUT(',')
|
||||
c64scr.print_b(expected)
|
||||
txt.print_b(expected)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub check_uw(uword value, uword expected) {
|
||||
if value==expected
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print(" uword ")
|
||||
c64scr.print_uw(value)
|
||||
txt.print("err! ")
|
||||
txt.print(" uword ")
|
||||
txt.print_uw(value)
|
||||
c64.CHROUT(',')
|
||||
c64scr.print_uw(expected)
|
||||
txt.print_uw(expected)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub check_w(word value, word expected) {
|
||||
if value==expected
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print(" word ")
|
||||
c64scr.print_w(value)
|
||||
txt.print("err! ")
|
||||
txt.print(" word ")
|
||||
txt.print_w(value)
|
||||
c64.CHROUT(',')
|
||||
c64scr.print_w(expected)
|
||||
txt.print_w(expected)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub check_fl(float value, float expected) {
|
||||
if value==expected
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print(" float ")
|
||||
txt.print("err! ")
|
||||
txt.print(" float ")
|
||||
c64flt.print_f(value)
|
||||
c64.CHROUT(',')
|
||||
c64flt.print_f(expected)
|
||||
|
@ -1,6 +1,4 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -20,30 +18,30 @@ main {
|
||||
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||
ubyte r = a1%a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("ubyte ")
|
||||
c64scr.print_ub(a1)
|
||||
c64scr.print(" % ")
|
||||
c64scr.print_ub(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_ub(r)
|
||||
txt.print("err! ")
|
||||
txt.print("ubyte ")
|
||||
txt.print_ub(a1)
|
||||
txt.print(" % ")
|
||||
txt.print_ub(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_ub(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
|
||||
sub remainder_uword(uword a1, uword a2, uword c) {
|
||||
uword r = a1%a2
|
||||
if r==c
|
||||
c64scr.print(" ok ")
|
||||
txt.print(" ok ")
|
||||
else
|
||||
c64scr.print("err! ")
|
||||
c64scr.print("uword ")
|
||||
c64scr.print_uw(a1)
|
||||
c64scr.print(" % ")
|
||||
c64scr.print_uw(a2)
|
||||
c64scr.print(" = ")
|
||||
c64scr.print_uw(r)
|
||||
txt.print("err! ")
|
||||
txt.print("uword ")
|
||||
txt.print_uw(a1)
|
||||
txt.print(" % ")
|
||||
txt.print_uw(a2)
|
||||
txt.print(" = ")
|
||||
txt.print_uw(r)
|
||||
c64.CHROUT('\n')
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
%import c64flt
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -18,113 +19,113 @@ main {
|
||||
b1 = 10
|
||||
b2 = 10
|
||||
if sgn(b2-b1) != 0
|
||||
c64scr.print("sgn1 error1\n")
|
||||
txt.print("sgn1 error1\n")
|
||||
|
||||
b1 = -100
|
||||
b2 = -100
|
||||
if sgn(b2-b1) != 0
|
||||
c64scr.print("sgn1 error2\n")
|
||||
txt.print("sgn1 error2\n")
|
||||
|
||||
ub1 = 200
|
||||
ub2 = 200
|
||||
if sgn(ub2-ub1) != 0
|
||||
c64scr.print("sgn1 error3\n")
|
||||
txt.print("sgn1 error3\n")
|
||||
|
||||
w1 = 100
|
||||
w2 = 100
|
||||
if sgn(w2-w1) != 0
|
||||
c64scr.print("sgn1 error4\n")
|
||||
txt.print("sgn1 error4\n")
|
||||
|
||||
w1 = -2000
|
||||
w2 = -2000
|
||||
if sgn(w2-w1) != 0
|
||||
c64scr.print("sgn1 error5\n")
|
||||
txt.print("sgn1 error5\n")
|
||||
|
||||
uw1 = 999
|
||||
uw2 = 999
|
||||
if sgn(uw2-uw1) != 0
|
||||
c64scr.print("sgn1 error6\n")
|
||||
txt.print("sgn1 error6\n")
|
||||
|
||||
f1 = 3.45
|
||||
f2 = 3.45
|
||||
if sgn(f2-f1) != 0
|
||||
c64scr.print("sgn1 error7\n")
|
||||
txt.print("sgn1 error7\n")
|
||||
|
||||
|
||||
; -1
|
||||
b1 = 11
|
||||
b2 = 10
|
||||
if sgn(b2-b1) != -1
|
||||
c64scr.print("sgn2 error1\n")
|
||||
txt.print("sgn2 error1\n")
|
||||
|
||||
b1 = -10
|
||||
b2 = -100
|
||||
if sgn(b2-b1) != -1
|
||||
c64scr.print("sgn2 error2\n")
|
||||
txt.print("sgn2 error2\n")
|
||||
|
||||
ub1 = 202
|
||||
ub2 = 200
|
||||
if sgn(ub2 as byte - ub1 as byte) != -1
|
||||
c64scr.print("sgn2 error3\n")
|
||||
txt.print("sgn2 error3\n")
|
||||
|
||||
w1 = 101
|
||||
w2 = 100
|
||||
if sgn(w2-w1) != -1
|
||||
c64scr.print("sgn2 error4\n")
|
||||
txt.print("sgn2 error4\n")
|
||||
|
||||
w1 = -200
|
||||
w2 = -2000
|
||||
if sgn(w2-w1) != -1
|
||||
c64scr.print("sgn2 error5\n")
|
||||
txt.print("sgn2 error5\n")
|
||||
|
||||
uw1 = 2222
|
||||
uw2 = 999
|
||||
if sgn((uw2 as word) - (uw1 as word)) != -1
|
||||
c64scr.print("sgn2 error6a\n")
|
||||
txt.print("sgn2 error6a\n")
|
||||
if sgn(uw2 - uw1) != 1 ; always 0 or 1 if unsigned
|
||||
c64scr.print("sgn2 error6b\n")
|
||||
txt.print("sgn2 error6b\n")
|
||||
|
||||
f1 = 3.45
|
||||
f2 = 1.11
|
||||
if sgn(f2-f1) != -1
|
||||
c64scr.print("sgn2 error7\n")
|
||||
txt.print("sgn2 error7\n")
|
||||
|
||||
; +1
|
||||
b1 = 11
|
||||
b2 = 20
|
||||
if sgn(b2-b1) != 1
|
||||
c64scr.print("sgn3 error1\n")
|
||||
txt.print("sgn3 error1\n")
|
||||
|
||||
b1 = -10
|
||||
b2 = -1
|
||||
if sgn(b2-b1) != 1
|
||||
c64scr.print("sgn3 error2\n")
|
||||
txt.print("sgn3 error2\n")
|
||||
|
||||
ub1 = 202
|
||||
ub2 = 205
|
||||
if sgn(ub2-ub1) != 1
|
||||
c64scr.print("sgn3 error3\n")
|
||||
txt.print("sgn3 error3\n")
|
||||
|
||||
w1 = 101
|
||||
w2 = 200
|
||||
if sgn(w2-w1) != 1
|
||||
c64scr.print("sgn3 error4\n")
|
||||
txt.print("sgn3 error4\n")
|
||||
|
||||
w1 = -200
|
||||
w2 = -20
|
||||
if sgn(w2-w1) != 1
|
||||
c64scr.print("sgn3 error5\n")
|
||||
txt.print("sgn3 error5\n")
|
||||
|
||||
uw1 = 2222
|
||||
uw2 = 9999
|
||||
if sgn(uw2-uw1) != 1
|
||||
c64scr.print("sgn3 error6\n")
|
||||
txt.print("sgn3 error6\n")
|
||||
|
||||
f1 = 3.45
|
||||
f2 = 5.11
|
||||
if sgn(f2-f1) != 1
|
||||
c64scr.print("sgn3 error7\n")
|
||||
txt.print("sgn3 error7\n")
|
||||
|
||||
c64scr.print("should see no sgn errors\n")
|
||||
txt.print("should see no sgn errors\n")
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
%import c64lib
|
||||
%import c64utils
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -15,7 +15,7 @@ main {
|
||||
|
||||
c64.SCROLX &= %11110111 ; 38 column mode
|
||||
|
||||
c64utils.set_rasterirq(1) ; enable animation
|
||||
c64.set_rasterirq(1) ; enable animation
|
||||
|
||||
ubyte target_height = 10
|
||||
ubyte active_height = 24
|
||||
@ -43,7 +43,7 @@ main {
|
||||
}
|
||||
|
||||
perform_scroll = false
|
||||
c64scr.scroll_left_full(true)
|
||||
txt.scroll_left_full(true)
|
||||
if c64.RASTER & 1
|
||||
c64.SPXY[1] ++
|
||||
else
|
||||
@ -51,17 +51,17 @@ main {
|
||||
|
||||
ubyte yy
|
||||
for yy in 0 to active_height-1 {
|
||||
c64scr.setcc(39, yy, 32, 2) ; clear top of screen
|
||||
txt.setcc(39, yy, 32, 2) ; clear top of screen
|
||||
}
|
||||
c64scr.setcc(39, active_height, mountain, 8) ; mountain edge
|
||||
txt.setcc(39, active_height, mountain, 8) ; mountain edge
|
||||
for yy in active_height+1 to 24 {
|
||||
c64scr.setcc(39, yy, 160, 8) ; draw mountain
|
||||
txt.setcc(39, yy, 160, 8) ; draw mountain
|
||||
}
|
||||
|
||||
yy = rnd()
|
||||
if yy > 100 {
|
||||
; draw a star
|
||||
c64scr.setcc(39, yy % (active_height-1), '.', rnd())
|
||||
txt.setcc(39, yy % (active_height-1), '.', rnd())
|
||||
}
|
||||
|
||||
if yy > 200 {
|
||||
@ -74,12 +74,12 @@ main {
|
||||
tree = 65
|
||||
if rnd() > 130
|
||||
treecolor = 13
|
||||
c64scr.setcc(39, active_height, tree, treecolor)
|
||||
txt.setcc(39, active_height, tree, treecolor)
|
||||
}
|
||||
|
||||
if yy > 235 {
|
||||
; draw a camel
|
||||
c64scr.setcc(39, active_height, 94, 9)
|
||||
txt.setcc(39, active_height, 94, 9)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
%zeropage basicsafe
|
||||
%import c64lib
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
c64scr.print("playing the music from boulderdash,\nmade in 1984 by peter liepa.\n\n")
|
||||
c64utils.set_rasterirq(60) ; enable raster irq
|
||||
txt.print("playing the music from boulderdash,\nmade in 1984 by peter liepa.\n\n")
|
||||
c64.set_rasterirq(60) ; enable raster irq
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
%import c64lib
|
||||
%import c64textio
|
||||
|
||||
main {
|
||||
|
||||
@ -12,7 +12,7 @@ sub start() {
|
||||
c64.SR2 = %00000000
|
||||
c64.MVOL = 15
|
||||
|
||||
c64scr.print("will play the music from boulderdash,\nmade in 1984 by peter liepa.\npress enter to start: ")
|
||||
txt.print("will play the music from boulderdash,\nmade in 1984 by peter liepa.\npress enter to start: ")
|
||||
void c64.CHRIN()
|
||||
c64.CLEARSCR()
|
||||
|
||||
@ -37,19 +37,20 @@ sub start() {
|
||||
}
|
||||
|
||||
sub delay() {
|
||||
repeat 32 {
|
||||
while c64.RASTER {
|
||||
repeat 8 {
|
||||
ubyte jiffy = c64.TIME_LO
|
||||
while c64.TIME_LO==jiffy {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub print_notes(ubyte n1, ubyte n2) {
|
||||
c64.CHROUT('\n')
|
||||
c64scr.plot(n1/2, 24)
|
||||
c64.COLOR=7
|
||||
txt.plot(n1/2, 24)
|
||||
txt.color(7)
|
||||
c64.CHROUT('Q')
|
||||
c64scr.plot(n2/2, 24)
|
||||
c64.COLOR=4
|
||||
txt.plot(n2/2, 24)
|
||||
txt.color(4)
|
||||
c64.CHROUT('Q')
|
||||
}
|
||||
|
||||
|
@ -1,252 +0,0 @@
|
||||
%import c64lib
|
||||
|
||||
; bitmap pixel graphics module for the C64
|
||||
; only black/white monchrome for now
|
||||
|
||||
; you could put this code at $4000 which is after the bitmap screen in memory ($2000-$3fff),
|
||||
; this leaves more space for user program code.
|
||||
|
||||
graphics {
|
||||
const uword bitmap_address = $2000
|
||||
|
||||
sub enable_bitmap_mode() {
|
||||
; enable bitmap screen, erase it and set colors to black/white.
|
||||
c64.SCROLY |= %00100000
|
||||
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
|
||||
memset(bitmap_address, 320*200/8, 0)
|
||||
c64scr.clear_screen($10, 0) ; pixel color $1 (white) backround $0 (black)
|
||||
}
|
||||
|
||||
sub line(uword x1, ubyte y1, uword x2, ubyte y2) {
|
||||
; Bresenham algorithm.
|
||||
; This code special cases various quadrant loops to allow simple ++ and -- operations.
|
||||
if y1>y2 {
|
||||
; make sure dy is always positive to avoid 8 instead of just 4 special cases
|
||||
swap(x1, x2)
|
||||
swap(y1, y2)
|
||||
}
|
||||
word d = 0
|
||||
ubyte positive_ix = true
|
||||
word dx = x2 - x1 as word
|
||||
word dy = y2 as word - y1 as word
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
positive_ix = false
|
||||
}
|
||||
dx *= 2
|
||||
dy *= 2
|
||||
plotx = x1
|
||||
|
||||
if dx >= dy {
|
||||
if positive_ix {
|
||||
repeat {
|
||||
plot(y1)
|
||||
if plotx==x2
|
||||
return
|
||||
plotx++
|
||||
d += dy
|
||||
if d > dx {
|
||||
y1++
|
||||
d -= dx
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
plot(y1)
|
||||
if plotx==x2
|
||||
return
|
||||
plotx--
|
||||
d += dy
|
||||
if d > dx {
|
||||
y1++
|
||||
d -= dx
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if positive_ix {
|
||||
repeat {
|
||||
plot(y1)
|
||||
if y1 == y2
|
||||
return
|
||||
y1++
|
||||
d += dx
|
||||
if d > dy {
|
||||
plotx++
|
||||
d -= dy
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
plot(y1)
|
||||
if y1 == y2
|
||||
return
|
||||
y1++
|
||||
d += dx
|
||||
if d > dy {
|
||||
plotx--
|
||||
d -= dy
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
; Midpoint algorithm
|
||||
ubyte ploty
|
||||
ubyte xx = radius
|
||||
ubyte yy = 0
|
||||
byte decisionOver2 = 1-xx as byte
|
||||
|
||||
while xx>=yy {
|
||||
plotx = xcenter + xx
|
||||
ploty = ycenter + yy
|
||||
plot(ploty)
|
||||
plotx = xcenter - xx
|
||||
plot(ploty)
|
||||
plotx = xcenter + xx
|
||||
ploty = ycenter - yy
|
||||
plot(ploty)
|
||||
plotx = xcenter - xx
|
||||
plot(ploty)
|
||||
plotx = xcenter + yy
|
||||
ploty = ycenter + xx
|
||||
plot(ploty)
|
||||
plotx = xcenter - yy
|
||||
plot(ploty)
|
||||
plotx = xcenter + yy
|
||||
ploty = ycenter - xx
|
||||
plot(ploty)
|
||||
plotx = xcenter - yy
|
||||
plot(ploty)
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += 2*yy+1
|
||||
else {
|
||||
xx--
|
||||
decisionOver2 += 2*(yy-xx)+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub disc(uword cx, ubyte cy, ubyte radius) {
|
||||
; Midpoint algorithm, filled
|
||||
ubyte xx = radius
|
||||
ubyte yy = 0
|
||||
byte decisionOver2 = 1-xx as byte
|
||||
|
||||
while xx>=yy {
|
||||
ubyte cy_plus_yy = cy + yy
|
||||
ubyte cy_min_yy = cy - yy
|
||||
ubyte cy_plus_xx = cy + xx
|
||||
ubyte cy_min_xx = cy - xx
|
||||
|
||||
for plotx in cx to cx+xx {
|
||||
plot(cy_plus_yy)
|
||||
plot(cy_min_yy)
|
||||
}
|
||||
for plotx in cx-xx to cx-1 {
|
||||
plot(cy_plus_yy)
|
||||
plot(cy_min_yy)
|
||||
}
|
||||
for plotx in cx to cx+yy {
|
||||
plot(cy_plus_xx)
|
||||
plot(cy_min_xx)
|
||||
}
|
||||
for plotx in cx-yy to cx {
|
||||
plot(cy_plus_xx)
|
||||
plot(cy_min_xx)
|
||||
}
|
||||
yy++
|
||||
if decisionOver2<=0
|
||||
decisionOver2 += 2*yy+1
|
||||
else {
|
||||
xx--
|
||||
decisionOver2 += 2*(yy-xx)+1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; here is the non-asm code for the plot routine below:
|
||||
; sub plot_nonasm(uword px, ubyte py) {
|
||||
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
; uword addr = bitmap_address + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
||||
; @(addr) |= ormask[lsb(px) & 7]
|
||||
; }
|
||||
|
||||
uword plotx ; 0..319 ; separate 'parameter' for plot()
|
||||
|
||||
asmsub plot(ubyte ploty @A) { ; plotx is 16 bits 0 to 319... doesn't fit in a register
|
||||
%asm {{
|
||||
tay
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
lda plotx+1
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
lsr a ; 0
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
lda plotx
|
||||
pha
|
||||
and #7
|
||||
tax
|
||||
|
||||
lda _y_lookup_lo,y
|
||||
clc
|
||||
adc c64.SCRATCH_ZPWORD2
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
lda _y_lookup_hi,y
|
||||
adc c64.SCRATCH_ZPWORD2+1
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
|
||||
pla ; plotx
|
||||
and #%11111000
|
||||
tay
|
||||
lda (c64.SCRATCH_ZPWORD2),y
|
||||
ora _ormask,x
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
rts
|
||||
|
||||
_ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
||||
|
||||
; note: this can be even faster if we also have a 256 byte x-lookup table, but hey.
|
||||
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
||||
; the y lookup tables encodes this formula: bitmap_address + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||
_y_lookup_hi
|
||||
.byte $20, $20, $20, $20, $20, $20, $20, $20, $21, $21, $21, $21, $21, $21, $21, $21
|
||||
.byte $22, $22, $22, $22, $22, $22, $22, $22, $23, $23, $23, $23, $23, $23, $23, $23
|
||||
.byte $25, $25, $25, $25, $25, $25, $25, $25, $26, $26, $26, $26, $26, $26, $26, $26
|
||||
.byte $27, $27, $27, $27, $27, $27, $27, $27, $28, $28, $28, $28, $28, $28, $28, $28
|
||||
.byte $2a, $2a, $2a, $2a, $2a, $2a, $2a, $2a, $2b, $2b, $2b, $2b, $2b, $2b, $2b, $2b
|
||||
.byte $2c, $2c, $2c, $2c, $2c, $2c, $2c, $2c, $2d, $2d, $2d, $2d, $2d, $2d, $2d, $2d
|
||||
.byte $2f, $2f, $2f, $2f, $2f, $2f, $2f, $2f, $30, $30, $30, $30, $30, $30, $30, $30
|
||||
.byte $31, $31, $31, $31, $31, $31, $31, $31, $32, $32, $32, $32, $32, $32, $32, $32
|
||||
.byte $34, $34, $34, $34, $34, $34, $34, $34, $35, $35, $35, $35, $35, $35, $35, $35
|
||||
.byte $36, $36, $36, $36, $36, $36, $36, $36, $37, $37, $37, $37, $37, $37, $37, $37
|
||||
.byte $39, $39, $39, $39, $39, $39, $39, $39, $3a, $3a, $3a, $3a, $3a, $3a, $3a, $3a
|
||||
.byte $3b, $3b, $3b, $3b, $3b, $3b, $3b, $3b, $3c, $3c, $3c, $3c, $3c, $3c, $3c, $3c
|
||||
.byte $3e, $3e, $3e, $3e, $3e, $3e, $3e, $3e
|
||||
|
||||
_y_lookup_lo
|
||||
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||
.byte $00, $01, $02, $03, $04, $05, $06, $07
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
109
examples/cmp/comparison_ifs_byte.p8
Normal file
109
examples/cmp/comparison_ifs_byte.p8
Normal file
@ -0,0 +1,109 @@
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
byte v1
|
||||
byte v2
|
||||
|
||||
v1 = 100
|
||||
v2 = 127
|
||||
if v1==v2
|
||||
txt.print("error in 100==127!\n")
|
||||
else
|
||||
txt.print("ok: 100 not == 127\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("ok: 100 != 127\n")
|
||||
else
|
||||
txt.print("error in 100!=127!\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("ok: 100 < 127\n")
|
||||
else
|
||||
txt.print("error in 100<127!\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: 100 <= 127\n")
|
||||
else
|
||||
txt.print("error in 100<=127!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in 100>127!\n")
|
||||
else
|
||||
txt.print("ok: 100 is not >127\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("error in 100>=127!\n")
|
||||
else
|
||||
txt.print("ok: 100 is not >=127\n")
|
||||
|
||||
|
||||
v1 = 125
|
||||
v2 = 22
|
||||
if v1==v2
|
||||
txt.print("error in 125==22!\n")
|
||||
else
|
||||
txt.print("ok: 125 not == 22\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("ok: 125 != 22\n")
|
||||
else
|
||||
txt.print("error in 125!=22!\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in 125<22!\n")
|
||||
else
|
||||
txt.print("ok: 125 is not < 22\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("error in 125<=22!\n")
|
||||
else
|
||||
txt.print("ok: 125 is not <= 22\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("ok: 125 > 22\n")
|
||||
else
|
||||
txt.print("error in 125>22!\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: 125 >= 22\n")
|
||||
else
|
||||
txt.print("error in 125>=22!\n")
|
||||
|
||||
v1 = 22
|
||||
v2 = 22
|
||||
if v1==v2
|
||||
txt.print("ok: 22 == 22\n")
|
||||
else
|
||||
txt.print("error in 22==22!\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("error in 22!=22!\n")
|
||||
else
|
||||
txt.print("ok: 22 is not != 22\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in 22<22!\n")
|
||||
else
|
||||
txt.print("ok: 22 is not < 22\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: 22 <= 22\n")
|
||||
else
|
||||
txt.print("error in 22<=22!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in 22>22!\n")
|
||||
else
|
||||
txt.print("ok: 22 is not > 22\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: 22 >= 22\n")
|
||||
else
|
||||
txt.print("error in 22>=22!\n")
|
||||
}
|
||||
}
|
109
examples/cmp/comparison_ifs_float.p8
Normal file
109
examples/cmp/comparison_ifs_float.p8
Normal file
@ -0,0 +1,109 @@
|
||||
%import c64textio
|
||||
%import c64flt
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
float v1
|
||||
float v2
|
||||
|
||||
v1 = 1.11
|
||||
v2 = 699.99
|
||||
if v1==v2
|
||||
txt.print("error in 1.11==699.99!\n")
|
||||
else
|
||||
txt.print("ok: 1.11 not == 699.99\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("ok: 1.11 != 699.99\n")
|
||||
else
|
||||
txt.print("error in 1.11!=699.99!\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("ok: 1.11 < 699.99\n")
|
||||
else
|
||||
txt.print("error in 1.11<699.99!\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: 1.11 <= 699.99\n")
|
||||
else
|
||||
txt.print("error in 1.11<=699.99!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in 1.11>699.99!\n")
|
||||
else
|
||||
txt.print("ok: 1.11 is not >699.99\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("error in 1.11>=699.99!\n")
|
||||
else
|
||||
txt.print("ok: 1.11 is not >=699.99\n")
|
||||
|
||||
|
||||
v1 = 555.5
|
||||
v2 = -22.2
|
||||
if v1==v2
|
||||
txt.print("error in 555.5==-22.2!\n")
|
||||
else
|
||||
txt.print("ok: 555.5 not == -22.2\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("ok: 555.5 != -22.2\n")
|
||||
else
|
||||
txt.print("error in 555.5!=-22.2!\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in 555.5<-22.2!\n")
|
||||
else
|
||||
txt.print("ok: 555.5 is not < -22.2\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("error in 555.5<=-22.2!\n")
|
||||
else
|
||||
txt.print("ok: 555.5 is not <= -22.2\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("ok: 555.5 > -22.2\n")
|
||||
else
|
||||
txt.print("error in 555.5>-22.2!\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: 555.5 >= -22.2\n")
|
||||
else
|
||||
txt.print("error in 555.5>=-22.2!\n")
|
||||
|
||||
v1 = -22.2
|
||||
v2 = -22.2
|
||||
if v1==v2
|
||||
txt.print("ok: -22.2 == -22.2\n")
|
||||
else
|
||||
txt.print("error in -22.2==-22.2!\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("error in -22.2!=-22.2!\n")
|
||||
else
|
||||
txt.print("ok: -22.2 is not != -22.2\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in -22.2<-22.2!\n")
|
||||
else
|
||||
txt.print("ok: -22.2 is not < -22.2\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: -22.2 <= -22.2\n")
|
||||
else
|
||||
txt.print("error in -22.2<=-22.2!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in -22.2>-22.2!\n")
|
||||
else
|
||||
txt.print("ok: -22.2 is not > -22.2\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: -22.2 >= -22.2\n")
|
||||
else
|
||||
txt.print("error in -22.2>=-22.2!\n")
|
||||
}
|
||||
}
|
109
examples/cmp/comparison_ifs_ubyte.p8
Normal file
109
examples/cmp/comparison_ifs_ubyte.p8
Normal file
@ -0,0 +1,109 @@
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
ubyte v1
|
||||
ubyte v2
|
||||
|
||||
v1 = 100
|
||||
v2 = 200
|
||||
if v1==v2
|
||||
txt.print("error in 100==200!\n")
|
||||
else
|
||||
txt.print("ok: 100 not == 200\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("ok: 100 != 200\n")
|
||||
else
|
||||
txt.print("error in 100!=200!\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("ok: 100 < 200\n")
|
||||
else
|
||||
txt.print("error in 100<200!\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: 100 <= 200\n")
|
||||
else
|
||||
txt.print("error in 100<=200!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in 100>200!\n")
|
||||
else
|
||||
txt.print("ok: 100 is not >200\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("error in 100>=200!\n")
|
||||
else
|
||||
txt.print("ok: 100 is not >=200\n")
|
||||
|
||||
|
||||
v1 = 155
|
||||
v2 = 22
|
||||
if v1==v2
|
||||
txt.print("error in 155==22!\n")
|
||||
else
|
||||
txt.print("ok: 155 not == 22\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("ok: 155 != 22\n")
|
||||
else
|
||||
txt.print("error in 155!=22!\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in 155<22!\n")
|
||||
else
|
||||
txt.print("ok: 155 is not < 22\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("error in 155<=22!\n")
|
||||
else
|
||||
txt.print("ok: 155 is not <= 22\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("ok: 155 > 22\n")
|
||||
else
|
||||
txt.print("error in 155>22!\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: 155 >= 22\n")
|
||||
else
|
||||
txt.print("error in 155>=22!\n")
|
||||
|
||||
v1 = 22
|
||||
v2 = 22
|
||||
if v1==v2
|
||||
txt.print("ok: 22 == 22\n")
|
||||
else
|
||||
txt.print("error in 22==22!\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("error in 22!=22!\n")
|
||||
else
|
||||
txt.print("ok: 22 is not != 22\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in 22<22!\n")
|
||||
else
|
||||
txt.print("ok: 22 is not < 22\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: 22 <= 22\n")
|
||||
else
|
||||
txt.print("error in 22<=22!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in 22>22!\n")
|
||||
else
|
||||
txt.print("ok: 22 is not > 22\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: 22 >= 22\n")
|
||||
else
|
||||
txt.print("error in 22>=22!\n")
|
||||
}
|
||||
}
|
109
examples/cmp/comparison_ifs_uword.p8
Normal file
109
examples/cmp/comparison_ifs_uword.p8
Normal file
@ -0,0 +1,109 @@
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
uword v1
|
||||
uword v2
|
||||
|
||||
v1 = 100
|
||||
v2 = 64444
|
||||
if v1==v2
|
||||
txt.print("error in 100==64444!\n")
|
||||
else
|
||||
txt.print("ok: 100 not == 64444\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("ok: 100 != 64444\n")
|
||||
else
|
||||
txt.print("error in 100!=64444!\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("ok: 100 < 64444\n")
|
||||
else
|
||||
txt.print("error in 100<64444!\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: 100 <= 64444\n")
|
||||
else
|
||||
txt.print("error in 100<=64444!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in 100>64444!\n")
|
||||
else
|
||||
txt.print("ok: 100 is not >64444\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("error in 100>=64444!\n")
|
||||
else
|
||||
txt.print("ok: 100 is not >=64444\n")
|
||||
|
||||
|
||||
v1 = 5555
|
||||
v2 = 322
|
||||
if v1==v2
|
||||
txt.print("error in 5555==322!\n")
|
||||
else
|
||||
txt.print("ok: 5555 not == 322\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("ok: 5555 != 322\n")
|
||||
else
|
||||
txt.print("error in 5555!=322!\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in 5555<322!\n")
|
||||
else
|
||||
txt.print("ok: 5555 is not < 322\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("error in 5555<=322!\n")
|
||||
else
|
||||
txt.print("ok: 5555 is not <= 322\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("ok: 5555 > 322\n")
|
||||
else
|
||||
txt.print("error in 5555>322!\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: 5555 >= 322\n")
|
||||
else
|
||||
txt.print("error in 5555>=322!\n")
|
||||
|
||||
v1 = 322
|
||||
v2 = 322
|
||||
if v1==v2
|
||||
txt.print("ok: 322 == 322\n")
|
||||
else
|
||||
txt.print("error in 322==322!\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("error in 322!=322!\n")
|
||||
else
|
||||
txt.print("ok: 322 is not != 322\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in 322<322!\n")
|
||||
else
|
||||
txt.print("ok: 322 is not < 322\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: 322 <= 322\n")
|
||||
else
|
||||
txt.print("error in 322<=322!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in 322>322!\n")
|
||||
else
|
||||
txt.print("ok: 322 is not > 322\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: 322 >= 322\n")
|
||||
else
|
||||
txt.print("error in 322>=322!\n")
|
||||
}
|
||||
}
|
141
examples/cmp/comparison_ifs_word.p8
Normal file
141
examples/cmp/comparison_ifs_word.p8
Normal file
@ -0,0 +1,141 @@
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
word v1
|
||||
word v2
|
||||
|
||||
v1 = 100
|
||||
v2 = 30333
|
||||
if v1==v2
|
||||
txt.print("error in 100==30333!\n")
|
||||
else
|
||||
txt.print("ok: 100 not == 30333\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("ok: 100 != 30333\n")
|
||||
else
|
||||
txt.print("error in 100!=30333!\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("ok: 100 < 30333\n")
|
||||
else
|
||||
txt.print("error in 100<30333!\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: 100 <= 30333\n")
|
||||
else
|
||||
txt.print("error in 100<=30333!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in 100>30333!\n")
|
||||
else
|
||||
txt.print("ok: 100 is not >30333\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("error in 100>=30333!\n")
|
||||
else
|
||||
txt.print("ok: 100 is not >=30333\n")
|
||||
|
||||
|
||||
v1 = 125
|
||||
v2 = -222
|
||||
if v1==v2
|
||||
txt.print("error in 125==-222!\n")
|
||||
else
|
||||
txt.print("ok: 125 not == -222\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("ok: 125 != -222\n")
|
||||
else
|
||||
txt.print("error in 125!=-222!\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in 125<-222!\n")
|
||||
else
|
||||
txt.print("ok: 125 is not < -222\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("error in 125<=-222!\n")
|
||||
else
|
||||
txt.print("ok: 125 is not <= -222\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("ok: 125 > -222\n")
|
||||
else
|
||||
txt.print("error in 125>-222!\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: 125 >= -222\n")
|
||||
else
|
||||
txt.print("error in 125>=-222!\n")
|
||||
|
||||
v1 = -222
|
||||
v2 = -222
|
||||
if v1==v2
|
||||
txt.print("ok: -222 == -222\n")
|
||||
else
|
||||
txt.print("error in -222==-222!\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("error in -222!=-222!\n")
|
||||
else
|
||||
txt.print("ok: -222 is not != -222\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in -222<-222!\n")
|
||||
else
|
||||
txt.print("ok: -222 is not < -222\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: -222 <= -222\n")
|
||||
else
|
||||
txt.print("error in -222<=-222!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in -222>-222!\n")
|
||||
else
|
||||
txt.print("ok: -222 is not > -222\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: -222 >= -222\n")
|
||||
else
|
||||
txt.print("error in -222>=-222!\n")
|
||||
|
||||
v1 = 1000
|
||||
v2 = 1000
|
||||
if v1==v2
|
||||
txt.print("ok: 1000 == 1000\n")
|
||||
else
|
||||
txt.print("error in 1000==1000!\n")
|
||||
|
||||
if v1!=v2
|
||||
txt.print("error in 1000!=1000!\n")
|
||||
else
|
||||
txt.print("ok: 1000 is not != 1000\n")
|
||||
|
||||
if v1<v2
|
||||
txt.print("error in 1000<1000!\n")
|
||||
else
|
||||
txt.print("ok: 1000 is not < 1000\n")
|
||||
|
||||
if v1<=v2
|
||||
txt.print("ok: 1000 <= 1000\n")
|
||||
else
|
||||
txt.print("error in 1000<=1000!\n")
|
||||
|
||||
if v1>v2
|
||||
txt.print("error in 1000>1000!\n")
|
||||
else
|
||||
txt.print("ok: 1000 is not > 1000\n")
|
||||
|
||||
if v1>=v2
|
||||
txt.print("ok: 1000 >= 1000\n")
|
||||
else
|
||||
txt.print("error in 1000>=1000!\n")
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
%import c64utils
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -9,7 +9,7 @@ main {
|
||||
byte v2
|
||||
ubyte cr
|
||||
|
||||
c64scr.print("signed byte ")
|
||||
txt.print("signed byte ")
|
||||
|
||||
cr=v1==v2
|
||||
cr=v1==v2
|
||||
@ -39,52 +39,52 @@ main {
|
||||
; comparisons:
|
||||
v1=-20
|
||||
v2=125
|
||||
c64scr.print("v1=-20, v2=125\n")
|
||||
txt.print("v1=-20, v2=125\n")
|
||||
compare()
|
||||
|
||||
v1=80
|
||||
v2=80
|
||||
c64scr.print("v1 = v2 = 80\n")
|
||||
txt.print("v1 = v2 = 80\n")
|
||||
compare()
|
||||
|
||||
v1=20
|
||||
v2=-111
|
||||
c64scr.print("v1=20, v2=-111\n")
|
||||
txt.print("v1=20, v2=-111\n")
|
||||
compare()
|
||||
|
||||
return
|
||||
|
||||
sub compare() {
|
||||
c64scr.print(" == != < > <= >=\n")
|
||||
txt.print(" == != < > <= >=\n")
|
||||
|
||||
if v1==v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
if v1!=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
c64.CHROUT('\n')
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
%import c64utils
|
||||
%import c64textio
|
||||
%import c64flt
|
||||
%zeropage basicsafe
|
||||
|
||||
@ -10,7 +10,7 @@ main {
|
||||
float v2
|
||||
ubyte cr
|
||||
|
||||
c64scr.print("floating point ")
|
||||
txt.print("floating point ")
|
||||
|
||||
cr=v1==v2
|
||||
cr=v1==v2
|
||||
@ -40,67 +40,67 @@ main {
|
||||
; comparisons:
|
||||
v1=20
|
||||
v2=666.66
|
||||
c64scr.print("v1=20, v2=666.66\n")
|
||||
txt.print("v1=20, v2=666.66\n")
|
||||
compare()
|
||||
|
||||
v1=-20
|
||||
v2=666.66
|
||||
c64scr.print("v1=-20, v2=666.66\n")
|
||||
txt.print("v1=-20, v2=666.66\n")
|
||||
compare()
|
||||
|
||||
v1=666.66
|
||||
v2=555.55
|
||||
c64scr.print("v1=666.66, v2=555.55\n")
|
||||
txt.print("v1=666.66, v2=555.55\n")
|
||||
compare()
|
||||
|
||||
v1=3.1415
|
||||
v2=-3.1415
|
||||
c64scr.print("v1 = 3.1415, v2 = -3.1415\n")
|
||||
txt.print("v1 = 3.1415, v2 = -3.1415\n")
|
||||
compare()
|
||||
|
||||
v1=3.1415
|
||||
v2=3.1415
|
||||
c64scr.print("v1 = v2 = 3.1415\n")
|
||||
txt.print("v1 = v2 = 3.1415\n")
|
||||
compare()
|
||||
|
||||
v1=0
|
||||
v2=0
|
||||
c64scr.print("v1 = v2 = 0\n")
|
||||
txt.print("v1 = v2 = 0\n")
|
||||
compare()
|
||||
|
||||
return
|
||||
|
||||
sub compare() {
|
||||
c64scr.print(" == != < > <= >=\n")
|
||||
txt.print(" == != < > <= >=\n")
|
||||
|
||||
if v1==v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
if v1!=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
c64.CHROUT('\n')
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
%import c64utils
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -9,7 +9,7 @@ main {
|
||||
ubyte v2
|
||||
ubyte cr
|
||||
|
||||
c64scr.print("unsigned byte ")
|
||||
txt.print("unsigned byte ")
|
||||
|
||||
cr=v1==v2
|
||||
cr=v1==v2
|
||||
@ -39,52 +39,52 @@ main {
|
||||
; comparisons:
|
||||
v1=20
|
||||
v2=199
|
||||
c64scr.print("v1=20, v2=199\n")
|
||||
txt.print("v1=20, v2=199\n")
|
||||
compare()
|
||||
|
||||
v1=80
|
||||
v2=80
|
||||
c64scr.print("v1 = v2 = 80\n")
|
||||
txt.print("v1 = v2 = 80\n")
|
||||
compare()
|
||||
|
||||
v1=220
|
||||
v2=10
|
||||
c64scr.print("v1=220, v2=10\n")
|
||||
txt.print("v1=220, v2=10\n")
|
||||
compare()
|
||||
|
||||
return
|
||||
|
||||
sub compare() {
|
||||
c64scr.print(" == != < > <= >=\n")
|
||||
txt.print(" == != < > <= >=\n")
|
||||
|
||||
if v1==v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
if v1!=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
c64.CHROUT('\n')
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
%import c64utils
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -9,7 +9,7 @@ main {
|
||||
uword v2
|
||||
ubyte cr
|
||||
|
||||
c64scr.print("unsigned word ")
|
||||
txt.print("unsigned word ")
|
||||
|
||||
cr=v1==v2
|
||||
cr=v1==v2
|
||||
@ -39,82 +39,82 @@ main {
|
||||
; comparisons:
|
||||
v1=20
|
||||
v2=$00aa
|
||||
c64scr.print("v1=20, v2=$00aa\n")
|
||||
txt.print("v1=20, v2=$00aa\n")
|
||||
compare()
|
||||
|
||||
v1=20
|
||||
v2=$ea00
|
||||
c64scr.print("v1=20, v2=$ea00\n")
|
||||
txt.print("v1=20, v2=$ea00\n")
|
||||
compare()
|
||||
|
||||
v1=$c400
|
||||
v2=$22
|
||||
c64scr.print("v1=$c400, v2=$22\n")
|
||||
txt.print("v1=$c400, v2=$22\n")
|
||||
compare()
|
||||
|
||||
v1=$c400
|
||||
v2=$2a00
|
||||
c64scr.print("v1=$c400, v2=$2a00\n")
|
||||
txt.print("v1=$c400, v2=$2a00\n")
|
||||
compare()
|
||||
|
||||
v1=$c433
|
||||
v2=$2a00
|
||||
c64scr.print("v1=$c433, v2=$2a00\n")
|
||||
txt.print("v1=$c433, v2=$2a00\n")
|
||||
compare()
|
||||
|
||||
v1=$c433
|
||||
v2=$2aff
|
||||
c64scr.print("v1=$c433, v2=$2aff\n")
|
||||
txt.print("v1=$c433, v2=$2aff\n")
|
||||
compare()
|
||||
|
||||
v1=$aabb
|
||||
v2=$aabb
|
||||
c64scr.print("v1 = v2 = aabb\n")
|
||||
txt.print("v1 = v2 = aabb\n")
|
||||
compare()
|
||||
|
||||
v1=$aa00
|
||||
v2=$aa00
|
||||
c64scr.print("v1 = v2 = aa00\n")
|
||||
txt.print("v1 = v2 = aa00\n")
|
||||
compare()
|
||||
|
||||
v1=$aa
|
||||
v2=$aa
|
||||
c64scr.print("v1 = v2 = aa\n")
|
||||
txt.print("v1 = v2 = aa\n")
|
||||
compare()
|
||||
|
||||
return
|
||||
|
||||
sub compare() {
|
||||
c64scr.print(" == != < > <= >=\n")
|
||||
txt.print(" == != < > <= >=\n")
|
||||
|
||||
if v1==v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
if v1!=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
c64.CHROUT('\n')
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
%import c64utils
|
||||
%import c64textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
@ -9,7 +9,7 @@ main {
|
||||
word v2
|
||||
ubyte cr
|
||||
|
||||
c64scr.print("signed word ")
|
||||
txt.print("signed word ")
|
||||
|
||||
cr=v1==v2
|
||||
cr=v1==v2
|
||||
@ -39,118 +39,118 @@ main {
|
||||
; comparisons:
|
||||
v1=20
|
||||
v2=$00aa
|
||||
c64scr.print("v1=20, v2=$00aa\n")
|
||||
txt.print("v1=20, v2=$00aa\n")
|
||||
compare()
|
||||
|
||||
v1=20
|
||||
v2=$7a00
|
||||
c64scr.print("v1=20, v2=$7a00\n")
|
||||
txt.print("v1=20, v2=$7a00\n")
|
||||
compare()
|
||||
|
||||
v1=$7400
|
||||
v2=$22
|
||||
c64scr.print("v1=$7400, v2=$22\n")
|
||||
txt.print("v1=$7400, v2=$22\n")
|
||||
compare()
|
||||
|
||||
v1=$7400
|
||||
v2=$2a00
|
||||
c64scr.print("v1=$7400, v2=$2a00\n")
|
||||
txt.print("v1=$7400, v2=$2a00\n")
|
||||
compare()
|
||||
|
||||
v1=$7433
|
||||
v2=$2a00
|
||||
c64scr.print("v1=$7433, v2=$2a00\n")
|
||||
txt.print("v1=$7433, v2=$2a00\n")
|
||||
compare()
|
||||
|
||||
v1=$7433
|
||||
v2=$2aff
|
||||
c64scr.print("v1=$7433, v2=$2aff\n")
|
||||
txt.print("v1=$7433, v2=$2aff\n")
|
||||
compare()
|
||||
|
||||
; with negative numbers:
|
||||
v1=-512
|
||||
v2=$00aa
|
||||
c64scr.print("v1=-512, v2=$00aa\n")
|
||||
txt.print("v1=-512, v2=$00aa\n")
|
||||
compare()
|
||||
|
||||
v1=-512
|
||||
v2=$7a00
|
||||
c64scr.print("v1=-512, v2=$7a00\n")
|
||||
txt.print("v1=-512, v2=$7a00\n")
|
||||
compare()
|
||||
|
||||
v1=$7400
|
||||
v2=-512
|
||||
c64scr.print("v1=$7400, v2=-512\n")
|
||||
txt.print("v1=$7400, v2=-512\n")
|
||||
compare()
|
||||
|
||||
v1=-20000
|
||||
v2=-1000
|
||||
c64scr.print("v1=-20000, v2=-1000\n")
|
||||
txt.print("v1=-20000, v2=-1000\n")
|
||||
compare()
|
||||
|
||||
v1=-1000
|
||||
v2=-20000
|
||||
c64scr.print("v1=-1000, v2=-20000\n")
|
||||
txt.print("v1=-1000, v2=-20000\n")
|
||||
compare()
|
||||
|
||||
v1=-1
|
||||
v2=32767
|
||||
c64scr.print("v1=-1, v2=32767\n")
|
||||
txt.print("v1=-1, v2=32767\n")
|
||||
compare()
|
||||
|
||||
v1=32767
|
||||
v2=-1
|
||||
c64scr.print("v1=32767, v2=-1\n")
|
||||
txt.print("v1=32767, v2=-1\n")
|
||||
compare()
|
||||
|
||||
v1=$7abb
|
||||
v2=$7abb
|
||||
c64scr.print("v1 = v2 = 7abb\n")
|
||||
txt.print("v1 = v2 = 7abb\n")
|
||||
compare()
|
||||
|
||||
v1=$7a00
|
||||
v2=$7a00
|
||||
c64scr.print("v1 = v2 = 7a00\n")
|
||||
txt.print("v1 = v2 = 7a00\n")
|
||||
compare()
|
||||
|
||||
v1=$aa
|
||||
v2=$aa
|
||||
c64scr.print("v1 = v2 = aa\n")
|
||||
txt.print("v1 = v2 = aa\n")
|
||||
compare()
|
||||
|
||||
return
|
||||
|
||||
sub compare() {
|
||||
c64scr.print(" == != < > <= >=\n")
|
||||
txt.print(" == != < > <= >=\n")
|
||||
|
||||
if v1==v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
if v1!=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print(" Q ")
|
||||
txt.print(" Q ")
|
||||
else
|
||||
c64scr.print(" . ")
|
||||
txt.print(" . ")
|
||||
c64.CHROUT('\n')
|
||||
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
byte v1
|
||||
byte v2
|
||||
|
||||
v1 = 100
|
||||
v2 = 127
|
||||
if v1==v2
|
||||
c64scr.print("error in 100==127!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 not == 127\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("ok: 100 != 127\n")
|
||||
else
|
||||
c64scr.print("error in 100!=127!\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("ok: 100 < 127\n")
|
||||
else
|
||||
c64scr.print("error in 100<127!\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: 100 <= 127\n")
|
||||
else
|
||||
c64scr.print("error in 100<=127!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in 100>127!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 is not >127\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("error in 100>=127!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 is not >=127\n")
|
||||
|
||||
|
||||
v1 = 125
|
||||
v2 = 22
|
||||
if v1==v2
|
||||
c64scr.print("error in 125==22!\n")
|
||||
else
|
||||
c64scr.print("ok: 125 not == 22\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("ok: 125 != 22\n")
|
||||
else
|
||||
c64scr.print("error in 125!=22!\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in 125<22!\n")
|
||||
else
|
||||
c64scr.print("ok: 125 is not < 22\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("error in 125<=22!\n")
|
||||
else
|
||||
c64scr.print("ok: 125 is not <= 22\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("ok: 125 > 22\n")
|
||||
else
|
||||
c64scr.print("error in 125>22!\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: 125 >= 22\n")
|
||||
else
|
||||
c64scr.print("error in 125>=22!\n")
|
||||
|
||||
v1 = 22
|
||||
v2 = 22
|
||||
if v1==v2
|
||||
c64scr.print("ok: 22 == 22\n")
|
||||
else
|
||||
c64scr.print("error in 22==22!\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("error in 22!=22!\n")
|
||||
else
|
||||
c64scr.print("ok: 22 is not != 22\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in 22<22!\n")
|
||||
else
|
||||
c64scr.print("ok: 22 is not < 22\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: 22 <= 22\n")
|
||||
else
|
||||
c64scr.print("error in 22<=22!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in 22>22!\n")
|
||||
else
|
||||
c64scr.print("ok: 22 is not > 22\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: 22 >= 22\n")
|
||||
else
|
||||
c64scr.print("error in 22>=22!\n")
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
%import c64utils
|
||||
%import c64flt
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
float v1
|
||||
float v2
|
||||
|
||||
v1 = 1.11
|
||||
v2 = 699.99
|
||||
if v1==v2
|
||||
c64scr.print("error in 1.11==699.99!\n")
|
||||
else
|
||||
c64scr.print("ok: 1.11 not == 699.99\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("ok: 1.11 != 699.99\n")
|
||||
else
|
||||
c64scr.print("error in 1.11!=699.99!\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("ok: 1.11 < 699.99\n")
|
||||
else
|
||||
c64scr.print("error in 1.11<699.99!\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: 1.11 <= 699.99\n")
|
||||
else
|
||||
c64scr.print("error in 1.11<=699.99!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in 1.11>699.99!\n")
|
||||
else
|
||||
c64scr.print("ok: 1.11 is not >699.99\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("error in 1.11>=699.99!\n")
|
||||
else
|
||||
c64scr.print("ok: 1.11 is not >=699.99\n")
|
||||
|
||||
|
||||
v1 = 555.5
|
||||
v2 = -22.2
|
||||
if v1==v2
|
||||
c64scr.print("error in 555.5==-22.2!\n")
|
||||
else
|
||||
c64scr.print("ok: 555.5 not == -22.2\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("ok: 555.5 != -22.2\n")
|
||||
else
|
||||
c64scr.print("error in 555.5!=-22.2!\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in 555.5<-22.2!\n")
|
||||
else
|
||||
c64scr.print("ok: 555.5 is not < -22.2\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("error in 555.5<=-22.2!\n")
|
||||
else
|
||||
c64scr.print("ok: 555.5 is not <= -22.2\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("ok: 555.5 > -22.2\n")
|
||||
else
|
||||
c64scr.print("error in 555.5>-22.2!\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: 555.5 >= -22.2\n")
|
||||
else
|
||||
c64scr.print("error in 555.5>=-22.2!\n")
|
||||
|
||||
v1 = -22.2
|
||||
v2 = -22.2
|
||||
if v1==v2
|
||||
c64scr.print("ok: -22.2 == -22.2\n")
|
||||
else
|
||||
c64scr.print("error in -22.2==-22.2!\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("error in -22.2!=-22.2!\n")
|
||||
else
|
||||
c64scr.print("ok: -22.2 is not != -22.2\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in -22.2<-22.2!\n")
|
||||
else
|
||||
c64scr.print("ok: -22.2 is not < -22.2\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: -22.2 <= -22.2\n")
|
||||
else
|
||||
c64scr.print("error in -22.2<=-22.2!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in -22.2>-22.2!\n")
|
||||
else
|
||||
c64scr.print("ok: -22.2 is not > -22.2\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: -22.2 >= -22.2\n")
|
||||
else
|
||||
c64scr.print("error in -22.2>=-22.2!\n")
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
ubyte v1
|
||||
ubyte v2
|
||||
|
||||
v1 = 100
|
||||
v2 = 200
|
||||
if v1==v2
|
||||
c64scr.print("error in 100==200!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 not == 200\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("ok: 100 != 200\n")
|
||||
else
|
||||
c64scr.print("error in 100!=200!\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("ok: 100 < 200\n")
|
||||
else
|
||||
c64scr.print("error in 100<200!\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: 100 <= 200\n")
|
||||
else
|
||||
c64scr.print("error in 100<=200!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in 100>200!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 is not >200\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("error in 100>=200!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 is not >=200\n")
|
||||
|
||||
|
||||
v1 = 155
|
||||
v2 = 22
|
||||
if v1==v2
|
||||
c64scr.print("error in 155==22!\n")
|
||||
else
|
||||
c64scr.print("ok: 155 not == 22\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("ok: 155 != 22\n")
|
||||
else
|
||||
c64scr.print("error in 155!=22!\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in 155<22!\n")
|
||||
else
|
||||
c64scr.print("ok: 155 is not < 22\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("error in 155<=22!\n")
|
||||
else
|
||||
c64scr.print("ok: 155 is not <= 22\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("ok: 155 > 22\n")
|
||||
else
|
||||
c64scr.print("error in 155>22!\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: 155 >= 22\n")
|
||||
else
|
||||
c64scr.print("error in 155>=22!\n")
|
||||
|
||||
v1 = 22
|
||||
v2 = 22
|
||||
if v1==v2
|
||||
c64scr.print("ok: 22 == 22\n")
|
||||
else
|
||||
c64scr.print("error in 22==22!\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("error in 22!=22!\n")
|
||||
else
|
||||
c64scr.print("ok: 22 is not != 22\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in 22<22!\n")
|
||||
else
|
||||
c64scr.print("ok: 22 is not < 22\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: 22 <= 22\n")
|
||||
else
|
||||
c64scr.print("error in 22<=22!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in 22>22!\n")
|
||||
else
|
||||
c64scr.print("ok: 22 is not > 22\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: 22 >= 22\n")
|
||||
else
|
||||
c64scr.print("error in 22>=22!\n")
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
uword v1
|
||||
uword v2
|
||||
|
||||
v1 = 100
|
||||
v2 = 64444
|
||||
if v1==v2
|
||||
c64scr.print("error in 100==64444!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 not == 64444\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("ok: 100 != 64444\n")
|
||||
else
|
||||
c64scr.print("error in 100!=64444!\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("ok: 100 < 64444\n")
|
||||
else
|
||||
c64scr.print("error in 100<64444!\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: 100 <= 64444\n")
|
||||
else
|
||||
c64scr.print("error in 100<=64444!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in 100>64444!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 is not >64444\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("error in 100>=64444!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 is not >=64444\n")
|
||||
|
||||
|
||||
v1 = 5555
|
||||
v2 = 322
|
||||
if v1==v2
|
||||
c64scr.print("error in 5555==322!\n")
|
||||
else
|
||||
c64scr.print("ok: 5555 not == 322\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("ok: 5555 != 322\n")
|
||||
else
|
||||
c64scr.print("error in 5555!=322!\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in 5555<322!\n")
|
||||
else
|
||||
c64scr.print("ok: 5555 is not < 322\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("error in 5555<=322!\n")
|
||||
else
|
||||
c64scr.print("ok: 5555 is not <= 322\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("ok: 5555 > 322\n")
|
||||
else
|
||||
c64scr.print("error in 5555>322!\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: 5555 >= 322\n")
|
||||
else
|
||||
c64scr.print("error in 5555>=322!\n")
|
||||
|
||||
v1 = 322
|
||||
v2 = 322
|
||||
if v1==v2
|
||||
c64scr.print("ok: 322 == 322\n")
|
||||
else
|
||||
c64scr.print("error in 322==322!\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("error in 322!=322!\n")
|
||||
else
|
||||
c64scr.print("ok: 322 is not != 322\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in 322<322!\n")
|
||||
else
|
||||
c64scr.print("ok: 322 is not < 322\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: 322 <= 322\n")
|
||||
else
|
||||
c64scr.print("error in 322<=322!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in 322>322!\n")
|
||||
else
|
||||
c64scr.print("ok: 322 is not > 322\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: 322 >= 322\n")
|
||||
else
|
||||
c64scr.print("error in 322>=322!\n")
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
|
||||
word v1
|
||||
word v2
|
||||
|
||||
v1 = 100
|
||||
v2 = 30333
|
||||
if v1==v2
|
||||
c64scr.print("error in 100==30333!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 not == 30333\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("ok: 100 != 30333\n")
|
||||
else
|
||||
c64scr.print("error in 100!=30333!\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("ok: 100 < 30333\n")
|
||||
else
|
||||
c64scr.print("error in 100<30333!\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: 100 <= 30333\n")
|
||||
else
|
||||
c64scr.print("error in 100<=30333!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in 100>30333!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 is not >30333\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("error in 100>=30333!\n")
|
||||
else
|
||||
c64scr.print("ok: 100 is not >=30333\n")
|
||||
|
||||
|
||||
v1 = 125
|
||||
v2 = -222
|
||||
if v1==v2
|
||||
c64scr.print("error in 125==-222!\n")
|
||||
else
|
||||
c64scr.print("ok: 125 not == -222\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("ok: 125 != -222\n")
|
||||
else
|
||||
c64scr.print("error in 125!=-222!\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in 125<-222!\n")
|
||||
else
|
||||
c64scr.print("ok: 125 is not < -222\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("error in 125<=-222!\n")
|
||||
else
|
||||
c64scr.print("ok: 125 is not <= -222\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("ok: 125 > -222\n")
|
||||
else
|
||||
c64scr.print("error in 125>-222!\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: 125 >= -222\n")
|
||||
else
|
||||
c64scr.print("error in 125>=-222!\n")
|
||||
|
||||
v1 = -222
|
||||
v2 = -222
|
||||
if v1==v2
|
||||
c64scr.print("ok: -222 == -222\n")
|
||||
else
|
||||
c64scr.print("error in -222==-222!\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("error in -222!=-222!\n")
|
||||
else
|
||||
c64scr.print("ok: -222 is not != -222\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in -222<-222!\n")
|
||||
else
|
||||
c64scr.print("ok: -222 is not < -222\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: -222 <= -222\n")
|
||||
else
|
||||
c64scr.print("error in -222<=-222!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in -222>-222!\n")
|
||||
else
|
||||
c64scr.print("ok: -222 is not > -222\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: -222 >= -222\n")
|
||||
else
|
||||
c64scr.print("error in -222>=-222!\n")
|
||||
|
||||
v1 = 1000
|
||||
v2 = 1000
|
||||
if v1==v2
|
||||
c64scr.print("ok: 1000 == 1000\n")
|
||||
else
|
||||
c64scr.print("error in 1000==1000!\n")
|
||||
|
||||
if v1!=v2
|
||||
c64scr.print("error in 1000!=1000!\n")
|
||||
else
|
||||
c64scr.print("ok: 1000 is not != 1000\n")
|
||||
|
||||
if v1<v2
|
||||
c64scr.print("error in 1000<1000!\n")
|
||||
else
|
||||
c64scr.print("ok: 1000 is not < 1000\n")
|
||||
|
||||
if v1<=v2
|
||||
c64scr.print("ok: 1000 <= 1000\n")
|
||||
else
|
||||
c64scr.print("error in 1000<=1000!\n")
|
||||
|
||||
if v1>v2
|
||||
c64scr.print("error in 1000>1000!\n")
|
||||
else
|
||||
c64scr.print("ok: 1000 is not > 1000\n")
|
||||
|
||||
if v1>=v2
|
||||
c64scr.print("ok: 1000 >= 1000\n")
|
||||
else
|
||||
c64scr.print("error in 1000>=1000!\n")
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user