mirror of
https://github.com/irmen/prog8.git
synced 2025-06-18 08:23:37 +00:00
Compare commits
80 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
63
README.md
63
README.md
@ -2,19 +2,28 @@
|
|||||||
[](https://travis-ci.org/irmen/prog8)
|
[](https://travis-ci.org/irmen/prog8)
|
||||||
[](https://prog8.readthedocs.io/)
|
[](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)*
|
*Written by Irmen de Jong (irmen@razorvine.net)*
|
||||||
|
|
||||||
*Software license: GNU GPL 3.0, see file LICENSE*
|
*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,
|
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
|
- modularity, symbol scoping, subroutines
|
||||||
- various data types other than just bytes (16-bit words, floats, strings)
|
- various data types other than just bytes (16-bit words, floats, strings)
|
||||||
- automatic variable allocations, automatic string and array variables and string sharing
|
- 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
|
- 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``
|
- 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
|
- use a modern PC to do the work on
|
||||||
- very quick compilation times
|
- 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
|
- 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
|
- 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.
|
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
|
||||||
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
|
||||||
|
|
||||||
Documentation/manual
|
- "c64": Commodore-64 (6510 CPU = almost a 6502) premium support.
|
||||||
--------------------
|
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) experimental support.
|
||||||
https://prog8.readthedocs.io/
|
|
||||||
|
|
||||||
Required tools
|
|
||||||
--------------
|
|
||||||
|
|
||||||
|
Additional required tools
|
||||||
|
-------------------------
|
||||||
|
|
||||||
[64tass](https://sourceforge.net/projects/tass64/) - cross assembler. Install this on your shell path.
|
[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.
|
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,
|
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).
|
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
|
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/)
|
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
|
Example code
|
||||||
@ -64,7 +75,7 @@ Example code
|
|||||||
|
|
||||||
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||||
|
|
||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -73,35 +84,33 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
ubyte candidate_prime = 2
|
ubyte candidate_prime = 2
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
memset(sieve, 256, false)
|
memset(sieve, 256, false) ; clear the sieve
|
||||||
|
txt.print("prime numbers up to 255:\n\n")
|
||||||
c64scr.print("prime numbers up to 255:\n\n")
|
|
||||||
ubyte amount=0
|
ubyte amount=0
|
||||||
repeat {
|
repeat {
|
||||||
ubyte prime = find_next_prime()
|
ubyte prime = find_next_prime()
|
||||||
if prime==0
|
if prime==0
|
||||||
break
|
break
|
||||||
c64scr.print_ub(prime)
|
txt.print_ub(prime)
|
||||||
c64scr.print(", ")
|
txt.print(", ")
|
||||||
amount++
|
amount++
|
||||||
}
|
}
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("number of primes (expected 54): ")
|
txt.print("number of primes (expected 54): ")
|
||||||
c64scr.print_ub(amount)
|
txt.print_ub(amount)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub find_next_prime() -> ubyte {
|
sub find_next_prime() -> ubyte {
|
||||||
|
|
||||||
while sieve[candidate_prime] {
|
while sieve[candidate_prime] {
|
||||||
candidate_prime++
|
candidate_prime++
|
||||||
if candidate_prime==0
|
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
|
sieve[candidate_prime] = true
|
||||||
uword multiple = candidate_prime
|
uword multiple = candidate_prime
|
||||||
|
|
||||||
while multiple < len(sieve) {
|
while multiple < len(sieve) {
|
||||||
sieve[lsb(multiple)] = true
|
sieve[lsb(multiple)] = true
|
||||||
multiple += candidate_prime
|
multiple += candidate_prime
|
||||||
|
@ -1,49 +1,51 @@
|
|||||||
; --- low level floating point assembly routines for the C64
|
; --- low level floating point assembly routines for the C64
|
||||||
|
|
||||||
|
|
||||||
ub2float .proc
|
ub2float .proc
|
||||||
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
; -- convert ubyte in SCRATCH_ZPB1 to float at address A/Y
|
||||||
; clobbers A, Y
|
; clobbers A, Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
sta c64.SCRATCH_ZPWORD2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
ldy c64.SCRATCH_ZPB1
|
ldy P8ZP_SCRATCH_B1
|
||||||
jsr FREADUY
|
lda #0
|
||||||
_fac_to_mem ldx c64.SCRATCH_ZPWORD2
|
jsr GIVAYF
|
||||||
ldy c64.SCRATCH_ZPWORD2+1
|
_fac_to_mem ldx P8ZP_SCRATCH_W2
|
||||||
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
jsr MOVMF
|
jsr MOVMF
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
b2float .proc
|
b2float .proc
|
||||||
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
; -- convert byte in SCRATCH_ZPB1 to float at address A/Y
|
||||||
; clobbers A, Y
|
; clobbers A, Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
sta c64.SCRATCH_ZPWORD2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
lda c64.SCRATCH_ZPB1
|
lda P8ZP_SCRATCH_B1
|
||||||
jsr FREADSA
|
jsr FREADSA
|
||||||
jmp ub2float._fac_to_mem
|
jmp ub2float._fac_to_mem
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
uw2float .proc
|
uw2float .proc
|
||||||
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
; -- convert uword in SCRATCH_ZPWORD1 to float at address A/Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
sta c64.SCRATCH_ZPWORD2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr GIVUAYFAY
|
jsr GIVUAYFAY
|
||||||
jmp ub2float._fac_to_mem
|
jmp ub2float._fac_to_mem
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
w2float .proc
|
w2float .proc
|
||||||
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
; -- convert word in SCRATCH_ZPWORD1 to float at address A/Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
sta c64.SCRATCH_ZPWORD2
|
sta P8ZP_SCRATCH_W2
|
||||||
sty c64.SCRATCH_ZPWORD2+1
|
sty P8ZP_SCRATCH_W2+1
|
||||||
ldy c64.SCRATCH_ZPWORD1
|
ldy P8ZP_SCRATCH_W1
|
||||||
lda c64.SCRATCH_ZPWORD1+1
|
lda P8ZP_SCRATCH_W1+1
|
||||||
jsr GIVAYF
|
jsr GIVAYF
|
||||||
jmp ub2float._fac_to_mem
|
jmp ub2float._fac_to_mem
|
||||||
.pend
|
.pend
|
||||||
@ -51,8 +53,8 @@ w2float .proc
|
|||||||
stack_b2float .proc
|
stack_b2float .proc
|
||||||
; -- b2float operating on the stack
|
; -- b2float operating on the stack
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr FREADSA
|
jsr FREADSA
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -60,9 +62,9 @@ stack_b2float .proc
|
|||||||
stack_w2float .proc
|
stack_w2float .proc
|
||||||
; -- w2float operating on the stack
|
; -- w2float operating on the stack
|
||||||
inx
|
inx
|
||||||
ldy c64.ESTACK_LO,x
|
ldy P8ESTACK_LO,x
|
||||||
lda c64.ESTACK_HI,x
|
lda P8ESTACK_HI,x
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr GIVAYF
|
jsr GIVAYF
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -70,44 +72,45 @@ stack_w2float .proc
|
|||||||
stack_ub2float .proc
|
stack_ub2float .proc
|
||||||
; -- ub2float operating on the stack
|
; -- ub2float operating on the stack
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
tay
|
tay
|
||||||
jsr FREADUY
|
lda #0
|
||||||
|
jsr GIVAYF
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
stack_uw2float .proc
|
stack_uw2float .proc
|
||||||
; -- uw2float operating on the stack
|
; -- uw2float operating on the stack
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
ldy c64.ESTACK_HI,x
|
ldy P8ESTACK_HI,x
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr GIVUAYFAY
|
jsr GIVUAYFAY
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
stack_float2w .proc ; also used for float2b
|
stack_float2w .proc ; also used for float2b
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr AYINT
|
jsr AYINT
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
lda $64
|
lda $64
|
||||||
sta c64.ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
lda $65
|
lda $65
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
stack_float2uw .proc ; also used for float2ub
|
stack_float2uw .proc ; also used for float2ub
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr GETADR
|
jsr GETADR
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
sta c64.ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
tya
|
tya
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
@ -115,79 +118,68 @@ stack_float2uw .proc ; also used for float2ub
|
|||||||
push_float .proc
|
push_float .proc
|
||||||
; ---- push mflpt5 in A/Y onto stack
|
; ---- push mflpt5 in A/Y onto stack
|
||||||
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
; (taking 3 stack positions = 6 bytes of which 1 is padding)
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
ldy #0
|
ldy #0
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
iny
|
iny
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
sta c64.ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
dex
|
dex
|
||||||
iny
|
iny
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
iny
|
iny
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
sta c64.ESTACK_HI,x
|
sta P8ESTACK_HI,x
|
||||||
dex
|
dex
|
||||||
iny
|
iny
|
||||||
lda (c64.SCRATCH_ZPWORD1),y
|
lda (P8ZP_SCRATCH_W1),y
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
func_rndf .proc
|
func_rndf .proc
|
||||||
; -- put a random floating point value on the stack
|
; -- put a random floating point value on the stack
|
||||||
stx c64.SCRATCH_ZPREG
|
stx P8ZP_SCRATCH_REG
|
||||||
lda #1
|
lda #1
|
||||||
jsr FREADSA
|
jsr FREADSA
|
||||||
jsr RND ; rng into fac1
|
jsr RND ; rng into fac1
|
||||||
ldx #<_rndf_rnum5
|
ldx #<_rndf_rnum5
|
||||||
ldy #>_rndf_rnum5
|
ldy #>_rndf_rnum5
|
||||||
jsr MOVMF ; fac1 to mem X/Y
|
jsr MOVMF ; fac1 to mem X/Y
|
||||||
ldx c64.SCRATCH_ZPREG
|
ldx P8ZP_SCRATCH_REG
|
||||||
lda #<_rndf_rnum5
|
lda #<_rndf_rnum5
|
||||||
ldy #>_rndf_rnum5
|
ldy #>_rndf_rnum5
|
||||||
jmp push_float
|
jmp push_float
|
||||||
_rndf_rnum5 .byte 0,0,0,0,0
|
_rndf_rnum5 .byte 0,0,0,0,0
|
||||||
.pend
|
.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
|
pop_float .proc
|
||||||
; ---- pops mflpt5 from stack to memory A/Y
|
; ---- pops mflpt5 from stack to memory A/Y
|
||||||
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
; (frees 3 stack positions = 6 bytes of which 1 is padding)
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
ldy #4
|
ldy #4
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_HI,x
|
lda P8ESTACK_HI,x
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_HI,x
|
lda P8ESTACK_HI,x
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -213,12 +205,12 @@ pop_float_fac2 .proc
|
|||||||
|
|
||||||
pop_float_to_indexed_var .proc
|
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
|
; -- 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
|
sta P8ZP_SCRATCH_W1
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
jsr prog8_lib.pop_index_times_5
|
jsr prog8_lib.pop_index_times_5
|
||||||
jsr prog8_lib.add_a_to_zpword
|
jsr prog8_lib.add_a_to_zpword
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jmp pop_float
|
jmp pop_float
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -228,7 +220,7 @@ copy_float .proc
|
|||||||
sta _target+1
|
sta _target+1
|
||||||
sty _target+2
|
sty _target+2
|
||||||
ldy #4
|
ldy #4
|
||||||
_loop lda (c64.SCRATCH_ZPWORD1),y
|
_loop lda (P8ZP_SCRATCH_W1),y
|
||||||
_target sta $ffff,y ; modified
|
_target sta $ffff,y ; modified
|
||||||
dey
|
dey
|
||||||
bpl _loop
|
bpl _loop
|
||||||
@ -237,74 +229,38 @@ _target sta $ffff,y ; modified
|
|||||||
|
|
||||||
inc_var_f .proc
|
inc_var_f .proc
|
||||||
; -- add 1 to float pointed to by A/Y
|
; -- add 1 to float pointed to by A/Y
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr MOVFM
|
jsr MOVFM
|
||||||
lda #<FL_FONE
|
lda #<FL_FONE
|
||||||
ldy #>FL_FONE
|
ldy #>FL_FONE
|
||||||
jsr FADD
|
jsr FADD
|
||||||
ldx c64.SCRATCH_ZPWORD1
|
ldx P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr MOVMF
|
jsr MOVMF
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
dec_var_f .proc
|
dec_var_f .proc
|
||||||
; -- subtract 1 from float pointed to by A/Y
|
; -- subtract 1 from float pointed to by A/Y
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
sty c64.SCRATCH_ZPWORD1+1
|
sty P8ZP_SCRATCH_W1+1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
lda #<FL_FONE
|
lda #<FL_FONE
|
||||||
ldy #>FL_FONE
|
ldy #>FL_FONE
|
||||||
jsr MOVFM
|
jsr MOVFM
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr FSUB
|
jsr FSUB
|
||||||
ldx c64.SCRATCH_ZPWORD1
|
ldx P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr MOVMF
|
jsr MOVMF
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
rts
|
rts
|
||||||
.pend
|
.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_f2_in_fac1 .proc
|
||||||
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
; -- pop 2 floats from stack, load the second one in FAC1 as well
|
||||||
@ -330,7 +286,7 @@ push_fac1_as_result .proc
|
|||||||
jsr MOVMF
|
jsr MOVMF
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
jmp push_float
|
jmp push_float
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -342,21 +298,21 @@ pow_f .proc
|
|||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr pop_float
|
jsr pop_float
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr CONUPK ; fac2 = float1
|
jsr CONUPK ; fac2 = float1
|
||||||
lda #<fmath_float2
|
lda #<fmath_float2
|
||||||
ldy #>fmath_float2
|
ldy #>fmath_float2
|
||||||
jsr FPWR
|
jsr FPWR
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
div_f .proc
|
div_f .proc
|
||||||
; -- push f1/f2 on stack
|
; -- push f1/f2 on stack
|
||||||
jsr pop_2_floats_f2_in_fac1
|
jsr pop_2_floats_f2_in_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr FDIV
|
jsr FDIV
|
||||||
@ -366,7 +322,7 @@ div_f .proc
|
|||||||
add_f .proc
|
add_f .proc
|
||||||
; -- push f1+f2 on stack
|
; -- push f1+f2 on stack
|
||||||
jsr pop_2_floats_f2_in_fac1
|
jsr pop_2_floats_f2_in_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr FADD
|
jsr FADD
|
||||||
@ -376,7 +332,7 @@ add_f .proc
|
|||||||
sub_f .proc
|
sub_f .proc
|
||||||
; -- push f1-f2 on stack
|
; -- push f1-f2 on stack
|
||||||
jsr pop_2_floats_f2_in_fac1
|
jsr pop_2_floats_f2_in_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr FSUB
|
jsr FSUB
|
||||||
@ -386,7 +342,7 @@ sub_f .proc
|
|||||||
mul_f .proc
|
mul_f .proc
|
||||||
; -- push f1*f2 on stack
|
; -- push f1*f2 on stack
|
||||||
jsr pop_2_floats_f2_in_fac1
|
jsr pop_2_floats_f2_in_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
lda #<fmath_float1
|
lda #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr FMULT
|
jsr FMULT
|
||||||
@ -396,7 +352,7 @@ mul_f .proc
|
|||||||
neg_f .proc
|
neg_f .proc
|
||||||
; -- push -flt back on stack
|
; -- push -flt back on stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr NEGOP
|
jsr NEGOP
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -404,7 +360,7 @@ neg_f .proc
|
|||||||
abs_f .proc
|
abs_f .proc
|
||||||
; -- push abs(float) on stack (as float)
|
; -- push abs(float) on stack (as float)
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr ABS
|
jsr ABS
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -415,24 +371,24 @@ equal_f .proc
|
|||||||
inx
|
inx
|
||||||
inx
|
inx
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO-3,x
|
lda P8ESTACK_LO-3,x
|
||||||
cmp c64.ESTACK_LO,x
|
cmp P8ESTACK_LO,x
|
||||||
bne _equals_false
|
bne _equals_false
|
||||||
lda c64.ESTACK_LO-2,x
|
lda P8ESTACK_LO-2,x
|
||||||
cmp c64.ESTACK_LO+1,x
|
cmp P8ESTACK_LO+1,x
|
||||||
bne _equals_false
|
bne _equals_false
|
||||||
lda c64.ESTACK_LO-1,x
|
lda P8ESTACK_LO-1,x
|
||||||
cmp c64.ESTACK_LO+2,x
|
cmp P8ESTACK_LO+2,x
|
||||||
bne _equals_false
|
bne _equals_false
|
||||||
lda c64.ESTACK_HI-2,x
|
lda P8ESTACK_HI-2,x
|
||||||
cmp c64.ESTACK_HI+1,x
|
cmp P8ESTACK_HI+1,x
|
||||||
bne _equals_false
|
bne _equals_false
|
||||||
lda c64.ESTACK_HI-1,x
|
lda P8ESTACK_HI-1,x
|
||||||
cmp c64.ESTACK_HI+2,x
|
cmp P8ESTACK_HI+2,x
|
||||||
bne _equals_false
|
bne _equals_false
|
||||||
_equals_true lda #1
|
_equals_true lda #1
|
||||||
_equals_store inx
|
_equals_store inx
|
||||||
sta c64.ESTACK_LO+1,x
|
sta P8ESTACK_LO+1,x
|
||||||
rts
|
rts
|
||||||
_equals_false lda #0
|
_equals_false lda #0
|
||||||
beq _equals_store
|
beq _equals_store
|
||||||
@ -442,7 +398,7 @@ notequal_f .proc
|
|||||||
; -- are the two mflpt5 numbers on the stack different?
|
; -- are the two mflpt5 numbers on the stack different?
|
||||||
jsr equal_f
|
jsr equal_f
|
||||||
eor #1 ; invert the result
|
eor #1 ; invert the result
|
||||||
sta c64.ESTACK_LO+1,x
|
sta P8ESTACK_LO+1,x
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -495,12 +451,12 @@ compare_floats .proc
|
|||||||
jsr MOVFM ; fac1 = flt1
|
jsr MOVFM ; fac1 = flt1
|
||||||
lda #<fmath_float2
|
lda #<fmath_float2
|
||||||
ldy #>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)
|
jsr FCOMP ; A = flt1 compared with flt2 (0=equal, 1=flt1>flt2, 255=flt1<flt2)
|
||||||
ldx c64.SCRATCH_ZPREG
|
ldx P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
_return_false lda #0
|
_return_false lda #0
|
||||||
_return_result sta c64.ESTACK_LO,x
|
_return_result sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
_return_true lda #1
|
_return_true lda #1
|
||||||
@ -510,7 +466,7 @@ _return_true lda #1
|
|||||||
func_sin .proc
|
func_sin .proc
|
||||||
; -- push sin(f) back onto stack
|
; -- push sin(f) back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr SIN
|
jsr SIN
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -518,7 +474,7 @@ func_sin .proc
|
|||||||
func_cos .proc
|
func_cos .proc
|
||||||
; -- push cos(f) back onto stack
|
; -- push cos(f) back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr COS
|
jsr COS
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -526,7 +482,7 @@ func_cos .proc
|
|||||||
func_tan .proc
|
func_tan .proc
|
||||||
; -- push tan(f) back onto stack
|
; -- push tan(f) back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr TAN
|
jsr TAN
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -534,7 +490,7 @@ func_tan .proc
|
|||||||
func_atan .proc
|
func_atan .proc
|
||||||
; -- push atan(f) back onto stack
|
; -- push atan(f) back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr ATN
|
jsr ATN
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -542,7 +498,7 @@ func_atan .proc
|
|||||||
func_ln .proc
|
func_ln .proc
|
||||||
; -- push ln(f) back onto stack
|
; -- push ln(f) back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr LOG
|
jsr LOG
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -550,7 +506,7 @@ func_ln .proc
|
|||||||
func_log2 .proc
|
func_log2 .proc
|
||||||
; -- push log base 2, ln(f)/ln(2), back onto stack
|
; -- push log base 2, ln(f)/ln(2), back onto stack
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr LOG
|
jsr LOG
|
||||||
jsr MOVEF
|
jsr MOVEF
|
||||||
lda #<c64.FL_LOG2
|
lda #<c64.FL_LOG2
|
||||||
@ -562,7 +518,7 @@ func_log2 .proc
|
|||||||
|
|
||||||
func_sqrt .proc
|
func_sqrt .proc
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr SQR
|
jsr SQR
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -570,7 +526,7 @@ func_sqrt .proc
|
|||||||
func_rad .proc
|
func_rad .proc
|
||||||
; -- convert degrees to radians (d * pi / 180)
|
; -- convert degrees to radians (d * pi / 180)
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
lda #<_pi_div_180
|
lda #<_pi_div_180
|
||||||
ldy #>_pi_div_180
|
ldy #>_pi_div_180
|
||||||
jsr FMULT
|
jsr FMULT
|
||||||
@ -581,7 +537,7 @@ _pi_div_180 .byte 123, 14, 250, 53, 18 ; pi / 180
|
|||||||
func_deg .proc
|
func_deg .proc
|
||||||
; -- convert radians to degrees (d * (1/ pi * 180))
|
; -- convert radians to degrees (d * (1/ pi * 180))
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
lda #<_one_over_pi_div_180
|
lda #<_one_over_pi_div_180
|
||||||
ldy #>_one_over_pi_div_180
|
ldy #>_one_over_pi_div_180
|
||||||
jsr FMULT
|
jsr FMULT
|
||||||
@ -591,7 +547,7 @@ _one_over_pi_div_180 .byte 134, 101, 46, 224, 211 ; 1 / (pi * 180)
|
|||||||
|
|
||||||
func_round .proc
|
func_round .proc
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr FADDH
|
jsr FADDH
|
||||||
jsr INT
|
jsr INT
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
@ -599,7 +555,7 @@ func_round .proc
|
|||||||
|
|
||||||
func_floor .proc
|
func_floor .proc
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
jsr INT
|
jsr INT
|
||||||
jmp push_fac1_as_result
|
jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -607,7 +563,7 @@ func_floor .proc
|
|||||||
func_ceil .proc
|
func_ceil .proc
|
||||||
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
|
; -- ceil: tr = int(f); if tr==f -> return else return tr+1
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
ldx #<fmath_float1
|
ldx #<fmath_float1
|
||||||
ldy #>fmath_float1
|
ldy #>fmath_float1
|
||||||
jsr MOVMF
|
jsr MOVMF
|
||||||
@ -625,45 +581,45 @@ func_ceil .proc
|
|||||||
|
|
||||||
func_any_f .proc
|
func_any_f .proc
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x ; array size
|
lda P8ESTACK_LO,x ; array size
|
||||||
sta c64.SCRATCH_ZPB1
|
sta P8ZP_SCRATCH_B1
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
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
|
jmp prog8_lib.func_any_b._entry
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
func_all_f .proc
|
func_all_f .proc
|
||||||
inx
|
inx
|
||||||
jsr prog8_lib.peek_address
|
jsr prog8_lib.peek_address
|
||||||
lda c64.ESTACK_LO,x ; array size
|
lda P8ESTACK_LO,x ; array size
|
||||||
sta c64.SCRATCH_ZPB1
|
sta P8ZP_SCRATCH_B1
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc c64.SCRATCH_ZPB1 ; times 5 because of float
|
adc P8ZP_SCRATCH_B1 ; times 5 because of float
|
||||||
tay
|
tay
|
||||||
dey
|
dey
|
||||||
- lda (c64.SCRATCH_ZPWORD1),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
clc
|
clc
|
||||||
dey
|
dey
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
adc (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
adc (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
adc (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
adc (c64.SCRATCH_ZPWORD1),y
|
adc (P8ZP_SCRATCH_W1),y
|
||||||
dey
|
dey
|
||||||
cmp #0
|
cmp #0
|
||||||
beq +
|
beq +
|
||||||
cpy #255
|
cpy #255
|
||||||
bne -
|
bne -
|
||||||
lda #1
|
lda #1
|
||||||
sta c64.ESTACK_LO+1,x
|
sta P8ESTACK_LO+1,x
|
||||||
rts
|
rts
|
||||||
+ sta c64.ESTACK_LO+1,x
|
+ sta P8ESTACK_LO+1,x
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -674,23 +630,23 @@ func_max_f .proc
|
|||||||
ldy #>_largest_neg_float
|
ldy #>_largest_neg_float
|
||||||
_minmax_entry jsr MOVFM
|
_minmax_entry jsr MOVFM
|
||||||
jsr prog8_lib.pop_array_and_lengthmin1Y
|
jsr prog8_lib.pop_array_and_lengthmin1Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
- sty c64.SCRATCH_ZPREG
|
- sty P8ZP_SCRATCH_REG
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr FCOMP
|
jsr FCOMP
|
||||||
_minmax_cmp cmp #255 ; modified
|
_minmax_cmp cmp #255 ; modified
|
||||||
bne +
|
bne +
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr MOVFM
|
jsr MOVFM
|
||||||
+ lda c64.SCRATCH_ZPWORD1
|
+ lda P8ZP_SCRATCH_W1
|
||||||
clc
|
clc
|
||||||
adc #5
|
adc #5
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc +
|
bcc +
|
||||||
inc c64.SCRATCH_ZPWORD1+1
|
inc P8ZP_SCRATCH_W1+1
|
||||||
+ ldy c64.SCRATCH_ZPREG
|
+ ldy P8ZP_SCRATCH_REG
|
||||||
dey
|
dey
|
||||||
cpy #255
|
cpy #255
|
||||||
bne -
|
bne -
|
||||||
@ -709,25 +665,25 @@ _largest_pos_float .byte 255,127,255,255,255 ; largest positive float
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
func_sum_f .proc
|
func_sum_f .proc
|
||||||
lda #<FL_ZERO
|
lda #<ZERO
|
||||||
ldy #>FL_ZERO
|
ldy #>ZERO
|
||||||
jsr MOVFM
|
jsr MOVFM
|
||||||
jsr prog8_lib.pop_array_and_lengthmin1Y
|
jsr prog8_lib.pop_array_and_lengthmin1Y
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
- sty c64.SCRATCH_ZPREG
|
- sty P8ZP_SCRATCH_REG
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
ldy c64.SCRATCH_ZPWORD1+1
|
ldy P8ZP_SCRATCH_W1+1
|
||||||
jsr FADD
|
jsr FADD
|
||||||
ldy c64.SCRATCH_ZPREG
|
ldy P8ZP_SCRATCH_REG
|
||||||
dey
|
dey
|
||||||
cpy #255
|
cpy #255
|
||||||
beq +
|
beq +
|
||||||
lda c64.SCRATCH_ZPWORD1
|
lda P8ZP_SCRATCH_W1
|
||||||
clc
|
clc
|
||||||
adc #5
|
adc #5
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta P8ZP_SCRATCH_W1
|
||||||
bcc -
|
bcc -
|
||||||
inc c64.SCRATCH_ZPWORD1+1
|
inc P8ZP_SCRATCH_W1+1
|
||||||
bne -
|
bne -
|
||||||
+ jmp push_fac1_as_result
|
+ jmp push_fac1_as_result
|
||||||
.pend
|
.pend
|
||||||
@ -735,7 +691,7 @@ func_sum_f .proc
|
|||||||
sign_f .proc
|
sign_f .proc
|
||||||
jsr pop_float_fac1
|
jsr pop_float_fac1
|
||||||
jsr SIGN
|
jsr SIGN
|
||||||
sta c64.ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
@ -744,22 +700,22 @@ sign_f .proc
|
|||||||
set_0_array_float .proc
|
set_0_array_float .proc
|
||||||
; -- set a float in an array to zero (index on stack, array in SCRATCH_ZPWORD1)
|
; -- set a float in an array to zero (index on stack, array in SCRATCH_ZPWORD1)
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc c64.ESTACK_LO,x
|
adc P8ESTACK_LO,x
|
||||||
tay
|
tay
|
||||||
lda #0
|
lda #0
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
iny
|
iny
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
iny
|
iny
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
iny
|
iny
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
iny
|
iny
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
@ -767,13 +723,13 @@ set_0_array_float .proc
|
|||||||
set_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)
|
; -- set a float in an array to a value (index on stack, float in SCRATCH_ZPWORD1, array in SCRATCH_ZPWORD2)
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda P8ESTACK_LO,x
|
||||||
asl a
|
asl a
|
||||||
asl a
|
asl a
|
||||||
clc
|
clc
|
||||||
adc c64.ESTACK_LO,x
|
adc P8ESTACK_LO,x
|
||||||
adc c64.SCRATCH_ZPWORD2
|
adc P8ZP_SCRATCH_W2
|
||||||
ldy c64.SCRATCH_ZPWORD2+1
|
ldy P8ZP_SCRATCH_W2+1
|
||||||
bcc +
|
bcc +
|
||||||
iny
|
iny
|
||||||
+ jmp copy_float
|
+ jmp copy_float
|
||||||
@ -785,12 +741,12 @@ set_array_float .proc
|
|||||||
swap_floats .proc
|
swap_floats .proc
|
||||||
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
|
; -- swap floats pointed to by SCRATCH_ZPWORD1, SCRATCH_ZPWORD2
|
||||||
ldy #4
|
ldy #4
|
||||||
- lda (c64.SCRATCH_ZPWORD1),y
|
- lda (P8ZP_SCRATCH_W1),y
|
||||||
pha
|
pha
|
||||||
lda (c64.SCRATCH_ZPWORD2),y
|
lda (P8ZP_SCRATCH_W2),y
|
||||||
sta (c64.SCRATCH_ZPWORD1),y
|
sta (P8ZP_SCRATCH_W1),y
|
||||||
pla
|
pla
|
||||||
sta (c64.SCRATCH_ZPWORD2),y
|
sta (P8ZP_SCRATCH_W2),y
|
||||||
dey
|
dey
|
||||||
bpl -
|
bpl -
|
||||||
rts
|
rts
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
|
|
||||||
%option enable_floats
|
%option enable_floats
|
||||||
|
|
||||||
|
|
||||||
c64flt {
|
c64flt {
|
||||||
; ---- this block contains C-64 floating point related functions ----
|
; ---- this block contains C-64 floating point related functions ----
|
||||||
|
|
||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
const float TWOPI = 6.283185307179586
|
const float TWOPI = 6.283185307179586
|
||||||
|
const float ZERO = 0.0
|
||||||
|
|
||||||
|
|
||||||
; ---- C64 basic and kernal ROM float constants and functions ----
|
; ---- C64 basic and kernal ROM float constants and functions ----
|
||||||
@ -35,13 +35,11 @@ c64flt {
|
|||||||
&float FL_TWOPI = $e2e5 ; 2 * PI
|
&float FL_TWOPI = $e2e5 ; 2 * PI
|
||||||
&float FL_FR4 = $e2ea ; .25
|
&float FL_FR4 = $e2ea ; .25
|
||||||
; oddly enough, 0.0 isn't available in the kernel.
|
; 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: 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.
|
; 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 $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 $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
|
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 $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 $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 $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 $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
|
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) {
|
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||||
%asm {{
|
%asm {{
|
||||||
sta c64.SCRATCH_ZPREG
|
sta P8ZP_SCRATCH_REG
|
||||||
tya
|
tya
|
||||||
ldy c64.SCRATCH_ZPREG
|
ldy P8ZP_SCRATCH_REG
|
||||||
jmp GIVAYF ; this uses the inverse order, Y/A
|
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
|
; ---- fac1 to signed word in A/Y
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
jsr FTOSWORDYA ; note the inverse Y/A order
|
||||||
sta c64.SCRATCH_ZPREG
|
sta P8ZP_SCRATCH_REG
|
||||||
tya
|
tya
|
||||||
ldy c64.SCRATCH_ZPREG
|
ldy P8ZP_SCRATCH_REG
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@ -185,41 +184,34 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
|||||||
; ---- fac1 to unsigned word in A/Y
|
; ---- fac1 to unsigned word in A/Y
|
||||||
%asm {{
|
%asm {{
|
||||||
jsr GETADR ; this uses the inverse order, Y/A
|
jsr GETADR ; this uses the inverse order, Y/A
|
||||||
sta c64.SCRATCH_ZPB1
|
sta P8ZP_SCRATCH_B1
|
||||||
tya
|
tya
|
||||||
ldy c64.SCRATCH_ZPB1
|
ldy P8ZP_SCRATCH_B1
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub print_f (float value) {
|
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 {{
|
%asm {{
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx P8ZP_SCRATCH_REG_X
|
||||||
lda #<value
|
lda #<value
|
||||||
ldy #>value
|
ldy #>value
|
||||||
jsr MOVFM ; load float into fac1
|
jsr MOVFM ; load float into fac1
|
||||||
jsr FOUT ; fac1 to string in A/Y
|
jsr FOUT ; fac1 to string in A/Y
|
||||||
jsr c64.STROUT ; print string in A/Y
|
sta P8ZP_SCRATCH_B1
|
||||||
ldx c64.SCRATCH_ZPREGX
|
sty P8ZP_SCRATCH_REG
|
||||||
rts
|
ldy #0
|
||||||
|
- lda (P8ZP_SCRATCH_B1),y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
|
+ 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", ""
|
%asminclude "library:c64floats.asm", ""
|
||||||
|
|
||||||
} ; ------ end of block c64flt
|
}
|
||||||
|
243
compiler/res/prog8lib/c64graphics.p8
Normal file
243
compiler/res/prog8lib/c64graphics.p8
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
%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]
|
||||||
|
; }
|
||||||
|
|
||||||
|
; TODO fix the use of X (or XY) as parameter so we can actually use this plot() routine
|
||||||
|
; calling it with a byte results in a compiler crash, calling it with word results in clobbering X register I think
|
||||||
|
asmsub plotXXX(uword plotx @XY, ubyte ploty @A) {
|
||||||
|
%asm {{
|
||||||
|
stx internal_plotx
|
||||||
|
sty internal_plotx+1
|
||||||
|
jmp internal_plot
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
uword internal_plotx ; 0..319 ; separate 'parameter' for internal_plot()
|
||||||
|
|
||||||
|
asmsub internal_plot(ubyte ploty @A) { ; internal_plotx is 16 bits 0 to 319... doesn't fit in a register
|
||||||
|
%asm {{
|
||||||
|
tay
|
||||||
|
stx P8ZP_SCRATCH_REG_X
|
||||||
|
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
|
||||||
|
|
||||||
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
|
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
|
||||||
|
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -7,15 +7,6 @@
|
|||||||
|
|
||||||
|
|
||||||
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_HI = $a0 ; software jiffy clock, hi byte
|
||||||
&ubyte TIME_MID = $a1 ; .. mid byte
|
&ubyte TIME_MID = $a1 ; .. mid byte
|
||||||
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
|
||||||
@ -183,19 +174,11 @@ c64 {
|
|||||||
; ---- end of SID registers ----
|
; ---- end of SID registers ----
|
||||||
|
|
||||||
|
|
||||||
|
; ---- C64 ROM kernal routines ----
|
||||||
; ---- 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 ----
|
|
||||||
|
|
||||||
romsub $AB1E = STROUT(uword strptr @ AY) clobbers(A, X, Y) ; print null-terminated string (use c64scr.print instead)
|
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 $EA31 = IRQDFRT() clobbers(A,X,Y) ; default IRQ routine
|
||||||
romsub $EA81 = IRQDFEND() clobbers(A,X,Y) ; default IRQ end/cleanup
|
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
|
romsub $FF81 = CINT() clobbers(A,X,Y) ; (alias: SCINIT) initialize screen editor and video chip
|
||||||
@ -238,6 +221,212 @@ 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 $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
|
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_REG_X
|
||||||
|
sta IRQ_SCRATCH_ZPREGX
|
||||||
|
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_ZPREGX
|
||||||
|
sta P8ZP_SCRATCH_REG_X
|
||||||
|
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_ZPREGX .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 ----
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
557
compiler/res/prog8lib/c64textio.p8
Normal file
557
compiler/res/prog8lib/c64textio.p8
Normal file
@ -0,0 +1,557 @@
|
|||||||
|
; 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
|
||||||
|
|
||||||
|
|
||||||
|
%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_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
jsr conv.ubyte2decimal
|
||||||
|
pha
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
jsr c64.CHROUT
|
||||||
|
txa
|
||||||
|
jsr c64.CHROUT
|
||||||
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
pha
|
||||||
|
cmp #0
|
||||||
|
bpl +
|
||||||
|
lda #'-'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
+ pla
|
||||||
|
jsr conv.byte2decimal
|
||||||
|
jsr print_ub._print_byte_digits
|
||||||
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_X
|
||||||
|
bcc +
|
||||||
|
pha
|
||||||
|
lda #'$'
|
||||||
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
+ jsr conv.ubyte2hex
|
||||||
|
jsr c64.CHROUT
|
||||||
|
tya
|
||||||
|
jsr c64.CHROUT
|
||||||
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
ldy #0
|
||||||
|
- lda conv.uword2decimal.decTenThousands,y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
+ ldx P8ZP_SCRATCH_REG_X
|
||||||
|
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_X
|
||||||
|
jsr conv.uword2decimal
|
||||||
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
|
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_X
|
||||||
|
tax
|
||||||
|
clc
|
||||||
|
jsr c64.PLOT
|
||||||
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
|
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_X
|
||||||
|
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_X
|
||||||
|
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
|
||||||
|
|
||||||
|
%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 ZERO = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
; ---- 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 {{
|
||||||
|
stx P8ZP_SCRATCH_REG_X
|
||||||
|
lda #<value
|
||||||
|
ldy #>value
|
||||||
|
jsr MOVFM ; load float into fac1
|
||||||
|
jsr FOUT ; fac1 to string in A/Y
|
||||||
|
sta P8ZP_SCRATCH_B1
|
||||||
|
sty P8ZP_SCRATCH_REG
|
||||||
|
ldy #0
|
||||||
|
- lda (P8ZP_SCRATCH_B1),y
|
||||||
|
beq +
|
||||||
|
jsr c64.CHROUT
|
||||||
|
iny
|
||||||
|
bne -
|
||||||
|
ldx P8ZP_SCRATCH_REG_X
|
||||||
|
+ rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
%asminclude "library:c64floats.asm", ""
|
||||||
|
|
||||||
|
}
|
221
compiler/res/prog8lib/cx16lib.p8
Normal file
221
compiler/res/prog8lib/cx16lib.p8
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
; 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
|
||||||
|
|
||||||
|
|
||||||
|
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 {
|
||||||
|
|
||||||
|
; ---- Commander X-16 additions on top of C64 kernal routines ----
|
||||||
|
; spelling of the names is taken from the Commander X-16 rom sources
|
||||||
|
|
||||||
|
; 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
|
||||||
|
&uword VERA_ADDR_L = VERA_BASE + $00
|
||||||
|
&uword VERA_ADDR_M = VERA_BASE + $01
|
||||||
|
&uword VERA_ADDR_H = VERA_BASE + $02
|
||||||
|
&uword VERA_DATA0 = VERA_BASE + $03
|
||||||
|
&uword VERA_DATA1 = VERA_BASE + $04
|
||||||
|
&uword VERA_CTRL = VERA_BASE + $05
|
||||||
|
&uword VERA_IEN = VERA_BASE + $06
|
||||||
|
&uword VERA_ISR = VERA_BASE + $07
|
||||||
|
&uword VERA_IRQ_LINE_L = VERA_BASE + $08
|
||||||
|
&uword VERA_DC_VIDEO = VERA_BASE + $09
|
||||||
|
&uword VERA_DC_HSCALE = VERA_BASE + $0A
|
||||||
|
&uword VERA_DC_VSCALE = VERA_BASE + $0B
|
||||||
|
&uword VERA_DC_BORDER = VERA_BASE + $0C
|
||||||
|
&uword VERA_DC_HSTART = VERA_BASE + $09
|
||||||
|
&uword VERA_DC_HSTOP = VERA_BASE + $0A
|
||||||
|
&uword VERA_DC_VSTART = VERA_BASE + $0B
|
||||||
|
&uword VERA_DC_VSTOP = VERA_BASE + $0C
|
||||||
|
&uword VERA_L0_CONFIG = VERA_BASE + $0D
|
||||||
|
&uword VERA_L0_MAPBASE = VERA_BASE + $0E
|
||||||
|
&uword VERA_L0_TILEBASE = VERA_BASE + $0F
|
||||||
|
&uword VERA_L0_HSCROLL_L = VERA_BASE + $10
|
||||||
|
&uword VERA_L0_HSCROLL_H = VERA_BASE + $11
|
||||||
|
&uword VERA_L0_VSCROLL_L = VERA_BASE + $12
|
||||||
|
&uword VERA_L0_VSCROLL_H = VERA_BASE + $13
|
||||||
|
&uword VERA_L1_CONFIG = VERA_BASE + $14
|
||||||
|
&uword VERA_L1_MAPBASE = VERA_BASE + $15
|
||||||
|
&uword VERA_L1_TILEBASE = VERA_BASE + $16
|
||||||
|
&uword VERA_L1_HSCROLL_L = VERA_BASE + $17
|
||||||
|
&uword VERA_L1_HSCROLL_H = VERA_BASE + $18
|
||||||
|
&uword VERA_L1_VSCROLL_L = VERA_BASE + $19
|
||||||
|
&uword VERA_L1_VSCROLL_H = VERA_BASE + $1A
|
||||||
|
&uword VERA_AUDIO_CTRL = VERA_BASE + $1B
|
||||||
|
&uword VERA_AUDIO_RATE = VERA_BASE + $1C
|
||||||
|
&uword VERA_AUDIO_DATA = VERA_BASE + $1D
|
||||||
|
&uword VERA_SPI_DATA = VERA_BASE + $1E
|
||||||
|
&uword VERA_SPI_CTRL = VERA_BASE + $1F
|
||||||
|
; VERA_PSG_BASE = $1F9C0
|
||||||
|
; VERA_PALETTE_BASE = $1FA00
|
||||||
|
; VERA_SPRITES_BASE = $1FC00
|
||||||
|
|
||||||
|
|
||||||
|
; supported C128 additions
|
||||||
|
romsub $ff4a = close_all()
|
||||||
|
romsub $ff59 = lkupla()
|
||||||
|
romsub $ff5c = lkupsa()
|
||||||
|
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()
|
||||||
|
romsub $ff6e = jsrfar()
|
||||||
|
romsub $ff74 = fetch()
|
||||||
|
romsub $ff77 = stash()
|
||||||
|
romsub $ff7a = cmpare()
|
||||||
|
romsub $ff7d = primm()
|
||||||
|
|
||||||
|
; X16 additions
|
||||||
|
romsub $ff44 = macptr()
|
||||||
|
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc)
|
||||||
|
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() ; uses vectors=r0
|
||||||
|
romsub $ff23 = GRAPH_clear()
|
||||||
|
romsub $ff26 = GRAPH_set_window() ; uses x=r0, y=r1, width=r2, height=r3
|
||||||
|
romsub $ff29 = GRAPH_set_colors(ubyte stroke @A, ubyte fill @X, ubyte background @Y)
|
||||||
|
romsub $ff2c = GRAPH_draw_line() ; uses x1=r0, y1=r1, x2=r2, y2=r3
|
||||||
|
romsub $ff2f = GRAPH_draw_rect(ubyte fill @Pc) ; uses x=r0, y=r1, width=r2, height=r3, cornerradius=r4
|
||||||
|
romsub $ff32 = GRAPH_move_rect() ; uses sx=r0, sy=r1, tx=r2, ty=r3, width=r4, height=r5
|
||||||
|
romsub $ff35 = GRAPH_draw_oval(ubyte fill @Pc) ; uses x=r0, y=r1, width=r2, height=r3
|
||||||
|
romsub $ff38 = GRAPH_draw_image() ; uses x=r0, y=r1, ptr=r2, width=r3, height=r4
|
||||||
|
romsub $ff3b = GRAPH_set_font() ; uses ptr=r0
|
||||||
|
romsub $ff3e = GRAPH_get_char_size(ubyte baseline @A, ubyte width @X, ubyte height_or_style @Y, ubyte is_control @Pc)
|
||||||
|
romsub $ff41 = GRAPH_put_char(ubyte char @A) ; uses x=r0, y=r1
|
||||||
|
|
||||||
|
; framebuffer
|
||||||
|
romsub $fef6 = FB_init()
|
||||||
|
romsub $fef9 = FB_get_info() -> byte @A ; also outputs width=r0, height=r1
|
||||||
|
romsub $fefc = FB_set_palette(ubyte index @A, ubyte bytecount @X) ; also uses pointer=r0
|
||||||
|
romsub $feff = FB_cursor_position() ; uses x=r0, y=r1
|
||||||
|
romsub $ff02 = FB_cursor_next_line() ; uses x=r0
|
||||||
|
romsub $ff05 = FB_get_pixel() -> ubyte @A
|
||||||
|
romsub $ff08 = FB_get_pixels() ; uses ptr=r0, count=r1
|
||||||
|
romsub $ff0b = FB_set_pixel(ubyte color @A)
|
||||||
|
romsub $ff0e = FB_set_pixels() ; uses ptr=r0, count=r1
|
||||||
|
romsub $ff11 = FB_set_8_pixels(ubyte pattern @A, ubyte color @X)
|
||||||
|
romsub $ff14 = FB_set_8_pixels_opaque(ubyte pattern @A, ubyte color1 @X, ubyte color2 @Y) ; also uses mask=r0L
|
||||||
|
romsub $ff17 = FB_fill_pixels(ubyte color @A) ; also uses count=r0, step=r1
|
||||||
|
romsub $ff1a = FB_filter_pixels() ; uses ptr=r0, count=r1
|
||||||
|
romsub $ff1d = FB_move_pixels() ; 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) -> ubyte @Pc ; also uses pixels=r0, mask=r1, bpp=r2L
|
||||||
|
romsub $fef3 = sprite_set_position(ubyte number @A) ; also uses x=r0 and y=r1
|
||||||
|
romsub $fee4 = memory_fill(ubyte value @A) ; uses address=r0, num_bytes=r1
|
||||||
|
romsub $fee7 = memory_copy() ; uses source=r0, target=r1, num_bytes=r2
|
||||||
|
romsub $feea = memory_crc() ; uses address=r0, num_bytes=r1 result->r2
|
||||||
|
romsub $feed = memory_decompress() ; uses input=r0, output=r1 result->r1
|
||||||
|
romsub $fedb = console_init() ; uses x=r0, y=r1, width=r2, height=r3
|
||||||
|
romsub $fede = console_put_char(ubyte char @A, ubyte wrapping @Pc)
|
||||||
|
romsub $fee1 = console_get_char() -> ubyte @A
|
||||||
|
romsub $fed8 = console_put_image() ; uses ptr=r0, width=r1, height=r2
|
||||||
|
romsub $fed5 = console_set_paging_message() ; uses messageptr=r0
|
||||||
|
romsub $fed2 = kbdbuf_put(ubyte key @A)
|
||||||
|
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
|
||||||
|
romsub $fecc = monitor()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; ---- 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
|
||||||
|
jsr c64.IOINIT
|
||||||
|
jsr c64.RESTOR
|
||||||
|
jsr c64.CINT
|
||||||
|
lda #0
|
||||||
|
tax
|
||||||
|
tay
|
||||||
|
clc
|
||||||
|
clv
|
||||||
|
cli
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
314
compiler/res/prog8lib/cx16textio.p8
Normal file
314
compiler/res/prog8lib/cx16textio.p8
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
; 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
|
||||||
|
|
||||||
|
|
||||||
|
%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
|
||||||
|
}}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
%import c64lib
|
|
||||||
|
|
||||||
math {
|
math {
|
||||||
%asminclude "library:math.asm", ""
|
%asminclude "library:math.asm", ""
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -4,8 +4,6 @@
|
|||||||
;
|
;
|
||||||
; indent format: TABS, size=8
|
; indent format: TABS, size=8
|
||||||
|
|
||||||
%import c64lib
|
|
||||||
|
|
||||||
prog8_lib {
|
prog8_lib {
|
||||||
%asminclude "library:prog8lib.asm", ""
|
%asminclude "library:prog8lib.asm", ""
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
3.2
|
4.1
|
||||||
|
@ -8,6 +8,7 @@ import prog8.compiler.target.CompilationTarget
|
|||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
|
import prog8.compiler.target.cx16.CX16MachineDefinition
|
||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
@ -35,12 +36,12 @@ fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDe
|
|||||||
|
|
||||||
private fun compileMain(args: Array<String>) {
|
private fun compileMain(args: Array<String>) {
|
||||||
val cli = CommandLineInterface("prog8compiler")
|
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 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 dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
|
||||||
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
|
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 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 'c64' and 'cx16' available", "c64")
|
||||||
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -52,7 +53,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
when(compilationTarget) {
|
when(compilationTarget) {
|
||||||
"c64" -> {
|
"c64" -> {
|
||||||
with(CompilationTarget) {
|
with(CompilationTarget) {
|
||||||
name = "c64"
|
name = "Commodore-64"
|
||||||
machine = C64MachineDefinition
|
machine = C64MachineDefinition
|
||||||
encodeString = { str, altEncoding ->
|
encodeString = { str, altEncoding ->
|
||||||
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||||
@ -63,8 +64,21 @@ private fun compileMain(args: Array<String>) {
|
|||||||
asmGenerator = ::AsmGen
|
asmGenerator = ::AsmGen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"cx16" -> {
|
||||||
|
with(CompilationTarget) {
|
||||||
|
name = "Commander X16"
|
||||||
|
machine = CX16MachineDefinition
|
||||||
|
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 -> {
|
else -> {
|
||||||
System.err.println("invalid compilation target")
|
System.err.println("invalid compilation target. Available are: c64, cx16")
|
||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,20 +135,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
if (compilationResult.programName.isEmpty())
|
if (compilationResult.programName.isEmpty())
|
||||||
println("\nCan't start emulator because no program was assembled.")
|
println("\nCan't start emulator because no program was assembled.")
|
||||||
else if(startEmulator) {
|
else if(startEmulator) {
|
||||||
for(emulator in listOf("x64sc", "x64")) {
|
CompilationTarget.machine.launchEmulator(compilationResult.programName)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,7 +288,9 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
|
|
||||||
override fun visit(assignment: Assignment) {
|
override fun visit(assignment: Assignment) {
|
||||||
val binExpr = assignment.value as? BinaryExpression
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
if(binExpr!=null && binExpr.left isSameAs assignment.target) {
|
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>
|
// we only support the inplace assignments of the form A = A <operator> <value>
|
||||||
assignment.target.accept(this)
|
assignment.target.accept(this)
|
||||||
output(" ${binExpr.operator}= ")
|
output(" ${binExpr.operator}= ")
|
||||||
@ -427,8 +429,4 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
whenChoice.statements.accept(this)
|
whenChoice.statements.accept(this)
|
||||||
outputln("")
|
outputln("")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(nopStatement: NopStatement) {
|
|
||||||
output("; NOP @ ${nopStatement.position} $nopStatement")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,9 @@ enum class DataType {
|
|||||||
STRUCT; // pass by reference
|
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) =
|
infix fun isAssignableTo(targetType: DataType) =
|
||||||
// what types are assignable to others, perhaps via a typecast, without loss of precision?
|
|
||||||
when(this) {
|
when(this) {
|
||||||
UBYTE -> targetType in setOf(UBYTE, WORD, UWORD, FLOAT)
|
UBYTE -> targetType in setOf(UBYTE, WORD, UWORD, FLOAT)
|
||||||
BYTE -> targetType in setOf(BYTE, WORD, FLOAT)
|
BYTE -> targetType in setOf(BYTE, WORD, FLOAT)
|
||||||
|
@ -16,6 +16,7 @@ import kotlin.math.abs
|
|||||||
|
|
||||||
|
|
||||||
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||||
|
val comparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
|
||||||
|
|
||||||
|
|
||||||
sealed class Expression: Node {
|
sealed class Expression: Node {
|
||||||
@ -294,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 inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
||||||
override fun constValue(program: Program): NumericLiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
val cv = expression.constValue(program) ?: return null
|
val cv = expression.constValue(program) ?: return null
|
||||||
return cv.castNoCheck(type)
|
val cast = cv.cast(type)
|
||||||
// val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
|
return if(cast.isValid)
|
||||||
// return LiteralValue.fromNumber(value.numericValue(), value.type, position).cast(type)
|
cast.valueOrZero()
|
||||||
|
else
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
@ -415,62 +418,66 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
|
|
||||||
operator fun compareTo(other: NumericLiteralValue): Int = number.toDouble().compareTo(other.number.toDouble())
|
operator fun compareTo(other: NumericLiteralValue): Int = number.toDouble().compareTo(other.number.toDouble())
|
||||||
|
|
||||||
fun castNoCheck(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)
|
if(type==targettype)
|
||||||
return this
|
return CastValue(true, this)
|
||||||
val numval = number.toDouble()
|
val numval = number.toDouble()
|
||||||
when(type) {
|
when(type) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(targettype== DataType.BYTE && numval <= 127)
|
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)
|
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)
|
if(targettype== DataType.FLOAT)
|
||||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
if(targettype== DataType.UBYTE && numval >= 0)
|
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)
|
if(targettype== DataType.UWORD && numval >= 0)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
if(targettype== DataType.WORD)
|
if(targettype== DataType.WORD)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
if(targettype== DataType.FLOAT)
|
if(targettype== DataType.FLOAT)
|
||||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(targettype== DataType.BYTE && numval <= 127)
|
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)
|
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)
|
if(targettype== DataType.WORD && numval <= 32767)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
if(targettype== DataType.FLOAT)
|
if(targettype== DataType.FLOAT)
|
||||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if(targettype== DataType.BYTE && numval >= -128 && numval <=127)
|
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)
|
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)
|
if(targettype== DataType.UWORD && numval >=0)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
if(targettype== DataType.FLOAT)
|
if(targettype== DataType.FLOAT)
|
||||||
return NumericLiteralValue(targettype, number.toDouble(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toDouble(), position))
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
if (targettype == DataType.BYTE && numval >= -128 && numval <=127)
|
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)
|
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)
|
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)
|
if (targettype == DataType.UWORD && numval >=0 && numval <= 65535)
|
||||||
return NumericLiteralValue(targettype, number.toInt(), position)
|
return CastValue(true, NumericLiteralValue(targettype, number.toInt(), position))
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
throw ExpressionError("can't cast $type into $targettype", position)
|
return CastValue(false, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,14 +587,14 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
if(num==null) {
|
if(num==null) {
|
||||||
// an array of UWORDs could possibly also contain AddressOfs, other stuff can't be casted
|
// an array of UWORDs could possibly also contain AddressOfs, other stuff can't be casted
|
||||||
if (elementType != DataType.UWORD || it !is AddressOf)
|
if (elementType != DataType.UWORD || it !is AddressOf)
|
||||||
return null
|
return null // can't cast a value of the array, abort
|
||||||
it
|
it
|
||||||
} else {
|
} else {
|
||||||
try {
|
val cast = num.cast(elementType)
|
||||||
num.castNoCheck(elementType)
|
if(cast.isValid)
|
||||||
} catch(x: ExpressionError) {
|
cast.valueOrZero()
|
||||||
return null
|
else
|
||||||
}
|
return null // can't cast a value of the array, abort
|
||||||
}
|
}
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
return ArrayLiteralValue(InferredTypes.InferredType.known(targettype), castArray, position = position)
|
return ArrayLiteralValue(InferredTypes.InferredType.known(targettype), castArray, position = position)
|
||||||
@ -635,7 +642,14 @@ class RangeExpr(var from: Expression,
|
|||||||
fromDt istype DataType.STR && toDt istype DataType.STR -> InferredTypes.knownFor(DataType.STR)
|
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.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
|
||||||
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
|
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 {
|
override fun toString(): String {
|
||||||
|
@ -118,6 +118,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
||||||
errors.err("for loop requires a variable to loop with", forLoop.position)
|
errors.err("for loop requires a variable to loop with", forLoop.position)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
fun checkLoopRangeValues() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
when (loopvar.datatype) {
|
when (loopvar.datatype) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
||||||
@ -142,6 +147,22 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
else -> errors.err("loop variable must be numeric type", forLoop.position)
|
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,7 +325,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||||
// Instead, their reference (address) should be passed (as an UWORD).
|
// Instead, their reference (address) should be passed (as an UWORD).
|
||||||
// TODO The language has no (typed) pointers at this time. Should this be handled better?
|
|
||||||
if(subroutine.parameters.any{it.type in PassByReferenceDatatypes }) {
|
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 to receive 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.")
|
||||||
}
|
}
|
||||||
@ -361,8 +381,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
val targetDt = assignment.target.inferType(program, assignment)
|
val targetDt = assignment.target.inferType(program, assignment)
|
||||||
if(assignment.value.inferType(program) != targetDt)
|
if(assignment.value.inferType(program) != targetDt) {
|
||||||
errors.err("assignment value is of different type as the target", assignment.value.position)
|
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.value is TypecastExpression) {
|
||||||
if(assignment.isAugmentable && targetDt.istype(DataType.FLOAT))
|
if(assignment.isAugmentable && targetDt.istype(DataType.FLOAT))
|
||||||
@ -542,7 +566,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
VarDeclType.MEMORY -> {
|
VarDeclType.MEMORY -> {
|
||||||
if(decl.arraysize!=null) {
|
if(decl.arraysize!=null) {
|
||||||
val arraySize = decl.arraysize!!.size() ?: 1
|
val arraySize = decl.arraysize!!.constIndex() ?: 1
|
||||||
when(decl.datatype) {
|
when(decl.datatype) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB ->
|
DataType.ARRAY_B, DataType.ARRAY_UB ->
|
||||||
if(arraySize > 256)
|
if(arraySize > 256)
|
||||||
@ -584,7 +608,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
// array length limits
|
// array length limits
|
||||||
if(decl.isArray) {
|
if(decl.isArray) {
|
||||||
val length = decl.arraysize!!.size() ?: 1
|
val length = decl.arraysize!!.constIndex() ?: 1
|
||||||
when (decl.datatype) {
|
when (decl.datatype) {
|
||||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
if(length==0 || length>256)
|
if(length==0 || length>256)
|
||||||
@ -771,12 +795,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||||
errors.err("bitwise operator can only be used on integer operands", expr.right.position)
|
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 TODO remove this restriction
|
|
||||||
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)
|
if(leftDt !in NumericDatatypes)
|
||||||
@ -869,7 +887,7 @@ 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
|
// in-place modification, can't be done on literals
|
||||||
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
|
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)
|
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
|
||||||
@ -918,9 +936,6 @@ internal class AstChecker(private val program: Program,
|
|||||||
val argIDt = arg.first.value.inferType(program)
|
val argIDt = arg.first.value.inferType(program)
|
||||||
if (!argIDt.isKnown)
|
if (!argIDt.isKnown)
|
||||||
return
|
return
|
||||||
if (target.asmParameterRegisters[arg.first.index].registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.XY, RegisterOrPair.X)
|
|
||||||
&& 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -960,7 +975,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(target is VarDecl) {
|
if(target is VarDecl) {
|
||||||
if(target.datatype !in IterableDatatypes)
|
if(target.datatype !in IterableDatatypes)
|
||||||
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
|
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
|
||||||
val arraysize = target.arraysize?.size()
|
val arraysize = target.arraysize?.constIndex()
|
||||||
if(arraysize!=null) {
|
if(arraysize!=null) {
|
||||||
// check out of bounds
|
// check out of bounds
|
||||||
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
|
||||||
@ -1045,7 +1060,10 @@ internal class AstChecker(private val program: Program,
|
|||||||
val targetStatement = target.targetStatement(program.namespace)
|
val targetStatement = target.targetStatement(program.namespace)
|
||||||
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
||||||
return targetStatement
|
return targetStatement
|
||||||
|
else if(targetStatement==null)
|
||||||
errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position)
|
errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position)
|
||||||
|
else
|
||||||
|
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", statement.position)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1078,7 +1096,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(value.type.istype(targetDt)) {
|
if(value.type.istype(targetDt)) {
|
||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.constIndex()
|
||||||
val arraySize = value.value.size
|
val arraySize = value.value.size
|
||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize<1 || arraySpecSize>256)
|
if(arraySpecSize<1 || arraySpecSize>256)
|
||||||
@ -1100,7 +1118,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(value.type.istype(targetDt)) {
|
if(value.type.istype(targetDt)) {
|
||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.constIndex()
|
||||||
val arraySize = value.value.size
|
val arraySize = value.value.size
|
||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize<1 || arraySpecSize>128)
|
if(arraySpecSize<1 || arraySpecSize>128)
|
||||||
@ -1123,7 +1141,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(!checkArrayValues(value, targetDt))
|
if(!checkArrayValues(value, targetDt))
|
||||||
return false
|
return false
|
||||||
val arraySize = value.value.size
|
val arraySize = value.value.size
|
||||||
val arraySpecSize = arrayspec.size()
|
val arraySpecSize = arrayspec.constIndex()
|
||||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||||
if(arraySpecSize < 1 || arraySpecSize>51)
|
if(arraySpecSize < 1 || arraySpecSize>51)
|
||||||
return err("float array length must be 1-51")
|
return err("float array length must be 1-51")
|
||||||
@ -1215,7 +1233,11 @@ internal class AstChecker(private val program: Program,
|
|||||||
is AddressOf -> it.identifier.heapId(program.namespace)
|
is AddressOf -> it.identifier.heapId(program.namespace)
|
||||||
is TypecastExpression -> {
|
is TypecastExpression -> {
|
||||||
val constVal = it.expression.constValue(program)
|
val constVal = it.expression.constValue(program)
|
||||||
constVal?.castNoCheck(it.type)?.number?.toInt() ?: -9999999
|
val cast = constVal?.cast(it.type)
|
||||||
|
if(cast==null || !cast.isValid)
|
||||||
|
-9999999
|
||||||
|
else
|
||||||
|
cast.valueOrZero().number.toInt()
|
||||||
}
|
}
|
||||||
else -> -9999999
|
else -> -9999999
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,9 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(block: Block) {
|
override fun visit(block: Block) {
|
||||||
|
if(block.name in CompilationTarget.machine.opcodeNames)
|
||||||
|
errors.err("can't use a cpu opcode name as a symbol: '${block.name}'", block.position)
|
||||||
|
|
||||||
val existing = blocks[block.name]
|
val existing = blocks[block.name]
|
||||||
if(existing!=null)
|
if(existing!=null)
|
||||||
nameError(block.name, block.position, existing)
|
nameError(block.name, block.position, existing)
|
||||||
|
@ -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 {
|
class ReplaceNode(val node: Node, val replacement: Node, val parent: Node) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
parent.replaceChildNode(node, replacement)
|
parent.replaceChildNode(node, replacement)
|
||||||
|
@ -51,8 +51,13 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
|
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
|
||||||
assignment))
|
assignment))
|
||||||
} else {
|
} else {
|
||||||
fun castLiteral(cvalue: NumericLiteralValue): List<IAstModification.ReplaceNode> =
|
fun castLiteral(cvalue: NumericLiteralValue): List<IAstModification.ReplaceNode> {
|
||||||
listOf(IAstModification.ReplaceNode(cvalue, cvalue.castNoCheck(targettype), cvalue.parent))
|
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)
|
val cvalue = assignment.value.constValue(program)
|
||||||
if(cvalue!=null) {
|
if(cvalue!=null) {
|
||||||
val number = cvalue.number.toDouble()
|
val number = cvalue.number.toDouble()
|
||||||
@ -109,17 +114,12 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
||||||
call as Node)
|
call as Node)
|
||||||
} else if(arg.second.value is NumericLiteralValue) {
|
} else if(arg.second.value is NumericLiteralValue) {
|
||||||
if(argtype.isAssignableTo(requiredType)) {
|
val cast = (arg.second.value as NumericLiteralValue).cast(requiredType)
|
||||||
try {
|
if(cast.isValid)
|
||||||
val castedValue = (arg.second.value as NumericLiteralValue).castNoCheck(requiredType)
|
|
||||||
modifications += IAstModification.ReplaceNode(
|
modifications += IAstModification.ReplaceNode(
|
||||||
call.args[arg.second.index],
|
call.args[arg.second.index],
|
||||||
castedValue,
|
cast.valueOrZero(),
|
||||||
call as Node)
|
call as Node)
|
||||||
} catch (x: ExpressionError) {
|
|
||||||
// cast failed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,8 +144,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
null -> { }
|
else -> { }
|
||||||
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return modifications
|
return modifications
|
||||||
@ -154,7 +153,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// warn about any implicit type casts to Float, because that may not be intended
|
// 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)) {
|
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
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -163,7 +162,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
// make sure the memory address is an uword
|
// make sure the memory address is an uword
|
||||||
val dt = memread.addressExpression.inferType(program)
|
val dt = memread.addressExpression.inferType(program)
|
||||||
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||||
val typecast = (memread.addressExpression as? NumericLiteralValue)?.castNoCheck(DataType.UWORD)
|
val typecast = (memread.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)?.valueOrZero()
|
||||||
?: TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
?: TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
||||||
return listOf(IAstModification.ReplaceNode(memread.addressExpression, typecast, memread))
|
return listOf(IAstModification.ReplaceNode(memread.addressExpression, typecast, memread))
|
||||||
}
|
}
|
||||||
@ -174,7 +173,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
// make sure the memory address is an uword
|
// make sure the memory address is an uword
|
||||||
val dt = memwrite.addressExpression.inferType(program)
|
val dt = memwrite.addressExpression.inferType(program)
|
||||||
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||||
val typecast = (memwrite.addressExpression as? NumericLiteralValue)?.castNoCheck(DataType.UWORD)
|
val typecast = (memwrite.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)?.valueOrZero()
|
||||||
?: TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
?: TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
||||||
return listOf(IAstModification.ReplaceNode(memwrite.addressExpression, typecast, memwrite))
|
return listOf(IAstModification.ReplaceNode(memwrite.addressExpression, typecast, memwrite))
|
||||||
}
|
}
|
||||||
@ -191,7 +190,9 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
if (returnValue.inferType(program).istype(subReturnType))
|
if (returnValue.inferType(program).istype(subReturnType))
|
||||||
return noModifications
|
return noModifications
|
||||||
if (returnValue is NumericLiteralValue) {
|
if (returnValue is NumericLiteralValue) {
|
||||||
returnStmt.value = returnValue.castNoCheck(subroutine.returntypes.single())
|
val cast = returnValue.cast(subroutine.returntypes.single())
|
||||||
|
if(cast.isValid)
|
||||||
|
returnStmt.value = cast.valueOrZero()
|
||||||
} else {
|
} else {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
returnValue,
|
returnValue,
|
||||||
|
@ -34,8 +34,9 @@ internal class VariousCleanups: AstWalker() {
|
|||||||
|
|
||||||
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
if(typecast.expression is NumericLiteralValue) {
|
if(typecast.expression is NumericLiteralValue) {
|
||||||
val value = (typecast.expression as NumericLiteralValue).castNoCheck(typecast.type)
|
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
|
return noModifications
|
||||||
|
@ -261,8 +261,8 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
member.isArray,
|
member.isArray,
|
||||||
true,
|
true,
|
||||||
member.position
|
member.position
|
||||||
) as Statement
|
)
|
||||||
}.toMutableList()
|
}.toMutableList<Statement>()
|
||||||
structHasBeenFlattened = true
|
structHasBeenFlattened = true
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
|
|
||||||
// a vardecl used only for subroutine parameters
|
// a vardecl used only for subroutine parameters
|
||||||
class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Position)
|
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 {
|
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
||||||
@ -300,7 +300,7 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
|
|||||||
return("ArrayIndex($index, pos=$position)")
|
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 value: Expression, override val position: Position) : Statement() {
|
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
|
||||||
@ -336,8 +336,8 @@ open class Assignment(var target: AssignTarget, var value: Expression, override
|
|||||||
get() {
|
get() {
|
||||||
val binExpr = value as? BinaryExpression
|
val binExpr = value as? BinaryExpression
|
||||||
if(binExpr!=null) {
|
if(binExpr!=null) {
|
||||||
if(binExpr.right !is BinaryExpression && binExpr.left isSameAs target)
|
if(binExpr.left isSameAs target)
|
||||||
return true // A = A <operator> v
|
return true // A = A <operator> Something
|
||||||
|
|
||||||
if(binExpr.operator in associativeOperators) {
|
if(binExpr.operator in associativeOperators) {
|
||||||
if (binExpr.left !is BinaryExpression && binExpr.right isSameAs target)
|
if (binExpr.left !is BinaryExpression && binExpr.right isSameAs target)
|
||||||
@ -449,9 +449,9 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
|||||||
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
|
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
|
||||||
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
||||||
value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource &&
|
value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource &&
|
||||||
value.arrayspec.size()!=null &&
|
value.arrayspec.constIndex()!=null &&
|
||||||
arrayindexed!!.arrayspec.size()!=null &&
|
arrayindexed!!.arrayspec.constIndex()!=null &&
|
||||||
value.arrayspec.size()==arrayindexed!!.arrayspec.size()
|
value.arrayspec.constIndex()==arrayindexed!!.arrayspec.constIndex()
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,6 +657,7 @@ class Subroutine(override val name: String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
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
|
fun amountOfRtsInAsm(): Int = statements
|
||||||
.asSequence()
|
.asSequence()
|
||||||
|
@ -21,6 +21,25 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
|||||||
return noModifications
|
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> {
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
val decls = scope.statements.filterIsInstance<VarDecl>()
|
val decls = scope.statements.filterIsInstance<VarDecl>()
|
||||||
val sub = scope.definingSubroutine()
|
val sub = scope.definingSubroutine()
|
||||||
|
@ -90,11 +90,8 @@ private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program,
|
|||||||
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
||||||
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type 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
|
// depending on the mach9ine and compiler options we may have to include some libraries
|
||||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) {
|
CompilationTarget.machine.importLibs(compilerOptions, importer, programAst)
|
||||||
importer.importLibraryModule(programAst, "c64lib")
|
|
||||||
importer.importLibraryModule(programAst, "c64utils")
|
|
||||||
}
|
|
||||||
|
|
||||||
// always import prog8lib and math
|
// always import prog8lib and math
|
||||||
importer.importLibraryModule(programAst, "math")
|
importer.importLibraryModule(programAst, "math")
|
||||||
@ -189,16 +186,16 @@ private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerO
|
|||||||
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
||||||
optimize: Boolean, compilerOptions: CompilationOptions): String {
|
optimize: Boolean, compilerOptions: CompilationOptions): String {
|
||||||
// asm generation directly from the Ast,
|
// asm generation directly from the Ast,
|
||||||
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
|
|
||||||
programAst.processAstBeforeAsmGeneration(errors)
|
programAst.processAstBeforeAsmGeneration(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
|
||||||
// printAst(programAst)
|
// printAst(programAst)
|
||||||
|
|
||||||
|
CompilationTarget.machine.initializeZeropage(compilerOptions)
|
||||||
val assembly = CompilationTarget.asmGenerator(
|
val assembly = CompilationTarget.asmGenerator(
|
||||||
programAst,
|
programAst,
|
||||||
errors,
|
errors,
|
||||||
zeropage,
|
CompilationTarget.machine.zeropage,
|
||||||
compilerOptions,
|
compilerOptions,
|
||||||
outputDir).compileToAssembly(optimize)
|
outputDir).compileToAssembly(optimize)
|
||||||
assembly.assemble(compilerOptions)
|
assembly.assemble(compilerOptions)
|
||||||
|
@ -8,6 +8,13 @@ class ZeropageDepletedError(message: String) : Exception(message)
|
|||||||
|
|
||||||
abstract class Zeropage(protected val options: CompilationOptions) {
|
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_REG_X : Int // temp storage for register X (the evaluation stack pointer)
|
||||||
|
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>>()
|
private val allocations = mutableMapOf<Int, Pair<String, DataType>>()
|
||||||
val free = mutableListOf<Int>() // subclasses must set this to the appropriate free locations.
|
val free = mutableListOf<Int>() // subclasses must set this to the appropriate free locations.
|
||||||
|
|
||||||
|
@ -1,16 +1,39 @@
|
|||||||
package prog8.compiler.target
|
package prog8.compiler.target
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.Zeropage
|
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_NEGATIVE: Double
|
||||||
val FLOAT_MAX_POSITIVE: Double
|
val FLOAT_MAX_POSITIVE: Double
|
||||||
val FLOAT_MEM_SIZE: Int
|
val FLOAT_MEM_SIZE: Int
|
||||||
val POINTER_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>
|
val opcodeNames: Set<String>
|
||||||
|
var zeropage: Zeropage
|
||||||
|
val initSystemProcname: String
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyPro
|
|||||||
val outFile = when (options.output) {
|
val outFile = when (options.output) {
|
||||||
OutputType.PRG -> {
|
OutputType.PRG -> {
|
||||||
command.add("--cbm-prg")
|
command.add("--cbm-prg")
|
||||||
println("\nCreating C-64 prg.")
|
println("\nCreating prg.")
|
||||||
prgFile
|
prgFile
|
||||||
}
|
}
|
||||||
OutputType.RAW -> {
|
OutputType.RAW -> {
|
||||||
|
@ -1,35 +1,97 @@
|
|||||||
package prog8.compiler.target.c64
|
package prog8.compiler.target.c64
|
||||||
|
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.ast.Program
|
||||||
import prog8.compiler.CompilerException
|
import prog8.compiler.*
|
||||||
import prog8.compiler.Zeropage
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.ZeropageType
|
|
||||||
import prog8.compiler.target.IMachineDefinition
|
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.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
object C64MachineDefinition: IMachineDefinition {
|
internal object C64MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
// 5-byte cbm MFLPT format limitations:
|
// 5-byte cbm MFLPT format limitations:
|
||||||
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
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_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||||
override val FLOAT_MEM_SIZE = 5
|
override val FLOAT_MEM_SIZE = 5
|
||||||
override val POINTER_MEM_SIZE = 2
|
override val POINTER_MEM_SIZE = 2
|
||||||
const val BASIC_LOAD_ADDRESS = 0x0801
|
override val BASIC_LOAD_ADDRESS = 0x0801
|
||||||
const val RAW_LOAD_ADDRESS = 0xc000
|
override val RAW_LOAD_ADDRESS = 0xc000
|
||||||
|
|
||||||
// the 2*256 byte evaluation stack (on which bytes, words, and even floats are stored during calculations)
|
// 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
|
// and some heavily used string constants derived from the two values above
|
||||||
const val ESTACK_LO_VALUE = 0xce00 // $ce00-$ceff inclusive
|
override val ESTACK_LO = 0xce00 // $ce00-$ceff inclusive
|
||||||
const val ESTACK_HI_VALUE = 0xcf00 // $cf00-$cfff inclusive
|
override val ESTACK_HI = 0xcf00 // $ce00-$ceff 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 fun getZeropage(compilerOptions: CompilationOptions) = C64Zeropage(compilerOptions)
|
override lateinit var zeropage: Zeropage
|
||||||
|
override val initSystemProcname = "c64.init_system"
|
||||||
|
|
||||||
|
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
|
// 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",
|
override val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
|
||||||
@ -43,15 +105,14 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
"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_REG_X = 0xfa // temp storage for register X (the evaluation stack pointer)
|
||||||
|
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) {
|
override val exitProgramStrategy: ExitProgramStrategy = when (options.zeropage) {
|
||||||
ZeropageType.BASICSAFE, ZeropageType.DONTUSE -> ExitProgramStrategy.CLEAN_EXIT
|
ZeropageType.BASICSAFE, ZeropageType.DONTUSE -> ExitProgramStrategy.CLEAN_EXIT
|
||||||
@ -107,19 +168,18 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
free.clear()
|
free.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(SCRATCH_B1 !in free)
|
require(SCRATCH_B1 !in free)
|
||||||
assert(SCRATCH_REG !in free)
|
require(SCRATCH_REG !in free)
|
||||||
assert(SCRATCH_REG_X !in free)
|
require(SCRATCH_REG_X !in free)
|
||||||
assert(SCRATCH_W1 !in free)
|
require(SCRATCH_W1 !in free)
|
||||||
assert(SCRATCH_W2 !in free)
|
require(SCRATCH_W2 !in free)
|
||||||
|
|
||||||
for (reserved in options.zpReserved)
|
for (reserved in options.zpReserved)
|
||||||
reserve(reserved)
|
reserve(reserved)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short): IMachineFloat {
|
||||||
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short) {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val zero = Mflpt5(0, 0, 0, 0, 0)
|
val zero = Mflpt5(0, 0, 0, 0, 0)
|
||||||
@ -166,7 +226,7 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toDouble(): Double {
|
override fun toDouble(): Double {
|
||||||
if (this == zero) return 0.0
|
if (this == zero) return 0.0
|
||||||
val exp = b0 - 128
|
val exp = b0 - 128
|
||||||
val sign = (b1.toInt() and 0x80) > 0
|
val sign = (b1.toInt() and 0x80) > 0
|
||||||
@ -174,5 +234,14 @@ object C64MachineDefinition: IMachineDefinition {
|
|||||||
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
val result = number.toDouble() * (2.0).pow(exp) / 0x100000000
|
||||||
return if (sign) -result else result
|
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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,17 +8,20 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.*
|
import prog8.compiler.*
|
||||||
|
import prog8.compiler.target.CompilationTarget
|
||||||
|
import prog8.compiler.target.CpuType
|
||||||
import prog8.compiler.target.IAssemblyGenerator
|
import prog8.compiler.target.IAssemblyGenerator
|
||||||
import prog8.compiler.target.IAssemblyProgram
|
import prog8.compiler.target.IAssemblyProgram
|
||||||
import prog8.compiler.target.c64.AssemblyProgram
|
import prog8.compiler.target.c64.AssemblyProgram
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
|
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.AssignmentAsmGen
|
||||||
|
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
|
||||||
import prog8.compiler.target.generatedLabelPrefix
|
import prog8.compiler.target.generatedLabelPrefix
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import prog8.functions.FSignature
|
import prog8.functions.FSignature
|
||||||
import java.math.RoundingMode
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
@ -76,16 +79,33 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
private fun header() {
|
private fun header() {
|
||||||
val ourName = this.javaClass.name
|
val ourName = this.javaClass.name
|
||||||
out("; 6502 assembly code for '${program.name}'")
|
val cpu = when(CompilationTarget.machine.cpu) {
|
||||||
|
CpuType.CPU6502 -> "6502"
|
||||||
|
CpuType.CPU65c02 -> "65c02"
|
||||||
|
else -> "unsupported"
|
||||||
|
}
|
||||||
|
|
||||||
|
out("; $cpu assembly code for '${program.name}'")
|
||||||
out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
|
out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
|
||||||
out("; assembler syntax is for the 64tasm cross-assembler")
|
out("; assembler syntax is for the 64tasm cross-assembler")
|
||||||
out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
|
out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
|
||||||
out("\n.cpu '6502'\n.enc 'none'\n")
|
out("\n.cpu '$cpu'\n.enc 'none'\n")
|
||||||
|
|
||||||
program.actualLoadAddress = program.definedLoadAddress
|
program.actualLoadAddress = program.definedLoadAddress
|
||||||
if (program.actualLoadAddress == 0) // fix load address
|
if (program.actualLoadAddress == 0) // fix load address
|
||||||
program.actualLoadAddress = if (options.launcher == LauncherType.BASIC)
|
program.actualLoadAddress = if (options.launcher == LauncherType.BASIC)
|
||||||
C64MachineDefinition.BASIC_LOAD_ADDRESS else C64MachineDefinition.RAW_LOAD_ADDRESS
|
CompilationTarget.machine.BASIC_LOAD_ADDRESS else CompilationTarget.machine.RAW_LOAD_ADDRESS
|
||||||
|
|
||||||
|
// the global prog8 variables needed
|
||||||
|
val zp = CompilationTarget.machine.zeropage
|
||||||
|
val initproc = CompilationTarget.machine.initSystemProcname
|
||||||
|
out("P8ZP_SCRATCH_B1 = ${zp.SCRATCH_B1}")
|
||||||
|
out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
|
||||||
|
out("P8ZP_SCRATCH_REG_X = ${zp.SCRATCH_REG_X}")
|
||||||
|
out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
|
||||||
|
out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
|
||||||
|
out("P8ESTACK_LO = ${CompilationTarget.machine.ESTACK_LO.toHex()}")
|
||||||
|
out("P8ESTACK_HI = ${CompilationTarget.machine.ESTACK_HI.toHex()}")
|
||||||
|
|
||||||
when {
|
when {
|
||||||
options.launcher == LauncherType.BASIC -> {
|
options.launcher == LauncherType.BASIC -> {
|
||||||
@ -100,14 +120,16 @@ internal class AsmGen(private val program: Program,
|
|||||||
out("_prog8_entrypoint\t; assembly code starts here\n")
|
out("_prog8_entrypoint\t; assembly code starts here\n")
|
||||||
out(" tsx")
|
out(" tsx")
|
||||||
out(" stx prog8_lib.orig_stackpointer")
|
out(" stx prog8_lib.orig_stackpointer")
|
||||||
out(" jsr prog8_lib.init_system")
|
if(!initproc.isNullOrEmpty())
|
||||||
|
out(" jsr $initproc")
|
||||||
}
|
}
|
||||||
options.output == OutputType.PRG -> {
|
options.output == OutputType.PRG -> {
|
||||||
out("; ---- program without basic sys call ----")
|
out("; ---- program without basic sys call ----")
|
||||||
out("* = ${program.actualLoadAddress.toHex()}\n")
|
out("* = ${program.actualLoadAddress.toHex()}\n")
|
||||||
out(" tsx")
|
out(" tsx")
|
||||||
out(" stx prog8_lib.orig_stackpointer")
|
out(" stx prog8_lib.orig_stackpointer")
|
||||||
out(" jsr prog8_lib.init_system")
|
if(!initproc.isNullOrEmpty())
|
||||||
|
out(" jsr $initproc")
|
||||||
}
|
}
|
||||||
options.output == OutputType.RAW -> {
|
options.output == OutputType.RAW -> {
|
||||||
out("; ---- raw assembler program ----")
|
out("; ---- raw assembler program ----")
|
||||||
@ -148,8 +170,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
// the global list of all floating point constants for the whole program
|
// the global list of all floating point constants for the whole program
|
||||||
out("; global float constants")
|
out("; global float constants")
|
||||||
for (flt in globalFloatConsts) {
|
for (flt in globalFloatConsts) {
|
||||||
val mflpt5 = C64MachineDefinition.Mflpt5.fromNumber(flt.key)
|
val floatFill = CompilationTarget.machine.getFloat(flt.key).makeFloatFillAsm()
|
||||||
val floatFill = makeFloatFill(mflpt5)
|
|
||||||
val floatvalue = flt.key
|
val floatvalue = flt.key
|
||||||
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
|
||||||
}
|
}
|
||||||
@ -182,10 +203,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
blockLevelVarInits.getValue(block).forEach { decl ->
|
blockLevelVarInits.getValue(block).forEach { decl ->
|
||||||
val scopedFullName = decl.makeScopedName(decl.name).split('.')
|
val scopedFullName = decl.makeScopedName(decl.name).split('.')
|
||||||
require(scopedFullName.first()==block.name)
|
require(scopedFullName.first()==block.name)
|
||||||
val target = AssignTarget(IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position)
|
assignInitialValueToVar(decl, scopedFullName.drop(1))
|
||||||
val assign = Assignment(target, decl.value!!, decl.position)
|
|
||||||
assign.linkParents(decl.parent)
|
|
||||||
assignmentAsmGen.translate(assign)
|
|
||||||
}
|
}
|
||||||
out(" rts\n .pend")
|
out(" rts\n .pend")
|
||||||
}
|
}
|
||||||
@ -193,6 +211,16 @@ internal class AsmGen(private val program: Program,
|
|||||||
out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
|
out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assignInitialValueToVar(decl: VarDecl, variableName: List<String>) {
|
||||||
|
val variable = IdentifierReference(variableName, decl.position)
|
||||||
|
variable.linkParents(decl.parent)
|
||||||
|
val asgn = AsmAssignment(
|
||||||
|
AsmAssignSource.fromAstSource(decl.value!!, program),
|
||||||
|
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, decl.datatype, variable = variable),
|
||||||
|
false, decl.position)
|
||||||
|
assignmentAsmGen.translateNormalAssignment(asgn)
|
||||||
|
}
|
||||||
|
|
||||||
private var generatedLabelSequenceNumber: Int = 0
|
private var generatedLabelSequenceNumber: Int = 0
|
||||||
|
|
||||||
internal fun makeLabel(postfix: String): String {
|
internal fun makeLabel(postfix: String): String {
|
||||||
@ -216,15 +244,6 @@ internal class AsmGen(private val program: Program,
|
|||||||
} else assemblyLines.add(fragment)
|
} else assemblyLines.add(fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeFloatFill(flt: C64MachineDefinition.Mflpt5): String {
|
|
||||||
val b0 = "$" + flt.b0.toString(16).padStart(2, '0')
|
|
||||||
val b1 = "$" + flt.b1.toString(16).padStart(2, '0')
|
|
||||||
val b2 = "$" + flt.b2.toString(16).padStart(2, '0')
|
|
||||||
val b3 = "$" + flt.b3.toString(16).padStart(2, '0')
|
|
||||||
val b4 = "$" + flt.b4.toString(16).padStart(2, '0')
|
|
||||||
return "$b0, $b1, $b2, $b3, $b4"
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun encode(str: String, altEncoding: Boolean): List<Short> {
|
private fun encode(str: String, altEncoding: Boolean): List<Short> {
|
||||||
val bytes = if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
val bytes = if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||||
return bytes.plus(0)
|
return bytes.plus(0)
|
||||||
@ -234,7 +253,6 @@ internal class AsmGen(private val program: Program,
|
|||||||
out("; vars allocated on zeropage")
|
out("; vars allocated on zeropage")
|
||||||
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
||||||
for(variable in variables) {
|
for(variable in variables) {
|
||||||
// should NOT allocate subroutine parameters on the zero page
|
|
||||||
val fullName = variable.makeScopedName(variable.name)
|
val fullName = variable.makeScopedName(variable.name)
|
||||||
val zpVar = allocatedZeropageVariables[fullName]
|
val zpVar = allocatedZeropageVariables[fullName]
|
||||||
if(zpVar==null) {
|
if(zpVar==null) {
|
||||||
@ -259,12 +277,13 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun vardecl2asm(decl: VarDecl) {
|
private fun vardecl2asm(decl: VarDecl) {
|
||||||
|
val name = decl.name
|
||||||
when (decl.datatype) {
|
when (decl.datatype) {
|
||||||
DataType.UBYTE -> out("${decl.name}\t.byte 0")
|
DataType.UBYTE -> out("$name\t.byte 0")
|
||||||
DataType.BYTE -> out("${decl.name}\t.char 0")
|
DataType.BYTE -> out("$name\t.char 0")
|
||||||
DataType.UWORD -> out("${decl.name}\t.word 0")
|
DataType.UWORD -> out("$name\t.word 0")
|
||||||
DataType.WORD -> out("${decl.name}\t.sint 0")
|
DataType.WORD -> out("$name\t.sint 0")
|
||||||
DataType.FLOAT -> out("${decl.name}\t.byte 0,0,0,0,0 ; float")
|
DataType.FLOAT -> out("$name\t.byte 0,0,0,0,0 ; float")
|
||||||
DataType.STRUCT -> {} // is flattened
|
DataType.STRUCT -> {} // is flattened
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
val str = decl.value as StringLiteralValue
|
val str = decl.value as StringLiteralValue
|
||||||
@ -273,9 +292,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
DataType.ARRAY_UB -> {
|
DataType.ARRAY_UB -> {
|
||||||
val data = makeArrayFillDataUnsigned(decl)
|
val data = makeArrayFillDataUnsigned(decl)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${decl.name}\t.byte ${data.joinToString()}")
|
out("$name\t.byte ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(decl.name)
|
out(name)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .byte " + chunk.joinToString())
|
out(" .byte " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
@ -283,9 +302,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
DataType.ARRAY_B -> {
|
DataType.ARRAY_B -> {
|
||||||
val data = makeArrayFillDataSigned(decl)
|
val data = makeArrayFillDataSigned(decl)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${decl.name}\t.char ${data.joinToString()}")
|
out("$name\t.char ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(decl.name)
|
out(name)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .char " + chunk.joinToString())
|
out(" .char " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
@ -293,9 +312,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
val data = makeArrayFillDataUnsigned(decl)
|
val data = makeArrayFillDataUnsigned(decl)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${decl.name}\t.word ${data.joinToString()}")
|
out("$name\t.word ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(decl.name)
|
out(name)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .word " + chunk.joinToString())
|
out(" .word " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
@ -303,9 +322,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
val data = makeArrayFillDataSigned(decl)
|
val data = makeArrayFillDataSigned(decl)
|
||||||
if (data.size <= 16)
|
if (data.size <= 16)
|
||||||
out("${decl.name}\t.sint ${data.joinToString()}")
|
out("$name\t.sint ${data.joinToString()}")
|
||||||
else {
|
else {
|
||||||
out(decl.name)
|
out(name)
|
||||||
for (chunk in data.chunked(16))
|
for (chunk in data.chunked(16))
|
||||||
out(" .sint " + chunk.joinToString())
|
out(" .sint " + chunk.joinToString())
|
||||||
}
|
}
|
||||||
@ -317,13 +336,13 @@ internal class AsmGen(private val program: Program,
|
|||||||
else {
|
else {
|
||||||
// no init value, use zeros
|
// no init value, use zeros
|
||||||
val zero = decl.zeroElementValue()
|
val zero = decl.zeroElementValue()
|
||||||
Array(decl.arraysize!!.size()!!) { zero }
|
Array(decl.arraysize!!.constIndex()!!) { zero }
|
||||||
}
|
}
|
||||||
val floatFills = array.map {
|
val floatFills = array.map {
|
||||||
val number = (it as NumericLiteralValue).number
|
val number = (it as NumericLiteralValue).number
|
||||||
makeFloatFill(C64MachineDefinition.Mflpt5.fromNumber(number))
|
CompilationTarget.machine.getFloat(number).makeFloatFillAsm()
|
||||||
}
|
}
|
||||||
out(decl.name)
|
out(name)
|
||||||
for (f in array.zip(floatFills))
|
for (f in array.zip(floatFills))
|
||||||
out(" .byte ${f.second} ; float ${f.first}")
|
out(" .byte ${f.second} ; float ${f.first}")
|
||||||
}
|
}
|
||||||
@ -392,7 +411,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
else {
|
else {
|
||||||
// no array init value specified, use a list of zeros
|
// no array init value specified, use a list of zeros
|
||||||
val zero = decl.zeroElementValue()
|
val zero = decl.zeroElementValue()
|
||||||
Array(decl.arraysize!!.size()!!) { zero }
|
Array(decl.arraysize!!.constIndex()!!) { zero }
|
||||||
}
|
}
|
||||||
return when (decl.datatype) {
|
return when (decl.datatype) {
|
||||||
DataType.ARRAY_UB ->
|
DataType.ARRAY_UB ->
|
||||||
@ -419,7 +438,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
else {
|
else {
|
||||||
// no array init value specified, use a list of zeros
|
// no array init value specified, use a list of zeros
|
||||||
val zero = decl.zeroElementValue()
|
val zero = decl.zeroElementValue()
|
||||||
Array(decl.arraysize!!.size()!!) { zero }
|
Array(decl.arraysize!!.constIndex()!!) { zero }
|
||||||
}
|
}
|
||||||
return when (decl.datatype) {
|
return when (decl.datatype) {
|
||||||
DataType.ARRAY_UB ->
|
DataType.ARRAY_UB ->
|
||||||
@ -454,116 +473,90 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getFloatConst(number: Double): String {
|
internal fun getFloatAsmConst(number: Double): String {
|
||||||
// try to match the ROM float constants to save memory
|
var asmName = CompilationTarget.machine.getFloatRomConst(number)
|
||||||
val mflpt5 = C64MachineDefinition.Mflpt5.fromNumber(number)
|
if(asmName.isNullOrEmpty()) {
|
||||||
val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
|
|
||||||
when {
|
|
||||||
floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_ZERO"
|
|
||||||
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 -> {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no ROM float const for this value, create our own
|
// no ROM float const for this value, create our own
|
||||||
val name = globalFloatConsts[number]
|
asmName = globalFloatConsts[number]
|
||||||
if(name!=null)
|
if(asmName==null) {
|
||||||
return name
|
asmName = "prog8_float_const_${globalFloatConsts.size}"
|
||||||
val newName = "prog8_float_const_${globalFloatConsts.size}"
|
globalFloatConsts[number] = asmName
|
||||||
globalFloatConsts[number] = newName
|
|
||||||
return newName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return asmName
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun signExtendAtoMsb(destination: String) =
|
internal fun asmSymbolName(identifier: IdentifierReference): String {
|
||||||
"""
|
return if(identifier.memberOfStruct(program.namespace)!=null) {
|
||||||
ora #$7f
|
val name = identifier.targetVarDecl(program.namespace)!!.name
|
||||||
bmi +
|
fixNameSymbols(name)
|
||||||
lda #0
|
|
||||||
+ sta $destination
|
|
||||||
"""
|
|
||||||
|
|
||||||
internal fun asmIdentifierName(identifier: IdentifierReference): String {
|
|
||||||
val name = if(identifier.memberOfStruct(program.namespace)!=null) {
|
|
||||||
identifier.targetVarDecl(program.namespace)!!.name
|
|
||||||
} else {
|
} else {
|
||||||
identifier.nameInSource.joinToString(".")
|
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun asmVariableName(identifier: IdentifierReference): String {
|
||||||
|
return if(identifier.memberOfStruct(program.namespace)!=null) {
|
||||||
|
val name = identifier.targetVarDecl(program.namespace)!!.name
|
||||||
|
fixNameSymbols(name)
|
||||||
|
} else {
|
||||||
|
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun loadByteFromPointerIntoA(pointervar: IdentifierReference): Pair<Boolean, String> {
|
||||||
|
// returns if the pointer is already on the ZP itself or not (in which case SCRATCH_W1 is used as intermediary)
|
||||||
|
val sourceName = asmVariableName(pointervar)
|
||||||
|
val vardecl = pointervar.targetVarDecl(program.namespace)!!
|
||||||
|
val scopedName = vardecl.makeScopedName(vardecl.name)
|
||||||
|
return if(scopedName in allocatedZeropageVariables) {
|
||||||
|
// pointervar is already in the zero page, no need to copy
|
||||||
|
out(" ldy #0 | lda ($sourceName),y")
|
||||||
|
Pair(true, sourceName)
|
||||||
|
} else {
|
||||||
|
out("""
|
||||||
|
lda $sourceName
|
||||||
|
ldy $sourceName+1
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #0
|
||||||
|
lda (P8ZP_SCRATCH_W1),y""")
|
||||||
|
Pair(false, sourceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun storeByteIntoPointer(pointervar: IdentifierReference, ldaInstructionArg: String?) {
|
||||||
|
val sourceName = asmVariableName(pointervar)
|
||||||
|
val vardecl = pointervar.targetVarDecl(program.namespace)!!
|
||||||
|
val scopedName = vardecl.makeScopedName(vardecl.name)
|
||||||
|
if(scopedName in allocatedZeropageVariables) {
|
||||||
|
// pointervar is already in the zero page, no need to copy
|
||||||
|
if(ldaInstructionArg!=null)
|
||||||
|
out(" lda $ldaInstructionArg")
|
||||||
|
out(" ldy #0 | sta ($sourceName),y")
|
||||||
|
} else {
|
||||||
|
out("""
|
||||||
|
ldy $sourceName
|
||||||
|
sty P8ZP_SCRATCH_W2
|
||||||
|
ldy $sourceName+1
|
||||||
|
sty P8ZP_SCRATCH_W2+1
|
||||||
|
${if(ldaInstructionArg==null) "" else "lda $ldaInstructionArg"}
|
||||||
|
ldy #0
|
||||||
|
sta (P8ZP_SCRATCH_W2),y""")
|
||||||
}
|
}
|
||||||
return fixNameSymbols(name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
internal fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
||||||
|
|
||||||
private fun branchInstruction(condition: BranchCondition, complement: Boolean) =
|
|
||||||
if(complement) {
|
|
||||||
when (condition) {
|
|
||||||
BranchCondition.CS -> "bcc"
|
|
||||||
BranchCondition.CC -> "bcs"
|
|
||||||
BranchCondition.EQ, BranchCondition.Z -> "beq"
|
|
||||||
BranchCondition.NE, BranchCondition.NZ -> "bne"
|
|
||||||
BranchCondition.VS -> "bvc"
|
|
||||||
BranchCondition.VC -> "bvs"
|
|
||||||
BranchCondition.MI, BranchCondition.NEG -> "bmi"
|
|
||||||
BranchCondition.PL, BranchCondition.POS -> "bpl"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
when (condition) {
|
|
||||||
BranchCondition.CS -> "bcs"
|
|
||||||
BranchCondition.CC -> "bcc"
|
|
||||||
BranchCondition.EQ, BranchCondition.Z -> "beq"
|
|
||||||
BranchCondition.NE, BranchCondition.NZ -> "bne"
|
|
||||||
BranchCondition.VS -> "bvs"
|
|
||||||
BranchCondition.VC -> "bvc"
|
|
||||||
BranchCondition.MI, BranchCondition.NEG -> "bmi"
|
|
||||||
BranchCondition.PL, BranchCondition.POS -> "bpl"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun readAndPushArrayvalueWithIndexA(arrayDt: DataType, variable: IdentifierReference) {
|
|
||||||
val variablename = asmIdentifierName(variable)
|
|
||||||
when (arrayDt) {
|
|
||||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
|
||||||
out(" tay | lda $variablename,y | sta $ESTACK_LO_HEX,x | dex")
|
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
|
||||||
out(" asl a | tay | lda $variablename,y | sta $ESTACK_LO_HEX,x | lda $variablename+1,y | sta $ESTACK_HI_HEX,x | dex")
|
|
||||||
DataType.ARRAY_F ->
|
|
||||||
// index * 5 is done in the subroutine that's called
|
|
||||||
out("""
|
|
||||||
sta $ESTACK_LO_HEX,x
|
|
||||||
dex
|
|
||||||
lda #<$variablename
|
|
||||||
ldy #>$variablename
|
|
||||||
jsr c64flt.push_float_from_indexed_var
|
|
||||||
""")
|
|
||||||
else ->
|
|
||||||
throw AssemblyError("weird array type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun saveRegister(register: CpuRegister) {
|
internal fun saveRegister(register: CpuRegister) {
|
||||||
when(register) {
|
when(register) {
|
||||||
CpuRegister.A -> out(" pha")
|
CpuRegister.A -> out(" pha")
|
||||||
CpuRegister.X -> out(" txa | pha")
|
CpuRegister.X -> {
|
||||||
|
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
|
||||||
|
out(" phx")
|
||||||
|
else
|
||||||
|
out(" stx P8ZP_SCRATCH_REG_X")
|
||||||
|
}
|
||||||
CpuRegister.Y -> out(" tya | pha")
|
CpuRegister.Y -> out(" tya | pha")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -571,36 +564,16 @@ internal class AsmGen(private val program: Program,
|
|||||||
internal fun restoreRegister(register: CpuRegister) {
|
internal fun restoreRegister(register: CpuRegister) {
|
||||||
when(register) {
|
when(register) {
|
||||||
CpuRegister.A -> out(" pla")
|
CpuRegister.A -> out(" pla")
|
||||||
CpuRegister.X -> out(" pla | tax")
|
CpuRegister.X -> {
|
||||||
|
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
|
||||||
|
out(" plx")
|
||||||
|
else
|
||||||
|
out(" ldx P8ZP_SCRATCH_REG_X")
|
||||||
|
}
|
||||||
CpuRegister.Y -> out(" pla | tay")
|
CpuRegister.Y -> out(" pla | tay")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateSubroutine(sub: Subroutine) {
|
|
||||||
out("")
|
|
||||||
outputSourceLine(sub)
|
|
||||||
|
|
||||||
if(sub.isAsmSubroutine) {
|
|
||||||
if(sub.asmAddress!=null)
|
|
||||||
return // already done at the memvars section
|
|
||||||
|
|
||||||
// asmsub with most likely just an inline asm in it
|
|
||||||
out("${sub.name}\t.proc")
|
|
||||||
sub.statements.forEach{ translate(it) }
|
|
||||||
out(" .pend\n")
|
|
||||||
} else {
|
|
||||||
// regular subroutine
|
|
||||||
out("${sub.name}\t.proc")
|
|
||||||
zeropagevars2asm(sub.statements)
|
|
||||||
memdefs2asm(sub.statements)
|
|
||||||
out("; statements")
|
|
||||||
sub.statements.forEach{ translate(it) }
|
|
||||||
out("; variables")
|
|
||||||
vardecls2asm(sub.statements)
|
|
||||||
out(" .pend\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun translate(stmt: Statement) {
|
internal fun translate(stmt: Statement) {
|
||||||
outputSourceLine(stmt)
|
outputSourceLine(stmt)
|
||||||
when(stmt) {
|
when(stmt) {
|
||||||
@ -652,6 +625,172 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun loadScaledArrayIndexIntoRegister(expr: ArrayIndexedExpression,
|
||||||
|
elementDt: DataType,
|
||||||
|
register: CpuRegister,
|
||||||
|
addOneExtra: Boolean=false) {
|
||||||
|
val reg = register.toString().toLowerCase()
|
||||||
|
val index = expr.arrayspec.index
|
||||||
|
if(index is NumericLiteralValue) {
|
||||||
|
val indexValue = index.number.toInt() * elementDt.memorySize() + if(addOneExtra) 1 else 0
|
||||||
|
out(" ld$reg #$indexValue")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addOneExtra) {
|
||||||
|
// add 1 to the result
|
||||||
|
if (index is IdentifierReference) {
|
||||||
|
val indexName = asmVariableName(index)
|
||||||
|
when(elementDt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
out(" ldy $indexName | iny")
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> out(" tya")
|
||||||
|
CpuRegister.X -> out(" tyx")
|
||||||
|
CpuRegister.Y -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
out(" lda $indexName | sec | rol a")
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> {}
|
||||||
|
CpuRegister.X -> out(" tax")
|
||||||
|
CpuRegister.Y -> out(" tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
require(DataType.FLOAT.memorySize()==5)
|
||||||
|
out("""
|
||||||
|
lda $indexName
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
sec
|
||||||
|
adc $indexName""")
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> {}
|
||||||
|
CpuRegister.X -> out(" tax")
|
||||||
|
CpuRegister.Y -> out(" tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expressionsAsmGen.translateExpression(index)
|
||||||
|
out("""
|
||||||
|
inc P8ESTACK_LO,x
|
||||||
|
bne +
|
||||||
|
inc P8ESTACK_HI,x
|
||||||
|
+""")
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> out(" inx | lda P8ESTACK_LO,x")
|
||||||
|
CpuRegister.X -> throw AssemblyError("can't use X here")
|
||||||
|
CpuRegister.Y -> out(" inx | ldy P8ESTACK_LO,x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (index is IdentifierReference) {
|
||||||
|
val indexName = asmVariableName(index)
|
||||||
|
when(elementDt) {
|
||||||
|
in ByteDatatypes -> out(" ld$reg $indexName")
|
||||||
|
in WordDatatypes -> {
|
||||||
|
out(" lda $indexName | asl a")
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> {}
|
||||||
|
CpuRegister.X -> out(" tax")
|
||||||
|
CpuRegister.Y -> out(" tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
require(DataType.FLOAT.memorySize()==5)
|
||||||
|
out("""
|
||||||
|
lda $indexName
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc $indexName""")
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> {}
|
||||||
|
CpuRegister.X -> out(" tax")
|
||||||
|
CpuRegister.Y -> out(" tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expressionsAsmGen.translateExpression(index)
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> out(" inx | lda P8ESTACK_LO,x")
|
||||||
|
CpuRegister.X -> throw AssemblyError("can't use X here")
|
||||||
|
CpuRegister.Y -> out(" inx | ldy P8ESTACK_LO,x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun translateExpression(expression: Expression) =
|
||||||
|
expressionsAsmGen.translateExpression(expression)
|
||||||
|
|
||||||
|
internal fun translateFunctioncallExpression(functionCall: FunctionCall, signature: FSignature) =
|
||||||
|
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature)
|
||||||
|
|
||||||
|
internal fun translateFunctionCall(functionCall: FunctionCall) =
|
||||||
|
functioncallAsmGen.translateFunctionCall(functionCall)
|
||||||
|
|
||||||
|
internal fun translateNormalAssignment(assign: AsmAssignment) =
|
||||||
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
|
|
||||||
|
private fun translateSubroutine(sub: Subroutine) {
|
||||||
|
out("")
|
||||||
|
outputSourceLine(sub)
|
||||||
|
|
||||||
|
if(sub.isAsmSubroutine) {
|
||||||
|
if(sub.asmAddress!=null)
|
||||||
|
return // already done at the memvars section
|
||||||
|
|
||||||
|
// asmsub with most likely just an inline asm in it
|
||||||
|
out("${sub.name}\t.proc")
|
||||||
|
sub.statements.forEach{ translate(it) }
|
||||||
|
out(" .pend\n")
|
||||||
|
} else {
|
||||||
|
// regular subroutine
|
||||||
|
out("${sub.name}\t.proc")
|
||||||
|
zeropagevars2asm(sub.statements)
|
||||||
|
memdefs2asm(sub.statements)
|
||||||
|
out("; statements")
|
||||||
|
sub.statements.forEach{ translate(it) }
|
||||||
|
out("; variables")
|
||||||
|
vardecls2asm(sub.statements)
|
||||||
|
out(" .pend\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun branchInstruction(condition: BranchCondition, complement: Boolean) =
|
||||||
|
if(complement) {
|
||||||
|
when (condition) {
|
||||||
|
BranchCondition.CS -> "bcc"
|
||||||
|
BranchCondition.CC -> "bcs"
|
||||||
|
BranchCondition.EQ, BranchCondition.Z -> "beq"
|
||||||
|
BranchCondition.NE, BranchCondition.NZ -> "bne"
|
||||||
|
BranchCondition.VS -> "bvc"
|
||||||
|
BranchCondition.VC -> "bvs"
|
||||||
|
BranchCondition.MI, BranchCondition.NEG -> "bmi"
|
||||||
|
BranchCondition.PL, BranchCondition.POS -> "bpl"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when (condition) {
|
||||||
|
BranchCondition.CS -> "bcs"
|
||||||
|
BranchCondition.CC -> "bcc"
|
||||||
|
BranchCondition.EQ, BranchCondition.Z -> "beq"
|
||||||
|
BranchCondition.NE, BranchCondition.NZ -> "bne"
|
||||||
|
BranchCondition.VS -> "bvs"
|
||||||
|
BranchCondition.VC -> "bvc"
|
||||||
|
BranchCondition.MI, BranchCondition.NEG -> "bmi"
|
||||||
|
BranchCondition.PL, BranchCondition.POS -> "bpl"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun translate(stmt: IfStatement) {
|
private fun translate(stmt: IfStatement) {
|
||||||
expressionsAsmGen.translateExpression(stmt.condition)
|
expressionsAsmGen.translateExpression(stmt.condition)
|
||||||
translateTestStack(stmt.condition.inferType(program).typeOrElse(DataType.STRUCT))
|
translateTestStack(stmt.condition.inferType(program).typeOrElse(DataType.STRUCT))
|
||||||
@ -667,8 +806,8 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
private fun translateTestStack(dataType: DataType) {
|
private fun translateTestStack(dataType: DataType) {
|
||||||
when(dataType) {
|
when(dataType) {
|
||||||
in ByteDatatypes -> out(" inx | lda $ESTACK_LO_HEX,x")
|
in ByteDatatypes -> out(" inx | lda P8ESTACK_LO,x")
|
||||||
in WordDatatypes -> out(" inx | lda $ESTACK_LO_HEX,x | ora $ESTACK_HI_HEX,x")
|
in WordDatatypes -> out(" inx | lda P8ESTACK_LO,x | ora P8ESTACK_HI,x")
|
||||||
DataType.FLOAT -> throw AssemblyError("conditional value should be an integer (boolean)")
|
DataType.FLOAT -> throw AssemblyError("conditional value should be an integer (boolean)")
|
||||||
else -> throw AssemblyError("non-numerical dt")
|
else -> throw AssemblyError("non-numerical dt")
|
||||||
}
|
}
|
||||||
@ -705,7 +844,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val vardecl = (stmt.iterations as IdentifierReference).targetStatement(program.namespace) as VarDecl
|
val vardecl = (stmt.iterations as IdentifierReference).targetStatement(program.namespace) as VarDecl
|
||||||
val name = asmIdentifierName(stmt.iterations as IdentifierReference)
|
val name = asmVariableName(stmt.iterations as IdentifierReference)
|
||||||
when(vardecl.datatype) {
|
when(vardecl.datatype) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
out(" lda $name")
|
out(" lda $name")
|
||||||
@ -723,11 +862,11 @@ internal class AsmGen(private val program: Program,
|
|||||||
val dt = stmt.iterations!!.inferType(program).typeOrElse(DataType.STRUCT)
|
val dt = stmt.iterations!!.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
when (dt) {
|
when (dt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
out(" inx | lda ${ESTACK_LO_HEX},x")
|
out(" inx | lda P8ESTACK_LO,x")
|
||||||
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
|
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
out(" inx | lda ${ESTACK_LO_HEX},x | ldy ${ESTACK_HI_HEX},x")
|
out(" inx | lda P8ESTACK_LO,x | ldy P8ESTACK_HI,x")
|
||||||
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
|
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invalid loop expression datatype $dt")
|
else -> throw AssemblyError("invalid loop expression datatype $dt")
|
||||||
@ -799,13 +938,13 @@ $counterVar .byte 0""")
|
|||||||
if(!conditionDt.isKnown)
|
if(!conditionDt.isKnown)
|
||||||
throw AssemblyError("unknown condition dt")
|
throw AssemblyError("unknown condition dt")
|
||||||
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
|
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
|
||||||
out(" inx | lda $ESTACK_LO_HEX,x | beq $endLabel")
|
out(" inx | lda P8ESTACK_LO,x | beq $endLabel")
|
||||||
} else {
|
} else {
|
||||||
out("""
|
out("""
|
||||||
inx
|
inx
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
bne +
|
bne +
|
||||||
lda $ESTACK_HI_HEX,x
|
lda P8ESTACK_HI,x
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
+ """)
|
+ """)
|
||||||
}
|
}
|
||||||
@ -826,13 +965,13 @@ $counterVar .byte 0""")
|
|||||||
if(!conditionDt.isKnown)
|
if(!conditionDt.isKnown)
|
||||||
throw AssemblyError("unknown condition dt")
|
throw AssemblyError("unknown condition dt")
|
||||||
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
|
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes) {
|
||||||
out(" inx | lda $ESTACK_LO_HEX,x | beq $repeatLabel")
|
out(" inx | lda P8ESTACK_LO,x | beq $repeatLabel")
|
||||||
} else {
|
} else {
|
||||||
out("""
|
out("""
|
||||||
inx
|
inx
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
bne +
|
bne +
|
||||||
lda $ESTACK_HI_HEX,x
|
lda P8ESTACK_HI,x
|
||||||
beq $repeatLabel
|
beq $repeatLabel
|
||||||
+ """)
|
+ """)
|
||||||
}
|
}
|
||||||
@ -848,9 +987,9 @@ $counterVar .byte 0""")
|
|||||||
if(!conditionDt.isKnown)
|
if(!conditionDt.isKnown)
|
||||||
throw AssemblyError("unknown condition dt")
|
throw AssemblyError("unknown condition dt")
|
||||||
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes)
|
if(conditionDt.typeOrElse(DataType.BYTE) in ByteDatatypes)
|
||||||
out(" inx | lda $ESTACK_LO_HEX,x")
|
out(" inx | lda P8ESTACK_LO,x")
|
||||||
else
|
else
|
||||||
out(" inx | lda $ESTACK_LO_HEX,x | ldy $ESTACK_HI_HEX,x")
|
out(" inx | lda P8ESTACK_LO,x | ldy P8ESTACK_HI,x")
|
||||||
for(choice in stmt.choices) {
|
for(choice in stmt.choices) {
|
||||||
val choiceLabel = makeLabel("choice")
|
val choiceLabel = makeLabel("choice")
|
||||||
if(choice.values==null) {
|
if(choice.values==null) {
|
||||||
@ -938,10 +1077,7 @@ $counterVar .byte 0""")
|
|||||||
} else {
|
} else {
|
||||||
val next = (stmt.parent as INameScope).nextSibling(stmt)
|
val next = (stmt.parent as INameScope).nextSibling(stmt)
|
||||||
if (next !is ForLoop || next.loopVar.nameInSource.single() != stmt.name) {
|
if (next !is ForLoop || next.loopVar.nameInSource.single() != stmt.name) {
|
||||||
val target = AssignTarget(IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
|
assignInitialValueToVar(stmt, listOf(stmt.name))
|
||||||
val assign = Assignment(target, stmt.value!!, stmt.position)
|
|
||||||
assign.linkParents(stmt.parent)
|
|
||||||
translate(assign)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -979,7 +1115,7 @@ $counterVar .byte 0""")
|
|||||||
return when {
|
return when {
|
||||||
jmp.identifier!=null -> {
|
jmp.identifier!=null -> {
|
||||||
val target = jmp.identifier.targetStatement(program.namespace)
|
val target = jmp.identifier.targetStatement(program.namespace)
|
||||||
val asmName = asmIdentifierName(jmp.identifier)
|
val asmName = asmSymbolName(jmp.identifier)
|
||||||
if(target is Label)
|
if(target is Label)
|
||||||
"_$asmName" // prefix with underscore to jump to local label
|
"_$asmName" // prefix with underscore to jump to local label
|
||||||
else
|
else
|
||||||
@ -1000,30 +1136,4 @@ $counterVar .byte 0""")
|
|||||||
val assembly = asm.assembly.trimEnd().trimStart('\n')
|
val assembly = asm.assembly.trimEnd().trimStart('\n')
|
||||||
assemblyLines.add(assembly)
|
assemblyLines.add(assembly)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateArrayIndexIntoA(expr: ArrayIndexedExpression) {
|
|
||||||
when (val index = expr.arrayspec.index) {
|
|
||||||
is NumericLiteralValue -> throw AssemblyError("this should be optimized directly")
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val indexName = asmIdentifierName(index)
|
|
||||||
out(" lda $indexName")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
expressionsAsmGen.translateExpression(index)
|
|
||||||
out(" inx | lda $ESTACK_LO_HEX,x")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun translateExpression(expression: Expression) =
|
|
||||||
expressionsAsmGen.translateExpression(expression)
|
|
||||||
|
|
||||||
internal fun translateFunctioncallExpression(functionCall: FunctionCall, signature: FSignature) =
|
|
||||||
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature)
|
|
||||||
|
|
||||||
internal fun translateFunctionCall(functionCall: FunctionCall) =
|
|
||||||
functioncallAsmGen.translateFunctionCall(functionCall)
|
|
||||||
|
|
||||||
internal fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) =
|
|
||||||
assignmentAsmGen.assignToRegister(reg, value, identifier)
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
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
|
// 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
|
// the repeated lda can be removed
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for(lines in linesByFour) {
|
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[1].value.trim().startsWith("cmp ") &&
|
||||||
lines[2].value.trim().startsWith("beq ") &&
|
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
|
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.
|
// this is a lot harder for word values because the instruction sequence varies.
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for(lines in linesByFour) {
|
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[1].value.trim()=="dex" &&
|
||||||
lines[2].value.trim()=="inx" &&
|
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[1].index, true, null))
|
||||||
mods.add(Modification(lines[2].index, true, null))
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
mods.add(Modification(lines[3].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") &&
|
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 nineth = pair[8].value.trimStart()
|
||||||
val tenth = pair[9].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()
|
val fourteenth = pair[13].value.trimStart()
|
||||||
|
|
||||||
if(eighth.startsWith("lda") && nineth.startsWith("ldy") && tenth.startsWith("sta") && eleventh.startsWith("sty") &&
|
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)) {
|
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
||||||
// identical float init
|
// identical float init
|
||||||
|
@ -1,681 +0,0 @@
|
|||||||
package prog8.compiler.target.c64.codegen
|
|
||||||
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
import prog8.compiler.AssemblyError
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
|
||||||
import prog8.compiler.toHex
|
|
||||||
|
|
||||||
|
|
||||||
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
|
||||||
|
|
||||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen)
|
|
||||||
|
|
||||||
internal fun translate(assign: Assignment) {
|
|
||||||
when {
|
|
||||||
assign.value is NumericLiteralValue -> translateConstantValueAssignment(assign)
|
|
||||||
assign.value is IdentifierReference -> translateVariableAssignment(assign)
|
|
||||||
assign.isAugmentable -> augmentableAsmGen.translate(assign)
|
|
||||||
else -> translateOtherAssignment(assign)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) {
|
|
||||||
if(value!=null) {
|
|
||||||
asmgen.out(" ld${reg.toString().toLowerCase()} #${value.toHex()}")
|
|
||||||
} else if(identifier!=null) {
|
|
||||||
val name = asmgen.asmIdentifierName(identifier)
|
|
||||||
asmgen.out(" ld${reg.toString().toLowerCase()} $name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateVariableAssignment(assign: Assignment) {
|
|
||||||
val identifier = assign.value as IdentifierReference
|
|
||||||
when (val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT)) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> assignFromByteVariable(assign.target, identifier)
|
|
||||||
DataType.UWORD, DataType.WORD -> assignFromWordVariable(assign.target, identifier)
|
|
||||||
DataType.FLOAT -> assignFromFloatVariable(assign.target, identifier)
|
|
||||||
in PassByReferenceDatatypes -> assignFromAddressOf(assign.target, identifier)
|
|
||||||
else -> throw AssemblyError("unsupported assignment target type $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun translateConstantValueAssignment(assign: Assignment) {
|
|
||||||
val numVal = assign.value as NumericLiteralValue
|
|
||||||
when (numVal.type) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> assignFromByteConstant(assign.target, numVal.number.toShort())
|
|
||||||
DataType.UWORD, DataType.WORD -> assignFromWordConstant(assign.target, numVal.number.toInt())
|
|
||||||
DataType.FLOAT -> assignFromFloatConstant(assign.target, numVal.number.toDouble())
|
|
||||||
else -> throw AssemblyError("weird numval type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun translateOtherAssignment(assign: Assignment) {
|
|
||||||
when (assign.value) {
|
|
||||||
is AddressOf -> {
|
|
||||||
val identifier = (assign.value as AddressOf).identifier
|
|
||||||
assignFromAddressOf(assign.target, identifier)
|
|
||||||
}
|
|
||||||
is DirectMemoryRead -> {
|
|
||||||
val read = (assign.value as DirectMemoryRead)
|
|
||||||
when (read.addressExpression) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
val address = (read.addressExpression as NumericLiteralValue).number.toInt()
|
|
||||||
assignFromMemoryByte(assign.target, address, null)
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
assignFromMemoryByte(assign.target, null, read.addressExpression as IdentifierReference)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.translateExpression(read.addressExpression)
|
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | inx")
|
|
||||||
assignFromRegister(assign.target, CpuRegister.A)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is PrefixExpression -> {
|
|
||||||
asmgen.translateExpression(assign.value as PrefixExpression)
|
|
||||||
assignFromEvalResult(assign.target)
|
|
||||||
}
|
|
||||||
is BinaryExpression -> {
|
|
||||||
asmgen.translateExpression(assign.value as BinaryExpression)
|
|
||||||
assignFromEvalResult(assign.target)
|
|
||||||
}
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
val arrayExpr = assign.value as ArrayIndexedExpression
|
|
||||||
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
|
||||||
val index = arrayExpr.arrayspec.index
|
|
||||||
if (index is NumericLiteralValue) {
|
|
||||||
// constant array index value
|
|
||||||
val arrayVarName = asmgen.asmIdentifierName(arrayExpr.identifier)
|
|
||||||
val indexValue = index.number.toInt() * ArrayElementTypes.getValue(arrayDt).memorySize()
|
|
||||||
when (arrayDt) {
|
|
||||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
|
||||||
asmgen.out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | dex")
|
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
|
||||||
asmgen.out(" lda $arrayVarName+$indexValue | sta $ESTACK_LO_HEX,x | lda $arrayVarName+$indexValue+1 | sta $ESTACK_HI_HEX,x | dex")
|
|
||||||
DataType.ARRAY_F ->
|
|
||||||
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_float")
|
|
||||||
else ->
|
|
||||||
throw AssemblyError("weird array type")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
asmgen.translateArrayIndexIntoA(arrayExpr)
|
|
||||||
asmgen.readAndPushArrayvalueWithIndexA(arrayDt, arrayExpr.identifier)
|
|
||||||
}
|
|
||||||
assignFromEvalResult(assign.target)
|
|
||||||
}
|
|
||||||
is TypecastExpression -> {
|
|
||||||
val cast = assign.value as TypecastExpression
|
|
||||||
val sourceType = cast.expression.inferType(program)
|
|
||||||
if (sourceType.isKnown &&
|
|
||||||
(sourceType.typeOrElse(DataType.STRUCT) in ByteDatatypes && cast.type in ByteDatatypes) ||
|
|
||||||
(sourceType.typeOrElse(DataType.STRUCT) in WordDatatypes && cast.type in WordDatatypes)) {
|
|
||||||
// no need for a type cast
|
|
||||||
assign.value = cast.expression
|
|
||||||
translate(assign)
|
|
||||||
} else {
|
|
||||||
asmgen.translateExpression(assign.value as TypecastExpression)
|
|
||||||
assignFromEvalResult(assign.target)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is FunctionCall -> {
|
|
||||||
asmgen.translateExpression(assign.value as FunctionCall)
|
|
||||||
assignFromEvalResult(assign.target)
|
|
||||||
}
|
|
||||||
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array assignment $assign")
|
|
||||||
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values ${assign.value.position}")
|
|
||||||
else -> throw AssemblyError("assignment value type ${assign.value} should have been handled elsewhere")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assignFromEvalResult(target: AssignTarget) {
|
|
||||||
val targetIdent = target.identifier
|
|
||||||
val targetArrayIdx = target.arrayindexed
|
|
||||||
val targetMemory = target.memoryAddress
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
when (val targetDt = targetIdent.inferType(program).typeOrElse(DataType.STRUCT)) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
|
||||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $targetName")
|
|
||||||
}
|
|
||||||
DataType.UWORD, DataType.WORD -> {
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda $ESTACK_LO_HEX,x
|
|
||||||
sta $targetName
|
|
||||||
lda $ESTACK_HI_HEX,x
|
|
||||||
sta $targetName+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$targetName
|
|
||||||
ldy #>$targetName
|
|
||||||
jsr c64flt.pop_float
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird target variable type $targetDt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
targetMemory != null -> {
|
|
||||||
asmgen.out(" inx")
|
|
||||||
storeByteViaRegisterAInMemoryAddress("$ESTACK_LO_HEX,x", targetMemory)
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
|
||||||
val arrayVarName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
asmgen.translateExpression(targetArrayIdx.arrayspec.index)
|
|
||||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
|
||||||
popAndWriteArrayvalueWithIndexA(arrayDt, arrayVarName)
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird assignment target $target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assignFromAddressOf(target: AssignTarget, name: IdentifierReference) {
|
|
||||||
val targetIdent = target.identifier
|
|
||||||
val targetArrayIdx = target.arrayindexed
|
|
||||||
val struct = name.memberOfStruct(program.namespace)
|
|
||||||
val sourceName = if (struct != null) {
|
|
||||||
// take the address of the first struct member instead
|
|
||||||
val decl = name.targetVarDecl(program.namespace)!!
|
|
||||||
val firstStructMember = struct.nameOfFirstMember()
|
|
||||||
// find the flattened var that belongs to this first struct member
|
|
||||||
val firstVarName = listOf(decl.name, firstStructMember)
|
|
||||||
val firstVar = name.definingScope().lookup(firstVarName, name) as VarDecl
|
|
||||||
firstVar.name
|
|
||||||
} else {
|
|
||||||
asmgen.fixNameSymbols(name.nameInSource.joinToString("."))
|
|
||||||
}
|
|
||||||
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$sourceName
|
|
||||||
ldy #>$sourceName
|
|
||||||
sta $targetName
|
|
||||||
sty $targetName+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
target.memoryAddress != null -> {
|
|
||||||
throw AssemblyError("no asm gen for assign address $sourceName to memory word $target")
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
throw AssemblyError("no asm gen for assign address $sourceName to array $targetName [ $index ]")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign address $sourceName to $target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) {
|
|
||||||
val sourceName = asmgen.asmIdentifierName(variable)
|
|
||||||
val targetIdent = target.identifier
|
|
||||||
val targetArrayIdx = target.arrayindexed
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
asmgen.out("""
|
|
||||||
lda $sourceName
|
|
||||||
ldy $sourceName+1
|
|
||||||
sta $targetName
|
|
||||||
sty $targetName+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
target.memoryAddress != null -> {
|
|
||||||
throw AssemblyError("no asm gen for assign wordvar $sourceName to memory ${target.memoryAddress}")
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
asmgen.out(" lda $sourceName | sta $ESTACK_LO_HEX,x | lda $sourceName+1 | sta $ESTACK_HI_HEX,x | dex")
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
|
||||||
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
|
||||||
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign wordvar to $target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) {
|
|
||||||
val sourceName = asmgen.asmIdentifierName(variable)
|
|
||||||
val targetIdent = target.identifier
|
|
||||||
val targetArrayIdx = target.arrayindexed
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
asmgen.out("""
|
|
||||||
lda $sourceName
|
|
||||||
sta $targetName
|
|
||||||
lda $sourceName+1
|
|
||||||
sta $targetName+1
|
|
||||||
lda $sourceName+2
|
|
||||||
sta $targetName+2
|
|
||||||
lda $sourceName+3
|
|
||||||
sta $targetName+3
|
|
||||||
lda $sourceName+4
|
|
||||||
sta $targetName+4
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr c64flt.push_float")
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out(" lda #<$targetName | ldy #>$targetName | jsr c64flt.pop_float_to_indexed_var")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign floatvar to $target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assignFromByteVariable(target: AssignTarget, variable: IdentifierReference) {
|
|
||||||
val sourceName = asmgen.asmIdentifierName(variable)
|
|
||||||
val targetIdent = target.identifier
|
|
||||||
val targetArrayIdx = target.arrayindexed
|
|
||||||
val targetMemory = target.memoryAddress
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
asmgen.out("""
|
|
||||||
lda $sourceName
|
|
||||||
sta $targetName
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
targetMemory != null -> {
|
|
||||||
storeByteViaRegisterAInMemoryAddress(sourceName, targetMemory)
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
|
||||||
asmgen.out(" lda $sourceName | sta $ESTACK_LO_HEX,x | dex")
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
|
||||||
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign bytevar to $target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assignFromRegister(target: AssignTarget, register: CpuRegister) {
|
|
||||||
val targetIdent = target.identifier
|
|
||||||
val targetArrayIdx = target.arrayindexed
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
asmgen.out(" st${register.name.toLowerCase()} $targetName")
|
|
||||||
}
|
|
||||||
target.memoryAddress != null -> {
|
|
||||||
storeRegisterInMemoryAddress(register, target.memoryAddress)
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
when (index) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
val memindex = index.number.toInt()
|
|
||||||
when (register) {
|
|
||||||
CpuRegister.A -> asmgen.out(" sta $targetName+$memindex")
|
|
||||||
CpuRegister.X -> asmgen.out(" stx $targetName+$memindex")
|
|
||||||
CpuRegister.Y -> asmgen.out(" sty $targetName+$memindex")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
when (register) {
|
|
||||||
CpuRegister.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
|
|
||||||
CpuRegister.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
|
|
||||||
CpuRegister.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
|
||||||
lda ${asmgen.asmIdentifierName(index)}
|
|
||||||
tay
|
|
||||||
lda ${C64Zeropage.SCRATCH_B1}
|
|
||||||
sta $targetName,y
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.saveRegister(register)
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.restoreRegister(register)
|
|
||||||
when (register) {
|
|
||||||
CpuRegister.A -> asmgen.out(" sta ${C64Zeropage.SCRATCH_B1}")
|
|
||||||
CpuRegister.X -> asmgen.out(" stx ${C64Zeropage.SCRATCH_B1}")
|
|
||||||
CpuRegister.Y -> asmgen.out(" sty ${C64Zeropage.SCRATCH_B1}")
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda $ESTACK_LO_HEX,x
|
|
||||||
tay
|
|
||||||
lda ${C64Zeropage.SCRATCH_B1}
|
|
||||||
sta $targetName,y
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign register $register to $target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun storeByteViaRegisterAInMemoryAddress(ldaInstructionArg: String, memoryAddress: DirectMemoryWrite) {
|
|
||||||
val addressExpr = memoryAddress.addressExpression
|
|
||||||
val addressLv = addressExpr as? NumericLiteralValue
|
|
||||||
when {
|
|
||||||
addressLv != null -> {
|
|
||||||
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
|
|
||||||
}
|
|
||||||
addressExpr is IdentifierReference -> {
|
|
||||||
val pointerVarName = asmgen.asmIdentifierName(addressExpr)
|
|
||||||
asmgen.out("""
|
|
||||||
lda $pointerVarName
|
|
||||||
sta ${C64Zeropage.SCRATCH_W2}
|
|
||||||
lda $pointerVarName+1
|
|
||||||
sta ${C64Zeropage.SCRATCH_W2+1}
|
|
||||||
lda $ldaInstructionArg
|
|
||||||
ldy #0
|
|
||||||
sta (${C64Zeropage.SCRATCH_W2}),y""")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.translateExpression(addressExpr)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda $ESTACK_LO_HEX,x
|
|
||||||
sta ${C64Zeropage.SCRATCH_W2}
|
|
||||||
lda $ESTACK_HI_HEX,x
|
|
||||||
sta ${C64Zeropage.SCRATCH_W2+1}
|
|
||||||
lda $ldaInstructionArg
|
|
||||||
ldy #0
|
|
||||||
sta (${C64Zeropage.SCRATCH_W2}),y""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun storeRegisterInMemoryAddress(register: CpuRegister, memoryAddress: DirectMemoryWrite) {
|
|
||||||
// this is optimized for register A.
|
|
||||||
val addressExpr = memoryAddress.addressExpression
|
|
||||||
val addressLv = addressExpr as? NumericLiteralValue
|
|
||||||
val registerName = register.name.toLowerCase()
|
|
||||||
when {
|
|
||||||
addressLv != null -> {
|
|
||||||
asmgen.out(" st$registerName ${addressLv.number.toHex()}")
|
|
||||||
}
|
|
||||||
addressExpr is IdentifierReference -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(addressExpr)
|
|
||||||
when (register) {
|
|
||||||
CpuRegister.A -> {}
|
|
||||||
CpuRegister.X -> asmgen.out(" txa")
|
|
||||||
CpuRegister.Y -> asmgen.out(" tya")
|
|
||||||
}
|
|
||||||
asmgen.out("""
|
|
||||||
ldy $targetName
|
|
||||||
sty ${C64Zeropage.SCRATCH_W1}
|
|
||||||
ldy $targetName+1
|
|
||||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
|
||||||
ldy #0
|
|
||||||
sta (${C64Zeropage.SCRATCH_W1}),y""")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
asmgen.saveRegister(register)
|
|
||||||
asmgen.translateExpression(addressExpr)
|
|
||||||
asmgen.restoreRegister(CpuRegister.A)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
ldy $ESTACK_LO_HEX,x
|
|
||||||
sty ${C64Zeropage.SCRATCH_W1}
|
|
||||||
ldy $ESTACK_HI_HEX,x
|
|
||||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
|
||||||
ldy #0
|
|
||||||
sta (${C64Zeropage.SCRATCH_W1}),y""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assignFromWordConstant(target: AssignTarget, word: Int) {
|
|
||||||
val targetIdent = target.identifier
|
|
||||||
val targetArrayIdx = target.arrayindexed
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
if (word ushr 8 == word and 255) {
|
|
||||||
// lsb=msb
|
|
||||||
asmgen.out("""
|
|
||||||
lda #${(word and 255).toHex()}
|
|
||||||
sta $targetName
|
|
||||||
sta $targetName+1
|
|
||||||
""")
|
|
||||||
} else {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<${word.toHex()}
|
|
||||||
ldy #>${word.toHex()}
|
|
||||||
sta $targetName
|
|
||||||
sty $targetName+1
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
target.memoryAddress != null -> {
|
|
||||||
throw AssemblyError("no asm gen for assign word $word to memory ${target.memoryAddress}")
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda $ESTACK_LO_HEX,x
|
|
||||||
asl a
|
|
||||||
tay
|
|
||||||
lda #<${word.toHex()}
|
|
||||||
sta $targetName,y
|
|
||||||
lda #>${word.toHex()}
|
|
||||||
sta $targetName+1,y
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign word $word to $target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assignFromByteConstant(target: AssignTarget, byte: Short) {
|
|
||||||
val targetIdent = target.identifier
|
|
||||||
val targetArrayIdx = target.arrayindexed
|
|
||||||
val targetMemory = target.memoryAddress
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
asmgen.out(" lda #${byte.toHex()} | sta $targetName ")
|
|
||||||
}
|
|
||||||
targetMemory != null -> {
|
|
||||||
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", targetMemory)
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
ldy $ESTACK_LO_HEX,x
|
|
||||||
lda #${byte.toHex()}
|
|
||||||
sta $targetName,y
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign byte $byte to $target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assignFromFloatConstant(target: AssignTarget, float: Double) {
|
|
||||||
val targetIdent = target.identifier
|
|
||||||
val targetArrayIdx = target.arrayindexed
|
|
||||||
if (float == 0.0) {
|
|
||||||
// optimized case for float zero
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
asmgen.out("""
|
|
||||||
lda #0
|
|
||||||
sta $targetName
|
|
||||||
sta $targetName+1
|
|
||||||
sta $targetName+2
|
|
||||||
sta $targetName+3
|
|
||||||
sta $targetName+4
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
if (index is NumericLiteralValue) {
|
|
||||||
val indexValue = index.number.toInt() * C64MachineDefinition.FLOAT_MEM_SIZE
|
|
||||||
asmgen.out("""
|
|
||||||
lda #0
|
|
||||||
sta $targetName+$indexValue
|
|
||||||
sta $targetName+$indexValue+1
|
|
||||||
sta $targetName+$indexValue+2
|
|
||||||
sta $targetName+$indexValue+3
|
|
||||||
sta $targetName+$indexValue+4
|
|
||||||
""")
|
|
||||||
} else {
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<${targetName}
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
|
||||||
lda #>${targetName}
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1 + 1}
|
|
||||||
jsr c64flt.set_0_array_float
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign float 0.0 to $target")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// non-zero value
|
|
||||||
val constFloat = asmgen.getFloatConst(float)
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
asmgen.out("""
|
|
||||||
lda $constFloat
|
|
||||||
sta $targetName
|
|
||||||
lda $constFloat+1
|
|
||||||
sta $targetName+1
|
|
||||||
lda $constFloat+2
|
|
||||||
sta $targetName+2
|
|
||||||
lda $constFloat+3
|
|
||||||
sta $targetName+3
|
|
||||||
lda $constFloat+4
|
|
||||||
sta $targetName+4
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val arrayVarName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
if (index is NumericLiteralValue) {
|
|
||||||
val indexValue = index.number.toInt() * C64MachineDefinition.FLOAT_MEM_SIZE
|
|
||||||
asmgen.out("""
|
|
||||||
lda $constFloat
|
|
||||||
sta $arrayVarName+$indexValue
|
|
||||||
lda $constFloat+1
|
|
||||||
sta $arrayVarName+$indexValue+1
|
|
||||||
lda $constFloat+2
|
|
||||||
sta $arrayVarName+$indexValue+2
|
|
||||||
lda $constFloat+3
|
|
||||||
sta $arrayVarName+$indexValue+3
|
|
||||||
lda $constFloat+4
|
|
||||||
sta $arrayVarName+$indexValue+4
|
|
||||||
""")
|
|
||||||
} else {
|
|
||||||
asmgen.translateExpression(index)
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<${constFloat}
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
|
||||||
lda #>${constFloat}
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1 + 1}
|
|
||||||
lda #<${arrayVarName}
|
|
||||||
sta ${C64Zeropage.SCRATCH_W2}
|
|
||||||
lda #>${arrayVarName}
|
|
||||||
sta ${C64Zeropage.SCRATCH_W2 + 1}
|
|
||||||
jsr c64flt.set_array_float
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign float $float to $target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) {
|
|
||||||
val targetIdent = target.identifier
|
|
||||||
val targetArrayIdx = target.arrayindexed
|
|
||||||
val targetMemory = target.memoryAddress
|
|
||||||
if (address != null) {
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
asmgen.out("""
|
|
||||||
lda ${address.toHex()}
|
|
||||||
sta $targetName
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
targetMemory != null -> {
|
|
||||||
storeByteViaRegisterAInMemoryAddress(address.toHex(), targetMemory)
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
throw AssemblyError("no asm gen for assign memory byte at $address to array $targetName [ $index ]")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign memory byte $target")
|
|
||||||
}
|
|
||||||
} else if (identifier != null) {
|
|
||||||
val sourceName = asmgen.asmIdentifierName(identifier)
|
|
||||||
when {
|
|
||||||
targetIdent != null -> {
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
|
||||||
asmgen.out("""
|
|
||||||
lda $sourceName
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
|
||||||
lda $sourceName+1
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1+1}
|
|
||||||
ldy #0
|
|
||||||
lda (${C64Zeropage.SCRATCH_W1}),y
|
|
||||||
sta $targetName""")
|
|
||||||
}
|
|
||||||
targetMemory != null -> {
|
|
||||||
storeByteViaRegisterAInMemoryAddress(sourceName, targetMemory)
|
|
||||||
}
|
|
||||||
targetArrayIdx != null -> {
|
|
||||||
val index = targetArrayIdx.arrayspec.index
|
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
|
||||||
throw AssemblyError("no asm gen for assign memory byte $sourceName to array $targetName [ $index ]")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("no asm gen for assign memory byte $target")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun popAndWriteArrayvalueWithIndexA(arrayDt: DataType, variablename: String) {
|
|
||||||
when (arrayDt) {
|
|
||||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B ->
|
|
||||||
asmgen.out(" tay | inx | lda $ESTACK_LO_HEX,x | sta $variablename,y")
|
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W ->
|
|
||||||
asmgen.out(" asl a | tay | inx | lda $ESTACK_LO_HEX,x | sta $variablename,y | lda $ESTACK_HI_HEX,x | sta $variablename+1,y")
|
|
||||||
DataType.ARRAY_F ->
|
|
||||||
// index * 5 is done in the subroutine that's called
|
|
||||||
asmgen.out("""
|
|
||||||
sta $ESTACK_LO_HEX,x
|
|
||||||
dex
|
|
||||||
lda #<$variablename
|
|
||||||
ldy #>$variablename
|
|
||||||
jsr c64flt.pop_float_to_indexed_var
|
|
||||||
""")
|
|
||||||
else ->
|
|
||||||
throw AssemblyError("weird array type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
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.expressions.*
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.compiler.AssemblyError
|
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.compiler.toHex
|
||||||
import prog8.functions.FSignature
|
import prog8.functions.FSignature
|
||||||
|
|
||||||
@ -47,8 +42,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
"ln", "log2", "sqrt", "rad",
|
"ln", "log2", "sqrt", "rad",
|
||||||
"deg", "round", "floor", "ceil",
|
"deg", "round", "floor", "ceil",
|
||||||
"rdnf" -> funcVariousFloatFuncs(fcall, func, functionName)
|
"rdnf" -> funcVariousFloatFuncs(fcall, func, functionName)
|
||||||
"lsl" -> funcLsl(fcall)
|
|
||||||
"lsr" -> funcLsr(fcall)
|
|
||||||
"rol" -> funcRol(fcall)
|
"rol" -> funcRol(fcall)
|
||||||
"rol2" -> funcRol2(fcall)
|
"rol2" -> funcRol2(fcall)
|
||||||
"ror" -> funcRor(fcall)
|
"ror" -> funcRor(fcall)
|
||||||
@ -58,7 +51,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
"rsave" -> {
|
"rsave" -> {
|
||||||
// save cpu status flag and all registers A, X, Y.
|
// save cpu status flag and all registers A, X, Y.
|
||||||
// see http://6502.org/tutorials/register_preservation.html
|
// 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" -> {
|
"rrestore" -> {
|
||||||
// restore all registers and cpu status flag
|
// restore all registers and cpu status flag
|
||||||
@ -79,15 +72,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val variable = fcall.args.single()
|
val variable = fcall.args.single()
|
||||||
if (variable is IdentifierReference) {
|
if (variable is IdentifierReference) {
|
||||||
val decl = variable.targetVarDecl(program.namespace)!!
|
val decl = variable.targetVarDecl(program.namespace)!!
|
||||||
val varName = asmgen.asmIdentifierName(variable)
|
val varName = asmgen.asmVariableName(variable)
|
||||||
val numElements = decl.arraysize!!.size()
|
val numElements = decl.arraysize!!.constIndex()
|
||||||
when (decl.datatype) {
|
when (decl.datatype) {
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
ldy #>$varName
|
ldy #>$varName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements
|
lda #$numElements
|
||||||
jsr prog8_lib.reverse_b
|
jsr prog8_lib.reverse_b
|
||||||
""")
|
""")
|
||||||
@ -96,8 +89,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
ldy #>$varName
|
ldy #>$varName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements
|
lda #$numElements
|
||||||
jsr prog8_lib.reverse_w
|
jsr prog8_lib.reverse_w
|
||||||
""")
|
""")
|
||||||
@ -106,8 +99,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
ldy #>$varName
|
ldy #>$varName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements
|
lda #$numElements
|
||||||
jsr prog8_lib.reverse_f
|
jsr prog8_lib.reverse_f
|
||||||
""")
|
""")
|
||||||
@ -121,17 +114,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
val variable = fcall.args.single()
|
val variable = fcall.args.single()
|
||||||
if (variable is IdentifierReference) {
|
if (variable is IdentifierReference) {
|
||||||
val decl = variable.targetVarDecl(program.namespace)!!
|
val decl = variable.targetVarDecl(program.namespace)!!
|
||||||
val varName = asmgen.asmIdentifierName(variable)
|
val varName = asmgen.asmVariableName(variable)
|
||||||
val numElements = decl.arraysize!!.size()
|
val numElements = decl.arraysize!!.constIndex()
|
||||||
when (decl.datatype) {
|
when (decl.datatype) {
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
ldy #>$varName
|
ldy #>$varName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements
|
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")
|
asmgen.out(if (decl.datatype == DataType.ARRAY_UB) " jsr prog8_lib.sort_ub" else " jsr prog8_lib.sort_b")
|
||||||
}
|
}
|
||||||
@ -139,10 +132,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$varName
|
lda #<$varName
|
||||||
ldy #>$varName
|
ldy #>$varName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
sty P8ZP_SCRATCH_W1+1
|
||||||
lda #$numElements
|
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")
|
asmgen.out(if (decl.datatype == DataType.ARRAY_UW) " jsr prog8_lib.sort_uw" else " jsr prog8_lib.sort_w")
|
||||||
}
|
}
|
||||||
@ -174,7 +167,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -188,7 +181,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
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 |+ ")
|
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -217,16 +210,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.translateExpression(what.addressExpression)
|
asmgen.translateExpression(what.addressExpression)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
lda $ESTACK_HI_HEX,x
|
lda P8ESTACK_HI,x
|
||||||
sta (+) + 2
|
sta (+) + 2
|
||||||
+ ror ${'$'}ffff ; modified
|
+ ror ${'$'}ffff ; modified
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" ror $variable")
|
asmgen.out(" ror $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -240,7 +233,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" ror $variable+1 | ror $variable")
|
asmgen.out(" ror $variable+1 | ror $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -271,7 +264,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -285,7 +278,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
|
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -314,16 +307,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.translateExpression(what.addressExpression)
|
asmgen.translateExpression(what.addressExpression)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
lda $ESTACK_HI_HEX,x
|
lda P8ESTACK_HI,x
|
||||||
sta (+) + 2
|
sta (+) + 2
|
||||||
+ rol ${'$'}ffff ; modified
|
+ rol ${'$'}ffff ; modified
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" rol $variable")
|
asmgen.out(" rol $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -337,7 +330,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmVariableName(what)
|
||||||
asmgen.out(" rol $variable | rol $variable+1")
|
asmgen.out(" rol $variable | rol $variable+1")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -347,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) {
|
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, functionName: String) {
|
||||||
translateFunctionArguments(fcall.args, func)
|
translateFunctionArguments(fcall.args, func)
|
||||||
asmgen.out(" jsr c64flt.func_$functionName")
|
asmgen.out(" jsr c64flt.func_$functionName")
|
||||||
@ -516,16 +383,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun funcStrlen(fcall: IFunctionCall) {
|
private fun funcStrlen(fcall: IFunctionCall) {
|
||||||
outputPushAddressOfIdentifier(fcall.args[0])
|
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
|
||||||
asmgen.out(" jsr prog8_lib.func_strlen")
|
asmgen.out("""
|
||||||
|
lda #<$name
|
||||||
|
ldy #>$name
|
||||||
|
jsr prog8_lib.strlen
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun funcSwap(fcall: IFunctionCall) {
|
private fun funcSwap(fcall: IFunctionCall) {
|
||||||
val first = fcall.args[0]
|
val first = fcall.args[0]
|
||||||
val second = fcall.args[1]
|
val second = fcall.args[1]
|
||||||
if(first is IdentifierReference && second is IdentifierReference) {
|
if(first is IdentifierReference && second is IdentifierReference) {
|
||||||
val firstName = asmgen.asmIdentifierName(first)
|
val firstName = asmgen.asmVariableName(first)
|
||||||
val secondName = asmgen.asmIdentifierName(second)
|
val secondName = asmgen.asmVariableName(second)
|
||||||
val dt = first.inferType(program)
|
val dt = first.inferType(program)
|
||||||
if(dt.istype(DataType.BYTE) || dt.istype(DataType.UBYTE)) {
|
if(dt.istype(DataType.BYTE) || dt.istype(DataType.UBYTE)) {
|
||||||
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | tya | sta $secondName")
|
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | tya | sta $secondName")
|
||||||
@ -547,13 +419,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
if(dt.istype(DataType.FLOAT)) {
|
if(dt.istype(DataType.FLOAT)) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$firstName
|
lda #<$firstName
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
sta P8ZP_SCRATCH_W1
|
||||||
lda #>$firstName
|
lda #>$firstName
|
||||||
sta ${C64Zeropage.SCRATCH_W1+1}
|
sta P8ZP_SCRATCH_W1+1
|
||||||
lda #<$secondName
|
lda #<$secondName
|
||||||
sta ${C64Zeropage.SCRATCH_W2}
|
sta P8ZP_SCRATCH_W2
|
||||||
lda #>$secondName
|
lda #>$secondName
|
||||||
sta ${C64Zeropage.SCRATCH_W2+1}
|
sta P8ZP_SCRATCH_W2+1
|
||||||
jsr c64flt.swap_floats
|
jsr c64flt.swap_floats
|
||||||
""")
|
""")
|
||||||
return
|
return
|
||||||
@ -576,8 +448,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun funcMkword(fcall: IFunctionCall, func: FSignature) {
|
private fun funcMkword(fcall: IFunctionCall, func: FSignature) {
|
||||||
translateFunctionArguments(fcall.args, func)
|
// trick: push the args in reverse order (msb first, lsb second) this saves some instructions
|
||||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x")
|
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) {
|
private fun funcMsb(fcall: IFunctionCall) {
|
||||||
@ -587,11 +461,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
if (arg is NumericLiteralValue)
|
if (arg is NumericLiteralValue)
|
||||||
throw AssemblyError("msb(const) should have been const-folded away")
|
throw AssemblyError("msb(const) should have been const-folded away")
|
||||||
if (arg is IdentifierReference) {
|
if (arg is IdentifierReference) {
|
||||||
val sourceName = asmgen.asmIdentifierName(arg)
|
val sourceName = asmgen.asmVariableName(arg)
|
||||||
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
asmgen.out(" lda $sourceName+1 | sta P8ESTACK_LO,x | dex")
|
||||||
} else {
|
} else {
|
||||||
asmgen.translateExpression(arg)
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,8 +476,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
if (arg is NumericLiteralValue)
|
if (arg is NumericLiteralValue)
|
||||||
throw AssemblyError("lsb(const) should have been const-folded away")
|
throw AssemblyError("lsb(const) should have been const-folded away")
|
||||||
if (arg is IdentifierReference) {
|
if (arg is IdentifierReference) {
|
||||||
val sourceName = asmgen.asmIdentifierName(arg)
|
val sourceName = asmgen.asmVariableName(arg)
|
||||||
asmgen.out(" lda $sourceName | sta $ESTACK_LO_HEX,x | dex")
|
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
|
||||||
} else {
|
} else {
|
||||||
asmgen.translateExpression(arg)
|
asmgen.translateExpression(arg)
|
||||||
// just ignore any high-byte
|
// just ignore any high-byte
|
||||||
@ -612,27 +486,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
|
|
||||||
private fun outputPushAddressAndLenghtOfArray(arg: Expression) {
|
private fun outputPushAddressAndLenghtOfArray(arg: Expression) {
|
||||||
arg as IdentifierReference
|
arg as IdentifierReference
|
||||||
val identifierName = asmgen.asmIdentifierName(arg)
|
val identifierName = asmgen.asmVariableName(arg)
|
||||||
val size = arg.targetVarDecl(program.namespace)!!.arraysize!!.size()!!
|
val size = arg.targetVarDecl(program.namespace)!!.arraysize!!.constIndex()!!
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<$identifierName
|
lda #<$identifierName
|
||||||
sta $ESTACK_LO_HEX,x
|
sta P8ESTACK_LO,x
|
||||||
lda #>$identifierName
|
lda #>$identifierName
|
||||||
sta $ESTACK_HI_HEX,x
|
sta P8ESTACK_HI,x
|
||||||
dex
|
dex
|
||||||
lda #$size
|
lda #$size
|
||||||
sta $ESTACK_LO_HEX,x
|
sta P8ESTACK_LO,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
|
|
||||||
dex
|
dex
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,8 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.CpuType
|
||||||
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.toHex
|
import prog8.compiler.toHex
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
@ -20,7 +16,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
when(expression) {
|
when(expression) {
|
||||||
is PrefixExpression -> translateExpression(expression)
|
is PrefixExpression -> translateExpression(expression)
|
||||||
is BinaryExpression -> translateExpression(expression)
|
is BinaryExpression -> translateExpression(expression)
|
||||||
is ArrayIndexedExpression -> translatePushFromArray(expression)
|
is ArrayIndexedExpression -> translateExpression(expression)
|
||||||
is TypecastExpression -> translateExpression(expression)
|
is TypecastExpression -> translateExpression(expression)
|
||||||
is AddressOf -> translateExpression(expression)
|
is AddressOf -> translateExpression(expression)
|
||||||
is DirectMemoryRead -> 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
|
// result value in cpu or status registers, put it on the stack
|
||||||
if (reg.registerOrPair != null) {
|
if (reg.registerOrPair != null) {
|
||||||
when (reg.registerOrPair) {
|
when (reg.registerOrPair) {
|
||||||
RegisterOrPair.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
|
RegisterOrPair.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
RegisterOrPair.Y -> asmgen.out(" tya | sta $ESTACK_LO_HEX,x | dex")
|
RegisterOrPair.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
|
||||||
RegisterOrPair.AY -> asmgen.out(" sta $ESTACK_LO_HEX,x | tya | sta $ESTACK_HI_HEX,x | dex")
|
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||||
RegisterOrPair.X -> {
|
RegisterOrPair.X -> {
|
||||||
// return value in X register has been discarded, just push a zero
|
// return value in X register has been discarded, just push a zero
|
||||||
asmgen.out(" lda #0 | sta $ESTACK_LO_HEX,x | dex")
|
if(CompilationTarget.machine.cpu==CpuType.CPU65c02)
|
||||||
|
asmgen.out(" stz P8ESTACK_LO,x")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda #0 | sta P8ESTACK_LO,x")
|
||||||
|
asmgen.out(" dex")
|
||||||
}
|
}
|
||||||
RegisterOrPair.AX -> {
|
RegisterOrPair.AX -> {
|
||||||
// return value in X register has been discarded, just push a zero in this place
|
// 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.machine.cpu==CpuType.CPU65c02)
|
||||||
|
asmgen.out(" stz P8ESTACK_HI,x")
|
||||||
|
else
|
||||||
|
asmgen.out(" lda #0 | sta P8ESTACK_HI,x")
|
||||||
|
asmgen.out(" dex")
|
||||||
}
|
}
|
||||||
RegisterOrPair.XY -> {
|
RegisterOrPair.XY -> {
|
||||||
// return value in X register has been discarded, just push a zero in this place
|
// 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.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 -> {
|
DataType.UBYTE -> {
|
||||||
when(expr.type) {
|
when(expr.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {}
|
DataType.UBYTE, DataType.BYTE -> {}
|
||||||
DataType.UWORD, DataType.WORD -> asmgen.out(" lda #0 | sta $ESTACK_HI_PLUS1_HEX,x")
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
if(CompilationTarget.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")
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_ub2float")
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -84,7 +98,20 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
when(expr.type) {
|
when(expr.type) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {}
|
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 +""")
|
||||||
|
if(CompilationTarget.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_b2float")
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.stack_b2float")
|
||||||
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
in PassByReferenceDatatypes -> throw AssemblyError("cannot cast to a pass-by-reference datatype")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -125,49 +152,41 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: AddressOf) {
|
private fun translateExpression(expr: AddressOf) {
|
||||||
val name = asmgen.asmIdentifierName(expr.identifier)
|
val name = asmgen.asmVariableName(expr.identifier)
|
||||||
asmgen.out(" lda #<$name | sta $ESTACK_LO_HEX,x | lda #>$name | sta $ESTACK_HI_HEX,x | dex")
|
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: DirectMemoryRead) {
|
private fun translateExpression(expr: DirectMemoryRead) {
|
||||||
when(expr.addressExpression) {
|
when(expr.addressExpression) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
val address = (expr.addressExpression as NumericLiteralValue).number.toInt()
|
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 -> {
|
is IdentifierReference -> {
|
||||||
// the identifier is a pointer variable, so read the value from the address in it
|
// the identifier is a pointer variable, so read the value from the address in it
|
||||||
val sourceName = asmgen.asmIdentifierName(expr.addressExpression as IdentifierReference)
|
asmgen.loadByteFromPointerIntoA(expr.addressExpression as IdentifierReference)
|
||||||
asmgen.out("""
|
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
lda $sourceName
|
|
||||||
sta ${C64MachineDefinition.C64Zeropage.SCRATCH_W1}
|
|
||||||
lda $sourceName+1
|
|
||||||
sta ${C64MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
|
||||||
ldy #0
|
|
||||||
lda (${C64MachineDefinition.C64Zeropage.SCRATCH_W1}),y
|
|
||||||
sta $ESTACK_LO_HEX,x
|
|
||||||
dex""")
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
translateExpression(expr.addressExpression)
|
translateExpression(expr.addressExpression)
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack")
|
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack")
|
||||||
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: NumericLiteralValue) {
|
private fun translateExpression(expr: NumericLiteralValue) {
|
||||||
when(expr.type) {
|
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("""
|
DataType.UWORD, DataType.WORD -> asmgen.out("""
|
||||||
lda #<${expr.number.toHex()}
|
lda #<${expr.number.toHex()}
|
||||||
sta $ESTACK_LO_HEX,x
|
sta P8ESTACK_LO,x
|
||||||
lda #>${expr.number.toHex()}
|
lda #>${expr.number.toHex()}
|
||||||
sta $ESTACK_HI_HEX,x
|
sta P8ESTACK_HI,x
|
||||||
dex
|
dex
|
||||||
""")
|
""")
|
||||||
DataType.FLOAT -> {
|
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")
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -175,19 +194,19 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: IdentifierReference) {
|
private fun translateExpression(expr: IdentifierReference) {
|
||||||
val varname = asmgen.asmIdentifierName(expr)
|
val varname = asmgen.asmVariableName(expr)
|
||||||
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
|
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
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 -> {
|
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 -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$varname | ldy #>$varname| jsr c64flt.push_float")
|
asmgen.out(" lda #<$varname | ldy #>$varname| jsr c64flt.push_float")
|
||||||
}
|
}
|
||||||
in IterableDatatypes -> {
|
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")
|
else -> throw AssemblyError("stack push weird variable type $expr")
|
||||||
}
|
}
|
||||||
@ -207,26 +226,26 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
// see if we can apply some optimized routines
|
// see if we can apply some optimized routines
|
||||||
when(expr.operator) {
|
when(expr.operator) {
|
||||||
">>" -> {
|
">>" -> {
|
||||||
// bit-shifts are always by a constant number (for now)
|
|
||||||
translateExpression(expr.left)
|
translateExpression(expr.left)
|
||||||
val amount = expr.right.constValue(program)!!.number.toInt()
|
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if(amount!=null) {
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (amount <= 2)
|
if (amount <= 2)
|
||||||
repeat(amount) { asmgen.out(" lsr $ESTACK_LO_PLUS1_HEX,x") }
|
repeat(amount) { asmgen.out(" lsr P8ESTACK_LO+1,x") }
|
||||||
else {
|
else {
|
||||||
asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x")
|
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||||
repeat(amount) { asmgen.out(" lsr a") }
|
repeat(amount) { asmgen.out(" lsr a") }
|
||||||
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
if (amount <= 2)
|
if (amount <= 2)
|
||||||
repeat(amount) { asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | asl a | ror $ESTACK_LO_PLUS1_HEX,x") }
|
repeat(amount) { asmgen.out(" lda P8ESTACK_LO+1,x | asl a | ror P8ESTACK_LO+1,x") }
|
||||||
else {
|
else {
|
||||||
asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | sta ${C64MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
asmgen.out(" lda P8ESTACK_LO+1,x | sta P8ZP_SCRATCH_B1")
|
||||||
repeat(amount) { asmgen.out(" asl a | ror ${C64MachineDefinition.C64Zeropage.SCRATCH_B1} | lda ${C64MachineDefinition.C64Zeropage.SCRATCH_B1}") }
|
repeat(amount) { asmgen.out(" asl a | ror P8ZP_SCRATCH_B1 | lda P8ZP_SCRATCH_B1") }
|
||||||
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -236,7 +255,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
left -= 7
|
left -= 7
|
||||||
}
|
}
|
||||||
if (left in 0..2)
|
if (left in 0..2)
|
||||||
repeat(left) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
repeat(left) { asmgen.out(" lsr P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_right_uw_$left")
|
asmgen.out(" jsr math.shift_right_uw_$left")
|
||||||
}
|
}
|
||||||
@ -247,7 +266,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
left -= 7
|
left -= 7
|
||||||
}
|
}
|
||||||
if (left in 0..2)
|
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") }
|
repeat(left) { asmgen.out(" lda P8ESTACK_HI+1,x | asl a | ror P8ESTACK_HI+1,x | ror P8ESTACK_LO+1,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_right_w_$left")
|
asmgen.out(" jsr math.shift_right_w_$left")
|
||||||
}
|
}
|
||||||
@ -255,32 +274,33 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
// bit-shifts are always by a constant number (for now)
|
|
||||||
translateExpression(expr.left)
|
translateExpression(expr.left)
|
||||||
val amount = expr.right.constValue(program)!!.number.toInt()
|
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||||
|
if(amount!=null) {
|
||||||
if (leftDt in ByteDatatypes) {
|
if (leftDt in ByteDatatypes) {
|
||||||
if (amount <= 2)
|
if (amount <= 2)
|
||||||
repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x") }
|
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
||||||
else {
|
else {
|
||||||
asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x")
|
asmgen.out(" lda P8ESTACK_LO+1,x")
|
||||||
repeat(amount) { asmgen.out(" asl a") }
|
repeat(amount) { asmgen.out(" asl a") }
|
||||||
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
asmgen.out(" sta P8ESTACK_LO+1,x")
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var left = amount
|
var left = amount
|
||||||
while (left >= 7) {
|
while (left >= 7) {
|
||||||
asmgen.out(" jsr math.shift_left_w_7")
|
asmgen.out(" jsr math.shift_left_w_7")
|
||||||
left -= 7
|
left -= 7
|
||||||
}
|
}
|
||||||
if (left in 0..2)
|
if (left in 0..2)
|
||||||
repeat(left) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") }
|
repeat(left) { asmgen.out(" asl P8ESTACK_LO+1,x | rol P8ESTACK_HI+1,x") }
|
||||||
else
|
else
|
||||||
asmgen.out(" jsr math.shift_left_w_$left")
|
asmgen.out(" jsr math.shift_left_w_$left")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
val value = expr.right.constValue(program)
|
val value = expr.right.constValue(program)
|
||||||
if(value!=null) {
|
if(value!=null) {
|
||||||
@ -364,9 +384,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
when(type) {
|
when(type) {
|
||||||
in ByteDatatypes ->
|
in ByteDatatypes ->
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $ESTACK_LO_PLUS1_HEX,x
|
lda P8ESTACK_LO+1,x
|
||||||
eor #255
|
eor #255
|
||||||
sta $ESTACK_LO_PLUS1_HEX,x
|
sta P8ESTACK_LO+1,x
|
||||||
""")
|
""")
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.inv_word")
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
@ -383,29 +403,47 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translatePushFromArray(arrayExpr: ArrayIndexedExpression) {
|
private fun translateExpression(arrayExpr: ArrayIndexedExpression) {
|
||||||
// assume *reading* from an array
|
|
||||||
val index = arrayExpr.arrayspec.index
|
val index = arrayExpr.arrayspec.index
|
||||||
val arrayDt = arrayExpr.identifier.targetVarDecl(program.namespace)!!.datatype
|
val elementDt = arrayExpr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
val arrayVarName = asmgen.asmIdentifierName(arrayExpr.identifier)
|
val arrayVarName = asmgen.asmVariableName(arrayExpr.identifier)
|
||||||
if(index is NumericLiteralValue) {
|
if(index is NumericLiteralValue) {
|
||||||
val elementDt = ArrayElementTypes.getValue(arrayDt)
|
|
||||||
val indexValue = index.number.toInt() * elementDt.memorySize()
|
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||||
when(elementDt) {
|
when(elementDt) {
|
||||||
in ByteDatatypes -> {
|
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 -> {
|
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 -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" lda #<$arrayVarName+$indexValue | ldy #>$arrayVarName+$indexValue | jsr c64flt.push_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 {
|
} else {
|
||||||
asmgen.translateArrayIndexIntoA(arrayExpr)
|
when(elementDt) {
|
||||||
asmgen.readAndPushArrayvalueWithIndexA(arrayDt, arrayExpr.identifier)
|
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 +458,21 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
asmgen.out(" jsr prog8_lib.remainder_ub")
|
asmgen.out(" jsr prog8_lib.remainder_ub")
|
||||||
}
|
}
|
||||||
"+" -> asmgen.out("""
|
"+" -> asmgen.out("""
|
||||||
lda $ESTACK_LO_PLUS2_HEX,x
|
lda P8ESTACK_LO+2,x
|
||||||
clc
|
clc
|
||||||
adc $ESTACK_LO_PLUS1_HEX,x
|
adc P8ESTACK_LO+1,x
|
||||||
inx
|
inx
|
||||||
sta $ESTACK_LO_PLUS1_HEX,x
|
sta P8ESTACK_LO+1,x
|
||||||
""")
|
""")
|
||||||
"-" -> asmgen.out("""
|
"-" -> asmgen.out("""
|
||||||
lda $ESTACK_LO_PLUS2_HEX,x
|
lda P8ESTACK_LO+2,x
|
||||||
sec
|
sec
|
||||||
sbc $ESTACK_LO_PLUS1_HEX,x
|
sbc P8ESTACK_LO+1,x
|
||||||
inx
|
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.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.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")
|
"<=" -> asmgen.out(if(types==DataType.UBYTE) " jsr prog8_lib.lesseq_ub" else " jsr prog8_lib.lesseq_b")
|
||||||
|
@ -4,13 +4,12 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.RangeExpr
|
import prog8.ast.expressions.RangeExpr
|
||||||
import prog8.ast.statements.AssignTarget
|
|
||||||
import prog8.ast.statements.Assignment
|
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
|
||||||
|
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
@ -47,18 +46,18 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
if (stepsize==1 || stepsize==-1) {
|
if (stepsize==1 || stepsize==-1) {
|
||||||
|
|
||||||
// bytes, step 1 or -1
|
// bytes array, step 1 or -1
|
||||||
|
|
||||||
val incdec = if(stepsize==1) "inc" else "dec"
|
val incdec = if(stepsize==1) "inc" else "dec"
|
||||||
// loop over byte range via loopvar
|
// 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.to)
|
||||||
asmgen.translateExpression(range.from)
|
asmgen.translateExpression(range.from)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
sta $varname
|
sta $varname
|
||||||
lda $ESTACK_LO_PLUS1_HEX,x
|
lda P8ESTACK_LO+1,x
|
||||||
sta $modifiedLabel+1
|
sta $modifiedLabel+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
@ -75,14 +74,14 @@ $endLabel inx""")
|
|||||||
// bytes, step >= 2 or <= -2
|
// bytes, step >= 2 or <= -2
|
||||||
|
|
||||||
// loop over byte range via loopvar
|
// 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.to)
|
||||||
asmgen.translateExpression(range.from)
|
asmgen.translateExpression(range.from)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
sta $varname
|
sta $varname
|
||||||
lda $ESTACK_LO_PLUS1_HEX,x
|
lda P8ESTACK_LO+1,x
|
||||||
sta $modifiedLabel+1
|
sta $modifiedLabel+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
@ -115,14 +114,12 @@ $endLabel inx""")
|
|||||||
|
|
||||||
stepsize == 1 || stepsize == -1 -> {
|
stepsize == 1 || stepsize == -1 -> {
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
assignLoopvar(stmt, range)
|
||||||
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), range.from, range.position)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
assignLoopvar.linkParents(stmt)
|
|
||||||
asmgen.translate(assignLoopvar)
|
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $ESTACK_HI_PLUS1_HEX,x
|
lda P8ESTACK_HI+1,x
|
||||||
sta $modifiedLabel+1
|
sta $modifiedLabel+1
|
||||||
lda $ESTACK_LO_PLUS1_HEX,x
|
lda P8ESTACK_LO+1,x
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
$loopLabel""")
|
$loopLabel""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
@ -156,15 +153,13 @@ $modifiedLabel2 cmp #0 ; modified
|
|||||||
// (u)words, step >= 2
|
// (u)words, step >= 2
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $ESTACK_HI_PLUS1_HEX,x
|
lda P8ESTACK_HI+1,x
|
||||||
sta $modifiedLabel+1
|
sta $modifiedLabel+1
|
||||||
lda $ESTACK_LO_PLUS1_HEX,x
|
lda P8ESTACK_LO+1,x
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
""")
|
""")
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
assignLoopvar(stmt, range)
|
||||||
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), range.from, range.position)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
assignLoopvar.linkParents(stmt)
|
|
||||||
asmgen.translate(assignLoopvar)
|
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
|
|
||||||
@ -209,15 +204,13 @@ $endLabel inx""")
|
|||||||
// (u)words, step <= -2
|
// (u)words, step <= -2
|
||||||
asmgen.translateExpression(range.to)
|
asmgen.translateExpression(range.to)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $ESTACK_HI_PLUS1_HEX,x
|
lda P8ESTACK_HI+1,x
|
||||||
sta $modifiedLabel+1
|
sta $modifiedLabel+1
|
||||||
lda $ESTACK_LO_PLUS1_HEX,x
|
lda P8ESTACK_LO+1,x
|
||||||
sta $modifiedLabel2+1
|
sta $modifiedLabel2+1
|
||||||
""")
|
""")
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
assignLoopvar(stmt, range)
|
||||||
val assignLoopvar = Assignment(AssignTarget(stmt.loopVar, null, null, stmt.loopVar.position), range.from, range.position)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
assignLoopvar.linkParents(stmt)
|
|
||||||
asmgen.translate(assignLoopvar)
|
|
||||||
asmgen.out(loopLabel)
|
asmgen.out(loopLabel)
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
|
|
||||||
@ -269,7 +262,7 @@ $endLabel inx""")
|
|||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val iterableName = asmgen.asmIdentifierName(ident)
|
val iterableName = asmgen.asmVariableName(ident)
|
||||||
val decl = ident.targetVarDecl(program.namespace)!!
|
val decl = ident.targetVarDecl(program.namespace)!!
|
||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.STR -> {
|
DataType.STR -> {
|
||||||
@ -280,7 +273,7 @@ $endLabel inx""")
|
|||||||
sty $loopLabel+2
|
sty $loopLabel+2
|
||||||
$loopLabel lda ${65535.toHex()} ; modified
|
$loopLabel lda ${65535.toHex()} ; modified
|
||||||
beq $endLabel
|
beq $endLabel
|
||||||
sta ${asmgen.asmIdentifierName(stmt.loopVar)}""")
|
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inc $loopLabel+1
|
inc $loopLabel+1
|
||||||
@ -290,13 +283,13 @@ $loopLabel lda ${65535.toHex()} ; modified
|
|||||||
$endLabel""")
|
$endLabel""")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
val length = decl.arraysize!!.size()!!
|
val length = decl.arraysize!!.constIndex()!!
|
||||||
val indexVar = asmgen.makeLabel("for_index")
|
val indexVar = asmgen.makeLabel("for_index")
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $indexVar
|
$loopLabel sty $indexVar
|
||||||
lda $iterableName,y
|
lda $iterableName,y
|
||||||
sta ${asmgen.asmIdentifierName(stmt.loopVar)}""")
|
sta ${asmgen.asmVariableName(stmt.loopVar)}""")
|
||||||
asmgen.translate(stmt.body)
|
asmgen.translate(stmt.body)
|
||||||
if(length<=255) {
|
if(length<=255) {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -324,9 +317,9 @@ $indexVar .byte 0""")
|
|||||||
asmgen.out(endLabel)
|
asmgen.out(endLabel)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
val length = decl.arraysize!!.size()!! * 2
|
val length = decl.arraysize!!.constIndex()!! * 2
|
||||||
val indexVar = asmgen.makeLabel("for_index")
|
val indexVar = asmgen.makeLabel("for_index")
|
||||||
val loopvarName = asmgen.asmIdentifierName(stmt.loopVar)
|
val loopvarName = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
ldy #0
|
||||||
$loopLabel sty $indexVar
|
$loopLabel sty $indexVar
|
||||||
@ -389,7 +382,7 @@ $indexVar .byte 0""")
|
|||||||
when(iterableDt) {
|
when(iterableDt) {
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||||
// loop over byte range via loopvar, step >= 2 or <= -2
|
// loop over byte range via loopvar, step >= 2 or <= -2
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
@ -454,7 +447,7 @@ $loopLabel""")
|
|||||||
}
|
}
|
||||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||||
// loop over word range via loopvar, step >= 2 or <= -2
|
// loop over word range via loopvar, step >= 2 or <= -2
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
when (range.step) {
|
when (range.step) {
|
||||||
0, 1, -1 -> {
|
0, 1, -1 -> {
|
||||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||||
@ -498,7 +491,7 @@ $endLabel""")
|
|||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
@ -525,7 +518,7 @@ $endLabel""")
|
|||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #${range.first}
|
lda #${range.first}
|
||||||
sta $varname
|
sta $varname
|
||||||
@ -563,7 +556,7 @@ $endLabel""")
|
|||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${range.first}
|
lda #<${range.first}
|
||||||
ldy #>${range.first}
|
ldy #>${range.first}
|
||||||
@ -591,7 +584,7 @@ $endLabel""")
|
|||||||
val loopLabel = asmgen.makeLabel("for_loop")
|
val loopLabel = asmgen.makeLabel("for_loop")
|
||||||
val endLabel = asmgen.makeLabel("for_end")
|
val endLabel = asmgen.makeLabel("for_end")
|
||||||
asmgen.loopEndLabels.push(endLabel)
|
asmgen.loopEndLabels.push(endLabel)
|
||||||
val varname = asmgen.asmIdentifierName(stmt.loopVar)
|
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda #<${range.first}
|
lda #<${range.first}
|
||||||
ldy #>${range.first}
|
ldy #>${range.first}
|
||||||
@ -615,4 +608,11 @@ $loopLabel""")
|
|||||||
$endLabel""")
|
$endLabel""")
|
||||||
asmgen.loopEndLabels.pop()
|
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,14 +4,10 @@ import prog8.ast.IFunctionCall
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.AssignTarget
|
|
||||||
import prog8.ast.statements.Assignment
|
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.statements.SubroutineParameter
|
import prog8.ast.statements.SubroutineParameter
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.codegen.assignment.*
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
|
||||||
import prog8.compiler.toHex
|
|
||||||
|
|
||||||
|
|
||||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -20,11 +16,11 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
// output the code to setup the parameters and perform the actual call
|
// output the code to setup the parameters and perform the actual call
|
||||||
// does NOT output the code to deal with the result values!
|
// 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 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)
|
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) // we only save X for now (required! is the eval stack pointer), screw A and Y...
|
||||||
|
|
||||||
val subName = asmgen.asmIdentifierName(stmt.target)
|
val subName = asmgen.asmSymbolName(stmt.target)
|
||||||
if(stmt.args.isNotEmpty()) {
|
if(stmt.args.isNotEmpty()) {
|
||||||
if(sub.asmParameterRegisters.isEmpty()) {
|
if(sub.asmParameterRegisters.isEmpty()) {
|
||||||
// via variables
|
// via variables
|
||||||
@ -52,7 +48,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// Risk of clobbering due to complex expression args. Work via the stack.
|
// Risk of clobbering due to complex expression args. Work via the stack.
|
||||||
argsViaStackEvaluation(stmt, sub)
|
registerArgsViaStackEvaluation(stmt, sub)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,37 +57,37 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.out(" jsr $subName")
|
asmgen.out(" jsr $subName")
|
||||||
|
|
||||||
if(saveX)
|
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.
|
||||||
for (arg in stmt.args.reversed())
|
for (arg in stmt.args.reversed())
|
||||||
asmgen.translateExpression(arg)
|
asmgen.translateExpression(arg)
|
||||||
for (regparam in sub.asmParameterRegisters) {
|
for (regparam in sub.asmParameterRegisters) {
|
||||||
when (regparam.registerOrPair) {
|
when {
|
||||||
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
regparam.statusflag==Statusflag.Pc -> {
|
||||||
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
asmgen.out("""
|
||||||
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 -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
when (regparam.statusflag) {
|
|
||||||
Statusflag.Pc -> asmgen.out("""
|
|
||||||
inx
|
inx
|
||||||
pha
|
pha
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
beq +
|
beq +
|
||||||
sec
|
sec
|
||||||
bcs ++
|
bcs ++
|
||||||
+ clc
|
+ clc
|
||||||
+ pla
|
+ pla""")
|
||||||
""")
|
|
||||||
null -> {
|
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("can only use Carry as status flag parameter")
|
regparam.statusflag!=null -> {
|
||||||
|
throw AssemblyError("can only use Carry as status flag parameter")
|
||||||
|
}
|
||||||
|
regparam.registerOrPair!=null -> {
|
||||||
|
val tgt = AsmAssignTarget.fromRegisters(regparam.registerOrPair, program, asmgen)
|
||||||
|
val source = AsmAssignSource(SourceStorageKind.STACK, program, tgt.datatype)
|
||||||
|
val asgn = AsmAssignment(source, tgt, false, Position.DUMMY)
|
||||||
|
asmgen.translateNormalAssignment(asgn)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,15 +98,16 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
if(!valueIDt.isKnown)
|
if(!valueIDt.isKnown)
|
||||||
throw AssemblyError("arg type unknown")
|
throw AssemblyError("arg type unknown")
|
||||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||||
if(!argumentTypeCompatible(valueDt, parameter.value.type))
|
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val paramVar = parameter.value
|
val scopedParamVar = (sub.scopedname+"."+parameter.value.name).split(".")
|
||||||
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
val identifier = IdentifierReference(scopedParamVar, sub.position)
|
||||||
val target = AssignTarget(IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
identifier.linkParents(value.parent)
|
||||||
val assign = Assignment(target, value, value.position)
|
val tgt = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, parameter.value.type, variable = identifier)
|
||||||
assign.linkParents(value.parent)
|
val source = AsmAssignSource.fromAstSource(value, program).adjustDataTypeToTarget(tgt)
|
||||||
asmgen.translate(assign)
|
val asgn = AsmAssignment(source, tgt, false, Position.DUMMY)
|
||||||
|
asmgen.translateNormalAssignment(asgn)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||||
@ -119,7 +116,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
if(!valueIDt.isKnown)
|
if(!valueIDt.isKnown)
|
||||||
throw AssemblyError("arg type unknown")
|
throw AssemblyError("arg type unknown")
|
||||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||||
if(!argumentTypeCompatible(valueDt, parameter.value.type))
|
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||||
throw AssemblyError("argument type incompatible")
|
throw AssemblyError("argument type incompatible")
|
||||||
|
|
||||||
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
val paramRegister = sub.asmParameterRegisters[parameter.index]
|
||||||
@ -142,7 +139,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.out(if(carrySet) " sec" else " clc")
|
asmgen.out(if(carrySet) " sec" else " clc")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val sourceName = asmgen.asmIdentifierName(value)
|
val sourceName = asmgen.asmVariableName(value)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
pha
|
pha
|
||||||
lda $sourceName
|
lda $sourceName
|
||||||
@ -158,7 +155,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
pha
|
pha
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
beq +
|
beq +
|
||||||
sec
|
sec
|
||||||
bcs ++
|
bcs ++
|
||||||
@ -170,79 +167,22 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
else throw AssemblyError("can only use Carry as status flag parameter")
|
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 -> {
|
else -> {
|
||||||
asmgen.translateExpression(value)
|
// via register or register pair
|
||||||
when(register) {
|
val target = AsmAssignTarget.fromRegisters(register!!, program, asmgen)
|
||||||
RegisterOrPair.A -> asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
val src = if(valueDt in PassByReferenceDatatypes) {
|
||||||
RegisterOrPair.X -> throw AssemblyError("can't pop into X register - use a variable instead")
|
val addr = AddressOf(value as IdentifierReference, Position.DUMMY)
|
||||||
RegisterOrPair.Y -> asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
AsmAssignSource.fromAstSource(addr, program).adjustDataTypeToTarget(target)
|
||||||
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 {
|
} else {
|
||||||
when (register) {
|
AsmAssignSource.fromAstSource(value, program).adjustDataTypeToTarget(target)
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
if(argType isAssignableTo paramType)
|
||||||
return true
|
return true
|
||||||
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
if(argType in ByteDatatypes && paramType in ByteDatatypes)
|
||||||
|
@ -6,9 +6,6 @@ import prog8.ast.expressions.IdentifierReference
|
|||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.statements.PostIncrDecr
|
import prog8.ast.statements.PostIncrDecr
|
||||||
import prog8.compiler.AssemblyError
|
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_LO_HEX
|
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +17,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
val targetArrayIdx = stmt.target.arrayindexed
|
val targetArrayIdx = stmt.target.arrayindexed
|
||||||
when {
|
when {
|
||||||
targetIdent!=null -> {
|
targetIdent!=null -> {
|
||||||
val what = asmgen.asmIdentifierName(targetIdent)
|
val what = asmgen.asmVariableName(targetIdent)
|
||||||
when (stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)) {
|
when (stmt.target.inferType(program, stmt).typeOrElse(DataType.STRUCT)) {
|
||||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
@ -48,7 +45,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.out(if(incr) " inc $what" else " dec $what")
|
asmgen.out(if(incr) " inc $what" else " dec $what")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val what = asmgen.asmIdentifierName(addressExpr)
|
val what = asmgen.asmVariableName(addressExpr)
|
||||||
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
||||||
if(incr)
|
if(incr)
|
||||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||||
@ -59,9 +56,9 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
asmgen.translateExpression(addressExpr)
|
asmgen.translateExpression(addressExpr)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
inx
|
||||||
lda $ESTACK_LO_HEX,x
|
lda P8ESTACK_LO,x
|
||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
lda $ESTACK_HI_HEX,x
|
lda P8ESTACK_HI,x
|
||||||
sta (+) + 2
|
sta (+) + 2
|
||||||
""")
|
""")
|
||||||
if(incr)
|
if(incr)
|
||||||
@ -73,71 +70,66 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
val what = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.identifier)
|
||||||
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
val elementDt = ArrayElementTypes.getValue(arrayDt)
|
|
||||||
when(index) {
|
when(index) {
|
||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
val indexValue = index.number.toInt() * elementDt.memorySize()
|
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||||
when(elementDt) {
|
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 -> {
|
in WordDatatypes -> {
|
||||||
if(incr)
|
if(incr)
|
||||||
asmgen.out(" inc $what+$indexValue | bne + | inc $what+$indexValue+1 |+")
|
asmgen.out(" inc $asmArrayvarname+$indexValue | bne + | inc $asmArrayvarname+$indexValue+1 |+")
|
||||||
else
|
else
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
lda $what+$indexValue
|
lda $asmArrayvarname+$indexValue
|
||||||
bne +
|
bne +
|
||||||
dec $what+$indexValue+1
|
dec $asmArrayvarname+$indexValue+1
|
||||||
+ dec $what+$indexValue
|
+ dec $asmArrayvarname+$indexValue
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
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")
|
asmgen.out(if(incr) " jsr c64flt.inc_var_f" else " jsr c64flt.dec_var_f")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("need numeric type")
|
else -> throw AssemblyError("need numeric type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
|
||||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
|
||||||
}
|
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
|
||||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
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}")
|
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,164 @@
|
|||||||
|
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 {
|
||||||
|
require(source.datatype==target.datatype) {"source and target datatype must be identical"}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,966 @@
|
|||||||
|
package prog8.compiler.target.c64.codegen.assignment
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.compiler.AssemblyError
|
||||||
|
import prog8.compiler.target.CompilationTarget
|
||||||
|
import prog8.compiler.target.CpuType
|
||||||
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
|
||||||
|
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
|
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen)
|
||||||
|
|
||||||
|
fun translate(assignment: Assignment) {
|
||||||
|
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
|
||||||
|
val source = AsmAssignSource.fromAstSource(assignment.value, program).adjustDataTypeToTarget(target)
|
||||||
|
|
||||||
|
val assign = AsmAssignment(source, target, assignment.isAugmentable, assignment.position)
|
||||||
|
target.origAssign = assign
|
||||||
|
|
||||||
|
if(assign.isAugmentable)
|
||||||
|
augmentableAsmGen.translate(assign)
|
||||||
|
else
|
||||||
|
translateNormalAssignment(assign)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun translateNormalAssignment(assign: AsmAssignment) {
|
||||||
|
when(assign.source.kind) {
|
||||||
|
SourceStorageKind.LITERALNUMBER -> {
|
||||||
|
// simple case: assign a constant number
|
||||||
|
val num = assign.source.number!!.number
|
||||||
|
when (assign.source.datatype) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> assignConstantByte(assign.target, num.toShort())
|
||||||
|
DataType.UWORD, DataType.WORD -> assignConstantWord(assign.target, num.toInt())
|
||||||
|
DataType.FLOAT -> assignConstantFloat(assign.target, num.toDouble())
|
||||||
|
else -> throw AssemblyError("weird numval type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SourceStorageKind.VARIABLE -> {
|
||||||
|
// simple case: assign from another variable
|
||||||
|
val variable = assign.source.variable!!
|
||||||
|
when (assign.source.datatype) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
|
||||||
|
DataType.UWORD, DataType.WORD -> assignVariableWord(assign.target, variable)
|
||||||
|
DataType.FLOAT -> assignVariableFloat(assign.target, variable)
|
||||||
|
in PassByReferenceDatatypes -> assignAddressOf(assign.target, variable)
|
||||||
|
else -> throw AssemblyError("unsupported assignment target type ${assign.target.datatype}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SourceStorageKind.ARRAY -> {
|
||||||
|
val value = assign.source.array!!
|
||||||
|
val elementDt = assign.source.datatype
|
||||||
|
val index = value.arrayspec.index
|
||||||
|
val arrayVarName = asmgen.asmVariableName(value.identifier)
|
||||||
|
if (index is NumericLiteralValue) {
|
||||||
|
// constant array index value
|
||||||
|
val indexValue = index.number.toInt() * elementDt.memorySize()
|
||||||
|
when (elementDt) {
|
||||||
|
in ByteDatatypes ->
|
||||||
|
asmgen.out(" lda $arrayVarName+$indexValue | sta P8ESTACK_LO,x | dex")
|
||||||
|
in WordDatatypes ->
|
||||||
|
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 array type")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when (elementDt) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||||
|
asmgen.out(" lda $arrayVarName,y | sta P8ESTACK_LO,x | dex")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(value, 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(value, elementDt, CpuRegister.A)
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #>$arrayVarName
|
||||||
|
clc
|
||||||
|
adc #<$arrayVarName
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jsr c64flt.push_float""")
|
||||||
|
}
|
||||||
|
else ->
|
||||||
|
throw AssemblyError("weird array elt type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assignStackValue(assign.target)
|
||||||
|
}
|
||||||
|
SourceStorageKind.MEMORY -> {
|
||||||
|
val value = assign.source.memory!!
|
||||||
|
when (value.addressExpression) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
||||||
|
assignMemoryByte(assign.target, address, null)
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(value.addressExpression)
|
||||||
|
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | inx")
|
||||||
|
assignRegisterByte(assign.target, CpuRegister.A)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SourceStorageKind.EXPRESSION -> {
|
||||||
|
val value = assign.source.expression!!
|
||||||
|
when(value) {
|
||||||
|
is AddressOf -> assignAddressOf(assign.target, value.identifier)
|
||||||
|
is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber")
|
||||||
|
is IdentifierReference -> throw AssemblyError("source kind should have been variable")
|
||||||
|
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
|
||||||
|
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
|
||||||
|
// is TypecastExpression -> {
|
||||||
|
// if(assign.target.kind == TargetStorageKind.STACK) {
|
||||||
|
// asmgen.translateExpression(value)
|
||||||
|
// assignStackValue(assign.target)
|
||||||
|
// } else {
|
||||||
|
// println("!!!!TYPECAST to ${assign.target.kind} $value")
|
||||||
|
// // TODO maybe we can do the typecast on the target directly instead of on the stack?
|
||||||
|
// asmgen.translateExpression(value)
|
||||||
|
// assignStackValue(assign.target)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// is FunctionCall -> {
|
||||||
|
// if (assign.target.kind == TargetStorageKind.STACK) {
|
||||||
|
// asmgen.translateExpression(value)
|
||||||
|
// assignStackValue(assign.target)
|
||||||
|
// } else {
|
||||||
|
// val functionName = value.target.nameInSource.last()
|
||||||
|
// val builtinFunc = BuiltinFunctions[functionName]
|
||||||
|
// if (builtinFunc != null) {
|
||||||
|
// println("!!!!BUILTIN-FUNCCALL target=${assign.target.kind} $value") // TODO optimize certain functions?
|
||||||
|
// }
|
||||||
|
// asmgen.translateExpression(value)
|
||||||
|
// assignStackValue(assign.target)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
else -> {
|
||||||
|
// everything else just evaluate via the stack.
|
||||||
|
asmgen.translateExpression(value)
|
||||||
|
assignStackValue(assign.target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SourceStorageKind.REGISTER -> {
|
||||||
|
assignRegisterByte(assign.target, assign.source.register!!)
|
||||||
|
}
|
||||||
|
SourceStorageKind.STACK -> {
|
||||||
|
assignStackValue(assign.target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignStackValue(target: AsmAssignTarget) {
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
when (target.datatype) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
lda P8ESTACK_HI,x
|
||||||
|
sta ${target.asmVarname}+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${target.asmVarname}
|
||||||
|
ldy #>${target.asmVarname}
|
||||||
|
jsr c64flt.pop_float
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
asmgen.out(" inx")
|
||||||
|
storeByteViaRegisterAInMemoryAddress("P8ESTACK_LO,x", target.memory!!)
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
val index = target.array!!.arrayspec.index
|
||||||
|
when {
|
||||||
|
target.constArrayIndexValue!=null -> {
|
||||||
|
val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize()
|
||||||
|
when(target.datatype) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname}+$scaledIdx")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
sta ${target.asmVarname}+$scaledIdx
|
||||||
|
lda P8ESTACK_HI,x
|
||||||
|
sta ${target.asmVarname}+$scaledIdx+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${target.asmVarname}+$scaledIdx
|
||||||
|
ldy #>${target.asmVarname}+$scaledIdx
|
||||||
|
jsr c64flt.pop_float
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index is IdentifierReference -> {
|
||||||
|
when(target.datatype) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||||
|
asmgen.out(" inx | lda P8ESTACK_LO,x | sta ${target.asmVarname},y")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
sta ${target.asmVarname},y
|
||||||
|
lda P8ESTACK_HI,x
|
||||||
|
sta ${target.asmVarname}+1,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A)
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #>${target.asmVarname}
|
||||||
|
clc
|
||||||
|
adc #<${target.asmVarname}
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jsr c64flt.pop_float""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out(" inx | lda P8ESTACK_LO,x")
|
||||||
|
popAndWriteArrayvalueWithUnscaledIndexA(target.datatype, target.asmVarname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> {
|
||||||
|
when (target.datatype) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
when(target.register!!) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" inx | lda P8ESTACK_LO,x")
|
||||||
|
RegisterOrPair.X -> throw AssemblyError("can't use X here")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" inx | ldy P8ESTACK_LO,x")
|
||||||
|
else -> throw AssemblyError("can't assign byte to register pair word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD, in PassByReferenceDatatypes -> {
|
||||||
|
when(target.register!!) {
|
||||||
|
RegisterOrPair.AX -> throw AssemblyError("can't use X here")
|
||||||
|
RegisterOrPair.AY-> asmgen.out(" inx | lda P8ESTACK_LO,x | ldy P8ESTACK_HI,x")
|
||||||
|
RegisterOrPair.XY-> throw AssemblyError("can't use X here")
|
||||||
|
else -> throw AssemblyError("can't assign word to single byte register")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignAddressOf(target: AsmAssignTarget, name: IdentifierReference) {
|
||||||
|
val struct = name.memberOfStruct(program.namespace)
|
||||||
|
val sourceName = if (struct != null) {
|
||||||
|
// take the address of the first struct member instead
|
||||||
|
val decl = name.targetVarDecl(program.namespace)!!
|
||||||
|
val firstStructMember = struct.nameOfFirstMember()
|
||||||
|
// find the flattened var that belongs to this first struct member
|
||||||
|
val firstVarName = listOf(decl.name, firstStructMember)
|
||||||
|
val firstVar = name.definingScope().lookup(firstVarName, name) as VarDecl
|
||||||
|
firstVar.name
|
||||||
|
} else {
|
||||||
|
asmgen.fixNameSymbols(name.nameInSource.joinToString("."))
|
||||||
|
}
|
||||||
|
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$sourceName
|
||||||
|
ldy #>$sourceName
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
sty ${target.asmVarname}+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
throw AssemblyError("no asm gen for assign address $sourceName to memory word $target")
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
throw AssemblyError("no asm gen for assign address $sourceName to array ${target.asmVarname}")
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> {
|
||||||
|
when(target.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 -> throw AssemblyError("can't load address in a single 8-bit register")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
val srcname = asmgen.asmVariableName(name)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$srcname
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
lda #>$srcname
|
||||||
|
sta P8ESTACK_HI,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignVariableWord(target: AsmAssignTarget, variable: IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmVariableName(variable)
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
ldy $sourceName+1
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
sty ${target.asmVarname}+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
throw AssemblyError("no asm gen for assign wordvar $sourceName to memory ${target.memory}")
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
val index = target.array!!.arrayspec.index
|
||||||
|
when {
|
||||||
|
target.constArrayIndexValue!=null -> {
|
||||||
|
val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize()
|
||||||
|
when(target.datatype) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||||
|
}
|
||||||
|
in WordDatatypes -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta ${target.asmVarname}+$scaledIdx
|
||||||
|
lda $sourceName+1
|
||||||
|
sta ${target.asmVarname}+$scaledIdx+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$sourceName
|
||||||
|
ldy #>$sourceName
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
lda #<${target.asmVarname}+$scaledIdx
|
||||||
|
ldy #>${target.asmVarname}+$scaledIdx
|
||||||
|
jsr c64flt.copy_float
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird target variable type ${target.datatype}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index is IdentifierReference -> {
|
||||||
|
when(target.datatype) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||||
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta ${target.asmVarname},y
|
||||||
|
lda $sourceName+1
|
||||||
|
sta ${target.asmVarname}+1,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.A)
|
||||||
|
asmgen.out("""
|
||||||
|
ldy #<$sourceName
|
||||||
|
sty P8ZP_SCRATCH_W1
|
||||||
|
ldy #>$sourceName
|
||||||
|
sty P8ZP_SCRATCH_W1+1
|
||||||
|
ldy #>${target.asmVarname}
|
||||||
|
clc
|
||||||
|
adc #<${target.asmVarname}
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jsr c64flt.copy_float""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | lda $sourceName+1 | sta P8ESTACK_HI,x | dex")
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out(" inx | lda P8ESTACK_LO,x")
|
||||||
|
popAndWriteArrayvalueWithUnscaledIndexA(target.datatype, target.asmVarname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> {
|
||||||
|
when(target.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 -> throw AssemblyError("can't load word in a single 8-bit register")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #$sourceName
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
lda #$sourceName+1
|
||||||
|
sta P8ESTACK_HI,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignVariableFloat(target: AsmAssignTarget, variable: IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmVariableName(variable)
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
lda $sourceName+1
|
||||||
|
sta ${target.asmVarname}+1
|
||||||
|
lda $sourceName+2
|
||||||
|
sta ${target.asmVarname}+2
|
||||||
|
lda $sourceName+3
|
||||||
|
sta ${target.asmVarname}+3
|
||||||
|
lda $sourceName+4
|
||||||
|
sta ${target.asmVarname}+4
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
// TODO optimize this, but the situation doesn't occur very often
|
||||||
|
// if(target.constArrayIndexValue!=null) {
|
||||||
|
// TODO("const index ${target.constArrayIndexValue}")
|
||||||
|
// } else if(target.array!!.arrayspec.index is IdentifierReference) {
|
||||||
|
// TODO("array[var] ${target.constArrayIndexValue}")
|
||||||
|
// }
|
||||||
|
val index = target.array!!.arrayspec.index
|
||||||
|
asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr c64flt.push_float")
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out(" lda #<${target.asmVarname} | ldy #>${target.asmVarname} | jsr c64flt.pop_float_to_indexed_var")
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
|
||||||
|
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
||||||
|
TargetStorageKind.STACK -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignVariableByte(target: AsmAssignTarget, variable: IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmVariableName(variable)
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!)
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
val index = target.array!!.arrayspec.index
|
||||||
|
when {
|
||||||
|
target.constArrayIndexValue!=null -> {
|
||||||
|
val scaledIdx = target.constArrayIndexValue!! * target.datatype.memorySize()
|
||||||
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||||
|
}
|
||||||
|
index is IdentifierReference -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, target.datatype, CpuRegister.Y)
|
||||||
|
asmgen.out(" lda $sourceName | sta ${target.asmVarname},y")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x | dex")
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out(" inx | lda P8ESTACK_LO,x")
|
||||||
|
popAndWriteArrayvalueWithUnscaledIndexA(target.datatype, target.asmVarname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> {
|
||||||
|
when(target.register!!) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" lda $sourceName")
|
||||||
|
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #$sourceName
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
|
||||||
|
require(target.datatype in ByteDatatypes)
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
asmgen.out(" st${register.name.toLowerCase()} ${target.asmVarname}")
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
storeRegisterInMemoryAddress(register, target.memory!!)
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
val index = target.array!!.arrayspec.index
|
||||||
|
when (index) {
|
||||||
|
is NumericLiteralValue -> {
|
||||||
|
val memindex = index.number.toInt()
|
||||||
|
when (register) {
|
||||||
|
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+$memindex")
|
||||||
|
CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+$memindex")
|
||||||
|
CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+$memindex")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
when (register) {
|
||||||
|
CpuRegister.A -> {}
|
||||||
|
CpuRegister.X -> asmgen.out(" txa")
|
||||||
|
CpuRegister.Y -> asmgen.out(" tya")
|
||||||
|
}
|
||||||
|
asmgen.out(" ldy ${asmgen.asmVariableName(index)} | sta ${target.asmVarname},y")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.saveRegister(register)
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.restoreRegister(register)
|
||||||
|
when (register) {
|
||||||
|
CpuRegister.A -> asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||||
|
CpuRegister.X -> asmgen.out(" stx P8ZP_SCRATCH_B1")
|
||||||
|
CpuRegister.Y -> asmgen.out(" sty P8ZP_SCRATCH_B1")
|
||||||
|
}
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
tay
|
||||||
|
lda P8ZP_SCRATCH_B1
|
||||||
|
sta ${target.asmVarname},y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> {
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> when(target.register!!) {
|
||||||
|
RegisterOrPair.A -> {}
|
||||||
|
RegisterOrPair.X -> { asmgen.out(" tax") }
|
||||||
|
RegisterOrPair.Y -> { asmgen.out(" tay") }
|
||||||
|
else -> throw AssemblyError("attempt to assign byte to register pair word")
|
||||||
|
}
|
||||||
|
CpuRegister.X -> when(target.register!!) {
|
||||||
|
RegisterOrPair.A -> { asmgen.out(" txa") }
|
||||||
|
RegisterOrPair.X -> { }
|
||||||
|
RegisterOrPair.Y -> { asmgen.out(" txy") }
|
||||||
|
else -> throw AssemblyError("attempt to assign byte to register pair word")
|
||||||
|
}
|
||||||
|
CpuRegister.Y -> when(target.register!!) {
|
||||||
|
RegisterOrPair.A -> { asmgen.out(" tya") }
|
||||||
|
RegisterOrPair.X -> { asmgen.out(" tyx") }
|
||||||
|
RegisterOrPair.Y -> { }
|
||||||
|
else -> throw AssemblyError("attempt to assign byte to register pair word")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
when(register) {
|
||||||
|
CpuRegister.A -> asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
|
CpuRegister.X -> throw AssemblyError("can't use X here")
|
||||||
|
CpuRegister.Y -> asmgen.out(" tya | sta P8ESTACK_LO,x | dex")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignConstantWord(target: AsmAssignTarget, word: Int) {
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
if (word ushr 8 == word and 255) {
|
||||||
|
// lsb=msb
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${(word and 255).toHex()}
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
sta ${target.asmVarname}+1
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${word.toHex()}
|
||||||
|
ldy #>${word.toHex()}
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
sty ${target.asmVarname}+1
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
throw AssemblyError("no asm gen for assign word $word to memory ${target.memory}")
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
// TODO optimize this, but the situation doesn't occur very often
|
||||||
|
// if(target.constArrayIndexValue!=null) {
|
||||||
|
// TODO("const index ${target.constArrayIndexValue}")
|
||||||
|
// } else if(target.array!!.arrayspec.index is IdentifierReference) {
|
||||||
|
// TODO("array[var] ${target.constArrayIndexValue}")
|
||||||
|
// }
|
||||||
|
val index = target.array!!.arrayspec.index
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
lda #<${word.toHex()}
|
||||||
|
sta ${target.asmVarname},y
|
||||||
|
lda #>${word.toHex()}
|
||||||
|
sta ${target.asmVarname}+1,y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> {
|
||||||
|
when(target.register!!) {
|
||||||
|
RegisterOrPair.AX -> asmgen.out(" lda #<${word.toHex()} | ldx #>${word.toHex()}")
|
||||||
|
RegisterOrPair.AY -> asmgen.out(" lda #<${word.toHex()} | ldy #>${word.toHex()}")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" ldx #<${word.toHex()} | ldy #>${word.toHex()}")
|
||||||
|
else -> throw AssemblyError("can't assign word to single byte register")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${word.toHex()}
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
lda #>${word.toHex()}
|
||||||
|
sta P8ESTACK_HI,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignConstantByte(target: AsmAssignTarget, byte: Short) {
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname} ")
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", target.memory!!)
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
val index = target.array!!.arrayspec.index
|
||||||
|
when {
|
||||||
|
target.constArrayIndexValue!=null -> {
|
||||||
|
val indexValue = target.constArrayIndexValue!!
|
||||||
|
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
|
||||||
|
}
|
||||||
|
index is IdentifierReference -> {
|
||||||
|
asmgen.loadScaledArrayIndexIntoRegister(target.array, DataType.UBYTE, CpuRegister.Y)
|
||||||
|
asmgen.out(" lda #<${byte.toHex()} | sta ${target.asmVarname},y")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
ldy P8ESTACK_LO,x
|
||||||
|
lda #${byte.toHex()}
|
||||||
|
sta ${target.asmVarname},y
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> when(target.register!!) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" lda #${byte.toHex()}")
|
||||||
|
RegisterOrPair.X -> asmgen.out(" ldx #${byte.toHex()}")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" ldy #${byte.toHex()}")
|
||||||
|
else -> throw AssemblyError("can't assign byte to word register apir")
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #${byte.toHex()}
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignConstantFloat(target: AsmAssignTarget, float: Double) {
|
||||||
|
if (float == 0.0) {
|
||||||
|
// optimized case for float zero
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
if(CompilationTarget.machine.cpu == CpuType.CPU65c02)
|
||||||
|
asmgen.out("""
|
||||||
|
stz ${target.asmVarname}
|
||||||
|
stz ${target.asmVarname}+1
|
||||||
|
stz ${target.asmVarname}+2
|
||||||
|
stz ${target.asmVarname}+3
|
||||||
|
stz ${target.asmVarname}+4
|
||||||
|
""")
|
||||||
|
else
|
||||||
|
asmgen.out("""
|
||||||
|
lda #0
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
sta ${target.asmVarname}+1
|
||||||
|
sta ${target.asmVarname}+2
|
||||||
|
sta ${target.asmVarname}+3
|
||||||
|
sta ${target.asmVarname}+4
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
// TODO optimize this, but the situation doesn't occur very often
|
||||||
|
// if(target.constArrayIndexValue!=null) {
|
||||||
|
// TODO("const index ${target.constArrayIndexValue}")
|
||||||
|
// } else if(target.array!!.arrayspec.index is IdentifierReference) {
|
||||||
|
// TODO("array[var] ${target.constArrayIndexValue}")
|
||||||
|
// }
|
||||||
|
val index = target.array!!.arrayspec.index
|
||||||
|
if (index is NumericLiteralValue) {
|
||||||
|
val indexValue = index.number.toInt() * DataType.FLOAT.memorySize()
|
||||||
|
asmgen.out("""
|
||||||
|
lda #0
|
||||||
|
sta ${target.asmVarname}+$indexValue
|
||||||
|
sta ${target.asmVarname}+$indexValue+1
|
||||||
|
sta ${target.asmVarname}+$indexValue+2
|
||||||
|
sta ${target.asmVarname}+$indexValue+3
|
||||||
|
sta ${target.asmVarname}+$indexValue+4
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${target.asmVarname}
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda #>${target.asmVarname}
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
jsr c64flt.set_0_array_float
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
|
||||||
|
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
val floatConst = asmgen.getFloatAsmConst(float)
|
||||||
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// non-zero value
|
||||||
|
val constFloat = asmgen.getFloatAsmConst(float)
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda $constFloat
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
lda $constFloat+1
|
||||||
|
sta ${target.asmVarname}+1
|
||||||
|
lda $constFloat+2
|
||||||
|
sta ${target.asmVarname}+2
|
||||||
|
lda $constFloat+3
|
||||||
|
sta ${target.asmVarname}+3
|
||||||
|
lda $constFloat+4
|
||||||
|
sta ${target.asmVarname}+4
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
// TODO optimize this, but the situation doesn't occur very often
|
||||||
|
// if(target.constArrayIndexValue!=null) {
|
||||||
|
// TODO("const index ${target.constArrayIndexValue}")
|
||||||
|
// } else if(target.array!!.arrayspec.index is IdentifierReference) {
|
||||||
|
// TODO("array[var] ${target.constArrayIndexValue}")
|
||||||
|
// }
|
||||||
|
val index = target.array!!.arrayspec.index
|
||||||
|
val arrayVarName = target.asmVarname
|
||||||
|
if (index is NumericLiteralValue) {
|
||||||
|
val indexValue = index.number.toInt() * DataType.FLOAT.memorySize()
|
||||||
|
asmgen.out("""
|
||||||
|
lda $constFloat
|
||||||
|
sta $arrayVarName+$indexValue
|
||||||
|
lda $constFloat+1
|
||||||
|
sta $arrayVarName+$indexValue+1
|
||||||
|
lda $constFloat+2
|
||||||
|
sta $arrayVarName+$indexValue+2
|
||||||
|
lda $constFloat+3
|
||||||
|
sta $arrayVarName+$indexValue+3
|
||||||
|
lda $constFloat+4
|
||||||
|
sta $arrayVarName+$indexValue+4
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(index)
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<${constFloat}
|
||||||
|
sta P8ZP_SCRATCH_W1
|
||||||
|
lda #>${constFloat}
|
||||||
|
sta P8ZP_SCRATCH_W1+1
|
||||||
|
lda #<${arrayVarName}
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda #>${arrayVarName}
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
jsr c64flt.set_array_float
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
|
||||||
|
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
val floatConst = asmgen.getFloatAsmConst(float)
|
||||||
|
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignMemoryByte(target: AsmAssignTarget, address: Int?, identifier: IdentifierReference?) {
|
||||||
|
if (address != null) {
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${address.toHex()}
|
||||||
|
sta ${target.asmVarname}
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
storeByteViaRegisterAInMemoryAddress(address.toHex(), target.memory!!)
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
throw AssemblyError("no asm gen for assign memory byte at $address to array ${target.asmVarname}")
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> when(target.register!!) {
|
||||||
|
RegisterOrPair.A -> asmgen.out(" lda ${address.toHex()}")
|
||||||
|
RegisterOrPair.X -> asmgen.out(" ldx ${address.toHex()}")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" ldy ${address.toHex()}")
|
||||||
|
else -> throw AssemblyError("can't assign byte to word register apir")
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda ${address.toHex()}
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (identifier != null) {
|
||||||
|
when(target.kind) {
|
||||||
|
TargetStorageKind.VARIABLE -> {
|
||||||
|
asmgen.loadByteFromPointerIntoA(identifier)
|
||||||
|
asmgen.out(" sta ${target.asmVarname}")
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> {
|
||||||
|
val sourceName = asmgen.asmVariableName(identifier)
|
||||||
|
storeByteViaRegisterAInMemoryAddress(sourceName, target.memory!!)
|
||||||
|
}
|
||||||
|
TargetStorageKind.ARRAY -> {
|
||||||
|
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${target.asmVarname} ")
|
||||||
|
}
|
||||||
|
TargetStorageKind.REGISTER -> {
|
||||||
|
asmgen.loadByteFromPointerIntoA(identifier)
|
||||||
|
when(target.register!!) {
|
||||||
|
RegisterOrPair.A -> {}
|
||||||
|
RegisterOrPair.X -> asmgen.out(" tax")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" tay")
|
||||||
|
else -> throw AssemblyError("can't assign byte to word register apir")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> {
|
||||||
|
asmgen.loadByteFromPointerIntoA(identifier)
|
||||||
|
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun storeByteViaRegisterAInMemoryAddress(ldaInstructionArg: String, memoryAddress: DirectMemoryWrite) {
|
||||||
|
val addressExpr = memoryAddress.addressExpression
|
||||||
|
val addressLv = addressExpr as? NumericLiteralValue
|
||||||
|
when {
|
||||||
|
addressLv != null -> {
|
||||||
|
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
|
||||||
|
}
|
||||||
|
addressExpr is IdentifierReference -> {
|
||||||
|
asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.translateExpression(addressExpr)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda P8ESTACK_LO,x
|
||||||
|
sta P8ZP_SCRATCH_W2
|
||||||
|
lda P8ESTACK_HI,x
|
||||||
|
sta P8ZP_SCRATCH_W2+1
|
||||||
|
lda $ldaInstructionArg
|
||||||
|
ldy #0
|
||||||
|
sta (P8ZP_SCRATCH_W2),y""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun storeRegisterInMemoryAddress(register: CpuRegister, memoryAddress: DirectMemoryWrite) {
|
||||||
|
// this is optimized for register A.
|
||||||
|
val addressExpr = memoryAddress.addressExpression
|
||||||
|
val addressLv = addressExpr as? NumericLiteralValue
|
||||||
|
val registerName = register.name.toLowerCase()
|
||||||
|
when {
|
||||||
|
addressLv != null -> {
|
||||||
|
asmgen.out(" st$registerName ${addressLv.number.toHex()}")
|
||||||
|
}
|
||||||
|
addressExpr is IdentifierReference -> {
|
||||||
|
when (register) {
|
||||||
|
CpuRegister.A -> {}
|
||||||
|
CpuRegister.X -> asmgen.out(" txa")
|
||||||
|
CpuRegister.Y -> asmgen.out(" tya")
|
||||||
|
}
|
||||||
|
asmgen.storeByteIntoPointer(addressExpr, null)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
asmgen.saveRegister(register)
|
||||||
|
asmgen.translateExpression(addressExpr)
|
||||||
|
asmgen.restoreRegister(CpuRegister.A)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
ldy P8ESTACK_LO,x
|
||||||
|
sty P8ZP_SCRATCH_W2
|
||||||
|
ldy P8ESTACK_HI,x
|
||||||
|
sty P8ZP_SCRATCH_W2+1
|
||||||
|
ldy #0
|
||||||
|
sta (P8ZP_SCRATCH_W2),y""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun popAndWriteArrayvalueWithUnscaledIndexA(elementDt: DataType, asmArrayvarname: String) {
|
||||||
|
when (elementDt) {
|
||||||
|
in ByteDatatypes ->
|
||||||
|
asmgen.out(" tay | inx | lda P8ESTACK_LO,x | sta $asmArrayvarname,y")
|
||||||
|
in WordDatatypes ->
|
||||||
|
asmgen.out(" asl a | tay | inx | lda P8ESTACK_LO,x | sta $asmArrayvarname,y | lda P8ESTACK_HI,x | sta $asmArrayvarname+1,y")
|
||||||
|
DataType.FLOAT ->
|
||||||
|
// scaling * 5 is done in the subroutine that's called
|
||||||
|
asmgen.out("""
|
||||||
|
sta P8ESTACK_LO,x
|
||||||
|
dex
|
||||||
|
lda #<$asmArrayvarname
|
||||||
|
ldy #>$asmArrayvarname
|
||||||
|
jsr c64flt.pop_float_to_indexed_var
|
||||||
|
""")
|
||||||
|
else ->
|
||||||
|
throw AssemblyError("weird array type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
126
compiler/src/prog8/compiler/target/cx16/CX16MachineDefinition.kt
Normal file
126
compiler/src/prog8/compiler/target/cx16/CX16MachineDefinition.kt
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
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 val initSystemProcname = "cx16.init_system"
|
||||||
|
|
||||||
|
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_REG_X = 0x7b // temp storage for register X (the evaluation stack pointer)
|
||||||
|
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_REG_X, 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_REG_X, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
||||||
|
}
|
||||||
|
ZeropageType.BASICSAFE -> {
|
||||||
|
free.addAll(0x22..0x7f)
|
||||||
|
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_REG_X, 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_REG_X !in free)
|
||||||
|
require(SCRATCH_W1 !in free)
|
||||||
|
require(SCRATCH_W2 !in free)
|
||||||
|
|
||||||
|
for (reserved in options.zpReserved)
|
||||||
|
reserve(reserved)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,8 +27,6 @@ val BuiltinFunctions = mapOf(
|
|||||||
"ror" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
"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),
|
"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),
|
"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),
|
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
"reverse" 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):
|
// these few have a return value depending on the argument(s):
|
||||||
@ -65,7 +63,7 @@ val BuiltinFunctions = mapOf(
|
|||||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
"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 }},
|
"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}},
|
"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),
|
"rnd" to FSignature(true, emptyList(), DataType.UBYTE),
|
||||||
"rndw" to FSignature(true, emptyList(), DataType.UWORD),
|
"rndw" to FSignature(true, emptyList(), DataType.UWORD),
|
||||||
"rndf" to FSignature(true, emptyList(), DataType.FLOAT),
|
"rndf" to FSignature(true, emptyList(), DataType.FLOAT),
|
||||||
@ -260,7 +258,7 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
|||||||
|
|
||||||
return when {
|
return when {
|
||||||
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
||||||
val length = (target as VarDecl).arraysize!!.size() ?: throw CannotEvaluateException("sizeof", "unknown array size")
|
val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size")
|
||||||
val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT))
|
val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT))
|
||||||
numericLiteral(elementDt.memorySize() * length, position)
|
numericLiteral(elementDt.memorySize() * length, position)
|
||||||
}
|
}
|
||||||
@ -282,11 +280,18 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
|||||||
private fun builtinStrlen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinStrlen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 1)
|
if (args.size != 1)
|
||||||
throw SyntaxError("strlen requires one argument", position)
|
throw SyntaxError("strlen requires one argument", position)
|
||||||
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val argument=args[0]
|
||||||
if(argument.type != DataType.STR)
|
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)
|
throw SyntaxError("strlen must have string argument", position)
|
||||||
|
if(vardecl.autogeneratedDontRemove) {
|
||||||
throw NotConstArgumentException() // this function is not considering the string argument a constant
|
return NumericLiteralValue.optimalInteger((vardecl.value as StringLiteralValue).value.length, argument.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw NotConstArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
@ -295,7 +300,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
|||||||
throw SyntaxError("len requires one argument", position)
|
throw SyntaxError("len requires one argument", position)
|
||||||
|
|
||||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
|
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)
|
if(arraySize != null)
|
||||||
return NumericLiteralValue.optimalInteger(arraySize, position)
|
return NumericLiteralValue.optimalInteger(arraySize, position)
|
||||||
if(args[0] is ArrayLiteralValue)
|
if(args[0] is ArrayLiteralValue)
|
||||||
@ -307,7 +312,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
|||||||
|
|
||||||
return when(target.datatype) {
|
return when(target.datatype) {
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> {
|
||||||
arraySize = target.arraysize?.size()
|
arraySize = target.arraysize?.constIndex()
|
||||||
if(arraySize==null)
|
if(arraySize==null)
|
||||||
throw CannotEvaluateException("len", "arraysize unknown")
|
throw CannotEvaluateException("len", "arraysize unknown")
|
||||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||||
@ -325,9 +330,9 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
|||||||
|
|
||||||
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||||
if (args.size != 2)
|
if (args.size != 2)
|
||||||
throw SyntaxError("mkword requires lsb and msb arguments", position)
|
throw SyntaxError("mkword requires msb and lsb arguments", position)
|
||||||
val constLsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
val constMsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val constMsb = args[1].constValue(program) ?: throw NotConstArgumentException()
|
val constLsb = args[1].constValue(program) ?: throw NotConstArgumentException()
|
||||||
val result = (constMsb.number.toInt() shl 8) or constLsb.number.toInt()
|
val result = (constMsb.number.toInt() shl 8) or constLsb.number.toInt()
|
||||||
return NumericLiteralValue(DataType.UWORD, result, position)
|
return NumericLiteralValue(DataType.UWORD, result, position)
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,7 @@ import prog8.compiler.loadAsmIncludeFile
|
|||||||
|
|
||||||
private val alwaysKeepSubroutines = setOf(
|
private val alwaysKeepSubroutines = setOf(
|
||||||
Pair("main", "start"),
|
Pair("main", "start"),
|
||||||
Pair("irq", "irq"),
|
Pair("irq", "irq")
|
||||||
Pair("prog8_lib", "init_system")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||||
|
@ -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)
|
val size = decl.arraysize!!.index.constValue(program)
|
||||||
if(size!=null) {
|
if(size!=null) {
|
||||||
return listOf(IAstModification.SetExpression(
|
return listOf(IAstModification.SetExpression(
|
||||||
@ -81,7 +81,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
val rangeExpr = decl.value as? RangeExpr
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
if(rangeExpr!=null) {
|
if(rangeExpr!=null) {
|
||||||
// convert the initializer range expression to an actual array
|
// 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())
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||||
val constRange = rangeExpr.toConstantIntegerRange()
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
@ -101,7 +101,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
}
|
}
|
||||||
if(numericLv!=null && numericLv.type==DataType.FLOAT)
|
if(numericLv!=null && numericLv.type==DataType.FLOAT)
|
||||||
errors.err("arraysize requires only integers here", numericLv.position)
|
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) {
|
if (rangeExpr==null && numericLv!=null) {
|
||||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||||
val fillvalue = numericLv.number.toInt()
|
val fillvalue = numericLv.number.toInt()
|
||||||
@ -125,18 +125,18 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
// create the array itself, filled with the fillvalue.
|
// 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)
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
val size = decl.arraysize?.size() ?: return noModifications
|
val size = decl.arraysize?.constIndex() ?: return noModifications
|
||||||
val litval = decl.value as? NumericLiteralValue
|
val litval = decl.value as? NumericLiteralValue
|
||||||
val rangeExpr = decl.value as? RangeExpr
|
val rangeExpr = decl.value as? RangeExpr
|
||||||
if(rangeExpr!=null) {
|
if(rangeExpr!=null) {
|
||||||
// convert the initializer range expression to an actual array of floats
|
// 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())
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||||
val constRange = rangeExpr.toConstantIntegerRange()
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
@ -154,7 +154,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
errors.err("float value overflow", litval.position)
|
errors.err("float value overflow", litval.position)
|
||||||
else {
|
else {
|
||||||
// create the array itself, filled with the fillvalue.
|
// 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)
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = litval.position)
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
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
|
if(declValue!=null && decl.type==VarDeclType.VAR
|
||||||
&& declValue is NumericLiteralValue && !declValue.inferType(program).istype(decl.datatype)) {
|
&& declValue is NumericLiteralValue && !declValue.inferType(program).istype(decl.datatype)) {
|
||||||
// cast the numeric literal to the appropriate datatype of the variable
|
// cast the numeric literal to the appropriate datatype of the variable
|
||||||
return listOf(IAstModification.ReplaceNode(decl.value!!, declValue.castNoCheck(decl.datatype), decl))
|
val cast = declValue.cast(decl.datatype)
|
||||||
|
if(cast.isValid)
|
||||||
|
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -316,21 +318,24 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||||
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr {
|
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr? {
|
||||||
val newFrom: NumericLiteralValue
|
val fromCast = rangeFrom.cast(targetDt)
|
||||||
val newTo: NumericLiteralValue
|
val toCast = rangeTo.cast(targetDt)
|
||||||
try {
|
if(!fromCast.isValid || !toCast.isValid)
|
||||||
newFrom = rangeFrom.castNoCheck(targetDt)
|
return null
|
||||||
newTo = rangeTo.castNoCheck(targetDt)
|
|
||||||
} catch (x: ExpressionError) {
|
val newStep =
|
||||||
return range
|
if(stepLiteral!=null) {
|
||||||
}
|
val stepCast = stepLiteral.cast(targetDt)
|
||||||
val newStep: Expression = try {
|
if(stepCast.isValid)
|
||||||
stepLiteral?.castNoCheck(targetDt)?: range.step
|
stepCast.valueOrZero()
|
||||||
} catch(ee: ExpressionError) {
|
else
|
||||||
|
range.step
|
||||||
|
} else {
|
||||||
range.step
|
range.step
|
||||||
}
|
}
|
||||||
return RangeExpr(newFrom, newTo, newStep, range.position)
|
|
||||||
|
return RangeExpr(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, range.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||||
@ -346,6 +351,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
if(rangeFrom.type!= DataType.UBYTE) {
|
if(rangeFrom.type!= DataType.UBYTE) {
|
||||||
// attempt to translate the iterable into ubyte values
|
// attempt to translate the iterable into ubyte values
|
||||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
if(newIter!=null)
|
||||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,6 +359,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
if(rangeFrom.type!= DataType.BYTE) {
|
if(rangeFrom.type!= DataType.BYTE) {
|
||||||
// attempt to translate the iterable into byte values
|
// attempt to translate the iterable into byte values
|
||||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
if(newIter!=null)
|
||||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -360,6 +367,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
if(rangeFrom.type!= DataType.UWORD) {
|
if(rangeFrom.type!= DataType.UWORD) {
|
||||||
// attempt to translate the iterable into uword values
|
// attempt to translate the iterable into uword values
|
||||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
if(newIter!=null)
|
||||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -367,6 +375,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
if(rangeFrom.type!= DataType.WORD) {
|
if(rangeFrom.type!= DataType.WORD) {
|
||||||
// attempt to translate the iterable into word values
|
// attempt to translate the iterable into word values
|
||||||
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
val newIter = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||||
|
if(newIter!=null)
|
||||||
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
return listOf(IAstModification.ReplaceNode(forLoop.iterable, newIter, forLoop))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -381,8 +390,9 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
if(decl.type== VarDeclType.CONST && numval!=null) {
|
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||||
val valueDt = numval.inferType(program)
|
val valueDt = numval.inferType(program)
|
||||||
if(!valueDt.istype(decl.datatype)) {
|
if(!valueDt.istype(decl.datatype)) {
|
||||||
val adjustedVal = numval.castNoCheck(decl.datatype)
|
val cast = numval.cast(decl.datatype)
|
||||||
return listOf(IAstModification.ReplaceNode(numval, adjustedVal, decl))
|
if(cast.isValid)
|
||||||
|
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -415,8 +425,9 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
|||||||
// todo: this implements only a small set of possible reorderings at this time
|
// todo: this implements only a small set of possible reorderings at this time
|
||||||
if(expr.operator==subExpr.operator) {
|
if(expr.operator==subExpr.operator) {
|
||||||
// both operators are the same.
|
// 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) {
|
return if(leftIsConst) {
|
||||||
if(subleftIsConst)
|
if(subleftIsConst)
|
||||||
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)
|
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)
|
||||||
|
@ -29,9 +29,9 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
// try to statically convert a literal value into one of the desired type
|
// try to statically convert a literal value into one of the desired type
|
||||||
val literal = typecast.expression as? NumericLiteralValue
|
val literal = typecast.expression as? NumericLiteralValue
|
||||||
if (literal != null) {
|
if (literal != null) {
|
||||||
val newLiteral = literal.castNoCheck(typecast.type)
|
val newLiteral = literal.cast(typecast.type)
|
||||||
if (newLiteral !== literal)
|
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal)
|
||||||
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral, typecast)
|
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral.valueOrZero(), typecast)
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove redundant nested typecasts
|
// remove redundant nested typecasts
|
||||||
@ -586,10 +586,10 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
|||||||
} else if (amount >= 8) {
|
} else if (amount >= 8) {
|
||||||
val lsb = TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
|
val lsb = TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
|
||||||
if (amount == 8) {
|
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)
|
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 -> {
|
else -> {
|
||||||
|
@ -229,7 +229,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(iterable.datatype in ArrayDatatypes) {
|
else if(iterable.datatype in ArrayDatatypes) {
|
||||||
val size = iterable.arraysize!!.size()
|
val size = iterable.arraysize!!.constIndex()
|
||||||
if(size==1) {
|
if(size==1) {
|
||||||
// loop over array of length 1 -> just assign the single value
|
// loop over array of length 1 -> just assign the single value
|
||||||
val av = (iterable.value as ArrayLiteralValue).value[0].constValue(program)?.number
|
val av = (iterable.value as ArrayLiteralValue).value[0].constValue(program)?.number
|
||||||
@ -438,28 +438,10 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
"<<" -> {
|
"<<" -> {
|
||||||
if (cv == 0.0)
|
if (cv == 0.0)
|
||||||
return listOf(IAstModification.Remove(assignment, parent))
|
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)
|
if (cv == 0.0)
|
||||||
return listOf(IAstModification.Remove(assignment, parent))
|
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import org.antlr.v4.runtime.*
|
|||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.antlr.toAst
|
import prog8.ast.antlr.toAst
|
||||||
import prog8.ast.base.ErrorReporter
|
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.base.SyntaxError
|
import prog8.ast.base.SyntaxError
|
||||||
import prog8.ast.base.checkImportedValid
|
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 fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
||||||
|
|
||||||
|
|
||||||
internal class ModuleImporter() {
|
internal class ModuleImporter {
|
||||||
|
|
||||||
internal fun importModule(program: Program, filePath: Path): Module {
|
internal fun importModule(program: Program, filePath: Path): Module {
|
||||||
print("importing '${moduleName(filePath.fileName)}'")
|
print("importing '${moduleName(filePath.fileName)}'")
|
||||||
@ -95,7 +94,7 @@ internal class ModuleImporter() {
|
|||||||
|
|
||||||
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
|
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
|
||||||
val fileName = "$name.p8"
|
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")
|
val propPath = System.getProperty("prog8.libdir")
|
||||||
if(propPath!=null)
|
if(propPath!=null)
|
||||||
@ -110,7 +109,7 @@ internal class ModuleImporter() {
|
|||||||
if (Files.isReadable(file)) return file
|
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? {
|
private fun executeImportDirective(program: Program, import: Directive, source: Path): Module? {
|
||||||
@ -129,9 +128,6 @@ internal class ModuleImporter() {
|
|||||||
if(resource!=null) {
|
if(resource!=null) {
|
||||||
// load the module from the embedded resource
|
// load the module from the embedded resource
|
||||||
resource.use {
|
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)
|
importModule(program, CharStreams.fromStream(it), Paths.get("@embedded@/$moduleName"), true)
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ class TestCompiler {
|
|||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestZeropage {
|
class TestC64Zeropage {
|
||||||
|
|
||||||
private val errors = ErrorReporter()
|
private val errors = ErrorReporter()
|
||||||
|
|
||||||
|
@ -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``.
|
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
|
It is then (automatically) fed to the `64tass <https://sourceforge.net/projects/tass64/>`_ cross assembler tool
|
||||||
that assembles it into the final program.
|
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
|
a successful compilation. This will load your program and the symbol and breakpoint lists
|
||||||
(for the machine code monitor) into the emulator.
|
(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,
|
It consists of compilation options and other directives, imports of other modules,
|
||||||
and source code for one or more code blocks.
|
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:
|
Prog8 has various *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.
|
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
|
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.
|
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
|
This is an experimental compiled programming language targeting the 8-bit
|
||||||
`6502 <https://en.wikipedia.org/wiki/MOS_Technology_6502>`_ /
|
`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.
|
`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,
|
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>`_.
|
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::
|
This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
||||||
|
|
||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -51,35 +52,33 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
|
|||||||
ubyte candidate_prime = 2
|
ubyte candidate_prime = 2
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
memset(sieve, 256, false)
|
memset(sieve, 256, false) ; clear the sieve
|
||||||
|
txt.print("prime numbers up to 255:\n\n")
|
||||||
c64scr.print("prime numbers up to 255:\n\n")
|
|
||||||
ubyte amount=0
|
ubyte amount=0
|
||||||
repeat {
|
repeat {
|
||||||
ubyte prime = find_next_prime()
|
ubyte prime = find_next_prime()
|
||||||
if prime==0
|
if prime==0
|
||||||
break
|
break
|
||||||
c64scr.print_ub(prime)
|
txt.print_ub(prime)
|
||||||
c64scr.print(", ")
|
txt.print(", ")
|
||||||
amount++
|
amount++
|
||||||
}
|
}
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("number of primes (expected 54): ")
|
txt.print("number of primes (expected 54): ")
|
||||||
c64scr.print_ub(amount)
|
txt.print_ub(amount)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub find_next_prime() -> ubyte {
|
sub find_next_prime() -> ubyte {
|
||||||
|
|
||||||
while sieve[candidate_prime] {
|
while sieve[candidate_prime] {
|
||||||
candidate_prime++
|
candidate_prime++
|
||||||
if candidate_prime==0
|
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
|
sieve[candidate_prime] = true
|
||||||
uword multiple = candidate_prime
|
uword multiple = candidate_prime
|
||||||
|
|
||||||
while multiple < len(sieve) {
|
while multiple < len(sieve) {
|
||||||
sieve[lsb(multiple)] = true
|
sieve[lsb(multiple)] = true
|
||||||
multiple += candidate_prime
|
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:
|
when compiled an ran on a C-64 you get this:
|
||||||
|
|
||||||
.. image:: _static/primes_example.png
|
.. 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::
|
The following programs shows a use of the high level ``struct`` type::
|
||||||
|
|
||||||
%import c64utils
|
|
||||||
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -111,22 +112,25 @@ The following programs shows a use of the high level ``struct`` type::
|
|||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
Color purple = {255, 0, 255}
|
Color purple = [255, 0, 255]
|
||||||
|
|
||||||
Color other
|
Color other
|
||||||
|
|
||||||
other = purple
|
other = purple
|
||||||
other.red /= 2
|
other.red /= 2
|
||||||
other.green = 10 + other.green / 2
|
other.green = 10 + other.green / 2
|
||||||
other.blue = 99
|
other.blue = 99
|
||||||
|
|
||||||
c64scr.print_ub(other.red)
|
txt.print_ub(other.red)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_ub(other.green)
|
txt.print_ub(other.green)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_ub(other.blue)
|
txt.print_ub(other.blue)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
when compiled and ran, it prints ``127,10,99`` on the screen.
|
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.
|
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/>`_.
|
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::
|
.. important::
|
||||||
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)
|
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)
|
||||||
|
@ -748,8 +748,9 @@ msb(x)
|
|||||||
sgn(x)
|
sgn(x)
|
||||||
Get the sign of the value. Result is -1, 0 or 1 (negative, zero, positive).
|
Get the sign of the value. Result is -1, 0 or 1 (negative, zero, positive).
|
||||||
|
|
||||||
mkword(lsb, msb)
|
mkword(msb, lsb)
|
||||||
Efficiently create a word value from two bytes (the lsb and the msb). Avoids multiplication and shifting.
|
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)
|
any(x)
|
||||||
1 ('true') if any of the values in the array value x is 'true' (not zero), else 0 ('false')
|
1 ('true') if any of the values in the array value x is 'true' (not zero), else 0 ('false')
|
||||||
@ -766,16 +767,6 @@ rndw()
|
|||||||
rndf()
|
rndf()
|
||||||
returns a pseudo-random float between 0.0 and 1.0
|
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)
|
rol(x)
|
||||||
Rotate the bits in x (byte or word) one position to the left.
|
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,
|
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,
|
||||||
@ -812,7 +803,7 @@ memset(address, numbytes, bytevalue)
|
|||||||
Efficiently set a part of memory to the given (u)byte value.
|
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!
|
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
|
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)
|
memsetw(address, numwords, wordvalue)
|
||||||
Efficiently set a part of memory to the given (u)word value.
|
Efficiently set a part of memory to the given (u)word value.
|
||||||
|
@ -157,7 +157,7 @@ Directives
|
|||||||
Identifiers
|
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::
|
and after that, a combination of letters, numbers, or underscores. Examples of valid identifiers::
|
||||||
|
|
||||||
a
|
a
|
||||||
@ -165,7 +165,7 @@ and after that, a combination of letters, numbers, or underscores. Examples of v
|
|||||||
monkey
|
monkey
|
||||||
COUNTER
|
COUNTER
|
||||||
Better_Name_2
|
Better_Name_2
|
||||||
_something_strange_
|
something_strange__
|
||||||
|
|
||||||
|
|
||||||
Code blocks
|
Code blocks
|
||||||
|
@ -4,12 +4,17 @@ Target system specification
|
|||||||
|
|
||||||
Prog8 targets the following hardware:
|
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)
|
- 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.
|
Currently there are two machines that are supported as compiler target (via the ``-target`` compiler argument):
|
||||||
This chapter explains the relevant system details of such a machine.
|
|
||||||
|
- '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
|
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,
|
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).
|
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()
|
c64.set_irqvec()
|
||||||
c64utils.set_irqvec_excl()
|
c64.set_irqvec_excl()
|
||||||
|
|
||||||
c64utils.set_rasterirq( <raster line> )
|
c64.set_rasterirq( <raster line> )
|
||||||
c64utils.set_rasterirq_excl( <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
|
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::
|
as a subroutine ``irq`` in the module ``irq`` so like this::
|
||||||
|
@ -4,6 +4,7 @@ TODO
|
|||||||
|
|
||||||
- optimize assignment codegeneration
|
- optimize assignment codegeneration
|
||||||
- get rid of all TODO's ;-)
|
- get rid of all TODO's ;-)
|
||||||
|
- 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)
|
- 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' ?
|
- aliases for imported symbols for example perhaps '%alias print = c64scr.print' ?
|
||||||
- investigate support for 8bitguy's Commander X16 platform https://www.commanderx16.com and https://github.com/commanderx16/x16-docs
|
- investigate support for 8bitguy's Commander X16 platform https://www.commanderx16.com and https://github.com/commanderx16/x16-docs
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
%import c64lib
|
|
||||||
%import c64utils
|
|
||||||
%import c64flt
|
%import c64flt
|
||||||
%zeropage dontuse
|
%import c64textio
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
@ -20,90 +19,90 @@ main {
|
|||||||
|
|
||||||
; LEN/STRLEN
|
; LEN/STRLEN
|
||||||
ubyte length = len(name)
|
ubyte length = len(name)
|
||||||
if length!=5 c64scr.print("error len1\n")
|
if length!=5 txt.print("error len1\n")
|
||||||
length = len(uwarr)
|
length = len(uwarr)
|
||||||
if length!=5 c64scr.print("error len2\n")
|
if length!=5 txt.print("error len2\n")
|
||||||
length=strlen(name)
|
length=strlen(name)
|
||||||
if length!=5 c64scr.print("error strlen1\n")
|
if length!=5 txt.print("error strlen1\n")
|
||||||
name[3] = 0
|
name[3] = 0
|
||||||
length=strlen(name)
|
length=strlen(name)
|
||||||
if length!=3 c64scr.print("error strlen2\n")
|
if length!=3 txt.print("error strlen2\n")
|
||||||
|
|
||||||
; MAX
|
; MAX
|
||||||
ub = max(ubarr)
|
ub = max(ubarr)
|
||||||
if ub!=199 c64scr.print("error max1\n")
|
if ub!=199 txt.print("error max1\n")
|
||||||
bb = max(barr)
|
bb = max(barr)
|
||||||
if bb!=99 c64scr.print("error max2\n")
|
if bb!=99 txt.print("error max2\n")
|
||||||
uw = max(uwarr)
|
uw = max(uwarr)
|
||||||
if uw!=4444 c64scr.print("error max3\n")
|
if uw!=4444 txt.print("error max3\n")
|
||||||
ww = max(warr)
|
ww = max(warr)
|
||||||
if ww!=999 c64scr.print("error max4\n")
|
if ww!=999 txt.print("error max4\n")
|
||||||
ff = max(farr)
|
ff = max(farr)
|
||||||
if ff!=999.9 c64scr.print("error max5\n")
|
if ff!=999.9 txt.print("error max5\n")
|
||||||
|
|
||||||
; MIN
|
; MIN
|
||||||
ub = min(ubarr)
|
ub = min(ubarr)
|
||||||
if ub!=0 c64scr.print("error min1\n")
|
if ub!=0 txt.print("error min1\n")
|
||||||
bb = min(barr)
|
bb = min(barr)
|
||||||
if bb!=-122 c64scr.print("error min2\n")
|
if bb!=-122 txt.print("error min2\n")
|
||||||
uw = min(uwarr)
|
uw = min(uwarr)
|
||||||
if uw!=0 c64scr.print("error min3\n")
|
if uw!=0 txt.print("error min3\n")
|
||||||
ww = min(warr)
|
ww = min(warr)
|
||||||
if ww!=-4444 c64scr.print("error min4\n")
|
if ww!=-4444 txt.print("error min4\n")
|
||||||
ff = min(farr)
|
ff = min(farr)
|
||||||
if ff!=-4444.4 c64scr.print("error min5\n")
|
if ff!=-4444.4 txt.print("error min5\n")
|
||||||
|
|
||||||
; SUM
|
; SUM
|
||||||
uw = sum(ubarr)
|
uw = sum(ubarr)
|
||||||
if uw!=420 c64scr.print("error sum1\n")
|
if uw!=420 txt.print("error sum1\n")
|
||||||
ww = sum(barr)
|
ww = sum(barr)
|
||||||
if ww!=-101 c64scr.print("error sum2\n")
|
if ww!=-101 txt.print("error sum2\n")
|
||||||
uw = sum(uwarr)
|
uw = sum(uwarr)
|
||||||
if uw!=6665 c64scr.print("error sum3\n")
|
if uw!=6665 txt.print("error sum3\n")
|
||||||
ww = sum(warr)
|
ww = sum(warr)
|
||||||
if ww!=-4223 c64scr.print("error sum4\n")
|
if ww!=-4223 txt.print("error sum4\n")
|
||||||
ff = sum(farr)
|
ff = sum(farr)
|
||||||
if ff!=-4222.4 c64scr.print("error sum5\n")
|
if ff!=-4222.4 txt.print("error sum5\n")
|
||||||
|
|
||||||
; ANY
|
; ANY
|
||||||
ub = any(ubarr)
|
ub = any(ubarr)
|
||||||
if ub==0 c64scr.print("error any1\n")
|
if ub==0 txt.print("error any1\n")
|
||||||
ub = any(barr)
|
ub = any(barr)
|
||||||
if ub==0 c64scr.print("error any2\n")
|
if ub==0 txt.print("error any2\n")
|
||||||
ub = any(uwarr)
|
ub = any(uwarr)
|
||||||
if ub==0 c64scr.print("error any3\n")
|
if ub==0 txt.print("error any3\n")
|
||||||
ub = any(warr)
|
ub = any(warr)
|
||||||
if ub==0 c64scr.print("error any4\n")
|
if ub==0 txt.print("error any4\n")
|
||||||
ub = any(farr)
|
ub = any(farr)
|
||||||
if ub==0 c64scr.print("error any5\n")
|
if ub==0 txt.print("error any5\n")
|
||||||
|
|
||||||
; ALL
|
; ALL
|
||||||
ub = all(ubarr)
|
ub = all(ubarr)
|
||||||
if ub==1 c64scr.print("error all1\n")
|
if ub==1 txt.print("error all1\n")
|
||||||
ub = all(barr)
|
ub = all(barr)
|
||||||
if ub==1 c64scr.print("error all2\n")
|
if ub==1 txt.print("error all2\n")
|
||||||
ub = all(uwarr)
|
ub = all(uwarr)
|
||||||
if ub==1 c64scr.print("error all3\n")
|
if ub==1 txt.print("error all3\n")
|
||||||
ub = all(warr)
|
ub = all(warr)
|
||||||
if ub==1 c64scr.print("error all4\n")
|
if ub==1 txt.print("error all4\n")
|
||||||
ub = all(farr)
|
ub = all(farr)
|
||||||
if ub==1 c64scr.print("error all5\n")
|
if ub==1 txt.print("error all5\n")
|
||||||
ubarr[1]=$40
|
ubarr[1]=$40
|
||||||
barr[1]=$40
|
barr[1]=$40
|
||||||
uwarr[1]=$4000
|
uwarr[1]=$4000
|
||||||
warr[1]=$4000
|
warr[1]=$4000
|
||||||
farr[1]=1.1
|
farr[1]=1.1
|
||||||
ub = all(ubarr)
|
ub = all(ubarr)
|
||||||
if ub==0 c64scr.print("error all6\n")
|
if ub==0 txt.print("error all6\n")
|
||||||
ub = all(barr)
|
ub = all(barr)
|
||||||
if ub==0 c64scr.print("error all7\n")
|
if ub==0 txt.print("error all7\n")
|
||||||
ub = all(uwarr)
|
ub = all(uwarr)
|
||||||
if ub==0 c64scr.print("error all8\n")
|
if ub==0 txt.print("error all8\n")
|
||||||
ub = all(warr)
|
ub = all(warr)
|
||||||
if ub==0 c64scr.print("error all9\n")
|
if ub==0 txt.print("error all9\n")
|
||||||
ub = all(farr)
|
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
|
%import c64textio
|
||||||
%zeropage dontuse
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -7,379 +7,379 @@ main {
|
|||||||
sub start() {
|
sub start() {
|
||||||
ubyte A
|
ubyte A
|
||||||
|
|
||||||
c64scr.print("ubyte shift left\n")
|
txt.print("ubyte shift left\n")
|
||||||
A = shiftlb0()
|
A = shiftlb0()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb1()
|
A = shiftlb1()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb2()
|
A = shiftlb2()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb3()
|
A = shiftlb3()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb4()
|
A = shiftlb4()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb5()
|
A = shiftlb5()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb6()
|
A = shiftlb6()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb7()
|
A = shiftlb7()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb8()
|
A = shiftlb8()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftlb9()
|
A = shiftlb9()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
c64scr.print("ubyte shift right\n")
|
txt.print("ubyte shift right\n")
|
||||||
A = shiftrb0()
|
A = shiftrb0()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb1()
|
A = shiftrb1()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb2()
|
A = shiftrb2()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb3()
|
A = shiftrb3()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb4()
|
A = shiftrb4()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb5()
|
A = shiftrb5()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb6()
|
A = shiftrb6()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb7()
|
A = shiftrb7()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb8()
|
A = shiftrb8()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
A = shiftrb9()
|
A = shiftrb9()
|
||||||
c64scr.print_ubbin(A, true)
|
txt.print_ubbin(A, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
c64scr.print("signed byte shift left\n")
|
txt.print("signed byte shift left\n")
|
||||||
byte signedb
|
byte signedb
|
||||||
signedb = shiftlsb0()
|
signedb = shiftlsb0()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb1()
|
signedb = shiftlsb1()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb2()
|
signedb = shiftlsb2()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb3()
|
signedb = shiftlsb3()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb4()
|
signedb = shiftlsb4()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb5()
|
signedb = shiftlsb5()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb6()
|
signedb = shiftlsb6()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb7()
|
signedb = shiftlsb7()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb8()
|
signedb = shiftlsb8()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftlsb9()
|
signedb = shiftlsb9()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
c64scr.print("signed byte shift right\n")
|
txt.print("signed byte shift right\n")
|
||||||
signedb = shiftrsb0()
|
signedb = shiftrsb0()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb1()
|
signedb = shiftrsb1()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb2()
|
signedb = shiftrsb2()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb3()
|
signedb = shiftrsb3()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb4()
|
signedb = shiftrsb4()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb5()
|
signedb = shiftrsb5()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb6()
|
signedb = shiftrsb6()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb7()
|
signedb = shiftrsb7()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb8()
|
signedb = shiftrsb8()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
signedb = shiftrsb9()
|
signedb = shiftrsb9()
|
||||||
c64scr.print_ubbin(signedb as ubyte, true)
|
txt.print_ubbin(signedb as ubyte, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
c64scr.print("uword shift left\n")
|
txt.print("uword shift left\n")
|
||||||
uword uw
|
uword uw
|
||||||
uw = shiftluw0()
|
uw = shiftluw0()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw1()
|
uw = shiftluw1()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw2()
|
uw = shiftluw2()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw3()
|
uw = shiftluw3()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw4()
|
uw = shiftluw4()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw5()
|
uw = shiftluw5()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw6()
|
uw = shiftluw6()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw7()
|
uw = shiftluw7()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw8()
|
uw = shiftluw8()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw9()
|
uw = shiftluw9()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw10()
|
uw = shiftluw10()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw11()
|
uw = shiftluw11()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw12()
|
uw = shiftluw12()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw13()
|
uw = shiftluw13()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw14()
|
uw = shiftluw14()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw15()
|
uw = shiftluw15()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw16()
|
uw = shiftluw16()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftluw17()
|
uw = shiftluw17()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
c64scr.print("uword shift right\n")
|
txt.print("uword shift right\n")
|
||||||
uw = shiftruw0()
|
uw = shiftruw0()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw1()
|
uw = shiftruw1()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw2()
|
uw = shiftruw2()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw3()
|
uw = shiftruw3()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw4()
|
uw = shiftruw4()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw5()
|
uw = shiftruw5()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw6()
|
uw = shiftruw6()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw7()
|
uw = shiftruw7()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw8()
|
uw = shiftruw8()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw9()
|
uw = shiftruw9()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw10()
|
uw = shiftruw10()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw11()
|
uw = shiftruw11()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw12()
|
uw = shiftruw12()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw13()
|
uw = shiftruw13()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw14()
|
uw = shiftruw14()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw15()
|
uw = shiftruw15()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw16()
|
uw = shiftruw16()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
uw = shiftruw17()
|
uw = shiftruw17()
|
||||||
c64scr.print_uwbin(uw, true)
|
txt.print_uwbin(uw, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
c64scr.print("signed word shift left\n")
|
txt.print("signed word shift left\n")
|
||||||
word sw
|
word sw
|
||||||
sw = shiftlsw0()
|
sw = shiftlsw0()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw1()
|
sw = shiftlsw1()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw2()
|
sw = shiftlsw2()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw3()
|
sw = shiftlsw3()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw4()
|
sw = shiftlsw4()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw5()
|
sw = shiftlsw5()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw6()
|
sw = shiftlsw6()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw7()
|
sw = shiftlsw7()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw8()
|
sw = shiftlsw8()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw9()
|
sw = shiftlsw9()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw10()
|
sw = shiftlsw10()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw11()
|
sw = shiftlsw11()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw12()
|
sw = shiftlsw12()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw13()
|
sw = shiftlsw13()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw14()
|
sw = shiftlsw14()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw15()
|
sw = shiftlsw15()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw16()
|
sw = shiftlsw16()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftlsw17()
|
sw = shiftlsw17()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.print("enter to continue:\n")
|
txt.print("enter to continue:\n")
|
||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
|
|
||||||
c64scr.print("signed word shift right\n")
|
txt.print("signed word shift right\n")
|
||||||
sw = shiftrsw0()
|
sw = shiftrsw0()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw1()
|
sw = shiftrsw1()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw2()
|
sw = shiftrsw2()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw3()
|
sw = shiftrsw3()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw4()
|
sw = shiftrsw4()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw5()
|
sw = shiftrsw5()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw6()
|
sw = shiftrsw6()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw7()
|
sw = shiftrsw7()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw8()
|
sw = shiftrsw8()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw9()
|
sw = shiftrsw9()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw10()
|
sw = shiftrsw10()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw11()
|
sw = shiftrsw11()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw12()
|
sw = shiftrsw12()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw13()
|
sw = shiftrsw13()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw14()
|
sw = shiftrsw14()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw15()
|
sw = shiftrsw15()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw16()
|
sw = shiftrsw16()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
sw = shiftrsw17()
|
sw = shiftrsw17()
|
||||||
c64scr.print_uwbin(sw as uword, true)
|
txt.print_uwbin(sw as uword, true)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
%import c64lib
|
|
||||||
%import c64utils
|
|
||||||
%import c64flt
|
%import c64flt
|
||||||
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -29,75 +28,75 @@ main {
|
|||||||
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
ubyte r = a1/a2
|
ubyte r = a1/a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("ubyte ")
|
txt.print("ubyte ")
|
||||||
c64scr.print_ub(a1)
|
txt.print_ub(a1)
|
||||||
c64scr.print(" / ")
|
txt.print(" / ")
|
||||||
c64scr.print_ub(a2)
|
txt.print_ub(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_ub(r)
|
txt.print_ub(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_byte(byte a1, byte a2, byte c) {
|
sub div_byte(byte a1, byte a2, byte c) {
|
||||||
byte r = a1/a2
|
byte r = a1/a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("byte ")
|
txt.print("byte ")
|
||||||
c64scr.print_b(a1)
|
txt.print_b(a1)
|
||||||
c64scr.print(" / ")
|
txt.print(" / ")
|
||||||
c64scr.print_b(a2)
|
txt.print_b(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_b(r)
|
txt.print_b(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_uword(uword a1, uword a2, uword c) {
|
sub div_uword(uword a1, uword a2, uword c) {
|
||||||
uword r = a1/a2
|
uword r = a1/a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("uword ")
|
txt.print("uword ")
|
||||||
c64scr.print_uw(a1)
|
txt.print_uw(a1)
|
||||||
c64scr.print(" / ")
|
txt.print(" / ")
|
||||||
c64scr.print_uw(a2)
|
txt.print_uw(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_uw(r)
|
txt.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_word(word a1, word a2, word c) {
|
sub div_word(word a1, word a2, word c) {
|
||||||
word r = a1/a2
|
word r = a1/a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("word ")
|
txt.print("word ")
|
||||||
c64scr.print_w(a1)
|
txt.print_w(a1)
|
||||||
c64scr.print(" / ")
|
txt.print(" / ")
|
||||||
c64scr.print_w(a2)
|
txt.print_w(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_w(r)
|
txt.print_w(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_float(float a1, float a2, float c) {
|
sub div_float(float a1, float a2, float c) {
|
||||||
float r = a1/a2
|
float r = a1/a2
|
||||||
if abs(r-c)<0.00001
|
if abs(r-c)<0.00001
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
c64scr.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
c64flt.print_f(a1)
|
||||||
c64scr.print(" / ")
|
txt.print(" / ")
|
||||||
c64flt.print_f(a2)
|
c64flt.print_f(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
%import c64lib
|
|
||||||
%import c64utils
|
|
||||||
%import c64flt
|
%import c64flt
|
||||||
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -37,75 +36,75 @@ main {
|
|||||||
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
ubyte r = a1-a2
|
ubyte r = a1-a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("ubyte ")
|
txt.print("ubyte ")
|
||||||
c64scr.print_ub(a1)
|
txt.print_ub(a1)
|
||||||
c64scr.print(" - ")
|
txt.print(" - ")
|
||||||
c64scr.print_ub(a2)
|
txt.print_ub(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_ub(r)
|
txt.print_ub(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_byte(byte a1, byte a2, byte c) {
|
sub minus_byte(byte a1, byte a2, byte c) {
|
||||||
byte r = a1-a2
|
byte r = a1-a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("byte ")
|
txt.print("byte ")
|
||||||
c64scr.print_b(a1)
|
txt.print_b(a1)
|
||||||
c64scr.print(" - ")
|
txt.print(" - ")
|
||||||
c64scr.print_b(a2)
|
txt.print_b(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_b(r)
|
txt.print_b(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_uword(uword a1, uword a2, uword c) {
|
sub minus_uword(uword a1, uword a2, uword c) {
|
||||||
uword r = a1-a2
|
uword r = a1-a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("uword ")
|
txt.print("uword ")
|
||||||
c64scr.print_uw(a1)
|
txt.print_uw(a1)
|
||||||
c64scr.print(" - ")
|
txt.print(" - ")
|
||||||
c64scr.print_uw(a2)
|
txt.print_uw(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_uw(r)
|
txt.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_word(word a1, word a2, word c) {
|
sub minus_word(word a1, word a2, word c) {
|
||||||
word r = a1-a2
|
word r = a1-a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("word ")
|
txt.print("word ")
|
||||||
c64scr.print_w(a1)
|
txt.print_w(a1)
|
||||||
c64scr.print(" - ")
|
txt.print(" - ")
|
||||||
c64scr.print_w(a2)
|
txt.print_w(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_w(r)
|
txt.print_w(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_float(float a1, float a2, float c) {
|
sub minus_float(float a1, float a2, float c) {
|
||||||
float r = a1-a2
|
float r = a1-a2
|
||||||
if abs(r-c)<0.00001
|
if abs(r-c)<0.00001
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
c64scr.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
c64flt.print_f(a1)
|
||||||
c64scr.print(" - ")
|
txt.print(" - ")
|
||||||
c64flt.print_f(a2)
|
c64flt.print_f(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
%import c64lib
|
|
||||||
%import c64utils
|
|
||||||
%import c64flt
|
%import c64flt
|
||||||
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -31,75 +30,75 @@ main {
|
|||||||
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
ubyte r = a1*a2
|
ubyte r = a1*a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("ubyte ")
|
txt.print("ubyte ")
|
||||||
c64scr.print_ub(a1)
|
txt.print_ub(a1)
|
||||||
c64scr.print(" * ")
|
txt.print(" * ")
|
||||||
c64scr.print_ub(a2)
|
txt.print_ub(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_ub(r)
|
txt.print_ub(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_byte(byte a1, byte a2, byte c) {
|
sub mul_byte(byte a1, byte a2, byte c) {
|
||||||
byte r = a1*a2
|
byte r = a1*a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("byte ")
|
txt.print("byte ")
|
||||||
c64scr.print_b(a1)
|
txt.print_b(a1)
|
||||||
c64scr.print(" * ")
|
txt.print(" * ")
|
||||||
c64scr.print_b(a2)
|
txt.print_b(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_b(r)
|
txt.print_b(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_uword(uword a1, uword a2, uword c) {
|
sub mul_uword(uword a1, uword a2, uword c) {
|
||||||
uword r = a1*a2
|
uword r = a1*a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("uword ")
|
txt.print("uword ")
|
||||||
c64scr.print_uw(a1)
|
txt.print_uw(a1)
|
||||||
c64scr.print(" * ")
|
txt.print(" * ")
|
||||||
c64scr.print_uw(a2)
|
txt.print_uw(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_uw(r)
|
txt.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_word(word a1, word a2, word c) {
|
sub mul_word(word a1, word a2, word c) {
|
||||||
word r = a1*a2
|
word r = a1*a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("word ")
|
txt.print("word ")
|
||||||
c64scr.print_w(a1)
|
txt.print_w(a1)
|
||||||
c64scr.print(" * ")
|
txt.print(" * ")
|
||||||
c64scr.print_w(a2)
|
txt.print_w(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_w(r)
|
txt.print_w(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_float(float a1, float a2, float c) {
|
sub mul_float(float a1, float a2, float c) {
|
||||||
float r = a1*a2
|
float r = a1*a2
|
||||||
if abs(r-c)<0.00001
|
if abs(r-c)<0.00001
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
c64scr.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
c64flt.print_f(a1)
|
||||||
c64scr.print(" * ")
|
txt.print(" * ")
|
||||||
c64flt.print_f(a2)
|
c64flt.print_f(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
%import c64lib
|
|
||||||
%import c64utils
|
|
||||||
%import c64flt
|
%import c64flt
|
||||||
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -35,75 +34,75 @@ main {
|
|||||||
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
ubyte r = a1+a2
|
ubyte r = a1+a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("ubyte ")
|
txt.print("ubyte ")
|
||||||
c64scr.print_ub(a1)
|
txt.print_ub(a1)
|
||||||
c64scr.print(" + ")
|
txt.print(" + ")
|
||||||
c64scr.print_ub(a2)
|
txt.print_ub(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_ub(r)
|
txt.print_ub(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_byte(byte a1, byte a2, byte c) {
|
sub plus_byte(byte a1, byte a2, byte c) {
|
||||||
byte r = a1+a2
|
byte r = a1+a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("byte ")
|
txt.print("byte ")
|
||||||
c64scr.print_b(a1)
|
txt.print_b(a1)
|
||||||
c64scr.print(" + ")
|
txt.print(" + ")
|
||||||
c64scr.print_b(a2)
|
txt.print_b(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_b(r)
|
txt.print_b(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_uword(uword a1, uword a2, uword c) {
|
sub plus_uword(uword a1, uword a2, uword c) {
|
||||||
uword r = a1+a2
|
uword r = a1+a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("uword ")
|
txt.print("uword ")
|
||||||
c64scr.print_uw(a1)
|
txt.print_uw(a1)
|
||||||
c64scr.print(" + ")
|
txt.print(" + ")
|
||||||
c64scr.print_uw(a2)
|
txt.print_uw(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_uw(r)
|
txt.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_word(word a1, word a2, word c) {
|
sub plus_word(word a1, word a2, word c) {
|
||||||
word r = a1+a2
|
word r = a1+a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("word ")
|
txt.print("word ")
|
||||||
c64scr.print_w(a1)
|
txt.print_w(a1)
|
||||||
c64scr.print(" + ")
|
txt.print(" + ")
|
||||||
c64scr.print_w(a2)
|
txt.print_w(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_w(r)
|
txt.print_w(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_float(float a1, float a2, float c) {
|
sub plus_float(float a1, float a2, float c) {
|
||||||
float r = a1+a2
|
float r = a1+a2
|
||||||
if abs(r-c)<0.00001
|
if abs(r-c)<0.00001
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
|
|
||||||
c64scr.print("float ")
|
txt.print("float ")
|
||||||
c64flt.print_f(a1)
|
c64flt.print_f(a1)
|
||||||
c64scr.print(" + ")
|
txt.print(" + ")
|
||||||
c64flt.print_f(a2)
|
c64flt.print_f(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
%import c64utils
|
|
||||||
%import c64flt
|
%import c64flt
|
||||||
%option enable_floats
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
c64scr.plot(0,24)
|
txt.plot(0,24)
|
||||||
|
|
||||||
ubyte Y
|
ubyte Y
|
||||||
ubyte ub=200
|
ubyte ub=200
|
||||||
@ -21,7 +20,7 @@ main {
|
|||||||
word[3] warr = -1000
|
word[3] warr = -1000
|
||||||
float[3] flarr = 999.99
|
float[3] flarr = 999.99
|
||||||
|
|
||||||
c64scr.print("++\n")
|
txt.print("++\n")
|
||||||
ub++
|
ub++
|
||||||
bb++
|
bb++
|
||||||
uw++
|
uw++
|
||||||
@ -52,7 +51,7 @@ main {
|
|||||||
check_uw(uwarr[1], 2001)
|
check_uw(uwarr[1], 2001)
|
||||||
check_w(warr[1], -999)
|
check_w(warr[1], -999)
|
||||||
|
|
||||||
c64scr.print("--\n")
|
txt.print("--\n")
|
||||||
ub--
|
ub--
|
||||||
bb--
|
bb--
|
||||||
uw--
|
uw--
|
||||||
@ -81,58 +80,58 @@ main {
|
|||||||
|
|
||||||
sub check_ub(ubyte value, ubyte expected) {
|
sub check_ub(ubyte value, ubyte expected) {
|
||||||
if value==expected
|
if value==expected
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print(" ubyte ")
|
txt.print(" ubyte ")
|
||||||
c64scr.print_ub(value)
|
txt.print_ub(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_ub(expected)
|
txt.print_ub(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_b(byte value, byte expected) {
|
sub check_b(byte value, byte expected) {
|
||||||
if value==expected
|
if value==expected
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print(" byte ")
|
txt.print(" byte ")
|
||||||
c64scr.print_b(value)
|
txt.print_b(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_b(expected)
|
txt.print_b(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_uw(uword value, uword expected) {
|
sub check_uw(uword value, uword expected) {
|
||||||
if value==expected
|
if value==expected
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print(" uword ")
|
txt.print(" uword ")
|
||||||
c64scr.print_uw(value)
|
txt.print_uw(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_uw(expected)
|
txt.print_uw(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_w(word value, word expected) {
|
sub check_w(word value, word expected) {
|
||||||
if value==expected
|
if value==expected
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print(" word ")
|
txt.print(" word ")
|
||||||
c64scr.print_w(value)
|
txt.print_w(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64scr.print_w(expected)
|
txt.print_w(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_fl(float value, float expected) {
|
sub check_fl(float value, float expected) {
|
||||||
if value==expected
|
if value==expected
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print(" float ")
|
txt.print(" float ")
|
||||||
c64flt.print_f(value)
|
c64flt.print_f(value)
|
||||||
c64.CHROUT(',')
|
c64.CHROUT(',')
|
||||||
c64flt.print_f(expected)
|
c64flt.print_f(expected)
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
%import c64lib
|
%import c64textio
|
||||||
%import c64utils
|
|
||||||
%import c64flt
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -20,30 +18,30 @@ main {
|
|||||||
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
ubyte r = a1%a2
|
ubyte r = a1%a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("ubyte ")
|
txt.print("ubyte ")
|
||||||
c64scr.print_ub(a1)
|
txt.print_ub(a1)
|
||||||
c64scr.print(" % ")
|
txt.print(" % ")
|
||||||
c64scr.print_ub(a2)
|
txt.print_ub(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_ub(r)
|
txt.print_ub(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub remainder_uword(uword a1, uword a2, uword c) {
|
sub remainder_uword(uword a1, uword a2, uword c) {
|
||||||
uword r = a1%a2
|
uword r = a1%a2
|
||||||
if r==c
|
if r==c
|
||||||
c64scr.print(" ok ")
|
txt.print(" ok ")
|
||||||
else
|
else
|
||||||
c64scr.print("err! ")
|
txt.print("err! ")
|
||||||
c64scr.print("uword ")
|
txt.print("uword ")
|
||||||
c64scr.print_uw(a1)
|
txt.print_uw(a1)
|
||||||
c64scr.print(" % ")
|
txt.print(" % ")
|
||||||
c64scr.print_uw(a2)
|
txt.print_uw(a2)
|
||||||
c64scr.print(" = ")
|
txt.print(" = ")
|
||||||
c64scr.print_uw(r)
|
txt.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
%import c64flt
|
%import c64flt
|
||||||
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -18,113 +19,113 @@ main {
|
|||||||
b1 = 10
|
b1 = 10
|
||||||
b2 = 10
|
b2 = 10
|
||||||
if sgn(b2-b1) != 0
|
if sgn(b2-b1) != 0
|
||||||
c64scr.print("sgn1 error1\n")
|
txt.print("sgn1 error1\n")
|
||||||
|
|
||||||
b1 = -100
|
b1 = -100
|
||||||
b2 = -100
|
b2 = -100
|
||||||
if sgn(b2-b1) != 0
|
if sgn(b2-b1) != 0
|
||||||
c64scr.print("sgn1 error2\n")
|
txt.print("sgn1 error2\n")
|
||||||
|
|
||||||
ub1 = 200
|
ub1 = 200
|
||||||
ub2 = 200
|
ub2 = 200
|
||||||
if sgn(ub2-ub1) != 0
|
if sgn(ub2-ub1) != 0
|
||||||
c64scr.print("sgn1 error3\n")
|
txt.print("sgn1 error3\n")
|
||||||
|
|
||||||
w1 = 100
|
w1 = 100
|
||||||
w2 = 100
|
w2 = 100
|
||||||
if sgn(w2-w1) != 0
|
if sgn(w2-w1) != 0
|
||||||
c64scr.print("sgn1 error4\n")
|
txt.print("sgn1 error4\n")
|
||||||
|
|
||||||
w1 = -2000
|
w1 = -2000
|
||||||
w2 = -2000
|
w2 = -2000
|
||||||
if sgn(w2-w1) != 0
|
if sgn(w2-w1) != 0
|
||||||
c64scr.print("sgn1 error5\n")
|
txt.print("sgn1 error5\n")
|
||||||
|
|
||||||
uw1 = 999
|
uw1 = 999
|
||||||
uw2 = 999
|
uw2 = 999
|
||||||
if sgn(uw2-uw1) != 0
|
if sgn(uw2-uw1) != 0
|
||||||
c64scr.print("sgn1 error6\n")
|
txt.print("sgn1 error6\n")
|
||||||
|
|
||||||
f1 = 3.45
|
f1 = 3.45
|
||||||
f2 = 3.45
|
f2 = 3.45
|
||||||
if sgn(f2-f1) != 0
|
if sgn(f2-f1) != 0
|
||||||
c64scr.print("sgn1 error7\n")
|
txt.print("sgn1 error7\n")
|
||||||
|
|
||||||
|
|
||||||
; -1
|
; -1
|
||||||
b1 = 11
|
b1 = 11
|
||||||
b2 = 10
|
b2 = 10
|
||||||
if sgn(b2-b1) != -1
|
if sgn(b2-b1) != -1
|
||||||
c64scr.print("sgn2 error1\n")
|
txt.print("sgn2 error1\n")
|
||||||
|
|
||||||
b1 = -10
|
b1 = -10
|
||||||
b2 = -100
|
b2 = -100
|
||||||
if sgn(b2-b1) != -1
|
if sgn(b2-b1) != -1
|
||||||
c64scr.print("sgn2 error2\n")
|
txt.print("sgn2 error2\n")
|
||||||
|
|
||||||
ub1 = 202
|
ub1 = 202
|
||||||
ub2 = 200
|
ub2 = 200
|
||||||
if sgn(ub2 as byte - ub1 as byte) != -1
|
if sgn(ub2 as byte - ub1 as byte) != -1
|
||||||
c64scr.print("sgn2 error3\n")
|
txt.print("sgn2 error3\n")
|
||||||
|
|
||||||
w1 = 101
|
w1 = 101
|
||||||
w2 = 100
|
w2 = 100
|
||||||
if sgn(w2-w1) != -1
|
if sgn(w2-w1) != -1
|
||||||
c64scr.print("sgn2 error4\n")
|
txt.print("sgn2 error4\n")
|
||||||
|
|
||||||
w1 = -200
|
w1 = -200
|
||||||
w2 = -2000
|
w2 = -2000
|
||||||
if sgn(w2-w1) != -1
|
if sgn(w2-w1) != -1
|
||||||
c64scr.print("sgn2 error5\n")
|
txt.print("sgn2 error5\n")
|
||||||
|
|
||||||
uw1 = 2222
|
uw1 = 2222
|
||||||
uw2 = 999
|
uw2 = 999
|
||||||
if sgn((uw2 as word) - (uw1 as word)) != -1
|
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
|
if sgn(uw2 - uw1) != 1 ; always 0 or 1 if unsigned
|
||||||
c64scr.print("sgn2 error6b\n")
|
txt.print("sgn2 error6b\n")
|
||||||
|
|
||||||
f1 = 3.45
|
f1 = 3.45
|
||||||
f2 = 1.11
|
f2 = 1.11
|
||||||
if sgn(f2-f1) != -1
|
if sgn(f2-f1) != -1
|
||||||
c64scr.print("sgn2 error7\n")
|
txt.print("sgn2 error7\n")
|
||||||
|
|
||||||
; +1
|
; +1
|
||||||
b1 = 11
|
b1 = 11
|
||||||
b2 = 20
|
b2 = 20
|
||||||
if sgn(b2-b1) != 1
|
if sgn(b2-b1) != 1
|
||||||
c64scr.print("sgn3 error1\n")
|
txt.print("sgn3 error1\n")
|
||||||
|
|
||||||
b1 = -10
|
b1 = -10
|
||||||
b2 = -1
|
b2 = -1
|
||||||
if sgn(b2-b1) != 1
|
if sgn(b2-b1) != 1
|
||||||
c64scr.print("sgn3 error2\n")
|
txt.print("sgn3 error2\n")
|
||||||
|
|
||||||
ub1 = 202
|
ub1 = 202
|
||||||
ub2 = 205
|
ub2 = 205
|
||||||
if sgn(ub2-ub1) != 1
|
if sgn(ub2-ub1) != 1
|
||||||
c64scr.print("sgn3 error3\n")
|
txt.print("sgn3 error3\n")
|
||||||
|
|
||||||
w1 = 101
|
w1 = 101
|
||||||
w2 = 200
|
w2 = 200
|
||||||
if sgn(w2-w1) != 1
|
if sgn(w2-w1) != 1
|
||||||
c64scr.print("sgn3 error4\n")
|
txt.print("sgn3 error4\n")
|
||||||
|
|
||||||
w1 = -200
|
w1 = -200
|
||||||
w2 = -20
|
w2 = -20
|
||||||
if sgn(w2-w1) != 1
|
if sgn(w2-w1) != 1
|
||||||
c64scr.print("sgn3 error5\n")
|
txt.print("sgn3 error5\n")
|
||||||
|
|
||||||
uw1 = 2222
|
uw1 = 2222
|
||||||
uw2 = 9999
|
uw2 = 9999
|
||||||
if sgn(uw2-uw1) != 1
|
if sgn(uw2-uw1) != 1
|
||||||
c64scr.print("sgn3 error6\n")
|
txt.print("sgn3 error6\n")
|
||||||
|
|
||||||
f1 = 3.45
|
f1 = 3.45
|
||||||
f2 = 5.11
|
f2 = 5.11
|
||||||
if sgn(f2-f1) != 1
|
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 c64lib
|
||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -15,7 +15,7 @@ main {
|
|||||||
|
|
||||||
c64.SCROLX &= %11110111 ; 38 column mode
|
c64.SCROLX &= %11110111 ; 38 column mode
|
||||||
|
|
||||||
c64utils.set_rasterirq(1) ; enable animation
|
c64.set_rasterirq(1) ; enable animation
|
||||||
|
|
||||||
ubyte target_height = 10
|
ubyte target_height = 10
|
||||||
ubyte active_height = 24
|
ubyte active_height = 24
|
||||||
@ -43,7 +43,7 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
perform_scroll = false
|
perform_scroll = false
|
||||||
c64scr.scroll_left_full(true)
|
txt.scroll_left_full(true)
|
||||||
if c64.RASTER & 1
|
if c64.RASTER & 1
|
||||||
c64.SPXY[1] ++
|
c64.SPXY[1] ++
|
||||||
else
|
else
|
||||||
@ -51,17 +51,17 @@ main {
|
|||||||
|
|
||||||
ubyte yy
|
ubyte yy
|
||||||
for yy in 0 to active_height-1 {
|
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 {
|
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()
|
yy = rnd()
|
||||||
if yy > 100 {
|
if yy > 100 {
|
||||||
; draw a star
|
; draw a star
|
||||||
c64scr.setcc(39, yy % (active_height-1), '.', rnd())
|
txt.setcc(39, yy % (active_height-1), '.', rnd())
|
||||||
}
|
}
|
||||||
|
|
||||||
if yy > 200 {
|
if yy > 200 {
|
||||||
@ -74,12 +74,12 @@ main {
|
|||||||
tree = 65
|
tree = 65
|
||||||
if rnd() > 130
|
if rnd() > 130
|
||||||
treecolor = 13
|
treecolor = 13
|
||||||
c64scr.setcc(39, active_height, tree, treecolor)
|
txt.setcc(39, active_height, tree, treecolor)
|
||||||
}
|
}
|
||||||
|
|
||||||
if yy > 235 {
|
if yy > 235 {
|
||||||
; draw a camel
|
; 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 c64lib
|
||||||
|
%import c64textio
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
c64scr.print("playing the music from boulderdash,\nmade in 1984 by peter liepa.\n\n")
|
txt.print("playing the music from boulderdash,\nmade in 1984 by peter liepa.\n\n")
|
||||||
c64utils.set_rasterirq(60) ; enable raster irq
|
c64.set_rasterirq(60) ; enable raster irq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64lib
|
%import c64textio
|
||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ sub start() {
|
|||||||
c64.SR2 = %00000000
|
c64.SR2 = %00000000
|
||||||
c64.MVOL = 15
|
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()
|
void c64.CHRIN()
|
||||||
c64.CLEARSCR()
|
c64.CLEARSCR()
|
||||||
|
|
||||||
@ -46,11 +46,11 @@ sub delay() {
|
|||||||
|
|
||||||
sub print_notes(ubyte n1, ubyte n2) {
|
sub print_notes(ubyte n1, ubyte n2) {
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
c64scr.plot(n1/2, 24)
|
txt.plot(n1/2, 24)
|
||||||
c64.COLOR=7
|
txt.color(7)
|
||||||
c64.CHROUT('Q')
|
c64.CHROUT('Q')
|
||||||
c64scr.plot(n2/2, 24)
|
txt.plot(n2/2, 24)
|
||||||
c64.COLOR=4
|
txt.color(4)
|
||||||
c64.CHROUT('Q')
|
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
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
@ -12,98 +12,98 @@ main {
|
|||||||
v1 = 100
|
v1 = 100
|
||||||
v2 = 127
|
v2 = 127
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("error in 100==127!\n")
|
txt.print("error in 100==127!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 not == 127\n")
|
txt.print("ok: 100 not == 127\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("ok: 100 != 127\n")
|
txt.print("ok: 100 != 127\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100!=127!\n")
|
txt.print("error in 100!=127!\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("ok: 100 < 127\n")
|
txt.print("ok: 100 < 127\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100<127!\n")
|
txt.print("error in 100<127!\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: 100 <= 127\n")
|
txt.print("ok: 100 <= 127\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100<=127!\n")
|
txt.print("error in 100<=127!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in 100>127!\n")
|
txt.print("error in 100>127!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 is not >127\n")
|
txt.print("ok: 100 is not >127\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("error in 100>=127!\n")
|
txt.print("error in 100>=127!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 is not >=127\n")
|
txt.print("ok: 100 is not >=127\n")
|
||||||
|
|
||||||
|
|
||||||
v1 = 125
|
v1 = 125
|
||||||
v2 = 22
|
v2 = 22
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("error in 125==22!\n")
|
txt.print("error in 125==22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 125 not == 22\n")
|
txt.print("ok: 125 not == 22\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("ok: 125 != 22\n")
|
txt.print("ok: 125 != 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 125!=22!\n")
|
txt.print("error in 125!=22!\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in 125<22!\n")
|
txt.print("error in 125<22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 125 is not < 22\n")
|
txt.print("ok: 125 is not < 22\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("error in 125<=22!\n")
|
txt.print("error in 125<=22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 125 is not <= 22\n")
|
txt.print("ok: 125 is not <= 22\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("ok: 125 > 22\n")
|
txt.print("ok: 125 > 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 125>22!\n")
|
txt.print("error in 125>22!\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: 125 >= 22\n")
|
txt.print("ok: 125 >= 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 125>=22!\n")
|
txt.print("error in 125>=22!\n")
|
||||||
|
|
||||||
v1 = 22
|
v1 = 22
|
||||||
v2 = 22
|
v2 = 22
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("ok: 22 == 22\n")
|
txt.print("ok: 22 == 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 22==22!\n")
|
txt.print("error in 22==22!\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("error in 22!=22!\n")
|
txt.print("error in 22!=22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 22 is not != 22\n")
|
txt.print("ok: 22 is not != 22\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in 22<22!\n")
|
txt.print("error in 22<22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 22 is not < 22\n")
|
txt.print("ok: 22 is not < 22\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: 22 <= 22\n")
|
txt.print("ok: 22 <= 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 22<=22!\n")
|
txt.print("error in 22<=22!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in 22>22!\n")
|
txt.print("error in 22>22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 22 is not > 22\n")
|
txt.print("ok: 22 is not > 22\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: 22 >= 22\n")
|
txt.print("ok: 22 >= 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 22>=22!\n")
|
txt.print("error in 22>=22!\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64utils
|
%import c64textio
|
||||||
%import c64flt
|
%import c64flt
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
@ -12,98 +12,98 @@ main {
|
|||||||
v1 = 1.11
|
v1 = 1.11
|
||||||
v2 = 699.99
|
v2 = 699.99
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("error in 1.11==699.99!\n")
|
txt.print("error in 1.11==699.99!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 1.11 not == 699.99\n")
|
txt.print("ok: 1.11 not == 699.99\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("ok: 1.11 != 699.99\n")
|
txt.print("ok: 1.11 != 699.99\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 1.11!=699.99!\n")
|
txt.print("error in 1.11!=699.99!\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("ok: 1.11 < 699.99\n")
|
txt.print("ok: 1.11 < 699.99\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 1.11<699.99!\n")
|
txt.print("error in 1.11<699.99!\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: 1.11 <= 699.99\n")
|
txt.print("ok: 1.11 <= 699.99\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 1.11<=699.99!\n")
|
txt.print("error in 1.11<=699.99!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in 1.11>699.99!\n")
|
txt.print("error in 1.11>699.99!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 1.11 is not >699.99\n")
|
txt.print("ok: 1.11 is not >699.99\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("error in 1.11>=699.99!\n")
|
txt.print("error in 1.11>=699.99!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 1.11 is not >=699.99\n")
|
txt.print("ok: 1.11 is not >=699.99\n")
|
||||||
|
|
||||||
|
|
||||||
v1 = 555.5
|
v1 = 555.5
|
||||||
v2 = -22.2
|
v2 = -22.2
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("error in 555.5==-22.2!\n")
|
txt.print("error in 555.5==-22.2!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 555.5 not == -22.2\n")
|
txt.print("ok: 555.5 not == -22.2\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("ok: 555.5 != -22.2\n")
|
txt.print("ok: 555.5 != -22.2\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 555.5!=-22.2!\n")
|
txt.print("error in 555.5!=-22.2!\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in 555.5<-22.2!\n")
|
txt.print("error in 555.5<-22.2!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 555.5 is not < -22.2\n")
|
txt.print("ok: 555.5 is not < -22.2\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("error in 555.5<=-22.2!\n")
|
txt.print("error in 555.5<=-22.2!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 555.5 is not <= -22.2\n")
|
txt.print("ok: 555.5 is not <= -22.2\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("ok: 555.5 > -22.2\n")
|
txt.print("ok: 555.5 > -22.2\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 555.5>-22.2!\n")
|
txt.print("error in 555.5>-22.2!\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: 555.5 >= -22.2\n")
|
txt.print("ok: 555.5 >= -22.2\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 555.5>=-22.2!\n")
|
txt.print("error in 555.5>=-22.2!\n")
|
||||||
|
|
||||||
v1 = -22.2
|
v1 = -22.2
|
||||||
v2 = -22.2
|
v2 = -22.2
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("ok: -22.2 == -22.2\n")
|
txt.print("ok: -22.2 == -22.2\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in -22.2==-22.2!\n")
|
txt.print("error in -22.2==-22.2!\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("error in -22.2!=-22.2!\n")
|
txt.print("error in -22.2!=-22.2!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: -22.2 is not != -22.2\n")
|
txt.print("ok: -22.2 is not != -22.2\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in -22.2<-22.2!\n")
|
txt.print("error in -22.2<-22.2!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: -22.2 is not < -22.2\n")
|
txt.print("ok: -22.2 is not < -22.2\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: -22.2 <= -22.2\n")
|
txt.print("ok: -22.2 <= -22.2\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in -22.2<=-22.2!\n")
|
txt.print("error in -22.2<=-22.2!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in -22.2>-22.2!\n")
|
txt.print("error in -22.2>-22.2!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: -22.2 is not > -22.2\n")
|
txt.print("ok: -22.2 is not > -22.2\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: -22.2 >= -22.2\n")
|
txt.print("ok: -22.2 >= -22.2\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in -22.2>=-22.2!\n")
|
txt.print("error in -22.2>=-22.2!\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
@ -12,98 +12,98 @@ main {
|
|||||||
v1 = 100
|
v1 = 100
|
||||||
v2 = 200
|
v2 = 200
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("error in 100==200!\n")
|
txt.print("error in 100==200!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 not == 200\n")
|
txt.print("ok: 100 not == 200\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("ok: 100 != 200\n")
|
txt.print("ok: 100 != 200\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100!=200!\n")
|
txt.print("error in 100!=200!\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("ok: 100 < 200\n")
|
txt.print("ok: 100 < 200\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100<200!\n")
|
txt.print("error in 100<200!\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: 100 <= 200\n")
|
txt.print("ok: 100 <= 200\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100<=200!\n")
|
txt.print("error in 100<=200!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in 100>200!\n")
|
txt.print("error in 100>200!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 is not >200\n")
|
txt.print("ok: 100 is not >200\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("error in 100>=200!\n")
|
txt.print("error in 100>=200!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 is not >=200\n")
|
txt.print("ok: 100 is not >=200\n")
|
||||||
|
|
||||||
|
|
||||||
v1 = 155
|
v1 = 155
|
||||||
v2 = 22
|
v2 = 22
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("error in 155==22!\n")
|
txt.print("error in 155==22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 155 not == 22\n")
|
txt.print("ok: 155 not == 22\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("ok: 155 != 22\n")
|
txt.print("ok: 155 != 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 155!=22!\n")
|
txt.print("error in 155!=22!\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in 155<22!\n")
|
txt.print("error in 155<22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 155 is not < 22\n")
|
txt.print("ok: 155 is not < 22\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("error in 155<=22!\n")
|
txt.print("error in 155<=22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 155 is not <= 22\n")
|
txt.print("ok: 155 is not <= 22\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("ok: 155 > 22\n")
|
txt.print("ok: 155 > 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 155>22!\n")
|
txt.print("error in 155>22!\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: 155 >= 22\n")
|
txt.print("ok: 155 >= 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 155>=22!\n")
|
txt.print("error in 155>=22!\n")
|
||||||
|
|
||||||
v1 = 22
|
v1 = 22
|
||||||
v2 = 22
|
v2 = 22
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("ok: 22 == 22\n")
|
txt.print("ok: 22 == 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 22==22!\n")
|
txt.print("error in 22==22!\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("error in 22!=22!\n")
|
txt.print("error in 22!=22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 22 is not != 22\n")
|
txt.print("ok: 22 is not != 22\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in 22<22!\n")
|
txt.print("error in 22<22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 22 is not < 22\n")
|
txt.print("ok: 22 is not < 22\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: 22 <= 22\n")
|
txt.print("ok: 22 <= 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 22<=22!\n")
|
txt.print("error in 22<=22!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in 22>22!\n")
|
txt.print("error in 22>22!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 22 is not > 22\n")
|
txt.print("ok: 22 is not > 22\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: 22 >= 22\n")
|
txt.print("ok: 22 >= 22\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 22>=22!\n")
|
txt.print("error in 22>=22!\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
@ -12,98 +12,98 @@ main {
|
|||||||
v1 = 100
|
v1 = 100
|
||||||
v2 = 64444
|
v2 = 64444
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("error in 100==64444!\n")
|
txt.print("error in 100==64444!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 not == 64444\n")
|
txt.print("ok: 100 not == 64444\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("ok: 100 != 64444\n")
|
txt.print("ok: 100 != 64444\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100!=64444!\n")
|
txt.print("error in 100!=64444!\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("ok: 100 < 64444\n")
|
txt.print("ok: 100 < 64444\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100<64444!\n")
|
txt.print("error in 100<64444!\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: 100 <= 64444\n")
|
txt.print("ok: 100 <= 64444\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100<=64444!\n")
|
txt.print("error in 100<=64444!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in 100>64444!\n")
|
txt.print("error in 100>64444!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 is not >64444\n")
|
txt.print("ok: 100 is not >64444\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("error in 100>=64444!\n")
|
txt.print("error in 100>=64444!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 is not >=64444\n")
|
txt.print("ok: 100 is not >=64444\n")
|
||||||
|
|
||||||
|
|
||||||
v1 = 5555
|
v1 = 5555
|
||||||
v2 = 322
|
v2 = 322
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("error in 5555==322!\n")
|
txt.print("error in 5555==322!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 5555 not == 322\n")
|
txt.print("ok: 5555 not == 322\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("ok: 5555 != 322\n")
|
txt.print("ok: 5555 != 322\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 5555!=322!\n")
|
txt.print("error in 5555!=322!\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in 5555<322!\n")
|
txt.print("error in 5555<322!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 5555 is not < 322\n")
|
txt.print("ok: 5555 is not < 322\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("error in 5555<=322!\n")
|
txt.print("error in 5555<=322!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 5555 is not <= 322\n")
|
txt.print("ok: 5555 is not <= 322\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("ok: 5555 > 322\n")
|
txt.print("ok: 5555 > 322\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 5555>322!\n")
|
txt.print("error in 5555>322!\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: 5555 >= 322\n")
|
txt.print("ok: 5555 >= 322\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 5555>=322!\n")
|
txt.print("error in 5555>=322!\n")
|
||||||
|
|
||||||
v1 = 322
|
v1 = 322
|
||||||
v2 = 322
|
v2 = 322
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("ok: 322 == 322\n")
|
txt.print("ok: 322 == 322\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 322==322!\n")
|
txt.print("error in 322==322!\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("error in 322!=322!\n")
|
txt.print("error in 322!=322!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 322 is not != 322\n")
|
txt.print("ok: 322 is not != 322\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in 322<322!\n")
|
txt.print("error in 322<322!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 322 is not < 322\n")
|
txt.print("ok: 322 is not < 322\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: 322 <= 322\n")
|
txt.print("ok: 322 <= 322\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 322<=322!\n")
|
txt.print("error in 322<=322!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in 322>322!\n")
|
txt.print("error in 322>322!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 322 is not > 322\n")
|
txt.print("ok: 322 is not > 322\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: 322 >= 322\n")
|
txt.print("ok: 322 >= 322\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 322>=322!\n")
|
txt.print("error in 322>=322!\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
|
||||||
@ -12,130 +12,130 @@ main {
|
|||||||
v1 = 100
|
v1 = 100
|
||||||
v2 = 30333
|
v2 = 30333
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("error in 100==30333!\n")
|
txt.print("error in 100==30333!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 not == 30333\n")
|
txt.print("ok: 100 not == 30333\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("ok: 100 != 30333\n")
|
txt.print("ok: 100 != 30333\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100!=30333!\n")
|
txt.print("error in 100!=30333!\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("ok: 100 < 30333\n")
|
txt.print("ok: 100 < 30333\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100<30333!\n")
|
txt.print("error in 100<30333!\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: 100 <= 30333\n")
|
txt.print("ok: 100 <= 30333\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 100<=30333!\n")
|
txt.print("error in 100<=30333!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in 100>30333!\n")
|
txt.print("error in 100>30333!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 is not >30333\n")
|
txt.print("ok: 100 is not >30333\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("error in 100>=30333!\n")
|
txt.print("error in 100>=30333!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 100 is not >=30333\n")
|
txt.print("ok: 100 is not >=30333\n")
|
||||||
|
|
||||||
|
|
||||||
v1 = 125
|
v1 = 125
|
||||||
v2 = -222
|
v2 = -222
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("error in 125==-222!\n")
|
txt.print("error in 125==-222!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 125 not == -222\n")
|
txt.print("ok: 125 not == -222\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("ok: 125 != -222\n")
|
txt.print("ok: 125 != -222\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 125!=-222!\n")
|
txt.print("error in 125!=-222!\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in 125<-222!\n")
|
txt.print("error in 125<-222!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 125 is not < -222\n")
|
txt.print("ok: 125 is not < -222\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("error in 125<=-222!\n")
|
txt.print("error in 125<=-222!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 125 is not <= -222\n")
|
txt.print("ok: 125 is not <= -222\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("ok: 125 > -222\n")
|
txt.print("ok: 125 > -222\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 125>-222!\n")
|
txt.print("error in 125>-222!\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: 125 >= -222\n")
|
txt.print("ok: 125 >= -222\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 125>=-222!\n")
|
txt.print("error in 125>=-222!\n")
|
||||||
|
|
||||||
v1 = -222
|
v1 = -222
|
||||||
v2 = -222
|
v2 = -222
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("ok: -222 == -222\n")
|
txt.print("ok: -222 == -222\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in -222==-222!\n")
|
txt.print("error in -222==-222!\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("error in -222!=-222!\n")
|
txt.print("error in -222!=-222!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: -222 is not != -222\n")
|
txt.print("ok: -222 is not != -222\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in -222<-222!\n")
|
txt.print("error in -222<-222!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: -222 is not < -222\n")
|
txt.print("ok: -222 is not < -222\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: -222 <= -222\n")
|
txt.print("ok: -222 <= -222\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in -222<=-222!\n")
|
txt.print("error in -222<=-222!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in -222>-222!\n")
|
txt.print("error in -222>-222!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: -222 is not > -222\n")
|
txt.print("ok: -222 is not > -222\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: -222 >= -222\n")
|
txt.print("ok: -222 >= -222\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in -222>=-222!\n")
|
txt.print("error in -222>=-222!\n")
|
||||||
|
|
||||||
v1 = 1000
|
v1 = 1000
|
||||||
v2 = 1000
|
v2 = 1000
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print("ok: 1000 == 1000\n")
|
txt.print("ok: 1000 == 1000\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 1000==1000!\n")
|
txt.print("error in 1000==1000!\n")
|
||||||
|
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print("error in 1000!=1000!\n")
|
txt.print("error in 1000!=1000!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 1000 is not != 1000\n")
|
txt.print("ok: 1000 is not != 1000\n")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print("error in 1000<1000!\n")
|
txt.print("error in 1000<1000!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 1000 is not < 1000\n")
|
txt.print("ok: 1000 is not < 1000\n")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print("ok: 1000 <= 1000\n")
|
txt.print("ok: 1000 <= 1000\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 1000<=1000!\n")
|
txt.print("error in 1000<=1000!\n")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print("error in 1000>1000!\n")
|
txt.print("error in 1000>1000!\n")
|
||||||
else
|
else
|
||||||
c64scr.print("ok: 1000 is not > 1000\n")
|
txt.print("ok: 1000 is not > 1000\n")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print("ok: 1000 >= 1000\n")
|
txt.print("ok: 1000 >= 1000\n")
|
||||||
else
|
else
|
||||||
c64scr.print("error in 1000>=1000!\n")
|
txt.print("error in 1000>=1000!\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -9,7 +9,7 @@ main {
|
|||||||
byte v2
|
byte v2
|
||||||
ubyte cr
|
ubyte cr
|
||||||
|
|
||||||
c64scr.print("signed byte ")
|
txt.print("signed byte ")
|
||||||
|
|
||||||
cr=v1==v2
|
cr=v1==v2
|
||||||
cr=v1==v2
|
cr=v1==v2
|
||||||
@ -39,52 +39,52 @@ main {
|
|||||||
; comparisons:
|
; comparisons:
|
||||||
v1=-20
|
v1=-20
|
||||||
v2=125
|
v2=125
|
||||||
c64scr.print("v1=-20, v2=125\n")
|
txt.print("v1=-20, v2=125\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=80
|
v1=80
|
||||||
v2=80
|
v2=80
|
||||||
c64scr.print("v1 = v2 = 80\n")
|
txt.print("v1 = v2 = 80\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=20
|
v1=20
|
||||||
v2=-111
|
v2=-111
|
||||||
c64scr.print("v1=20, v2=-111\n")
|
txt.print("v1=20, v2=-111\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
c64scr.print(" == != < > <= >=\n")
|
txt.print(" == != < > <= >=\n")
|
||||||
|
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64utils
|
%import c64textio
|
||||||
%import c64flt
|
%import c64flt
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ main {
|
|||||||
float v2
|
float v2
|
||||||
ubyte cr
|
ubyte cr
|
||||||
|
|
||||||
c64scr.print("floating point ")
|
txt.print("floating point ")
|
||||||
|
|
||||||
cr=v1==v2
|
cr=v1==v2
|
||||||
cr=v1==v2
|
cr=v1==v2
|
||||||
@ -40,67 +40,67 @@ main {
|
|||||||
; comparisons:
|
; comparisons:
|
||||||
v1=20
|
v1=20
|
||||||
v2=666.66
|
v2=666.66
|
||||||
c64scr.print("v1=20, v2=666.66\n")
|
txt.print("v1=20, v2=666.66\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=-20
|
v1=-20
|
||||||
v2=666.66
|
v2=666.66
|
||||||
c64scr.print("v1=-20, v2=666.66\n")
|
txt.print("v1=-20, v2=666.66\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=666.66
|
v1=666.66
|
||||||
v2=555.55
|
v2=555.55
|
||||||
c64scr.print("v1=666.66, v2=555.55\n")
|
txt.print("v1=666.66, v2=555.55\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=3.1415
|
v1=3.1415
|
||||||
v2=-3.1415
|
v2=-3.1415
|
||||||
c64scr.print("v1 = 3.1415, v2 = -3.1415\n")
|
txt.print("v1 = 3.1415, v2 = -3.1415\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=3.1415
|
v1=3.1415
|
||||||
v2=3.1415
|
v2=3.1415
|
||||||
c64scr.print("v1 = v2 = 3.1415\n")
|
txt.print("v1 = v2 = 3.1415\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=0
|
v1=0
|
||||||
v2=0
|
v2=0
|
||||||
c64scr.print("v1 = v2 = 0\n")
|
txt.print("v1 = v2 = 0\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
c64scr.print(" == != < > <= >=\n")
|
txt.print(" == != < > <= >=\n")
|
||||||
|
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -9,7 +9,7 @@ main {
|
|||||||
ubyte v2
|
ubyte v2
|
||||||
ubyte cr
|
ubyte cr
|
||||||
|
|
||||||
c64scr.print("unsigned byte ")
|
txt.print("unsigned byte ")
|
||||||
|
|
||||||
cr=v1==v2
|
cr=v1==v2
|
||||||
cr=v1==v2
|
cr=v1==v2
|
||||||
@ -39,52 +39,52 @@ main {
|
|||||||
; comparisons:
|
; comparisons:
|
||||||
v1=20
|
v1=20
|
||||||
v2=199
|
v2=199
|
||||||
c64scr.print("v1=20, v2=199\n")
|
txt.print("v1=20, v2=199\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=80
|
v1=80
|
||||||
v2=80
|
v2=80
|
||||||
c64scr.print("v1 = v2 = 80\n")
|
txt.print("v1 = v2 = 80\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=220
|
v1=220
|
||||||
v2=10
|
v2=10
|
||||||
c64scr.print("v1=220, v2=10\n")
|
txt.print("v1=220, v2=10\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
c64scr.print(" == != < > <= >=\n")
|
txt.print(" == != < > <= >=\n")
|
||||||
|
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -9,7 +9,7 @@ main {
|
|||||||
uword v2
|
uword v2
|
||||||
ubyte cr
|
ubyte cr
|
||||||
|
|
||||||
c64scr.print("unsigned word ")
|
txt.print("unsigned word ")
|
||||||
|
|
||||||
cr=v1==v2
|
cr=v1==v2
|
||||||
cr=v1==v2
|
cr=v1==v2
|
||||||
@ -39,82 +39,82 @@ main {
|
|||||||
; comparisons:
|
; comparisons:
|
||||||
v1=20
|
v1=20
|
||||||
v2=$00aa
|
v2=$00aa
|
||||||
c64scr.print("v1=20, v2=$00aa\n")
|
txt.print("v1=20, v2=$00aa\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=20
|
v1=20
|
||||||
v2=$ea00
|
v2=$ea00
|
||||||
c64scr.print("v1=20, v2=$ea00\n")
|
txt.print("v1=20, v2=$ea00\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$c400
|
v1=$c400
|
||||||
v2=$22
|
v2=$22
|
||||||
c64scr.print("v1=$c400, v2=$22\n")
|
txt.print("v1=$c400, v2=$22\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$c400
|
v1=$c400
|
||||||
v2=$2a00
|
v2=$2a00
|
||||||
c64scr.print("v1=$c400, v2=$2a00\n")
|
txt.print("v1=$c400, v2=$2a00\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$c433
|
v1=$c433
|
||||||
v2=$2a00
|
v2=$2a00
|
||||||
c64scr.print("v1=$c433, v2=$2a00\n")
|
txt.print("v1=$c433, v2=$2a00\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$c433
|
v1=$c433
|
||||||
v2=$2aff
|
v2=$2aff
|
||||||
c64scr.print("v1=$c433, v2=$2aff\n")
|
txt.print("v1=$c433, v2=$2aff\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$aabb
|
v1=$aabb
|
||||||
v2=$aabb
|
v2=$aabb
|
||||||
c64scr.print("v1 = v2 = aabb\n")
|
txt.print("v1 = v2 = aabb\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$aa00
|
v1=$aa00
|
||||||
v2=$aa00
|
v2=$aa00
|
||||||
c64scr.print("v1 = v2 = aa00\n")
|
txt.print("v1 = v2 = aa00\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$aa
|
v1=$aa
|
||||||
v2=$aa
|
v2=$aa
|
||||||
c64scr.print("v1 = v2 = aa\n")
|
txt.print("v1 = v2 = aa\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
c64scr.print(" == != < > <= >=\n")
|
txt.print(" == != < > <= >=\n")
|
||||||
|
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
%import c64utils
|
%import c64textio
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -9,7 +9,7 @@ main {
|
|||||||
word v2
|
word v2
|
||||||
ubyte cr
|
ubyte cr
|
||||||
|
|
||||||
c64scr.print("signed word ")
|
txt.print("signed word ")
|
||||||
|
|
||||||
cr=v1==v2
|
cr=v1==v2
|
||||||
cr=v1==v2
|
cr=v1==v2
|
||||||
@ -39,118 +39,118 @@ main {
|
|||||||
; comparisons:
|
; comparisons:
|
||||||
v1=20
|
v1=20
|
||||||
v2=$00aa
|
v2=$00aa
|
||||||
c64scr.print("v1=20, v2=$00aa\n")
|
txt.print("v1=20, v2=$00aa\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=20
|
v1=20
|
||||||
v2=$7a00
|
v2=$7a00
|
||||||
c64scr.print("v1=20, v2=$7a00\n")
|
txt.print("v1=20, v2=$7a00\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$7400
|
v1=$7400
|
||||||
v2=$22
|
v2=$22
|
||||||
c64scr.print("v1=$7400, v2=$22\n")
|
txt.print("v1=$7400, v2=$22\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$7400
|
v1=$7400
|
||||||
v2=$2a00
|
v2=$2a00
|
||||||
c64scr.print("v1=$7400, v2=$2a00\n")
|
txt.print("v1=$7400, v2=$2a00\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$7433
|
v1=$7433
|
||||||
v2=$2a00
|
v2=$2a00
|
||||||
c64scr.print("v1=$7433, v2=$2a00\n")
|
txt.print("v1=$7433, v2=$2a00\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$7433
|
v1=$7433
|
||||||
v2=$2aff
|
v2=$2aff
|
||||||
c64scr.print("v1=$7433, v2=$2aff\n")
|
txt.print("v1=$7433, v2=$2aff\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
; with negative numbers:
|
; with negative numbers:
|
||||||
v1=-512
|
v1=-512
|
||||||
v2=$00aa
|
v2=$00aa
|
||||||
c64scr.print("v1=-512, v2=$00aa\n")
|
txt.print("v1=-512, v2=$00aa\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=-512
|
v1=-512
|
||||||
v2=$7a00
|
v2=$7a00
|
||||||
c64scr.print("v1=-512, v2=$7a00\n")
|
txt.print("v1=-512, v2=$7a00\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$7400
|
v1=$7400
|
||||||
v2=-512
|
v2=-512
|
||||||
c64scr.print("v1=$7400, v2=-512\n")
|
txt.print("v1=$7400, v2=-512\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=-20000
|
v1=-20000
|
||||||
v2=-1000
|
v2=-1000
|
||||||
c64scr.print("v1=-20000, v2=-1000\n")
|
txt.print("v1=-20000, v2=-1000\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=-1000
|
v1=-1000
|
||||||
v2=-20000
|
v2=-20000
|
||||||
c64scr.print("v1=-1000, v2=-20000\n")
|
txt.print("v1=-1000, v2=-20000\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=-1
|
v1=-1
|
||||||
v2=32767
|
v2=32767
|
||||||
c64scr.print("v1=-1, v2=32767\n")
|
txt.print("v1=-1, v2=32767\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=32767
|
v1=32767
|
||||||
v2=-1
|
v2=-1
|
||||||
c64scr.print("v1=32767, v2=-1\n")
|
txt.print("v1=32767, v2=-1\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$7abb
|
v1=$7abb
|
||||||
v2=$7abb
|
v2=$7abb
|
||||||
c64scr.print("v1 = v2 = 7abb\n")
|
txt.print("v1 = v2 = 7abb\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$7a00
|
v1=$7a00
|
||||||
v2=$7a00
|
v2=$7a00
|
||||||
c64scr.print("v1 = v2 = 7a00\n")
|
txt.print("v1 = v2 = 7a00\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
v1=$aa
|
v1=$aa
|
||||||
v2=$aa
|
v2=$aa
|
||||||
c64scr.print("v1 = v2 = aa\n")
|
txt.print("v1 = v2 = aa\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
c64scr.print(" == != < > <= >=\n")
|
txt.print(" == != < > <= >=\n")
|
||||||
|
|
||||||
if v1==v2
|
if v1==v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
if v1!=v2
|
if v1!=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1<v2
|
if v1<v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1>v2
|
if v1>v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1<=v2
|
if v1<=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
|
|
||||||
if v1>=v2
|
if v1>=v2
|
||||||
c64scr.print(" Q ")
|
txt.print(" Q ")
|
||||||
else
|
else
|
||||||
c64scr.print(" . ")
|
txt.print(" . ")
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
examples/compiled/cube3d-gfx.prg
Normal file
BIN
examples/compiled/cube3d-gfx.prg
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user