Compare commits

...

84 Commits
v6.2 ... v6.4

Author SHA1 Message Date
4cae2c56ec implemented last remaining codegen for word-byte division and remainders. 2021-03-25 22:03:36 +01:00
d840975054 remove unreached error checks 2021-03-25 21:47:05 +01:00
1b14da6c03 compiler warning instead of crash when attempting to assign invalid array value to other array 2021-03-24 22:01:22 +01:00
292640b17a asmgen: string values cannot be typecasted 2021-03-24 21:49:33 +01:00
112a7b09f2 added codegen for expression that needs the status-flag register result as a value on the stack 2021-03-24 21:42:27 +01:00
863ec9ce8a Merge pull request #26 from Elektron72/vim-syntax
Add support for built-in functions to Vim syntax file (and other fixes)
2021-03-24 20:50:53 +01:00
2eb346a205 Add support for built-ins to Vim syntax file
This commit adds support for highlighting built-in functions and
variables to the Vim syntax file.
2021-03-23 19:53:20 -04:00
8092355acb Add syntax sync to Vim syntax file
This will make the highlighting slightly slower, but will fix issues
with assembly not being highlighted properly.
2021-03-23 19:41:34 -04:00
e7ef2ed31b todo 2021-03-23 23:48:53 +01:00
af4de6d2fc replacing complex array indexer expressions moved to BeforeAsmGeneration + use cx16 virtualregister instead of adding temp variables for this 2021-03-23 23:44:14 +01:00
69f73dd779 Add void operator to Vim syntax file 2021-03-23 18:12:52 -04:00
9706b46012 credits 2021-03-23 02:50:16 +01:00
6d75dd3bb8 Merge pull request #25 from Elektron72/vim-syntax
Add Vim syntax highlighting file
2021-03-23 01:29:57 +01:00
bd295ffc99 array indexer complexity is now dealt with in the asm-generator only 2021-03-22 19:40:57 +01:00
07ce3e3c9d Add Vim syntax highlighting file
The readme file in syntax-files/Vim/ was also modified to give simple
installation instructions.
2021-03-22 12:13:20 -04:00
cbc3e37a89 stuff 2021-03-22 02:29:59 +01:00
3626828ceb decided 2021-03-22 01:45:19 +01:00
24b77fb5a5 comments. 2021-03-21 21:10:29 +01:00
1505fe686a updated vtui example 2021-03-21 20:40:35 +01:00
0991131fa8 don't stript unused asmsub definitions 2021-03-21 19:55:21 +01:00
2e928bd3c2 fix compiler crash for certain str argument to asm functions 2021-03-21 18:39:39 +01:00
ca868ae19e added cx16.vload() (like the VLOAD basic instruction) 2021-03-20 02:39:53 +01:00
3e286dd14c move test 2021-03-18 19:34:54 +01:00
11247d52b1 fix bugs in word <= and >= comparisons 2021-03-18 19:20:48 +01:00
1dbc902513 fix bugs in uword <= and >= comparisons 2021-03-18 18:41:41 +01:00
330e691b78 wip 2021-03-18 02:43:08 +01:00
6780d4f562 fix bug in uword > comparison 2021-03-18 02:21:21 +01:00
b30b8b7368 fix bug in float < and > comparisons 2021-03-18 01:41:54 +01:00
3df182b8c3 created extensive comparison test suite 2021-03-18 00:50:13 +01:00
7f21d89fea moved test programs to test folder in compiler module 2021-03-17 20:15:16 +01:00
2b267b4ba1 IDE syntax 2021-03-17 19:36:37 +01:00
ef64881528 busy creating extensive comparison test suite 2021-03-17 19:35:22 +01:00
9a6bd760bd fixed issues in uword '>' 2021-03-16 23:40:32 +01:00
00b9766aea fixed issues in word '>' 2021-03-16 23:22:58 +01:00
6381d2b6ac improve word '<', word (u)word '<=' , uword '>=' codegen 2021-03-16 18:15:47 +01:00
d2ab5f230d example TODOs 2021-03-16 01:09:25 +01:00
824b41d457 improve word '>' and '>=' codegen 2021-03-16 00:48:03 +01:00
b5523c7077 don't optimize with inlining too aggressively (code bloat) 2021-03-16 00:33:15 +01:00
eb3594b18c revert to just using comparison expressions in graphics code (we're optimizing these now!) 2021-03-16 00:11:55 +01:00
852d85d010 improve uword '<' and '>' codegen 2021-03-16 00:03:51 +01:00
5e0aef04fe improve (u)byte '>=' codegen 2021-03-15 23:20:16 +01:00
a00c693f93 improve (u)byte '<=' codegen 2021-03-15 23:17:04 +01:00
c943da1448 improve ubyte '<' and '>' codegen 2021-03-15 23:12:52 +01:00
b630fae580 refactor byte '==', '!=', '<' and '>' codegen 2 2021-03-15 23:08:30 +01:00
38e40084f1 refactor byte '==', '!=', '<' and '>' codegen 2021-03-15 22:47:18 +01:00
bf23ad78e6 improve byte '<' and '>' codegen 2021-03-15 22:26:00 +01:00
ded1d19737 improve '==' and '!=' codegen 2021-03-15 19:29:32 +01:00
496a3b0d2c todo 2021-03-15 18:56:25 +01:00
6922333755 add a cmp(x,y) function that returns no value but only sets the status bits based off the comparison (can be used with a conditional jump afterwards) 2021-03-13 15:11:22 +01:00
a00c39e9cf compiler error instead of crash when using functioncall without returnvalue 2021-03-12 19:31:04 +01:00
1c1da8e38e additional optimization to the bresenham line routines 2021-03-10 18:49:40 +01:00
50a306f492 line drawing fixes 2021-03-09 22:11:30 +01:00
6995ee2d17 fix cx16 bresenham line inaccuracy 2021-03-09 22:04:19 +01:00
6c60ea9cac allocate even more c64 zeropage locations for floats 2021-03-09 21:47:36 +01:00
2431ed811a don't remove typecasts in asmsub argument lists 2021-03-09 21:29:48 +01:00
6bd205c02a fix c64 bresenham line inaccuracy 2021-03-09 21:07:55 +01:00
62ec77e148 ver 2021-03-08 23:35:52 +01:00
9120e1de88 fix ubyte/uword to float conversion crashes on Commander X16 2021-03-08 23:21:52 +01:00
60e169bd87 added optimized integer square (x*x) routine 2021-03-08 23:08:47 +01:00
e4bca5fe47 version 2021-03-06 23:07:30 +01:00
a1729b65ab fix min(), max(), sum(), abs() 2021-03-06 22:57:22 +01:00
2950d26c8e array and struct value assignments now via memcopy instead of assignment per element 2021-03-06 22:10:03 +01:00
4f8d4a9585 use memcopy to assign arrays 2021-03-06 19:01:16 +01:00
d787795759 simplified 2021-03-06 15:43:23 +01:00
cf74e73e27 IDEA syntax colors 2021-03-06 15:23:58 +01:00
2770254fd9 removed inline assembly from bobs demo 2021-03-06 14:31:26 +01:00
de04bd8cfa added more convenient number-to-string functions to conv library 2021-03-06 13:47:27 +01:00
076a547f91 added more convenient number-to-string functions to conv library 2021-03-06 13:34:57 +01:00
dffd0a2706 added fastrnd8() with the old rnd() generator code in it, new code for rnd() uses the much better rndw() generator now. 2021-03-05 22:49:14 +01:00
6c66f86103 todo 2021-03-05 21:07:35 +01:00
26502c949a add unlimited bobs example 2021-03-05 19:05:13 +01:00
8dfe510883 avoid compiler crash when evaluating const expressions fails due to things like integer out of bounds 2021-03-04 01:32:02 +01:00
96ba9f5902 spelling correction 2021-03-04 01:31:29 +01:00
3a6ba0ab71 added 'kefrenbars' example 2021-03-03 01:09:18 +01:00
32d894d6b6 optimized repeat loop for word counts 2021-02-28 21:22:46 +01:00
543efa4299 attempt 2 at optimizing repeats 2021-02-28 21:02:17 +01:00
eba0708099 Revert "optimized repeat loop for word counts"
This reverts commit 51e6bf0d
2021-02-28 20:29:28 +01:00
51e6bf0d45 optimized repeat loop for word counts 2021-02-28 17:34:18 +01:00
07b5c44a54 preparing to optimize 16 bit repeat loop 2021-02-28 17:13:15 +01:00
9fe32c1c34 codegen uses 'bra' on 65c02 instead of 'jmp' 2021-02-28 16:46:08 +01:00
0e0278c84a for loops now use 'bra' if available 2021-02-28 16:35:59 +01:00
dea775a9cd package refactor 2021-02-28 16:29:15 +01:00
7e3e18a5c7 deal with 'bra' better on 65c02 2021-02-28 16:20:03 +01:00
8e3ebc84f0 readme 2021-02-28 15:40:04 +01:00
102 changed files with 4174 additions and 4019 deletions

View File

@ -23,13 +23,14 @@ https://prog8.readthedocs.io/
What does Prog8 provide?
------------------------
- big reduction of source code length over raw assembly
- 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
- no stack frame allocations because parameters and local variables are automatically allocated statically
- constant folding in expressions and other high-level program optimizations
- subroutines with input parameters and result values
- high-level program optimizations
- small program boilerplate/compilersupport overhead
- sane variable initialization, programs can be restarted again just fine after exiting to basic
- conditional branches
- floating point operations (requires the C64 Basic ROM routines for this)
- 'when' statement to provide a concise jump table alternative to if/elseif chains
@ -38,8 +39,10 @@ What does Prog8 provide?
- various powerful built-in libraries to do I/O, number conversions, graphics and more
- 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
- variables are allocated statically
- inline assembly allows you to have full control when every cycle or byte matters
- supports the sixteen 'virtual' 16-bit registers R0 .. R15 from the Commander X16, and provides them also on the C64.
- encode strings and characters into petscii or screencodes as desired (C64/Cx16)
*Rapid edit-compile-run-debug cycle:*

View File

@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Python" name="Python">
<configuration sdkName="Python 3.9" />
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
@ -14,5 +19,6 @@
<orderEntry type="library" name="unittest-libs" level="project" />
<orderEntry type="library" name="kotlinx-cli-jvm" level="project" />
<orderEntry type="module" module-name="compilerAst" />
<orderEntry type="library" name="Python 3.9 interpreter library" level="application" />
</component>
</module>

View File

@ -428,7 +428,9 @@ var_fac1_greater_f .proc
cmp #1
beq +
lda #0
+ rts
rts
+ lda #1
rts
.pend
var_fac1_greatereq_f .proc

View File

@ -2,7 +2,7 @@
%import textio
; bitmap pixel graphics module for the C64
; only black/white monchrome 320x200 for now
; only black/white monochrome 320x200 for now
; assumes bitmap screen memory is $2000-$3fff
graphics {
@ -34,36 +34,33 @@ graphics {
sub line(uword @zp x1, ubyte @zp y1, uword @zp x2, ubyte @zp y2) {
; Bresenham algorithm.
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
; TODO there are some slight errors at the first/last pixels in certain slopes...??
if y1>y2 {
; make sure dy is always positive to have only 4 instead of 8 special cases
swap(x1, x2)
swap(y1, y2)
}
word @zp dx = x2-x1 as word
word @zp dy = y2-y1
word @zp dx = (x2 as word)-x1
word @zp dy = (y2 as word)-y1
if dx==0 {
vertical_line(x1, y1, abs(dy)+1 as ubyte)
vertical_line(x1, y1, abs(dy) as ubyte +1)
return
}
if dy==0 {
if x1>x2
x1=x2
horizontal_line(x1, y1, abs(dx)+1 as uword)
horizontal_line(x1, y1, abs(dx) as uword +1)
return
}
; TODO rewrite the rest in optimized assembly
word @zp d = 0
ubyte positive_ix = true
if dx < 0 {
dx = -dx
positive_ix = false
}
dx *= 2
dy *= 2
word @zp dx2 = dx*2
word @zp dy2 = dy*2
internal_plotx = x1
if dx >= dy {
@ -73,10 +70,10 @@ graphics {
if internal_plotx==x2
return
internal_plotx++
d += dy
d += dy2
if d > dx {
y1++
d -= dx
d -= dx2
}
}
} else {
@ -85,10 +82,10 @@ graphics {
if internal_plotx==x2
return
internal_plotx--
d += dy
d += dy2
if d > dx {
y1++
d -= dx
d -= dx2
}
}
}
@ -100,10 +97,10 @@ graphics {
if y1 == y2
return
y1++
d += dx
d += dx2
if d > dy {
internal_plotx++
d -= dy
d -= dy2
}
}
} else {
@ -112,10 +109,10 @@ graphics {
if y1 == y2
return
y1++
d += dx
d += dx2
if d > dy {
internal_plotx--
d -= dy
d -= dy2
}
}
}

View File

@ -202,7 +202,7 @@ romsub $FFAE = UNLSN() clobbers(A) ; command serial
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
@ -211,8 +211,8 @@ romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)

View File

@ -7,239 +7,213 @@ conv {
; ----- number conversions to decimal strings ----
asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
str string_out = "????????????????" ; result buffer for the string conversion routines
asmsub str_ub0 (ubyte value @ A) clobbers(A,Y) {
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
%asm {{
ldy #uword2decimal.ASCII_0_OFFSET
bne uword2decimal.hex_try200
rts
phx
jsr conv.ubyte2decimal
sty string_out
sta string_out+1
stx string_out+2
lda #0
sta string_out+3
plx
rts
}}
}
asmsub uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- convert 16 bit uword in A/Y to decimal
; output in uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
; (these are terminated by a zero byte so they can be easily printed)
; also returns Y = 100's, A = 10's, X = 1's
asmsub str_ub (ubyte value @ A) clobbers(A,Y) {
; ---- convert the ubyte in A in decimal string form, without left padding 0s
%asm {{
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
;By Omegamatrix Further optimizations by tepples
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15
;HexToDec99
; start in A
; end with A = 10's, decOnes (also in X)
;HexToDec255
; start in A
; end with Y = 100's, A = 10's, decOnes (also in X)
;HexToDec999
; start with A = high byte, Y = low byte
; end with Y = 100's, A = 10's, decOnes (also in X)
; requires 1 extra temp register on top of decOnes, could combine
; these two if HexToDec65535 was eliminated...
;HexToDec65535
; start with A/Y (low/high) as 16 bit value
; end with decTenThousand, decThousand, Y = 100's, A = 10's, decOnes (also in X)
; (irmen: I store Y and A in decHundreds and decTens too, so all of it can be easily printed)
ASCII_0_OFFSET = $30
temp = P8ZP_SCRATCH_B1 ; byte in zeropage
hexHigh = P8ZP_SCRATCH_W1 ; byte in zeropage
hexLow = P8ZP_SCRATCH_W1+1 ; byte in zeropage
HexToDec65535; SUBROUTINE
sty hexHigh ;3 @9
sta hexLow ;3 @12
tya
tax ;2 @14
lsr a ;2 @16
lsr a ;2 @18 integer divide 1024 (result 0-63)
cpx #$A7 ;2 @20 account for overflow of multiplying 24 from 43,000 ($A7F8) onward,
adc #1 ;2 @22 we can just round it to $A700, and the divide by 1024 is fine...
;at this point we have a number 1-65 that we have to times by 24,
;add to original sum, and Mod 1024 to get a remainder 0-999
sta temp ;3 @25
asl a ;2 @27
adc temp ;3 @30 x3
tay ;2 @32
lsr a ;2 @34
lsr a ;2 @36
lsr a ;2 @38
lsr a ;2 @40
lsr a ;2 @42
tax ;2 @44
tya ;2 @46
asl a ;2 @48
asl a ;2 @50
asl a ;2 @52
clc ;2 @54
adc hexLow ;3 @57
sta hexLow ;3 @60
txa ;2 @62
adc hexHigh ;3 @65
sta hexHigh ;3 @68
ror a ;2 @70
lsr a ;2 @72
tay ;2 @74 integer divide 1,000 (result 0-65)
lsr a ;2 @76 split the 1,000 and 10,000 digit
tax ;2 @78
lda ShiftedBcdTab,x ;4 @82
tax ;2 @84
rol a ;2 @86
and #$0F ;2 @88
ora #ASCII_0_OFFSET
sta decThousands ;3 @91
txa ;2 @93
lsr a ;2 @95
lsr a ;2 @97
lsr a ;2 @99
ora #ASCII_0_OFFSET
sta decTenThousands ;3 @102
lda hexLow ;3 @105
cpy temp ;3 @108
bmi _doSubtract ;2³ @110/111
beq _useZero ;2³ @112/113
adc #23 + 24 ;2 @114
_doSubtract
sbc #23 ;2 @116
sta hexLow ;3 @119
_useZero
lda hexHigh ;3 @122
sbc #0 ;2 @124
Start100s
and #$03 ;2 @126
tax ;2 @128 0,1,2,3
cmp #2 ;2 @130
rol a ;2 @132 0,2,5,7
ora #ASCII_0_OFFSET
tay ;2 @134 Y = Hundreds digit
lda hexLow ;3 @137
adc Mod100Tab,x ;4 @141 adding remainder of 256, 512, and 256+512 (all mod 100)
bcs hex_doSub200 ;2³ @143/144
hex_try200
cmp #200 ;2 @145
bcc hex_try100 ;2³ @147/148
hex_doSub200
iny ;2 @149
iny ;2 @151
sbc #200 ;2 @153
hex_try100
cmp #100 ;2 @155
bcc HexToDec99 ;2³ @157/158
iny ;2 @159
sbc #100 ;2 @161
HexToDec99; SUBROUTINE
lsr a ;2 @163
tax ;2 @165
lda ShiftedBcdTab,x ;4 @169
tax ;2 @171
rol a ;2 @173
and #$0F ;2 @175
ora #ASCII_0_OFFSET
sta decOnes ;3 @178
txa ;2 @180
lsr a ;2 @182
lsr a ;2 @184
lsr a ;2 @186
ora #ASCII_0_OFFSET
; irmen: load X with ones, and store Y and A too, for easy printing afterwards
sty decHundreds
sta decTens
ldx decOnes
rts ;6 @192 Y=hundreds, A = tens digit, X=ones digit
HexToDec999; SUBROUTINE
sty hexLow ;3 @9
jmp Start100s ;3 @12
Mod100Tab
.byte 0,56,12,56+12
ShiftedBcdTab
.byte $00,$01,$02,$03,$04,$08,$09,$0A,$0B,$0C
.byte $10,$11,$12,$13,$14,$18,$19,$1A,$1B,$1C
.byte $20,$21,$22,$23,$24,$28,$29,$2A,$2B,$2C
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
decTenThousands .byte 0
decThousands .byte 0
decHundreds .byte 0
decTens .byte 0
decOnes .byte 0
.byte 0 ; zero-terminate the decimal output string
}}
}
asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
; note: if the number is negative, you have to deal with the '-' yourself!
%asm {{
cmp #0
bpl +
eor #255
clc
adc #1
+ jmp ubyte2decimal
}}
}
asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
%asm {{
stx P8ZP_SCRATCH_REG
phx
ldy #0
sty P8ZP_SCRATCH_B1
jsr conv.ubyte2decimal
_output_byte_digits
; hundreds?
cpy #'0'
beq +
pha
and #$0f
tax
ldy _hex_digits,x
tya
ldy P8ZP_SCRATCH_B1
sta string_out,y
pla
lsr a
lsr a
lsr a
lsr a
tax
lda _hex_digits,x
ldx P8ZP_SCRATCH_REG
rts
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
inc P8ZP_SCRATCH_B1
; tens?
+ ldy P8ZP_SCRATCH_B1
cmp #'0'
beq +
sta string_out,y
iny
+ ; ones.
txa
sta string_out,y
iny
lda #0
sta string_out,y
plx
rts
}}
}
asmsub uword2hex (uword value @AY) clobbers(A,Y) {
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
asmsub str_b (byte value @ A) clobbers(A,Y) {
; ---- convert the byte in A in decimal string form, without left padding 0s
%asm {{
sta P8ZP_SCRATCH_REG
tya
jsr ubyte2hex
sta output
sty output+1
lda P8ZP_SCRATCH_REG
jsr ubyte2hex
sta output+2
sty output+3
rts
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
phx
ldy #0
sty P8ZP_SCRATCH_B1
cmp #0
bpl +
pha
lda #'-'
sta string_out
inc P8ZP_SCRATCH_B1
pla
+ jsr conv.byte2decimal
bra str_ub._output_byte_digits
}}
}
asmsub str_ubhex (ubyte value @ A) clobbers(A,Y) {
; ---- convert the ubyte in A in hex string form
%asm {{
jsr conv.ubyte2hex
sta string_out
sty string_out+1
lda #0
sta string_out+2
rts
}}
}
asmsub str_ubbin (ubyte value @ A) clobbers(A,Y) {
; ---- convert the ubyte in A in binary string form
%asm {{
sta P8ZP_SCRATCH_B1
ldy #0
sty string_out+8
ldy #7
- lsr P8ZP_SCRATCH_B1
bcc +
lda #'1'
bne _digit
+ lda #'0'
_digit sta string_out,y
dey
bpl -
rts
}}
}
asmsub str_uwbin (uword value @ AY) clobbers(A,Y) {
; ---- convert the uword in A/Y in binary string form
%asm {{
sta P8ZP_SCRATCH_REG
tya
jsr str_ubbin
ldy #0
sty string_out+16
ldy #7
- lsr P8ZP_SCRATCH_REG
bcc +
lda #'1'
bne _digit
+ lda #'0'
_digit sta string_out+8,y
dey
bpl -
rts
}}
}
asmsub str_uwhex (uword value @ AY) clobbers(A,Y) {
; ---- convert the uword in A/Y in hexadecimal string form (4 digits)
%asm {{
pha
tya
jsr conv.ubyte2hex
sta string_out
sty string_out+1
pla
jsr conv.ubyte2hex
sta string_out+2
sty string_out+3
lda #0
sta string_out+4
rts
}}
}
asmsub str_uw0 (uword value @ AY) clobbers(A,Y) {
; ---- convert the uword in A/Y in decimal string form, with left padding 0s (5 positions total)
%asm {{
phx
jsr conv.uword2decimal
ldy #0
- lda conv.uword2decimal.decTenThousands,y
sta string_out,y
beq +
iny
bne -
+ plx
rts
}}
}
asmsub str_uw (uword value @ AY) clobbers(A,Y) {
; ---- convert the uword in A/Y in decimal string form, without left padding 0s
%asm {{
phx
jsr conv.uword2decimal
ldx #0
_output_digits
ldy #0
- lda conv.uword2decimal.decTenThousands,y
beq _allzero
cmp #'0'
bne _gotdigit
iny
bne -
_gotdigit sta string_out,x
inx
iny
lda conv.uword2decimal.decTenThousands,y
bne _gotdigit
_end lda #0
sta string_out,x
plx
rts
_allzero lda #'0'
sta string_out,x
inx
bne _end
}}
}
asmsub str_w (word value @ AY) clobbers(A,Y) {
; ---- convert the (signed) word in A/Y in decimal string form, without left padding 0's
%asm {{
cpy #0
bpl str_uw
phx
pha
lda #'-'
sta string_out
tya
eor #255
tay
pla
eor #255
clc
adc #1
bcc +
iny
+ jsr conv.uword2decimal
ldx #1
bne str_uw._output_digits
}}
}
@ -520,4 +494,243 @@ _stop
}}
}
; ----- low level number conversions to decimal strings ----
asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
%asm {{
ldy #uword2decimal.ASCII_0_OFFSET
bne uword2decimal.hex_try200
rts
}}
}
asmsub uword2decimal (uword value @AY) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- convert 16 bit uword in A/Y to decimal
; output in uword2decimal.decTenThousands, decThousands, decHundreds, decTens, decOnes
; (these are terminated by a zero byte so they can be easily printed)
; also returns Y = 100's, A = 10's, X = 1's
%asm {{
;Convert 16 bit Hex to Decimal (0-65535) Rev 2
;By Omegamatrix Further optimizations by tepples
; routine from http://forums.nesdev.com/viewtopic.php?f=2&t=11341&start=15
;HexToDec99
; start in A
; end with A = 10's, decOnes (also in X)
;HexToDec255
; start in A
; end with Y = 100's, A = 10's, decOnes (also in X)
;HexToDec999
; start with A = high byte, Y = low byte
; end with Y = 100's, A = 10's, decOnes (also in X)
; requires 1 extra temp register on top of decOnes, could combine
; these two if HexToDec65535 was eliminated...
;HexToDec65535
; start with A/Y (low/high) as 16 bit value
; end with decTenThousand, decThousand, Y = 100's, A = 10's, decOnes (also in X)
; (irmen: I store Y and A in decHundreds and decTens too, so all of it can be easily printed)
ASCII_0_OFFSET = $30
temp = P8ZP_SCRATCH_B1 ; byte in zeropage
hexHigh = P8ZP_SCRATCH_W1 ; byte in zeropage
hexLow = P8ZP_SCRATCH_W1+1 ; byte in zeropage
HexToDec65535; SUBROUTINE
sty hexHigh ;3 @9
sta hexLow ;3 @12
tya
tax ;2 @14
lsr a ;2 @16
lsr a ;2 @18 integer divide 1024 (result 0-63)
cpx #$A7 ;2 @20 account for overflow of multiplying 24 from 43,000 ($A7F8) onward,
adc #1 ;2 @22 we can just round it to $A700, and the divide by 1024 is fine...
;at this point we have a number 1-65 that we have to times by 24,
;add to original sum, and Mod 1024 to get a remainder 0-999
sta temp ;3 @25
asl a ;2 @27
adc temp ;3 @30 x3
tay ;2 @32
lsr a ;2 @34
lsr a ;2 @36
lsr a ;2 @38
lsr a ;2 @40
lsr a ;2 @42
tax ;2 @44
tya ;2 @46
asl a ;2 @48
asl a ;2 @50
asl a ;2 @52
clc ;2 @54
adc hexLow ;3 @57
sta hexLow ;3 @60
txa ;2 @62
adc hexHigh ;3 @65
sta hexHigh ;3 @68
ror a ;2 @70
lsr a ;2 @72
tay ;2 @74 integer divide 1,000 (result 0-65)
lsr a ;2 @76 split the 1,000 and 10,000 digit
tax ;2 @78
lda ShiftedBcdTab,x ;4 @82
tax ;2 @84
rol a ;2 @86
and #$0F ;2 @88
ora #ASCII_0_OFFSET
sta decThousands ;3 @91
txa ;2 @93
lsr a ;2 @95
lsr a ;2 @97
lsr a ;2 @99
ora #ASCII_0_OFFSET
sta decTenThousands ;3 @102
lda hexLow ;3 @105
cpy temp ;3 @108
bmi _doSubtract ;2³ @110/111
beq _useZero ;2³ @112/113
adc #23 + 24 ;2 @114
_doSubtract
sbc #23 ;2 @116
sta hexLow ;3 @119
_useZero
lda hexHigh ;3 @122
sbc #0 ;2 @124
Start100s
and #$03 ;2 @126
tax ;2 @128 0,1,2,3
cmp #2 ;2 @130
rol a ;2 @132 0,2,5,7
ora #ASCII_0_OFFSET
tay ;2 @134 Y = Hundreds digit
lda hexLow ;3 @137
adc Mod100Tab,x ;4 @141 adding remainder of 256, 512, and 256+512 (all mod 100)
bcs hex_doSub200 ;2³ @143/144
hex_try200
cmp #200 ;2 @145
bcc hex_try100 ;2³ @147/148
hex_doSub200
iny ;2 @149
iny ;2 @151
sbc #200 ;2 @153
hex_try100
cmp #100 ;2 @155
bcc HexToDec99 ;2³ @157/158
iny ;2 @159
sbc #100 ;2 @161
HexToDec99; SUBROUTINE
lsr a ;2 @163
tax ;2 @165
lda ShiftedBcdTab,x ;4 @169
tax ;2 @171
rol a ;2 @173
and #$0F ;2 @175
ora #ASCII_0_OFFSET
sta decOnes ;3 @178
txa ;2 @180
lsr a ;2 @182
lsr a ;2 @184
lsr a ;2 @186
ora #ASCII_0_OFFSET
; irmen: load X with ones, and store Y and A too, for easy printing afterwards
sty decHundreds
sta decTens
ldx decOnes
rts ;6 @192 Y=hundreds, A = tens digit, X=ones digit
HexToDec999; SUBROUTINE
sty hexLow ;3 @9
jmp Start100s ;3 @12
Mod100Tab
.byte 0,56,12,56+12
ShiftedBcdTab
.byte $00,$01,$02,$03,$04,$08,$09,$0A,$0B,$0C
.byte $10,$11,$12,$13,$14,$18,$19,$1A,$1B,$1C
.byte $20,$21,$22,$23,$24,$28,$29,$2A,$2B,$2C
.byte $30,$31,$32,$33,$34,$38,$39,$3A,$3B,$3C
.byte $40,$41,$42,$43,$44,$48,$49,$4A,$4B,$4C
decTenThousands .byte 0
decThousands .byte 0
decHundreds .byte 0
decTens .byte 0
decOnes .byte 0
.byte 0 ; zero-terminate the decimal output string
}}
}
asmsub byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A (signed byte) to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
; note: if the number is negative, you have to deal with the '-' yourself!
%asm {{
cmp #0
bpl +
eor #255
clc
adc #1
+ jmp ubyte2decimal
}}
}
asmsub ubyte2hex (ubyte value @A) -> ubyte @A, ubyte @Y {
; ---- A to hex petscii string in AY (first hex char in A, second hex char in Y)
%asm {{
stx P8ZP_SCRATCH_REG
pha
and #$0f
tax
ldy _hex_digits,x
pla
lsr a
lsr a
lsr a
lsr a
tax
lda _hex_digits,x
ldx P8ZP_SCRATCH_REG
rts
_hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as well
}}
}
asmsub uword2hex (uword value @AY) clobbers(A,Y) {
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
%asm {{
sta P8ZP_SCRATCH_REG
tya
jsr ubyte2hex
sta output
sty output+1
lda P8ZP_SCRATCH_REG
jsr ubyte2hex
sta output+2
sty output+3
rts
output .text "0000", $00 ; 0-terminated output buffer (to make printing easier)
}}
}
}

