Compare commits

...
25 Commits
Author SHA1 Message Date
Irmen de Jong e4bca5fe47 version 2021-03-06 23:07:30 +01:00
Irmen de Jong a1729b65ab fix min(), max(), sum(), abs() 2021-03-06 22:57:22 +01:00
Irmen de Jong 2950d26c8e array and struct value assignments now via memcopy instead of assignment per element 2021-03-06 22:10:03 +01:00
Irmen de Jong 4f8d4a9585 use memcopy to assign arrays 2021-03-06 19:01:16 +01:00
Irmen de Jong d787795759 simplified 2021-03-06 15:43:23 +01:00
Irmen de Jong cf74e73e27 IDEA syntax colors 2021-03-06 15:23:58 +01:00
Irmen de Jong 2770254fd9 removed inline assembly from bobs demo 2021-03-06 14:31:26 +01:00
Irmen de Jong de04bd8cfa added more convenient number-to-string functions to conv library 2021-03-06 13:47:27 +01:00
Irmen de Jong 076a547f91 added more convenient number-to-string functions to conv library 2021-03-06 13:34:57 +01:00
Irmen de Jong 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
Irmen de Jong 6c66f86103 todo 2021-03-05 21:07:35 +01:00
Irmen de Jong 26502c949a add unlimited bobs example 2021-03-05 19:05:13 +01:00
Irmen de Jong 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
Irmen de Jong 96ba9f5902 spelling correction 2021-03-04 01:31:29 +01:00
Irmen de Jong 3a6ba0ab71 added 'kefrenbars' example 2021-03-03 01:09:18 +01:00
Irmen de Jong 32d894d6b6 optimized repeat loop for word counts 2021-02-28 21:22:46 +01:00
Irmen de Jong 543efa4299 attempt 2 at optimizing repeats 2021-02-28 21:02:17 +01:00
Irmen de Jong eba0708099 Revert "optimized repeat loop for word counts"
This reverts commit 51e6bf0d
2021-02-28 20:29:28 +01:00
Irmen de Jong 51e6bf0d45 optimized repeat loop for word counts 2021-02-28 17:34:18 +01:00
Irmen de Jong 07b5c44a54 preparing to optimize 16 bit repeat loop 2021-02-28 17:13:15 +01:00
Irmen de Jong 9fe32c1c34 codegen uses 'bra' on 65c02 instead of 'jmp' 2021-02-28 16:46:08 +01:00
Irmen de Jong 0e0278c84a for loops now use 'bra' if available 2021-02-28 16:35:59 +01:00
Irmen de Jong dea775a9cd package refactor 2021-02-28 16:29:15 +01:00
Irmen de Jong 7e3e18a5c7 deal with 'bra' better on 65c02 2021-02-28 16:20:03 +01:00
Irmen de Jong 8e3ebc84f0 readme 2021-02-28 15:40:04 +01:00
56 changed files with 1306 additions and 631 deletions

No files matched your search

+7 -4
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:*
+1 -1
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 {
+435 -222
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)
}}
}
}
+1 -1
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
+1 -1
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.
+5 -5
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
}}
}
+6 -2
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
+10
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
+1 -1
View File
@@ -1 +1 @@
6.2
6.3
@@ -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)")
}
}
}
@@ -268,32 +268,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 +337,94 @@ 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)
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 emptyList()
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 +432,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")
}
@@ -137,6 +137,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 +321,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 -> {
@@ -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)
}
@@ -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)
@@ -930,7 +930,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 +952,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 +976,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 +1008,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 +1021,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 +1075,7 @@ $counterVar .word 0""")
out(whileLabel)
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
translate(stmt.body)
out(" jmp $whileLabel")
jmp(whileLabel)
out(endLabel)
loopEndLabels.pop()
}
@@ -1138,7 +1109,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 +1128,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 +1191,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 +1248,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 +1347,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)
@@ -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
@@ -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,12 @@ 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.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.SourceStorageKind
import prog8.compiler.target.cpu6502.codegen.assignment.TargetStorageKind
import prog8.compiler.target.subroutineFloatEvalResultVar2
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) {
@@ -51,7 +56,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)
@@ -478,11 +483,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 +514,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")
@@ -924,15 +929,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 +946,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")
@@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package prog8.compiler.target.cpu6502.codegen
import prog8.ast.Program
import prog8.ast.base.*
@@ -370,7 +370,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -381,7 +381,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
cmp #${rightConstVal.number}
bcs $jumpIfFalseLabel""")
else
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
}
else if (left is DirectMemoryRead) {
@@ -390,7 +390,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.out(" cmp #${rightConstVal.number} | bcs $jumpIfFalseLabel")
}
else
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
}
}
@@ -405,7 +405,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -439,7 +439,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -455,7 +455,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
bcs $jumpIfFalseLabel
+""")
else
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
}
}
@@ -470,7 +470,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -500,7 +500,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -538,7 +538,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -576,7 +576,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -611,7 +611,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -647,7 +647,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -693,7 +693,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -731,7 +731,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -765,7 +765,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal>leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -801,7 +801,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -831,7 +831,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -868,7 +868,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -897,7 +897,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal<leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -928,7 +928,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal!=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -959,7 +959,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal==leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -990,7 +990,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal!=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -1027,7 +1027,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal==leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -1067,7 +1067,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal!=leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -1151,7 +1151,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
if(rightConstVal!=null) {
if(leftConstVal!=null) {
if(rightConstVal==leftConstVal)
asmgen.out(" jmp $jumpIfFalseLabel")
asmgen.jmp(jumpIfFalseLabel)
return
} else {
if (left is IdentifierReference) {
@@ -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()
}
@@ -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) {
@@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package prog8.compiler.target.cpu6502.codegen
import prog8.ast.Program
import prog8.ast.base.*
@@ -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 {
@@ -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)
@@ -356,7 +357,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)
@@ -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)
+2 -2
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)
@@ -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)
}
}
@@ -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.
@@ -728,7 +728,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 +1000,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)
+3 -2
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.
+1 -1
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.*)
+7 -3
View File
@@ -757,11 +757,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
@@ -824,6 +825,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,
+1 -1
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::
+3 -4
View File
@@ -2,13 +2,12 @@
TODO
====
- optimize assigning array and struct variables (multi-element assings -> memcopy)
- optimize several inner loops in gfx2
- fix the bresenham line routines in graphics and gfx2 (sometimes they're a pixel 'off') (or maybe finally just replace them with a proper all-assembly implementation)
- 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?
+5 -5
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)
}
+6 -6
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
+10 -3
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() {
+10 -4
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() {
+10 -3
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() {
+10 -3
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() {
+10 -3
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() {
+10 -3
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() {
+10 -3
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() {
+10 -3
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() {
+10 -3
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() {
+10 -4
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() {
+330
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
}
+16 -16
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
}}
}
+67
View File
@@ -0,0 +1,67 @@
%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)
}
}
+4 -1
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 {
-1
View File
@@ -2,7 +2,6 @@
%import gfx2
%import textio
%import test_stack
%zeropage dontuse
main {
-2
View File
@@ -4,8 +4,6 @@
main {
sub start() {
; todo feedback: new routines at the end of the jump table so existing jump vectors remain unchanged
txt.lowercase()
vtui.initialize()
-1
View File
@@ -14,4 +14,3 @@ main {
}
}
}
+1 -1
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]
}
}
+2 -2
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
+2 -11
View File
@@ -1,18 +1,9 @@
%import textio
%zeropage basicsafe
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("done\n")
txt.print("hello")
}
}
+10 -3
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() {
+1 -1
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;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" />