Compare commits

...
98 Commits
Author SHA1 Message Date
Irmen de Jong 52e8a44517 version 5.4 2020-12-15 22:59:02 +01:00
Irmen de Jong 59f33658ad removed the rom path argument for launching the x16emu which made it fail on a non-Linux system 2020-12-15 22:51:10 +01:00
Irmen de Jong e0315bffdc decided not to change mkword() again, added note to docs about argument order 2020-12-15 22:25:06 +01:00
Irmen de Jong cd28d0c0e0 tweak 2020-12-14 21:57:16 +01:00
Irmen de Jong 0baa2c8b23 fix oversight in binexpr operand swap that could result in suboptimal code 2020-12-14 21:37:40 +01:00
Irmen de Jong 4977d1fbd5 bit shift expressions are "expanded" to the target value's datatype, now also for subroutine arguments.
implemented word bit shifts by variable number of bits.
2020-12-14 20:44:48 +01:00
Irmen de Jong 3b7a92f1b4 adding strcopy() 2020-12-14 17:26:17 +01:00
Irmen de Jong f6920172dd image viewer tweaks 2020-12-14 15:36:15 +01:00
Irmen de Jong 93bfc8f5f4 rename 2020-12-14 14:30:55 +01:00
Irmen de Jong 39b7655264 imageviewer is now a single program 2020-12-14 14:30:18 +01:00
Irmen de Jong 8b75ceb412 diskio.list_files now has a bigger buffer to store more filenames (around 30-40 max) 2020-12-14 14:29:42 +01:00
Irmen de Jong c39fc4010d textio.clear_screen() now uses kernal routine to clear the text screen, this also resets the cursor to top left. 2020-12-14 14:28:53 +01:00
Irmen de Jong 8df778a515 fixed crash when importing modules from the same directory as the main program 2020-12-14 13:14:12 +01:00
Irmen de Jong 5134ea76bf added bmp viewer 2020-12-14 02:12:26 +01:00
Irmen de Jong 3ba37df29d added iff viewer 2020-12-13 19:42:30 +01:00
Irmen de Jong e221d674d9 pcxviewer done 2020-12-13 01:32:03 +01:00
Irmen de Jong 251f947293 fixed parameter signature for FB_set_8_pixels_opaque() (docs are wrong) 2020-12-12 03:32:01 +01:00
Irmen de Jong 41e1e1cbb0 adding pcxviewer 2020-12-12 02:40:54 +01:00
Irmen de Jong da1bc351d2 koalaviewer auto disk detect 2020-12-11 23:32:47 +01:00
Irmen de Jong 43c0afdea0 fixed strlen() to work on arguments other than just a variable 2020-12-11 23:32:29 +01:00
Irmen de Jong add5bfa2ec koalaviewer scans directory for *.koa 2020-12-11 23:00:58 +01:00
Irmen de Jong 34babfb5de added diskio.list_files(). ci-viewer now loads all *.ci files it finds. 2020-12-11 22:36:14 +01:00
Irmen de Jong 4f6c45c86c incremental file loading 2020-12-11 21:05:03 +01:00
Irmen de Jong e6220a464c using progend() to maximize amount of mem available to load image 2020-12-10 23:52:30 +01:00
Irmen de Jong 8dcd49934a added progend() builtin function 2020-12-10 23:33:45 +01:00
Irmen de Jong bedc3bdb56 allow bit shifting to be as large as the target variable's datatype 2020-12-10 22:49:27 +01:00
Irmen de Jong 83ceb0fde9 optimize various simple cases for '**' (pow) like 2**x => bitshift 2020-12-10 22:37:12 +01:00
Irmen de Jong 1d299c56e0 fix float '**' (pow) on cx16 2020-12-10 22:19:07 +01:00
Irmen de Jong 0d735c2ccc workaround for FB_set_pixels bug 2020-12-10 21:51:32 +01:00
Irmen de Jong 4094f89d4a not a bug 2020-12-10 03:22:43 +01:00
Irmen de Jong cf1e8b194a fix compiler crash for expressions of the form x = x and y (the logical booleans, not the bitwise) 2020-12-10 03:12:32 +01:00
Irmen de Jong 74e5644f55 working on CI viewer 2020-12-10 03:00:37 +01:00
Irmen de Jong b5dc5fc615 added iterative file loading to diskio 2020-12-10 00:58:59 +01:00
Irmen de Jong 7a7270d769 adding CI (CommanderX16 Image) file viewer 2020-12-10 00:03:47 +01:00
Irmen de Jong 7549ddcd2b added TODOs for missing assignments 2020-12-10 00:03:20 +01:00
Irmen de Jong 08f0303178 diskio status() now returns the status string instead of printing it 2020-12-10 00:02:21 +01:00
Irmen de Jong 0d7a291b81 regenerated example disk , version 5.3 2020-12-08 23:15:31 +01:00
Irmen de Jong 2265ae9600 optimized setting word values into array if index is fixed number 2020-12-08 22:54:20 +01:00
Irmen de Jong cba502e87a fixed crash when trying to assign a string literal to an array element in a string-array 2020-12-08 22:27:42 +01:00
Irmen de Jong ac94236614 fixed compiler crash when declaring a str(pointer) array without initializer 2020-12-08 22:19:11 +01:00
Irmen de Jong ddf1be2a13 status condition couldn't properly be tested because restoring the X register clobbers the status flag 2020-12-08 22:15:07 +01:00
Irmen de Jong b7694686c2 optimized code for branches containing just a goto or break statement 2020-12-08 22:00:52 +01:00
Irmen de Jong 63332c0530 fix wrong branch instructions for some if_xxx 2020-12-08 21:29:40 +01:00
Irmen de Jong 8a504f8eee fixed compiler crash: when passing the name of a subroutine instead of an array or string to an UWORD parameter
now allows taking the address of a subroutine &routine
2020-12-08 21:17:31 +01:00
Irmen de Jong 106fc5daa4 tweak 2020-12-08 03:39:45 +01:00
Irmen de Jong 7accb73993 iterative file listing instead 2020-12-08 03:34:45 +01:00
Irmen de Jong e9aa6a0956 TODOs 2020-12-08 02:20:24 +01:00
Irmen de Jong df20467e03 completed diskio file lister 2020-12-08 02:16:41 +01:00
Irmen de Jong ecbd9d739e completed diskio file lister 2020-12-08 01:34:08 +01:00
Irmen de Jong 8af17c295a fixed diskio directory block sizes 2020-12-08 01:02:38 +01:00
Irmen de Jong 329b28cad1 making diskio.listfiles 2020-12-07 23:49:34 +01:00
Irmen de Jong 452c29574d added optimized mul 320 routine 2020-12-07 22:55:16 +01:00
Irmen de Jong 5bedc1b333 remove test file 2020-12-06 18:40:47 +01:00
Irmen de Jong 0bf6d2f72c tweak 2020-12-06 18:38:27 +01:00
Irmen de Jong c09b8af491 optimized koalaviewer to plot 8 pixels at once in the loop 2020-12-06 18:25:01 +01:00
Irmen de Jong 260bcd3a55 added syntax error for non-constant array size declaration 2020-12-06 17:02:56 +01:00
Irmen de Jong 6b5211ad12 tweak word shift unroll 2020-12-06 08:36:19 +01:00
Irmen de Jong a92ec14989 use 'stz' more often on 65c02 cpu (cx16) 2020-12-06 08:30:13 +01:00
Irmen de Jong b3348eb22b formatting 2020-12-06 07:52:58 +01:00
Irmen de Jong bec5a261e5 optimizing koalaviewer 2020-12-06 07:47:54 +01:00
Irmen de Jong 4b53641e1d optimized text screen clear/fill and scrolling on c64 2020-12-06 01:16:31 +01:00
Irmen de Jong 00071d53d5 optimized disc (filled circle) drawing on c64, fixed off by 1 disc width in cx16 version 2020-12-06 00:33:32 +01:00
Irmen de Jong 6902834568 remove dummy argument for txt.scroll_XXXX() functions on cx16 2020-12-06 00:19:47 +01:00
Irmen de Jong fa2d87f3dd optimized disc (filled circle) drawing on cx16 2020-12-06 00:01:19 +01:00
Irmen de Jong 44019d1a61 strings and arrays are no longer directly assignable to an UWORD, you need an explicit & (address-of) now 2020-12-03 18:39:32 +01:00
Irmen de Jong 6f74fb49bd added c64colors module. added vpeek/vpoke to cx16 syslib. koalaviewer example now uses better c64 color palette. 2020-12-03 18:14:49 +01:00
Irmen de Jong a303b39cf0 added C64 'koala' image viewer example for Cx16 2020-12-03 16:02:51 +01:00
Irmen de Jong 3e63a29c59 diskio now properly closes files after a load or save 2020-12-03 16:01:58 +01:00
Irmen de Jong 261c0fc9b6 started adding syntax highlighting files 2020-12-02 20:48:50 +01:00
Irmen de Jong 895b30f7e5 version 5.2 2020-12-01 22:49:08 +01:00
Irmen de Jong b985604e22 slight tweak to word bitshift for large shift values 2020-12-01 22:48:02 +01:00
Irmen de Jong f7953e4ef3 fix float comparison error that creeped in with no longer using the stack for that 2020-12-01 22:19:03 +01:00
Irmen de Jong 63483d1f0e warnings, errors and todos 2020-12-01 03:24:06 +01:00
Irmen de Jong 8b981f03bf optimized reg_lesseq_w (word <= word) to avoid using extra zp word, by swapping operands 2020-12-01 02:09:48 +01:00
Irmen de Jong d0d0910bf2 corrected greatereq_w (word >= word) 2020-12-01 01:57:12 +01:00
Irmen de Jong 57ac820767 readme 2020-11-30 22:42:51 +01:00
Irmen de Jong b8bda867b6 optimized reg_lesseq_w (word <= word) 2020-11-30 02:26:00 +01:00
Irmen de Jong 05d3a2450c optimized reg_less_w (word < word) 2020-11-30 01:53:44 +01:00
Irmen de Jong d40788adfa optimized in-place array element modification to use simpler assignment asm code 2020-11-28 00:44:38 +01:00
Irmen de Jong 83fbf86b1c no longer generate double assignment to the indexer var for in-place modifying array variable 2020-11-27 23:46:01 +01:00
Irmen de Jong e876008427 tiny tweak of typecasting str to uword 2020-11-26 19:21:07 +01:00
Irmen de Jong 2b43353eb4 readme 2020-11-26 02:04:01 +01:00
Irmen de Jong a74403c347 float typecasts optimization 2020-11-26 01:52:48 +01:00
Irmen de Jong 2f4c6c8697 float typecasts optimization 2020-11-26 01:39:27 +01:00
Irmen de Jong 238d8197f5 byte/word typecasts optimized even further to just use cpu registers (and fixed sign extending AY) 2020-11-26 01:33:45 +01:00
Irmen de Jong 53a600d87b fix typecasting of signed byte to signed word in a variable 2020-11-25 22:33:41 +01:00
Irmen de Jong 2a0ffaf45d started to optimize typecasts to use translateExpression() less 2020-11-25 00:17:42 +01:00
Irmen de Jong 936b046ed9 optimize word [operator] byte, without translateExpression() 2020-11-24 23:41:10 +01:00
Irmen de Jong 378dcfe351 fix computation error of word - byte 2020-11-24 22:23:16 +01:00
Irmen de Jong 0a330b9288 warmings 2020-11-24 22:21:54 +01:00
Irmen de Jong a88b40d6c1 fix stack corruption with bitshifts 2020-11-24 21:58:14 +01:00
Irmen de Jong 09f25ffbd9 optimized in-place memory var modification, not using translateExpression() 2020-11-24 21:41:44 +01:00
Irmen de Jong ab1232d742 optimized in-place float var modification, not using translateExpression() 2020-11-24 01:09:24 +01:00
Irmen de Jong a7f56fe0fc remaining float comparisons with expression now without translateExpression() 2020-11-24 00:35:30 +01:00
Irmen de Jong 58a9452c36 fixed the YSCROLL graphics mode on the C64 (mistake in 5.1) 2020-11-23 23:05:51 +01:00
Irmen de Jong 6d8c4f403f updated Kotlin version to 1.4.20, updated targeted JDK version to 11 (LTS) 2020-11-23 22:28:24 +01:00
Irmen de Jong 88b80fed90 returning float values now via fac1 instead of stack 2020-11-23 22:14:45 +01:00
Irmen de Jong acdbd0c391 todos for next version 2020-11-22 19:18:57 +01:00
92 changed files with 3919 additions and 886 deletions

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
+14 -14
View File
@@ -20,36 +20,36 @@ Full documentation (syntax reference, how to use the language and the compiler,
https://prog8.readthedocs.io/
What use Prog8 provide?
-----------------------
What does Prog8 provide?
------------------------
- reduction of source code length over raw assembly
- big reduction of source code length over raw assembly
- modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings)
- automatic variable allocations, automatic string and array variables and string sharing
- subroutines with an input- and output parameter signature
- constant folding in expressions
- no stack frame allocations because parameters and local variables are automatically allocated statically
- constant folding in expressions and other high-level program optimizations
- conditional branches
- 'when' statement to provide a concise jump table alternative to if/elseif chains
- structs to group together sets of variables and manipulate them at once
- floating point operations (requires the C64 Basic ROM routines for this)
- abstracting away low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- various code optimizations (code structure, logical and numerical expressions, unused code removal...)
- inline assembly allows you to have full control when every cycle or byte matters
- 'when' statement to provide a concise jump table alternative to if/elseif chains
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
- structs to group together sets of variables and manipulate them at once
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- fast execution speed due to compilation to native assembly code
- inline assembly allows you to have full control when every cycle or byte matters
*Rapid edit-compile-run-debug cycle:*
- use a modern PC to do the work on
- very quick compilation times
- use a modern PC to do the work on, use nice editors and enjoy quick compilation times
- can automatically run the program in the Vice emulator after succesful compilation
- 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
*Two supported compiler targets* (contributions to improve these or to add support for other machines are welcome!):
- "c64": Commodore-64 (6510 CPU = almost a 6502) premium support.
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) experimental support.
- "c64": Commodore-64 (6510 CPU = almost a 6502), the main target.
- "cx16": [CommanderX16](https://www.commanderx16.com) (65c02 CPU) .
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
@@ -61,7 +61,7 @@ Additional required tools
A recent .exe version of this tool for Windows can be obtained from my [clone](https://github.com/irmen/64tass/releases) of this project.
For other platforms it is very easy to compile it yourself (make ; make install).
A **Java runtime (jre or jdk), version 8 or newer** is required to run a prepackaged version of the compiler.
A **Java runtime (jre or jdk), version 11 or newer** is required to run a prepackaged version of the compiler.
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).
+6 -6
View File
@@ -1,11 +1,11 @@
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20"
}
}
plugins {
// id "org.jetbrains.kotlin.jvm" version "1.4.10"
// id "org.jetbrains.kotlin.jvm" version "1.4.20"
id 'application'
id 'org.jetbrains.dokka' version "0.9.18"
id 'com.github.johnrengelman.shadow' version '5.2.0'
@@ -15,8 +15,8 @@ plugins {
apply plugin: "kotlin"
apply plugin: "java"
targetCompatibility = 1.8
sourceCompatibility = 1.8
targetCompatibility = 11
sourceCompatibility = 11
repositories {
mavenLocal()
@@ -45,7 +45,7 @@ dependencies {
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
// verbose = true
// freeCompilerArgs += "-XXLanguage:+NewInference"
}
@@ -53,7 +53,7 @@ compileKotlin {
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
}
}
+77 -4
View File
@@ -89,17 +89,27 @@ cast_from_b .proc
cast_as_uw_into_ya .proc ; also used for float 2 ub
; -- cast float at A/Y to uword into Y/A
stx P8ZP_SCRATCH_REG
jsr MOVFM
jmp cast_FAC1_as_uw_into_ya
.pend
cast_as_w_into_ay .proc ; also used for float 2 b
; -- cast float at A/Y to word into A/Y
jsr MOVFM
jmp cast_FAC1_as_w_into_ay
.pend
cast_FAC1_as_uw_into_ya .proc ; also used for float 2 ub
; -- cast fac1 to uword into Y/A
stx P8ZP_SCRATCH_REG
jsr GETADR ; into Y/A
ldx P8ZP_SCRATCH_REG
rts
.pend
cast_as_w_into_ay .proc ; also used for float 2 b
; -- cast float at A/Y to word into A/Y
cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
; -- cast fac1 to word into A/Y
stx P8ZP_SCRATCH_REG
jsr MOVFM
jsr AYINT
ldy $64
lda $65
@@ -380,6 +390,69 @@ neg_f .proc
rts
.pend
var_fac1_less_f .proc
; -- is the float in FAC1 < the variable AY?
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #255
beq +
lda #0
rts
+ lda #1
rts
.pend
var_fac1_lesseq_f .proc
; -- is the float in FAC1 <= the variable AY?
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #0
beq +
cmp #255
beq +
lda #0
rts
+ lda #1
rts
.pend
var_fac1_greater_f .proc
; -- is the float in FAC1 > the variable AY?
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #1
beq +
lda #0
+ rts
.pend
var_fac1_greatereq_f .proc
; -- is the float in FAC1 >= the variable AY?
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
cmp #0
beq +
cmp #1
beq +
lda #0
rts
+ lda #1
rts
.pend
var_fac1_notequal_f .proc
; -- are the floats numbers in FAC1 and the variable AY *not* identical?
stx P8ZP_SCRATCH_REG
jsr FCOMP
ldx P8ZP_SCRATCH_REG
and #1
rts
.pend
vars_equal_f .proc
; -- are the mflpt5 numbers in P8ZP_SCRATCH_W1 and AY identical?
sta P8ZP_SCRATCH_W2
+8 -12
View File
@@ -12,7 +12,7 @@ graphics {
sub enable_bitmap_mode() {
; enable bitmap screen, erase it and set colors to black/white.
c64.SCROLY = %00111000
c64.SCROLY = %00111011
c64.SCROLX = %00001000
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
clear_screen(1, 0)
@@ -20,7 +20,7 @@ graphics {
sub disable_bitmap_mode() {
; enables text mode, erase the text screen, color white
c64.SCROLY = %00011000
c64.SCROLY = %00011011
c64.SCROLX = %00001000
c64.VMCSB = (c64.VMCSB & %11110000) | %00000100 ; $1000-$2fff
txt.fill_screen(' ', 1)
@@ -158,21 +158,17 @@ graphics {
ubyte ycenter_plus_xx = ycenter + xx
ubyte ycenter_min_xx = ycenter - xx
for internal_plotx in xcenter to xcenter+xx {
internal_plotx = xcenter-xx
repeat xx*2+1 {
internal_plot(ycenter_plus_yy)
internal_plot(ycenter_min_yy)
internal_plotx++
}
for internal_plotx in xcenter-xx to xcenter-1 {
internal_plot(ycenter_plus_yy)
internal_plot(ycenter_min_yy)
}
for internal_plotx in xcenter to xcenter+yy {
internal_plot(ycenter_plus_xx)
internal_plot(ycenter_min_xx)
}
for internal_plotx in xcenter-yy to xcenter {
internal_plotx = xcenter-yy
repeat yy*2+1 {
internal_plot(ycenter_plus_xx)
internal_plot(ycenter_min_xx)
internal_plotx++
}
yy++
if decisionOver2<=0
+16
View File
@@ -269,6 +269,22 @@ asmsub reset_system() {
}}
}
sub wait(uword jiffies) {
uword current_time = 0
c64.SETTIM(0,0,0)
while current_time < jiffies {
; read clock
%asm {{
stx P8ZP_SCRATCH_REG
jsr c64.RDTIM
sta current_time
stx current_time+1
ldx P8ZP_SCRATCH_REG
}}
}
}
asmsub disable_runstop_and_charsetswitch() {
%asm {{
lda #$80
+71 -63
View File
@@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 25
sub clear_screen() {
clear_screenchars(' ')
txt.chrout(147)
}
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
@@ -38,13 +38,13 @@ 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
ldy #250
- sta c64.Screen+250*0-1,y
sta c64.Screen+250*1-1,y
sta c64.Screen+250*2-1,y
sta c64.Screen+250*3-1,y
dey
bne -
rts
}}
}
@@ -53,13 +53,13 @@ asmsub clear_screencolors (ubyte color @ 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
ldy #250
- sta c64.Colors+250*0-1,y
sta c64.Colors+250*1-1,y
sta c64.Colors+250*2-1,y
sta c64.Colors+250*3-1,y
dey
bne -
rts
}}
}
@@ -83,29 +83,31 @@ asmsub scroll_left (ubyte alsocolors @ Pc) clobbers(A, Y) {
%asm {{
stx P8ZP_SCRATCH_REG
bcs +
jmp _scroll_screen
bcc _scroll_screen
+ ; scroll the color memory
+ ; scroll the screen and 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
.for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 1,x
sta c64.Screen + 40*row + 0,x
lda c64.Colors + 40*row + 1,x
sta c64.Colors + 40*row + 0,x
.next
inx
dey
bpl -
rts
_scroll_screen ; scroll the screen memory
_scroll_screen ; scroll only 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
.for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 1,x
sta c64.Screen + 40*row + 0,x
.next
inx
dey
bpl -
@@ -121,26 +123,28 @@ asmsub scroll_right (ubyte alsocolors @ Pc) clobbers(A) {
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcs +
jmp _scroll_screen
bcc _scroll_screen
+ ; scroll the color memory
+ ; scroll the screen and 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
.for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 0,x
sta c64.Screen + 40*row + 1,x
lda c64.Colors + 40*row + 0,x
sta c64.Colors + 40*row + 1,x
.next
dex
bpl -
rts
_scroll_screen ; scroll the screen memory
_scroll_screen ; scroll only 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
.for row=0, row<=24, row+=1
lda c64.Screen + 40*row + 0,x
sta c64.Screen + 40*row + 1,x
.next
dex
bpl -
@@ -155,26 +159,28 @@ asmsub scroll_up (ubyte alsocolors @ Pc) clobbers(A) {
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcs +
jmp _scroll_screen
bcc _scroll_screen
+ ; scroll the color memory
+ ; scroll the screen and 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
.for row=1, row<=24, row+=1
lda c64.Screen + 40*row,x
sta c64.Screen + 40*(row-1),x
lda c64.Colors + 40*row,x
sta c64.Colors + 40*(row-1),x
.next
dex
bpl -
rts
_scroll_screen ; scroll the screen memory
_scroll_screen ; scroll only 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
.for row=1, row<=24, row+=1
lda c64.Screen + 40*row,x
sta c64.Screen + 40*(row-1),x
.next
dex
bpl -
@@ -189,26 +195,28 @@ asmsub scroll_down (ubyte alsocolors @ Pc) clobbers(A) {
; Carry flag determines if screen color data must be scrolled too
%asm {{
stx P8ZP_SCRATCH_REG
bcs +
jmp _scroll_screen
bcc _scroll_screen
+ ; scroll the color memory
+ ; scroll the screen and 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
.for row=23, row>=0, row-=1
lda c64.Colors + 40*row,x
sta c64.Colors + 40*(row+1),x
lda c64.Screen + 40*row,x
sta c64.Screen + 40*(row+1),x
.next
dex
bpl -
rts
_scroll_screen ; scroll the screen memory
_scroll_screen ; scroll only 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
.for row=23, row>=0, row-=1
lda c64.Screen + 40*row,x
sta c64.Screen + 40*(row+1),x
.next
dex
bpl -
+87
View File
@@ -0,0 +1,87 @@
%target cx16
c64colors {
uword[] colorpalette_dark = [ ; this is a darker palette with more contrast
$000, ; 0 = black
$FFF, ; 1 = white
$632, ; 2 = red
$7AB, ; 3 = cyan
$638, ; 4 = purple
$584, ; 5 = green
$327, ; 6 = blue
$BC6, ; 7 = yellow
$642, ; 8 = orange
$430, ; 9 = brown
$965, ; 10 = light red
$444, ; 11 = dark grey
$666, ; 12 = medium grey
$9D8, ; 13 = light green
$65B, ; 14 = light blue
$999 ; 15 = light grey
]
uword[] colorpalette_pepto = [ ; # this is Pepto's Commodore-64 palette http://www.pepto.de/projects/colorvic/
$000, ; 0 = black
$FFF, ; 1 = white
$833, ; 2 = red
$7cc, ; 3 = cyan
$839, ; 4 = purple
$5a4, ; 5 = green
$229, ; 6 = blue
$ef7, ; 7 = yellow
$852, ; 8 = orange
$530, ; 9 = brown
$c67, ; 10 = light red
$444, ; 11 = dark grey
$777, ; 12 = medium grey
$af9, ; 13 = light green
$76e, ; 14 = light blue
$bbb ; 15 = light grey
]
uword[] colorpalette_light = [ ; this is a lighter palette
$000, ; 0 = black
$FFF, ; 1 = white
$944, ; 2 = red
$7CC, ; 3 = cyan
$95A, ; 4 = purple
$6A5, ; 5 = green
$549, ; 6 = blue
$CD8, ; 7 = yellow
$963, ; 8 = orange
$650, ; 9 = brown
$C77, ; 10 = light red
$666, ; 11 = dark grey
$888, ; 12 = medium grey
$AE9, ; 13 = light green
$87C, ; 14 = light blue
$AAA ; 15 = light grey
]
ubyte color
sub set_palette_pepto() {
for color in 0 to 15 {
uword cc = colorpalette_pepto[color]
cx16.vpoke(1, $fa00 + color*2, lsb(cc)) ; G, B
cx16.vpoke(1, $fa01 + color*2, msb(cc)) ; R
}
}
sub set_palette_light() {
for color in 0 to 15 {
uword cc = colorpalette_light[color]
cx16.vpoke(1, $fa00 + color*2, lsb(cc)) ; G, B
cx16.vpoke(1, $fa01 + color*2, msb(cc)) ; R
}
}
sub set_palette_dark() {
for color in 0 to 15 {
uword cc = colorpalette_dark[color]
cx16.vpoke(1, $fa00 + color*2, lsb(cc)) ; G, B
cx16.vpoke(1, $fa01 + color*2, msb(cc)) ; R
}
}
}
+1
View File
@@ -64,6 +64,7 @@ romsub $fe7e = FINLOG(byte value @A) clobbers (A, X, Y) ; fac1 += sign
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
; note: there is no FPWR() on the Cx16
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
+23 -35
View File
@@ -21,7 +21,7 @@ graphics {
sub disable_bitmap_mode() {
; enables text mode, erase the text screen, color white
void cx16.screen_set_mode(2)
txt.fill_screen(' ', 1) ; TODO doesn't seem to fully clear the text screen after returning from gfx mode
txt.fill_screen(' ', 1) ; doesn't seem to fully clear the text screen after returning from gfx mode
}
@@ -43,7 +43,7 @@ graphics {
;cx16.r1 = ycenter - radius/2
;cx16.r2 = radius*2
;cx16.r3 = radius*2
;cx16.GRAPH_draw_oval(false) ; TODO currently is not implemented on cx16, does a BRK
;cx16.GRAPH_draw_oval(false) ; currently this call is not implemented on cx16, does a BRK
; Midpoint algorithm
ubyte @zp xx = radius
@@ -94,7 +94,7 @@ graphics {
; cx16.r1 = ycenter - radius/2
; cx16.r2 = radius*2
; cx16.r3 = radius*2
; cx16.GRAPH_draw_oval(true) ; TODO currently is not implemented on cx16, does a BRK
; cx16.GRAPH_draw_oval(true) ; currently this call is not implemented on cx16, does a BRK
ubyte xx = radius
ubyte yy = 0
@@ -107,42 +107,30 @@ graphics {
ubyte ycenter_min_xx = ycenter - xx
uword @zp plotx
for plotx in xcenter to xcenter+xx {
cx16.r0 = plotx
cx16.r1 = ycenter_plus_yy
cx16.FB_cursor_position()
cx16.r0 = xcenter-xx
cx16.r1 = ycenter_plus_yy
cx16.FB_cursor_position()
repeat xx*2+1
cx16.FB_set_pixel(1)
cx16.r1 = ycenter_min_yy
cx16.FB_cursor_position()
cx16.r0 = xcenter-xx
cx16.r1 = ycenter_min_yy
cx16.FB_cursor_position()
repeat xx*2+1
cx16.FB_set_pixel(1)
}
for plotx in xcenter-xx to xcenter-1 {
cx16.r0 = plotx
cx16.r1 = ycenter_plus_yy
cx16.FB_cursor_position()
cx16.r0 = xcenter-yy
cx16.r1 = ycenter_plus_xx
cx16.FB_cursor_position()
repeat yy*2+1
cx16.FB_set_pixel(1)
cx16.r1 = ycenter_min_yy
cx16.FB_cursor_position()
cx16.r0 = xcenter-yy
cx16.r1 = ycenter_min_xx
cx16.FB_cursor_position()
repeat yy*2+1
cx16.FB_set_pixel(1)
}
for plotx in xcenter to xcenter+yy {
cx16.r0 = plotx
cx16.r1 = ycenter_plus_xx
cx16.FB_cursor_position()
cx16.FB_set_pixel(1)
cx16.r1 = ycenter_min_xx
cx16.FB_cursor_position()
cx16.FB_set_pixel(1)
}
for plotx in xcenter-yy to xcenter {
cx16.r0 = plotx
cx16.r1 = ycenter_plus_xx
cx16.FB_cursor_position()
cx16.FB_set_pixel(1)
cx16.r1 = ycenter_min_xx
cx16.FB_cursor_position()
cx16.FB_set_pixel(1)
}
yy++
if decisionOver2<=0
decisionOver2 += 2*yy+1
+89 -2
View File
@@ -220,7 +220,7 @@ romsub $ff08 = FB_get_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
romsub $ff0b = FB_set_pixel(ubyte color @A) clobbers(A,X,Y)
romsub $ff0e = FB_set_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
romsub $ff11 = FB_set_8_pixels(ubyte pattern @A, ubyte color @X) clobbers(A,X,Y)
romsub $ff14 = FB_set_8_pixels_opaque(ubyte pattern @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y) ; also uses mask=r0L
romsub $ff14 = FB_set_8_pixels_opaque(ubyte mask @A, ubyte color1 @X, ubyte color2 @Y) clobbers(A,X,Y) ; also uses r0L=pattern
romsub $ff17 = FB_fill_pixels(ubyte color @A) clobbers(A,X,Y) ; also uses count=r0, step=r1
romsub $ff1a = FB_filter_pixels() clobbers(A,X,Y) ; uses ptr=r0, count=r1
romsub $ff1d = FB_move_pixels() clobbers(A,X,Y) ; uses sx=r0, sy=r1, tx=r2, ty=r3, count=r4
@@ -242,9 +242,96 @@ romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
romsub $fecc = monitor() clobbers(A,X,Y)
; ---- end of kernal routines ----
; ---- utilities -----
asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A {
; -- get a byte from VERA's video memory
; note: inefficient when reading multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
sty cx16.VERA_ADDR_M
stx cx16.VERA_ADDR_L
lda cx16.VERA_DATA0
rts
}}
}
sub vpoke(ubyte bank, uword address, ubyte value) {
; -- write a single byte to VERA's video memory
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
lda bank
and #1
sta cx16.VERA_ADDR_H
lda address
sta cx16.VERA_ADDR_L
lda address+1
sta cx16.VERA_ADDR_M
lda value
sta cx16.VERA_DATA0
rts
}}
}
sub FB_set_pixels_from_buf(uword buffer, uword count) {
%asm {{
; -- This is replacement code for the normal FB_set_pixels subroutine in ROM
; However that routine contains a bug in the current v38 ROM that makes it crash when count > 255.
; So the code below replaces that. Once the ROM is patched this routine is no longer necessary.
; See https://github.com/commanderx16/x16-rom/issues/179
phx
lda buffer
ldy buffer+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
jsr _pixels
plx
rts
_pixels lda count+1
beq +
ldx #0
- jsr _loop
inc P8ZP_SCRATCH_W1+1
dec count+1
bne -
+ ldx count
_loop ldy #0
- lda (P8ZP_SCRATCH_W1),y
sta cx16.VERA_DATA0
iny
dex
bne -
rts
}}
}
sub wait(uword jiffies) {
uword current_time = 0
c64.SETTIM(0,0,0)
while current_time < jiffies {
; read clock
%asm {{
phx
jsr c64.RDTIM
sta current_time
stx current_time+1
plx
}}
}
}
; ---- system stuff -----
asmsub init_system() {
; Initializes the machine to a sane starting state.
; Called automatically by the loader program logic.
+5 -9
View File
@@ -16,7 +16,7 @@ const ubyte DEFAULT_HEIGHT = 60
sub clear_screen() {
clear_screenchars(' ')
txt.chrout(147)
}
asmsub fill_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
@@ -154,10 +154,9 @@ sub uppercase() {
cx16.screen_set_charset(2, 0) ; uppercase charset
}
asmsub scroll_left (ubyte dummy @ Pc) clobbers(A, Y) {
asmsub scroll_left() 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 is a dummy on the cx16
%asm {{
phx
jsr c64.SCREEN
@@ -198,10 +197,9 @@ _lx ldx #0 ; modified
}}
}
asmsub scroll_right (ubyte dummy @ Pc) clobbers(A) {
asmsub scroll_right() 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 is a dummy on the cx16
%asm {{
phx
jsr c64.SCREEN
@@ -250,10 +248,9 @@ _lx ldx #0 ; modified
}}
}
asmsub scroll_up (ubyte dummy @ Pc) clobbers(A, Y) {
asmsub scroll_up() clobbers(A, Y) {
; ---- scroll the whole screen 1 character up
; contents of the bottom row are unchanged, you should refill/clear this yourself
; Carry flag is a dummy on the cx16
%asm {{
phx
jsr c64.SCREEN
@@ -300,10 +297,9 @@ _nextline
}}
}
asmsub scroll_down (ubyte dummy @ Pc) clobbers(A, Y) {
asmsub scroll_down() clobbers(A, Y) {
; ---- scroll the whole screen 1 character down
; contents of the top row are unchanged, you should refill/clear this yourself
; Carry flag is a dummy on the cx16
%asm {{
phx
jsr c64.SCREEN
+228 -16
View File
@@ -6,15 +6,15 @@
diskio {
sub directory(ubyte drivenumber) -> byte {
; -- Shows the directory contents of disk drive 8-11 (provide as argument).
sub directory(ubyte drivenumber) -> ubyte {
; -- Prints the directory contents of disk drive 8-11 to the screen. Returns success.
c64.SETNAM(1, "$")
c64.SETLFS(1, drivenumber, 0)
void c64.OPEN() ; open 1,8,0,"$"
c64.SETLFS(13, drivenumber, 0)
void c64.OPEN() ; open 13,8,0,"$"
if_cs
goto io_error
void c64.CHKIN(1) ; use #1 as input channel
void c64.CHKIN(13) ; use #13 as input channel
if_cs
goto io_error
@@ -25,7 +25,9 @@ diskio {
; while not key pressed / EOF encountered, read data.
ubyte status = c64.READST()
while not status {
txt.print_uw(mkword(c64.CHRIN(), c64.CHRIN()))
ubyte low = c64.CHRIN()
ubyte high = c64.CHRIN()
txt.print_uw(mkword(high, low))
txt.chrout(' ')
ubyte @zp char
do {
@@ -37,14 +39,14 @@ diskio {
void c64.CHRIN()
status = c64.READST()
void c64.STOP()
if_nz
if_z
break
}
io_error:
status = c64.READST()
c64.CLRCHN() ; restore default i/o devices
c64.CLOSE(1)
c64.CLOSE(13)
if status and status != 64 { ; 64=end of file
txt.print("\ni/o error, status: ")
@@ -57,9 +59,208 @@ io_error:
}
sub status(ubyte drivenumber) {
; -- display the disk drive's current status message
c64.SETNAM(0, $0000)
; internal variables for the iterative file lister / loader
ubyte list_suffixmatch
ubyte list_pattern_size
ubyte list_skip_disk_name
uword list_pattern
uword list_blocks
ubyte iteration_in_progress = false
str list_filename = "?" * 32
; ----- get a list of files (uses iteration functions internally -----
sub list_files(ubyte drivenumber, uword pattern, ubyte suffixmatch, uword name_ptrs, ubyte max_names) -> ubyte {
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested.
ubyte[256] names_buffer
ubyte[256] names_buffer1 ; to store a bit more names
uword buf_ptr = &names_buffer
ubyte files_found = 0
if lf_start_list(drivenumber, pattern, suffixmatch) {
while lf_next_entry() {
@(name_ptrs) = lsb(buf_ptr)
name_ptrs++
@(name_ptrs) = msb(buf_ptr)
name_ptrs++
buf_ptr += strcopy(diskio.list_filename, buf_ptr) + 1
files_found++
if buf_ptr - &names_buffer > (len(names_buffer) + len(names_buffer1) - 18)
break
if files_found == max_names
break
}
lf_end_list()
}
return files_found
}
; ----- iterative file lister functions -----
sub lf_start_list(ubyte drivenumber, uword pattern, ubyte suffixmatch) -> ubyte {
; -- start an iterative file listing with optional prefix or suffix name matching.
; note: only a single iteration loop can be active at a time!
lf_end_list()
list_pattern = pattern
list_suffixmatch = suffixmatch
list_skip_disk_name = true
iteration_in_progress = true
if pattern==0
list_pattern_size = 0
else
list_pattern_size = strlen(pattern)
c64.SETNAM(1, "$")
c64.SETLFS(12, drivenumber, 0)
void c64.OPEN() ; open 12,8,0,"$"
if_cs
goto io_error
void c64.CHKIN(12) ; use #12 as input channel
if_cs
goto io_error
repeat 4 {
void c64.CHRIN() ; skip the 4 prologue bytes
}
if c64.READST()==0
return true
io_error:
lf_end_list()
return false
}
sub lf_next_entry() -> ubyte {
; -- retrieve the next entry from an iterative file listing session.
; results will be found in list_blocks and list_filename.
; if it returns false though, there are no more entries (or an error occurred).
if not iteration_in_progress
return false
repeat {
void c64.CHKIN(12) ; use #12 as input channel again
uword nameptr = &list_filename
ubyte blocks_lsb = c64.CHRIN()
ubyte blocks_msb = c64.CHRIN()
if c64.READST()
goto close_end
list_blocks = mkword(blocks_msb, blocks_lsb)
; read until the filename starts after the first "
while c64.CHRIN()!='\"' {
if c64.READST()
goto close_end
}
; read the filename
repeat {
ubyte char = c64.CHRIN()
if_z
break
if char=='\"'
break
@(nameptr) = char
nameptr++
}
@(nameptr) = 0
while c64.CHRIN() {
; read the rest of the entry until the end
}
void c64.CHRIN() ; skip 2 bytes
void c64.CHRIN()
if not list_skip_disk_name {
if list_pattern_size {
; do filename matching
if list_suffixmatch
rightstr(list_filename, filename, list_pattern_size)
else
leftstr(list_filename, filename, list_pattern_size)
if strcmp(filename, list_pattern)==0
return true
} else
return true
}
list_skip_disk_name = false
}
close_end:
lf_end_list()
return false
}
sub lf_end_list() {
; -- end an iterative file listing session (close channels).
if iteration_in_progress {
c64.CLRCHN()
c64.CLOSE(12)
iteration_in_progress = false
}
}
; ----- iterative file loader functions -----
sub f_open(ubyte drivenumber, uword filenameptr) -> ubyte {
; -- open a file for iterative reading with f_read
; note: only a single iteration loop can be active at a time!
f_close()
c64.SETNAM(strlen(filenameptr), filenameptr)
c64.SETLFS(11, drivenumber, 0)
void c64.OPEN() ; open 11,8,0,"filename"
if_cc {
iteration_in_progress = true
void c64.CHKIN(11) ; use #11 as input channel
if_cc
return true
}
f_close()
return false
}
sub f_read(uword bufferpointer, uword buffersize) -> uword {
if not iteration_in_progress
return 0
uword actual = 0
void c64.CHKIN(11) ; use #11 as input channel again
repeat buffersize {
ubyte data = c64.CHRIN()
@(bufferpointer) = data
bufferpointer++
actual++
ubyte status = c64.READST()
if status==64
f_close() ; end of file, close it
if status
return actual
}
return actual
}
sub f_close() {
; -- end an iterative file loading session (close channels).
if iteration_in_progress {
c64.CLRCHN()
c64.CLOSE(11)
iteration_in_progress = false
}
}
sub status(ubyte drivenumber) -> uword {
; -- retrieve the disk drive's current status message
uword messageptr = &filename
c64.SETNAM(0, filename)
c64.SETLFS(15, drivenumber, 15)
void c64.OPEN() ; open 15,8,15
if_cs
@@ -68,16 +269,20 @@ io_error:
if_cs
goto io_error
while not c64.READST()
txt.chrout(c64.CHRIN())
while not c64.READST() {
@(messageptr) = c64.CHRIN()
messageptr++
}
io_error:
@(messageptr) = 0
c64.CLRCHN() ; restore default i/o devices
c64.CLOSE(15)
return filename
}
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> byte {
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
c64.SETNAM(strlen(filenameptr), filenameptr)
c64.SETLFS(1, drivenumber, 0)
uword end_address = address + size
@@ -97,10 +302,14 @@ io_error:
plp
}}
ubyte result=0
if_cc
return c64.READST()==0
result = c64.READST()==0
return false
c64.CLRCHN()
c64.CLOSE(1)
return result
}
sub load(ubyte drivenumber, uword filenameptr, uword address_override) -> uword {
@@ -122,6 +331,9 @@ io_error:
+ ldx P8ZP_SCRATCH_REG
}}
c64.CLRCHN()
c64.CLOSE(1)
if end_of_load
return end_of_load - address_override
+93
View File
@@ -774,6 +774,31 @@ stack_mul_word_100 .proc
rts
.pend
stack_mul_word_320 .proc
; stackW = stackLo * 256 + stackLo * 64 (stackHi doesn't matter)
ldy P8ESTACK_LO+1,x
lda #0
sta P8ESTACK_HI+1,x
tya
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
asl a
rol P8ESTACK_HI+1,x
sta P8ESTACK_LO+1,x
tya
clc
adc P8ESTACK_HI+1,x
sta P8ESTACK_HI+1,x
rts
.pend
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
mul_byte_3 .proc
@@ -1241,6 +1266,32 @@ mul_word_100 .proc
rts
.pend
mul_word_320 .proc
; AY = A * 256 + A * 64 (msb doesn't matter)
sta P8ZP_SCRATCH_B1
ldy #0
sty P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
asl a
rol P8ZP_SCRATCH_REG
pha
clc
lda P8ZP_SCRATCH_B1
adc P8ZP_SCRATCH_REG
tay
pla
rts
.pend
; ----------- end optimized multiplications -----------
@@ -1301,6 +1352,33 @@ shift_left_w_3 .proc
jmp shift_left_w_7._shift3
.pend
shift_left_w .proc
; -- variable number of shifts left
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift asl P8ESTACK_LO+1,x
rol P8ESTACK_HI+1,x
dey
bne _shift
rts
.pend
shift_right_uw .proc
; -- uword variable number of shifts right
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift lsr P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x
dey
bne _shift
rts
.pend
shift_right_uw_7 .proc
lda P8ESTACK_LO+1,x
sta P8ZP_SCRATCH_B1
@@ -1431,6 +1509,21 @@ shift_right_w_3 .proc
.pend
shift_right_w .proc
; -- signed word variable number of shifts right
inx
ldy P8ESTACK_LO,x
bne _shift
rts
_shift lda P8ESTACK_HI+1,x
asl a
ror P8ESTACK_HI+1,x
ror P8ESTACK_LO+1,x
dey
bne _shift
rts
.pend
; support for bit shifting that is too large to be unrolled:
+21
View File
@@ -1346,3 +1346,24 @@ func_strcmp_stack .proc
dex
rts
.pend
func_strcopy .proc
lda _arg_to
ldy _arg_to+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda _arg_from
ldy _arg_from+1
jsr strcpy
tya
rts
_arg_from .word 0
_arg_to .word 0
.pend
func_strcopy_to_stack .proc
jsr func_strcopy
sta P8ESTACK_LO,x
dex
rts
.pend
+36 -28
View File
@@ -470,27 +470,19 @@ less_uw .proc
reg_less_w .proc
; -- AY < P8ZP_SCRATCH_W2?
sta P8ZP_SCRATCH_B1
cmp P8ZP_SCRATCH_W2
tya
sec
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #$80
+ bmi _true
bvc +
eor #$80
+ bne _false
lda P8ZP_SCRATCH_B1
sbc P8ZP_SCRATCH_W2
bcs _false
_true lda #1
lda #0
rts
_false lda #0
_true lda #1
rts
.pend
less_w .proc
; TODO is this word comparison < correct? reg_less_w is a lot larger...
lda P8ESTACK_LO+2,x
cmp P8ESTACK_LO+1,x
lda P8ESTACK_HI+2,x
@@ -549,7 +541,6 @@ _true lda #1
.pend
lesseq_uw .proc
; TODO is this comparison uword <= correct????
lda P8ESTACK_HI+1,x
cmp P8ESTACK_HI+2,x
bcc equal_b._equal_b_false
@@ -561,29 +552,20 @@ lesseq_uw .proc
.pend
reg_lesseq_w .proc
; -- AY <= P8ZP_SCRATCH_W2?
sta P8ZP_SCRATCH_B1
; -- P8ZP_SCRATCH_W2 <= AY ? (note: order different from other routines)
cmp P8ZP_SCRATCH_W2
tya
sec
sbc P8ZP_SCRATCH_W2+1
bvc +
eor #$80
+ bmi _true
bvc +
eor #$80
+ bne _false
lda P8ZP_SCRATCH_B1
sbc P8ZP_SCRATCH_W2
beq _true ; TODO is this correct word compare <= for all cases?
bcs _false
_true lda #1
+ bpl +
lda #0
rts
_false lda #0
+ lda #1
rts
.pend
lesseq_w .proc
; TODO is this word compare <= correct? it is different from reg_lesseq_w
lda P8ESTACK_LO+1,x
cmp P8ESTACK_LO+2,x
lda P8ESTACK_HI+1,x
@@ -671,8 +653,8 @@ greatereq_w .proc
sbc P8ESTACK_HI+1,x
bvc +
eor #$80
+ bpl equal_b._equal_b_true
bmi equal_b._equal_b_false
+ bmi equal_b._equal_b_false
bpl equal_b._equal_b_true
.pend
@@ -1011,6 +993,7 @@ _arg_index .byte 0
strcpy .proc
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
; it is assumed the target string is large enough.
; returns the length of the string that was copied in Y.
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #$ff
@@ -1065,3 +1048,28 @@ _return_minusone
lda #-1
rts
.pend
sign_extend_stack_byte .proc
; -- sign extend the (signed) byte on the stack to full 16 bits
lda P8ESTACK_LO+1,x
ora #$7f
bmi +
lda #0
+ sta P8ESTACK_HI+1,x
rts
.pend
sign_extend_AY_byte .proc
; -- sign extend the (signed) byte in AY to full 16 bits
pha
and #$80
beq +
ldy #$ff
pla
rts
+ ldy #0
pla
rts
.pend
+1 -1
View File
@@ -1 +1 @@
5.1
5.4
+8
View File
@@ -216,6 +216,14 @@ interface INameScope {
null
}
fun previousSibling(stmt: Statement): Statement? {
val previousIdx = statements.indexOfFirst { it===stmt } - 1
return if(previousIdx>=0)
statements[previousIdx]
else
null
}
fun indexOfChild(stmt: Statement): Int {
val idx = statements.indexOfFirst { it===stmt }
if(idx>=0)
+5 -3
View File
@@ -30,8 +30,8 @@ enum class DataType {
UWORD -> targetType in setOf(UWORD, FLOAT)
WORD -> targetType in setOf(WORD, FLOAT)
FLOAT -> targetType == FLOAT
STR -> targetType == STR || targetType == UWORD
in ArrayDatatypes -> targetType == this || targetType == UWORD
STR -> targetType == STR
in ArrayDatatypes -> targetType == this
else -> false
}
@@ -80,7 +80,9 @@ enum class RegisterOrPair {
Y,
AX,
AY,
XY;
XY,
FAC1,
FAC2;
companion object {
val names by lazy { values().map { it.toString()} }
+27 -24
View File
@@ -288,6 +288,7 @@ internal class AstChecker(private val program: Program,
regCounts[CpuRegister.X]=regCounts.getValue(CpuRegister.X)+1
regCounts[CpuRegister.Y]=regCounts.getValue(CpuRegister.Y)+1
}
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> { /* no sensible way to count this */ }
null ->
if(p.statusflag!=null)
statusflagCounts[p.statusflag] = statusflagCounts.getValue(p.statusflag) + 1
@@ -383,7 +384,7 @@ internal class AstChecker(private val program: Program,
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
if(targetDt.typeOrElse(DataType.STRUCT) in IterableDatatypes)
errors.err("cannot assign value to string or array", assignment.value.position)
else
else if(!(valueDt.istype(DataType.STR) && targetDt.istype(DataType.UWORD)))
errors.err("value's type doesn't match target", assignment.value.position)
}
@@ -454,15 +455,12 @@ internal class AstChecker(private val program: Program,
override fun visit(addressOf: AddressOf) {
val variable=addressOf.identifier.targetVarDecl(program.namespace)
if(variable==null)
errors.err("pointer-of operand must be the name of a heap variable", addressOf.position)
else {
if(variable.datatype !in ArrayDatatypes
&& variable.type!=VarDeclType.MEMORY
&& variable.struct == null
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
if(variable!=null
&& variable.datatype !in ArrayDatatypes
&& variable.type!=VarDeclType.MEMORY
&& variable.struct == null
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
errors.err("invalid pointer-of operand type", addressOf.position)
}
super.visit(addressOf)
}
@@ -603,23 +601,28 @@ internal class AstChecker(private val program: Program,
}
}
// array length limits
// array length limits and constant lenghts
if(decl.isArray) {
val length = decl.arraysize!!.constIndex() ?: 1
when (decl.datatype) {
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
if(length==0 || length>256)
err("string and byte array length must be 1-256")
val length = decl.arraysize!!.constIndex()
if(length==null)
err("array length must be a constant")
else {
when (decl.datatype) {
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
if (length == 0 || length > 256)
err("string and byte array length must be 1-256")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
if (length == 0 || length > 128)
err("word array length must be 1-128")
}
DataType.ARRAY_F -> {
if (length == 0 || length > 51)
err("float array length must be 1-51")
}
else -> {
}
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
if(length==0 || length>128)
err("word array length must be 1-128")
}
DataType.ARRAY_F -> {
if(length==0 || length>51)
err("float array length must be 1-51")
}
else -> {}
}
}
@@ -4,6 +4,7 @@ import prog8.ast.*
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.functions.BuiltinFunctions
internal class StatementReorderer(val program: Program, val errors: ErrorReporter) : AstWalker() {
@@ -91,41 +92,84 @@ internal class StatementReorderer(val program: Program, val errors: ErrorReporte
}
}
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
// when using a simple bit shift and assigning it to a variable of a different type,
// try to make the bit shifting 'wide enough' to fall into the variable's type.
// with this, for instance, uword x = 1 << 10 will result in 1024 rather than 0 (the ubyte result).
if(expr.operator=="<<" || expr.operator==">>") {
val leftDt = expr.left.inferType(program)
when (parent) {
is Assignment -> {
val targetDt = parent.target.inferType(program)
if(leftDt != targetDt) {
val cast = TypecastExpression(expr.left, targetDt.typeOrElse(DataType.STRUCT), true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is VarDecl -> {
if(!leftDt.istype(parent.datatype)) {
val cast = TypecastExpression(expr.left, parent.datatype, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is IFunctionCall -> {
val argnum = parent.args.indexOf(expr)
when (val callee = parent.target.targetStatement(program.namespace)) {
is Subroutine -> {
val paramType = callee.parameters[argnum].type
if(leftDt isAssignableTo paramType) {
val cast = TypecastExpression(expr.left, paramType, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
is BuiltinFunctionStatementPlaceholder -> {
val func = BuiltinFunctions.getValue(callee.name)
val paramTypes = func.parameters[argnum].possibleDatatypes
for(type in paramTypes) {
if(leftDt isAssignableTo type) {
val cast = TypecastExpression(expr.left, type, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
}
}
else -> throw FatalAstException("weird callee")
}
}
else -> return noModifications
}
}
return noModifications
}
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
val modifications = mutableListOf<IAstModification>()
val subroutine = expr.definingSubroutine()!!
val statement = expr.containingStatement()
val indexerVarPrefix = "prog8_autovar_index_"
val indexerVarName: String
val repo = subroutine.asmGenInfo.usedAutoArrayIndexerForStatements
val freeVar = repo.filter { statement !in it.value }.map { it.key }.firstOrNull()
if(freeVar==null) {
// TODO make this even smarter so that an indexerVar can be reused for a different following statement... requires updating the partOfStatement?
var indexerVar = repo.firstOrNull { it.replaces isSameAs expr.indexer }
if(indexerVar==null) {
// add another loop index var to be used for this expression
val statementId = expr.hashCode()
indexerVarName = "$indexerVarPrefix$statementId"
val indexerVarName = "$indexerVarPrefix${expr.indexer.hashCode()}"
indexerVar = AsmGenInfo.ArrayIndexerInfo(indexerVarName, expr.indexer, statement)
repo.add(indexerVar)
// create the indexer var at block level scope
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE,
null, indexerVarName, null, null, isArray = false, autogeneratedDontRemove = true, position = expr.position)
modifications.add(IAstModification.InsertFirst(vardecl, subroutine))
var statements = repo[indexerVarName]
if(statements==null) {
statements = mutableSetOf()
repo[indexerVarName] = statements
}
statements.add(statement)
} else {
// reuse an already created indexer autovar
repo.getValue(freeVar).add(statement)
indexerVarName = freeVar
}
indexerVar.used++ // keep track of how many times it it used, to avoid assigning it multiple times
// assign the indexing expression to the helper variable, and replace the indexer with just the variable
// replace the indexer with just the variable
// assign the indexing expression to the helper variable, but only if that hasn't been done already
val indexerExpression = expr.indexer.origExpression!!
val target = AssignTarget(IdentifierReference(listOf(indexerVarName), indexerExpression.position), null, null, indexerExpression.position)
val assign = Assignment(target, indexerExpression, indexerExpression.position)
val beforeWhat = expr.containingStatement()
modifications.add(IAstModification.InsertBefore(beforeWhat, assign, beforeWhat.definingScope()))
val target = AssignTarget(IdentifierReference(listOf(indexerVar.name), indexerExpression.position), null, null, indexerExpression.position)
if(indexerVar.used==1) {
val assign = Assignment(target, indexerExpression, indexerExpression.position)
modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope()))
}
modifications.add(IAstModification.SetExpression( {
expr.indexer.indexVar = it as IdentifierReference
expr.indexer.indexNum = null
@@ -41,8 +41,9 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? {
val argITypes = call.args.map { it.inferType(program) }
if(argITypes.any { !it.isKnown })
throw FatalAstException("unknown dt")
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
if(firstUnknownDt>=0)
return "argument ${firstUnknownDt+1} invalid argument type"
val argtypes = argITypes.map { it.typeOrElse(DataType.STRUCT) }
val target = call.target.targetStatement(scope)
if (target is Subroutine) {
@@ -200,7 +200,7 @@ open class VarDecl(val type: VarDeclType,
fun defaultZero(dt: DataType, position: Position) = when(dt) {
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, 0, position)
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, 0, position)
DataType.UWORD -> NumericLiteralValue(DataType.UWORD, 0, position)
DataType.UWORD, DataType.STR -> NumericLiteralValue(DataType.UWORD, 0, position)
DataType.WORD -> NumericLiteralValue(DataType.WORD, 0, position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
else -> throw FatalAstException("can only determine default zero value for a numeric type")
@@ -313,6 +313,7 @@ class ArrayIndex(var origExpression: Expression?, // will be replaced
}
is IdentifierReference -> {
indexVar = replacement
indexNum = null
}
else -> {
throw FatalAstException("invalid replace")
@@ -347,7 +348,12 @@ class ArrayIndex(var origExpression: Expression?, // will be replaced
fun constIndex() = indexNum?.number?.toInt()
infix fun isSameAs(other: ArrayIndex) = indexNum==other.indexNum && indexVar == other.indexVar
infix fun isSameAs(other: ArrayIndex): Boolean {
return if(indexNum!=null || indexVar!=null)
indexNum==other.indexNum && indexVar == other.indexVar
else
other.origExpression!=null && origExpression!! isSameAs other.origExpression!!
}
}
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
@@ -679,16 +685,20 @@ class NopStatement(override val position: Position): Statement() {
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
}
class AsmGenInfo {
// This class contains various attributes that influence the assembly code generator.
// Conceptually it should be part of any INameScope.
// But because the resulting code only creates "real" scopes on a subroutine level,
// it's more consistent to only define these attributes on a Subroutine node.
var usedAutoArrayIndexerForStatements = mutableMapOf<String, MutableSet<Statement>>()
var usedAutoArrayIndexerForStatements = mutableListOf<ArrayIndexerInfo>()
var usedRegsaveA = false
var usedRegsaveX = false
var usedRegsaveY = false
var usedFloatEvalResultVar = false
var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false
class ArrayIndexerInfo(val name: String, val replaces: ArrayIndex, val partOfStatement: Statement, var used: Int=0)
}
// the subroutine class covers both the normal user-defined subroutines,
@@ -747,6 +757,8 @@ class Subroutine(override val name: String,
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun shouldPreserveStatusRegisterAfterCall() = asmReturnvaluesRegisters.any { it.statusflag != null } || shouldSaveX()
fun shouldSaveX() = CpuRegister.X in asmClobbers || regXasResult() || regXasParam()
fun amountOfRtsInAsm(): Int = statements
.asSequence()
@@ -170,6 +170,12 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
AddressOf(typecast.expression as IdentifierReference, typecast.position),
parent
))
} else if(typecast.expression is IFunctionCall) {
return listOf(IAstModification.ReplaceNode(
typecast,
typecast.expression,
parent
))
}
} else {
errors.err("cannot cast pass-by-reference value to type ${typecast.type} (only to UWORD)", typecast.position)
@@ -7,6 +7,8 @@ internal interface IAssemblyGenerator {
}
internal const val generatedLabelPrefix = "_prog8_label_"
internal const val subroutineFloatEvalResultVar1 = "_prog8_float_eval_result1"
internal const val subroutineFloatEvalResultVar2 = "_prog8_float_eval_result2"
internal interface IAssemblyProgram {
val name: String
@@ -8,6 +8,7 @@ import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.compiler.*
import prog8.compiler.target.*
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.IAssemblyGenerator
@@ -35,7 +36,7 @@ internal class AsmGen(private val program: Program,
// for expressions and augmented assignments:
val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100)
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320)
private val assemblyLines = mutableListOf<String>()
private val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
@@ -159,6 +160,7 @@ internal class AsmGen(private val program: Program,
val floatvalue = flt.key
out("${flt.value}\t.byte $floatFill ; float $floatvalue")
}
out("prog8_program_end\t; end of program label for progend()")
}
private fun block2asm(block: Block) {
@@ -799,8 +801,10 @@ internal class AsmGen(private val program: Program,
out("_prog8_regsaveX .byte 0")
if(sub.asmGenInfo.usedRegsaveY)
out("_prog8_regsaveY .byte 0")
if(sub.asmGenInfo.usedFloatEvalResultVar)
out("_prog8_float_eval_result .byte 0,0,0,0,0")
if(sub.asmGenInfo.usedFloatEvalResultVar1)
out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
if(sub.asmGenInfo.usedFloatEvalResultVar2)
out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
vardecls2asm(sub.statements)
out(" .pend\n")
}
@@ -811,12 +815,12 @@ internal class AsmGen(private val program: Program,
when (condition) {
BranchCondition.CS -> "bcc"
BranchCondition.CC -> "bcs"
BranchCondition.EQ, BranchCondition.Z -> "beq"
BranchCondition.NE, BranchCondition.NZ -> "bne"
BranchCondition.EQ, BranchCondition.Z -> "bne"
BranchCondition.NE, BranchCondition.NZ -> "beq"
BranchCondition.VS -> "bvc"
BranchCondition.VC -> "bvs"
BranchCondition.MI, BranchCondition.NEG -> "bmi"
BranchCondition.PL, BranchCondition.POS -> "bpl"
BranchCondition.MI, BranchCondition.NEG -> "bpl"
BranchCondition.PL, BranchCondition.POS -> "bmi"
}
} else {
when (condition) {
@@ -1068,26 +1072,48 @@ $counterVar .byte 0""")
val jump = stmt.truepart.statements.first() as? Jump
if(jump!=null) {
// branch with only a jump
// branch with only a jump (goto)
val instruction = branchInstruction(stmt.condition, false)
out(" $instruction ${getJumpTarget(jump)}")
translate(stmt.elsepart)
} else {
val truePartIsJustBreak = stmt.truepart.statements.firstOrNull() is Break
val elsePartIsJustBreak = stmt.elsepart.statements.firstOrNull() is Break
if(stmt.elsepart.containsNoCodeNorVars()) {
if(truePartIsJustBreak) {
// branch with just a break (jump out of loop)
val instruction = branchInstruction(stmt.condition, false)
val loopEndLabel = loopEndLabels.peek()
out(" $instruction $loopEndLabel")
} else {
val instruction = branchInstruction(stmt.condition, true)
val elseLabel = makeLabel("branch_else")
out(" $instruction $elseLabel")
translate(stmt.truepart)
out(elseLabel)
}
}
else if(truePartIsJustBreak) {
// branch with just a break (jump out of loop)
val instruction = branchInstruction(stmt.condition, false)
val loopEndLabel = loopEndLabels.peek()
out(" $instruction $loopEndLabel")
translate(stmt.elsepart)
} else if(elsePartIsJustBreak) {
// branch with just a break (jump out of loop) but true/false inverted
val instruction = branchInstruction(stmt.condition, true)
val loopEndLabel = loopEndLabels.peek()
out(" $instruction $loopEndLabel")
translate(stmt.truepart)
} else {
val instruction = branchInstruction(stmt.condition, true)
val elseLabel = makeLabel("branch_else")
val endLabel = makeLabel("branch_end")
out(" $instruction $elseLabel")
translate(stmt.truepart)
out(elseLabel)
} else {
val instruction = branchInstruction(stmt.condition, false)
val trueLabel = makeLabel("branch_true")
val endLabel = makeLabel("branch_end")
out(" $instruction $trueLabel")
translate(stmt.elsepart)
out(" jmp $endLabel")
out(trueLabel)
translate(stmt.truepart)
out(elseLabel)
translate(stmt.elsepart)
out(endLabel)
}
}
@@ -1174,18 +1200,7 @@ $label nop""")
}
DataType.FLOAT -> {
// return the float value via FAC1
when (returnvalue) {
is NumericLiteralValue -> throw AssemblyError("float literal should have been changed to auto var")
is IdentifierReference -> {
val asmVar = asmVariableName(returnvalue)
out(" lda #<${asmVar} | ldy #>${asmVar} | jsr floats.MOVFM")
}
else -> {
// todo evaluate directly into fac1 instead of via stack intermediate (add RegisterOrPair.FAC1/FAC2 ??)
translateExpression(returnvalue)
out(" jsr floats.pop_float_fac1")
}
}
assignExpressionToRegister(returnvalue, RegisterOrPair.FAC1)
}
else -> {
// all else take its address and assign that also to AY register pair
@@ -1202,33 +1217,41 @@ $label nop""")
assemblyLines.add(assembly)
}
internal fun signExtendAYlsb(valueDt: DataType) {
// sign extend signed byte in A to full word in AY
when(valueDt) {
DataType.UBYTE -> out(" ldy #0")
DataType.BYTE -> out(" jsr prog8_lib.sign_extend_AY_byte")
else -> throw AssemblyError("need byte type")
}
}
internal fun signExtendStackLsb(valueDt: DataType) {
// sign extend signed byte on stack to signed word
// sign extend signed byte on stack to signed word on stack
when(valueDt) {
DataType.UBYTE -> {
out(" lda #0 | sta P8ESTACK_HI+1,x")
}
DataType.BYTE -> {
out("""
lda P8ESTACK_LO+1,x
ora #$7f
bmi +
lda #0
+ sta P8ESTACK_HI+1,x""")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
out(" stz P8ESTACK_HI+1,x")
else
out(" lda #0 | sta P8ESTACK_HI+1,x")
}
DataType.BYTE -> out(" jsr prog8_lib.sign_extend_stack_byte")
else -> throw AssemblyError("need byte type")
}
}
internal fun signExtendVariableLsb(asmvar: String, valueDt: DataType) {
// sign extend signed byte in a word variable
// sign extend signed byte in a var to a full word in that variable
when(valueDt) {
DataType.UBYTE -> {
out(" lda #0 | sta $asmvar+1")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
out(" stz $asmvar+1")
else
out(" lda #0 | sta $asmvar+1")
}
DataType.BYTE -> {
out("""
lda $asmvar+1
lda $asmvar
ora #$7f
bmi +
lda #0
@@ -75,11 +75,11 @@ private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
// the when statement (on bytes) generates a sequence of:
// lda $ce01,x
// cmp #$20
// lda $ce01,x
// cmp #$20
// beq check_prog8_s72choice_32
// lda $ce01,x
// cmp #$21
// lda $ce01,x
// cmp #$21
// beq check_prog8_s73choice_33
// the repeated lda can be removed
val mods = mutableListOf<Modification>()
@@ -5,10 +5,7 @@ import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.ArrayIndex
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.Subroutine
import prog8.ast.statements.*
import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.Cx16Target
@@ -17,6 +14,7 @@ import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
import prog8.compiler.target.c64.codegen.assignment.SourceStorageKind
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
import prog8.compiler.target.subroutineFloatEvalResultVar2
import prog8.compiler.toHex
import prog8.functions.FSignature
@@ -83,6 +81,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
"set_irqd" -> asmgen.out(" sei")
"strlen" -> funcStrlen(fcall, resultToStack)
"strcmp" -> funcStrcmp(fcall, func, resultToStack, scope)
"strcopy" -> {
translateArguments(fcall.args, func, scope)
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_strcopy_to_stack")
else
asmgen.out(" jsr prog8_lib.func_strcopy")
}
"memcopy", "memset", "memsetw" -> funcMemSetCopy(fcall, func, scope)
"substr", "leftstr", "rightstr" -> {
translateArguments(fcall.args, func, scope)
@@ -92,6 +97,17 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
translateArguments(fcall.args, func, scope)
asmgen.out(" jmp prog8_lib.func_exit")
}
"progend" -> {
if(resultToStack)
asmgen.out("""
lda #<prog8_program_end
sta P8ESTACK_LO,x
lda #>prog8_program_end
sta P8ESTACK_HI,x
dex""")
else
asmgen.out(" lda #<prog8_program_end | ldy #>prog8_program_end")
}
else -> TODO("missing asmgen for builtin func ${func.name}")
}
}
@@ -531,14 +547,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
private fun funcStrlen(fcall: IFunctionCall, resultToStack: Boolean) {
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
val type = fcall.args[0].inferType(program)
when {
type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name")
type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1")
else -> throw AssemblyError("strlen requires str or uword arg")
if (fcall.args[0] is IdentifierReference) {
// use the address of the variable
val name = asmgen.asmVariableName(fcall.args[0] as IdentifierReference)
val type = fcall.args[0].inferType(program)
when {
type.istype(DataType.STR) -> asmgen.out(" lda #<$name | ldy #>$name")
type.istype(DataType.UWORD) -> asmgen.out(" lda $name | ldy $name+1")
else -> throw AssemblyError("strlen requires str or uword arg")
}
}
if(resultToStack)
else {
// use the expression value as address of the string
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
}
if (resultToStack)
asmgen.out(" jsr prog8_lib.func_strlen_stack")
else
asmgen.out(" jsr prog8_lib.func_strlen_into_A")
@@ -1032,8 +1055,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
throw AssemblyError("float literals should have been converted into autovar")
}
else -> {
scope.asmGenInfo.usedFloatEvalResultVar = true
val variable = IdentifierReference(listOf("_prog8_float_eval_result"), value.position)
scope.asmGenInfo.usedFloatEvalResultVar2 = true
val variable = IdentifierReference(listOf(subroutineFloatEvalResultVar2), value.position)
val addr = AddressOf(variable, value.position)
addr.linkParents(value)
asmgen.assignExpressionToVariable(value, asmgen.asmVariableName(variable), DataType.FLOAT, scope)
@@ -9,6 +9,7 @@ import prog8.ast.statements.Subroutine
import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.subroutineFloatEvalResultVar1
import prog8.compiler.toHex
import prog8.functions.BuiltinFunctions
import kotlin.math.absoluteValue
@@ -215,11 +216,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
jsr floats.vars_less_f
beq $jumpIfFalseLabel""")
} else {
if (asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (e1): '<' at ${left.position}") // TODO float via func args?
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.less_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
val subroutine = left.definingSubroutine()!!
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
asmgen.out("""
lda #<$subroutineFloatEvalResultVar1
ldy #>$subroutineFloatEvalResultVar1
jsr floats.var_fac1_less_f
beq $jumpIfFalseLabel""")
}
}
@@ -256,11 +261,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
jsr floats.vars_lesseq_f
beq $jumpIfFalseLabel""")
} else {
if (asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (e1): '<=' at ${left.position}") // TODO float via func args?
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.lesseq_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
val subroutine = left.definingSubroutine()!!
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
asmgen.out("""
lda #<$subroutineFloatEvalResultVar1
ldy #>$subroutineFloatEvalResultVar1
jsr floats.var_fac1_lesseq_f
beq $jumpIfFalseLabel""")
}
}
@@ -297,11 +306,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
jsr floats.vars_less_f
beq $jumpIfFalseLabel""")
} else {
if (asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (e1): '>' at ${left.position}") // TODO float via func args?
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.greater_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
val subroutine = left.definingSubroutine()!!
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
asmgen.out("""
lda #<$subroutineFloatEvalResultVar1
ldy #>$subroutineFloatEvalResultVar1
jsr floats.var_fac1_greater_f
beq $jumpIfFalseLabel""")
}
}
@@ -338,11 +351,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
jsr floats.vars_lesseq_f
beq $jumpIfFalseLabel""")
} else {
if (asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (e1): '>=' at ${left.position}") // TODO float via func args
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.greatereq_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
val subroutine = left.definingSubroutine()!!
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
asmgen.out("""
lda #<$subroutineFloatEvalResultVar1
ldy #>$subroutineFloatEvalResultVar1
jsr floats.var_fac1_greatereq_f
beq $jumpIfFalseLabel""")
}
}
@@ -772,8 +789,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
}
asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.assignExpressionToRegister(left, RegisterOrPair.AY)
asmgen.assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
}
@@ -899,8 +916,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
}
asmgen.assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.assignExpressionToRegister(right, RegisterOrPair.AY)
asmgen.assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.assignExpressionToRegister(left, RegisterOrPair.AY)
asmgen.out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
}
@@ -1115,11 +1132,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
jsr floats.vars_equal_f
beq $jumpIfFalseLabel""")
} else {
if (asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (e22): '==' at ${left.position}") // TODO float via func args?
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.equal_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
val subroutine = left.definingSubroutine()!!
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
asmgen.out("""
lda #<$subroutineFloatEvalResultVar1
ldy #>$subroutineFloatEvalResultVar1
jsr floats.var_fac1_notequal_f
bne $jumpIfFalseLabel""")
}
}
@@ -1196,11 +1217,15 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
jsr floats.vars_equal_f
bne $jumpIfFalseLabel""")
} else {
if (asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (e23): '!=' at ${left.position}") // TODO float via func args?
translateExpression(left)
translateExpression(right)
asmgen.out(" jsr floats.notequal_f | inx | lda P8ESTACK_LO,x | beq $jumpIfFalseLabel")
val subroutine = left.definingSubroutine()!!
subroutine.asmGenInfo.usedFloatEvalResultVar1 = true
asmgen.assignExpressionToVariable(right, subroutineFloatEvalResultVar1, DataType.FLOAT, subroutine)
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
asmgen.out("""
lda #<$subroutineFloatEvalResultVar1
ldy #>$subroutineFloatEvalResultVar1
jsr floats.var_fac1_notequal_f
beq $jumpIfFalseLabel""")
}
}
@@ -1304,7 +1329,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.translateBuiltinFunctionCallExpression(expression, builtinFunc, true)
} else {
sub as Subroutine
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any {it.statusflag!=null}
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
asmgen.translateFunctionCall(expression, preserveStatusRegisterAfterCall)
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
for ((_, reg) in returns) {
@@ -1339,6 +1364,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.out(" lda #0 | sta P8ESTACK_LO,x")
asmgen.out(" tya | sta P8ESTACK_HI,x | dex")
}
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.push_fac1")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.push_fac2")
}
}
else if(reg.statusflag!=null) {
@@ -1592,9 +1619,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
}
">>" -> {
translateExpression(expr.left)
val amount = expr.right.constValue(program)?.number?.toInt()
if(amount!=null) {
translateExpression(expr.left)
when (leftDt) {
DataType.UBYTE -> {
if (amount <= 2)
@@ -1615,6 +1642,13 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
}
DataType.UWORD -> {
if(amount>=16) {
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_LO+1,x | stz P8ESTACK_HI+1,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_LO+1,x | sta P8ESTACK_HI+1,x")
return
}
var left = amount
while (left >= 7) {
asmgen.out(" jsr math.shift_right_uw_7")
@@ -1626,6 +1660,20 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.out(" jsr math.shift_right_uw_$left")
}
DataType.WORD -> {
if(amount>=16) {
asmgen.out("""
lda P8ESTACK_HI+1,x
bmi +
lda #0
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
beq ++
+ lda #255
sta P8ESTACK_LO+1,x
sta P8ESTACK_HI+1,x
+""")
return
}
var left = amount
while (left >= 7) {
asmgen.out(" jsr math.shift_right_w_7")
@@ -1642,9 +1690,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
}
"<<" -> {
translateExpression(expr.left)
val amount = expr.right.constValue(program)?.number?.toInt()
if(amount!=null) {
translateExpression(expr.left)
if (leftDt in ByteDatatypes) {
if (amount <= 2)
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
@@ -1899,24 +1947,29 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
}
private fun translateBinaryOperatorWords(operator: String, types: DataType) {
private fun translateBinaryOperatorWords(operator: String, dt: DataType) {
when(operator) {
"**" -> throw AssemblyError("** operator requires floats")
"*" -> asmgen.out(" jsr prog8_lib.mul_word")
"/" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
"/" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.idiv_uw" else " jsr prog8_lib.idiv_w")
"%" -> {
if(types==DataType.WORD)
if(dt==DataType.WORD)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out(" jsr prog8_lib.remainder_uw")
}
"+" -> asmgen.out(" jsr prog8_lib.add_w")
"-" -> asmgen.out(" jsr prog8_lib.sub_w")
"<<" -> throw AssemblyError("<< should not operate via stack")
">>" -> throw AssemblyError(">> should not operate via stack")
"<" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
">" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
"<=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
">=" -> asmgen.out(if(types==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
"<<" -> asmgen.out(" jsr math.shift_left_w")
">>" -> {
if(dt==DataType.UWORD)
asmgen.out(" jsr math.shift_right_uw")
else
asmgen.out(" jsr math.shift_right_w")
}
"<" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.less_uw" else " jsr prog8_lib.less_w")
">" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greater_uw" else " jsr prog8_lib.greater_w")
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
@@ -16,11 +16,11 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
internal fun translateFunctionCallStatement(stmt: IFunctionCall) {
val sub = stmt.target.targetSubroutine(program.namespace)!!
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any {it.statusflag!=null}
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
// functioncalls no longer return results on the stack, so simply ignore the results in the registers
if(preserveStatusRegisterAfterCall)
asmgen.out(" plp\t; restore status flags from call")
asmgen.out(" plp") // restore status flags from call
}
@@ -28,7 +28,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// output the code to setup the parameters and perform the actual call
// does NOT output the code to deal with the result values!
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
val saveX = CpuRegister.X in sub.asmClobbers || sub.regXasResult() || sub.regXasParam()
val saveX = sub.shouldSaveX()
if(saveX)
asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall, (stmt as Node).definingSubroutine()!!)
@@ -69,7 +69,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
asmgen.out(" jsr $subName")
if(preserveStatusRegisterAfterCall) {
asmgen.out(" php\t; save status flags from call")
asmgen.out(" php") // save status flags from call
// note: the containing statement (such as the FunctionCallStatement or the Assignment or the Expression)
// must take care of popping this value again at the end!
}
@@ -49,8 +49,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
lateinit var origAssign: AsmAssignment
init {
if(register!=null && datatype !in IntegerDatatypes)
throw AssemblyError("register must be integer type")
if(register!=null && datatype !in NumericDatatypes)
throw AssemblyError("register must be integer or float type")
}
companion object {
@@ -75,6 +75,8 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
RegisterOrPair.AX,
RegisterOrPair.AY,
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, scope, register = registers)
RegisterOrPair.FAC1,
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, scope, register = registers)
}
}
}
@@ -122,7 +122,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
assignMemoryByte(assign.target, null, value.addressExpression as IdentifierReference)
}
else -> {
asmgen.assignExpressionToVariable(value.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
assignExpressionToVariable(value.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope)
asmgen.out(" ldy #0 | lda (P8ZP_SCRATCH_W2),y")
assignRegisterByte(assign.target, CpuRegister.A)
}
@@ -138,11 +138,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
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 -> assignTypeCastedValue(assign.target, value.type, value.expression, assign)
is TypecastExpression -> assignTypeCastedValue(assign.target, value.type, value.expression, value)
is FunctionCall -> {
when (val sub = value.target.targetStatement(program.namespace)) {
is Subroutine -> {
val preserveStatusRegisterAfterCall = sub.asmReturnvaluesRegisters.any { it.statusflag != null }
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
asmgen.translateFunctionCall(value, preserveStatusRegisterAfterCall)
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.registerOrPair!=null }
when (returnValue.first) {
@@ -183,7 +183,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
if (preserveStatusRegisterAfterCall)
asmgen.out(" plp\t; restore status flags from call")
asmgen.out(" plp") // restore status flags from call
}
is BuiltinFunctionStatementPlaceholder -> {
val signature = BuiltinFunctions.getValue(sub.name)
@@ -211,6 +211,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
// Everything else just evaluate via the stack.
// (we can't use the assignment helper functions to do it via registers here,
// because the code here is the implementation of exactly that...)
if(value.parent is Return) {
if (this.asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used for return: $value target=${assign.target.kind} at ${value.position}")
}
asmgen.translateExpression(value)
if(assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes)
asmgen.signExtendStackLsb(assign.source.datatype)
@@ -227,11 +231,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origAssign: AsmAssignment) {
private fun assignTypeCastedValue(target: AsmAssignTarget, targetDt: DataType, value: Expression, origTypeCastExpression: TypecastExpression) {
val valueIDt = value.inferType(program)
if(!valueIDt.isKnown)
throw AssemblyError("unknown dt")
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
if(valueDt==targetDt)
throw AssemblyError("type cast to identical dt should have been removed")
when(value) {
is IdentifierReference -> {
if(targetDt in WordDatatypes) {
@@ -262,37 +269,62 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> {}
}
when(value) {
is IdentifierReference -> {
if(target.kind==TargetStorageKind.VARIABLE) {
val sourceDt = value.inferType(program).typeOrElse(DataType.STRUCT)
if (sourceDt != DataType.STRUCT)
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), sourceDt, origAssign.source.expression!!)
// special case optimizations
if(target.kind==TargetStorageKind.VARIABLE) {
if(value is IdentifierReference && valueDt != DataType.STRUCT)
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
when (valueDt) {
in ByteDatatypes -> {
assignExpressionToRegister(value, RegisterOrPair.A)
assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.A, valueDt)
}
in WordDatatypes -> {
assignExpressionToRegister(value, RegisterOrPair.AY)
assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.AY, valueDt)
}
DataType.FLOAT -> {
assignExpressionToRegister(value, RegisterOrPair.FAC1)
assignTypecastedFloatFAC1(target.asmVarname, targetDt)
}
in PassByReferenceDatatypes -> {
// str/array value cast (most likely to UWORD, take address-of)
assignExpressionToVariable(value, target.asmVarname, targetDt, null)
}
else -> throw AssemblyError("strange dt in typecast assign to var: $valueDt --> $targetDt")
}
is PrefixExpression -> {}
is BinaryExpression -> {}
is ArrayIndexedExpression -> {}
is TypecastExpression -> {}
is RangeExpr -> {}
is FunctionCall -> {}
else -> {
// TODO optimize the others further?
if(this.asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used for typecast: $value into $targetDt at ${value.position}")
}
return
}
// give up, do it via eval stack
asmgen.translateExpression(origAssign.source.expression!!)
// TODO optimize typecasts for more special cases?
// note: cannot use assignTypeCastedValue because that is ourselves :P
if(this.asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used for typecast: $value into $targetDt (target=${target.kind} at ${value.position}")
asmgen.translateExpression(origTypeCastExpression) // this performs the actual type cast in translateExpression(Typecast)
assignStackValue(target)
}
private fun assignTypecastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {
if(targetDt==DataType.FLOAT)
throw AssemblyError("typecast to identical type")
when(targetDt) {
DataType.UBYTE -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName")
DataType.BYTE -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName")
DataType.UWORD -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
DataType.WORD -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("weird type")
}
}
private fun assignTypeCastedIdentifier(targetAsmVarName: String, targetDt: DataType,
sourceAsmVarName: String, sourceDt: DataType,
origExpr: Expression) {
sourceAsmVarName: String, sourceDt: DataType) {
if(sourceDt == targetDt)
throw AssemblyError("typecast to identical value")
throw AssemblyError("typecast to identical type")
// also see: ExpressionAsmGen, fun translateExpression(typecast: TypecastExpression)
when(sourceDt) {
@@ -324,7 +356,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
}
DataType.UWORD, DataType.WORD -> {
DataType.UWORD -> {
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | stz $targetAsmVarName+1")
else
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
DataType.WORD -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, DataType.BYTE)
}
@@ -383,13 +421,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
DataType.FLOAT -> {
asmgen.out("""
lda #<$targetAsmVarName
ldy #>$targetAsmVarName
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda #<$sourceAsmVarName
ldy #>$sourceAsmVarName""")
asmgen.out(" lda #<$sourceAsmVarName | ldy #>$sourceAsmVarName")
when(targetDt) {
DataType.UBYTE -> asmgen.out(" jsr floats.cast_as_uw_into_ya | sty $targetAsmVarName")
DataType.BYTE -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName")
@@ -401,6 +433,146 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
DataType.STR -> {
if (targetDt != DataType.UWORD && targetDt == DataType.STR)
throw AssemblyError("cannot typecast a string into another incompatitble type")
TODO("assign typecasted string into target var")
}
else -> throw AssemblyError("weird type")
}
}
private fun assignTypeCastedRegisters(targetAsmVarName: String, targetDt: DataType,
regs: RegisterOrPair, sourceDt: DataType) {
if(sourceDt == targetDt)
throw AssemblyError("typecast to identical type")
// also see: ExpressionAsmGen, fun translateExpression(typecast: TypecastExpression)
when(sourceDt) {
DataType.UBYTE -> {
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
}
DataType.UWORD, DataType.WORD -> {
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
else
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
DataType.FLOAT -> {
when(regs) {
RegisterOrPair.A -> asmgen.out(" tay")
RegisterOrPair.X -> asmgen.out(" txa | tay")
RegisterOrPair.Y -> {}
else -> throw AssemblyError("non-byte regs")
}
asmgen.out("""
lda #<$targetAsmVarName
sta P8ZP_SCRATCH_W2
lda #>$targetAsmVarName
sta P8ZP_SCRATCH_W2+1
jsr floats.cast_from_ub""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.BYTE -> {
when(targetDt) {
DataType.UBYTE, DataType.BYTE -> {
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
}
DataType.UWORD -> {
if(CompilationTarget.instance.machine.cpu==CpuType.CPU65c02)
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
else
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
DataType.WORD -> {
when(regs) {
RegisterOrPair.A -> {}
RegisterOrPair.X -> asmgen.out(" txa")
RegisterOrPair.Y -> asmgen.out(" tya")
else -> throw AssemblyError("non-byte regs")
}
asmgen.signExtendAYlsb(sourceDt)
asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
}
DataType.FLOAT -> {
when(regs) {
RegisterOrPair.A -> {}
RegisterOrPair.X -> asmgen.out(" txa")
RegisterOrPair.Y -> asmgen.out(" tya")
else -> throw AssemblyError("non-byte regs")
}
asmgen.out("""
ldy #<$targetAsmVarName
sty P8ZP_SCRATCH_W2
ldy #>$targetAsmVarName
sty P8ZP_SCRATCH_W2+1
jsr floats.cast_from_b""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.UWORD -> {
when(targetDt) {
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" st${regs.toString().toLowerCase().first()} $targetAsmVarName")
}
DataType.WORD, DataType.UWORD -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
RegisterOrPair.XY -> asmgen.out(" stx $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("non-word regs")
}
}
DataType.FLOAT -> {
if(regs!=RegisterOrPair.AY)
throw AssemblyError("only supports AY here")
asmgen.out("""
pha
lda #<$targetAsmVarName
sta P8ZP_SCRATCH_W2
lda #>$targetAsmVarName
sta P8ZP_SCRATCH_W2+1
pla
jsr floats.cast_from_uw""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.WORD -> {
when(targetDt) {
DataType.BYTE, DataType.UBYTE -> {
asmgen.out(" st${regs.toString().toLowerCase().first()} $targetAsmVarName")
}
DataType.WORD, DataType.UWORD -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
RegisterOrPair.XY -> asmgen.out(" stx $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("non-word regs")
}
}
DataType.FLOAT -> {
if(regs!=RegisterOrPair.AY)
throw AssemblyError("only supports AY here")
asmgen.out("""
pha
lda #<$targetAsmVarName
sta P8ZP_SCRATCH_W2
lda #>$targetAsmVarName
sta P8ZP_SCRATCH_W2+1
pla
jsr floats.cast_from_w""")
}
else -> throw AssemblyError("weird type")
}
}
DataType.STR -> {
if (targetDt != DataType.UWORD && targetDt == DataType.STR)
throw AssemblyError("cannot typecast a string into another incompatitble type")
TODO("assign typecasted string into target var")
}
else -> throw AssemblyError("weird type")
}
@@ -527,6 +699,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> throw AssemblyError("can't assign word to single byte register")
}
}
DataType.FLOAT -> {
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.pop_float_fac1")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.pop_float_fac2")
else -> throw AssemblyError("can only assign float to Fac1 or 2")
}
}
else -> throw AssemblyError("weird dt")
}
}
@@ -545,10 +725,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
""")
}
TargetStorageKind.MEMORY -> {
throw AssemblyError("no asm gen for assign address $sourceName to memory word $target")
throw AssemblyError("can't store word into memory byte")
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign address $sourceName to array ${target.asmVarname}")
asmgen.out(" lda #<$sourceName | ldy #>$sourceName")
assignRegisterpairWord(target, RegisterOrPair.AY)
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
@@ -575,10 +756,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
when(target.datatype) {
DataType.UWORD -> {
asmgen.out("""
lda #<$sourceName
ldy #>$sourceName
sta ${target.asmVarname}
sty ${target.asmVarname}+1
lda #<$sourceName
ldy #>$sourceName
sta ${target.asmVarname}
sty ${target.asmVarname}+1
""")
}
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
@@ -702,7 +883,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignFAC1float(target: AsmAssignTarget) {
internal fun assignFAC1float(target: AsmAssignTarget) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
@@ -729,7 +910,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" jsr floats.set_array_float_from_fac1")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
TargetStorageKind.REGISTER -> {
if (target.register!! != RegisterOrPair.FAC1)
throw AssemblyError("can't assign Fac1 float to another fac register")
}
TargetStorageKind.STACK -> asmgen.out(" jsr floats.push_fac1")
}
}
@@ -762,7 +946,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" jsr floats.set_array_float")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" jsr floats.MOVFM")
RegisterOrPair.FAC2 -> asmgen.out(" jsr floats.CONUPK")
else -> throw AssemblyError("can only assign float to Fac1 or 2")
}
}
TargetStorageKind.STACK -> asmgen.out(" jsr floats.push_float")
}
}
@@ -803,7 +993,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
asmgen.out(" jsr floats.set_array_float")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr floats.MOVFM")
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr floats.CONUPK")
else -> throw AssemblyError("can only assign float to Fac1 or 2")
}
}
TargetStorageKind.STACK -> asmgen.out(" lda #<$sourceName | ldy #>$sourceName | jsr floats.push_float")
}
}
@@ -837,6 +1033,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
}
}
TargetStorageKind.STACK -> {
@@ -914,21 +1111,28 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val sourceName = asmgen.asmVariableName(bytevar)
when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda $sourceName
sta ${wordtarget.asmVarname}
lda #0
sta ${wordtarget.asmVarname}+1
""")
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${wordtarget.asmVarname}+1")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
if (wordtarget.constArrayIndexValue!=null) {
val scaledIdx = wordtarget.constArrayIndexValue!! * 2
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx | lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname}+$scaledIdx")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${wordtarget.asmVarname}+$scaledIdx+1")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+$scaledIdx+1")
}
else {
asmgen.loadScaledArrayIndexIntoRegister(wordtarget.array!!, wordtarget.datatype, CpuRegister.Y)
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | lda #0 | iny | sta ${wordtarget.asmVarname},y")
asmgen.out(" lda $sourceName | sta ${wordtarget.asmVarname},y | iny")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${wordtarget.asmVarname},y")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname},y")
}
}
TargetStorageKind.REGISTER -> {
@@ -940,18 +1144,17 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.STACK -> {
asmgen.out("""
lda $sourceName
sta P8ESTACK_LO,x
lda #0
sta P8ESTACK_HI,x
dex""")
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_HI,x | dex")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
}
else -> throw AssemblyError("target type isn't word")
}
}
private fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
require(target.datatype in ByteDatatypes)
when(target.kind) {
TargetStorageKind.VARIABLE -> {
@@ -961,22 +1164,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
storeRegisterInMemoryAddress(register, target.memory!!)
}
TargetStorageKind.ARRAY -> {
when {
target.constArrayIndexValue!=null -> {
when (register) {
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+${target.constArrayIndexValue}")
CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+${target.constArrayIndexValue}")
}
if (target.constArrayIndexValue!=null) {
when (register) {
CpuRegister.A -> asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
CpuRegister.X -> asmgen.out(" stx ${target.asmVarname}+${target.constArrayIndexValue}")
CpuRegister.Y -> asmgen.out(" sty ${target.asmVarname}+${target.constArrayIndexValue}")
}
else -> {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
asmgen.out(" ldy ${asmgen.asmVariableName(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y")
}
else {
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
asmgen.out(" ldy ${asmgen.asmVariableName(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y")
}
}
TargetStorageKind.REGISTER -> {
@@ -988,6 +1189,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AY -> { asmgen.out(" ldy #0") }
RegisterOrPair.AX -> { asmgen.out(" ldx #0") }
RegisterOrPair.XY -> { asmgen.out(" tax | ldy #0") }
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
}
CpuRegister.X -> when(target.register!!) {
RegisterOrPair.A -> { asmgen.out(" txa") }
@@ -996,6 +1198,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AY -> { asmgen.out(" txa | ldy #0") }
RegisterOrPair.AX -> { asmgen.out(" txa | ldx #0") }
RegisterOrPair.XY -> { asmgen.out(" ldy #0") }
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
}
CpuRegister.Y -> when(target.register!!) {
RegisterOrPair.A -> { asmgen.out(" tya") }
@@ -1004,6 +1207,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AY -> { asmgen.out(" tya | ldy #0") }
RegisterOrPair.AX -> { asmgen.out(" tya | ldx #0") }
RegisterOrPair.XY -> { asmgen.out(" tya | tax | ldy #0") }
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
}
}
}
@@ -1017,7 +1221,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
private fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
internal fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
require(target.datatype in NumericDatatypes)
if(target.datatype==DataType.FLOAT)
throw AssemblyError("float value should be from FAC1 not from registerpair memory pointer")
@@ -1032,20 +1236,30 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.ARRAY -> {
// TODO can be a bit more optimized if the array indexer is a number
when(regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
if (target.constArrayIndexValue!=null) {
val idx = target.constArrayIndexValue!! * 2
when (regs) {
RegisterOrPair.AX -> asmgen.out(" sta ${target.asmVarname}+$idx | stx ${target.asmVarname}+$idx+1")
RegisterOrPair.AY -> asmgen.out(" sta ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
RegisterOrPair.XY -> asmgen.out(" stx ${target.asmVarname}+$idx | sty ${target.asmVarname}+$idx+1")
else -> throw AssemblyError("expected reg pair")
}
}
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
asmgen.out("""
pla
sta ${target.asmVarname},y
dey
pla
sta ${target.asmVarname},y""")
else {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" pha | txa | pha")
RegisterOrPair.AY -> asmgen.out(" pha | tya | pha")
RegisterOrPair.XY -> asmgen.out(" txa | pha | tya | pha")
else -> throw AssemblyError("expected reg pair")
}
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UWORD, CpuRegister.Y, true)
asmgen.out("""
pla
sta ${target.asmVarname},y
dey
pla
sta ${target.asmVarname},y""")
}
}
TargetStorageKind.REGISTER -> {
when(regs) {
@@ -1156,6 +1370,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AX -> asmgen.out(" lda #${byte.toHex()} | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda #${byte.toHex()} | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx #${byte.toHex()} | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
}
TargetStorageKind.STACK -> {
asmgen.out("""
@@ -1192,14 +1407,23 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
TargetStorageKind.ARRAY -> {
if (target.array!!.indexer.indexNum!=null) {
val indexValue = target.array.indexer.constIndex()!! * 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
""")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out("""
stz ${target.asmVarname}+$indexValue
stz ${target.asmVarname}+$indexValue+1
stz ${target.asmVarname}+$indexValue+2
stz ${target.asmVarname}+$indexValue+3
stz ${target.asmVarname}+$indexValue+4
""")
else
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 {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
asmgen.out("""
@@ -1213,7 +1437,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
TargetStorageKind.REGISTER -> {
val floatConst = asmgen.getFloatAsmConst(float)
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
else -> throw AssemblyError("can only assign float to Fac1 or 2")
}
}
TargetStorageKind.STACK -> {
val floatConst = asmgen.getFloatAsmConst(float)
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
@@ -1270,7 +1501,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
TargetStorageKind.REGISTER -> throw AssemblyError("can't assign float to register")
TargetStorageKind.REGISTER -> {
val floatConst = asmgen.getFloatAsmConst(float)
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
else -> throw AssemblyError("can only assign float to Fac1 or 2")
}
}
TargetStorageKind.STACK -> {
val floatConst = asmgen.getFloatAsmConst(float)
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
@@ -1301,6 +1539,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AX -> asmgen.out(" lda ${address.toHex()} | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda ${address.toHex()} | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldy ${address.toHex()} | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
}
TargetStorageKind.STACK -> {
asmgen.out("""
@@ -1331,6 +1570,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
RegisterOrPair.AX -> asmgen.out(" ldx #0")
RegisterOrPair.AY -> asmgen.out(" ldy #0")
RegisterOrPair.XY -> asmgen.out(" tax | ldy #0")
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
}
}
TargetStorageKind.STACK -> {
@@ -1345,12 +1585,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
if (address != null) {
when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda ${address.toHex()}
sta ${wordtarget.asmVarname}
lda #0
sta ${wordtarget.asmVarname}+1
""")
asmgen.out(" lda ${address.toHex()} | sta ${wordtarget.asmVarname}")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${wordtarget.asmVarname}+1")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte at $address to array ${wordtarget.asmVarname}")
@@ -1362,12 +1601,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> throw AssemblyError("word regs can only be pair")
}
TargetStorageKind.STACK -> {
asmgen.out("""
lda ${address.toHex()}
sta P8ESTACK_LO,x
lda #0
sta P8ESTACK_HI,x
dex""")
asmgen.out(" lda ${address.toHex()} | sta P8ESTACK_LO,x")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_HI,x | dex")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
}
else -> throw AssemblyError("other types aren't word")
}
@@ -1375,7 +1613,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
when(wordtarget.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.loadByteFromPointerIntoA(identifier)
asmgen.out(" sta ${wordtarget.asmVarname} | lda #0 | sta ${wordtarget.asmVarname}+1")
asmgen.out(" sta ${wordtarget.asmVarname}")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz ${wordtarget.asmVarname}+1")
else
asmgen.out(" lda #0 | sta ${wordtarget.asmVarname}+1")
}
TargetStorageKind.ARRAY -> {
throw AssemblyError("no asm gen for assign memory byte $identifier to array ${wordtarget.asmVarname} ")
@@ -1391,7 +1633,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
TargetStorageKind.STACK -> {
asmgen.loadByteFromPointerIntoA(identifier)
asmgen.out(" sta P8ESTACK_LO,x | lda #0 | sta P8ESTACK_HI,x | dex")
asmgen.out(" sta P8ESTACK_LO,x")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz P8ESTACK_HI,x | dex")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x | dex")
}
else -> throw AssemblyError("other types aren't word")
}
@@ -1403,13 +1649,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val addressLv = addressExpr as? NumericLiteralValue
when {
addressLv != null -> {
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
}
addressExpr is IdentifierReference -> {
asmgen.storeByteIntoPointer(addressExpr, ldaInstructionArg)
}
else -> {
asmgen.assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.out(" ldy #0 | lda $ldaInstructionArg | sta (P8ZP_SCRATCH_W2),y")
}
}
@@ -1434,7 +1680,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
else -> {
asmgen.saveRegister(register, false, memoryAddress.definingSubroutine()!!)
asmgen.assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
asmgen.restoreRegister(CpuRegister.A, false)
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
}
@@ -7,6 +7,7 @@ import prog8.ast.statements.Subroutine
import prog8.compiler.AssemblyError
import prog8.compiler.target.CompilationTarget
import prog8.compiler.target.CpuType
import prog8.compiler.target.Cx16Target
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
import prog8.compiler.toHex
@@ -160,7 +161,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when {
valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable(addr.toHex(), DataType.UBYTE, operator, ident)
// TODO more specialized code for types such as memory read etc.
// TODO more specialized code for types such as memory read etc. -> inplaceModification_byte_memread_to_variable()
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
@@ -173,7 +174,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when {
valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident)
// TODO more specialized code for types such as memory read etc.
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_pointer(pointer, operator, value)
@@ -182,29 +182,92 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
else -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (1): ${memory.addressExpression::class.simpleName} at ${memory.addressExpression.position}") // TODO optimize...
asmgen.translateExpression(memory.addressExpression) // TODO directly into P8ZP_SCRATCH_W2?
asmgen.translateExpression(memory.addressExpression)
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
val zp = CompilationTarget.instance.machine.zeropage
when {
valueLv != null -> inplaceModification_byte_litval_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, ident)
memread != null -> inplaceModification_byte_memread_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, memread)
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, ident)
memread != null -> inplaceModification_byte_memread_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, value)
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
}
else -> inplaceModification_byte_value_to_variable(zp.SCRATCH_B1.toHex(), DataType.UBYTE, operator, value)
else -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
}
asmgen.out(" lda P8ZP_SCRATCH_B1 | jsr prog8_lib.write_byte_to_address_on_stack | inx")
}
}
}
TargetStorageKind.ARRAY -> {
if(asmgen.options.slowCodegenWarnings)
println("*** TODO optimize simple inplace array assignment ${target.array} $operator= $value")
assignmentAsmGen.translateNormalAssignment(target.origAssign) // TODO get rid of this fallback for the most common cases here
with(target.array!!.indexer) {
when {
indexNum!=null -> {
val targetVarName = "${target.asmVarname} + ${indexNum!!.number.toInt()*target.datatype.memorySize()}"
when(target.datatype) {
in ByteDatatypes -> {
when {
valueLv != null -> inplaceModification_byte_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable(targetVarName, target.datatype, operator, ident)
memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
}
else -> inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
}
}
in WordDatatypes -> {
when {
valueLv != null -> inplaceModification_word_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
ident != null -> inplaceModification_word_variable_to_variable(targetVarName, target.datatype, operator, ident)
memread != null -> inplaceModification_word_memread_to_variable(targetVarName, target.datatype, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
}
else -> inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
}
}
DataType.FLOAT -> {
when {
valueLv != null -> inplaceModification_float_litval_to_variable(targetVarName, operator, valueLv.toDouble(), target.scope!!)
ident != null -> inplaceModification_float_variable_to_variable(targetVarName, operator, ident, target.scope!!)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
}
else -> inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
}
}
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
}
}
indexVar!=null -> {
when(target.datatype) {
in ByteDatatypes -> {
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.A, null, program, asmgen)
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
}
in WordDatatypes -> {
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
}
DataType.FLOAT -> {
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.FAC1, null, program, asmgen)
val assign = AsmAssignment(target.origAssign.source, tgt, false, value.position)
assignmentAsmGen.translateNormalAssignment(assign)
assignmentAsmGen.assignFAC1float(target)
}
else -> throw AssemblyError("weird type to do in-place modification on ${target.datatype}")
}
}
else -> throw AssemblyError("indexer expression should have been replaced by auto indexer var")
}
}
}
TargetStorageKind.REGISTER -> TODO("reg in-place modification")
TargetStorageKind.STACK -> TODO("stack in-place modification")
@@ -228,26 +291,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
private fun inplaceModification_byte_value_to_pointer(pointervar: IdentifierReference, operator: String, value: Expression) {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (3): @(${pointervar.nameInSource.last()}) $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", DataType.UBYTE, null)
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
when (operator) {
// note: ** (power) operator requires floats.
"+" -> asmgen.out(" clc | adc P8ESTACK_LO+1,x")
"-" -> asmgen.out(" sec | sbc P8ESTACK_LO+1,x")
"*" -> asmgen.out(" pha | lda P8ESTACK_LO+1,x | tay | pla | jsr math.multiply_bytes | ldy #0")
"/" -> asmgen.out(" pha | lda P8ESTACK_LO+1,x | tay | pla | jsr math.divmod_ub_asm | tya | ldy #0")
"%" -> asmgen.out(" pha | lda P8ESTACK_LO+1,x | tay | pla | jsr math.divmod_ub_asm | ldy #0")
"+" -> asmgen.out(" clc | adc P8ZP_SCRATCH_B1")
"-" -> asmgen.out(" sec | sbc P8ZP_SCRATCH_B1")
"*" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.multiply_bytes | ldy #0")
"/" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.divmod_ub_asm | tya | ldy #0")
"%" -> asmgen.out(" ldy P8ZP_SCRATCH_B1 | jsr math.divmod_ub_asm | ldy #0")
"<<" -> {
asmgen.out("""
pha
lda P8ESTACK_LO+1,x
bne +
pla
rts
+ tay
pla
ldy P8ZP_SCRATCH_B1
beq +
- asl a
dey
bne -
@@ -255,28 +311,23 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
">>" -> {
asmgen.out("""
pha
lda P8ESTACK_LO+1,x
bne +
pla
rts
+ tay
pla
ldy P8ZP_SCRATCH_B1
beq +
- lsr a
dey
bne -
+""")
}
"&" -> asmgen.out(" and P8ESTACK_LO+1,x")
"^" -> asmgen.out(" eor P8ESTACK_LO+1,x")
"|" -> asmgen.out(" ora P8ESTACK_LO+1,x")
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_B1")
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_B1")
"^", "xor" -> asmgen.out(" eor P8ZP_SCRATCH_B1")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
if(ptrOnZp)
asmgen.out(" sta ($sourceName),y")
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
asmgen.out(" inx")
}
private fun inplaceModification_byte_variable_to_pointer(pointervar: IdentifierReference, operator: String, value: IdentifierReference) {
@@ -308,9 +359,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne -
+""")
}
"&" -> asmgen.out(" and $otherName")
"^" -> asmgen.out(" eor $otherName")
"|" -> asmgen.out(" ora $otherName")
"&", "and" -> asmgen.out(" and $otherName")
"|", "or" -> asmgen.out(" ora $otherName")
"^", "xor" -> asmgen.out(" eor $otherName")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
if(ptrOnZp)
@@ -389,7 +441,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
}
"&" -> {
"&", "and" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" and #$value")
if(ptrOnZp)
@@ -397,15 +449,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
"^" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" eor #$value")
if(ptrOnZp)
asmgen.out(" sta ($sourceName),y")
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
"|" -> {
"|", "or" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" ora #$value")
if(ptrOnZp)
@@ -413,6 +457,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
"^", "xor" -> {
val (ptrOnZp, sourceName) = asmgen.loadByteFromPointerIntoA(pointervar)
asmgen.out(" eor #$value")
if(ptrOnZp)
asmgen.out(" sta ($sourceName),y")
else
asmgen.out(" sta (P8ZP_SCRATCH_W1),y")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -476,18 +529,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
}
"&" -> {
"&", "and" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name")
}
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
"|" -> {
"|", "or" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name")
}
"^", "xor" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -542,27 +596,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
}
"&" -> asmgen.out(" lda $name | and $otherName | sta $name")
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name")
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name")
"and" -> asmgen.out("""
lda $name
and $otherName
beq +
lda #1
+ sta $name""")
"or" -> asmgen.out("""
lda $name
ora $otherName
beq +
lda #1
+ sta $name""")
"xor" -> asmgen.out("""
lda $name
eor $otherName
beq +
lda #1
+ sta $name""")
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name")
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name")
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -594,14 +631,24 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta $name""")
}
"<<" -> {
if(value>=8) asmgen.out(" lda #0 | sta $name")
else repeat(value) { asmgen.out(" asl $name") }
if(value>=8) {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name")
else
asmgen.out(" lda #0 | sta $name")
}
else repeat(value) { asmgen.out(" asl $name") }
}
">>" -> {
if(value>0) {
if (dt == DataType.UBYTE) {
if(value>=8) asmgen.out(" lda #0 | sta $name")
else repeat(value) { asmgen.out(" lsr $name") }
if(value>=8) {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name")
else
asmgen.out(" lda #0 | sta $name")
}
else repeat(value) { asmgen.out(" lsr $name") }
} else {
when {
value>=8 -> asmgen.out("""
@@ -616,14 +663,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ldy #$value
jsr math.lsr_byte_A
sta $name""")
else -> repeat(value) { asmgen.out(" lda $name | asl a | ror $name") }
else -> repeat(value) { asmgen.out(" lda $name | asl a | ror $name") }
}
}
}
}
"&" -> asmgen.out(" lda $name | and #$value | sta $name")
"^" -> asmgen.out(" lda $name | eor #$value | sta $name")
"|" -> asmgen.out(" lda $name | ora #$value | sta $name")
"&", "and" -> asmgen.out(" lda $name | and #$value | sta $name")
"|", "or" -> asmgen.out(" lda $name | ora #$value | sta $name")
"^", "xor" -> asmgen.out(" lda $name | eor #$value | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -809,9 +857,20 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
"<<" -> {
when {
value>=16 -> asmgen.out(" lda #0 | sta $name | sta $name+1")
value==8 -> asmgen.out(" lda $name | sta $name+1 | lda #0 | sta $name")
value>2 -> asmgen.out("""
value>=16 -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name | stz $name+1")
else
asmgen.out(" lda #0 | sta $name | sta $name+1")
}
value==8 -> {
asmgen.out(" lda $name | sta $name+1")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name")
else
asmgen.out(" lda #0 | sta $name")
}
value>3 -> asmgen.out("""
ldy #$value
- asl $name
rol $name+1
@@ -825,8 +884,19 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
if (value > 0) {
if(dt==DataType.UWORD) {
when {
value>=16 -> asmgen.out(" lda #0 | sta $name | sta $name+1")
value==8 -> asmgen.out(" lda $name+1 | sta $name | lda #0 | sta $name+1")
value>=16 -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name | stz $name+1")
else
asmgen.out(" lda #0 | sta $name | sta $name+1")
}
value==8 -> {
asmgen.out(" lda $name+1 | sta $name")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
value>2 -> asmgen.out("""
ldy #$value
- lsr $name+1
@@ -868,47 +938,48 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
}
"&" -> {
"&", "and" -> {
when {
value == 0 -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name | stz $name+1")
asmgen.out(" stz $name | stz $name+1")
else
asmgen.out(" lda #0 | sta $name | sta $name+1")
asmgen.out(" lda #0 | sta $name | sta $name+1")
}
value and 255 == 0 -> {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name")
asmgen.out(" stz $name")
else
asmgen.out(" lda #0 | sta $name")
asmgen.out(" lda $name+1 | and #>$value | sta $name+1")
asmgen.out(" lda #0 | sta $name")
asmgen.out(" lda $name+1 | and #>$value | sta $name+1")
}
value < 0x0100 -> {
asmgen.out(" lda $name | and #$value | sta $name")
asmgen.out(" lda $name | and #$value | sta $name")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
else -> asmgen.out(" lda $name | and #<$value | sta $name | lda $name+1 | and #>$value | sta $name+1")
}
}
"^" -> {
"|", "or" -> {
when {
value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
value < 0x0100 -> asmgen.out(" lda $name | eor #$value | sta $name")
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
value < 0x0100 -> asmgen.out(" lda $name | ora #$value | sta $name")
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
}
}
"|" -> {
"^", "xor" -> {
when {
value == 0 -> {}
value and 255 == 0 -> asmgen.out(" lda $name+1 | ora #>$value | sta $name+1")
value < 0x0100 -> asmgen.out(" lda $name | ora #$value | sta $name")
else -> asmgen.out(" lda $name | ora #<$value | sta $name | lda $name+1 | ora #>$value | sta $name+1")
value and 255 == 0 -> asmgen.out(" lda $name+1 | eor #>$value | sta $name+1")
value < 0x0100 -> asmgen.out(" lda $name | eor #$value | sta $name")
else -> asmgen.out(" lda $name | eor #<$value | sta $name | lda $name+1 | eor #>$value | sta $name+1")
}
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -970,11 +1041,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta $name+1""")
}
"*" -> {
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
else
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
asmgen.out("""
lda $otherName
sta P8ZP_SCRATCH_W1
lda #0
sta P8ZP_SCRATCH_W1+1
lda $name
ldy $name+1
jsr math.multiply_words
@@ -1018,13 +1090,18 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
}
}
"&" -> {
asmgen.out(" lda $otherName | and $name | sta $name")
if(dt in WordDatatypes)
asmgen.out(" lda #0 | sta $name+1")
"&", "and" -> {
asmgen.out(" lda $otherName | and $name | sta $name")
if(dt in WordDatatypes) {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
}
"^" -> asmgen.out(" lda $otherName | eor $name | sta $name")
"|" -> asmgen.out(" lda $otherName | ora $name | sta $name")
"|", "or" -> asmgen.out(" lda $otherName | ora $name | sta $name")
"^", "xor" -> asmgen.out(" lda $otherName | eor $name | sta $name")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1032,8 +1109,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
// the value is a proper 16-bit word, so use both bytes of it.
when (operator) {
// note: ** (power) operator requires floats.
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
"+" -> asmgen.out(" lda $name | clc | adc $otherName | sta $name | lda $name+1 | adc $otherName+1 | sta $name+1")
"-" -> asmgen.out(" lda $name | sec | sbc $otherName | sta $name | lda $name+1 | sbc $otherName+1 | sta $name+1")
"*" -> {
asmgen.out("""
lda $otherName
@@ -1095,9 +1172,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
""")
}
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
"^" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
"|" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
"&", "and" -> asmgen.out(" lda $name | and $otherName | sta $name | lda $name+1 | and $otherName+1 | sta $name+1")
"|", "or" -> asmgen.out(" lda $name | ora $otherName | sta $name | lda $name+1 | ora $otherName+1 | sta $name+1")
"^", "xor" -> asmgen.out(" lda $name | eor $otherName | sta $name | lda $name+1 | eor $otherName+1 | sta $name+1")
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1116,11 +1194,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
throw AssemblyError("unknown dt")
val valueDt = valueiDt.typeOrElse(DataType.STRUCT)
// TODO can use registers instead of stack value?
fun multiplyWord() {
fun multiplyVarByWordInAY() {
asmgen.out("""
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda $name
@@ -1133,51 +1208,36 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
""")
}
// TODO can use registers instead of stack value?
fun divideWord() {
if (dt == DataType.WORD) {
asmgen.out("""
fun divideVarByWordInAY() {
asmgen.out("""
pha
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
jsr math.divmod_w_asm
sta $name
sty $name+1
""")
} else {
asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
jsr math.divmod_uw_asm
sta $name
sty $name+1
""")
}
lda $name+1
sta P8ZP_SCRATCH_W1+1
pla""")
if (dt == DataType.WORD)
asmgen.out(" jsr math.divmod_w_asm")
else
asmgen.out(" jsr math.divmod_uw_asm")
asmgen.out(" sta $name | sty $name+1")
}
// TODO can use registers instead of stack value?
fun remainderWord() {
fun remainderVarByWordInAY() {
if(dt==DataType.WORD)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out("""
pha
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda P8ESTACK_LO+1,x
ldy P8ESTACK_HI+1,x
lda $name+1
sta P8ZP_SCRATCH_W1+1
pla
jsr math.divmod_uw_asm
lda P8ZP_SCRATCH_W2
ldy P8ZP_SCRATCH_W2+1
sta $name
lda P8ZP_SCRATCH_W2+1
sta $name+1
sty $name+1
""")
}
@@ -1187,14 +1247,12 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when (operator) {
// note: ** (power) operator requires floats.
"+" -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt, null)
if(valueDt==DataType.UBYTE)
asmgen.out("""
lda $name
clc
adc P8ESTACK_LO+1,x
adc P8ZP_SCRATCH_B1
sta $name
bcc +
inc $name+1
@@ -1202,7 +1260,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else
asmgen.out("""
ldy #0
lda P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_B1
bpl +
dey ; sign extend
+ clc
@@ -1211,63 +1269,53 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
tya
adc $name+1
sta $name+1""")
asmgen.out(" inx")
}
"-" -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", valueDt, null)
if(valueDt==DataType.UBYTE)
asmgen.out("""
lda $name
sec
sbc P8ESTACK_LO+1,x
sbc P8ZP_SCRATCH_REG
sta $name
bcc +
bcs +
dec $name+1
+""")
else
asmgen.out("""
ldy #0
lda P8ESTACK_LO+1,x
lda P8ZP_SCRATCH_REG
bpl +
dey ; sign extend
+ sty P8ZP_SCRATCH_B1
lda $name
sec
sbc P8ESTACK_LO+1,x
sbc P8ZP_SCRATCH_REG
sta $name
lda $name+1
sbc P8ZP_SCRATCH_B1
sta $name+1""")
asmgen.out(" inx")
}
"*" -> {
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.signExtendStackLsb(valueDt)
multiplyWord()
asmgen.out(" inx")
// TODO use an optimized word * byte multiplication routine
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.signExtendAYlsb(valueDt)
multiplyVarByWordInAY()
}
"/" -> {
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.signExtendStackLsb(valueDt)
divideWord()
asmgen.out(" inx")
// TODO use an optimized word / byte divmod routine
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.signExtendAYlsb(valueDt)
divideVarByWordInAY()
}
"%" -> {
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.signExtendStackLsb(valueDt)
remainderWord()
asmgen.out(" inx")
// TODO use an optimized word / byte divmod routine
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.signExtendAYlsb(valueDt)
remainderVarByWordInAY()
}
"<<" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.Y)
@@ -1300,20 +1348,25 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
bne -
+""")
}
"&" -> {
"&", "and" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" and $name | sta $name")
if(dt in WordDatatypes)
asmgen.out(" lda #0 | sta $name+1")
if(dt in WordDatatypes) {
if(CompilationTarget.instance.machine.cpu == CpuType.CPU65c02)
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
}
"^" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
"|" -> {
"|", "or" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" ora $name | sta $name")
}
"^", "xor" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out(" eor $name | sta $name")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1322,57 +1375,39 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when (operator) {
// note: ** (power) operator requires floats.
"+" -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.out(" lda $name | clc | adc P8ESTACK_LO+1,x | sta $name | lda $name+1 | adc P8ESTACK_HI+1,x | sta $name+1 | inx")
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" clc | adc $name | sta $name | tya | adc $name+1 | sta $name+1")
}
"-" -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.out(" lda $name | sec | sbc P8ESTACK_LO+1,x | sta $name | lda $name+1 | sbc P8ESTACK_HI+1,x | sta $name+1 | inx")
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", valueDt, null)
asmgen.out(" lda $name | sec | sbc P8ZP_SCRATCH_W1 | sta $name | lda $name+1 | sbc P8ZP_SCRATCH_W1+1 | sta $name+1")
}
"*" -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
multiplyWord()
asmgen.out(" inx")
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
multiplyVarByWordInAY()
}
"/" -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
divideWord()
asmgen.out(" inx")
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
divideVarByWordInAY()
}
"%" -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
remainderWord()
asmgen.out(" inx")
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
remainderVarByWordInAY()
}
"<<", ">>" -> throw AssemblyError("shift by a word value not supported, max is a byte")
"&" -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.out(" lda $name | and P8ESTACK_LO+1,x | sta $name | lda $name+1 | and P8ESTACK_HI+1,x | sta $name+1 | inx")
"&", "and" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" and $name | sta $name | tya | and $name+1 | sta $name+1")
}
"^" -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.out(" lda $name | eor P8ESTACK_LO+1,x | sta $name | lda $name+1 | eor P8ESTACK_HI+1,x | sta $name+1 | inx")
"|", "or" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" ora $name | sta $name | tya | ora $name+1 | sta $name+1")
}
"|" -> {
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (4): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.out(" lda $name | ora P8ESTACK_LO+1,x | sta $name | lda $name+1 | ora P8ESTACK_HI+1,x | sta $name+1 | inx")
"^", "xor" -> {
asmgen.assignExpressionToRegister(value, RegisterOrPair.AY)
asmgen.out(" eor $name | sta $name | tya | eor $name+1 | sta $name+1")
}
in comparisonOperators -> TODO("in-place modification for $operator")
else -> throw AssemblyError("invalid operator for in-place modification $operator")
}
}
@@ -1381,12 +1416,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression, scope: Subroutine) {
// this should be the last resort for code generation for this,
// because the value is evaluated onto the eval stack (=slow).
if(asmgen.options.slowCodegenWarnings)
println("warning: slow stack evaluation used (2): $name $operator= ${value::class.simpleName} at ${value.position}") // TODO
asmgen.translateExpression(value)
asmgen.out(" jsr floats.pop_float_fac1")
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
"**" -> {
@@ -1425,6 +1455,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV
""")
}
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
}
asmgen.out("""
@@ -1444,14 +1475,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
"**" -> {
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$otherName
ldy #>$otherName
jsr floats.FPWR
""")
if(CompilationTarget.instance is Cx16Target) {
// cx16 doesn't have FPWR() only FPWRT()
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$otherName
ldy #>$otherName
jsr floats.MOVFM
jsr floats.FPWRT
""")
} else
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$otherName
ldy #>$otherName
jsr floats.FPWR
""")
}
"+" -> {
asmgen.out("""
@@ -1493,6 +1536,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV
""")
}
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
}
// store Fac1 back into memory
@@ -1509,14 +1553,26 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegister(CpuRegister.X, false, scope)
when (operator) {
"**" -> {
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$constValueName
ldy #>$constValueName
jsr floats.FPWR
""")
if(CompilationTarget.instance is Cx16Target) {
// cx16 doesn't have FPWR() only FPWRT()
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$constValueName
ldy #>$constValueName
jsr floats.MOVFM
jsr floats.FPWRT
""")
} else
asmgen.out("""
lda #<$name
ldy #>$name
jsr floats.CONUPK
lda #<$constValueName
ldy #>$constValueName
jsr floats.FPWR
""")
}
"+" -> {
if (value == 0.0)
@@ -1565,6 +1621,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
jsr floats.FDIV
""")
}
in comparisonOperators -> TODO("in-place float modification for $operator")
else -> throw AssemblyError("invalid operator for in-place float modification $operator")
}
// store Fac1 back into memory
@@ -38,8 +38,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
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 cmdline = listOf(emulator, "-scale", "2", "-run", "-prg", "$programName.prg")
val processb = ProcessBuilder(cmdline).inheritIO()
val process: Process
try {
@@ -141,6 +141,7 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("set_irqd" , false, emptyList(), null),
FSignature("clear_irqd" , false, emptyList(), null),
FSignature("read_flags" , false, emptyList(), DataType.UBYTE),
FSignature("progend" , true, emptyList(), DataType.UWORD),
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
FSignature("memcopy" , false, listOf(
FParam("from", IterableDatatypes + DataType.UWORD),
@@ -155,6 +156,7 @@ private val functionSignatures: List<FSignature> = listOf(
FParam("numwords", setOf(DataType.UWORD)),
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
FSignature("strlen" , true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen),
FSignature("strcopy" , false, listOf(FParam("from", IterableDatatypes + DataType.UWORD), FParam("to", IterableDatatypes + DataType.UWORD)), DataType.UBYTE),
FSignature("substr" , false, listOf(
FParam("source", IterableDatatypes + DataType.UWORD),
FParam("target", IterableDatatypes + DataType.UWORD),
@@ -353,7 +355,7 @@ private fun builtinStrlen(args: List<Expression>, position: Position, program: P
val argument=args[0]
if(argument is StringLiteralValue)
return NumericLiteralValue.optimalInteger(argument.value.length, argument.position)
val vardecl = (argument as IdentifierReference).targetVarDecl(program.namespace)
val vardecl = (argument as? IdentifierReference)?.targetVarDecl(program.namespace)
if(vardecl!=null) {
if(vardecl.datatype!=DataType.STR && vardecl.datatype!=DataType.UWORD)
throw SyntaxError("strlen must have string argument", position)
@@ -7,6 +7,7 @@ import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.*
import kotlin.math.pow
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
@@ -97,6 +98,43 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
val leftconst = expr.left.constValue(program)
val rightconst = expr.right.constValue(program)
val modifications = mutableListOf<IAstModification>()
if(expr.operator == "**" && leftconst!=null) {
// optimize various simple cases of ** :
// optimize away 1 ** x into just 1 and 0 ** x into just 0
// optimize 2 ** x into (1<<x) if both operands are integer.
val leftDt = leftconst.inferType(program).typeOrElse(DataType.STRUCT)
when (leftconst.number.toDouble()) {
0.0 -> {
val value = NumericLiteralValue(leftDt, 0, expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
}
1.0 -> {
val value = NumericLiteralValue(leftDt, 1, expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
}
2.0 -> {
if(rightconst!=null) {
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number.toDouble()), expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
} else {
val rightDt = expr.right.inferType(program).typeOrElse(DataType.STRUCT)
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
val targetDt =
when (parent) {
is Assignment -> parent.target.inferType(program).typeOrElse(DataType.STRUCT)
is VarDecl -> parent.datatype
else -> leftDt
}
val one = NumericLiteralValue(targetDt, 1, expr.position)
val shift = BinaryExpression(one, "<<", expr.right, expr.position)
modifications += IAstModification.ReplaceNode(expr, shift, parent)
}
}
}
}
}
val subExpr: BinaryExpression? = when {
leftconst!=null -> expr.right as? BinaryExpression
@@ -111,7 +149,8 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
val change = groupTwoConstsTogether(expr, subExpr,
leftconst != null, rightconst != null,
subleftconst != null, subrightconst != null)
return change?.let { listOf(it) } ?: noModifications
if(change!=null)
modifications += change
}
}
@@ -119,10 +158,10 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
if(leftconst != null && rightconst != null) {
val evaluator = ConstExprEvaluator()
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
return listOf(IAstModification.ReplaceNode(expr, result, parent))
modifications += IAstModification.ReplaceNode(expr, result, parent)
}
return noModifications
return modifications
}
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
@@ -6,6 +6,7 @@ import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.processing.AstWalker
import prog8.ast.processing.IAstModification
import prog8.ast.statements.Assignment
import kotlin.math.abs
import kotlin.math.log2
import kotlin.math.pow
@@ -98,6 +99,12 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (leftVal != null && expr.operator in associativeOperators && rightVal == null)
return listOf(IAstModification.SwapOperands(expr))
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
if (expr.operator in associativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
if(parent !is Assignment || !(expr.left isSameAs parent.target))
return listOf(IAstModification.SwapOperands(expr))
}
// X + (-A) --> X - A
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
return listOf(IAstModification.ReplaceNode(
@@ -338,7 +345,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (leftVal == null && rightVal == null)
return null
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
if (rightVal2 != null) {
// right value is a constant, see if we can optimize
val rightConst: NumericLiteralValue = rightVal2
@@ -562,7 +569,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
if (leftVal == null && rightVal == null)
return null
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
if (rightVal2 != null) {
// right value is a constant, see if we can optimize
val leftValue: Expression = expr2.left
@@ -682,17 +689,17 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
return null
}
private fun reorderAssociative(expr: BinaryExpression, leftVal: NumericLiteralValue?): ReorderedAssociativeBinaryExpr {
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteralValue?): BinExprWithConstants {
if (expr.operator in associativeOperators && leftVal != null) {
// swap left and right so that right is always the constant
val tmp = expr.left
expr.left = expr.right
expr.right = tmp
return ReorderedAssociativeBinaryExpr(expr, expr.right.constValue(program), leftVal)
return BinExprWithConstants(expr, expr.right.constValue(program), leftVal)
}
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program))
return BinExprWithConstants(expr, leftVal, expr.right.constValue(program))
}
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
private data class BinExprWithConstants(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
}
@@ -323,7 +323,7 @@ internal class StatementOptimizer(private val program: Program,
val op1 = binExpr.operator
val op2 = rExpr.operator
if(rExpr.left is NumericLiteralValue && op2 in setOf("+", "*", "&", "|")) {
if(rExpr.left is NumericLiteralValue && op2 in associativeOperators) {
// associative operator, make sure the constant numeric value is second (right)
return listOf(IAstModification.SwapOperands(rExpr))
}
+1 -1
View File
@@ -102,7 +102,7 @@ internal class ModuleImporter {
private fun discoverImportedModuleFile(name: String, source: Path, position: Position?): Path {
val fileName = "$name.p8"
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent)
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent ?: Path.of("."))
val propPath = System.getProperty("prog8.libdir")
if(propPath!=null)
+22 -32
View File
@@ -38,6 +38,27 @@ This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/license
:alt: Fully playable tetris clone
Language features
-----------------
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
The generated output is a machine code program runnable on actual 8-bit 6502 hardware.
- Provide a very convenient edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator.
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
- Modular programming and scoping via modules, code blocks, and subroutines.
- Provide high level programming constructs but at the same time stay close to the metal;
still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters
- Arbitrary number of subroutine parameters, Complex nested expressions are possible
- No stack frame allocations because parameters and local variables are automatically allocated statically
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
- Variable data types include signed and unsigned bytes and words, arrays, strings and floats.
- High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others)
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
Code example
------------
@@ -104,37 +125,6 @@ when the exact same program is compiled for the Commander X16 target, and run on
Design principles and features
------------------------------
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
The generated output is a machine code program runnable on actual 8-bit 6502 hardware.
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
- 'One statement per line' code, resulting in clear readable programs.
- Modular programming and scoping via modules, code blocks, and subroutines.
- Provide high level programming constructs but at the same time stay close to the metal;
still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters
- Arbitrary number of subroutine parameters
- Complex nested expressions are possible
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
- Values are typed. Available data types include signed and unsigned bytes and words, arrays, strings and floats.
- No dynamic memory allocation or sizing! All variables stay fixed size as determined at compile time.
- Provide various quality of life language features and library subroutines specifically for the target platform.
- Provide a very convenient edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator.
- Arbitrary control flow jumps and branches are possible,
and will usually translate directly into the appropriate single 6502 jump/branch instruction.
- There are no complicated built-in error handling or overflow checks, you'll have to take care
of this yourself if required. This keeps the language and code simple and efficient.
- The compiler tries to optimize the program and generated code a bit, but hand-tuning of the
performance or space-critical parts will likely still be required. This is supported by
the ability to easily write embedded assembly code directly in the program source code.
- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``substr``, ``sort`` and ``reverse`` (and others)
- Assembling the generated code into a program wil be done by an external cross-assembler tool.
- If you only use standard kernel and prog8 library routines, it is possible to compile the *exact same program* for both machines (just change the compiler target flag)!
.. _requirements:
Required tools
@@ -146,7 +136,7 @@ A recent precompiled .exe for Windows can be obtained from my `clone <https://gi
*You need at least version 1.55.2257 of this assembler to correctly use the breakpoints feature.*
It's possible to use older versions, but it is very likely that the automatic Vice breakpoints won't work with them.
A **Java runtime (jre or jdk), version 8 or newer** is required to run the prog8 compiler itself.
A **Java runtime (jre or jdk), version 11 or newer** is required to run the prog8 compiler itself.
If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK in their packages repository instead.
For Windows it's possible to get that as well; check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ .
For MacOS you can use the Homebrew system to install a recent version of OpenJDK.
+11 -1
View File
@@ -54,7 +54,8 @@ diskio
------
Provides several routines that deal with disk drive I/O, such as:
- show directory
- list files on disk, optionally filtering by prefix or suffix
- show disk directory as-is
- display disk drive status
- load and save data from and to the disk
- delete and rename files on the disk
@@ -88,6 +89,15 @@ A 'fun' module that contains the Commander X16 logo and that allows you
to print it anywhere on the screen.
c64colors
---------
Available for the CommanderX16 target, a module that contains a few better
color palettes for how the colors of the VIC-II looked on the Commodore-64.
There are subroutines to activate one of the several palettes of your liking.
The Commander X16's default colors for this (the first 16 colors) are too saturated
and are quite different than how a C-64 looked.
prog8_lib
---------
Low level language support. You should not normally have to bother with this directly.
+25 -6
View File
@@ -309,18 +309,21 @@ read the syntax reference on strings.
.. hint::
Strings and uwords (=memory address) can often be interchanged.
Strings/arrays and uwords (=memory address) can often be interchanged.
An array of strings is actually an array of uwords where every element is the memory
address of the string. You can pass a memory address to assembly functions
that require a string as an argument.
For regular assignments you still need to use an explicit ``&`` (address-of) to take
the address of the string or array.
.. caution::
It's probably best to avoid changing strings after they've been created. This
includes changing certain letters by index, or by assigning a new value, or by
It's probably best to avoid changing the contents in strings and treat them as static.
This includes changing certain letters by index, or by assigning a new value, or by
modifying the string via other means for example ``substr`` function and its cousins.
This is because if your program exits and is restarted (without loading it again),
it will then start working with the changed strings instead of the original ones!
The same is true for arrays.
This is because the changes persist in memory. If your program exits and is restarted
(without reloading it from disk), it will then start working with the modified strings
instead of the original ones!
The same is true for arrays! So be careful to (re)initialize them if needed.
Structs
@@ -811,6 +814,12 @@ substr(source, target, start, length)
Also, you have to make sure yourself that start and length are within bounds of the strings.
Modifies in-place, doesn't return a value (so can't be used in an expression).
strcopy(from, to)
Copy a string to another, overwriting that one. Returns the length of the string that was copied.
Often you don't have to call this explicitly and can just write ``string1 = string2``
but this function is useful if you're dealing with addresses for instance.
Miscellaneous
^^^^^^^^^^^^^
exit(returncode)
@@ -827,6 +836,12 @@ mkword(msb, lsb)
Efficiently create a word value from two bytes (the msb and the lsb). Avoids multiplication and shifting.
So mkword($80, $22) results in $8022.
.. note::
The arguments to the mkword() function are in 'natural' order that is first the msb then the lsb.
Don't get confused by how the system actually stores this 16-bit word value in memory (which is
in little-endian format, so lsb first then msb)
rnd()
returns a pseudo-random byte from 0..255
@@ -891,6 +906,10 @@ set_irqd() / clear_irqd()
swap(x, y)
Swap the values of numerical variables (or memory locations) x and y in a fast way.
progend()
Returns the last address of the program in memory + 1.
Can be used to load dynamic data after the program, instead of hardcoding something.
Library routines
----------------
+3 -1
View File
@@ -2,9 +2,11 @@
TODO
====
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
- Cx16 target: support full-screen 640x480 and 320x240 graphics? That requires our own custom graphics routines though to draw lines.
- hoist all variable declarations up to the subroutine scope *before* even the constant folding takes place (to avoid undefined symbol errors when referring to a variable from another nested scope in the subroutine)
- 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)
- see if we can group some errors together for instance the (now single) errors about unidentified symbols
- use VIC banking to move up the graphics bitmap memory location. Move it to $e000 under the kernal rom?
- some support for recursive subroutines?
- via %option recursive?: allocate all params and local vars on estack, don't allow nested subroutines, can begin by first not allowing any local variables just fixing the parameters
+4
View File
@@ -324,6 +324,10 @@ main {
txt.print(result)
txt.chrout('\n')
void strcopy(s2, s1)
txt.print_ub(99+strcopy(s2,s1))
txt.chrout('\n')
test_stack.test()
}
+6 -2
View File
@@ -1,5 +1,6 @@
%import floats
%import textio
%import test_stack
%zeropage basicsafe
main {
@@ -31,6 +32,8 @@ main {
minus_float(0,0,0)
minus_float(2.5,1.5,1.0)
minus_float(-1.5,3.5,-5.0)
test_stack.test()
}
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
@@ -97,10 +100,11 @@ main {
float r = a1-a2
if abs(r-c)<0.00001
txt.print(" ok ")
else
else {
txt.print("err! ")
}
txt.print("float ")
txt.print(" float ")
floats.print_f(a1)
txt.print(" - ")
floats.print_f(a2)
+9 -3
View File
@@ -4,6 +4,12 @@
%import test_stack
%zeropage basicsafe
; note: The flickering in the scrolling is caused by the CPU requiring
; too long to scroll the characters + the colors in course scroll.
; This takes nearly a full frame to accomplish, and causes tearing.
; It's very difficult to remove this flicker: it requires double buffering
; and splitting the coarse character scrolling on multiple phases...
main {
ubyte perform_scroll = false
@@ -17,7 +23,7 @@ main {
c64.SCROLX &= %11110111 ; 38 column mode
c64.set_rasterirq(1) ; enable animation
c64.set_rasterirq(200) ; enable animation
ubyte target_height = 10
ubyte active_height = 24
@@ -123,10 +129,10 @@ spritedata $0f00 {
irq {
ubyte smoothx=7
ubyte smoothx=0
sub irq() {
smoothx = (smoothx-1) & 7
main.perform_scroll = smoothx==0
main.perform_scroll = smoothx==7
c64.SCROLX = (c64.SCROLX & %11111000) | smoothx
}
}
+913
View File
@@ -0,0 +1,913 @@
%import textio
%import floats
%zeropage basicsafe
%import test_stack
main {
sub start() {
word_less()
word_lessequal()
word_greaterequal()
word_greater()
uword_lessequal()
}
sub uword_lessequal() {
uword lessvar
uword comparevar
txt.print("uword <=\n")
txt.print_uw(65535)
txt.chrout('\n')
check_lesseq_uw(0, 65535)
txt.print_uw(0)
txt.chrout('\n')
check_not_lesseq_uw(65535, 0)
comparevar = 65535
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-2
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-254
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-255
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-256
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-5000
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 32769
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 32768
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 1
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 0
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 11111
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 255
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 256
txt.print_uw(comparevar)
txt.chrout('\n')
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
test_stack.test()
return
sub check_lesseq_uw(uword w1, uword w2) {
uword zero = 0
ubyte error=0
ubyte ub = w1<=w2
if not ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_uw(w1)
txt.print(" <= ")
txt.print_uw(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_lesseq_uw(uword w1, uword w2) {
uword zero = 0
ubyte error=0
ubyte ub = w1<=w2
if ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_uw(w1)
txt.print(" not <= ")
txt.print_uw(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_greater() {
word biggervar
word comparevar
txt.print("word >\n")
txt.print_w(-32767)
txt.chrout('\n')
check_greater_w(32767, -32767)
txt.print_w(32766)
txt.chrout('\n')
check_not_greater_w(-32766, 32766)
comparevar = 32765
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 32760
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
test_stack.test()
return
sub check_greater_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>(w2+zero)
if not ub {
error++
txt.print("ubz!")
}
ub = w1>w2
if not ub {
error++
txt.print("ub!")
}
if w1>(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" > ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_greater_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>w2
if ub {
error++
txt.print("ub!")
}
if w1>(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if w1>w2 {
error++
txt.print("c2!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not > ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_greaterequal() {
word biggervar
word comparevar
txt.print("word >=\n")
txt.print_w(-32767)
txt.chrout('\n')
check_greatereq_w(32767, -32767)
txt.print_w(32766)
txt.chrout('\n')
check_not_greatereq_w(-32766, 32766)
comparevar = 32765
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.chrout('\n')
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
test_stack.test()
return
sub check_greatereq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>=(w2+zero)
if not ub {
error++
txt.print("ubz!")
}
ub = w1>=w2
if not ub {
error++
txt.print("ub!")
}
if w1>=(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" >= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_greatereq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1>=w2
if ub {
error++
txt.print("ub!")
}
if w1>=(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if w1>=w2 {
error++
txt.print("c2!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not >= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_lessequal() {
word lessvar
word comparevar
txt.print("word <=\n")
txt.print_w(32767)
txt.chrout('\n')
check_lesseq_w(-32767, 32767)
txt.print_w(-32767)
txt.chrout('\n')
check_not_lesseq_w(32767, -32767)
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -32768
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
test_stack.test()
return
sub check_lesseq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<=w2
if not ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" <= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_lesseq_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<=w2
if ub {
error++
txt.print("ub!")
}
if w1<=(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not <= ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
sub word_less() {
word lessvar
word comparevar
txt.print("word <\n")
txt.print_w(32767)
txt.chrout('\n')
check_less_w(-32767, 32767)
txt.print_w(-32767)
txt.chrout('\n')
check_not_less_w(32767, -32767)
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -1 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -3 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -255 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -256 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -257 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in -5001 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 0 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 254 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 255 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 256 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -32768
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto -32768 {
check_not_less_w(lessvar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto -1 {
check_not_less_w(lessvar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto 0 {
check_not_less_w(lessvar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.chrout('\n')
for lessvar in 32766 downto 11111 {
check_not_less_w(lessvar, comparevar)
}
test_stack.test()
return
sub check_less_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<w2
if not ub {
error++
txt.print("ub!")
}
if w1<(w2+zero) {
zero = 0 ; dummy
} else {
error++
txt.print("c!")
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" < ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
sub check_not_less_w(word w1, word w2) {
word zero = 0
ubyte error=0
ubyte ub = w1<w2
if ub {
error++
txt.print("ub!")
}
if w1<(w2+zero) {
error++
txt.print("c!")
} else {
zero = 0 ; dummy
}
if error {
txt.print(" ")
txt.print_w(w1)
txt.print(" not < ")
txt.print_w(w2)
txt.chrout('\n')
exit(1)
}
}
}
}
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.
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.
+1
View File
@@ -3,6 +3,7 @@
%import textio
%import test_stack
spritedata $2000 {
; this memory block contains the sprite data
; it must start on an address aligned to 64 bytes.
+1
View File
@@ -3,6 +3,7 @@
%import test_stack
%import textio
main {
; vertices
+125
View File
@@ -0,0 +1,125 @@
%target cx16
%import graphics
%import diskio
bmp_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
ubyte[$36] header
uword size
uword width
uword height
ubyte bpp
uword offsetx
uword offsety
ubyte[256] palette0
ubyte[256] palette1
ubyte[256] palette2
ubyte[256] palette3
uword total_read = 0
palette0[0] = 0
palette1[0] = 0
palette2[0] = 0
palette3[0] = 0
if diskio.f_open(8, filenameptr) {
size = diskio.f_read(header, $36)
if size==$36 {
total_read = $36
if header[0]=='b' and header[1]=='m' {
uword bm_data_offset = mkword(header[11], header[10])
uword header_size = mkword(header[$f], header[$e])
width = mkword(header[$13], header[$12])
height = mkword(header[$17], header[$16])
bpp = header[$1c]
uword num_colors = header[$2e]
if num_colors == 0
num_colors = 2**bpp
uword skip_hdr = header_size - 40
repeat skip_hdr
void c64.CHRIN()
total_read += skip_hdr
size = diskio.f_read(&palette0, num_colors*4)
if size==num_colors*4 {
total_read += size
repeat bm_data_offset - total_read
void c64.CHRIN()
graphics.clear_screen(1, 0)
palette.set_bgra(&palette0, num_colors)
decode_bitmap()
load_ok = true
}
}
}
diskio.f_close()
}
return load_ok
sub start_plot() {
offsetx = 0
offsety = 0
if width < graphics.WIDTH
offsetx = (graphics.WIDTH - width - 1) / 2
if height < graphics.HEIGHT
offsety = (graphics.HEIGHT - height - 1) / 2
if width > graphics.WIDTH
width = graphics.WIDTH
if height > graphics.HEIGHT-1
height = graphics.HEIGHT-1
}
sub set_cursor(uword x, uword y) {
cx16.r0=offsetx+x
cx16.r1=offsety+y
cx16.FB_cursor_position()
}
sub decode_bitmap() {
start_plot()
uword bits_width = width * bpp
ubyte pad_bytes = (((bits_width + 31) >> 5) << 2) - ((bits_width + 7) >> 3) as ubyte
uword x
uword y
ubyte b
for y in height-1 downto 0 {
set_cursor(0, y)
when bpp {
8 -> {
for x in 0 to width-1
cx16.FB_set_pixel(c64.CHRIN())
}
4 -> {
for x in 0 to width-1 step 2 {
b = c64.CHRIN()
cx16.FB_set_pixel(b>>4)
cx16.FB_set_pixel(b&15)
}
}
2 -> {
for x in 0 to width-1 step 4 {
b = c64.CHRIN()
cx16.FB_set_pixel(b>>6)
cx16.FB_set_pixel(b>>4 & 3)
cx16.FB_set_pixel(b>>2 & 3)
cx16.FB_set_pixel(b & 3)
}
}
1 -> {
for x in 0 to width-1 step 8 {
cx16.r0 = c64.CHRIN()
cx16.FB_set_8_pixels_opaque(255, 255, 0)
}
}
}
repeat pad_bytes
void c64.CHRIN()
}
}
}
}
+140
View File
@@ -0,0 +1,140 @@
%target cx16
%import graphics
%import textio
%import diskio
%option no_sysinit
; CommanderX16 Image file format. (EXPERIMENTAL/ UNFINISHED)
; Numbers are encoded in little endian format (lsb first).
;
; offset value
; -----------------
; HEADER (12 bytes):
; 0-1 'CI' in petscii , from "CommanderX16 Image".
; 2 Size of the header data following this byte (always 9, could become more if format changes)
; 3-4 Width in pixels (must be multiple of 8)
; 5-6 Height in pixels
; 7 Bits-per-pixel (1, 2, 4 or 8) (= 2, 4, 16 or 256 colors)
; this also determines the number of palette entries following later.
; 8 Settings bits.
; bit 0 and 1 = compression. 00 = uncompressed
; 01 = RLE [TODO not yet implemented]
; 10 = LZSA [TODO not yet implemented]
; 11 = Exomizer [TODO not yet implemented]
; bit 2 = palette format. 0 = 4 bits/channel (2 bytes per color, $0R $GB)
; 1 = 8 bits/channel (3 bytes per color, $RR $GG $BB)
; 4 bits per channel is what the Vera in the Cx16 supports.
; bit 3 = bitmap format. 0 = raw bitmap pixels
; 1 = tile-based image [TODO not yet implemented]
; bit 4 = hscale (horizontal display resulution) 0 = 320 pixels, 1 = 640 pixels
; bit 5 = vscale (vertical display resulution) 0 = 240 pixels, 1 = 480 pixels
; bit 6,7: reserved, set to 0
; 9-11 Size of the bitmap data following the palette data.
; This is a 24-bits number, can be 0 ("unknown", in which case just read until the end).
;
; PALETTE (always present but size varies):
; 12-... Color palette. Number of entries = 2 ^ bits-per-pixel. Number of bytes per
; entry is 2 or 3, depending on the chosen palette format in the setting bits.
;
; BITMAPDATA (size varies):
; After this, the actual image data follows.
; If the bitmap format is 'raw bitmap pixels', the bimap is simply written as a sequence
; of bytes making up the image's scan lines. #bytes per scan line = width * bits-per-pixel / 8
; If it is 'tiles', .... [TODO]
; If a compression scheme is used, the bitmap data here has to be decompressed first.
; TODO: with compressed files, store the data in compressed chunks of max 8kb uncompressed?
; (it is a problem to load let alone decompress a full bitmap at once because there will likely not be enough ram to do that)
; (doing it in chunks of 8 kb allows for sticking each chunk in one of the banked 8kb ram blocks, or even copy it directly to the screen)
ci_module {
%option force_output
ubyte[256] buffer
ubyte[256] buffer2 ; add two more buffers to make enough space
ubyte[256] buffer3 ; to store a 256 color palette
ubyte[256] buffer4 ; .. and some more to be able to store 1280=
ubyte[256] buffer5 ; two 640 bytes worth of bitmap scanline data
sub show_image(uword filename) -> ubyte {
ubyte read_success = false
uword bitmap_load_address = progend()
; uword max_bitmap_size = $9eff - bitmap_load_address
if(diskio.f_open(8, filename)) {
uword size = diskio.f_read(buffer, 12) ; read the header
if size==12 {
if buffer[0]=='c' and buffer[1]=='i' and buffer[2] == 9 {
uword width = mkword(buffer[4], buffer[3])
uword height = mkword(buffer[6], buffer[5])
ubyte bpp = buffer[7]
uword num_colors = 2 ** bpp
ubyte flags = buffer[8]
ubyte compression = flags & %00000011
ubyte palette_format = (flags & %00000100) >> 2
ubyte bitmap_format = (flags & %00001000) >> 3
; ubyte hscale = (flags & %00010000) >> 4
; ubyte vscale = (flags & %00100000) >> 5
uword bitmap_size = mkword(buffer[10], buffer[9])
uword palette_size = num_colors*2
if palette_format
palette_size += num_colors ; 3
if width > graphics.WIDTH {
txt.print("image is too wide for the display!\n")
} else if compression!=0 {
txt.print("compressed image not yet supported!\n") ; TODO implement the various decompressions
} else if bitmap_format==1 {
txt.print("tiled bitmap not yet supported!\n") ; TODO implement tiled image
} else {
size = diskio.f_read(buffer, palette_size)
if size==palette_size {
if compression {
txt.print("todo: compressed image support\n")
} else {
; uncompressed bitmap data. read it a scanline at a time and display as we go.
; restrict height to the maximun that can be displayed
if height > graphics.HEIGHT
height = graphics.HEIGHT
graphics.enable_bitmap_mode()
if palette_format
palette.set_rgb8(buffer, num_colors)
else
palette.set_rgb4(buffer, num_colors)
graphics.clear_screen(1,0)
cx16.r0 = 0
cx16.r1 = 0
cx16.FB_cursor_position()
uword scanline_size = width * bpp / 8
ubyte y
for y in 0 to lsb(height)-1 {
void diskio.f_read(buffer, scanline_size)
when bpp {
8 -> cx16.FB_set_pixels_from_buf(buffer, scanline_size) ; FB_set_pixels in rom v38 crashes with a size > 255 so we use our own replacement for now
4 -> display_scanline_16c(buffer, scanline_size)
2 -> display_scanline_4c(buffer, scanline_size)
1 -> display_scanline_2c(buffer, scanline_size)
}
}
read_success = true
}
}
}
}
}
diskio.f_close()
}
return read_success
}
sub display_scanline_16c(uword dataptr, uword numbytes) {
; TODO
}
sub display_scanline_4c(uword dataptr, uword numbytes) {
; TODO
}
sub display_scanline_2c(uword dataptr, uword numbytes) {
; TODO
}
}
+214
View File
@@ -0,0 +1,214 @@
%target cx16
%import graphics
%import textio
%import diskio
iff_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
uword size
ubyte[32] buffer
ubyte[256] cmap
ubyte[256] cmap1
ubyte[256] cmap2
uword camg = 0
str chunk_id = "????"
uword chunk_size_hi
uword chunk_size_lo
uword scanline_data_ptr = progend()
uword width
uword height
ubyte num_planes
uword num_colors
ubyte compression
ubyte have_cmap = false
cmap[0] = 0
cmap1[0] = 0
cmap2[0] = 0
if diskio.f_open(8, filenameptr) {
size = diskio.f_read(buffer, 12)
if size==12 {
if buffer[0]=='f' and buffer[1]=='o' and buffer[2]=='r' and buffer[3]=='m'
and buffer[8]=='i' and buffer[9]=='l' and buffer[10]=='b' and buffer[11]=='m'{
while read_chunk_header() {
if chunk_id == "bmhd" {
void diskio.f_read(buffer, chunk_size_lo)
width = mkword(buffer[0], buffer[1])
height = mkword(buffer[2], buffer[3])
num_planes = buffer[8]
num_colors = 2 ** num_planes
compression = buffer[10]
}
else if chunk_id == "camg" {
void diskio.f_read(buffer, chunk_size_lo)
camg = mkword(buffer[2], buffer[3])
if camg & $0800 {
txt.print("ham mode not supported!\n")
break
}
}
else if chunk_id == "cmap" {
have_cmap = true
void diskio.f_read(&cmap, chunk_size_lo)
}
else if chunk_id == "body" {
graphics.clear_screen(1, 0)
if camg & $0004
height /= 2 ; interlaced: just skip every odd scanline later
if camg & $0080 and have_cmap
make_ehb_palette()
palette.set_rgb8(&cmap, num_colors)
if compression
decode_rle()
else
decode_raw()
load_ok = true
break ; done after body
}
else {
skip_chunk()
}
}
} else
txt.print("not an iff ilbm file!\n")
}
diskio.f_close()
}
return load_ok
sub read_chunk_header() -> ubyte {
size = diskio.f_read(buffer, 8)
if size==8 {
chunk_id[0] = buffer[0]
chunk_id[1] = buffer[1]
chunk_id[2] = buffer[2]
chunk_id[3] = buffer[3]
chunk_size_hi = mkword(buffer[4], buffer[5])
chunk_size_lo = mkword(buffer[6], buffer[7])
return true
}
return false
}
sub skip_chunk() {
repeat lsb(chunk_size_hi)*8 + (chunk_size_lo >> 13)
void diskio.f_read(scanline_data_ptr, $2000)
void diskio.f_read(scanline_data_ptr, chunk_size_lo & $1fff)
}
sub make_ehb_palette() {
; generate 32 additional Extra-Halfbrite colors in the cmap
uword palletteptr = &cmap
uword ehbptr = palletteptr + 32*3
repeat 32 {
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
@(ehbptr) = @(palletteptr)>>1
ehbptr++
palletteptr++
}
}
uword bitplane_stride
uword interleave_stride
uword offsetx
uword offsety
sub start_plot() {
bitplane_stride = width>>3
interleave_stride = bitplane_stride * num_planes
offsetx = 0
offsety = 0
if width < graphics.WIDTH
offsetx = (graphics.WIDTH - width - 1) / 2
if height < graphics.HEIGHT
offsety = (graphics.HEIGHT - height - 1) / 2
if width > graphics.WIDTH
width = graphics.WIDTH
if height > graphics.HEIGHT-1
height = graphics.HEIGHT-1
}
sub set_cursor(uword x, uword y) {
cx16.r0=offsetx+x
cx16.r1=offsety+y
cx16.FB_cursor_position()
}
sub decode_raw() {
start_plot()
ubyte interlaced = (camg & $0004) != 0
uword y
for y in 0 to height-1 {
void diskio.f_read(scanline_data_ptr, interleave_stride)
if interlaced
void diskio.f_read(scanline_data_ptr, interleave_stride)
set_cursor(0, y)
planar_to_chunky_scanline()
}
}
sub decode_rle() {
start_plot()
ubyte interlaced = (camg & $0004) != 0
uword y
for y in 0 to height-1 {
decode_rle_scanline()
if interlaced
decode_rle_scanline()
set_cursor(0, y)
planar_to_chunky_scanline()
}
}
sub decode_rle_scanline() {
uword x = interleave_stride
uword plane_ptr = scanline_data_ptr
while x {
ubyte b = c64.CHRIN()
if b > 128 {
ubyte b2 = c64.CHRIN()
repeat 2+(b^255) {
@(plane_ptr) = b2
plane_ptr++
x--
}
} else if b < 128 {
repeat b+1 {
@(plane_ptr) = c64.CHRIN()
plane_ptr++
x--
}
} else
break
}
}
sub planar_to_chunky_scanline() {
uword x
for x in 0 to width-1 {
ubyte bitnr = ((lsb(x) ^ 255) & 7) + 1
uword pixptr = x/8 + scanline_data_ptr
ubyte bits=0
repeat num_planes {
ubyte bb = @(pixptr) >> bitnr ; extract bit
ror(bits) ; shift it into the byte
pixptr += bitplane_stride
}
bits >>= 8-num_planes
cx16.FB_set_pixel(bits)
}
}
}
}
+197
View File
@@ -0,0 +1,197 @@
%target cx16
%import graphics
%import textio
%import diskio
%import koala_module
%import iff_module
%import pcx_module
%import bmp_module
;; %import ci_module
main {
sub start() {
; trick to check if we're running on sdcard or host system shared folder
txt.print("\nimage viewer for commander x16\nformats supported: .iff, .pcx, .bmp, .koa (c64 koala)\n\n")
if strlen(diskio.status(8)) {
txt.print("enter image file name or just enter for all on disk: ")
ubyte i = txt.input_chars(diskio.filename)
graphics.enable_bitmap_mode()
if i
attempt_load(diskio.filename)
else
show_pics_sdcard()
txt.print("\nnothing more to do.\n")
}
else
txt.print("files are read with sequential file loading.\nin the emulator this currently only works with files on an sd-card image.\nsorry :(\n")
repeat {
;
}
}
sub show_pics_sdcard() {
; load and show the images on the disk with the given extensions.
; this only works in the emulator V38 with an sd-card image with the files on it.
str[40] filename_ptrs
ubyte num_files = diskio.list_files(8, 0, false, &filename_ptrs, len(filename_ptrs))
if num_files {
while num_files {
num_files--
attempt_load(filename_ptrs[num_files])
}
} else
txt.print("no files in directory!\n")
}
sub attempt_load(uword filenameptr) {
txt.print(">> ")
txt.print(filenameptr)
txt.chrout('\n')
uword extension = filenameptr + rfind(filenameptr, '.')
if strcmp(extension, ".iff")==0 {
txt.print("loading ")
txt.print("iff\n")
if iff_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
else if strcmp(extension, ".pcx")==0 {
txt.print("loading ")
txt.print("pcx\n")
if pcx_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
else if strcmp(extension, ".koa")==0 {
txt.print("loading ")
txt.print("koala\n")
if koala_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
else if strcmp(extension, ".bmp")==0 {
txt.print("loading ")
txt.print("bmp\n")
if bmp_module.show_image(filenameptr)
txt.clear_screen()
else
txt.print("load error!\n")
cx16.wait(120)
}
; else if strcmp(extension, ".ci")==0 {
; txt.print("loading ")
; txt.print("ci\n")
; if ci_module.show_image(filenameptr)
; txt.clear_screen()
; else
; txt.print("load error!\n")
; cx16.wait(120)
; }
}
sub extension_equals(uword stringptr, uword extensionptr) -> ubyte {
ubyte ix = rfind(stringptr, '.')
return ix<255 and strcmp(stringptr+ix, extensionptr)==0
}
sub rfind(uword stringptr, ubyte char) -> ubyte {
ubyte i
for i in strlen(stringptr)-1 downto 0 {
if @(stringptr+i)==char
return i
}
return 0
}
}
palette {
sub set_rgb4(uword palletteptr, uword num_colors) {
; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped
uword vera_palette_ptr = $fa00
repeat num_colors {
cx16.vpoke(1, vera_palette_ptr+1, @(palletteptr))
palletteptr++
cx16.vpoke(1, vera_palette_ptr, @(palletteptr))
palletteptr++
vera_palette_ptr+=2
}
}
sub set_rgb8(uword palletteptr, uword num_colors) {
; 3 bytes per color entry, adjust color depth from 8 to 4 bits per channel.
uword vera_palette_ptr = $fa00
ubyte red
ubyte greenblue
repeat num_colors {
red = @(palletteptr) >> 4
palletteptr++
greenblue = @(palletteptr) & %11110000
palletteptr++
greenblue |= @(palletteptr) >> 4 ; add Blue
palletteptr++
cx16.vpoke(1, vera_palette_ptr, greenblue)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, red)
vera_palette_ptr++
}
}
sub set_bgra(uword palletteptr, uword num_colors) {
uword vera_palette_ptr = $fa00
ubyte red
ubyte greenblue
; 4 bytes per color entry (BGRA), adjust color depth from 8 to 4 bits per channel.
repeat num_colors {
red = @(palletteptr+2) >> 4
greenblue = @(palletteptr+1) & %11110000
greenblue |= @(palletteptr+0) >> 4 ; add Blue
palletteptr+=4
cx16.vpoke(1, vera_palette_ptr, greenblue)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, red)
vera_palette_ptr++
}
}
sub set_monochrome() {
uword vera_palette_ptr = $fa00
cx16.vpoke(1, vera_palette_ptr, 0)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, 0)
vera_palette_ptr++
repeat 255 {
cx16.vpoke(1, vera_palette_ptr, 255)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, 255)
vera_palette_ptr++
}
}
sub set_grayscale() {
ubyte c = 0
uword vera_palette_ptr = $fa00
repeat 16 {
repeat 16 {
cx16.vpoke(1, vera_palette_ptr, c)
vera_palette_ptr++
cx16.vpoke(1, vera_palette_ptr, c)
vera_palette_ptr++
}
c += $11
}
}
}
+86
View File
@@ -0,0 +1,86 @@
%target cx16
%import graphics
%import diskio
%import c64colors
koala_module {
const uword load_location = $6000
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok=false
if diskio.f_open(8, filenameptr) {
uword size = diskio.f_read(load_location, 2) ; skip the first 2 bytes (load address)
if size==2 {
size = diskio.f_read(load_location, 10001)
if size == 10001 {
; set a better C64 color palette, the Cx16's default is too saturated
c64colors.set_palette_pepto()
convert_koalapic()
load_ok = true
}
}
diskio.f_close()
}
return load_ok
}
sub convert_koalapic() {
ubyte cy
ubyte @zp cx
uword @zp cy_times_forty = 0
ubyte @zp d
uword bitmap_ptr = load_location
; theoretically you could put the 8-pixel array in zeropage to squeeze out another tiny bit of performance
ubyte[8] pixels
graphics.clear_screen(1, 0)
for cy in 0 to 24*8 step 8 {
for cx in 0 to 39 {
for d in 0 to 7 {
cx16.r0 = cx as uword * 8
cx16.r1 = cy as uword + d
cx16.FB_cursor_position()
get_8_pixels()
cx16.r0 = &pixels
cx16.r1 = 8
cx16.FB_set_pixels()
}
}
cy_times_forty += 40
}
sub get_8_pixels() {
ubyte bm = @(bitmap_ptr)
ubyte @zp m = mcol(bm)
pixels[7] = m
pixels[6] = m
bm >>= 2
m = mcol(bm)
pixels[5] = m
pixels[4] = m
bm >>= 2
m = mcol(bm)
pixels[3] = m
pixels[2] = m
bm >>= 2
m = mcol(bm)
pixels[1] = m
pixels[0] = m
bitmap_ptr++
sub mcol(ubyte b) -> ubyte {
ubyte @zp color
when b & 3 {
0 -> color = @(load_location + 8000 + 1000 + 1000) & 15
1 -> color = @(load_location + 8000 + cy_times_forty + cx) >>4
2 -> color = @(load_location + 8000 + cy_times_forty + cx) & 15
else -> color = @(load_location + 8000 + 1000 + cy_times_forty + cx) & 15
}
return color
}
}
}
}
+180
View File
@@ -0,0 +1,180 @@
%target cx16
%import graphics
%import textio
%import diskio
pcx_module {
sub show_image(uword filenameptr) -> ubyte {
ubyte load_ok = false
if diskio.f_open(8, filenameptr) {
ubyte[128] header
uword size = diskio.f_read(header, 128)
if size==128 {
if header[0] == $0a and header[2] == 1 {
ubyte bits_per_pixel = header[3]
if bits_per_pixel==1 or bits_per_pixel==4 or bits_per_pixel==8 {
uword width = mkword(header[$09], header[$08]) - mkword(header[$05], header[$04]) + 1
uword height = mkword(header[$0b], header[$0a]) - mkword(header[$07], header[$06]) + 1
ubyte number_of_planes = header[$41]
uword palette_format = mkword(header[$45], header[$44])
uword num_colors = 2**bits_per_pixel
if number_of_planes == 1 {
if (width & 7) == 0 {
graphics.clear_screen(1, 0)
if palette_format==2
palette.set_grayscale()
else if num_colors == 16
palette.set_rgb8(&header + $10, 16)
else if num_colors == 2
palette.set_monochrome()
when bits_per_pixel {
8 -> load_ok = bitmap.do8bpp(width, height)
4 -> load_ok = bitmap.do4bpp(width, height)
1 -> load_ok = bitmap.do1bpp(width, height)
}
if load_ok {
load_ok = c64.CHRIN()
if load_ok == 12 {
; there is 256 colors of palette data at the end
uword palette_mem = progend()
load_ok = false
size = diskio.f_read(palette_mem, 3*256)
if size==3*256 {
load_ok = true
palette.set_rgb8(palette_mem, num_colors)
}
}
}
} else
txt.print("width not multiple of 8!\n")
} else
txt.print("has >256 colors!\n")
}
}
}
diskio.f_close()
}
return load_ok
}
}
bitmap {
uword offsetx
uword offsety
uword py
uword px
ubyte y_ok
ubyte status
sub start_plot(uword width, uword height) {
offsetx = 0
offsety = 0
y_ok = true
py = 0
px = 0
if width < graphics.WIDTH
offsetx = (graphics.WIDTH - width - 1) / 2
if height < graphics.HEIGHT
offsety = (graphics.HEIGHT - height - 1) / 2
status = (not c64.READST()) or c64.READST()==64
}
sub set_cursor(uword x, uword y) {
cx16.r0=offsetx+x
cx16.r1=offsety+y
cx16.FB_cursor_position()
}
sub next_scanline() {
px = 0
py++
y_ok = py < graphics.HEIGHT-1
set_cursor(0, py)
status = (not c64.READST()) or c64.READST()==64
}
sub do1bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
set_cursor(0, 0)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
repeat b {
if y_ok {
cx16.r0 = dat
cx16.FB_set_8_pixels_opaque(255, 255, 0)
}
px += 8
}
} else {
if y_ok {
cx16.r0 = b
cx16.FB_set_8_pixels_opaque(255, 255, 0)
}
px += 8
}
if px==width
next_scanline()
}
return status
}
sub do4bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
set_cursor(0, 0)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
if y_ok
repeat b {
cx16.FB_set_pixel(dat>>4)
cx16.FB_set_pixel(dat & 15)
}
px += b*2
} else {
if y_ok {
cx16.FB_set_pixel(b>>4)
cx16.FB_set_pixel(b & 15)
}
px += 2
}
if px==width
next_scanline()
}
return status
}
sub do8bpp(uword width, uword height) -> ubyte {
start_plot(width, height)
set_cursor(0, 0)
while py < height and status {
ubyte b = c64.CHRIN()
if b>>6==3 {
b &= %00111111
ubyte dat = c64.CHRIN()
if y_ok
repeat b
cx16.FB_set_pixel(dat)
px += b
} else {
if y_ok
cx16.FB_set_pixel(b)
px++
}
if px==width
next_scanline()
}
return status
}
}
-40
View File
@@ -1,40 +0,0 @@
; CommanderX16 simple graphics example!
%target cx16
%zeropage basicsafe
main {
sub start() {
void cx16.screen_set_mode($80)
cx16.r0=0
void cx16.screen_set_mode(0)
cx16.FB_init()
cx16.GRAPH_init()
cx16.r0 = 0
cx16.r1 = 0
cx16.FB_cursor_position()
ubyte color
repeat 320*199 {
cx16.FB_set_pixel(color)
color++
}
uword xx
for xx in 0 to 319 step 32 {
ubyte q
for q in 0 to 31 {
cx16.GRAPH_set_colors(q, 2, 0)
cx16.r0 = xx+q
cx16.r1=0
cx16.r2=rnd()
cx16.r3=199
cx16.GRAPH_draw_line()
}
}
}
}
+56
View File
@@ -0,0 +1,56 @@
%import textio
%import diskio
%zeropage basicsafe
%import test_stack
%option no_sysinit
main {
sub start() {
txt.print("showing the full directory of drive 8:\n")
void diskio.directory(8)
txt.chrout('\n')
if diskio.lf_start_list(8, "cub", false) {
txt.print("\nfiles starting with 'cub':\n")
while diskio.lf_next_entry() {
txt.print(diskio.list_filename)
txt.print(" ")
txt.print_uw(diskio.list_blocks)
txt.print(" blocks\n")
}
diskio.lf_end_list()
} else {
txt.print("error\n")
}
if diskio.lf_start_list(8, "gfx", true) {
txt.print("\nfiles ending with 'gfx':\n")
while diskio.lf_next_entry() {
txt.print(diskio.list_filename)
txt.print(" ")
txt.print_uw(diskio.list_blocks)
txt.print(" blocks\n")
}
diskio.lf_end_list()
} else {
txt.print("error\n")
}
if diskio.lf_start_list(8, 0, false) {
txt.print("\nfirst 10 files:\n")
ubyte counter=0
while counter < 10 and diskio.lf_next_entry() {
txt.print(diskio.list_filename)
txt.print(" ")
txt.print_uw(diskio.list_blocks)
txt.print(" blocks\n")
counter++
}
diskio.lf_end_list()
} else {
txt.print("error\n")
}
; test_stack.test()
}
}
+11 -15
View File
@@ -41,7 +41,7 @@ main {
rect(10, 10, 10, 10, false)
rect(6, 0, 16, 20, true)
; test_stack.test()
test_stack.test()
sub rect(ubyte x1, ubyte y1, ubyte x2, ubyte y2, ubyte fill) {
@@ -140,21 +140,17 @@ main {
ubyte xx
while x>=y {
for xx in cx to cx+x {
txt.setcc(xx, cy + y as ubyte, 81, 1)
txt.setcc(xx, cy - y as ubyte, 81, 2)
xx = cx-x
repeat 2*x+1 {
txt.setcc(xx, cy + y as ubyte, 81, 11)
txt.setcc(xx, cy - y as ubyte, 81, 12)
xx++
}
for xx in cx-x to cx-1 {
txt.setcc(xx, cy + y as ubyte, 81, 3)
txt.setcc(xx, cy - y as ubyte, 81, 4)
}
for xx in cx to cx+y {
txt.setcc(xx, cy + x as ubyte, 81, 5)
txt.setcc(xx, cy - x as ubyte, 81, 6)
}
for xx in cx-y to cx {
txt.setcc(xx, cy + x as ubyte, 81, 7)
txt.setcc(xx, cy - x as ubyte, 81, 8)
xx = cx-y
repeat 2*y+1 {
txt.setcc(xx, cy + x as ubyte, 81, 13)
txt.setcc(xx, cy - x as ubyte, 81, 14)
xx++
}
y++
if decisionOver2<=0
+1
View File
@@ -45,6 +45,7 @@ main {
}
}
; graphics.disable_bitmap_mode()
; test_stack.test()
repeat {
+2 -1
View File
@@ -10,6 +10,7 @@
; Converted to prog8 by Irmen de Jong.
main {
const uword SCREEN1 = $E000
const uword SCREEN2 = $E400
@@ -27,7 +28,7 @@ main {
; ubyte v = c64.VMCSB
c64.CIA2PRA = (block & $FC) | (lsb(SCREEN1 >> 14) ^ $03)
repeat 100 {
repeat {
doplasma(SCREEN1)
c64.VMCSB = PAGE1
doplasma(SCREEN2)
+4 -38
View File
@@ -1,48 +1,14 @@
%import textio
%import diskio
%import floats
%import graphics
%zeropage basicsafe
%import test_stack
%option no_sysinit
main {
sub start() {
ubyte[] ubarray = [1,2,3,4]
byte[] barray = [1,2,3,4]
uword[] uwarray = [1,2,3,4]
word[] warray = [1,2,3,4]
float[] farray = [1.1,2.2,3.3,4.4]
ubyte ub
byte bb
uword uw
word ww
float fl
const ubyte i = 2
const ubyte j = 3
ub = ubarray[i]
txt.print_ub(ub)
txt.chrout('\n')
bb = barray[i]
txt.print_b(bb)
txt.chrout('\n')
uw = uwarray[i]
txt.print_uw(uw)
txt.chrout('\n')
ww = warray[i]
txt.print_w(ww)
txt.chrout('\n')
fl = farray[i]
floats.print_f(fl)
txt.chrout('\n')
sub start () {
test_stack.test()
txt.chrout('\n')
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ turtle {
uword xx = xpos as uword
c64.SPXY[0] = lsb(xx) + 12
c64.MSIGX = msb(xx) > 0
c64.SPXY[1] = ypos as ubyte + 37
c64.SPXY[1] = ypos as ubyte + 40
}
sub pos(float x, float y) {
+2 -2
View File
@@ -3,8 +3,8 @@ plugins {
id 'java'
}
targetCompatibility = 1.8
sourceCompatibility = 1.8
targetCompatibility = 11
sourceCompatibility = 11
repositories {
mavenCentral()
+23
View File
@@ -0,0 +1,23 @@
<filetype binary="false" description="Prog8 source file" name="Prog8">
<highlighting>
<options>
<option name="LINE_COMMENT" value=";" />
<option name="COMMENT_START" value="{{" />
<option name="COMMENT_END" value="}}" />
<option name="HEX_PREFIX" value="$" />
<option name="NUM_POSTFIXES" value="" />
<option name="HAS_BRACES" value="true" />
<option name="HAS_BRACKETS" value="true" />
<option name="HAS_PARENS" value="true" />
<option name="HAS_STRING_ESCAPES" value="true" />
</options>
<keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" />
<keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;clear_carry;clear_irqd;cos;cos16;cos16u;cos8;cos8u;deg;exit;floor;leftstr;len;ln;log2;lsb;lsl;lsr;max;memcopy;memset;memsetw;min;mkword;msb;progend;rad;read_flags;reverse;rightstr;rnd;rndf;rndw;rol;rol2;ror;ror2;round;rrestore;rsave;set_carry;set_irqd;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;strcmp;strlen;substr;sum;swap;tan" />
</highlighting>
<extensionMap>
<mapping ext="p8" />
<mapping ext="prog8" />
</extensionMap>
</filetype>
+11
View File
@@ -0,0 +1,11 @@
Prog8 syntax highlighting file for IntelliJ IDEA.
Copy the file Prog8.xml to your IDEA filetypes folder.
The exact path may vary with the version of the IDE,
but for me it is currently this on Linux:
$HOME/.config/JetBrains/IntelliJIdea2020.2/filetypes/
(note the version number in the path, adjust accordingly)
+1
View File
@@ -0,0 +1 @@
TODO
+1
View File
@@ -0,0 +1 @@
TODO
+4
View File
@@ -0,0 +1,4 @@
A contributed syntax definition file for Sublime Text 3 can be obtained from:
https://github.com/akubiczek/Prog8-TmLanguage-VsCode/tree/master/sublime3
+5
View File
@@ -0,0 +1,5 @@
A contributed syntax definition file for Visual Studio Code can be obtained from:
https://github.com/akubiczek/Prog8-TmLanguage-VsCode
+1
View File
@@ -0,0 +1 @@
TODO