View File

@ -79,10 +79,10 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
%asm {{
phx
sta P8ZP_SCRATCH_W2
sta _tmp
sty P8ZP_SCRATCH_B1
tya
ldy P8ZP_SCRATCH_W2
ldy _tmp
jsr GIVAYF ; load it as signed... correct afterwards
lda P8ZP_SCRATCH_B1
bpl +
@ -91,6 +91,7 @@ asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
jsr FADD
+ plx
rts
_tmp .byte 0
_flt65536 .byte 145,0,0,0,0 ; 65536.0
}}
}
@ -128,6 +129,14 @@ asmsub GETADRAY () clobbers(X) -> uword @ AY {
}}
}
asmsub FREADUY (ubyte value @Y) {
; -- 8 bit unsigned Y -> float in fac1
%asm {{
lda #0
jmp GIVAYF
}}
}
sub print_f (float value) {
; ---- prints the floating point value (without a newline).
%asm {{

View File

@ -36,7 +36,7 @@ gfx2 {
sub screen_mode(ubyte mode) {
when mode {
1 -> {
; lores monchrome
; lores monochrome
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
cx16.VERA_DC_HSCALE = 64
cx16.VERA_DC_VSCALE = 64
@ -404,7 +404,7 @@ _done
; TODO also mostly usable for lores 4c?
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
; TODO optimize the loop in pure assembly
; TODO optimize this vertical line loop in pure assembly
color &= 3
color <<= gfx2.plot.shift4c[lsb(x) & 3]
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
@ -433,48 +433,46 @@ _done
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
; Bresenham algorithm.
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
; TODO there are some slight errors at the first/last pixels in certain slopes...
if y1>y2 {
; make sure dy is always positive to have only 4 instead of 8 special cases
swap(x1, x2)
swap(y1, y2)
}
word @zp dx = x2-x1 as word
word @zp dy = y2-y1 as word
word @zp dx = (x2 as word)-x1
word @zp dy = (y2 as word)-y1
if dx==0 {
vertical_line(x1, y1, abs(dy)+1 as uword, color)
vertical_line(x1, y1, abs(dy) as uword +1, color)
return
}
if dy==0 {
if x1>x2
x1=x2
horizontal_line(x1, y1, abs(dx)+1 as uword, color)
horizontal_line(x1, y1, abs(dx) as uword +1, color)
return
}
; TODO rewrite the rest in optimized assembly (or reuse GRAPH_draw_line if we can get the FB replacement vector layer working)
word @zp d = 0
ubyte positive_ix = true
cx16.r13 = true ; 'positive_ix'
if dx < 0 {
dx = -dx
positive_ix = false
cx16.r13 = false
}
dx *= 2
dy *= 2
word @zp dx2 = dx*2
word @zp dy2 = dy*2
cx16.r14 = x1 ; internal plot X
if dx >= dy {
if positive_ix {
if cx16.r13 {
repeat {
plot(cx16.r14, y1, color)
if cx16.r14==x2
return
cx16.r14++
d += dy
d += dy2
if d > dx {
y1++
d -= dx
d -= dx2
}
}
} else {
@ -483,25 +481,25 @@ _done
if cx16.r14==x2
return
cx16.r14--
d += dy
d += dy2
if d > dx {
y1++
d -= dx
d -= dx2
}
}
}
}
else {
if positive_ix {
if cx16.r13 {
repeat {
plot(cx16.r14, y1, color)
if y1 == y2
return
y1++
d += dx
d += dx2
if d > dy {
cx16.r14++
d -= dy
d -= dy2
}
}
} else {
@ -510,10 +508,10 @@ _done
if y1 == y2
return
y1++
d += dx
d += dx2
if d > dy {
cx16.r14--
d -= dy
d -= dy2
}
}
}

View File

@ -4,7 +4,7 @@
; Bitmap pixel graphics module for the CommanderX16
; wraps the graphics functions that are in ROM.
; only black/white monchrome 320x200 for now. (i.e. truncated at the bottom)
; only black/white monochrome 320x200 for now. (i.e. truncated at the bottom)
; For full-screen 640x480 or 320x240 graphics, use the "gfx2" module instead. (but that is Cx16-specific)
; Note: there is no color palette manipulation here, you have to do that yourself or use the "palette" module.

View File

@ -35,7 +35,7 @@ romsub $FFAE = UNLSN() clobbers(A) ; command serial
romsub $FFB1 = LISTEN(ubyte device @ A) clobbers(A) ; command serial bus device to LISTEN
romsub $FFB4 = TALK(ubyte device @ A) clobbers(A) ; command serial bus device to TALK
romsub $FFB7 = READST() -> ubyte @ A ; read I/O status word
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte address @ Y) ; set logical file parameters
romsub $FFBA = SETLFS(ubyte logical @ A, ubyte device @ X, ubyte secondary @ Y) ; set logical file parameters
romsub $FFBD = SETNAM(ubyte namelen @ A, str filename @ XY) ; set filename parameters
romsub $FFC0 = OPEN() clobbers(X,Y) -> ubyte @Pc, ubyte @A ; (via 794 ($31A)) open a logical file
romsub $FFC3 = CLOSE(ubyte logical @ A) clobbers(A,X,Y) ; (via 796 ($31C)) close a logical file
@ -44,8 +44,8 @@ romsub $FFC9 = CHKOUT(ubyte logical @ X) clobbers(A,X) ; (via 800 ($320
romsub $FFCC = CLRCHN() clobbers(A,X) ; (via 802 ($322)) restore default devices
romsub $FFCF = CHRIN() clobbers(X, Y) -> ubyte @ A ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
romsub $FFD2 = CHROUT(ubyte char @ A) ; (via 806 ($326)) output a character
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, uword @ XY ; (via 816 ($330)) load from device
romsub $FFD8 = SAVE(ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A ; (via 818 ($332)) save to a device
romsub $FFDB = SETTIM(ubyte low @ A, ubyte middle @ X, ubyte high @ Y) ; set the software clock
romsub $FFDE = RDTIM() -> ubyte @ A, ubyte @ X, ubyte @ Y ; read the software clock
romsub $FFE1 = STOP() clobbers(X) -> ubyte @ Pz, ubyte @ A ; (via 808 ($328)) check the STOP key (and some others in A)
@ -350,75 +350,115 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO
}
asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
; -- write a single byte to VERA's video memory
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
sty cx16.VERA_DATA0
rts
}}
; -- write a single byte to VERA's video memory
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
sty cx16.VERA_DATA0
rts
}}
}
asmsub vpoke_or(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
; -- or a single byte to the value already in the VERA's video memory at that location
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
tya
ora cx16.VERA_DATA0
sta cx16.VERA_DATA0
rts
}}
; -- or a single byte to the value already in the VERA's video memory at that location
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
tya
ora cx16.VERA_DATA0
sta cx16.VERA_DATA0
rts
}}
}
asmsub vpoke_and(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) {
; -- and a single byte to the value already in the VERA's video memory at that location
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
tya
and cx16.VERA_DATA0
sta cx16.VERA_DATA0
rts
}}
; -- and a single byte to the value already in the VERA's video memory at that location
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
tya
and cx16.VERA_DATA0
sta cx16.VERA_DATA0
rts
}}
}
asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A) {
; -- xor a single byte to the value already in the VERA's video memory at that location
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
tya
eor cx16.VERA_DATA0
sta cx16.VERA_DATA0
rts
}}
; -- xor a single byte to the value already in the VERA's video memory at that location
; note: inefficient when writing multiple sequential bytes!
%asm {{
stz cx16.VERA_CTRL
and #1
sta cx16.VERA_ADDR_H
lda cx16.r0
sta cx16.VERA_ADDR_L
lda cx16.r0+1
sta cx16.VERA_ADDR_M
tya
eor cx16.VERA_DATA0
sta cx16.VERA_DATA0
rts
}}
}
asmsub vload(str name @R0, ubyte device @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
; -- like the basic command VLOAD "filename",device,bank,address
; loads a file into video memory in the given bank:address, returns success in A
; !! NOTE !! the V38 ROMs contain a bug in the LOAD code that makes the load address not work correctly,
; it works fine when loading from local filesystem
%asm {{
; -- load a file into video ram
phx
pha
tya
tax
lda #1
ldy #0
jsr c64.SETLFS
lda cx16.r0
ldy cx16.r0+1
jsr prog8_lib.strlen
tya
ldx cx16.r0
ldy cx16.r0+1
jsr c64.SETNAM
pla
clc
adc #2
ldx cx16.r1
ldy cx16.r1+1
stz P8ZP_SCRATCH_B1
jsr c64.LOAD
bcs +
inc P8ZP_SCRATCH_B1
+ jsr c64.CLRCHN
lda #1
jsr c64.CLOSE
plx
lda P8ZP_SCRATCH_B1
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

View File

@ -420,7 +420,7 @@ _print_byte_digits
jsr c64.CHROUT
pla
jsr c64.CHROUT
jmp _ones
bra _ones
+ pla
cmp #'0'
beq _ones
@ -443,7 +443,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
jsr c64.CHROUT
+ pla
jsr conv.byte2decimal
jmp print_ub._print_byte_digits
bra print_ub._print_byte_digits
}}
}
@ -494,7 +494,7 @@ asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
jsr print_ubbin
pla
clc
jmp print_ubbin
bra print_ubbin
}}
}
@ -507,7 +507,7 @@ asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
jsr print_ubhex
pla
clc
jmp print_ubhex
bra print_ubhex
}}
}
@ -570,7 +570,7 @@ asmsub print_w (word value @ AY) clobbers(A,Y) {
adc #1
bcc +
iny
+ jmp print_uw
+ bra print_uw
}}
}

View File

@ -244,8 +244,8 @@ randseed .proc
.pend
randbyte .proc
; -- 8-bit pseudo random number generator into A
fast_randbyte .proc
; -- fast but bad 8-bit pseudo random number generator into A
lda _seed
beq _eor
asl a
@ -263,6 +263,10 @@ _seed .byte $3a
.pend
randbyte .proc
; -- 8 bit pseudo random number generator into A (by just reusing randword)
jmp randword
.pend
randword .proc
; -- 16 bit pseudo random number generator into AY
@ -1537,3 +1541,71 @@ _negative lsr a
rts
.pend
square .proc
; -- calculate square root of signed word in AY, result in AY
; routine by Lee Davsion, source: http://6502.org/source/integers/square.htm
; using this routine is about twice as fast as doing a regular multiplication.
;
; Calculates the 16 bit unsigned integer square of the signed 16 bit integer in
; Numberl/Numberh. The result is always in the range 0 to 65025 and is held in
; Squarel/Squareh
;
; The maximum input range is only +/-255 and no checking is done to ensure that
; this is so.
;
; This routine is useful if you are trying to draw circles as for any circle
;
; x^2+y^2=r^2 where x and y are the co-ordinates of any point on the circle and
; r is the circle radius
numberl = P8ZP_SCRATCH_W1 ; number to square low byte
numberh = P8ZP_SCRATCH_W1+1 ; number to square high byte
squarel = P8ZP_SCRATCH_W2 ; square low byte
squareh = P8ZP_SCRATCH_W2+1 ; square high byte
tempsq = P8ZP_SCRATCH_B1 ; temp byte for intermediate result
sta numberl
sty numberh
stx P8ZP_SCRATCH_REG
lda #$00 ; clear a
sta squarel ; clear square low byte
; (no need to clear the high byte, it gets shifted out)
lda numberl ; get number low byte
ldx numberh ; get number high byte
bpl _nonneg ; if +ve don't negate it
; else do a two's complement
eor #$ff ; invert
sec ; +1
adc #$00 ; and add it
_nonneg:
sta tempsq ; save abs(number)
ldx #$08 ; set bit count
_nextr2bit:
asl squarel ; low byte *2
rol squareh ; high byte *2+carry from low
asl a ; shift number byte
bcc _nosqadd ; don't do add if c = 0
tay ; save a
clc ; clear carry for add
lda tempsq ; get number
adc squarel ; add number^2 low byte
sta squarel ; save number^2 low byte
lda #$00 ; clear a
adc squareh ; add number^2 high byte
sta squareh ; save number^2 high byte
tya ; get a back
_nosqadd:
dex ; decrement bit count
bne _nextr2bit ; go do next bit
lda squarel
ldy squareh
ldx P8ZP_SCRATCH_REG
rts
.pend

View File

@ -387,6 +387,14 @@ func_sqrt16_into_A .proc
rts
.pend
func_fastrnd8_stack .proc
; -- put a random ubyte on the estack (using fast but bad RNG)
jsr math.fast_randbyte
sta P8ESTACK_LO,x
dex
rts
.pend
func_rnd_stack .proc
; -- put a random ubyte on the estack
jsr math.randbyte
@ -432,6 +440,7 @@ func_min_ub_stack .proc
func_min_b_into_A .proc
; -- min(barray) -> A. (array in P8ZP_SCRATCH_W1, num elements in A)
tay
dey
lda #127
sta P8ZP_SCRATCH_B1
- lda (P8ZP_SCRATCH_W1),y
@ -548,6 +557,7 @@ func_min_w_stack .proc
func_max_ub_into_A .proc
; -- max(ubarray) -> A (array in P8ZP_SCRATCH_W1, num elements in A)
tay
dey
lda #0
sta P8ZP_SCRATCH_B1
- lda (P8ZP_SCRATCH_W1),y

View File

@ -1072,3 +1072,14 @@ sign_extend_AY_byte .proc
rts
.pend
strlen .proc
; -- returns the number of bytes in the string in AY, in Y.
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
ldy #0
- lda (P8ZP_SCRATCH_W1),y
beq +
iny
bne -
+ rts
.pend

View File

@ -1 +1 @@
6.2
6.4

View File

@ -8,6 +8,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor
import prog8.compiler.target.ICompilationTarget
@ -154,16 +155,6 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
// The only place for now where we can do this is for:
// asmsub register pair parameter.
if(typecast.type in WordDatatypes) {
val fcall = typecast.parent as? IFunctionCall
if (fcall != null) {
val sub = fcall.target.targetStatement(program) as? Subroutine
if (sub != null && sub.isAsmSubroutine) {
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
}
}
}
if(sourceDt in PassByReferenceDatatypes) {
if(typecast.type==DataType.UWORD) {
if(typecast.expression is IdentifierReference) {
@ -216,4 +207,93 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
}
return noModifications
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
if(functionCallStatement.target.nameInSource==listOf("cmp")) {
// if the datatype of the arguments of cmp() are different, cast the byte one to word.
val arg1 = functionCallStatement.args[0]
val arg2 = functionCallStatement.args[1]
val dt1 = arg1.inferType(program).typeOrElse(DataType.STRUCT)
val dt2 = arg2.inferType(program).typeOrElse(DataType.STRUCT)
if(dt1 in ByteDatatypes) {
if(dt2 in ByteDatatypes)
return noModifications
val cast1 = TypecastExpression(arg1, if(dt1==DataType.UBYTE) DataType.UWORD else DataType.WORD, true, functionCallStatement.position)
return listOf(IAstModification.ReplaceNode(arg1, cast1, functionCallStatement))
} else {
if(dt2 in WordDatatypes)
return noModifications
val cast2 = TypecastExpression(arg2, if(dt2==DataType.UBYTE) DataType.UWORD else DataType.WORD, true, functionCallStatement.position)
return listOf(IAstModification.ReplaceNode(arg2, cast2, functionCallStatement))
}
}
return noModifications
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val containingStatement = getContainingStatement(arrayIndexedExpression)
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
return noModifications
}
val index = arrayIndexedExpression.indexer.indexExpr
if(index !is NumericLiteralValue && index !is IdentifierReference) {
// replace complex indexing expression with a temp variable to hold the computed index first
return getAutoIndexerVarFor(arrayIndexedExpression)
}
return noModifications
}
private fun getComplexArrayIndexedExpressions(stmt: Statement): List<ArrayIndexedExpression> {
class Searcher : IAstVisitor {
val complexArrayIndexedExpressions = mutableListOf<ArrayIndexedExpression>()
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
val ix = arrayIndexedExpression.indexer.indexExpr
if(ix !is NumericLiteralValue && ix !is IdentifierReference)
complexArrayIndexedExpressions.add(arrayIndexedExpression)
}
override fun visit(branchStatement: BranchStatement) {}
override fun visit(forLoop: ForLoop) {}
override fun visit(ifStatement: IfStatement) {
ifStatement.condition.accept(this)
}
override fun visit(untilLoop: UntilLoop) {
untilLoop.condition.accept(this)
}
}
val searcher = Searcher()
stmt.accept(searcher)
return searcher.complexArrayIndexedExpressions
}
private fun getContainingStatement(expression: Expression): Statement {
var node: Node = expression
while(node !is Statement)
node = node.parent
return node
}
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
val modifications = mutableListOf<IAstModification>()
val statement = expr.containingStatement()
// replace the indexer with just the variable (simply use a cx16 virtual register r9, that we HOPE is not used for other things in the expression...)
// assign the indexing expression to the helper variable, but only if that hasn't been done already
val target = AssignTarget(IdentifierReference(listOf("cx16", "r9"), expr.indexer.position), null, null, expr.indexer.position)
val assign = Assignment(target, expr.indexer.indexExpr, expr.indexer.position)
modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope()))
modifications.add(IAstModification.ReplaceNode(expr.indexer.indexExpr, target.identifier!!.copy(), expr.indexer))
return modifications
}
}

View File

@ -157,7 +157,7 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
}
}
else if(func.known_returntype==null)
throw IllegalArgumentException("builtin function $name can't be used here because it doesn't return a value")
return null // builtin function $name can't be used here because it doesn't return a value
}
return null
}
@ -297,7 +297,7 @@ private fun writeAssembly(programAst: Program,
errors: IErrorReporter,
outputDir: Path,
compilerOptions: CompilationOptions): String {
// asm generation directly from the Ast,
// asm generation directly from the Ast
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
errors.report()

View File

@ -218,13 +218,13 @@ internal class AstChecker(private val program: Program,
err("subroutines can only have one return value")
// subroutine must contain at least one 'return' or 'goto'
// (or if it has an asm block, that must contain a 'rts' or 'jmp')
// (or if it has an asm block, that must contain a 'rts' or 'jmp' or 'bra')
if(subroutine.statements.count { it is Return || it is Jump } == 0) {
if (subroutine.amountOfRtsInAsm() == 0) {
if (subroutine.returntypes.isNotEmpty()) {
// for asm subroutines with an address, no statement check is possible.
if (subroutine.asmAddress == null && !subroutine.inline)
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)")
err("non-inline subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or rts/jmp/bra in case of %asm)")
}
}
}
@ -374,7 +374,7 @@ internal class AstChecker(private val program: Program,
if(!idt.isKnown) {
errors.err("return type mismatch", assignment.value.position)
}
if(stmt.returntypes.size <= 1 && stmt.returntypes.single() isNotAssignableTo idt.typeOrElse(DataType.BYTE)) {
if(stmt.returntypes.isEmpty() || (stmt.returntypes.size == 1 && stmt.returntypes.single() isNotAssignableTo idt.typeOrElse(DataType.BYTE))) {
errors.err("return type mismatch", assignment.value.position)
}
}
@ -493,7 +493,7 @@ internal class AstChecker(private val program: Program,
fun err(msg: String, position: Position?=null) = errors.err(msg, position ?: decl.position)
// the initializer value can't refer to the variable itself (recursive definition)
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexVar?.referencesIdentifier(decl.name) == true)
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.referencesIdentifier(decl.name) == true)
err("recursive var declaration")
// CONST can only occur on simple types (byte, word, float)
@ -953,6 +953,20 @@ internal class AstChecker(private val program: Program,
}
}
// functions that don't return a value, can't be used in an expression or assignment
if(targetStatement is Subroutine) {
if(targetStatement.returntypes.isEmpty()) {
if(functionCall.parent is Expression || functionCall.parent is Assignment)
errors.err("subroutine doesn't return a value", functionCall.position)
}
}
else if(targetStatement is BuiltinFunctionStatementPlaceholder) {
if(builtinFunctionReturnType(targetStatement.name, functionCall.args, program).isUnknown) {
if(functionCall.parent is Expression || functionCall.parent is Assignment)
errors.err("function doesn't return a value", functionCall.position)
}
}
super.visit(functionCall)
}
@ -1111,15 +1125,9 @@ internal class AstChecker(private val program: Program,
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
// check index value 0..255
val dtxNum = arrayIndexedExpression.indexer.indexNum?.inferType(program)?.typeOrElse(DataType.STRUCT)
if(dtxNum!=null && dtxNum != DataType.UBYTE && dtxNum != DataType.BYTE)
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
if(!dtxNum.istype(DataType.UBYTE) && !dtxNum.istype(DataType.BYTE))
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
val dtxVar = arrayIndexedExpression.indexer.indexVar?.inferType(program)?.typeOrElse(DataType.STRUCT)
if(dtxVar!=null && dtxVar != DataType.UBYTE && dtxVar != DataType.BYTE)
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
if(arrayIndexedExpression.indexer.origExpression!=null)
throw FatalAstException("array indexer should have been replaced with a temp var @ ${arrayIndexedExpression.indexer.position}")
super.visit(arrayIndexedExpression)
}

View File

@ -1,6 +1,9 @@
package prog8.compiler.astprocessing
import prog8.ast.*
import prog8.ast.IFunctionCall
import prog8.ast.Module
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
@ -89,8 +92,7 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
// rewrite pointervar[index] into @(pointervar+index)
val indexer = arrayIndexedExpression.indexer
val index = (indexer.indexNum ?: indexer.indexVar)!!
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", index, arrayIndexedExpression.position)
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", indexer.indexExpr, arrayIndexedExpression.position)
return if(parent is AssignTarget) {
// we're part of the target of an assignment, we have to actually change the assign target itself
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
@ -102,23 +104,7 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
}
}
when (val expr2 = arrayIndexedExpression.indexer.origExpression) {
is NumericLiteralValue -> {
arrayIndexedExpression.indexer.indexNum = expr2
arrayIndexedExpression.indexer.origExpression = null
return noModifications
}
is IdentifierReference -> {
arrayIndexedExpression.indexer.indexVar = expr2
arrayIndexedExpression.indexer.origExpression = null
return noModifications
}
is Expression -> {
// replace complex indexing with a temp variable
return getAutoIndexerVarFor(arrayIndexedExpression)
}
else -> return noModifications
}
return noModifications
}
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
@ -202,38 +188,6 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
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 repo = subroutine.asmGenInfo.usedAutoArrayIndexerForStatements
// TODO make this a bit smarter so it can reuse indexer variables. BUT BEWARE of scoping+initialization problems then
// add another loop index var to be used for this expression
val indexerVarName = "$indexerVarPrefix${expr.indexer.hashCode()}"
val indexerVar = AsmGenInfo.ArrayIndexerInfo(indexerVarName, expr.indexer)
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))
// 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(indexerVar.name), indexerExpression.position), null, null, indexerExpression.position)
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
expr.indexer.origExpression = null
}, target.identifier!!.copy(), expr.indexer))
return modifications
}
override fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> {
val choices = whenStatement.choiceValues(program).sortedBy {
it.first?.first() ?: Int.MAX_VALUE
@ -268,32 +222,23 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
val valueType = assignment.value.inferType(program)
val targetType = assignment.target.inferType(program)
var assignments = emptyList<Assignment>()
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
assignments = if (assignment.value is ArrayLiteralValue) {
flattenStructAssignmentFromStructLiteral(assignment) // 'structvar = [ ..... ] '
if (assignment.value is ArrayLiteralValue) {
errors.err("cannot assign non-const array value, use separate assignment per field", assignment.position)
} else {
flattenStructAssignmentFromIdentifier(assignment) // 'structvar1 = structvar2'
return copyStructValue(assignment)
}
}
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
assignments = if (assignment.value is ArrayLiteralValue) {
flattenArrayAssignmentFromArrayLiteral(assignment) // 'arrayvar = [ ..... ] '
if (assignment.value is ArrayLiteralValue) {
errors.err("cannot assign non-const array value, use separate assignment per element", assignment.position)
} else {
flattenArrayAssignmentFromIdentifier(assignment) // 'arrayvar1 = arrayvar2'
return copyArrayValue(assignment)
}
}
if(assignments.isNotEmpty()) {
val modifications = mutableListOf<IAstModification>()
val scope = assignment.definingScope()
assignments.reversed().mapTo(modifications) { IAstModification.InsertAfter(assignment, it, scope) }
modifications.add(IAstModification.Remove(assignment, scope))
return modifications
}
return noModifications
}
@ -346,114 +291,98 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
return noModifications
}
private fun flattenArrayAssignmentFromArrayLiteral(assign: Assignment): List<Assignment> {
private fun copyArrayValue(assign: Assignment): List<IAstModification> {
val identifier = assign.target.identifier!!
val targetVar = identifier.targetVarDecl(program)!!
val alv = assign.value as? ArrayLiteralValue
return flattenArrayAssign(targetVar, alv, identifier, assign.position)
}
private fun flattenArrayAssignmentFromIdentifier(assign: Assignment): List<Assignment> {
val identifier = assign.target.identifier!!
val targetVar = identifier.targetVarDecl(program)!!
if(targetVar.arraysize==null)
errors.err("array has no defined size", assign.position)
if(assign.value !is IdentifierReference) {
errors.err("invalid array value to assign to other array", assign.value.position)
return noModifications
}
val sourceIdent = assign.value as IdentifierReference
val sourceVar = sourceIdent.targetVarDecl(program)!!
if(!sourceVar.isArray) {
errors.err("value must be an array", sourceIdent.position)
return emptyList()
errors.err("value must be an array", sourceIdent.position)
} else {
if (sourceVar.arraysize!!.constIndex() != targetVar.arraysize!!.constIndex())
errors.err("element count mismatch", assign.position)
if (sourceVar.datatype != targetVar.datatype)
errors.err("element type mismatch", assign.position)
}
val alv = sourceVar.value as? ArrayLiteralValue
return flattenArrayAssign(targetVar, alv, identifier, assign.position)
if(!errors.isEmpty())
return noModifications
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assign.position),
mutableListOf(
AddressOf(sourceIdent, assign.position),
AddressOf(identifier, assign.position),
NumericLiteralValue.optimalInteger(targetVar.arraysize!!.constIndex()!!, assign.position)
),
true,
assign.position
)
return listOf(IAstModification.ReplaceNode(assign, memcopy, assign.parent))
}
private fun flattenArrayAssign(targetVar: VarDecl, alv: ArrayLiteralValue?, identifier: IdentifierReference, position: Position): List<Assignment> {
if(targetVar.arraysize==null) {
errors.err("array has no defined size", identifier.position)
return emptyList()
}
if(alv==null || alv.value.size != targetVar.arraysize!!.constIndex()) {
errors.err("element count mismatch", position)
return emptyList()
}
// TODO use memcopy instead of individual assignments after certain amount of elements
// TODO what does assigning a struct var use?
return alv.value.mapIndexed { index, value ->
val idx = ArrayIndexedExpression(identifier, ArrayIndex(NumericLiteralValue(DataType.UBYTE, index, position), position), position)
Assignment(AssignTarget(null, idx, null, position), value, value.position)
}
}
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment): List<Assignment> {
private fun copyStructValue(structAssignment: Assignment): List<IAstModification> {
val identifier = structAssignment.target.identifier!!
val identifierName = identifier.nameInSource.single()
val targetVar = identifier.targetVarDecl(program)!!
val struct = targetVar.struct!!
val slv = structAssignment.value as? ArrayLiteralValue
if(slv==null || slv.value.size != struct.numberOfElements) {
errors.err("element count mismatch", structAssignment.position)
return emptyList()
}
return struct.statements.zip(slv.value).map { (targetDecl, sourceValue) ->
targetDecl as VarDecl
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position),
sourceValue, sourceValue.position)
assign.linkParents(structAssignment)
assign
}
}
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment): List<Assignment> {
// TODO use memcopy beyond a certain number of elements
val identifier = structAssignment.target.identifier!!
val identifierName = identifier.nameInSource.single()
val targetVar = identifier.targetVarDecl(program)!!
val struct = targetVar.struct!!
when (structAssignment.value) {
is IdentifierReference -> {
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program)!!
val memsize = struct.memsize(program.memsizer)
when {
sourceVar.struct!=null -> {
// struct memberwise copy
val sourceStruct = sourceVar.struct!!
if(sourceStruct!==targetVar.struct) {
// structs are not the same in assignment
return listOf() // error will be printed elsewhere
errors.err("struct type mismatch", structAssignment.position)
return listOf()
}
if(struct.statements.size!=sourceStruct.statements.size)
return listOf() // error will be printed elsewhere
return struct.statements.zip(sourceStruct.statements).map { member ->
val targetDecl = member.first as VarDecl
val sourceDecl = member.second as VarDecl
if(targetDecl.name != sourceDecl.name)
throw FatalAstException("struct member mismatch")
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name)
val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position)
val assign = Assignment(AssignTarget(idref, null, null, structAssignment.position), sourceIdref, member.second.position)
assign.linkParents(structAssignment)
assign
if(struct.statements.size!=sourceStruct.statements.size) {
errors.err("struct element count mismatch", structAssignment.position)
return listOf()
}
if(memsize!=sourceStruct.memsize(program.memsizer)) {
errors.err("memory size mismatch", structAssignment.position)
return listOf()
}
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), structAssignment.position),
mutableListOf(
AddressOf(structAssignment.value as IdentifierReference, structAssignment.position),
AddressOf(identifier, structAssignment.position),
NumericLiteralValue.optimalInteger(memsize, structAssignment.position)
),
true,
structAssignment.position
)
return listOf(IAstModification.ReplaceNode(structAssignment, memcopy, structAssignment.parent))
}
sourceVar.isArray -> {
val array = (sourceVar.value as ArrayLiteralValue).value
if(struct.statements.size!=array.size)
return listOf() // error will be printed elsewhere
return struct.statements.zip(array).map {
val decl = it.first as VarDecl
val mangled = mangledStructMemberName(identifierName, decl.name)
val targetName = IdentifierReference(listOf(mangled), structAssignment.position)
val target = AssignTarget(targetName, null, null, structAssignment.position)
val assign = Assignment(target, it.second, structAssignment.position)
assign.linkParents(structAssignment)
assign
val array = sourceVar.value as ArrayLiteralValue
if(struct.statements.size!=array.value.size) {
errors.err("struct element count mismatch", structAssignment.position)
return listOf()
}
if(memsize!=array.memsize(program.memsizer)) {
errors.err("memory size mismatch", structAssignment.position)
return listOf()
}
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), structAssignment.position),
mutableListOf(
AddressOf(structAssignment.value as IdentifierReference, structAssignment.position),
AddressOf(identifier, structAssignment.position),
NumericLiteralValue.optimalInteger(memsize, structAssignment.position)
),
true,
structAssignment.position
)
return listOf(IAstModification.ReplaceNode(structAssignment, memcopy, structAssignment.parent))
}
else -> {
throw FatalAstException("can only assign arrays or structs to structs")
@ -461,7 +390,7 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
}
}
is ArrayLiteralValue -> {
throw IllegalArgumentException("not going to flatten a structLv assignment here")
throw IllegalArgumentException("not going to do a structLv assignment here")
}
else -> throw FatalAstException("strange struct value")
}

View File

@ -97,6 +97,7 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("ror2" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
// these few have a return value depending on the argument(s):
FSignature("max" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
FSignature("min" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
@ -137,6 +138,7 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("peekw" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UWORD),
FSignature("poke" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UBYTE))), null),
FSignature("pokew" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UWORD))), null),
FSignature("fastrnd8" , false, emptyList(), DataType.UBYTE),
FSignature("rnd" , false, emptyList(), DataType.UBYTE),
FSignature("rndw" , false, emptyList(), DataType.UWORD),
FSignature("rndf" , false, emptyList(), DataType.FLOAT),
@ -320,8 +322,7 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
val target = (args[0] as IdentifierReference).targetStatement(program)
?: throw CannotEvaluateException("sizeof", "no target")
fun structSize(target: StructDecl) =
NumericLiteralValue(DataType.UBYTE, target.statements.map { memsizer.memorySize((it as VarDecl).datatype) }.sum(), position)
fun structSize(target: StructDecl) = NumericLiteralValue(DataType.UBYTE, target.memsize(memsizer), position)
return when {
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {

View File

@ -12,7 +12,7 @@ import prog8.compiler.IErrorReporter
import prog8.compiler.Zeropage
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.cpu6502.codegen.AsmGen
import prog8.compiler.target.cx16.CX16MachineDefinition
import java.nio.file.Path
@ -114,5 +114,6 @@ internal fun asmGeneratorFor(
outputDir: Path
): IAssemblyGenerator
{
// at the moment we only have one code generation backend (for 6502 and 65c02)
return AsmGen(program, errors, zp, options, compTarget, outputDir)
}

View File

@ -109,6 +109,7 @@ internal object C64MachineDefinition: IMachineDefinition {
if (options.zeropage == ZeropageType.FLOATSAFE) {
// remove the zero page locations used for floating point operations from the free list
free.removeAll(listOf(
0x22, 0x23, 0x24, 0x25,
0x10, 0x11, 0x12, 0x26, 0x27, 0x28, 0x29, 0x2a,
0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package prog8.compiler.target.cpu6502.codegen
import prog8.ast.*
import prog8.ast.antlr.escape
@ -11,8 +11,8 @@ import prog8.compiler.functions.FSignature
import prog8.compiler.target.*
import prog8.compiler.target.c64.AssemblyProgram
import prog8.compiler.target.c64.Petscii
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
import prog8.compiler.target.c64.codegen.assignment.AssignmentAsmGen
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen
import java.io.CharConversionException
import java.nio.file.Path
import java.nio.file.Paths
@ -157,7 +157,7 @@ internal class AsmGen(private val program: Program,
pha""")
}
out(" jmp main.start ; start program / force start proc to be included")
jmp("main.start")
}
private fun slaballocations() {
@ -695,7 +695,7 @@ internal class AsmGen(private val program: Program,
is Break -> {
if(loopEndLabels.isEmpty())
throw AssemblyError("break statement out of context ${stmt.position}")
out(" jmp ${loopEndLabels.peek()}")
jmp(loopEndLabels.peek())
}
is WhileLoop -> translate(stmt)
is RepeatLoop -> translate(stmt)
@ -720,7 +720,10 @@ internal class AsmGen(private val program: Program,
return
}
val indexName = asmVariableName(expr.indexer.indexVar!!)
val indexVar = expr.indexer.indexExpr as? IdentifierReference
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.indexer.position}")
val indexName = asmVariableName(indexVar)
if(addOneExtra) {
// add 1 to the result
when(elementDt) {
@ -930,7 +933,7 @@ internal class AsmGen(private val program: Program,
val endLabel = makeLabel("if_end")
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, elseLabel)
translate(stmt.truepart)
out(" jmp $endLabel")
jmp(endLabel)
out(elseLabel)
translate(stmt.elsepart)
out(endLabel)
@ -952,7 +955,7 @@ internal class AsmGen(private val program: Program,
// endless loop
out(repeatLabel)
translate(stmt.body)
out(" jmp $repeatLabel")
jmp(repeatLabel)
out(endLabel)
}
is NumericLiteralValue -> {
@ -976,10 +979,12 @@ internal class AsmGen(private val program: Program,
val name = asmVariableName(stmt.iterations as IdentifierReference)
when(vardecl.datatype) {
DataType.UBYTE, DataType.BYTE -> {
repeatByteCountVar(name, repeatLabel, endLabel, stmt.body)
assignVariableToRegister(name, RegisterOrPair.A)
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
}
DataType.UWORD, DataType.WORD -> {
repeatWordCountVar(name, repeatLabel, endLabel, stmt.body)
assignVariableToRegister(name, RegisterOrPair.AY)
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
}
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
}
@ -1006,10 +1011,11 @@ internal class AsmGen(private val program: Program,
}
private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// note: A/Y must have been loaded with the number of iterations!
if(constIterations==0)
return
// note: A/Y must have been loaded with the number of iterations already!
// TODO can be even more optimized by iterating over pages
// no need to explicitly test for 0 iterations as this is done in the count down logic below
val counterVar = makeLabel("repeatcounter")
out("""
sta $counterVar
@ -1018,80 +1024,48 @@ $repeatLabel lda $counterVar
bne +
lda $counterVar+1
beq $endLabel
+ lda $counterVar
lda $counterVar
bne +
dec $counterVar+1
+ dec $counterVar
""")
translate(body)
out(" jmp $repeatLabel")
if(constIterations!=null && constIterations>=16 && zeropage.available() > 1) {
// allocate count var on ZP
val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, body.position, errors)
out("""$counterVar = $zpAddr ; auto zp UWORD""")
} else {
out("""
$counterVar .word 0""")
}
out(endLabel)
jmp(repeatLabel)
if(constIterations!=null && constIterations>=16 && zeropage.available() > 1) {
// allocate count var on ZP TODO can be shared with countervars from other subroutines
val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, body.position, errors)
out("$counterVar = $zpAddr ; auto zp UWORD")
} else {
out("$counterVar .word 0")
}
out(endLabel)
}
private fun repeatByteCountInA(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// note: A must have been loaded with the number of iterations!
if(constIterations==0)
return
// note: A must have been loaded with the number of iterations already!
val counterVar = makeLabel("repeatcounter")
if(constIterations==null)
out(" beq $endLabel")
out(" beq $endLabel ; skip loop if zero iters")
val counterVar = makeLabel("repeatcounter")
out(" sta $counterVar")
out(repeatLabel)
translate(body)
out("""
dec $counterVar
bne $repeatLabel
beq $endLabel
$counterVar .byte 0""")
out(endLabel)
}
beq $endLabel""")
private fun repeatByteCountVar(repeatCountVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// note: cannot use original counter variable because it should retain its original value
val counterVar = makeLabel("repeatcounter")
out(" lda $repeatCountVar | beq $endLabel | sta $counterVar")
out(repeatLabel)
translate(body)
out(" dec $counterVar | bne $repeatLabel")
// inline countervar:
out("""
beq $endLabel
$counterVar .byte 0""")
out(endLabel)
}
if(constIterations!=null && constIterations>=16 && zeropage.available() > 0) {
// allocate count var on ZP TODO can be shared with countervars from other subroutines
val zpAddr = zeropage.allocate(counterVar, DataType.UBYTE, body.position, errors)
out("$counterVar = $zpAddr ; auto zp UBYTE")
} else {
out("$counterVar .byte 0")
}
private fun repeatWordCountVar(repeatCountVar: String, repeatLabel: String, endLabel: String, body: AnonymousScope) {
// TODO can be even more optimized by iterating over pages
// note: cannot use original counter variable because it should retain its original value
val counterVar = makeLabel("repeatcounter")
out("""
lda $repeatCountVar
sta $counterVar
ora $repeatCountVar+1
beq $endLabel
lda $repeatCountVar+1
sta $counterVar+1""")
out(repeatLabel)
translate(body)
out("""
lda $counterVar
bne +
dec $counterVar+1
+ dec $counterVar
lda $counterVar
ora $counterVar+1
bne $repeatLabel
beq $endLabel
$counterVar .word 0""")
out(endLabel)
}
@ -1104,7 +1078,7 @@ $counterVar .word 0""")
out(whileLabel)
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
translate(stmt.body)
out(" jmp $whileLabel")
jmp(whileLabel)
out(endLabel)
loopEndLabels.pop()
}
@ -1138,7 +1112,7 @@ $counterVar .word 0""")
if(choice.values==null) {
// the else choice
translate(choice.statements)
out(" jmp $endLabel")
jmp(endLabel)
} else {
choiceBlocks.add(choiceLabel to choice.statements)
for (cv in choice.values!!) {
@ -1157,11 +1131,11 @@ $counterVar .word 0""")
}
}
}
out(" jmp $endLabel")
jmp(endLabel)
for(choiceBlock in choiceBlocks) {
out(choiceBlock.first)
translate(choiceBlock.second)
out(" jmp $endLabel")
jmp(endLabel)
}
out(endLabel)
}
@ -1220,7 +1194,7 @@ $counterVar .word 0""")
val endLabel = makeLabel("branch_end")
out(" $instruction $elseLabel")
translate(stmt.truepart)
out(" jmp $endLabel")
jmp(endLabel)
out(elseLabel)
translate(stmt.elsepart)
out(endLabel)
@ -1277,14 +1251,12 @@ $label nop""")
}
}
private fun translate(jmp: Jump) {
out(" jmp ${getJumpTarget(jmp)}")
}
private fun translate(jump: Jump) = jmp(getJumpTarget(jump))
private fun getJumpTarget(jmp: Jump): String {
val ident = jmp.identifier
val label = jmp.generatedLabel
val addr = jmp.address
private fun getJumpTarget(jump: Jump): String {
val ident = jump.identifier
val label = jump.generatedLabel
val addr = jump.address
return when {
ident!=null -> {
val target = ident.targetStatement(program)
@ -1378,6 +1350,13 @@ $label nop""")
return vardecl.makeScopedName(vardecl.name) in allocatedZeropageVariables
}
internal fun jmp(asmLabel: String) {
if(isTargetCpu(CpuType.CPU65c02))
out(" bra $asmLabel") // note: 64tass will convert this automatically to a jmp if the relative distance is too large
else
out(" jmp $asmLabel")
}
internal fun pointerViaIndexRegisterPossible(pointerOffsetExpr: Expression): Pair<Expression, Expression>? {
if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") {
val leftDt = pointerOffsetExpr.left.inferType(program)

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package prog8.compiler.target.cpu6502.codegen
// note: see https://wiki.nesdev.com/w/index.php/6502_assembly_optimisations

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package prog8.compiler.target.cpu6502.codegen
import prog8.ast.IFunctionCall
import prog8.ast.Node
@ -13,7 +13,7 @@ import prog8.ast.toHex
import prog8.compiler.AssemblyError
import prog8.compiler.functions.FSignature
import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.assignment.*
import prog8.compiler.target.cpu6502.codegen.assignment.*
import prog8.compiler.target.subroutineFloatEvalResultVar2
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) {
@ -51,7 +51,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
"ln", "log2", "sqrt", "rad",
"deg", "round", "floor", "ceil",
"rndf" -> funcVariousFloatFuncs(fcall, func, resultToStack, resultRegister, sscope)
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
"fastrnd8", "rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
"rol" -> funcRol(fcall)
"rol2" -> funcRol2(fcall)
@ -64,7 +64,77 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
"pokew" -> funcPokeW(fcall)
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
else -> TODO("missing asmgen for builtin func ${func.name}")
"cmp" -> funcCmp(fcall)
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}")
}
}
private fun funcCmp(fcall: IFunctionCall) {
val arg1 = fcall.args[0]
val arg2 = fcall.args[1]
val dt1 = arg1.inferType(program).typeOrElse(DataType.STRUCT)
val dt2 = arg2.inferType(program).typeOrElse(DataType.STRUCT)
if(dt1 in ByteDatatypes) {
if(dt2 in ByteDatatypes) {
when (arg2) {
is IdentifierReference -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}")
}
is NumericLiteralValue -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp #${arg2.number}")
}
is DirectMemoryRead -> {
if(arg2.addressExpression is NumericLiteralValue) {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}")
} else {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1")
}
}
else -> {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE, (fcall as Node).definingSubroutine())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp P8ZP_SCRATCH_B1")
}
}
} else
throw AssemblyError("args for cmp() should have same dt")
} else {
// dt1 is a word
if(dt2 in WordDatatypes) {
when (arg2) {
is IdentifierReference -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy ${asmgen.asmVariableName(arg2)}+1
bne +
cmp ${asmgen.asmVariableName(arg2)}
+""")
}
is NumericLiteralValue -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy #>${arg2.number}
bne +
cmp #<${arg2.number}
+""")
}
else -> {
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD, (fcall as Node).definingSubroutine())
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy P8ZP_SCRATCH_W1+1
bne +
cmp P8ZP_SCRATCH_W1
+""")
}
}
} else
throw AssemblyError("args for cmp() should have same dt")
}
}
@ -394,8 +464,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
val indexerExpr = if(indexer.indexVar!=null) indexer.indexVar!! else indexer.indexNum!!
asmgen.assignExpressionToVariable(indexerExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
}
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
@ -478,11 +547,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
DataType.ARRAY_UW -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_W -> {
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_F -> {
asmgen.out(" jsr floats.func_${function.name}_f_fac1")
@ -509,19 +578,19 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
when (dt.typeOrElse(DataType.STRUCT)) {
DataType.ARRAY_UB, DataType.STR -> {
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_B -> {
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_UW -> {
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_W -> {
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), RegisterOrPair.AY)
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.ARRAY_F -> {
asmgen.out(" jsr floats.func_sum_f_fac1")
@ -610,10 +679,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
throw AssemblyError("unknown dt")
val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
val firstNum = first.indexer.indexNum
val firstVar = first.indexer.indexVar
val secondNum = second.indexer.indexNum
val secondVar = second.indexer.indexVar
val firstNum = first.indexer.indexExpr as? NumericLiteralValue
val firstVar = first.indexer.indexExpr as? IdentifierReference
val secondNum = second.indexer.indexExpr as? NumericLiteralValue
val secondVar = second.indexer.indexExpr as? IdentifierReference
if(firstNum!=null && secondNum!=null) {
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum)
@ -924,15 +993,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
when (dt) {
in ByteDatatypes -> {
asmgen.out(" jsr prog8_lib.abs_b_into_A")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), CpuRegister.A)
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
}
in WordDatatypes -> {
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.AY)
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
}
DataType.FLOAT -> {
asmgen.out(" jsr floats.abs_f_fac1")
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister!!, scope, program, asmgen), RegisterOrPair.FAC1)
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
}
else -> throw AssemblyError("weird type")
}
@ -941,6 +1010,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
when(func.name) {
"fastrnd8" -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_fastrnd8_stack")
else {
asmgen.out(" jsr math.fast_randbyte")
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
}
}
"rnd" -> {
if(resultToStack)
asmgen.out(" jsr prog8_lib.func_rnd_stack")

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package prog8.compiler.target.cpu6502.codegen
import prog8.ast.Program
import prog8.ast.base.ArrayElementTypes
@ -57,9 +57,9 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
lda $varname
$modifiedLabel cmp #0 ; modified
beq $endLabel
$incdec $varname
jmp $loopLabel
$endLabel""")
$incdec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
} else {
@ -117,16 +117,15 @@ $modifiedLabel2 cmp #0 ; modified
asmgen.out("""
+ inc $varname
bne $loopLabel
inc $varname+1
jmp $loopLabel
""")
inc $varname+1""")
asmgen.jmp(loopLabel)
} else {
asmgen.out("""
+ lda $varname
bne +
dec $varname+1
+ dec $varname
jmp $loopLabel""")
+ dec $varname""")
asmgen.jmp(loopLabel)
}
asmgen.out(endLabel)
}
@ -386,23 +385,25 @@ $loopLabel""")
}
-2 -> {
when (range.last) {
0 -> asmgen.out("""
lda $varname
beq $endLabel
dec $varname
dec $varname
jmp $loopLabel""")
0 -> {
asmgen.out("""
lda $varname
beq $endLabel
dec $varname
dec $varname""")
asmgen.jmp(loopLabel)
}
1 -> asmgen.out("""
dec $varname
beq $endLabel
dec $varname
bne $loopLabel""")
dec $varname
beq $endLabel
dec $varname
bne $loopLabel""")
else -> asmgen.out("""
dec $varname
dec $varname
lda $varname
cmp #${range.last-2}
bne $loopLabel""")
dec $varname
dec $varname
lda $varname
cmp #${range.last-2}
bne $loopLabel""")
}
}
else -> {
@ -413,8 +414,8 @@ $loopLabel""")
beq $endLabel
clc
adc #${range.step}
sta $varname
jmp $loopLabel""")
sta $varname""")
asmgen.jmp(loopLabel)
}
}
asmgen.out(endLabel)
@ -450,9 +451,9 @@ $loopLabel""")
sta $varname
lda $varname+1
adc #>${range.step}
sta $varname+1
jmp $loopLabel
$endLabel""")
sta $varname+1""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
}
}
}
@ -502,9 +503,9 @@ $loopLabel""")
asmgen.out("""
lda $varname
beq $endLabel
dec $varname
jmp $loopLabel
$endLabel""")
dec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
}
1 -> {
asmgen.out("""
@ -545,9 +546,9 @@ $loopLabel""")
beq $endLabel
+ inc $varname
bne $loopLabel
inc $varname+1
jmp $loopLabel
$endLabel""")
inc $varname+1""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
asmgen.loopEndLabels.pop()
}
@ -573,9 +574,9 @@ $loopLabel""")
+ lda $varname
bne +
dec $varname+1
+ dec $varname
jmp $loopLabel
$endLabel""")
+ dec $varname""")
asmgen.jmp(loopLabel)
asmgen.out(endLabel)
asmgen.loopEndLabels.pop()
}

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package prog8.compiler.target.cpu6502.codegen
import prog8.ast.IFunctionCall
import prog8.ast.Node
@ -8,10 +8,10 @@ import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.compiler.AssemblyError
import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.assignment.AsmAssignSource
import prog8.compiler.target.c64.codegen.assignment.AsmAssignTarget
import prog8.compiler.target.c64.codegen.assignment.AsmAssignment
import prog8.compiler.target.c64.codegen.assignment.TargetStorageKind
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignSource
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignTarget
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
import prog8.compiler.target.cpu6502.codegen.assignment.TargetStorageKind
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
@ -177,7 +177,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
else
asmgen.out(" lda #0 | sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
}
in WordDatatypes ->
in WordDatatypes, in IterableDatatypes ->
asmgen.out("""
lda P8ESTACK_LO$plusIdxStr,x
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package prog8.compiler.target.cpu6502.codegen
import prog8.ast.Program
import prog8.ast.base.*
@ -66,8 +66,9 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
targetArrayIdx!=null -> {
val asmArrayvarname = asmgen.asmVariableName(targetArrayIdx.arrayvar)
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.STRUCT)
if(targetArrayIdx.indexer.indexNum!=null) {
val indexValue = targetArrayIdx.indexer.constIndex()!! * program.memsizer.memorySize(elementDt)
val constIndex = targetArrayIdx.indexer.constIndex()
if(constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
when(elementDt) {
in ByteDatatypes -> asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
in WordDatatypes -> {

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen.assignment
package prog8.compiler.target.cpu6502.codegen.assignment
import prog8.ast.IMemSizer
import prog8.ast.Program
@ -6,7 +6,7 @@ import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.compiler.AssemblyError
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.cpu6502.codegen.AsmGen
internal enum class TargetStorageKind {
@ -120,13 +120,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
asmgen.asmVariableName(array.arrayvar)
companion object {
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource {
return when {
indexer.indexNum!=null -> fromAstSource(indexer.indexNum!!, program, asmgen)
indexer.indexVar!=null -> fromAstSource(indexer.indexVar!!, program, asmgen)
else -> throw AssemblyError("weird indexer")
}
}
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource = fromAstSource(indexer.indexExpr, program, asmgen)
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
val cv = value.constValue(program)

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen.assignment
package prog8.compiler.target.cpu6502.codegen.assignment
import prog8.ast.Program
import prog8.ast.base.*
@ -9,12 +9,13 @@ import prog8.compiler.AssemblyError
import prog8.compiler.functions.BuiltinFunctions
import prog8.compiler.functions.builtinFunctionReturnType
import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
import prog8.compiler.target.cpu6502.codegen.AsmGen
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen,
private val exprAsmgen: ExpressionsAsmGen) {
private val exprAsmgen: ExpressionsAsmGen
) {
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, exprAsmgen, asmgen)
@ -64,9 +65,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val value = assign.source.array!!
val elementDt = assign.source.datatype
val arrayVarName = asmgen.asmVariableName(value.arrayvar)
if (value.indexer.indexNum!=null) {
val constIndex = value.indexer.constIndex()
if (constIndex!=null) {
// constant array index value
val indexValue = value.indexer.constIndex()!! * program.memsizer.memorySize(elementDt)
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
when (elementDt) {
in ByteDatatypes -> {
asmgen.out(" lda $arrayVarName+$indexValue")
@ -356,7 +358,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
// special case optimizations
if(target.kind==TargetStorageKind.VARIABLE) {
if(target.kind== TargetStorageKind.VARIABLE) {
if(value is IdentifierReference && valueDt != DataType.STRUCT)
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
@ -569,11 +571,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
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")
}
DataType.STR -> throw AssemblyError("cannot typecast a string value")
else -> throw AssemblyError("weird type")
}
}
@ -708,11 +706,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
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")
}
DataType.STR -> throw AssemblyError("cannot typecast a string value")
else -> throw AssemblyError("weird type")
}
}
@ -1073,11 +1067,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1""")
if(target.array!!.indexer.indexNum!=null) {
val index = target.array.indexer.constIndex()!!
asmgen.out(" lda #$index")
val constIndex = target.array!!.indexer.constIndex()
if(constIndex!=null) {
asmgen.out(" lda #$constIndex")
} else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
asmgen.out(" lda $asmvarname")
}
asmgen.out(" jsr floats.set_array_float_from_fac1")
@ -1109,11 +1103,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1""")
if(target.array!!.indexer.indexNum!=null) {
val index = target.array.indexer.constIndex()!!
asmgen.out(" lda #$index")
val constIndex = target.array!!.indexer.constIndex()
if(constIndex!=null) {
asmgen.out(" lda #$constIndex")
} else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
asmgen.out(" lda $asmvarname")
}
asmgen.out(" jsr floats.set_array_float")
@ -1156,11 +1150,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1""")
if(target.array!!.indexer.indexNum!=null) {
val index = target.array.indexer.constIndex()!!
asmgen.out(" lda #$index")
val constIndex = target.array!!.indexer.constIndex()
if(constIndex!=null) {
asmgen.out(" lda #$constIndex")
} else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
asmgen.out(" lda $asmvarname")
}
asmgen.out(" jsr floats.set_array_float")
@ -1339,8 +1333,16 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
internal fun assignRegisterByte(target: AsmAssignTarget, register: CpuRegister) {
if(target.register !in Cx16VirtualRegisters)
require(target.datatype in ByteDatatypes)
// we make an exception in the type check for assigning something to a cx16 virtual register
if(target.register !in Cx16VirtualRegisters) {
if(target.kind==TargetStorageKind.VARIABLE) {
val parts = target.asmVarname.split('.')
if (parts.size != 2 || parts[0] != "cx16")
require(target.datatype in ByteDatatypes)
} else {
require(target.datatype in ByteDatatypes)
}
}
when(target.kind) {
TargetStorageKind.VARIABLE -> {
@ -1368,7 +1370,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
CpuRegister.X -> asmgen.out(" txa")
CpuRegister.Y -> asmgen.out(" tya")
}
asmgen.out(" ldy ${asmgen.asmVariableName(target.array!!.indexer.indexVar!!)} | sta ${target.asmVarname},y")
val indexVar = target.array!!.indexer.indexExpr as IdentifierReference
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
}
}
TargetStorageKind.REGISTER -> {
@ -1776,8 +1779,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
""")
}
TargetStorageKind.ARRAY -> {
if (target.array!!.indexer.indexNum!=null) {
val indexValue = target.array.indexer.constIndex()!! * program.memsizer.memorySize(DataType.FLOAT)
val constIndex = target.array!!.indexer.constIndex()
if (constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out("""
stz ${target.asmVarname}+$indexValue
@ -1796,7 +1800,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
sta ${target.asmVarname}+$indexValue+4
""")
} else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
asmgen.out("""
lda #<${target.asmVarname}
sta P8ZP_SCRATCH_W1
@ -1841,8 +1845,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
TargetStorageKind.ARRAY -> {
val arrayVarName = target.asmVarname
if (target.array!!.indexer.indexNum!=null) {
val indexValue = target.array.indexer.constIndex()!! * program.memsizer.memorySize(DataType.FLOAT)
val constIndex = target.array!!.indexer.constIndex()
if (constIndex!=null) {
val indexValue = constIndex * program.memsizer.memorySize(DataType.FLOAT)
asmgen.out("""
lda $constFloat
sta $arrayVarName+$indexValue
@ -1856,7 +1861,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
sta $arrayVarName+$indexValue+4
""")
} else {
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexVar!!)
val asmvarname = asmgen.asmVariableName(target.array.indexer.indexExpr as IdentifierReference)
asmgen.out("""
lda #<${constFloat}
sta P8ZP_SCRATCH_W1

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen.assignment
package prog8.compiler.target.cpu6502.codegen.assignment
import prog8.ast.Program
import prog8.ast.base.*
@ -7,13 +7,14 @@ import prog8.ast.statements.Subroutine
import prog8.ast.toHex
import prog8.compiler.AssemblyError
import prog8.compiler.target.CpuType
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.c64.codegen.ExpressionsAsmGen
import prog8.compiler.target.cpu6502.codegen.AsmGen
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
internal class AugmentableAssignmentAsmGen(private val program: Program,
private val assignmentAsmGen: AssignmentAsmGen,
private val exprAsmGen: ExpressionsAsmGen,
private val asmgen: AsmGen) {
private val asmgen: AsmGen
) {
fun translate(assign: AsmAssignment) {
require(assign.isAugmentable)
require(assign.source.kind== SourceStorageKind.EXPRESSION)
@ -198,9 +199,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
TargetStorageKind.ARRAY -> {
with(target.array!!.indexer) {
val indexNum = indexExpr as? NumericLiteralValue
val indexVar = indexExpr as? IdentifierReference
when {
indexNum!=null -> {
val targetVarName = "${target.asmVarname} + ${indexNum!!.number.toInt()*program.memsizer.memorySize(target.datatype)}"
val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
when(target.datatype) {
in ByteDatatypes -> {
when {
@ -267,8 +270,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
}
TargetStorageKind.REGISTER -> TODO("reg in-place modification")
TargetStorageKind.STACK -> TODO("stack in-place modification")
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg in-place modification")
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack in-place modification")
}
}
@ -319,7 +322,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "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)
@ -360,7 +362,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "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)
@ -463,7 +464,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
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")
}
}
@ -539,7 +539,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
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")
}
}
@ -597,7 +596,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "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")
}
}
@ -669,7 +667,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "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")
}
}
@ -977,7 +974,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
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")
}
}
@ -1053,8 +1049,49 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
lda math.multiply_words.result+1
sta $name+1""")
}
"/" -> TODO("div (u)wordvar/bytevar")
"%" -> TODO("(u)word remainder bytevar")
"/" -> {
if(dt==DataType.UWORD) {
asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda $otherName
ldy #0
jsr math.divmod_uw_asm
sta $name
sty $name+1
""")
} else {
asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda $otherName
ldy #0
jsr math.divmod_w_asm
sta $name
sty $name+1
""")
}
}
"%" -> {
if(valueDt!=DataType.UBYTE || dt!=DataType.UWORD)
throw AssemblyError("remainder of signed integers is not properly defined/implemented, use unsigned instead")
asmgen.out("""
lda $name
ldy $name+1
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda $otherName
ldy #0
jsr math.divmod_uw_asm
lda P8ZP_SCRATCH_W2
sta $name
lda P8ZP_SCRATCH_W2+1
sta $name+1
""") }
"<<" -> {
asmgen.out("""
ldy $otherName
@ -1099,7 +1136,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
"|", "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")
}
}
@ -1173,7 +1209,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
"&", "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")
}
}
@ -1364,7 +1399,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
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")
}
}
@ -1405,7 +1439,6 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
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")
}
}
@ -1453,7 +1486,6 @@ 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("""
@ -1534,7 +1566,6 @@ 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
@ -1619,7 +1650,6 @@ 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
@ -1729,9 +1759,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
}
TargetStorageKind.ARRAY -> TODO("in-place not of ubyte array")
TargetStorageKind.REGISTER -> TODO("reg not")
TargetStorageKind.STACK -> TODO("stack not")
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place not of ubyte array")
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg not")
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack not")
}
}
DataType.UWORD -> {
@ -1748,9 +1778,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta ${target.asmVarname}+1""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory not")
TargetStorageKind.ARRAY -> TODO("in-place not of uword array")
TargetStorageKind.REGISTER -> TODO("reg not")
TargetStorageKind.STACK -> TODO("stack not")
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place not of uword array")
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg not")
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack not")
}
}
else -> throw AssemblyError("boolean-not of invalid type")
@ -1795,9 +1825,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
}
TargetStorageKind.ARRAY -> TODO("in-place invert ubyte array")
TargetStorageKind.REGISTER -> TODO("reg invert")
TargetStorageKind.STACK -> TODO("stack invert")
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place invert ubyte array")
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg invert")
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack invert")
}
}
DataType.UWORD -> {
@ -1812,9 +1842,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta ${target.asmVarname}+1""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory invert")
TargetStorageKind.ARRAY -> TODO("in-place invert uword array")
TargetStorageKind.REGISTER -> TODO("reg invert")
TargetStorageKind.STACK -> TODO("stack invert")
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place invert uword array")
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg invert")
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack invert")
}
}
else -> throw AssemblyError("invert of invalid type")
@ -1833,9 +1863,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta ${target.asmVarname}""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't in-place negate memory ubyte")
TargetStorageKind.ARRAY -> TODO("in-place negate byte array")
TargetStorageKind.REGISTER -> TODO("reg negate")
TargetStorageKind.STACK -> TODO("stack negate")
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate byte array")
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg negate")
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack negate")
}
}
DataType.WORD -> {
@ -1850,10 +1880,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sbc ${target.asmVarname}+1
sta ${target.asmVarname}+1""")
}
TargetStorageKind.ARRAY -> TODO("in-place negate word array")
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate word array")
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for word memory negate")
TargetStorageKind.REGISTER -> TODO("reg negate")
TargetStorageKind.STACK -> TODO("stack negate")
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg negate")
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack negate")
}
}
DataType.FLOAT -> {
@ -1866,8 +1896,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta ${target.asmVarname}+1
""")
}
TargetStorageKind.ARRAY -> TODO("in-place negate float array")
TargetStorageKind.STACK -> TODO("stack float negate")
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate float array")
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack float negate")
else -> throw AssemblyError("weird target kind for float")
}
}

View File

@ -19,7 +19,7 @@ private val alwaysKeepSubroutines = setOf(
Pair("main", "start")
)
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
private val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr|bra)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
private val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
@ -178,7 +178,7 @@ class CallGraph(private val program: Program, private val asmFileLoader: (filena
}
override fun visit(inlineAssembly: InlineAssembly) {
// parse inline asm for subroutine calls (jmp, jsr)
// parse inline asm for subroutine calls (jmp, jsr, bra)
val scope = inlineAssembly.definingSubroutine()
scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope)
super.visit(inlineAssembly)

View File

@ -9,28 +9,32 @@ import kotlin.math.pow
class ConstExprEvaluator {
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression {
return when(operator) {
"+" -> plus(left, right)
"-" -> minus(left, right)
"*" -> multiply(left, right)
"/" -> divide(left, right)
"%" -> remainder(left, right)
"**" -> power(left, right)
"&" -> bitwiseand(left, right)
"|" -> bitwiseor(left, right)
"^" -> bitwisexor(left, right)
"and" -> logicaland(left, right)
"or" -> logicalor(left, right)
"xor" -> logicalxor(left, right)
"<" -> NumericLiteralValue.fromBoolean(left < right, left.position)
">" -> NumericLiteralValue.fromBoolean(left > right, left.position)
"<=" -> NumericLiteralValue.fromBoolean(left <= right, left.position)
">=" -> NumericLiteralValue.fromBoolean(left >= right, left.position)
"==" -> NumericLiteralValue.fromBoolean(left == right, left.position)
"!=" -> NumericLiteralValue.fromBoolean(left != right, left.position)
"<<" -> shiftedleft(left, right)
">>" -> shiftedright(left, right)
else -> throw FatalAstException("const evaluation for invalid operator $operator")
try {
return when(operator) {
"+" -> plus(left, right)
"-" -> minus(left, right)
"*" -> multiply(left, right)
"/" -> divide(left, right)
"%" -> remainder(left, right)
"**" -> power(left, right)
"&" -> bitwiseand(left, right)
"|" -> bitwiseor(left, right)
"^" -> bitwisexor(left, right)
"and" -> logicaland(left, right)
"or" -> logicalor(left, right)
"xor" -> logicalxor(left, right)
"<" -> NumericLiteralValue.fromBoolean(left < right, left.position)
">" -> NumericLiteralValue.fromBoolean(left > right, left.position)
"<=" -> NumericLiteralValue.fromBoolean(left <= right, left.position)
">=" -> NumericLiteralValue.fromBoolean(left >= right, left.position)
"==" -> NumericLiteralValue.fromBoolean(left == right, left.position)
"!=" -> NumericLiteralValue.fromBoolean(left != right, left.position)
"<<" -> shiftedleft(left, right)
">>" -> shiftedright(left, right)
else -> throw FatalAstException("const evaluation for invalid operator $operator")
}
} catch (ax: FatalAstException) {
throw ExpressionError(ax.message, left.position)
}
}

View File

@ -75,7 +75,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
// the initializer value can't refer to the variable itself (recursive definition)
// TODO: use call graph for this?
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexVar?.referencesIdentifier(decl.name) == true) {
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.referencesIdentifier(decl.name) == true) {
errors.err("recursive var declaration", decl.position)
return noModifications
}
@ -93,19 +93,6 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
decl
))
}
} else if(arraysize.constIndex()==null) {
// see if we can calculate the size from other fields
try {
val cval = arraysize.indexVar?.constValue(program) ?: arraysize.origExpression?.constValue(program)
if (cval != null) {
arraysize.indexVar = null
arraysize.origExpression = null
arraysize.indexNum = cval
}
} catch (x: UndefinedSymbolError) {
errors.err(x.message, x.position)
return noModifications
}
}
}

View File

@ -56,9 +56,10 @@ internal class StatementOptimizer(private val program: Program,
}
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
if(!subroutine.isAsmSubroutine)
if(!subroutine.isAsmSubroutine) {
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope()))
}
}
return noModifications

View File

@ -25,6 +25,7 @@ internal class UnusedCodeRemover(private val program: Program,
val removals = mutableListOf<IAstModification>()
// remove all subroutines that aren't called, or are empty
// NOTE: part of this is also done already in the StatementOptimizer
val entrypoint = program.entrypoint()
program.modules.forEach {
callgraph.forAllSubroutines(it) { sub ->

View File

@ -191,7 +191,7 @@ class TestC64Zeropage {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
assertEquals(18, zp1.available())
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
assertEquals(89, zp2.available())
assertEquals(85, zp2.available())
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
assertEquals(125, zp3.available())
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))

View File

@ -0,0 +1,13 @@
.PHONY: all clean test
all: test
clean:
rm -f *.prg *.asm *.vice-*
test: clean
p8compile -target cx16 *.p8 >/dev/null
for program in *.prg; do \
echo "RUNNING:" $$program ; \
x16emu -run -prg $$program >/dev/null ; \
done

View File

@ -0,0 +1,18 @@
.PHONY: all clean test
all: test
clean:
rm -f *.prg *.asm *.vice-* test_*.p8
test: clean generate test_prgs
generate:
python make_tests.py
p8compile -noopt -target cx16 *.p8 >/dev/null
test_prgs:
for program in *.prg; do \
echo "RUNNING:" $$program ; \
x16emu -run -prg $$program >/dev/null ; \
done

View File

@ -0,0 +1,508 @@
# generates various Prog8 files with a huge amount of number comparion tests,
# for all supported datatypes and all comparison operators.
import sys
index = 0
def minmaxvalues(dt):
if dt == "ubyte":
return 0, 255
elif dt == "uword":
return 0, 65535
elif dt == "byte":
return -128, 127
elif dt == "word":
return -32768, 32767
elif dt == "float":
return -99999999, 99999999
else:
raise ValueError(dt)
def gen_test(dt, comparison, left, right, expected):
global index
etxt = f"{left} {comparison} {right}"
if eval(etxt) != expected:
raise ValueError("invalid comparison: "+etxt+" for "+dt)
if expected:
stmt_ok = lambda ix: "num_successes++"
stmt_else = lambda ix: f"error({ix})"
else:
stmt_ok = lambda ix: f"error({ix})"
stmt_else = lambda ix: "num_successes++"
def c(number):
if dt not in ("byte", "ubyte"):
return f"({number} as {dt})"
return str(number)
print(
f""" left = {c(left)}
right = {c(right)}
"""
)
# const <op> const
index += 1
print(
f""" ; test #{index}
if {c(left)} {comparison} {c(right)} {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# const <op> var
index += 1
print(
f""" ; test #{index}
if {c(left)} {comparison} right {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# const <op> expr
index += 1
print(
f""" ; test #{index}
if {c(left)} {comparison} right+zero {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# var <op> const
index += 1
print(
f""" ; test #{index}
if left {comparison} {c(right)} {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# var <op> var
index += 1
print(
f""" ; test #{index}
if left {comparison} right {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# var <op> expr
index += 1
print(
f""" ; test #{index}
if left {comparison} right+zero {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# expr <op> const
index += 1
print(
f""" ; test #{index}
if left+zero {comparison} {c(right)} {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# expr <op> var
index += 1
print(
f""" ; test #{index}
if left+zero {comparison} right {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
# expr <op> expr
index += 1
print(
f""" ; test #{index}
if left+zero {comparison} right+zero {{
{stmt_ok(index)}
}} else {{
{stmt_else(index)}
}}
""")
def gen_comp_header(dt, operator):
print(" ; tests: ", dt, operator)
print(" comparison = \""+operator+"\"")
print(" txt.print(datatype)")
print(" txt.spc()")
print(" txt.print(comparison)")
print(" txt.nl()")
def gen_comp_equal(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, "==")
gen_test(dt, "==", 0, 0, True)
gen_test(dt, "==", 0, 1, False)
gen_test(dt, "==", 100, 100, True)
gen_test(dt, "==", 100, 101, False)
if maxval >= 200:
gen_test(dt, "==", 200, 200, True)
gen_test(dt, "==", 200, 201, False)
if maxval >= 9999:
gen_test(dt, "==", 9999, 9999, True)
gen_test(dt, "==", 9999, 10000, False)
gen_test(dt, "==", 0x5000, 0x5000, True)
gen_test(dt, "==", 0x5000, 0x5001, False)
gen_test(dt, "==", 0x5000, 0x4fff, False)
if maxval >= 30000:
gen_test(dt, "==", 30000, 30000, True)
gen_test(dt, "==", 30000, 30001, False)
if maxval >= 40000:
gen_test(dt, "==", 0xf000, 0xf000, True)
gen_test(dt, "==", 0xf000, 0xf001, False)
gen_test(dt, "==", 0xf000, 0xffff, False)
if minval < 0:
gen_test(dt, "==", 0, -1, False)
gen_test(dt, "==", -100, -100, True)
if minval < -200:
gen_test(dt, "==", -200, -200, True)
gen_test(dt, "==", -200, -201, False)
if minval < -9999:
gen_test(dt, "==", -0x5000, -0x5000, True)
gen_test(dt, "==", -0x5000, -0x5001, False)
gen_test(dt, "==", -0x5000, -0x4fff, False)
gen_test(dt, "==", -9999, -9999, True)
gen_test(dt, "==", -9999, -10000, False)
gen_test(dt, "==", minval, minval, True)
gen_test(dt, "==", minval, minval+1, False)
gen_test(dt, "==", maxval, maxval, True)
gen_test(dt, "==", maxval, maxval-1, False)
def gen_comp_notequal(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, "!=")
gen_test(dt, "!=", 0, 0, False)
gen_test(dt, "!=", 0, 1, True)
gen_test(dt, "!=", 100, 100, False)
gen_test(dt, "!=", 100, 101, True)
if maxval >= 200:
gen_test(dt, "!=", 200, 200, False)
gen_test(dt, "!=", 200, 201, True)
if maxval >= 9999:
gen_test(dt, "!=", 9999, 9999, False)
gen_test(dt, "!=", 9999, 10000, True)
gen_test(dt, "!=", 0x5000, 0x5000, False)
gen_test(dt, "!=", 0x5000, 0x5001, True)
gen_test(dt, "!=", 0x5000, 0x4fff, True)
if maxval >= 30000:
gen_test(dt, "!=", 30000, 30000, False)
gen_test(dt, "!=", 30000, 30001, True)
if maxval >= 40000:
gen_test(dt, "!=", 0xf000, 0xf000, False)
gen_test(dt, "!=", 0xf000, 0xf001, True)
gen_test(dt, "!=", 0xf000, 0xffff, True)
if minval < 0:
gen_test(dt, "!=", 0, -1, True)
gen_test(dt, "!=", -100, -100, False)
if minval < -200:
gen_test(dt, "!=", -200, -200, False)
gen_test(dt, "!=", -200, -201, True)
if minval < -9999:
gen_test(dt, "!=", -0x5000, -0x5000, False)
gen_test(dt, "!=", -0x5000, -0x5001, True)
gen_test(dt, "!=", -0x5000, -0x4fff, True)
gen_test(dt, "!=", -9999, -9999, False)
gen_test(dt, "!=", -9999, -10000, True)
gen_test(dt, "!=", minval, minval, False)
gen_test(dt, "!=", minval, minval+1, True)
gen_test(dt, "!=", maxval, maxval, False)
gen_test(dt, "!=", maxval, maxval-1, True)
def gen_comp_less(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, "<")
gen_test(dt, "<", 0, 0, False)
gen_test(dt, "<", 0, 1, True)
gen_test(dt, "<", 100, 100, False)
gen_test(dt, "<", 100, 101, True)
gen_test(dt, "<", 100, 99, False)
if maxval >= 200:
gen_test(dt, "<", 200, 200, False)
gen_test(dt, "<", 200, 201, True)
gen_test(dt, "<", 200, 199, False)
if maxval >= 9999:
gen_test(dt, "<", 9999, 9999, False)
gen_test(dt, "<", 9999, 10000, True)
gen_test(dt, "<", 9999, 9998, False)
gen_test(dt, "<", 0x5000, 0x5000, False)
gen_test(dt, "<", 0x5000, 0x5001, True)
gen_test(dt, "<", 0x5000, 0x4fff, False)
if maxval >= 30000:
gen_test(dt, "<", 30000, 30000, False)
gen_test(dt, "<", 30000, 30001, True)
gen_test(dt, "<", 30000, 29999, False)
if maxval >= 40000:
gen_test(dt, "<", 0xf000, 0xf000, False)
gen_test(dt, "<", 0xf000, 0xf001, True)
gen_test(dt, "<", 0xf000, 0xefff, False)
if minval < 0:
gen_test(dt, "<", 0, -1, False)
gen_test(dt, "<", -100, -100, False)
gen_test(dt, "<", -100, -101, False)
gen_test(dt, "<", -100, -99, True)
if minval < -200:
gen_test(dt, "<", -200, -200, False)
gen_test(dt, "<", -200, -201, False)
gen_test(dt, "<", -200, -199, True)
if minval < -9999:
gen_test(dt, "<", -0x5000, -0x5000, False)
gen_test(dt, "<", -0x5000, -0x5001, False)
gen_test(dt, "<", -0x5000, -0x4fff, True)
gen_test(dt, "<", -9999, -9999, False)
gen_test(dt, "<", -9999, -10000, False)
gen_test(dt, "<", -9999, -9998, True)
def gen_comp_greater(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, ">")
gen_test(dt, ">", 0, 0, False)
gen_test(dt, ">", 0, 1, False)
gen_test(dt, ">", 100, 100, False)
gen_test(dt, ">", 100, 101, False)
gen_test(dt, ">", 100, 99, True)
if maxval >= 200:
gen_test(dt, ">", 200, 200, False)
gen_test(dt, ">", 200, 201, False)
gen_test(dt, ">", 200, 199, True)
if maxval >= 9999:
gen_test(dt, ">", 9999, 9999, False)
gen_test(dt, ">", 9999, 10000, False)
gen_test(dt, ">", 9999, 9998, True)
gen_test(dt, ">", 0x5000, 0x5000, False)
gen_test(dt, ">", 0x5000, 0x5001, False)
gen_test(dt, ">", 0x5000, 0x4fff, True)
if maxval >= 30000:
gen_test(dt, ">", 30000, 30000, False)
gen_test(dt, ">", 30000, 30001, False)
gen_test(dt, ">", 30000, 29999, True)
if maxval >= 40000:
gen_test(dt, ">", 0xf000, 0xf000, False)
gen_test(dt, ">", 0xf000, 0xf001, False)
gen_test(dt, ">", 0xf000, 0xefff, True)
if minval < 0:
gen_test(dt, ">", 0, -1, True)
gen_test(dt, ">", -100, -100, False)
gen_test(dt, ">", -100, -101, True)
gen_test(dt, ">", -100, -99, False)
if minval < -200:
gen_test(dt, ">", -200, -200, False)
gen_test(dt, ">", -200, -201, True)
gen_test(dt, ">", -200, -199, False)
if minval < -9999:
gen_test(dt, ">", -0x5000, -0x5000, False)
gen_test(dt, ">", -0x5000, -0x5001, True)
gen_test(dt, ">", -0x5000, -0x4fff, False)
gen_test(dt, ">", -9999, -9999, False)
gen_test(dt, ">", -9999, -10000, True)
gen_test(dt, ">", -9999, -9998, False)
def gen_comp_lessequal(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, "<=")
gen_test(dt, "<=", 0, 0, True)
gen_test(dt, "<=", 0, 1, True)
gen_test(dt, "<=", 100, 100, True)
gen_test(dt, "<=", 100, 101, True)
gen_test(dt, "<=", 100, 99, False)
if maxval >= 200:
gen_test(dt, "<=", 200, 200, True)
gen_test(dt, "<=", 200, 201, True)
gen_test(dt, "<=", 200, 199, False)
if maxval >= 9999:
gen_test(dt, "<=", 9999, 9999, True)
gen_test(dt, "<=", 9999, 10000, True)
gen_test(dt, "<=", 9999, 9998, False)
gen_test(dt, "<=", 0x5000, 0x5000, True)
gen_test(dt, "<=", 0x5000, 0x5001, True)
gen_test(dt, "<=", 0x5000, 0x4fff, False)
if maxval >= 30000:
gen_test(dt, "<=", 30000, 30000, True)
gen_test(dt, "<=", 30000, 30001, True)
gen_test(dt, "<=", 30000, 29999, False)
if maxval >= 40000:
gen_test(dt, "<=", 0xf000, 0xf000, True)
gen_test(dt, "<=", 0xf000, 0xf001, True)
gen_test(dt, "<=", 0xf000, 0xefff, False)
if minval < 0:
gen_test(dt, "<=", 0, -1, False)
gen_test(dt, "<=", -100, -100, True)
gen_test(dt, "<=", -100, -101, False)
gen_test(dt, "<=", -100, -99, True)
if minval < -200:
gen_test(dt, "<=", -200, -200, True)
gen_test(dt, "<=", -200, -201, False)
gen_test(dt, "<=", -200, -199, True)
if minval < -9999:
gen_test(dt, "<=", -0x5000, -0x5000, True)
gen_test(dt, "<=", -0x5000, -0x5001, False)
gen_test(dt, "<=", -0x5000, -0x4fff, True)
gen_test(dt, "<=", -9999, -9999, True)
gen_test(dt, "<=", -9999, -10000, False)
gen_test(dt, "<=", -9999, -9998, True)
def gen_comp_greaterequal(dt):
minval, maxval = minmaxvalues(dt)
gen_comp_header(dt, ">=")
gen_test(dt, ">=", 0, 0, True)
gen_test(dt, ">=", 0, 1, False)
gen_test(dt, ">=", 100, 100, True)
gen_test(dt, ">=", 100, 101, False)
gen_test(dt, ">=", 100, 99, True)
if maxval >= 200:
gen_test(dt, ">=", 200, 200, True)
gen_test(dt, ">=", 200, 201, False)
gen_test(dt, ">=", 200, 199, True)
if maxval >= 9999:
gen_test(dt, ">=", 9999, 9999, True)
gen_test(dt, ">=", 9999, 10000, False)
gen_test(dt, ">=", 9999, 9998, True)
gen_test(dt, ">=", 0x5000, 0x5000, True)
gen_test(dt, ">=", 0x5000, 0x5001, False)
gen_test(dt, ">=", 0x5000, 0x4fff, True)
if maxval >= 30000:
gen_test(dt, ">=", 30000, 30000, True)
gen_test(dt, ">=", 30000, 30001, False)
gen_test(dt, ">=", 30000, 29999, True)
if maxval >= 40000:
gen_test(dt, ">=", 0xf000, 0xf000, True)
gen_test(dt, ">=", 0xf000, 0xf001, False)
gen_test(dt, ">=", 0xf000, 0xefff, True)
if minval < 0:
gen_test(dt, ">=", 0, -1, True)
gen_test(dt, ">=", -100, -100, True)
gen_test(dt, ">=", -100, -101, True)
gen_test(dt, ">=", -100, -99, False)
if minval < -200:
gen_test(dt, ">=", -200, -200, True)
gen_test(dt, ">=", -200, -201, True)
gen_test(dt, ">=", -200, -199, False)
if minval < -9999:
gen_test(dt, ">=", -0x5000, -0x5000, True)
gen_test(dt, ">=", -0x5000, -0x5001, True)
gen_test(dt, ">=", -0x5000, -0x4fff, False)
gen_test(dt, ">=", -9999, -9999, True)
gen_test(dt, ">=", -9999, -10000, True)
gen_test(dt, ">=", -9999, -9998, False)
def generate_test_routine_equalsnotequals(dt):
print(f"""
sub test_comparisons() {{
{dt} left
{dt} right
{dt} zero = 0
""")
gen_comp_equal(dt)
gen_comp_notequal(dt)
print(" }")
def generate_test_routine_lessgreater(dt):
print(f"""
sub test_comparisons() {{
{dt} left
{dt} right
{dt} zero = 0
""")
gen_comp_less(dt)
gen_comp_greater(dt)
print(" }")
def generate_test_routine_lessequalsgreaterequals(dt):
print(f"""
sub test_comparisons() {{
{dt} left
{dt} right
{dt} zero = 0
""")
gen_comp_lessequal(dt)
gen_comp_greaterequal(dt)
print(" }")
def generate(dt, operators):
global index
index = 0
print(f"""
%import textio
%import floats
%import test_stack
%zeropage basicsafe
main {{
uword num_errors = 0
uword num_successes = 0
str datatype = "{dt}"
uword comparison
sub start() {{
test_comparisons()
print_results()
test_stack.test()
}}
sub error(uword index) {{
txt.print(" ! error in test ")
txt.print_uw(index)
txt.chrout(' ')
txt.print(datatype)
txt.chrout(' ')
txt.print(comparison)
txt.nl()
num_errors++
}}
""")
if operators=="eq":
generate_test_routine_equalsnotequals(dt)
elif operators=="lt":
generate_test_routine_lessgreater(dt)
elif operators=="lteq":
generate_test_routine_lessequalsgreaterequals(dt)
else:
raise ValueError(operators)
print(f"""
sub print_results() {{
txt.nl()
txt.print("total {index}: ")
txt.print_uw(num_successes)
txt.print(" good, ")
txt.print_uw(num_errors)
txt.print(" errors!\\n")
}}
}}
""")
if __name__ == '__main__':
for dt in ["ubyte", "uword", "byte", "word", "float"]:
sys.stdout = open(f"test_{dt}_eq.p8", "wt")
generate(dt, "eq")
sys.stdout = open(f"test_{dt}_lt.p8", "wt")
generate(dt, "lt")
sys.stdout = open(f"test_{dt}_lteq.p8", "wt")
generate(dt, "lteq")

View File

@ -396,14 +396,18 @@ galaxy10 {
sub init(ubyte galaxy10num) {
number = 1
planet10.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy10num-1 {
nextgalaxy10()
}
}
sub nextgalaxy10() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -576,8 +580,10 @@ galaxy10 {
planet10.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
planet10.species_kind = (planet10.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet10.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet10.goatsoup_seed[0] = lsb(seed[1])
planet10.goatsoup_seed[1] = msb(seed[1])
planet10.goatsoup_seed[2] = lsb(seed[2])
planet10.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy2 {
sub init(ubyte galaxy2num) {
number = 1
planet2.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy2num-1 {
nextgalaxy2()
}
}
sub nextgalaxy2() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -577,7 +581,10 @@ galaxy2 {
planet2.species_kind = (planet2.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet2.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet2.goatsoup_seed[0] = lsb(seed[1])
planet2.goatsoup_seed[1] = msb(seed[1])
planet2.goatsoup_seed[2] = lsb(seed[2])
planet2.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy3 {
sub init(ubyte galaxy3num) {
number = 1
planet3.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy3num-1 {
nextgalaxy3()
}
}
sub nextgalaxy3() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -577,7 +581,10 @@ galaxy3 {
planet3.species_kind = (planet3.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet3.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet3.goatsoup_seed[0] = lsb(seed[1])
planet3.goatsoup_seed[1] = msb(seed[1])
planet3.goatsoup_seed[2] = lsb(seed[2])
planet3.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy4 {
sub init(ubyte galaxy4num) {
number = 1
planet4.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy4num-1 {
nextgalaxy4()
}
}
sub nextgalaxy4() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -577,7 +581,10 @@ galaxy4 {
planet4.species_kind = (planet4.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet4.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet4.goatsoup_seed[0] = lsb(seed[1])
planet4.goatsoup_seed[1] = msb(seed[1])
planet4.goatsoup_seed[2] = lsb(seed[2])
planet4.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy5 {
sub init(ubyte galaxy5num) {
number = 1
planet5.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy5num-1 {
nextgalaxy5()
}
}
sub nextgalaxy5() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -577,7 +581,10 @@ galaxy5 {
planet5.species_kind = (planet5.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet5.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet5.goatsoup_seed[0] = lsb(seed[1])
planet5.goatsoup_seed[1] = msb(seed[1])
planet5.goatsoup_seed[2] = lsb(seed[2])
planet5.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy6 {
sub init(ubyte galaxy6num) {
number = 1
planet6.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy6num-1 {
nextgalaxy6()
}
}
sub nextgalaxy6() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -577,7 +581,10 @@ galaxy6 {
planet6.species_kind = (planet6.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet6.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet6.goatsoup_seed[0] = lsb(seed[1])
planet6.goatsoup_seed[1] = msb(seed[1])
planet6.goatsoup_seed[2] = lsb(seed[2])
planet6.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy7 {
sub init(ubyte galaxy7num) {
number = 1
planet7.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy7num-1 {
nextgalaxy7()
}
}
sub nextgalaxy7() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -577,7 +581,10 @@ galaxy7 {
planet7.species_kind = (planet7.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet7.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet7.goatsoup_seed[0] = lsb(seed[1])
planet7.goatsoup_seed[1] = msb(seed[1])
planet7.goatsoup_seed[2] = lsb(seed[2])
planet7.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy8 {
sub init(ubyte galaxy8num) {
number = 1
planet8.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy8num-1 {
nextgalaxy8()
}
}
sub nextgalaxy8() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -577,7 +581,10 @@ galaxy8 {
planet8.species_kind = (planet8.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet8.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet8.goatsoup_seed[0] = lsb(seed[1])
planet8.goatsoup_seed[1] = msb(seed[1])
planet8.goatsoup_seed[2] = lsb(seed[2])
planet8.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -396,14 +396,18 @@ galaxy9 {
sub init(ubyte galaxy9num) {
number = 1
planet9.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxy9num-1 {
nextgalaxy9()
}
}
sub nextgalaxy9() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -576,8 +580,10 @@ galaxy9 {
planet9.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
planet9.species_kind = (planet9.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet9.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet9.goatsoup_seed[0] = lsb(seed[1])
planet9.goatsoup_seed[1] = msb(seed[1])
planet9.goatsoup_seed[2] = lsb(seed[2])
planet9.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -406,14 +406,18 @@ galaxy {
sub init(ubyte galaxynum) {
number = 1
planet.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxynum-1 {
nextgalaxy()
}
}
sub nextgalaxy() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -587,7 +591,10 @@ galaxy {
planet.species_kind = (planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet.goatsoup_seed[0] = lsb(seed[1])
planet.goatsoup_seed[1] = msb(seed[1])
planet.goatsoup_seed[2] = lsb(seed[2])
planet.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -121,8 +121,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
output(datatypeString(decl.datatype))
if(decl.arraysize!=null) {
decl.arraysize!!.indexNum?.accept(this)
decl.arraysize!!.indexVar?.accept(this)
decl.arraysize!!.indexExpr.accept(this)
}
if(decl.isArray)
output("]")
@ -365,8 +364,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
arrayIndexedExpression.arrayvar.accept(this)
output("[")
arrayIndexedExpression.indexer.indexNum?.accept(this)
arrayIndexedExpression.indexer.indexVar?.accept(this)
arrayIndexedExpression.indexer.indexExpr.accept(this)
output("]")
}

View File

@ -60,7 +60,13 @@ enum class DataType {
enum class CpuRegister {
A,
X,
Y
Y;
fun asRegisterOrPair(): RegisterOrPair = when(this) {
A -> RegisterOrPair.A
X -> RegisterOrPair.X
Y -> RegisterOrPair.Y
}
}
enum class RegisterOrPair {

View File

@ -6,7 +6,7 @@ import prog8.ast.base.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor
import java.util.*
import java.util.Objects
import kotlin.math.abs
@ -542,6 +542,14 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
return type==other.type && value.contentEquals(other.value)
}
fun memsize(memsizer: IMemSizer): Int {
if(type.isKnown) {
val eltType = ArrayElementTypes.getValue(type.typeOrElse(DataType.STRUCT))
return memsizer.memorySize(eltType) * value.size
}
else throw IllegalArgumentException("array datatype is not yet known")
}
fun guessDatatype(program: Program): InferredTypes.InferredType {
// Educated guess of the desired array literal's datatype.
// If it's inside a for loop, assume the data type of the loop variable is what we want.

View File

@ -221,14 +221,20 @@ open class VarDecl(val type: VarDeclType,
value?.linkParents(this)
if(structName!=null) {
val structStmt = definingScope().lookup(listOf(structName), this)
if(structStmt!=null)
struct = definingScope().lookup(listOf(structName), this) as StructDecl
if(structStmt!=null) {
val node = definingScope().lookup(listOf(structName), this)
if(node is StructDecl)
struct = node
else
datatypeErrors.add(SyntaxError("invalid datatype declaration", position))
}
}
}
override fun replaceChildNode(node: Node, replacement: Node) {
// TODO the check that node===value is too strict sometimes, but leaving it out allows for bugs to creep through ... :( Perhaps check when adding the replace if there is already a replace on the same node?
require(replacement is Expression)
require(replacement is Expression && node===value)
// NOTE: ideally you also want to check that node===value but this sometimes crashes the optimizer when queueing multiple node replacements
// just accept the risk of having the wrong node specified in the IAstModification object...
value = replacement
replacement.parent = this
}
@ -274,46 +280,19 @@ class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Posit
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position)
class ArrayIndex(var origExpression: Expression?, // will be replaced later by either the number or the identifier
class ArrayIndex(var indexExpr: Expression,
override val position: Position) : Node {
// for code simplicity, either indexed via a constant number or via a variable (no arbitrary expressions)
override lateinit var parent: Node
var indexNum: NumericLiteralValue? = origExpression as? NumericLiteralValue
var indexVar: IdentifierReference? = origExpression as? IdentifierReference
init {
if(indexNum!=null || indexVar!=null)
origExpression = null
}
override fun linkParents(parent: Node) {
this.parent = parent
origExpression?.linkParents(this)
indexNum?.linkParents(this)
indexVar?.linkParents(this)
indexExpr.linkParents(this)
}
override fun replaceChildNode(node: Node, replacement: Node) {
require(replacement is Expression)
when {
node===origExpression -> origExpression = replacement
node===indexVar -> {
when (replacement) {
is NumericLiteralValue -> {
indexVar = null
indexNum = replacement
}
is IdentifierReference -> {
indexVar = replacement
indexNum = null
}
else -> {
throw FatalAstException("invalid replace")
}
}
}
else -> throw FatalAstException("invalid replace")
}
if (node===indexExpr) indexExpr = replacement
else throw FatalAstException("invalid replace")
}
companion object {
@ -323,29 +302,16 @@ class ArrayIndex(var origExpression: Expression?, // will be replaced
}
}
fun accept(visitor: IAstVisitor) {
origExpression?.accept(visitor)
indexNum?.accept(visitor)
indexVar?.accept(visitor)
}
fun accept(visitor: AstWalker, parent: Node) {
origExpression?.accept(visitor, this)
indexNum?.accept(visitor, this)
indexVar?.accept(visitor, this)
}
fun accept(visitor: IAstVisitor) = indexExpr.accept(visitor)
fun accept(visitor: AstWalker, parent: Node) = indexExpr.accept(visitor, this)
override fun toString(): String {
return("ArrayIndex($indexNum, $indexVar, pos=$position)")
return("ArrayIndex($indexExpr, pos=$position)")
}
fun constIndex() = indexNum?.number?.toInt()
fun constIndex() = (indexExpr as? NumericLiteralValue)?.number?.toInt()
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!!
}
infix fun isSameAs(other: ArrayIndex): Boolean = indexExpr isSameAs other.indexExpr
}
open class Assignment(var target: AssignTarget, var value: Expression, override val position: Position) : Statement() {
@ -645,14 +611,11 @@ class AsmGenInfo {
// 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 = mutableListOf<ArrayIndexerInfo>()
var usedRegsaveA = false
var usedRegsaveX = false
var usedRegsaveY = false
var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false
class ArrayIndexerInfo(val name: String, val replaces: ArrayIndex)
}
// the subroutine class covers both the normal user-defined subroutines,
@ -728,7 +691,7 @@ class Subroutine(override val name: String,
.asSequence()
.filter { it is InlineAssembly }
.map { (it as InlineAssembly).assembly }
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it || " bra" in it || "\tbra" in it}
}
@ -1000,6 +963,9 @@ class StructDecl(override val name: String,
val numberOfElements: Int
get() = this.statements.size
fun memsize(memsizer: IMemSizer) =
statements.map { memsizer.memorySize((it as VarDecl).datatype) }.sum()
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)

View File

@ -56,7 +56,7 @@ interface IAstModification {
}
}
class ReplaceNode(private val node: Node, private val replacement: Node, private val parent: Node) :
class ReplaceNode(val node: Node, private val replacement: Node, private val parent: Node) :
IAstModification {
override fun perform() {
parent.replaceChildNode(node, replacement)
@ -158,9 +158,19 @@ abstract class AstWalker {
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList()
private val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
// private val modificationsReplacedNodes = mutableSetOf<Pair<Node, Position>>()
private fun track(mods: Iterable<IAstModification>, node: Node, parent: Node) {
for (it in mods) modifications += Triple(it, node, parent)
for (it in mods) {
// if(it is IAstModification.ReplaceNode) {
// val replaceKey = Pair(it.node, it.node.position)
// if(replaceKey in modificationsReplacedNodes)
// throw FatalAstException("there already is a node replacement for $replaceKey - optimizer can't deal with multiple replacements for same node yet. Split the ast modification?")
// else
// modificationsReplacedNodes.add(replaceKey)
// }
modifications += Triple(it, node, parent)
}
}
fun applyModifications(): Int {
@ -169,6 +179,7 @@ abstract class AstWalker {
}
val amount = modifications.size
modifications.clear()
// modificationsReplacedNodes.clear()
return amount
}

View File

@ -50,8 +50,9 @@ Language features
- 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
- Subroutines with parameters and return values
- complex nested expressions are possible
- Variables are 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.

View File

@ -105,7 +105,7 @@ conv
Routines to convert strings to numbers or vice versa.
- numbers to strings, in various formats (binary, hex, decimal)
- strings in decimal, hex and binary format into numbers
- strings in decimal, hex and binary format into numbers (bytes, words)
textio (txt.*)

View File

@ -739,9 +739,11 @@ sin16(x)
sqrt16(w)
16 bit unsigned integer Square root. Result is unsigned byte.
To do the reverse, squaring an integer, just write ``x*x``.
sqrt(x)
Floating point Square root.
To do the reverse, squaring a floating point number, just write ``x*x`` or ``x**2``.
tan(x)
Tangent.
@ -757,11 +759,12 @@ all(x)
1 ('true') if all of the values in the array value x are 'true' (not zero), else 0 ('false')
len(x)
Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte).
Number of values in the array value x, or the number of characters in a string (excluding the 0-byte).
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. See sizeof().
Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual
length of the string during execution, the value of len(string) may no longer be correct!
(use strlen function if you want to dynamically determine the length)
length of the string during execution, the value of len(s) may no longer be correct!
(use the ``string.length`` routine if you want to dynamically determine the length by counting to the
first 0-byte)
max(x)
Maximum of the values in the array value x
@ -788,6 +791,12 @@ sort(array)
Miscellaneous
^^^^^^^^^^^^^
cmp(x,y)
Compare the integer value x to integer value y. Doesn't return a value or boolean result, only sets the processor's status bits!
You can use a conditional jumps (``if_cc`` etcetera) to act on this.
Normally you should just use a comparison expression (``x < y``)
lsb(x)
Get the least significant byte of the word x. Equivalent to the cast "x as ubyte".
@ -824,6 +833,9 @@ rndw()
rndf()
returns a pseudo-random float between 0.0 and 1.0
fastrnd8()
returns a pseudo-random byte from 0..255 (using a fast but not very good rng)
rol(x)
Rotate the bits in x (byte or word) one position to the left.
This uses the CPU's rotate semantics: bit 0 will be set to the current value of the Carry flag,

View File

@ -592,7 +592,7 @@ flag such as Carry (Pc).
Asmsubs can also be tagged as ``inline asmsub`` to make trivial pieces of assembly inserted
directly instead of a call to them. Note that it is literal copy-paste of code that is done,
so make sure the assembly is actually written to behave like such - which probably means you
don't want a ``rts`` or ``jmp`` in it!
don't want a ``rts`` or ``jmp`` or ``bra`` in it!
.. note::

View File

@ -2,18 +2,16 @@
TODO
====
- optimize assigning array and struct variables (multi-element assings -> memcopy)
- optimize several inner loops in gfx2
- 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)
- optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
- optimize several inner loops in gfx2
- try to fix the bresenham line routines in graphics and gfx2 (sometimes they're a pixel 'off')
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)
- add a flood fill routine to gfx2?
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color) ?
- add a f_seek() routine for the Cx16 that uses its seek dos api?
- refactor the asmgen into their own submodule?
- refactor the compiler optimizers into their own submodule?
- optimizer: detect variables that are written but never read - mark those as unused too and remove them, such as uword unused = memory("unused222", 20) - also remove the memory slab allocation
- add a compiler option to not remove unused subroutines. this allows for building library programs
- add a compiler option to not remove unused subroutines. this allows for building library programs. But this won't work with 64tass's .proc ...
- make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``v_``
- option to load the built-in library files from a directory instead of the embedded ones (for easier library development/debugging)
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)

View File

@ -42,7 +42,7 @@ main {
active_height--
upwards = false
} else {
target_height = 8 + rnd() % 16
target_height = 8 + fastrnd8() % 16
if upwards
mountain = 233
else
@ -57,7 +57,7 @@ main {
txt.scroll_left(true)
; float the balloon
if rnd() & %10000
if fastrnd8() & %10000
c64.SPXY[1] ++
else
c64.SPXY[1] --
@ -71,10 +71,10 @@ main {
txt.setcc(39, yy, 160, 8) ; draw mountain
}
yy = rnd()
yy = fastrnd8()
if yy > 100 {
; draw a star
txt.setcc(39, yy % (active_height-1), '.', rnd())
txt.setcc(39, yy % (active_height-1), '.', fastrnd8())
}
if yy > 200 {
@ -85,7 +85,7 @@ main {
tree = 88
else if yy & %00100000 != 0
tree = 65
if rnd() > 130
if fastrnd8() > 130
treecolor = 13
txt.setcc(39, active_height, tree, treecolor)
}

View File

@ -24,12 +24,12 @@ main {
; Setup Starting Ball Positions
ubyte lp
for lp in 0 to ballCount-1 {
BX[lp] = rnd() % txt.DEFAULT_WIDTH
BY[lp] = rnd() % txt.DEFAULT_HEIGHT
BC[lp] = rnd() & 15
DX[lp] = rnd() & 1
DY[lp] = rnd() & 1
void rnd()
BX[lp] = fastrnd8() % txt.DEFAULT_WIDTH
BY[lp] = fastrnd8() % txt.DEFAULT_HEIGHT
BC[lp] = fastrnd8() & 15
DX[lp] = fastrnd8() & 1
DY[lp] = fastrnd8() & 1
void fastrnd8()
}
; start clock

View File

@ -1,110 +0,0 @@
%import textio
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
byte v1
byte v2
v1 = 100
v2 = 127
if v1==v2
txt.print("error in 100==127!\n")
else
txt.print("ok: 100 not == 127\n")
if v1!=v2
txt.print("ok: 100 != 127\n")
else
txt.print("error in 100!=127!\n")
if v1<v2
txt.print("ok: 100 < 127\n")
else
txt.print("error in 100<127!\n")
if v1<=v2
txt.print("ok: 100 <= 127\n")
else
txt.print("error in 100<=127!\n")
if v1>v2
txt.print("error in 100>127!\n")
else
txt.print("ok: 100 is not >127\n")
if v1>=v2
txt.print("error in 100>=127!\n")
else
txt.print("ok: 100 is not >=127\n")
v1 = 125
v2 = 22
if v1==v2
txt.print("error in 125==22!\n")
else
txt.print("ok: 125 not == 22\n")
if v1!=v2
txt.print("ok: 125 != 22\n")
else
txt.print("error in 125!=22!\n")
if v1<v2
txt.print("error in 125<22!\n")
else
txt.print("ok: 125 is not < 22\n")
if v1<=v2
txt.print("error in 125<=22!\n")
else
txt.print("ok: 125 is not <= 22\n")
if v1>v2
txt.print("ok: 125 > 22\n")
else
txt.print("error in 125>22!\n")
if v1>=v2
txt.print("ok: 125 >= 22\n")
else
txt.print("error in 125>=22!\n")
v1 = 22
v2 = 22
if v1==v2
txt.print("ok: 22 == 22\n")
else
txt.print("error in 22==22!\n")
if v1!=v2
txt.print("error in 22!=22!\n")
else
txt.print("ok: 22 is not != 22\n")
if v1<v2
txt.print("error in 22<22!\n")
else
txt.print("ok: 22 is not < 22\n")
if v1<=v2
txt.print("ok: 22 <= 22\n")
else
txt.print("error in 22<=22!\n")
if v1>v2
txt.print("error in 22>22!\n")
else
txt.print("ok: 22 is not > 22\n")
if v1>=v2
txt.print("ok: 22 >= 22\n")
else
txt.print("error in 22>=22!\n")
}
}

View File

@ -1,111 +0,0 @@
%import textio
%import floats
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
float v1
float v2
v1 = 1.11
v2 = 699.99
if v1==v2
txt.print("error in 1.11==699.99!\n")
else
txt.print("ok: 1.11 not == 699.99\n")
if v1!=v2
txt.print("ok: 1.11 != 699.99\n")
else
txt.print("error in 1.11!=699.99!\n")
if v1<v2
txt.print("ok: 1.11 < 699.99\n")
else
txt.print("error in 1.11<699.99!\n")
if v1<=v2
txt.print("ok: 1.11 <= 699.99\n")
else
txt.print("error in 1.11<=699.99!\n")
if v1>v2
txt.print("error in 1.11>699.99!\n")
else
txt.print("ok: 1.11 is not >699.99\n")
if v1>=v2
txt.print("error in 1.11>=699.99!\n")
else
txt.print("ok: 1.11 is not >=699.99\n")
v1 = 555.5
v2 = -22.2
if v1==v2
txt.print("error in 555.5==-22.2!\n")
else
txt.print("ok: 555.5 not == -22.2\n")
if v1!=v2
txt.print("ok: 555.5 != -22.2\n")
else
txt.print("error in 555.5!=-22.2!\n")
if v1<v2
txt.print("error in 555.5<-22.2!\n")
else
txt.print("ok: 555.5 is not < -22.2\n")
if v1<=v2
txt.print("error in 555.5<=-22.2!\n")
else
txt.print("ok: 555.5 is not <= -22.2\n")
if v1>v2
txt.print("ok: 555.5 > -22.2\n")
else
txt.print("error in 555.5>-22.2!\n")
if v1>=v2
txt.print("ok: 555.5 >= -22.2\n")
else
txt.print("error in 555.5>=-22.2!\n")
v1 = -22.2
v2 = -22.2
if v1==v2
txt.print("ok: -22.2 == -22.2\n")
else
txt.print("error in -22.2==-22.2!\n")
if v1!=v2
txt.print("error in -22.2!=-22.2!\n")
else
txt.print("ok: -22.2 is not != -22.2\n")
if v1<v2
txt.print("error in -22.2<-22.2!\n")
else
txt.print("ok: -22.2 is not < -22.2\n")
if v1<=v2
txt.print("ok: -22.2 <= -22.2\n")
else
txt.print("error in -22.2<=-22.2!\n")
if v1>v2
txt.print("error in -22.2>-22.2!\n")
else
txt.print("ok: -22.2 is not > -22.2\n")
if v1>=v2
txt.print("ok: -22.2 >= -22.2\n")
else
txt.print("error in -22.2>=-22.2!\n")
}
}

View File

@ -1,111 +0,0 @@
%import textio
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
ubyte v1
ubyte v2
v1 = 100
v2 = 200
if v1==v2
txt.print("error in 100==200!\n")
else
txt.print("ok: 100 not == 200\n")
if v1!=v2
txt.print("ok: 100 != 200\n")
else
txt.print("error in 100!=200!\n")
if v1<v2
txt.print("ok: 100 < 200\n")
else
txt.print("error in 100<200!\n")
if v1<=v2
txt.print("ok: 100 <= 200\n")
else
txt.print("error in 100<=200!\n")
if v1>v2
txt.print("error in 100>200!\n")
else
txt.print("ok: 100 is not >200\n")
if v1>=v2
txt.print("error in 100>=200!\n")
else
txt.print("ok: 100 is not >=200\n")
v1 = 155
v2 = 22
if v1==v2
txt.print("error in 155==22!\n")
else
txt.print("ok: 155 not == 22\n")
if v1!=v2
txt.print("ok: 155 != 22\n")
else
txt.print("error in 155!=22!\n")
if v1<v2
txt.print("error in 155<22!\n")
else
txt.print("ok: 155 is not < 22\n")
if v1<=v2
txt.print("error in 155<=22!\n")
else
txt.print("ok: 155 is not <= 22\n")
if v1>v2
txt.print("ok: 155 > 22\n")
else
txt.print("error in 155>22!\n")
if v1>=v2
txt.print("ok: 155 >= 22\n")
else
txt.print("error in 155>=22!\n")
v1 = 22
v2 = 22
if v1==v2
txt.print("ok: 22 == 22\n")
else
txt.print("error in 22==22!\n")
if v1!=v2
txt.print("error in 22!=22!\n")
else
txt.print("ok: 22 is not != 22\n")
if v1<v2
txt.print("error in 22<22!\n")
else
txt.print("ok: 22 is not < 22\n")
if v1<=v2
txt.print("ok: 22 <= 22\n")
else
txt.print("error in 22<=22!\n")
if v1>v2
txt.print("error in 22>22!\n")
else
txt.print("ok: 22 is not > 22\n")
if v1>=v2
txt.print("ok: 22 >= 22\n")
else
txt.print("error in 22>=22!\n")
}
}

View File

@ -1,110 +0,0 @@
%import textio
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
uword v1
uword v2
v1 = 100
v2 = 64444
if v1==v2
txt.print("error in 100==64444!\n")
else
txt.print("ok: 100 not == 64444\n")
if v1!=v2
txt.print("ok: 100 != 64444\n")
else
txt.print("error in 100!=64444!\n")
if v1<v2
txt.print("ok: 100 < 64444\n")
else
txt.print("error in 100<64444!\n")
if v1<=v2
txt.print("ok: 100 <= 64444\n")
else
txt.print("error in 100<=64444!\n")
if v1>v2
txt.print("error in 100>64444!\n")
else
txt.print("ok: 100 is not >64444\n")
if v1>=v2
txt.print("error in 100>=64444!\n")
else
txt.print("ok: 100 is not >=64444\n")
v1 = 5555
v2 = 322
if v1==v2
txt.print("error in 5555==322!\n")
else
txt.print("ok: 5555 not == 322\n")
if v1!=v2
txt.print("ok: 5555 != 322\n")
else
txt.print("error in 5555!=322!\n")
if v1<v2
txt.print("error in 5555<322!\n")
else
txt.print("ok: 5555 is not < 322\n")
if v1<=v2
txt.print("error in 5555<=322!\n")
else
txt.print("ok: 5555 is not <= 322\n")
if v1>v2
txt.print("ok: 5555 > 322\n")
else
txt.print("error in 5555>322!\n")
if v1>=v2
txt.print("ok: 5555 >= 322\n")
else
txt.print("error in 5555>=322!\n")
v1 = 322
v2 = 322
if v1==v2
txt.print("ok: 322 == 322\n")
else
txt.print("error in 322==322!\n")
if v1!=v2
txt.print("error in 322!=322!\n")
else
txt.print("ok: 322 is not != 322\n")
if v1<v2
txt.print("error in 322<322!\n")
else
txt.print("ok: 322 is not < 322\n")
if v1<=v2
txt.print("ok: 322 <= 322\n")
else
txt.print("error in 322<=322!\n")
if v1>v2
txt.print("error in 322>322!\n")
else
txt.print("ok: 322 is not > 322\n")
if v1>=v2
txt.print("ok: 322 >= 322\n")
else
txt.print("error in 322>=322!\n")
}
}

View File

@ -1,142 +0,0 @@
%import textio
%zeropage basicsafe
; Note: this program is compatible with C64 and CX16.
main {
sub start() {
word v1
word v2
v1 = 100
v2 = 30333
if v1==v2
txt.print("error in 100==30333!\n")
else
txt.print("ok: 100 not == 30333\n")
if v1!=v2
txt.print("ok: 100 != 30333\n")
else
txt.print("error in 100!=30333!\n")
if v1<v2
txt.print("ok: 100 < 30333\n")
else
txt.print("error in 100<30333!\n")
if v1<=v2
txt.print("ok: 100 <= 30333\n")
else
txt.print("error in 100<=30333!\n")
if v1>v2
txt.print("error in 100>30333!\n")
else
txt.print("ok: 100 is not >30333\n")
if v1>=v2
txt.print("error in 100>=30333!\n")
else
txt.print("ok: 100 is not >=30333\n")
v1 = 125
v2 = -222
if v1==v2
txt.print("error in 125==-222!\n")
else
txt.print("ok: 125 not == -222\n")
if v1!=v2
txt.print("ok: 125 != -222\n")
else
txt.print("error in 125!=-222!\n")
if v1<v2
txt.print("error in 125<-222!\n")
else
txt.print("ok: 125 is not < -222\n")
if v1<=v2
txt.print("error in 125<=-222!\n")
else
txt.print("ok: 125 is not <= -222\n")
if v1>v2
txt.print("ok: 125 > -222\n")
else
txt.print("error in 125>-222!\n")
if v1>=v2
txt.print("ok: 125 >= -222\n")
else
txt.print("error in 125>=-222!\n")
v1 = -222
v2 = -222
if v1==v2
txt.print("ok: -222 == -222\n")
else
txt.print("error in -222==-222!\n")
if v1!=v2
txt.print("error in -222!=-222!\n")
else
txt.print("ok: -222 is not != -222\n")
if v1<v2
txt.print("error in -222<-222!\n")
else
txt.print("ok: -222 is not < -222\n")
if v1<=v2
txt.print("ok: -222 <= -222\n")
else
txt.print("error in -222<=-222!\n")
if v1>v2
txt.print("error in -222>-222!\n")
else
txt.print("ok: -222 is not > -222\n")
if v1>=v2
txt.print("ok: -222 >= -222\n")
else
txt.print("error in -222>=-222!\n")
v1 = 1000
v2 = 1000
if v1==v2
txt.print("ok: 1000 == 1000\n")
else
txt.print("error in 1000==1000!\n")
if v1!=v2
txt.print("error in 1000!=1000!\n")
else
txt.print("ok: 1000 is not != 1000\n")
if v1<v2
txt.print("error in 1000<1000!\n")
else
txt.print("ok: 1000 is not < 1000\n")
if v1<=v2
txt.print("ok: 1000 <= 1000\n")
else
txt.print("error in 1000<=1000!\n")
if v1>v2
txt.print("error in 1000>1000!\n")
else
txt.print("ok: 1000 is not > 1000\n")
if v1>=v2
txt.print("ok: 1000 >= 1000\n")
else
txt.print("error in 1000>=1000!\n")
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,913 +0,0 @@
%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.nl()
check_lesseq_uw(0, 65535)
txt.print_uw(0)
txt.nl()
check_not_lesseq_uw(65535, 0)
comparevar = 65535
txt.print_uw(comparevar)
txt.nl()
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-2
txt.print_uw(comparevar)
txt.nl()
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-254
txt.print_uw(comparevar)
txt.nl()
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-255
txt.print_uw(comparevar)
txt.nl()
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-256
txt.print_uw(comparevar)
txt.nl()
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 65535-5000
txt.print_uw(comparevar)
txt.nl()
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 32769
txt.print_uw(comparevar)
txt.nl()
for lessvar in comparevar downto 0 {
check_lesseq_uw(lessvar, comparevar)
}
comparevar = 32768
txt.print_uw(comparevar)
txt.nl()
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 1
txt.print_uw(comparevar)
txt.nl()
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 0
txt.print_uw(comparevar)
txt.nl()
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 11111
txt.print_uw(comparevar)
txt.nl()
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 255
txt.print_uw(comparevar)
txt.nl()
for lessvar in 65535 downto comparevar+1 {
check_not_lesseq_uw(lessvar, comparevar)
}
comparevar = 256
txt.print_uw(comparevar)
txt.nl()
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.nl()
sys.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.nl()
sys.exit(1)
}
}
}
sub word_greater() {
word biggervar
word comparevar
txt.print("word >\n")
txt.print_w(-32767)
txt.nl()
check_greater_w(32767, -32767)
txt.print_w(32766)
txt.nl()
check_not_greater_w(-32766, 32766)
comparevar = 32765
txt.print_w(comparevar)
txt.nl()
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.nl()
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.nl()
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.nl()
for biggervar in comparevar downto -32768 {
check_not_greater_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar+1 {
check_greater_w(biggervar, comparevar)
}
comparevar = 32760
txt.print_w(comparevar)
txt.nl()
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.nl()
sys.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.nl()
sys.exit(1)
}
}
}
sub word_greaterequal() {
word biggervar
word comparevar
txt.print("word >=\n")
txt.print_w(-32767)
txt.nl()
check_greatereq_w(32767, -32767)
txt.print_w(32766)
txt.nl()
check_not_greatereq_w(-32766, 32766)
comparevar = 32765
txt.print_w(comparevar)
txt.nl()
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.nl()
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.nl()
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.nl()
for biggervar in comparevar-1 downto -32768 {
check_not_greatereq_w(biggervar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.nl()
for biggervar in 32767 downto comparevar {
check_greatereq_w(biggervar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.nl()
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.nl()
sys.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.nl()
sys.exit(1)
}
}
}
sub word_lessequal() {
word lessvar
word comparevar
txt.print("word <=\n")
txt.print_w(32767)
txt.nl()
check_lesseq_w(-32767, 32767)
txt.print_w(-32767)
txt.nl()
check_not_lesseq_w(32767, -32767)
comparevar = 0
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.nl()
for lessvar in comparevar downto -32768 {
check_lesseq_w(lessvar, comparevar)
}
comparevar = -32768
txt.print_w(comparevar)
txt.nl()
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.nl()
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.nl()
for lessvar in 32766 downto comparevar+1 {
check_not_lesseq_w(lessvar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.nl()
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.nl()
sys.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.nl()
sys.exit(1)
}
}
}
sub word_less() {
word lessvar
word comparevar
txt.print("word <\n")
txt.print_w(32767)
txt.nl()
check_less_w(-32767, 32767)
txt.print_w(-32767)
txt.nl()
check_not_less_w(32767, -32767)
comparevar = 0
txt.print_w(comparevar)
txt.nl()
for lessvar in -1 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -2
txt.print_w(comparevar)
txt.nl()
for lessvar in -3 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -254
txt.print_w(comparevar)
txt.nl()
for lessvar in -255 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -255
txt.print_w(comparevar)
txt.nl()
for lessvar in -256 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -256
txt.print_w(comparevar)
txt.nl()
for lessvar in -257 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -5000
txt.print_w(comparevar)
txt.nl()
for lessvar in -5001 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 1
txt.print_w(comparevar)
txt.nl()
for lessvar in 0 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 255
txt.print_w(comparevar)
txt.nl()
for lessvar in 254 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 256
txt.print_w(comparevar)
txt.nl()
for lessvar in 255 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 257
txt.print_w(comparevar)
txt.nl()
for lessvar in 256 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = 32767
txt.print_w(comparevar)
txt.nl()
for lessvar in 32766 downto -32768 {
check_less_w(lessvar, comparevar)
}
comparevar = -32768
txt.print_w(comparevar)
txt.nl()
for lessvar in 32766 downto -32768 {
check_not_less_w(lessvar, comparevar)
}
comparevar = -1
txt.print_w(comparevar)
txt.nl()
for lessvar in 32766 downto -1 {
check_not_less_w(lessvar, comparevar)
}
comparevar = 0
txt.print_w(comparevar)
txt.nl()
for lessvar in 32766 downto 0 {
check_not_less_w(lessvar, comparevar)
}
comparevar = 11111
txt.print_w(comparevar)
txt.nl()
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.nl()
sys.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.nl()
sys.exit(1)
}
}
}
}

View File

@ -56,11 +56,11 @@ main {
sub vector_v(uword x, uword y) {
gfx2.horizontal_line(x, y, 12, 1)
gfx2.horizontal_line(x+16, y+16, 12,1)
gfx2.horizontal_line(x+16, y+16, 11,1)
gfx2.line(x,y,x+16,y+16,1)
gfx2.line(x+11,y,x+16+6,y+10,1)
gfx2.line(x+16+6,y+10,x+48,y-16,1)
gfx2.line(x+16+11,y+16,x+48+11,y-16,1)
gfx2.line(x+11,y,x+16+5,y+10,1)
gfx2.line(x+16+5,y+10,x+47,y-16,1)
gfx2.line(x+16+10,y+16,x+46+12,y-16,1)
}
sub window_system() {
@ -200,9 +200,9 @@ widget {
gfx2.horizontal_line(x+width-1-16, y+height-20, 16, 2)
gfx2.horizontal_line(x+width-1-16, y+height-21, 16, 1)
gfx2.horizontal_line(x+width-1-16, y+height-30, 16, 2)
gfx2.line(x+width-1-12, y+height-23, x+width-8, y+height-28, 1)
gfx2.line(x+width-1-13, y+height-23, x+width-9, y+height-28, 1)
gfx2.line(x+width-1-3, y+height-23, x+width-9, y+height-28, 1)
gfx2.line(x+width-1-13, y+height-18, x+width-8, y+height-13, 1)
gfx2.line(x+width-1-13, y+height-18, x+width-9, y+height-13, 1)
gfx2.line(x+width-1-3, y+height-18, x+width-9, y+height-13, 1)
}
}

330
examples/cx16/bobs.p8 Normal file
View File

@ -0,0 +1,330 @@
%target cx16
%import palette
%import conv
%import textio
; "unlimited sprites / bobs" demo effect.
; Note that everything is prog8, no inline assembly used/required.
main {
sub start() {
cx16.screen_set_mode(0)
txt.print("\n\n how many sprites does\n the commander x16 have?\n")
sys.wait(180)
txt.print("\n\n the manual says: '128'.\n")
sys.wait(80)
txt.print("\n\n but that's just a manual...\n")
sys.wait(80)
txt.print("\n\n let's find out for ourselves,\n shall we?")
sys.wait(180)
; enable bitmap mode 320x240, 1 bpp, only layer 1
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000
cx16.VERA_DC_HSCALE = 64
cx16.VERA_DC_VSCALE = 64
cx16.VERA_L1_CONFIG = %00000100
cx16.VERA_L1_MAPBASE = 0
cx16.VERA_L1_TILEBASE = 0
; limit display heigth to 200 pixels to have enough vram for 14 backbuffers
const ubyte vstart = 20
const ubyte vheight = 200
cx16.VERA_CTRL = %00000010
cx16.VERA_DC_VSTART = vstart
cx16.VERA_DC_VSTOP = vstart + vheight - 1
init_buffers()
palette.set_color(0, $000)
palette.set_color(1, $af8)
cx16.set_rasterirq(&irq, 340) ; time it so that the page flip occurs near the bottom of the screen to avoid tearing
repeat {
; don't exit
}
}
const ubyte num_backbuffers = 12 ; there is vram space for 14 backbuffers. reduce to make tighter "loops"
uword num_bobs = 0
ubyte backbuffer = num_backbuffers-1
ubyte blitbuffer = 0
uword anim1 = $0432
uword anim2 = $0123
uword anim3 = $4321
uword anim4 = $8500
sub irq() {
; palette.set_color(0, $f00) ; debug rastertime
; draw 2 bobs per frame to speed up bob count
ubyte vmembase = blitbuffer*4 ; 2048 * 4 per backbuffer
blit(vmembase)
blitbuffer++
if blitbuffer==num_backbuffers
blitbuffer=0
vmembase = blitbuffer*4 ; 2048 * 4 per backbuffer
blit(vmembase)
blitbuffer++
if blitbuffer==num_backbuffers
blitbuffer=0
backbuffer++
if backbuffer==num_backbuffers {
backbuffer=0
num_bobs+=2
}
vmembase = backbuffer*4 ; 2048 * 4 per backbuffer
draw_number(vmembase, num_bobs)
cx16.VERA_L1_TILEBASE = vmembase << 2 ; flip to next backbuffer
; palette.set_color(0, $000)
}
sub init_buffers() {
; erase all vram
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = 0
cx16.VERA_ADDR_M = 0
cx16.VERA_ADDR_H = %00010000 ; auto incr 1
repeat $ffff
cx16.VERA_DATA0 = 0
repeat $f960
cx16.VERA_DATA0 = 0
}
sub blit(ubyte vmembase) {
ubyte bank = vmembase>=32
uword vmem = vmembase * 2048 ; mkword(vmembase,0) * 8
uword blit_x = (cos8u(msb(anim1)) as uword) + sin8u(msb(anim2))/5
ubyte blit_y = sin8u(msb(anim3))/2 + cos8u(msb(anim4))/5
vmem += blit_x/8 + (blit_y as uword) * 40
bitshift(lsb(blit_x) & 7)
; left column of the (shifted)sprite
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
cx16.VERA_ADDR_H = bank | %10110000 ; increment 40 for read (next line)
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
cx16.VERA_ADDR_H = bank | %10110000 ; increment 40 for read (next line)
ubyte ix
for ix in 0 to len(shifted_sprite)-1 step 3
cx16.VERA_DATA1 = cx16.VERA_DATA0 & shifted_mask[ix] | shifted_sprite[ix]
; middle column of the (shifted)sprite
vmem++
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
for ix in 1 to len(shifted_sprite)-1 step 3
cx16.VERA_DATA1 = cx16.VERA_DATA0 & shifted_mask[ix] | shifted_sprite[ix]
; right column of the (shifted)sprite
vmem++
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
for ix in 2 to len(shifted_sprite)-1 step 3
cx16.VERA_DATA1 = cx16.VERA_DATA0 & shifted_mask[ix] | shifted_sprite[ix]
anim1 += 217
anim2 += 190
anim3 += 222
anim4 += 195
}
sub bitshift(ubyte shift) {
ubyte yix
ubyte yy
for yy in 0 to 15 {
uword @zp sprw = sprite[yy]
uword @zp maskw = mask[yy]
ubyte @zp sprite_3 = 0
ubyte @zp mask_3 = 255
repeat shift {
sprw >>= 1
ror(sprite_3)
sys.set_carry()
ror(maskw)
ror(mask_3)
}
shifted_sprite[yix] = msb(sprw)
shifted_mask[yix] = msb(maskw)
yix++
shifted_sprite[yix] = lsb(sprw)
shifted_mask[yix] = lsb(maskw)
yix++
shifted_sprite[yix] = sprite_3
shifted_mask[yix] = mask_3
yix++
}
}
sub draw_number(ubyte vmembase, uword number) {
uword vmem = vmembase * 2048 ; mkword(vmembase,0) * 8
ubyte bank = vmembase>=32
vmem += 35
conv.str_uw0(number)
uword pixelsptr = &numberpixels + (conv.string_out[1] & 15)*7
ubyte pix
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
cx16.VERA_ADDR_H = bank | %10110000 ; increment 40 for read (next line)
for pix in 0 to 6
cx16.VERA_DATA0 = pixelsptr[pix]
vmem++
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
pixelsptr = &numberpixels + (conv.string_out[2] & 15)*7
for pix in 0 to 6
cx16.VERA_DATA0 = pixelsptr[pix]
vmem++
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
pixelsptr = &numberpixels + (conv.string_out[3] & 15)*7
for pix in 0 to 6
cx16.VERA_DATA0 = pixelsptr[pix]
vmem++
cx16.VERA_ADDR_L = lsb(vmem)
cx16.VERA_ADDR_M = msb(vmem)
pixelsptr = &numberpixels + (conv.string_out[4] & 15)*7
for pix in 0 to 6
cx16.VERA_DATA0 = pixelsptr[pix]
}
ubyte[10*7] numberpixels = [
%00111000,
%01000100,
%10000100,
%10000100,
%10000100,
%01111000,
%00000000,
%00010000,
%00110000,
%01010000,
%00010000,
%00010000,
%01111100,
%00000000,
%01111000,
%10000100,
%00011000,
%00110000,
%01100000,
%11111100,
%00000000,
%01111000,
%00000100,
%00111000,
%00000100,
%00000100,
%11111000,
%00000000,
%00010100,
%00100100,
%01000100,
%11111100,
%00000100,
%00000100,
%00000000,
%11111000,
%10000000,
%11111000,
%00000100,
%00000100,
%11111000,
%00000000,
%01111000,
%10000000,
%11111000,
%10000100,
%10000100,
%01111000,
%00000000,
%11111100,
%00001000,
%00010000,
%00010000,
%00010000,
%00010000,
%00000000,
%01111000,
%10000100,
%01111000,
%10000100,
%10000100,
%01111000,
%00000000,
%01111000,
%10000100,
%01111100,
%00000100,
%10000100,
%01111000,
%00000000
]
uword[16] sprite = [
%0000000000000000,
%0110001110000000,
%0101001001000000,
%0100111001000000,
%0100000000100000,
%0101001000100000,
%0101001000110000,
%0100100000101000,
%0101111000101000,
%0010000001010100,
%0001111110010100,
%0001000000010000,
%0001000000010000,
%0001010111010000,
%0001101100110000,
%0000000000000000
]
uword[16] mask = [
%1111111111111111,
%1000000001111111,
%1000000000111111,
%1000000000111111,
%1000000000011111,
%1000000000011111,
%1000000000001111,
%1000000000000111,
%1000000000000111,
%1100000000001011,
%1110000000001011,
%1110000000001111,
%1110000000001111,
%1110000000001111,
%1110010011001111,
%1111111111111111
]
ubyte[16*3] shifted_sprite
ubyte[16*3] shifted_mask
}

View File

@ -64,25 +64,25 @@ main {
asmsub print_number_gfx(ubyte num @ A) clobbers(A,Y) {
%asm {{
phx
jsr conv.ubyte2decimal
phx
pha
cpy #'0'
beq +
tya
jsr cx16.GRAPH_put_char
pla
jsr cx16.GRAPH_put_char
jmp _ones
+ pla
phx
jsr conv.ubyte2decimal
phx
pha
cpy #'0'
beq +
tya
jsr cx16.GRAPH_put_char
pla
jsr cx16.GRAPH_put_char
bra _ones
+ pla
cmp #'0'
beq _ones
jsr cx16.GRAPH_put_char
_ones pla
jsr cx16.GRAPH_put_char
plx
rts
_ones pla
jsr cx16.GRAPH_put_char
plx
rts
}}
}

View File

@ -0,0 +1,68 @@
%target cx16
%import palette
%option no_sysinit
; Vertical rasterbars a.k.a. "Kefren bars"
; also see: rasterbars.p8
main {
sub start() {
uword[32] colors = [
$000,
$011, $112, $213, $214,
$315, $316, $417, $418,
$519, $51a, $62b, $62c,
$73d, $73e, $84f, $94f,
$93e, $83d, $82c, $72b,
$71a, $619, $618, $517,
$516, $415, $414, $313,
$312, $211, $100
]
; Not yet implemented in ROM: cx16.FB_set_palette(&colors, 0, len(colors)*3)
palette.set_rgb(&colors, len(colors))
cx16.screen_set_mode(128) ; low-res bitmap 256 colors
cx16.FB_init()
cx16.VERA_DC_VSCALE = 0 ; display trick spoiler.......: stretch display all the way to the bottom
cx16.set_rasterirq(&irq.irq, 0)
repeat {
; don't exit
}
}
}
irq {
uword next_irq_line = 0
ubyte anim1 = 0
ubyte av1 = 0
ubyte anim2 = 0
ubyte av2 = 0
ubyte[32] pixels = 0 to 31
sub irq() {
next_irq_line += 6
anim1 += 4
anim2 += 6
if next_irq_line > 400 {
av1++
av2 += 2
anim1 = av1
anim2 = av2
next_irq_line = 0
; erase the bars
cx16.FB_cursor_position(0, 0)
cx16.FB_fill_pixels(320, 1, 0)
} else {
; add new bar
cx16.FB_cursor_position(sin8u(anim1)/2 + cos8u(anim2)/2 + $0010, 0)
cx16.FB_set_pixels(pixels, len(pixels))
}
cx16.set_rasterline(next_irq_line)
}
}

View File

@ -2,6 +2,10 @@
%import textio
%import palette
; horizontal raster bars
; also see: kefrenbars.p8
main {
sub start() {
@ -9,7 +13,6 @@ main {
txt.plot(14,14)
txt.print("raster bars!")
cx16.rombank(0) ; switch to kernal rom for faster irq handling
cx16.set_rasterirq(&irq.irq, 0)
repeat {

View File

@ -2,7 +2,6 @@
%import gfx2
%import textio
%import test_stack
%zeropage dontuse
main {

Binary file not shown.

Binary file not shown.

View File

@ -1,14 +1,17 @@
%target cx16
%import textio
%option no_sysinit
%zeropage basicsafe
; simple test program for the "VTUI" text user interface library
; see: https://github.com/JimmyDansbo/VTUIlib
main {
sub start() {
; todo feedback: new routines at the end of the jump table so existing jump vectors remain unchanged
vtui.initialize()
store_logo()
txt.lowercase()
vtui.initialize()
vtui.screen_set(2)
vtui.clr_scr('%', $50)
vtui.gotoxy(5,5)
@ -16,29 +19,46 @@ main {
vtui.gotoxy(10,10)
vtui.border(1, 40, 6, $47)
vtui.gotoxy(12,12)
vtui.print_str(@"Hello, world! vtui from Prog8!", $f2, $80)
vtui.print_str2(@"Hello, world! vtui from Prog8!", $f2, $80)
vtui.gotoxy(12,13)
vtui.print_str("Hello, world! vtui from Prog8!", $f2, $00)
vtui.print_str2("Hello, world! vtui from Prog8!", $f2, $00)
repeat {
}
str inputbuffer = "?" * 20
; txt.print_uwhex(inputbuffer, 1)
; txt.chrout(':')
; txt.print(inputbuffer)
; txt.chrout('\n')
vtui.gotoxy(5,20)
vtui.print_str2(@"Enter your name: ", $e3, $80)
ubyte length = vtui.input_str(inputbuffer, len(inputbuffer), $21)
vtui.gotoxy(8,22)
vtui.print_str2(@"Your name is: ", $e3, $80)
;vtui.print_str2(inputbuffer, $67, $00)
vtui.print_str(inputbuffer, length, $67, $00)
; txt.uppercase() ; kills vtui?
logo_mover()
}
sub store_logo() {
vtui.gotoxy(0, 0)
vtui.save_rect($80, 1, $0000, 7, 7)
vtui.gotoxy(0, 0)
vtui.save_rect($80, 1, $0100, 7, 7)
}
sub logo_mover() {
ubyte xcoord
ubyte ycoord
ubyte newx
ubyte newy
ubyte xcoord = 0
ubyte ycoord = 0
ubyte newx = 0
ubyte newy = 0
vtui.initialize()
;vtui.screen_set(2)
vtui.gotoxy(30, 32)
vtui.print_str("arrow keys to move!", $61, 0)
vtui.gotoxy(0, 0)
vtui.save_rect(1, 1, $0000, 7, 7)
vtui.gotoxy(0, 0)
vtui.save_rect(1, 1, $0100, 7, 7)
vtui.print_str2("arrow keys to move!", $61, 0)
char_loop:
ubyte char = c64.GETIN()
@ -76,11 +96,11 @@ char_loop:
sub move_logo() {
vtui.gotoxy(xcoord, ycoord)
vtui.rest_rect(1, 1, $0100, 7, 7)
vtui.rest_rect($80, 1, $0100, 7, 7)
vtui.gotoxy(newx, newy)
vtui.save_rect(1, 1, $0100, 7, 7)
vtui.save_rect($80, 1, $0100, 7, 7)
vtui.gotoxy(newx, newy)
vtui.rest_rect(1, 1, $0000, 7, 7)
vtui.rest_rect($80, 1, $0000, 7, 7)
xcoord = newx
ycoord = newy
}
@ -91,7 +111,7 @@ char_loop:
vtui $1000 {
%asmbinary "VTUI0.6.BIN", 2 ; skip the 2 dummy load address bytes
%asmbinary "VTUI0.8.BIN", 2 ; skip the 2 dummy load address bytes
; NOTE: base address $1000 here must be the same as the block's memory address, for obvious reasons!
romsub $1000 = initialize() clobbers(A, X, Y)
@ -105,11 +125,24 @@ vtui $1000 {
romsub $1017 = scan_char() -> ubyte @A, ubyte @X
romsub $101a = hline(ubyte char @A, ubyte length @Y, ubyte colors @X) clobbers(A)
romsub $101d = vline(ubyte char @A, ubyte height @Y, ubyte colors @X) clobbers(A)
romsub $1020 = print_str(str string @R0, ubyte colors @X, ubyte convertchars @A) clobbers(A, Y)
romsub $1020 = print_str(str txtstring @R0, ubyte length @Y, ubyte colors @X, ubyte convertchars @A) clobbers(A, Y)
romsub $1023 = fill_box(ubyte char @A, ubyte width @R1, ubyte height @R2, ubyte colors @X) clobbers(A, Y)
romsub $1026 = pet2scr(ubyte char @A) -> ubyte @A
romsub $1029 = scr2pet(ubyte char @A) -> ubyte @A
romsub $102c = border(ubyte mode @A, ubyte width @R1, ubyte height @R2, ubyte colors @X) clobbers(Y) ; NOTE: mode 6 means 'custom' characters taken from r3 - r6
romsub $102f = save_rect(ubyte ramtype @A, ubyte vbank @Pc, uword address @R0, ubyte width @R1, ubyte height @R2) clobbers(A, X, Y)
romsub $1032 = rest_rect(ubyte ramtype @A, ubyte vbank @Pc, uword address @R0, ubyte width @R1, ubyte height @R2) clobbers(A, X, Y)
romsub $1035 = input_str(uword buffer @R0, ubyte buflen @Y, ubyte colors @X) clobbers (A) -> ubyte @Y
; -- helper function to do string length counting for you internally
asmsub print_str2(str txtstring @R0, ubyte colors @X, ubyte convertchars @A) clobbers(A, Y) {
%asm {{
pha
lda cx16.r0
ldy cx16.r0+1
jsr prog8_lib.strlen
pla
jmp print_str
}}
}
}

View File

@ -14,4 +14,3 @@ main {
}
}
}

View File

@ -106,7 +106,7 @@ main {
ubyte @zp ii
for ii in 0 to 7 {
; use 16 bit rng for a bit more randomness instead of the 8-bit rng
if lsb(rndw()) > s {
if rnd() > s {
b |= bittab[ii]
}
}

View File

@ -17,7 +17,7 @@ main {
irq {
const ubyte barheight = 4 ; should be big enough to re-trigger the Raster irq properly.
const ubyte barheight = 3 ; should be big enough to re-trigger the Raster irq properly.
ubyte[] colors = [6,2,4,5,15,7,1,13,3,12,8,11,9]
ubyte color = 0
ubyte yanim = 0

View File

@ -42,7 +42,7 @@ main {
for i in 0 to 7 {
c64.SPRPTR[i] = $0a00 / 64
c64.SPXY[i*2] = 50+25*i
c64.SPXY[i*2+1] = rnd()
c64.SPXY[i*2+1] = fastrnd8()
}
c64.SPENA = 255 ; enable all sprites
@ -60,7 +60,7 @@ irq {
ubyte @zp i
for i in 0 to 14 step 2 {
c64.SPXY[i+1]--
ubyte @zp r = rnd()
ubyte @zp r = fastrnd8()
if r>200
c64.SPXY[i]++
else if r<40

View File

@ -4,15 +4,21 @@
main {
sub start() {
uword screen = 2000
ubyte i = 1
uword w = 33
str derp ="derp"
ubyte[] array = [1,2,3]
@(screen+i) = 128
@(i+screen) = 129
txt.print("hello")
txt.print("done\n")
; str filename = "titlescreen.bin"
; ubyte success = cx16.vload(filename, 8, 0, $0000)
; if success {
; txt.print("load ok")
; cx16.VERA_DC_HSCALE = 64
; cx16.VERA_DC_VSCALE = 64
; cx16.VERA_L1_CONFIG = %00011111 ; 256c bitmap mode
; cx16.VERA_L1_MAPBASE = 0
; cx16.VERA_L1_TILEBASE = 0
; } else {
; txt.print("load fail")
; }
}
}

View File

@ -414,14 +414,18 @@ galaxy {
sub init(ubyte galaxynum) {
number = 1
planet.number = 255
seed = [base0, base1, base2]
seed[0] = base0
seed[1] = base1
seed[2] = base2
repeat galaxynum-1 {
nextgalaxy()
}
}
sub nextgalaxy() {
seed = [twist(seed[0]), twist(seed[1]), twist(seed[2])]
seed[0] = twist(seed[0])
seed[1] = twist(seed[1])
seed[2] = twist(seed[2])
number++
if number==9
number = 1
@ -658,7 +662,10 @@ galaxy {
planet.species_kind = (planet.species_look + (seed2_msb & 3)) & 7 ;Add bits 0-1 of w2_hi to A from previous step, and take bits 0-2 of the result
}
planet.goatsoup_seed = [lsb(seed[1]), msb(seed[1]), lsb(seed[2]), seed2_msb]
planet.goatsoup_seed[0] = lsb(seed[1])
planet.goatsoup_seed[1] = msb(seed[1])
planet.goatsoup_seed[2] = lsb(seed[2])
planet.goatsoup_seed[3] = seed2_msb
}
sub tweakseed() {

View File

@ -2,6 +2,7 @@
%import syslib
%zeropage basicsafe
spritedata $0a00 {
; this memory block contains the sprite data
; it must start on an address aligned to 64 bytes.

View File

@ -14,7 +14,7 @@
<keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;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;inline;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;cos;cos16;cos16u;cos8;cos8u;deg;floor;len;ln;log2;lsb;lsl;lsr;max;memory;min;mkword;msb;offsetof;peek;peekw;poke;pokew;rad;reverse;rnd;rndf;rndw;rol;rol2;ror;ror2;round;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;sum;swap;tan" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;cmp;cos;cos16;cos16u;cos8;cos8u;deg;fastrnd8;floor;len;ln;log2;lsb;lsl;lsr;max;memory;min;mkword;msb;offsetof;peek;peekw;poke;pokew;rad;reverse;rnd;rndf;rndw;rol;rol2;ror;ror2;round;sgn;sin;sin16;sin16u;sin8;sin8u;sizeof;sort;sqrt;sqrt16;sum;swap;tan" />
</highlighting>
<extensionMap>
<mapping ext="p8" />

109
syntax-files/Vim/prog8.vim Normal file
View File

@ -0,0 +1,109 @@
" Vim syntax file
" Language: Prog8
" Maintainer: Elektron72
" Latest Revision: 23 March 2021
if exists("b:current_syntax")
finish
endif
syn match prog8Comment ";.*$"
syn region prog8String start=+@\?"+ skip=+\\"+ end=+"+
syn region prog8Character start=+@\?'+ skip=+\\'+ end=+'+
syn match prog8Number "\<\d\+\>"
syn match prog8Number "$\x\+\>"
syn match prog8Number "%[01]\+\>"
syn keyword prog8Boolean true false
syn match prog8Float "\<\d\+\.\d\+\([eE]\d\+\)\?\>"
syn region prog8Expression matchgroup=prog8AddressOp start="@(" end=")"
\ transparent
syn match prog8Function "\(\<\(asm\)\?sub\>\s\+\)\@16<=\<\w\+\>"
syn match prog8Function "\(romsub\s\+$\x\+\s\+=\s\+\)\@16<=\<\w\+\>"
syn keyword prog8Statement break goto return asmsub sub inline
syn match prog8Statement "\<\(asm\|rom\)\?sub\>"
syn keyword prog8Conditional if else when
syn keyword prog8Conditional if_cs if_cc if_vs if_vc if_eq if_z if_ne if_nz
syn keyword prog8Conditional if_pl if_pos if_mi if_neg
syn keyword prog8Conditional when
syn keyword prog8Repeat for while in do until repeat
syn match prog8Label "\<\w\+\>:"
syn keyword prog8Operator and or to downto as void
syn match prog8Directive "\(^\|\s\)%\(target\|output\|launcher\|zeropage\)\>"
syn match prog8Directive "\(^\|\s\)%\(zpreserved\|address\|import\|option\)\>"
syn match prog8Directive "\(^\|\s\)%\(asmbinary\|asminclude\|breakpoint\)\>"
syn match prog8Directive "\(^\|\s\)%asm\>"
syn match prog8Type "\<\%(u\?byte\|u\?word\|float\|str\)\>"
syn region prog8ArrayType matchgroup=prog8Type
\ start="\<\%(u\?byte\|u\?word\|float\|str\)\[" end="\]"
\ transparent
syn keyword prog8StorageClass const
syn match prog8StorageClass "\(^\|\s\)@zp\>"
syn keyword prog8Structure struct
syn region prog8Block start="{" end="}" transparent
syn region prog8Expression start="(" end=")" transparent
syn region prog8Array start="\[" end="\]" transparent
if !exists("g:prog8_no_highlight_builtins")
runtime! syntax/prog8_builtins.vim
endif
syn region prog8Asm start="\(%asm\)\@16<=\s\+{{" end="}}" contains=
\prog8Comment,
\prog8Character,
\prog8Number,
\prog8AsmIdentifier,
\prog8AsmStatement,
\prog8AsmLabel,
\prog8BuiltInVar,
\prog8BuiltInFunc
syn sync match prog8AsmSync groupthere prog8Asm "%asm\s\+{{"
syn keyword prog8AsmIdentifier a x y contained
syn keyword prog8AsmStatement adc and asl bbr bbs bcc bcs beq bit bmi contained
syn keyword prog8AsmStatement bne bpl bra brk bvc bvs clc cld cli clv contained
syn keyword prog8AsmStatement cmp cpx cpy dec dex dey eor inc inx iny contained
syn keyword prog8AsmStatement jmp jsr lda ldx ldy lsr nop ora pha php contained
syn keyword prog8AsmStatement phx phy pla plp plx ply rmb rol ror rti contained
syn keyword prog8AsmStatement rts sbc sec sed sei smb sta stp stx sty contained
syn keyword prog8AsmStatement stz tax tay trb tsb tsx txa txs tya wai contained
syn match prog8AsmLabel "^\([-+]\|\(\w\+\.\)*\w\+\)" contained
hi def link prog8Comment Comment
hi def link prog8String String
hi def link prog8Character Character
hi def link prog8Number Number
hi def link prog8Boolean Boolean
hi def link prog8Float Float
hi def link prog8AddressOp Identifier
hi def link prog8Function Function
hi def link prog8Statement Statement
hi def link prog8Conditional Conditional
hi def link prog8Repeat Repeat
hi def link prog8Label Label
hi def link prog8Operator Operator
hi def link prog8Directive PreProc
hi def link prog8Type Type
hi def link prog8StorageClass StorageClass
hi def link prog8Structure Structure
hi def link prog8AsmIdentifier Identifier
hi def link prog8AsmStatement Statement
hi def link prog8AsmLabel Label

Some files were not shown because too many files have changed in this diff Show More