mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
82
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4b69ac79c | ||
|
|
e61a2d7083 | ||
|
|
c03f6604af | ||
|
|
572bb38ddb | ||
|
|
42c5c0cb9f | ||
|
|
e145d2255e | ||
|
|
442fa07dd4 | ||
|
|
31ae9e1243 | ||
|
|
d7f83f8df2 | ||
|
|
29e2d4e0c8 | ||
|
|
2732d2c844 | ||
|
|
c4a037b277 | ||
|
|
0e614ad6fc | ||
|
|
ca1a8cd617 | ||
|
|
ba96a637be | ||
|
|
c2cac772e3 | ||
|
|
6b7216f4ec | ||
|
|
e4fb5946dd | ||
|
|
ca61248861 | ||
|
|
68d7b4649e | ||
|
|
0416aacbbd | ||
|
|
bc731e6f8e | ||
|
|
ae5d7705bb | ||
|
|
b9bd541532 | ||
|
|
83639c2535 | ||
|
|
25d80f4df1 | ||
|
|
74f918d911 | ||
|
|
a20efa56eb | ||
|
|
f4d83075be | ||
|
|
254592c383 | ||
|
|
ee23ac0537 | ||
|
|
a48cf0bb24 | ||
|
|
dae59238cd | ||
|
|
8736da1a21 | ||
|
|
09a1de69e7 | ||
|
|
63d67bc6cb | ||
|
|
7099245204 | ||
|
|
4d097d2139 | ||
|
|
6485bf9ad9 | ||
|
|
b7c5b1bfc7 | ||
|
|
2b7546e827 | ||
|
|
3549ccf4b3 | ||
|
|
e2f5752d9a | ||
|
|
1a59019fc8 | ||
|
|
7bac7bdc3e | ||
|
|
19fe58dbac | ||
|
|
0a5b30e21c | ||
|
|
664818fd29 | ||
|
|
d5214e2505 | ||
|
|
d906fcea0e | ||
|
|
29c8e8b740 | ||
|
|
71fec4c555 | ||
|
|
5ee36c897d | ||
|
|
4aba0c7405 | ||
|
|
ed7479c854 | ||
|
|
8d3d5f726a | ||
|
|
a9a7068818 | ||
|
|
1bde7c7718 | ||
|
|
17068130bb | ||
|
|
81a91d62cb | ||
|
|
2575263438 | ||
|
|
7f0e25cb50 | ||
|
|
a1e4e9c50f | ||
|
|
98eff2701b | ||
|
|
8b84f87217 | ||
|
|
306a1b7bc2 | ||
|
|
481214c46e | ||
|
|
a5961cbeab | ||
|
|
3bf335e0a0 | ||
|
|
68f696d165 | ||
|
|
1170aed026 | ||
|
|
bf1b2066b6 | ||
|
|
4c080afb76 | ||
|
|
ee1c43ca91 | ||
|
|
1c2e6f9e4c | ||
|
|
dd379430d9 | ||
|
|
42033ebd35 | ||
|
|
a086d6e009 | ||
|
|
c70bbdab26 | ||
|
|
3d956ef554 | ||
|
|
329f491c30 | ||
|
|
e93701f50e |
No files matched your search
+1
-1
@@ -30,4 +30,4 @@ parsetab.py
|
||||
.gradle
|
||||
/prog8compiler.jar
|
||||
sd*.img
|
||||
|
||||
*.d64
|
||||
@@ -1,3 +1,10 @@
|
||||
|
||||
This sofware license is for Prog8 the compiler + associated libraries.
|
||||
The software generated by running the compiler is excluded from this.
|
||||
|
||||
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@ Prog8 - Structured Programming Language for 8-bit 6502/65c02 microprocessors
|
||||
|
||||
*Written by Irmen de Jong (irmen@razorvine.net)*
|
||||
|
||||
*Software license: GNU GPL 3.0, see file LICENSE*
|
||||
|
||||
|
||||
This is a structured programming language for the 8-bit 6502/6510/65c02 microprocessor from the late 1970's and 1980's
|
||||
as used in many home computers from that era. It is a medium to low level programming language,
|
||||
which aims to provide many conveniences over raw assembly code (even when using a macro assembler).
|
||||
@@ -19,6 +16,13 @@ Documentation
|
||||
Full documentation (syntax reference, how to use the language and the compiler, etc.) can be found at:
|
||||
https://prog8.readthedocs.io/
|
||||
|
||||
Software license
|
||||
----------------
|
||||
GNU GPL 3.0, see file LICENSE
|
||||
|
||||
- prog8 (the compiler + libraries) is licensed under GNU GPL 3.0
|
||||
- *exception:* the resulting files created by running the compiler are free to use in whatever way desired.
|
||||
|
||||
|
||||
What does Prog8 provide?
|
||||
------------------------
|
||||
@@ -27,20 +31,20 @@ What does Prog8 provide?
|
||||
- fast execution speed due to compilation to native assembly code. It's possible to write certain raster interrupt 'demoscene' effects purely in Prog8.
|
||||
- modularity, symbol scoping, subroutines
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
|
||||
- strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||
- automatic static variable allocations, automatic string and array variables and string sharing
|
||||
- subroutines with input parameters and result values
|
||||
- high-level program optimizations
|
||||
- small program boilerplate/compilersupport overhead
|
||||
- Programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
- 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
|
||||
- structs to group together sets of variables and manipulate them at once
|
||||
- many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
||||
- 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
|
||||
- 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.
|
||||
- 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,7 +1,7 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||
id "org.jetbrains.kotlin.jvm" version "1.5.10"
|
||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ sub print_f (float value) {
|
||||
}}
|
||||
}
|
||||
|
||||
%asminclude "library:c64/floats.asm", ""
|
||||
%asminclude "library:c64/floats_funcs.asm", ""
|
||||
%asminclude "library:c64/floats.asm"
|
||||
%asminclude "library:c64/floats_funcs.asm"
|
||||
|
||||
}
|
||||
@@ -703,4 +703,37 @@ cx16 {
|
||||
&uword r14 = $cf1c
|
||||
&uword r15 = $cf1e
|
||||
|
||||
&ubyte r0L = $cf00
|
||||
&ubyte r1L = $cf02
|
||||
&ubyte r2L = $cf04
|
||||
&ubyte r3L = $cf06
|
||||
&ubyte r4L = $cf08
|
||||
&ubyte r5L = $cf0a
|
||||
&ubyte r6L = $cf0c
|
||||
&ubyte r7L = $cf0e
|
||||
&ubyte r8L = $cf10
|
||||
&ubyte r9L = $cf12
|
||||
&ubyte r10L = $cf14
|
||||
&ubyte r11L = $cf16
|
||||
&ubyte r12L = $cf18
|
||||
&ubyte r13L = $cf1a
|
||||
&ubyte r14L = $cf1c
|
||||
&ubyte r15L = $cf1e
|
||||
|
||||
&ubyte r0H = $cf01
|
||||
&ubyte r1H = $cf03
|
||||
&ubyte r2H = $cf05
|
||||
&ubyte r3H = $cf07
|
||||
&ubyte r4H = $cf09
|
||||
&ubyte r5H = $cf0b
|
||||
&ubyte r6H = $cf0d
|
||||
&ubyte r7H = $cf0f
|
||||
&ubyte r8H = $cf11
|
||||
&ubyte r9H = $cf13
|
||||
&ubyte r10H = $cf15
|
||||
&ubyte r11H = $cf17
|
||||
&ubyte r12H = $cf19
|
||||
&ubyte r13H = $cf1b
|
||||
&ubyte r14H = $cf1d
|
||||
&ubyte r15H = $cf1f
|
||||
}
|
||||
@@ -158,7 +158,7 @@ sub print_f (float value) {
|
||||
}}
|
||||
}
|
||||
|
||||
%asminclude "library:c64/floats.asm", ""
|
||||
%asminclude "library:c64/floats_funcs.asm", ""
|
||||
%asminclude "library:c64/floats.asm"
|
||||
%asminclude "library:c64/floats_funcs.asm"
|
||||
|
||||
}
|
||||
+139
-122
@@ -22,6 +22,7 @@
|
||||
; mode 6 = bitmap 640 x 480 x 4c
|
||||
; higher color dephts in highres are not supported due to lack of VRAM
|
||||
|
||||
|
||||
; TODO can we make a FB vector table and emulation routines for the Cx16s' GRAPH_init() call? to replace the builtin 320x200 fb driver?
|
||||
|
||||
gfx2 {
|
||||
@@ -274,9 +275,9 @@ _done
|
||||
ora colorbits,y
|
||||
sta cx16.VERA_DATA0
|
||||
cpy #%00000011 ; next vera byte?
|
||||
bne +
|
||||
bne ++
|
||||
inc cx16.VERA_ADDR_L
|
||||
bne +
|
||||
bne ++
|
||||
inc cx16.VERA_ADDR_M
|
||||
+ bne +
|
||||
inc cx16.VERA_ADDR_H
|
||||
@@ -292,92 +293,56 @@ _done
|
||||
}
|
||||
|
||||
sub vertical_line(uword x, uword y, uword height, ubyte color) {
|
||||
position(x,y)
|
||||
when active_mode {
|
||||
1, 5 -> {
|
||||
; monochrome, either resolution
|
||||
; note for the 1 bpp modes we can't use vera's auto increment mode because we have to 'or' the pixel data in place.
|
||||
; TODO use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment IS possible
|
||||
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
|
||||
cx16.r15 = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
||||
if active_mode>=5
|
||||
cx16.r14 = 640/8
|
||||
else
|
||||
cx16.r14 = 320/8
|
||||
; monochrome, lo-res
|
||||
cx16.r15L = gfx2.plot.bits[x as ubyte & 7] ; bitmask
|
||||
if color {
|
||||
if monochrome_dont_stipple_flag {
|
||||
; draw continuous line.
|
||||
position2(x,y,true)
|
||||
if active_mode==1
|
||||
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||
repeat height {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
ora cx16.r15
|
||||
sta cx16.VERA_DATA0
|
||||
lda cx16.VERA_ADDR_L
|
||||
clc
|
||||
adc cx16.r14 ; advance vera ptr to go to the next line
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.VERA_ADDR_M
|
||||
adc #0
|
||||
sta cx16.VERA_ADDR_M
|
||||
; lda cx16.VERA_ADDR_H ; the bitmap size is small enough to not have to deal with the _H part.
|
||||
; adc #0
|
||||
; sta cx16.VERA_ADDR_H
|
||||
ora cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
} else {
|
||||
; stippling.
|
||||
height = (height+1)/2 ; TODO is the line sometimes 1 pixel too long now because of rounding?
|
||||
%asm {{
|
||||
lda x
|
||||
eor y
|
||||
and #1
|
||||
bne +
|
||||
lda cx16.VERA_ADDR_L
|
||||
clc
|
||||
adc cx16.r14 ; advance vera ptr to go to the next line for correct stipple pattern
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.VERA_ADDR_M
|
||||
adc #0
|
||||
sta cx16.VERA_ADDR_M
|
||||
+
|
||||
asl cx16.r14
|
||||
ldy height
|
||||
beq +
|
||||
- lda cx16.VERA_DATA0
|
||||
ora cx16.r15
|
||||
sta cx16.VERA_DATA0
|
||||
lda cx16.VERA_ADDR_L
|
||||
clc
|
||||
adc cx16.r14 ; advance vera data ptr to go to the next-next line
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.VERA_ADDR_M
|
||||
adc #0
|
||||
sta cx16.VERA_ADDR_M
|
||||
; lda cx16.VERA_ADDR_H ; the bitmap size is small enough to not have to deal with the _H part.
|
||||
; adc #0
|
||||
; sta cx16.VERA_ADDR_H
|
||||
dey
|
||||
bne -
|
||||
+
|
||||
}}
|
||||
; draw stippled line.
|
||||
if x&1 {
|
||||
y++
|
||||
height--
|
||||
}
|
||||
position2(x,y,true)
|
||||
if active_mode==1
|
||||
set_both_strides(12) ; 80 increment = 2 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(13) ; 160 increment = 2 line in 640 px monochrome
|
||||
repeat height/2 {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
ora cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cx16.r15 = ~cx16.r15
|
||||
position2(x,y,true)
|
||||
cx16.r15 = ~cx16.r15 ; erase pixels
|
||||
if active_mode==1
|
||||
set_both_strides(11) ; 40 increment = 1 line in 320 px monochrome
|
||||
else
|
||||
set_both_strides(12) ; 80 increment = 1 line in 640 px monochrome
|
||||
repeat height {
|
||||
%asm {{
|
||||
lda cx16.VERA_DATA0
|
||||
and cx16.r15
|
||||
sta cx16.VERA_DATA0
|
||||
lda cx16.VERA_ADDR_L
|
||||
clc
|
||||
adc cx16.r14 ; advance vera data ptr to go to the next line
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda cx16.VERA_ADDR_M
|
||||
adc #0
|
||||
sta cx16.VERA_ADDR_M
|
||||
; the bitmap size is small enough to not have to deal with the _H part:
|
||||
; lda cx16.VERA_ADDR_H
|
||||
; adc #0
|
||||
; sta cx16.VERA_ADDR_H
|
||||
and cx16.r15L
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -385,6 +350,7 @@ _done
|
||||
4 -> {
|
||||
; lores 256c
|
||||
; set vera auto-increment to 320 pixel increment (=next line)
|
||||
position(x,y)
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
|
||||
%asm {{
|
||||
ldy height
|
||||
@@ -398,36 +364,33 @@ _done
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c
|
||||
; note for this mode we can't use vera's auto increment mode because we have to 'or' the pixel data in place.
|
||||
; TODO use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment IS possible
|
||||
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
|
||||
; 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 this vertical line loop in pure assembly
|
||||
; use TWO vera adress pointers simultaneously one for reading, one for writing, so auto-increment is possible
|
||||
if height==0
|
||||
return
|
||||
position2(x,y,true)
|
||||
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
|
||||
color &= 3
|
||||
color <<= gfx2.plot.shift4c[lsb(x) & 3]
|
||||
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
|
||||
repeat height {
|
||||
ubyte value = cx16.vpeek(lsb(cx16.r1), cx16.r0) & mask | color
|
||||
cx16.vpoke(lsb(cx16.r1), cx16.r0, value)
|
||||
%asm {{
|
||||
; 24 bits add 160 (640/4)
|
||||
clc
|
||||
lda cx16.r0
|
||||
adc #640/4
|
||||
sta cx16.r0
|
||||
lda cx16.r0+1
|
||||
adc #0
|
||||
sta cx16.r0+1
|
||||
bcc +
|
||||
inc cx16.r1
|
||||
+
|
||||
lda cx16.VERA_DATA0
|
||||
and mask
|
||||
ora color
|
||||
sta cx16.VERA_DATA1
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub set_both_strides(ubyte stride) {
|
||||
stride <<= 4
|
||||
cx16.VERA_CTRL = 0
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | stride
|
||||
cx16.VERA_CTRL = 1
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | stride
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
|
||||
@@ -587,8 +550,6 @@ _done
|
||||
ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||
ubyte[4] mask4c = [%00111111, %11001111, %11110011, %11111100]
|
||||
ubyte[4] shift4c = [6,4,2,0]
|
||||
uword addr
|
||||
ubyte value
|
||||
|
||||
when active_mode {
|
||||
1 -> {
|
||||
@@ -600,23 +561,43 @@ _done
|
||||
and #1
|
||||
}}
|
||||
if_nz {
|
||||
addr = x/8 + y*(320/8)
|
||||
value = bits[lsb(x)&7]
|
||||
if color
|
||||
cx16.vpoke_or(0, addr, value)
|
||||
else {
|
||||
value = ~value
|
||||
cx16.vpoke_and(0, addr, value)
|
||||
}
|
||||
cx16.r0L = lsb(x) & 7 ; xbits
|
||||
x /= 8
|
||||
x += y*(320/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
sta cx16.VERA_ADDR_L
|
||||
ldy cx16.r0L ; xbits
|
||||
lda bits,y
|
||||
ldy color
|
||||
beq +
|
||||
tsb cx16.VERA_DATA0
|
||||
bra ++
|
||||
+ trb cx16.VERA_DATA0
|
||||
+
|
||||
}}
|
||||
}
|
||||
}
|
||||
; TODO mode 2,3
|
||||
4 -> {
|
||||
; lores 256c
|
||||
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
|
||||
cx16.vpoke(lsb(cx16.r1), cx16.r0, color)
|
||||
; activate vera auto-increment mode so next_pixel() can be used after this
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | %00010000
|
||||
color = cx16.VERA_DATA0
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1
|
||||
ora #%00010000 ; enable auto-increment so next_pixel() can be used after this
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.r0
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda color
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
5 -> {
|
||||
; highres monochrome
|
||||
@@ -627,26 +608,48 @@ _done
|
||||
and #1
|
||||
}}
|
||||
if_nz {
|
||||
addr = x/8 + y*(640/8)
|
||||
value = bits[lsb(x)&7]
|
||||
if color
|
||||
cx16.vpoke_or(0, addr, value)
|
||||
else {
|
||||
value = ~value
|
||||
cx16.vpoke_and(0, addr, value)
|
||||
}
|
||||
cx16.r0L = lsb(x) & 7 ; xbits
|
||||
x /= 8
|
||||
x += y*(640/8)
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
stz cx16.VERA_ADDR_H
|
||||
lda x+1
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda x
|
||||
sta cx16.VERA_ADDR_L
|
||||
ldy cx16.r0L ; xbits
|
||||
lda bits,y
|
||||
ldy color
|
||||
beq +
|
||||
tsb cx16.VERA_DATA0
|
||||
bra ++
|
||||
+ trb cx16.VERA_DATA0
|
||||
+
|
||||
}}
|
||||
}
|
||||
}
|
||||
6 -> {
|
||||
; highres 4c
|
||||
; 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)
|
||||
cx16.r2L = lsb(x) & 3 ; xbits
|
||||
color &= 3
|
||||
color <<= shift4c[lsb(x) & 3]
|
||||
; TODO optimize the vera memory manipulation in pure assembly
|
||||
cx16.VERA_ADDR_H &= %00000111 ; no auto advance
|
||||
value = cx16.vpeek(lsb(cx16.r1), cx16.r0) & mask4c[lsb(x) & 3] | color
|
||||
cx16.vpoke(lsb(cx16.r1), cx16.r0, value)
|
||||
color <<= shift4c[cx16.r2L]
|
||||
%asm {{
|
||||
stz cx16.VERA_CTRL
|
||||
lda cx16.r1L
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda cx16.r0H
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda cx16.r0L
|
||||
sta cx16.VERA_ADDR_L
|
||||
ldy cx16.r2L ; xbits
|
||||
lda mask4c,y
|
||||
and cx16.VERA_DATA0
|
||||
ora color
|
||||
sta cx16.VERA_DATA0
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -680,6 +683,20 @@ _done
|
||||
}
|
||||
}
|
||||
|
||||
sub position2(uword @zp x, uword y, ubyte also_port_1) {
|
||||
position(x, y)
|
||||
if also_port_1 {
|
||||
when active_mode {
|
||||
1, 5 -> cx16.vaddr(0, cx16.r0, 1, 1)
|
||||
; TODO modes 2, 3
|
||||
4, 6 -> {
|
||||
ubyte bank = lsb(cx16.r1)
|
||||
cx16.vaddr(bank, cx16.r0, 1, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline asmsub next_pixel(ubyte color @A) {
|
||||
; -- sets the next pixel byte to the graphics chip.
|
||||
; for 8 bpp screens this will plot 1 pixel.
|
||||
@@ -759,13 +776,13 @@ _done
|
||||
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
|
||||
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
|
||||
; You must also have called text_charset() first to select and prepare the character set to use.
|
||||
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to mulitples of 8 ! TODO allow per-pixel horizontal positioning
|
||||
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to multiples of 8 ! TODO allow per-pixel horizontal positioning
|
||||
uword chardataptr
|
||||
when active_mode {
|
||||
1, 5 -> {
|
||||
; monochrome mode, either resolution
|
||||
cx16.r2 = 40
|
||||
if active_mode>=5
|
||||
if active_mode==5
|
||||
cx16.r2 = 80
|
||||
while @(sctextptr) {
|
||||
chardataptr = charset_addr + (@(sctextptr) as uword)*8
|
||||
|
||||
@@ -70,6 +70,14 @@ palette {
|
||||
}
|
||||
}
|
||||
|
||||
inline sub set_all_black() {
|
||||
set_monochrome($000, $000)
|
||||
}
|
||||
|
||||
inline sub set_all_white() {
|
||||
set_monochrome($fff, $fff)
|
||||
}
|
||||
|
||||
sub set_grayscale() {
|
||||
vera_palette_ptr = $fa00
|
||||
repeat 16 {
|
||||
|
||||
@@ -116,6 +116,41 @@ cx16 {
|
||||
&uword r14 = $001e
|
||||
&uword r15 = $0020
|
||||
|
||||
&ubyte r0L = $0002
|
||||
&ubyte r1L = $0004
|
||||
&ubyte r2L = $0006
|
||||
&ubyte r3L = $0008
|
||||
&ubyte r4L = $000a
|
||||
&ubyte r5L = $000c
|
||||
&ubyte r6L = $000e
|
||||
&ubyte r7L = $0010
|
||||
&ubyte r8L = $0012
|
||||
&ubyte r9L = $0014
|
||||
&ubyte r10L = $0016
|
||||
&ubyte r11L = $0018
|
||||
&ubyte r12L = $001a
|
||||
&ubyte r13L = $001c
|
||||
&ubyte r14L = $001e
|
||||
&ubyte r15L = $0020
|
||||
|
||||
&ubyte r0H = $0003
|
||||
&ubyte r1H = $0005
|
||||
&ubyte r2H = $0007
|
||||
&ubyte r3H = $0009
|
||||
&ubyte r4H = $000b
|
||||
&ubyte r5H = $000d
|
||||
&ubyte r6H = $000f
|
||||
&ubyte r7H = $0011
|
||||
&ubyte r8H = $0013
|
||||
&ubyte r9H = $0015
|
||||
&ubyte r10H = $0017
|
||||
&ubyte r11H = $0019
|
||||
&ubyte r12H = $001b
|
||||
&ubyte r13H = $001d
|
||||
&ubyte r14H = $001f
|
||||
&ubyte r15H = $0021
|
||||
|
||||
|
||||
; VERA registers
|
||||
|
||||
const uword VERA_BASE = $9F20
|
||||
@@ -724,28 +759,28 @@ sys {
|
||||
}}
|
||||
}
|
||||
|
||||
sub wait(uword jiffies) {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds)
|
||||
repeat jiffies {
|
||||
ubyte jiff = lsb(c64.RDTIM16())
|
||||
while jiff==lsb(c64.RDTIM16()) {
|
||||
; wait until 1 jiffy has passed
|
||||
}
|
||||
}
|
||||
asmsub wait(uword jiffies @AY) {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
|
||||
; note: regular system vsync irq handler must be running, and no nother irqs
|
||||
%asm {{
|
||||
- wai ; wait for irq (assume it was vsync)
|
||||
cmp #0
|
||||
bne +
|
||||
dey
|
||||
+ dec a
|
||||
bne -
|
||||
cpy #0
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub waitvsync() clobbers(A, X, Y) {
|
||||
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
|
||||
; note: system vsync irq handler has to be active for this routine to work.
|
||||
; note 2: a more accurate way to wait for vsync is to set up a vsync irq handler instead.
|
||||
inline asmsub waitvsync() {
|
||||
; --- suspend execution until the next vsync has occurred, without depending on custom irq handling.
|
||||
; note: system vsync irq handler has to be active for this routine to work (and no other irqs).
|
||||
; note: a more accurate way to wait for vsync is to set up a vsync irq handler instead.
|
||||
%asm {{
|
||||
jsr c64.RDTIM
|
||||
sta _mod + 1
|
||||
inc _mod + 1
|
||||
_loop jsr c64.RDTIM
|
||||
_mod cmp #255 ; modified
|
||||
bne _loop
|
||||
rts
|
||||
wai
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -336,6 +336,45 @@ _end rts
|
||||
}
|
||||
|
||||
|
||||
; ----- iterative file saver functions (uses io channel 14) -----
|
||||
|
||||
sub f_open_w(ubyte drivenumber, uword filenameptr) -> ubyte {
|
||||
; -- open a file for iterative writing with f_write
|
||||
f_close_w()
|
||||
|
||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||
c64.SETLFS(14, drivenumber, 1)
|
||||
void c64.OPEN() ; open 14,8,1,"filename"
|
||||
if_cc {
|
||||
void c64.CHKOUT(14) ; use #14 as input channel
|
||||
return not c64.READST()
|
||||
}
|
||||
f_close_w()
|
||||
return false
|
||||
}
|
||||
|
||||
sub f_write(uword bufferpointer, uword num_bytes) -> ubyte {
|
||||
; -- write the given umber of bytes to the currently open file
|
||||
if num_bytes!=0 {
|
||||
void c64.CHKOUT(14) ; use #14 as input channel again
|
||||
repeat num_bytes {
|
||||
c64.CHROUT(@(bufferpointer))
|
||||
bufferpointer++
|
||||
}
|
||||
return not c64.READST()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sub f_close_w() {
|
||||
; -- end an iterative file writing session (close channels).
|
||||
c64.CLRCHN()
|
||||
c64.CLOSE(14)
|
||||
}
|
||||
|
||||
|
||||
; ---- other functions ----
|
||||
|
||||
sub status(ubyte drivenumber) -> uword {
|
||||
; -- retrieve the disk drive's current status message
|
||||
uword messageptr = &filename
|
||||
@@ -353,13 +392,16 @@ _end rts
|
||||
messageptr++
|
||||
}
|
||||
|
||||
io_error:
|
||||
@(messageptr) = 0
|
||||
done:
|
||||
c64.CLRCHN() ; restore default i/o devices
|
||||
c64.CLOSE(15)
|
||||
return filename
|
||||
}
|
||||
|
||||
io_error:
|
||||
filename = "?disk error"
|
||||
goto done
|
||||
}
|
||||
|
||||
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
|
||||
c64.SETNAM(string.length(filenameptr), filenameptr)
|
||||
|
||||
@@ -244,25 +244,6 @@ randseed .proc
|
||||
.pend
|
||||
|
||||
|
||||
fast_randbyte .proc
|
||||
; -- fast but bad 8-bit pseudo random number generator into A
|
||||
lda _seed
|
||||
beq _eor
|
||||
asl a
|
||||
beq _done ; if the input was $80, skip the EOR
|
||||
bcc _done
|
||||
_eor eor #$1d ; xor with magic value see below for possible values
|
||||
_done sta _seed
|
||||
rts
|
||||
|
||||
_seed .byte $3a
|
||||
|
||||
; possible 'magic' eor bytes are:
|
||||
; $1d, $2b, $2d, $4d, $5f, $63, $65, $69
|
||||
; $71, $87, $8d, $a9, $c3, $cf, $e7, $f5
|
||||
|
||||
.pend
|
||||
|
||||
randbyte .proc
|
||||
; -- 8 bit pseudo random number generator into A (by just reusing randword)
|
||||
jmp randword
|
||||
@@ -271,45 +252,37 @@ randbyte .proc
|
||||
randword .proc
|
||||
; -- 16 bit pseudo random number generator into AY
|
||||
|
||||
magic_eor = $3f1d
|
||||
; possible magic eor words are:
|
||||
; $3f1d, $3f81, $3fa5, $3fc5, $4075, $409d, $40cd, $4109
|
||||
; $413f, $414b, $4153, $4159, $4193, $4199, $41af, $41bb
|
||||
; rand64k ;Factors of 65535: 3 5 17 257
|
||||
lda sr1+1
|
||||
asl a
|
||||
asl a
|
||||
eor sr1+1
|
||||
asl a
|
||||
eor sr1+1
|
||||
asl a
|
||||
asl a
|
||||
eor sr1+1
|
||||
asl a
|
||||
rol sr1 ;shift this left, "random" bit comes from low
|
||||
rol sr1+1
|
||||
; rand32k ;Factors of 32767: 7 31 151 are independent and can be combined
|
||||
lda sr2+1
|
||||
asl a
|
||||
eor sr2+1
|
||||
asl a
|
||||
asl a
|
||||
ror sr2 ;shift this right, random bit comes from high - nicer when eor with sr1
|
||||
rol sr2+1
|
||||
lda sr1+1 ;can be left out
|
||||
eor sr2+1 ;if you dont use
|
||||
tay ;y as suggested
|
||||
lda sr1 ;mix up lowbytes of SR1
|
||||
eor sr2 ;and SR2 to combine both
|
||||
rts
|
||||
|
||||
lda _seed
|
||||
beq _lowZero ; $0000 and $8000 are special values to test for
|
||||
sr1 .word $a55a
|
||||
sr2 .word $7653
|
||||
|
||||
; Do a normal shift
|
||||
asl _seed
|
||||
lda _seed+1
|
||||
rol a
|
||||
bcc _noEor
|
||||
|
||||
_doEor ; high byte is in A
|
||||
eor #>magic_eor
|
||||
sta _seed+1
|
||||
lda _seed
|
||||
eor #<magic_eor
|
||||
sta _seed
|
||||
ldy _seed+1
|
||||
rts
|
||||
|
||||
_lowZero lda _seed+1
|
||||
beq _doEor ; High byte is also zero, so apply the EOR
|
||||
; For speed, you could store 'magic' into 'seed' directly
|
||||
; instead of running the EORs
|
||||
|
||||
; wasn't zero, check for $8000
|
||||
asl a
|
||||
beq _noEor ; if $00 is left after the shift, then it was $80
|
||||
bcs _doEor ; else, do the EOR based on the carry bit as usual
|
||||
|
||||
_noEor sta _seed+1
|
||||
tay
|
||||
lda _seed
|
||||
rts
|
||||
|
||||
_seed .word $2c9e
|
||||
.pend
|
||||
|
||||
|
||||
@@ -801,6 +774,13 @@ stack_mul_word_320 .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
stack_mul_word_640 .proc
|
||||
; stackW = (stackLo * 2 * 320) (stackHi doesn't matter)
|
||||
asl P8ESTACK_LO+1,x
|
||||
jmp stack_mul_word_320
|
||||
.pend
|
||||
|
||||
|
||||
; ----------- optimized multiplications (in-place A (byte) and ?? (word)) : ---------
|
||||
mul_byte_3 .proc
|
||||
; A = A + A*2
|
||||
@@ -1291,6 +1271,13 @@ mul_word_320 .proc
|
||||
rts
|
||||
.pend
|
||||
|
||||
mul_word_640 .proc
|
||||
; AY = (A * 2 * 320) (msb in Y doesn't matter)
|
||||
asl a
|
||||
jmp mul_word_320
|
||||
.pend
|
||||
|
||||
|
||||
; ----------- end optimized multiplications -----------
|
||||
|
||||
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
math {
|
||||
%asminclude "library:math.asm", ""
|
||||
%asminclude "library:math.asm"
|
||||
}
|
||||
@@ -387,14 +387,6 @@ 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
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
prog8_lib {
|
||||
%asminclude "library:prog8_lib.asm", ""
|
||||
%asminclude "library:prog8_funcs.asm", ""
|
||||
%asminclude "library:prog8_lib.asm"
|
||||
%asminclude "library:prog8_funcs.asm"
|
||||
|
||||
uword @zp retval_interm_uw ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
||||
word @zp retval_interm_w ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
||||
|
||||
@@ -178,7 +178,7 @@ _found sty P8ZP_SCRATCH_B1
|
||||
|
||||
asmsub compare(uword string1 @R0, uword string2 @AY) clobbers(Y) -> byte @A {
|
||||
; Compares two strings for sorting.
|
||||
; Returns -1 (255), 0 or 1 depeding on wether string1 sorts before, equal or after string2.
|
||||
; Returns -1 (255), 0 or 1 depending on wether string1 sorts before, equal or after string2.
|
||||
; Note that you can also directly compare strings and string values with eachother using
|
||||
; comparison operators ==, < etcetera (it will use strcmp for you under water automatically).
|
||||
%asm {{
|
||||
@@ -190,8 +190,8 @@ _found sty P8ZP_SCRATCH_B1
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub lower(uword st @AY) {
|
||||
; Lowercases the petscii string in-place.
|
||||
asmsub lower(uword st @AY) -> ubyte @Y {
|
||||
; Lowercases the petscii string in-place. Returns length of the string.
|
||||
; (for efficiency, non-letter characters > 128 will also not be left intact,
|
||||
; but regular text doesn't usually contain those characters anyway.)
|
||||
%asm {{
|
||||
@@ -213,8 +213,8 @@ _done rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub upper(uword st @AY) {
|
||||
; Uppercases the petscii string in-place.
|
||||
asmsub upper(uword st @AY) -> ubyte @Y {
|
||||
; Uppercases the petscii string in-place. Returns length of the string.
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
|
||||
@@ -1 +1 @@
|
||||
6.5-BETA
|
||||
7.0-BETA2
|
||||
@@ -1,15 +1,13 @@
|
||||
package prog8
|
||||
|
||||
import kotlinx.cli.ArgParser
|
||||
import kotlinx.cli.ArgType
|
||||
import kotlinx.cli.default
|
||||
import kotlinx.cli.multiple
|
||||
import kotlinx.cli.*
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.compiler.CompilationResult
|
||||
import prog8.compiler.compileProgram
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.parser.ParsingFailedError
|
||||
import java.io.File
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardWatchEventKinds
|
||||
@@ -42,6 +40,7 @@ private fun compileMain(args: Array<String>) {
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
||||
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name)
|
||||
val libDirs by cli.option(ArgType.String, fullName="libdirs", description = "list of extra paths to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||
|
||||
try {
|
||||
@@ -57,6 +56,16 @@ private fun compileMain(args: Array<String>) {
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
val faultyOption = moduleFiles.firstOrNull { it.startsWith('-') }
|
||||
if(faultyOption!=null) {
|
||||
System.err.println("Unknown command line option given: $faultyOption")
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
val libdirs = libDirs.toMutableList()
|
||||
if(libdirs.firstOrNull()!=".")
|
||||
libdirs.add(0, ".")
|
||||
|
||||
if(watchMode==true) {
|
||||
val watchservice = FileSystems.getDefault().newWatchService()
|
||||
val allImportedFiles = mutableSetOf<Path>()
|
||||
@@ -66,7 +75,7 @@ private fun compileMain(args: Array<String>) {
|
||||
val results = mutableListOf<CompilationResult>()
|
||||
for(filepathRaw in moduleFiles) {
|
||||
val filepath = pathFrom(filepathRaw).normalize()
|
||||
val compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, outputPath)
|
||||
val compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
|
||||
results.add(compilationResult)
|
||||
}
|
||||
|
||||
@@ -103,7 +112,7 @@ private fun compileMain(args: Array<String>) {
|
||||
val filepath = pathFrom(filepathRaw).normalize()
|
||||
val compilationResult: CompilationResult
|
||||
try {
|
||||
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, outputPath)
|
||||
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
|
||||
if(!compilationResult.success)
|
||||
exitProcess(1)
|
||||
} catch (x: ParsingFailedError) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
@@ -78,12 +77,12 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
||||
}
|
||||
|
||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||
val decls = scope.statements.filterIsInstance<VarDecl>()
|
||||
val decls = scope.statements.filterIsInstance<VarDecl>().filter { it.type == VarDeclType.VAR }
|
||||
subroutineVariables.addAll(decls.map { it.name to it })
|
||||
|
||||
val sub = scope.definingSubroutine()
|
||||
if (sub != null) {
|
||||
// move vardecls of the scope into the upper scope. Make sure the position remains the same!
|
||||
// move any remaining vardecls of the scope into the upper scope. Make sure the position remains the same!
|
||||
val replacements = mutableListOf<IAstModification>()
|
||||
val movements = mutableListOf<IAstModification.InsertFirst>()
|
||||
|
||||
@@ -147,7 +146,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
||||
// see if we can remove superfluous typecasts (outside of expressions)
|
||||
// such as casting byte<->ubyte, word<->uword
|
||||
// Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of.
|
||||
val sourceDt = typecast.expression.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val sourceDt = typecast.expression.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes
|
||||
|| typecast.type in WordDatatypes && sourceDt in WordDatatypes) {
|
||||
if(typecast.parent !is Expression) {
|
||||
@@ -194,7 +193,9 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
||||
return listOf(IAstModification.ReplaceNode(ifStatement.condition, booleanExpr, ifStatement))
|
||||
}
|
||||
|
||||
if((binExpr.left as? NumericLiteralValue)?.number==0)
|
||||
if((binExpr.operator=="==" || binExpr.operator=="!=") &&
|
||||
(binExpr.left as? NumericLiteralValue)?.number==0 &&
|
||||
(binExpr.right as? NumericLiteralValue)?.number!=0)
|
||||
throw CompilerException("if 0==X should have been swapped to if X==0")
|
||||
|
||||
// split the conditional expression into separate variables if the operand(s) is not simple.
|
||||
@@ -223,7 +224,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
||||
}
|
||||
|
||||
// private fun addIfOperandVar(sub: Subroutine, side: String, operand: Expression): Triple<VarDecl, Boolean, Assignment> {
|
||||
// val dt = operand.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
// val dt = operand.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
// val varname = "prog8_ifvar_${side}_${dt.name.toLowerCase()}"
|
||||
// val tgt = AssignTarget(IdentifierReference(listOf(varname), operand.position), null, null, operand.position)
|
||||
// val assign = Assignment(tgt, operand, operand.position)
|
||||
@@ -265,8 +266,8 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
||||
// 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)
|
||||
val dt1 = arg1.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
val dt2 = arg2.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
if(dt1 in ByteDatatypes) {
|
||||
if(dt2 in ByteDatatypes)
|
||||
return noModifications
|
||||
@@ -339,9 +340,11 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
||||
private fun getAutoIndexerVarFor(expr: ArrayIndexedExpression): MutableList<IAstModification> {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val statement = expr.containingStatement()
|
||||
val dt = expr.indexer.indexExpr.inferType(program)
|
||||
val register = if(dt.istype(DataType.UBYTE) || dt.istype(DataType.BYTE)) "r9L" else "r9"
|
||||
// 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 target = AssignTarget(IdentifierReference(listOf("cx16", register), 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))
|
||||
|
||||
@@ -70,6 +70,7 @@ fun compileProgram(filepath: Path,
|
||||
writeAssembly: Boolean,
|
||||
slowCodegenWarnings: Boolean,
|
||||
compilationTarget: String,
|
||||
libdirs: List<String>,
|
||||
outputDir: Path): CompilationResult {
|
||||
var programName = ""
|
||||
lateinit var programAst: Program
|
||||
@@ -89,7 +90,7 @@ fun compileProgram(filepath: Path,
|
||||
try {
|
||||
val totalTime = measureTimeMillis {
|
||||
// import main module and everything it needs
|
||||
val (ast, compilationOptions, imported) = parseImports(filepath, errors, compTarget)
|
||||
val (ast, compilationOptions, imported) = parseImports(filepath, errors, compTarget, libdirs)
|
||||
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
|
||||
compilationOptions.optimize = optimize
|
||||
programAst = ast
|
||||
@@ -165,14 +166,18 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
||||
builtinFunctionReturnType(name, args, program)
|
||||
}
|
||||
|
||||
private fun parseImports(filepath: Path, errors: IErrorReporter, compTarget: ICompilationTarget): Triple<Program, CompilationOptions, List<Path>> {
|
||||
private fun parseImports(filepath: Path,
|
||||
errors: IErrorReporter,
|
||||
compTarget: ICompilationTarget,
|
||||
libdirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
|
||||
val compilationTargetName = compTarget.name
|
||||
println("Compiler target: $compilationTargetName. Parsing...")
|
||||
val importer = ModuleImporter()
|
||||
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
|
||||
val programAst = Program(moduleName(filepath.fileName), mutableListOf(), bf, compTarget)
|
||||
bf.program = programAst
|
||||
importer.importModule(programAst, filepath, compTarget, compilationTargetName)
|
||||
|
||||
val importer = ModuleImporter(programAst, compTarget, compilationTargetName, libdirs)
|
||||
importer.importModule(filepath)
|
||||
errors.report()
|
||||
|
||||
val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source }
|
||||
@@ -182,11 +187,11 @@ private fun parseImports(filepath: Path, errors: IErrorReporter, compTarget: ICo
|
||||
|
||||
// depending on the machine and compiler options we may have to include some libraries
|
||||
for(lib in compTarget.machine.importLibs(compilerOptions, compilationTargetName))
|
||||
importer.importLibraryModule(programAst, lib, compTarget, compilationTargetName)
|
||||
importer.importLibraryModule(lib)
|
||||
|
||||
// always import prog8_lib and math
|
||||
importer.importLibraryModule(programAst, "math", compTarget, compilationTargetName)
|
||||
importer.importLibraryModule(programAst, "prog8_lib", compTarget, compilationTargetName)
|
||||
importer.importLibraryModule("math")
|
||||
importer.importLibraryModule("prog8_lib")
|
||||
errors.report()
|
||||
return Triple(programAst, compilerOptions, importedFiles)
|
||||
}
|
||||
@@ -194,58 +199,59 @@ private fun parseImports(filepath: Path, errors: IErrorReporter, compTarget: ICo
|
||||
private fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
|
||||
val mainModule = program.mainModule
|
||||
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
as? Directive)?.args?.single()?.name?.uppercase()
|
||||
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
as? Directive)?.args?.single()?.name?.uppercase()
|
||||
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
||||
as? Directive)?.args?.single()?.name?.uppercase()
|
||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }
|
||||
.flatMap { (it as Directive).args }.toSet()
|
||||
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
||||
val noSysInit = allOptions.any { it.name == "no_sysinit" }
|
||||
var zpType: ZeropageType =
|
||||
if (zpoption == null)
|
||||
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||
else
|
||||
try {
|
||||
ZeropageType.valueOf(zpoption)
|
||||
} catch (x: IllegalArgumentException) {
|
||||
ZeropageType.KERNALSAFE
|
||||
// error will be printed by the astchecker
|
||||
}
|
||||
if (zpoption == null)
|
||||
if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||
else
|
||||
try {
|
||||
ZeropageType.valueOf(zpoption)
|
||||
} catch (x: IllegalArgumentException) {
|
||||
ZeropageType.KERNALSAFE
|
||||
// error will be printed by the astchecker
|
||||
}
|
||||
|
||||
if (zpType==ZeropageType.FLOATSAFE && compTarget.name == Cx16Target.name) {
|
||||
if (zpType == ZeropageType.FLOATSAFE && compTarget.name == Cx16Target.name) {
|
||||
System.err.println("Warning: zp option floatsafe changed to basicsafe for cx16 target")
|
||||
zpType = ZeropageType.BASICSAFE
|
||||
}
|
||||
|
||||
val zpReserved = mainModule.statements
|
||||
.asSequence()
|
||||
.filter { it is Directive && it.directive == "%zpreserved" }
|
||||
.map { (it as Directive).args }
|
||||
.map { it[0].int!!..it[1].int!! }
|
||||
.toList()
|
||||
.asSequence()
|
||||
.filter { it is Directive && it.directive == "%zpreserved" }
|
||||
.map { (it as Directive).args }
|
||||
.map { it[0].int!!..it[1].int!! }
|
||||
.toList()
|
||||
|
||||
if(outputType!=null && !OutputType.values().any {it.name==outputType}) {
|
||||
if (outputType != null && !OutputType.values().any { it.name == outputType }) {
|
||||
System.err.println("invalid output type $outputType")
|
||||
exitProcess(1)
|
||||
}
|
||||
if(launcherType!=null && !LauncherType.values().any {it.name==launcherType}) {
|
||||
if (launcherType != null && !LauncherType.values().any { it.name == launcherType }) {
|
||||
System.err.println("invalid launcher type $launcherType")
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
return CompilationOptions(
|
||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||
zpType, zpReserved, floatsEnabled, noSysInit,
|
||||
compTarget
|
||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||
zpType, zpReserved, floatsEnabled, noSysInit,
|
||||
compTarget
|
||||
)
|
||||
}
|
||||
|
||||
private fun processAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
// perform initial syntax checks and processings
|
||||
println("Processing for target ${compilerOptions.compTarget.name}...")
|
||||
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
|
||||
programAst.checkIdentifiers(errors, compilerOptions)
|
||||
errors.report()
|
||||
programAst.constantFold(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
@@ -257,7 +263,7 @@ private fun processAst(programAst: Program, errors: IErrorReporter, compilerOpti
|
||||
errors.report()
|
||||
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
|
||||
programAst.checkIdentifiers(errors, compilerOptions)
|
||||
errors.report()
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,29 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
||||
|
||||
val allowedDatatypes = NumericDatatypes
|
||||
|
||||
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
||||
fun availableBytes() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
||||
fun hasByteAvailable() = if(options.zeropage==ZeropageType.DONTUSE) false else free.isNotEmpty()
|
||||
fun availableWords(): Int {
|
||||
if(options.zeropage==ZeropageType.DONTUSE)
|
||||
return 0
|
||||
|
||||
val words = free.windowed(2).filter { it[0] == it[1]-1 }
|
||||
var nonOverlappingWordsCount = 0
|
||||
var prevMsbLoc = -1
|
||||
for(w in words) {
|
||||
if(w[0]!=prevMsbLoc) {
|
||||
nonOverlappingWordsCount++
|
||||
prevMsbLoc = w[1]
|
||||
}
|
||||
}
|
||||
return nonOverlappingWordsCount
|
||||
}
|
||||
fun hasWordAvailable(): Boolean {
|
||||
if(options.zeropage==ZeropageType.DONTUSE)
|
||||
return false
|
||||
|
||||
return free.windowed(2).any { it[0] == it[1] - 1 }
|
||||
}
|
||||
|
||||
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: IErrorReporter): Int {
|
||||
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"scopedname can't be allocated twice"}
|
||||
|
||||
@@ -15,7 +15,9 @@ import prog8.compiler.functions.builtinFunctionReturnType
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
import java.io.CharConversionException
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
internal class AstChecker(private val program: Program,
|
||||
private val compilerOptions: CompilationOptions,
|
||||
@@ -78,7 +80,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(!valueDt.isKnown) {
|
||||
errors.err("return value type mismatch or unknown symbol", returnStmt.value!!.position)
|
||||
} else {
|
||||
if (expectedReturnValues[0] != valueDt.typeOrElse(DataType.STRUCT))
|
||||
if (expectedReturnValues[0] != valueDt.typeOrElse(DataType.UNDEFINED))
|
||||
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}", returnStmt.value!!.position)
|
||||
}
|
||||
}
|
||||
@@ -86,12 +88,24 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(ifStatement: IfStatement) {
|
||||
if(ifStatement.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
||||
if(!ifStatement.condition.inferType(program).isInteger())
|
||||
errors.err("condition value should be an integer type", ifStatement.condition.position)
|
||||
super.visit(ifStatement)
|
||||
}
|
||||
|
||||
override fun visit(forLoop: ForLoop) {
|
||||
|
||||
fun checkUnsignedLoopDownto0(range: RangeExpr?) {
|
||||
if(range==null)
|
||||
return
|
||||
val step = range.step.constValue(program)?.number?.toDouble() ?: 1.0
|
||||
if(step < -1.0) {
|
||||
val limit = range.to.constValue(program)?.number?.toDouble()
|
||||
if(limit==0.0 && range.from.constValue(program)==null)
|
||||
errors.err("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping", forLoop.position)
|
||||
}
|
||||
}
|
||||
|
||||
val iterableDt = forLoop.iterable.inferType(program).typeOrElse(DataType.BYTE)
|
||||
if(iterableDt !in IterableDatatypes && forLoop.iterable !is RangeExpr) {
|
||||
errors.err("can only loop over an iterable type", forLoop.position)
|
||||
@@ -104,11 +118,15 @@ internal class AstChecker(private val program: Program,
|
||||
DataType.UBYTE -> {
|
||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt != DataType.STR)
|
||||
errors.err("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position)
|
||||
|
||||
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpr)
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt != DataType.STR &&
|
||||
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
|
||||
errors.err("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position)
|
||||
|
||||
checkUnsignedLoopDownto0(forLoop.iterable as? RangeExpr)
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
|
||||
@@ -146,6 +164,7 @@ internal class AstChecker(private val program: Program,
|
||||
super.visit(forLoop)
|
||||
}
|
||||
|
||||
|
||||
override fun visit(jump: Jump) {
|
||||
val ident = jump.identifier
|
||||
if(ident!=null) {
|
||||
@@ -196,6 +215,28 @@ internal class AstChecker(private val program: Program,
|
||||
super.visit(label)
|
||||
}
|
||||
|
||||
private fun hasReturnOrJump(scope: INameScope): Boolean {
|
||||
class Searcher: IAstVisitor
|
||||
{
|
||||
var count=0
|
||||
|
||||
override fun visit(returnStmt: Return) {
|
||||
count++
|
||||
}
|
||||
override fun visit(jump: Jump) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
val s=Searcher()
|
||||
for(stmt in scope.statements) {
|
||||
stmt.accept(s)
|
||||
if(s.count>0)
|
||||
return true
|
||||
}
|
||||
return s.count > 0
|
||||
}
|
||||
|
||||
override fun visit(subroutine: Subroutine) {
|
||||
fun err(msg: String) = errors.err(msg, subroutine.position)
|
||||
|
||||
@@ -218,7 +259,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
// subroutine must contain at least one 'return' or 'goto'
|
||||
// (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(!hasReturnOrJump(subroutine)) {
|
||||
if (subroutine.amountOfRtsInAsm() == 0) {
|
||||
if (subroutine.returntypes.isNotEmpty()) {
|
||||
// for asm subroutines with an address, no statement check is possible.
|
||||
@@ -347,13 +388,13 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(untilLoop: UntilLoop) {
|
||||
if(untilLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
||||
if(!untilLoop.condition.inferType(program).isInteger())
|
||||
errors.err("condition value should be an integer type", untilLoop.condition.position)
|
||||
super.visit(untilLoop)
|
||||
}
|
||||
|
||||
override fun visit(whileLoop: WhileLoop) {
|
||||
if(whileLoop.condition.inferType(program).typeOrElse(DataType.STRUCT) !in IntegerDatatypes)
|
||||
if(!whileLoop.condition.inferType(program).isInteger())
|
||||
errors.err("condition value should be an integer type", whileLoop.condition.position)
|
||||
super.visit(whileLoop)
|
||||
}
|
||||
@@ -379,37 +420,13 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
val targetIdent = assignment.target.identifier
|
||||
if(targetIdent!=null) {
|
||||
val targetVar = targetIdent.targetVarDecl(program)
|
||||
if(targetVar?.struct != null) {
|
||||
val sourceStructLv = assignment.value as? ArrayLiteralValue
|
||||
if (sourceStructLv != null) {
|
||||
if (sourceStructLv.value.size != targetVar.struct?.numberOfElements)
|
||||
errors.err("number of elements doesn't match struct definition", sourceStructLv.position)
|
||||
} else {
|
||||
val sourceIdent = assignment.value as? IdentifierReference
|
||||
if (sourceIdent != null) {
|
||||
val sourceVar = sourceIdent.targetVarDecl(program)
|
||||
if (sourceVar?.struct != null) {
|
||||
if (sourceVar.struct !== targetVar.struct)
|
||||
errors.err("assignment of different struct types", assignment.position)
|
||||
} else if(sourceVar?.isArray==true) {
|
||||
if((sourceVar.value as ArrayLiteralValue).value.size != targetVar.struct?.numberOfElements)
|
||||
errors.err("number of elements doesn't match struct definition", sourceVar.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val targetDt = assignment.target.inferType(program)
|
||||
val valueDt = assignment.value.inferType(program)
|
||||
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
|
||||
if(targetDt.typeOrElse(DataType.STRUCT) in IterableDatatypes)
|
||||
if(targetDt.isIterable())
|
||||
errors.err("cannot assign value to string or array", assignment.value.position)
|
||||
else if(!(valueDt.istype(DataType.STR) && targetDt.istype(DataType.UWORD)))
|
||||
errors.err("value's type doesn't match target", assignment.value.position)
|
||||
errors.err("type of value doesn't match target", assignment.value.position)
|
||||
}
|
||||
|
||||
if(assignment.value is TypecastExpression) {
|
||||
@@ -479,12 +496,8 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
val variable=addressOf.identifier.targetVarDecl(program)
|
||||
if(variable!=null
|
||||
&& variable.datatype !in ArrayDatatypes
|
||||
&& variable.type!=VarDeclType.MEMORY
|
||||
&& variable.struct == null
|
||||
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||
if(variable!=null && variable.type==VarDeclType.CONST)
|
||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||
super.visit(addressOf)
|
||||
}
|
||||
|
||||
@@ -526,17 +539,6 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
when(decl.type) {
|
||||
VarDeclType.VAR, VarDeclType.CONST -> {
|
||||
if(decl.datatype==DataType.STRUCT) {
|
||||
if(decl.struct==null)
|
||||
throw FatalAstException("struct vardecl should be linked to its struct $decl")
|
||||
if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE || decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
|
||||
err("struct can not be in zeropage")
|
||||
}
|
||||
if(decl.struct!=null) {
|
||||
if(decl.zeropage==ZeropageWish.PREFER_ZEROPAGE || decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
|
||||
err("struct can not be in zeropage")
|
||||
}
|
||||
|
||||
when(decl.value) {
|
||||
null -> {
|
||||
// a vardecl without an initial value, don't bother with it
|
||||
@@ -546,30 +548,8 @@ internal class AstChecker(private val program: Program,
|
||||
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
|
||||
}
|
||||
is ArrayLiteralValue -> {
|
||||
if(decl.datatype==DataType.STRUCT) {
|
||||
val struct = decl.struct!!
|
||||
val structLv = decl.value as ArrayLiteralValue
|
||||
if(struct.numberOfElements != structLv.value.size) {
|
||||
errors.err("struct value has incorrect number of elements", structLv.position)
|
||||
return
|
||||
}
|
||||
for(value in structLv.value.zip(struct.statements)) {
|
||||
val memberdecl = value.second as VarDecl
|
||||
val constValue = value.first.constValue(program)
|
||||
if(constValue==null) {
|
||||
errors.err("struct literal value for field '${memberdecl.name}' should consist of compile-time constants", value.first.position)
|
||||
return
|
||||
}
|
||||
val memberDt = memberdecl.datatype
|
||||
if(!checkValueTypeAndRange(memberDt, constValue)) {
|
||||
errors.err("struct member value's type is not compatible with member field '${memberdecl.name}'", value.first.position)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
|
||||
checkValueTypeAndRangeArray(decl.datatype, decl.struct, arraySpec, decl.value as ArrayLiteralValue)
|
||||
}
|
||||
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
|
||||
checkValueTypeAndRangeArray(decl.datatype, arraySpec, decl.value as ArrayLiteralValue)
|
||||
}
|
||||
is NumericLiteralValue -> {
|
||||
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
|
||||
@@ -584,8 +564,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
VarDeclType.MEMORY -> {
|
||||
if(decl.arraysize!=null) {
|
||||
val arraySize = decl.arraysize!!.constIndex() ?: 1
|
||||
val arraysize = decl.arraysize
|
||||
if(arraysize!=null) {
|
||||
val arraySize = arraysize.constIndex() ?: 1
|
||||
when(decl.datatype) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB ->
|
||||
if(arraySize > 256)
|
||||
@@ -599,10 +580,9 @@ internal class AstChecker(private val program: Program,
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
if(decl.value is NumericLiteralValue) {
|
||||
val value = decl.value as NumericLiteralValue
|
||||
if (value.type !in IntegerDatatypes || value.number.toInt() < 0 || value.number.toInt() > 65535) {
|
||||
val numvalue = decl.value as? NumericLiteralValue
|
||||
if(numvalue!=null) {
|
||||
if (numvalue.type !in IntegerDatatypes || numvalue.number.toInt() < 0 || numvalue.number.toInt() > 65535) {
|
||||
err("memory address must be valid integer 0..\$ffff", decl.value?.position)
|
||||
}
|
||||
} else {
|
||||
@@ -613,23 +593,16 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
val declValue = decl.value
|
||||
if(declValue!=null && decl.type==VarDeclType.VAR) {
|
||||
if(decl.datatype==DataType.STRUCT) {
|
||||
val valueIdt = declValue.inferType(program)
|
||||
if(!valueIdt.isKnown)
|
||||
throw AstException("unknown dt")
|
||||
val valueDt = valueIdt.typeOrElse(DataType.STRUCT)
|
||||
if(valueDt !in ArrayDatatypes)
|
||||
err("initialisation of struct should be with array value", declValue.position)
|
||||
} else if (!declValue.inferType(program).istype(decl.datatype)) {
|
||||
if (!declValue.inferType(program).istype(decl.datatype)) {
|
||||
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
|
||||
}
|
||||
}
|
||||
|
||||
// array length limits and constant lenghts
|
||||
if(decl.isArray) {
|
||||
val length = decl.arraysize!!.constIndex()
|
||||
val length = decl.arraysize?.constIndex()
|
||||
if(length==null)
|
||||
err("array length must be a constant")
|
||||
err("array length must be known at compile-time")
|
||||
else {
|
||||
when (decl.datatype) {
|
||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
@@ -711,20 +684,20 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
"%breakpoint" -> {
|
||||
if(directive.parent !is INameScope || directive.parent is Module)
|
||||
err("this directive may only occur in a block")
|
||||
err("this directive can't be used here")
|
||||
if(directive.args.isNotEmpty())
|
||||
err("invalid breakpoint directive, expected no arguments")
|
||||
}
|
||||
"%asminclude" -> {
|
||||
if(directive.parent !is INameScope || directive.parent is Module)
|
||||
err("this directive may only occur in a block")
|
||||
if(directive.args.size!=2 || directive.args[0].str==null || directive.args[1].str==null)
|
||||
err("invalid asminclude directive, expected arguments: \"filename\", \"scopelabel\"")
|
||||
err("this directive can't be used here")
|
||||
if(directive.args.size!=1 || directive.args[0].str==null)
|
||||
err("invalid asminclude directive, expected argument: \"filename\"")
|
||||
checkFileExists(directive, directive.args[0].str!!)
|
||||
}
|
||||
"%asmbinary" -> {
|
||||
if(directive.parent !is INameScope || directive.parent is Module)
|
||||
err("this directive may only occur in a block")
|
||||
err("this directive can't be used here")
|
||||
val errormsg = "invalid asmbinary directive, expected arguments: \"filename\" [, offset [, length ] ]"
|
||||
if(directive.args.isEmpty()) err(errormsg)
|
||||
else if(directive.args.isNotEmpty() && directive.args[0].str==null) err(errormsg)
|
||||
@@ -764,11 +737,11 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(array: ArrayLiteralValue) {
|
||||
if(array.type.isKnown) {
|
||||
if (!compilerOptions.floats && array.type.typeOrElse(DataType.STRUCT) in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||
if (!compilerOptions.floats && array.type.typeOrElse(DataType.UNDEFINED) in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||
errors.err("floating point used, but that is not enabled via options", array.position)
|
||||
}
|
||||
val arrayspec = ArrayIndex.forArray(array)
|
||||
checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.STRUCT), null, arrayspec, array)
|
||||
checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.UNDEFINED), arrayspec, array)
|
||||
}
|
||||
|
||||
fun isPassByReferenceElement(e: Expression): Boolean {
|
||||
@@ -792,6 +765,13 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(string: StringLiteralValue) {
|
||||
checkValueTypeAndRangeString(DataType.STR, string)
|
||||
|
||||
try {
|
||||
compTarget.encodeString(string.value, string.altEncoding)
|
||||
} catch (cx: CharConversionException) {
|
||||
errors.err(cx.message ?: "can't encode string", string.position)
|
||||
}
|
||||
|
||||
super.visit(string)
|
||||
}
|
||||
|
||||
@@ -800,7 +780,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(!idt.isKnown)
|
||||
return // any error should be reported elsewhere
|
||||
|
||||
val dt = idt.typeOrElse(DataType.STRUCT)
|
||||
val dt = idt.typeOrElse(DataType.UNDEFINED)
|
||||
if(expr.operator=="-") {
|
||||
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
||||
errors.err("can only take negative of a signed number type", expr.position)
|
||||
@@ -825,8 +805,8 @@ internal class AstChecker(private val program: Program,
|
||||
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||
return // hopefully this error will be detected elsewhere
|
||||
|
||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
||||
val leftDt = leftIDt.typeOrElse(DataType.UNDEFINED)
|
||||
val rightDt = rightIDt.typeOrElse(DataType.UNDEFINED)
|
||||
|
||||
when(expr.operator){
|
||||
"/", "%" -> {
|
||||
@@ -1027,7 +1007,7 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("swap requires 2 variables, not constant value(s)", position)
|
||||
else if(args[0] isSameAs args[1])
|
||||
errors.err("swap should have 2 different args", position)
|
||||
else if(dt1.typeOrElse(DataType.STRUCT) !in NumericDatatypes)
|
||||
else if(!dt1.isNumeric())
|
||||
errors.err("swap requires args of numerical type", position)
|
||||
}
|
||||
else if(target.name=="all" || target.name=="any") {
|
||||
@@ -1058,7 +1038,7 @@ internal class AstChecker(private val program: Program,
|
||||
ident = fcall.args[0] as? IdentifierReference
|
||||
}
|
||||
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
|
||||
val reg = RegisterOrPair.valueOf(ident.nameInSource[1].toUpperCase())
|
||||
val reg = RegisterOrPair.valueOf(ident.nameInSource[1].uppercase())
|
||||
val same = params.filter { it.value.registerOrPair==reg }
|
||||
for(s in same) {
|
||||
if(s.index!=arg.index) {
|
||||
@@ -1132,8 +1112,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(whenStatement: WhenStatement) {
|
||||
val conditionType = whenStatement.condition.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if(conditionType !in IntegerDatatypes)
|
||||
if(!whenStatement.condition.inferType(program).isInteger())
|
||||
errors.err("when condition must be an integer value", whenStatement.position)
|
||||
val tally = mutableSetOf<Int>()
|
||||
for((choices, choiceNode) in whenStatement.choiceValues(program)) {
|
||||
@@ -1164,7 +1143,7 @@ internal class AstChecker(private val program: Program,
|
||||
when {
|
||||
constvalue == null -> errors.err("choice value must be a constant", whenChoice.position)
|
||||
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position)
|
||||
constvalue.type != conditionType.typeOrElse(DataType.STRUCT) -> errors.err("choice value datatype differs from condition value", whenChoice.position)
|
||||
constvalue.type != conditionType.typeOrElse(DataType.UNDEFINED) -> errors.err("choice value datatype differs from condition value", whenChoice.position)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1174,24 +1153,6 @@ internal class AstChecker(private val program: Program,
|
||||
super.visit(whenChoice)
|
||||
}
|
||||
|
||||
override fun visit(structDecl: StructDecl) {
|
||||
// a struct can only contain 1 or more vardecls and can not be nested
|
||||
if(structDecl.statements.isEmpty())
|
||||
errors.err("struct must contain at least one member", structDecl.position)
|
||||
|
||||
for(member in structDecl.statements){
|
||||
val decl = member as? VarDecl
|
||||
if(decl==null)
|
||||
errors.err("struct can only contain variable declarations", structDecl.position)
|
||||
else {
|
||||
if(decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE)
|
||||
errors.err("struct can not contain zeropage members", decl.position)
|
||||
if(decl.datatype !in NumericDatatypes)
|
||||
errors.err("structs can only contain numerical types", decl.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
||||
val targetStatement = target.targetStatement(program)
|
||||
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
||||
@@ -1215,8 +1176,7 @@ internal class AstChecker(private val program: Program,
|
||||
else false
|
||||
}
|
||||
|
||||
private fun checkValueTypeAndRangeArray(targetDt: DataType, struct: StructDecl?,
|
||||
arrayspec: ArrayIndex, value: ArrayLiteralValue) : Boolean {
|
||||
private fun checkValueTypeAndRangeArray(targetDt: DataType, arrayspec: ArrayIndex, value: ArrayLiteralValue) : Boolean {
|
||||
fun err(msg: String) : Boolean {
|
||||
errors.err(msg, value.position)
|
||||
return false
|
||||
@@ -1289,22 +1249,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
return err("invalid float array initialization value ${value.type}, expected $targetDt")
|
||||
}
|
||||
DataType.STRUCT -> {
|
||||
if(value.type.typeOrElse(DataType.STRUCT) in ArrayDatatypes) {
|
||||
if(value.value.size != struct!!.numberOfElements)
|
||||
return err("number of values is not the same as the number of members in the struct")
|
||||
for(elt in value.value.zip(struct.statements)) {
|
||||
val vardecl = elt.second as VarDecl
|
||||
val valuetype = elt.first.inferType(program)
|
||||
if (!valuetype.isKnown || valuetype isNotAssignableTo vardecl.datatype) {
|
||||
errors.err("invalid struct member init value type $valuetype, expected ${vardecl.datatype}", elt.first.position)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
@@ -1407,15 +1351,6 @@ internal class AstChecker(private val program: Program,
|
||||
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
|
||||
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
||||
DataType.STR -> sourceDatatype== DataType.STR
|
||||
DataType.STRUCT -> {
|
||||
if(sourceDatatype==DataType.STRUCT) {
|
||||
val structLv = sourceValue as ArrayLiteralValue
|
||||
val numValues = structLv.value.size
|
||||
val targetstruct = target.identifier!!.targetVarDecl(program)!!.struct!!
|
||||
return targetstruct.numberOfElements == numValues
|
||||
}
|
||||
false
|
||||
}
|
||||
else -> {
|
||||
errors.err("cannot assign new value to variable of type $targetDatatype", position)
|
||||
false
|
||||
@@ -1429,10 +1364,15 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("cannot assign word to byte, use msb() or lsb()?", position)
|
||||
}
|
||||
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||
errors.err("cannot assign float to ${targetDatatype.name.toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
||||
errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
||||
else {
|
||||
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes)
|
||||
errors.err("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}", position)
|
||||
errors.err(
|
||||
"cannot assign ${sourceDatatype.name.lowercase()} to ${
|
||||
targetDatatype.name.lowercase(
|
||||
Locale.getDefault()
|
||||
)
|
||||
}", position)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -44,9 +44,9 @@ internal fun Program.verifyFunctionArgTypes() {
|
||||
fixer.visit(this)
|
||||
}
|
||||
|
||||
internal fun Program.checkIdentifiers(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||
internal fun Program.checkIdentifiers(errors: IErrorReporter, options: CompilationOptions) {
|
||||
|
||||
val checker2 = AstIdentifiersChecker(this, errors, compTarget)
|
||||
val checker2 = AstIdentifiersChecker(this, errors, options.compTarget)
|
||||
checker2.visit(this)
|
||||
|
||||
if(errors.noErrors()) {
|
||||
|
||||
@@ -2,11 +2,7 @@ package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.NumericDatatypes
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.expressions.ArrayLiteralValue
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.expressions.StringLiteralValue
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
@@ -66,29 +62,6 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
||||
if(decl.name in compTarget.machine.opcodeNames)
|
||||
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
||||
|
||||
if(decl.datatype==DataType.STRUCT) {
|
||||
if (decl.structHasBeenFlattened)
|
||||
return super.visit(decl) // don't do this multiple times
|
||||
|
||||
if (decl.struct == null) {
|
||||
errors.err("undefined struct type", decl.position)
|
||||
return super.visit(decl)
|
||||
}
|
||||
|
||||
if (decl.struct!!.statements.any { (it as VarDecl).datatype !in NumericDatatypes })
|
||||
return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later
|
||||
|
||||
if (decl.value is NumericLiteralValue) {
|
||||
errors.err("you cannot initialize a struct using a single value", decl.position)
|
||||
return super.visit(decl)
|
||||
}
|
||||
|
||||
if (decl.value != null && decl.value !is ArrayLiteralValue) {
|
||||
errors.err("initializing a struct requires array literal value", decl.value?.position ?: decl.position)
|
||||
return super.visit(decl)
|
||||
}
|
||||
}
|
||||
|
||||
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
||||
if (existing != null && existing !== decl)
|
||||
nameError(decl.name, decl.position, existing)
|
||||
@@ -116,7 +89,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
||||
if (existing != null && existing !== subroutine)
|
||||
nameError(subroutine.name, subroutine.position, existing)
|
||||
|
||||
// check that there are no local variables, labels, or other subs that redefine the subroutine's parameters
|
||||
// check that there are no local variables, labels, or other subs that redefine the subroutine's parameters. Blocks are okay.
|
||||
val symbolsInSub = subroutine.allDefinedSymbols()
|
||||
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||
val paramNames = subroutine.parameters.map { it.name }.toSet()
|
||||
@@ -128,9 +101,6 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
||||
val sub = subroutine.statements.firstOrNull { it is Subroutine && it.name==name}
|
||||
if(sub!=null)
|
||||
nameError(name, subroutine.position, sub)
|
||||
val block = program.allBlocks().firstOrNull { it.name==name }
|
||||
if(block!=null)
|
||||
nameError(name, subroutine.position, block)
|
||||
}
|
||||
|
||||
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
||||
@@ -169,14 +139,4 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
||||
|
||||
super.visit(string)
|
||||
}
|
||||
|
||||
override fun visit(structDecl: StructDecl) {
|
||||
for(member in structDecl.statements){
|
||||
val decl = member as? VarDecl
|
||||
if(decl!=null && decl.datatype !in NumericDatatypes)
|
||||
errors.err("structs can only contain numerical types", decl.position)
|
||||
}
|
||||
|
||||
super.visit(structDecl)
|
||||
}
|
||||
}
|
||||
@@ -3,31 +3,17 @@ package prog8.compiler.astprocessing
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.expressions.ArrayIndexedExpression
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.DirectMemoryRead
|
||||
import prog8.ast.expressions.StringLiteralValue
|
||||
import prog8.ast.statements.AnonymousScope
|
||||
import prog8.ast.statements.ParameterVarDecl
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
|
||||
|
||||
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
||||
|
||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
// is it a struct variable? then define all its struct members as mangled names,
|
||||
// and include the original decl as well.
|
||||
if(decl.datatype==DataType.STRUCT && !decl.structHasBeenFlattened) {
|
||||
val decls = decl.flattenStructMembers()
|
||||
decls.add(decl)
|
||||
val result = AnonymousScope(decls, decl.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl, result, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
// For non-kernal subroutines and non-asm parameters:
|
||||
// inject subroutine params as local variables (if they're not there yet).
|
||||
@@ -80,6 +66,10 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
return replacePointerVarIndexWithMemread(program, arrayIndexedExpression, parent)
|
||||
}
|
||||
|
||||
private fun concatString(expr: BinaryExpression): StringLiteralValue? {
|
||||
val rightStrval = expr.right as? StringLiteralValue
|
||||
val leftStrval = expr.left as? StringLiteralValue
|
||||
@@ -106,3 +96,25 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal fun replacePointerVarIndexWithMemread(program: Program, arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
||||
// rewrite pointervar[index] into @(pointervar+index)
|
||||
val indexer = arrayIndexedExpression.indexer
|
||||
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)
|
||||
val newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
@@ -38,7 +38,7 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
||||
val arrayDt = array.guessDatatype(program)
|
||||
if(arrayDt.isKnown) {
|
||||
// this array literal is part of an expression, turn it into an identifier reference
|
||||
val litval2 = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
||||
val litval2 = array.cast(arrayDt.typeOrElse(DataType.UNDEFINED))
|
||||
if(litval2!=null) {
|
||||
val vardecl2 = VarDecl.createAuto(litval2)
|
||||
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
@@ -18,7 +22,6 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
||||
// - in every block and module, most directives and vardecls are moved to the top. (not in subroutines!)
|
||||
// - the 'start' subroutine is moved to the top.
|
||||
// - (syntax desugaring) a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
|
||||
// - (syntax desugaring) struct value assignment is expanded into several struct member assignments.
|
||||
// - in-place assignments are reordered a bit so that they are mostly of the form A = A <operator> <rest>
|
||||
// - sorts the choices in when statement.
|
||||
// - insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||
@@ -83,27 +86,17 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
||||
}
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
||||
// rewrite pointervar[index] into @(pointervar+index)
|
||||
val indexer = arrayIndexedExpression.indexer
|
||||
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)
|
||||
val newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
return replacePointerVarIndexWithMemread(program, arrayIndexedExpression, parent)
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||
// (this should be done by the ExpressionSimplifier when optimizing is enabled,
|
||||
// but the current assembly code generator for IF statements now also depends on it so we do it here regardless of optimization.)
|
||||
if (expr.left.constValue(program) != null && expr.operator in associativeOperators && expr.right.constValue(program) == null)
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
|
||||
// when using a simple bit shift and assigning it to a variable of a different type,
|
||||
// try to make the bit shifting 'wide enough' to fall into the variable's type.
|
||||
// with this, for instance, uword x = 1 << 10 will result in 1024 rather than 0 (the ubyte result).
|
||||
@@ -113,7 +106,7 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
||||
is Assignment -> {
|
||||
val targetDt = parent.target.inferType(program)
|
||||
if(leftDt != targetDt) {
|
||||
val cast = TypecastExpression(expr.left, targetDt.typeOrElse(DataType.STRUCT), true, parent.position)
|
||||
val cast = TypecastExpression(expr.left, targetDt.typeOrElse(DataType.UNDEFINED), true, parent.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
}
|
||||
@@ -193,38 +186,11 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
val declValue = decl.value
|
||||
if(declValue!=null && decl.type== VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||
val declConstValue = declValue.constValue(program)
|
||||
if(declConstValue==null) {
|
||||
// move the vardecl (without value) to the scope of the defining subroutine and put a regular assignment in its place here.
|
||||
decl.value = null
|
||||
decl.allowInitializeWithZero = false
|
||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||
val assign = Assignment(target, declValue, decl.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(decl, assign, parent),
|
||||
IAstModification.InsertFirst(decl, decl.definingSubroutine() as INameScope)
|
||||
)
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
val valueType = assignment.value.inferType(program)
|
||||
val targetType = assignment.target.inferType(program)
|
||||
|
||||
if(targetType.istype(DataType.STRUCT) && (valueType.istype(DataType.STRUCT) || valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes )) {
|
||||
if (assignment.value is ArrayLiteralValue) {
|
||||
errors.err("cannot assign array literal here, use separate assignment per field", assignment.position)
|
||||
} else {
|
||||
return copyStructValue(assignment)
|
||||
}
|
||||
}
|
||||
|
||||
if(targetType.typeOrElse(DataType.STRUCT) in ArrayDatatypes && valueType.typeOrElse(DataType.STRUCT) in ArrayDatatypes ) {
|
||||
if(targetType.isArray() && valueType.isArray() ) {
|
||||
if (assignment.value is ArrayLiteralValue) {
|
||||
errors.err("cannot assign array literal here, use separate assignment per element", assignment.position)
|
||||
} else {
|
||||
@@ -320,73 +286,4 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(assign, memcopy, assign.parent))
|
||||
}
|
||||
|
||||
private fun copyStructValue(structAssignment: Assignment): List<IAstModification> {
|
||||
val identifier = structAssignment.target.identifier!!
|
||||
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) {
|
||||
errors.err("struct type mismatch", structAssignment.position)
|
||||
return listOf()
|
||||
}
|
||||
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
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
is ArrayLiteralValue -> {
|
||||
throw IllegalArgumentException("not going to do a structLv assignment here")
|
||||
}
|
||||
else -> throw FatalAstException("strange struct value")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,12 +20,16 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
val declValue = decl.value
|
||||
if(decl.type==VarDeclType.VAR && declValue!=null && decl.struct==null) {
|
||||
if(decl.type==VarDeclType.VAR && declValue!=null) {
|
||||
val valueDt = declValue.inferType(program)
|
||||
if(!valueDt.istype(decl.datatype)) {
|
||||
|
||||
// don't add a typecast on an array initializer value
|
||||
if(valueDt.typeOrElse(DataType.STRUCT) in IntegerDatatypes && decl.datatype in ArrayDatatypes)
|
||||
if(valueDt.isInteger() && decl.datatype in ArrayDatatypes)
|
||||
return noModifications
|
||||
|
||||
// don't add a typecast if the initializer value is inherently not assignable
|
||||
if(valueDt isNotAssignableTo decl.datatype)
|
||||
return noModifications
|
||||
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
@@ -43,7 +47,7 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
|
||||
val rightDt = expr.right.inferType(program)
|
||||
if(leftDt.isKnown && rightDt.isKnown && leftDt!=rightDt) {
|
||||
// determine common datatype and add typecast as required to make left and right equal types
|
||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.typeOrElse(DataType.STRUCT), rightDt.typeOrElse(DataType.STRUCT), expr.left, expr.right)
|
||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.typeOrElse(DataType.UNDEFINED), rightDt.typeOrElse(DataType.UNDEFINED), expr.left, expr.right)
|
||||
if(toFix!=null) {
|
||||
return when {
|
||||
toFix===expr.left -> listOf(IAstModification.ReplaceNode(
|
||||
@@ -62,8 +66,8 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
|
||||
val valueItype = assignment.value.inferType(program)
|
||||
val targetItype = assignment.target.inferType(program)
|
||||
if(targetItype.isKnown && valueItype.isKnown) {
|
||||
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
||||
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
||||
val targettype = targetItype.typeOrElse(DataType.UNDEFINED)
|
||||
val valuetype = valueItype.typeOrElse(DataType.UNDEFINED)
|
||||
if (valuetype != targettype) {
|
||||
if (valuetype isAssignableTo targettype) {
|
||||
if(valuetype in IterableDatatypes && targettype==DataType.UWORD)
|
||||
@@ -122,7 +126,7 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
|
||||
sub.parameters.zip(call.args).forEachIndexed { index, pair ->
|
||||
val argItype = pair.second.inferType(program)
|
||||
if(argItype.isKnown) {
|
||||
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
||||
val argtype = argItype.typeOrElse(DataType.UNDEFINED)
|
||||
val requiredType = pair.first.type
|
||||
if (requiredType != argtype) {
|
||||
if (argtype isAssignableTo requiredType) {
|
||||
@@ -155,7 +159,7 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
|
||||
func.parameters.zip(call.args).forEachIndexed { index, pair ->
|
||||
val argItype = pair.second.inferType(program)
|
||||
if (argItype.isKnown) {
|
||||
val argtype = argItype.typeOrElse(DataType.STRUCT)
|
||||
val argtype = argItype.typeOrElse(DataType.UNDEFINED)
|
||||
if (pair.first.possibleDatatypes.all { argtype != it }) {
|
||||
for (possibleType in pair.first.possibleDatatypes) {
|
||||
if (argtype isAssignableTo possibleType) {
|
||||
|
||||
@@ -44,7 +44,7 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
||||
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
|
||||
if(firstUnknownDt>=0)
|
||||
return "argument ${firstUnknownDt+1} invalid argument type"
|
||||
val argtypes = argITypes.map { it.typeOrElse(DataType.STRUCT) }
|
||||
val argtypes = argITypes.map { it.typeOrElse(DataType.UNDEFINED) }
|
||||
val target = call.target.targetStatement(program)
|
||||
if (target is Subroutine) {
|
||||
if(call.args.size != target.parameters.size)
|
||||
|
||||
@@ -4,7 +4,6 @@ import prog8.ast.IMemSizer
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.StructDecl
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compiler.CompilerException
|
||||
import kotlin.math.*
|
||||
@@ -105,7 +104,6 @@ private val functionSignatures: List<FSignature> = listOf(
|
||||
FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||
FSignature("sizeof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
|
||||
FSignature("offsetof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinOffsetof),
|
||||
// normal functions follow:
|
||||
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
||||
FSignature("sin" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||
@@ -138,12 +136,13 @@ 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),
|
||||
FSignature("memory" , true, listOf(FParam("name", setOf(DataType.STR)), FParam("size", setOf(DataType.UWORD))), DataType.UWORD),
|
||||
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
|
||||
FSignature("callfar" , false, listOf(FParam("bank", setOf(DataType.UBYTE)), FParam("address", setOf(DataType.UWORD)), FParam("arg", setOf(DataType.UWORD))), null),
|
||||
FSignature("callrom" , false, listOf(FParam("bank", setOf(DataType.UBYTE)), FParam("address", setOf(DataType.UWORD)), FParam("arg", setOf(DataType.UWORD))), null),
|
||||
|
||||
)
|
||||
|
||||
@@ -154,7 +153,7 @@ fun builtinMax(array: List<Number>): Number = array.maxByOrNull { it.toDouble()
|
||||
|
||||
fun builtinMin(array: List<Number>): Number = array.minByOrNull { it.toDouble() }!!
|
||||
|
||||
fun builtinSum(array: List<Number>): Number = array.sumByDouble { it.toDouble() }
|
||||
fun builtinSum(array: List<Number>): Number = array.sumOf { it.toDouble() }
|
||||
|
||||
fun builtinAny(array: List<Number>): Number = if(array.any { it.toDouble()!=0.0 }) 1 else 0
|
||||
|
||||
@@ -165,7 +164,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
||||
|
||||
fun datatypeFromIterableArg(arglist: Expression): DataType {
|
||||
if(arglist is ArrayLiteralValue) {
|
||||
val dt = arglist.value.map {it.inferType(program).typeOrElse(DataType.STRUCT)}.toSet()
|
||||
val dt = arglist.value.map {it.inferType(program).typeOrElse(DataType.UNDEFINED)}.toSet()
|
||||
if(dt.any { it !in NumericDatatypes }) {
|
||||
throw FatalAstException("fuction $function only accepts array of numeric values")
|
||||
}
|
||||
@@ -179,9 +178,9 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
||||
val idt = arglist.inferType(program)
|
||||
if(!idt.isKnown)
|
||||
throw FatalAstException("couldn't determine type of iterable $arglist")
|
||||
return when(val dt = idt.typeOrElse(DataType.STRUCT)) {
|
||||
return when(val dt = idt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.STR, in NumericDatatypes -> dt
|
||||
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
|
||||
in ArrayDatatypes -> ArrayToElementTypes.getValue(dt)
|
||||
else -> throw FatalAstException("function '$function' requires one argument which is an iterable")
|
||||
}
|
||||
}
|
||||
@@ -196,7 +195,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
||||
return when (function) {
|
||||
"abs" -> {
|
||||
val dt = args.single().inferType(program)
|
||||
return if(dt.typeOrElse(DataType.STRUCT) in NumericDatatypes)
|
||||
return if(dt.isNumeric())
|
||||
dt
|
||||
else
|
||||
InferredTypes.InferredType.unknown()
|
||||
@@ -205,7 +204,7 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
|
||||
when(val dt = datatypeFromIterableArg(args.single())) {
|
||||
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
||||
in NumericDatatypes -> InferredTypes.knownFor(dt)
|
||||
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(dt))
|
||||
in ArrayDatatypes -> InferredTypes.knownFor(ArrayToElementTypes.getValue(dt))
|
||||
else -> InferredTypes.unknown()
|
||||
}
|
||||
}
|
||||
@@ -288,28 +287,6 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinOffsetof(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
// 1 arg, type = anything, result type = ubyte
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("offsetof requires one argument", position)
|
||||
val idref = args[0] as? IdentifierReference
|
||||
?: throw SyntaxError("offsetof argument should be an identifier", position)
|
||||
|
||||
val vardecl = idref.targetVarDecl(program)!!
|
||||
val struct = vardecl.struct
|
||||
if (struct == null || vardecl.datatype == DataType.STRUCT)
|
||||
throw SyntaxError("offsetof can only be used on struct members", position)
|
||||
|
||||
val membername = idref.nameInSource.last()
|
||||
var offset = 0
|
||||
for(member in struct.statements) {
|
||||
if((member as VarDecl).name == membername)
|
||||
return NumericLiteralValue(DataType.UBYTE, offset, position)
|
||||
offset += memsizer.memorySize(member.datatype)
|
||||
}
|
||||
throw SyntaxError("undefined struct member", position)
|
||||
}
|
||||
|
||||
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer): NumericLiteralValue {
|
||||
// 1 arg, type = anything, result type = ubyte
|
||||
if(args.size!=1)
|
||||
@@ -322,23 +299,14 @@ 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.memsize(memsizer), position)
|
||||
|
||||
return when {
|
||||
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
||||
dt.isArray() -> {
|
||||
val length = (target as VarDecl).arraysize!!.constIndex() ?: throw CannotEvaluateException("sizeof", "unknown array size")
|
||||
val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT))
|
||||
val elementDt = ArrayToElementTypes.getValue(dt.typeOrElse(DataType.UNDEFINED))
|
||||
numericLiteral(memsizer.memorySize(elementDt) * length, position)
|
||||
}
|
||||
dt.istype(DataType.STRUCT) -> {
|
||||
when (target) {
|
||||
is VarDecl -> structSize(target.struct!!)
|
||||
is StructDecl -> structSize(target)
|
||||
else -> throw CompilerException("weird struct type $target")
|
||||
}
|
||||
}
|
||||
dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
|
||||
else -> NumericLiteralValue(DataType.UBYTE, memsizer.memorySize(dt.typeOrElse(DataType.STRUCT)), position)
|
||||
else -> NumericLiteralValue(DataType.UBYTE, memsizer.memorySize(dt.typeOrElse(DataType.UNDEFINED)), position)
|
||||
}
|
||||
} else {
|
||||
throw SyntaxError("sizeof invalid argument type", position)
|
||||
@@ -373,7 +341,6 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
||||
val refLv = target.value as? StringLiteralValue ?: throw CannotEvaluateException("len", "stringsize unknown")
|
||||
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
|
||||
}
|
||||
DataType.STRUCT -> throw SyntaxError("cannot use len on struct, did you mean sizeof?", args[0].position)
|
||||
in NumericDatatypes -> throw SyntaxError("cannot use len on numeric value, did you mean sizeof?", args[0].position)
|
||||
else -> throw CompilerException("weird datatype")
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import prog8.ast.base.*
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.IErrorReporter
|
||||
import prog8.compiler.Zeropage
|
||||
@@ -75,13 +74,13 @@ internal object C64Target: ICompilationTarget {
|
||||
try {
|
||||
if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||
} catch (x: CharConversionException) {
|
||||
throw AssemblyError("There was a problem converting a string to the target machine's char encoding: ${x.message}")
|
||||
throw CharConversionException("can't convert string to target machine's char encoding: ${x.message}")
|
||||
}
|
||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||
try {
|
||||
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||
} catch (x: CharConversionException) {
|
||||
throw AssemblyError("There was a problem decoding to a string: ${x.message}")
|
||||
throw CharConversionException("can't decode string: ${x.message}")
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
@@ -102,13 +101,13 @@ internal object Cx16Target: ICompilationTarget {
|
||||
try {
|
||||
if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||
} catch (x: CharConversionException) {
|
||||
throw AssemblyError("There was a problem converting a string to the target machine's char encoding: ${x.message}")
|
||||
throw CharConversionException("can't convert string to target machine's char encoding: ${x.message}")
|
||||
}
|
||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||
try {
|
||||
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||
} catch (x: CharConversionException) {
|
||||
throw AssemblyError("There was a problem decoding to a string: ${x.message}")
|
||||
throw CharConversionException("can't decode string: ${x.message}")
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType): Int {
|
||||
|
||||
@@ -39,7 +39,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
||||
for(emulator in listOf("x64sc", "x64")) {
|
||||
println("\nStarting C-64 emulator $emulator...")
|
||||
val cmdline = listOf(emulator, "-silent", "-moncommands", "$programName.vice-mon-list",
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", programName + ".prg")
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", "$programName.prg")
|
||||
val processb = ProcessBuilder(cmdline).inheritIO()
|
||||
val process: Process
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.compiler.target.cbm
|
||||
|
||||
import prog8.ast.antlr.escape
|
||||
import java.io.CharConversionException
|
||||
|
||||
object Petscii {
|
||||
@@ -172,7 +173,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0xA6 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -236,7 +237,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0xE6 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -431,7 +432,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0xA1 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0xA2 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0xA3 -> UPPER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0xA4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0xA5 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0xA6 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0xA7 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -495,7 +496,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0xE1 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0xE2 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0xE3 -> UPPER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0xE4 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0xE5 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0xE6 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0xE7 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -626,7 +627,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0x66 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -885,7 +886,7 @@ object Petscii {
|
||||
'\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
|
||||
'\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
|
||||
'\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
|
||||
'_', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u2581', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
|
||||
'\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
|
||||
'\u2592', // ▒ 0x66 -> MEDIUM SHADE
|
||||
'\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
|
||||
@@ -1049,19 +1050,31 @@ object Petscii {
|
||||
private val encodingScreencodeLowercase = decodingScreencodeLowercase.withIndex().associate{it.value to it.index}
|
||||
private val encodingScreencodeUppercase = decodingScreencodeUppercase.withIndex().associate{it.value to it.index}
|
||||
|
||||
private fun replaceSpecial(chr: Char): Char =
|
||||
// characters often used in C like source code can be translated with a little bit of fantasy:
|
||||
when(chr) {
|
||||
'^' -> '↑'
|
||||
'_' -> '▁'
|
||||
'{' -> '┤'
|
||||
'}' -> '├'
|
||||
'|' -> '│'
|
||||
'\\' -> '╲'
|
||||
else -> chr
|
||||
}
|
||||
|
||||
fun encodePetscii(text: String, lowercase: Boolean = false): List<Short> {
|
||||
fun encodeChar(chr: Char, lowercase: Boolean): Short {
|
||||
fun encodeChar(chr3: Char, lowercase: Boolean): Short {
|
||||
val chr = replaceSpecial(chr3)
|
||||
val screencode = if(lowercase) encodingPetsciiLowercase[chr] else encodingPetsciiUppercase[chr]
|
||||
return screencode?.toShort() ?: when (chr) {
|
||||
'\u0000' -> 0.toShort()
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.toInt() - 0x8000).toShort()
|
||||
(chr.code - 0x8000).toShort()
|
||||
}
|
||||
else -> {
|
||||
val case = if (lowercase) "lower" else "upper"
|
||||
throw CharConversionException("no ${case}Petscii character for '$chr' (${chr.toShort()})")
|
||||
throw CharConversionException("no ${case}Petscii character for '${escape(chr.toString())}' (${chr.code})")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1087,17 +1100,18 @@ object Petscii {
|
||||
}
|
||||
|
||||
fun encodeScreencode(text: String, lowercase: Boolean = false): List<Short> {
|
||||
fun encodeChar(chr: Char, lowercase: Boolean): Short {
|
||||
fun encodeChar(chr3: Char, lowercase: Boolean): Short {
|
||||
val chr = replaceSpecial(chr3)
|
||||
val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
|
||||
return screencode?.toShort() ?: when (chr) {
|
||||
'\u0000' -> 0.toShort()
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.toInt() - 0x8000).toShort()
|
||||
(chr.code - 0x8000).toShort()
|
||||
}
|
||||
else -> {
|
||||
val case = if (lowercase) "lower" else "upper"
|
||||
throw CharConversionException("no ${case}Screencode character for '$chr' (${chr.toShort()})")
|
||||
throw CharConversionException("no ${case}Screencode character for '${escape(chr.toString())}' (${chr.code})")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ internal class AsmGen(private val program: Program,
|
||||
|
||||
// for expressions and augmented assignments:
|
||||
val optimizedByteMultiplications = setOf(3,5,6,7,9,10,11,12,13,14,15,20,25,40,50,80,100)
|
||||
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320)
|
||||
val optimizedWordMultiplications = setOf(3,5,6,7,9,10,12,15,20,25,40,50,80,100,320,640)
|
||||
private val callGraph = CallGraph(program)
|
||||
|
||||
private val assemblyLines = mutableListOf<String>()
|
||||
@@ -128,7 +128,7 @@ internal class AsmGen(private val program: Program,
|
||||
out("* = ${program.actualLoadAddress.toHex()}")
|
||||
val year = LocalDate.now().year
|
||||
out(" .word (+), $year")
|
||||
out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'")
|
||||
out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8'")
|
||||
out("+\t.word 0")
|
||||
out("_prog8_entrypoint\t; assembly code starts here\n")
|
||||
if(!options.noSysInit)
|
||||
@@ -290,7 +290,6 @@ internal class AsmGen(private val program: Program,
|
||||
DataType.UWORD -> out("$name\t.word 0")
|
||||
DataType.WORD -> out("$name\t.sint 0")
|
||||
DataType.FLOAT -> out("$name\t.byte 0,0,0,0,0 ; float")
|
||||
DataType.STRUCT -> {} // is flattened
|
||||
DataType.STR -> {
|
||||
val str = decl.value as StringLiteralValue
|
||||
outputStringvar(decl, compTarget.encodeString(str.value, str.altEncoding).plus(0))
|
||||
@@ -352,6 +351,9 @@ internal class AsmGen(private val program: Program,
|
||||
for (f in array.zip(floatFills))
|
||||
out(" .byte ${f.second} ; float ${f.first}")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,38 +381,28 @@ internal class AsmGen(private val program: Program,
|
||||
out("\n; non-zeropage variables")
|
||||
val vars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
||||
|
||||
// first output the flattened struct member variables *in order*
|
||||
// after that, the other variables sorted by their datatype
|
||||
|
||||
val (structMembers, normalVars) = vars.partition { it.struct!=null }
|
||||
structMembers.forEach { vardecl2asm(it) }
|
||||
|
||||
// special treatment for string types: merge strings that are identical
|
||||
val encodedstringVars = normalVars
|
||||
val encodedstringVars = vars
|
||||
.filter {it.datatype == DataType.STR }
|
||||
.map {
|
||||
val str = it.value as StringLiteralValue
|
||||
it to compTarget.encodeString(str.value, str.altEncoding).plus(0)
|
||||
}
|
||||
.groupBy({it.second}, {it.first})
|
||||
for((encoded, variables) in encodedstringVars) {
|
||||
variables.dropLast(1).forEach { out(it.name) }
|
||||
val lastvar = variables.last()
|
||||
outputStringvar(lastvar, encoded)
|
||||
for((decl, variables) in encodedstringVars) {
|
||||
outputStringvar(decl, variables)
|
||||
}
|
||||
|
||||
// non-string variables
|
||||
normalVars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
|
||||
vars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
|
||||
if(it.makeScopedName(it.name) !in allocatedZeropageVariables)
|
||||
vardecl2asm(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputStringvar(lastvar: VarDecl, encoded: List<Short>) {
|
||||
val sv = lastvar.value as StringLiteralValue
|
||||
private fun outputStringvar(strdecl: VarDecl, bytes: List<Short>) {
|
||||
val sv = strdecl.value as StringLiteralValue
|
||||
val altEncoding = if(sv.altEncoding) "@" else ""
|
||||
out("${lastvar.name}\t; ${lastvar.datatype} $altEncoding\"${escape(sv.value).replace("\u0000", "<NULL>")}\"")
|
||||
val outputBytes = encoded.map { "$" + it.toString(16).padStart(2, '0') }
|
||||
out("${strdecl.name}\t; ${strdecl.datatype} $altEncoding\"${escape(sv.value).replace("\u0000", "<NULL>")}\"")
|
||||
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
|
||||
for (chunk in outputBytes.chunked(16))
|
||||
out(" .byte " + chunk.joinToString())
|
||||
}
|
||||
@@ -437,10 +429,10 @@ internal class AsmGen(private val program: Program,
|
||||
"$" + it.number.toInt().toString(16).padStart(4, '0')
|
||||
}
|
||||
is AddressOf -> {
|
||||
it.identifier.firstStructVarName(program) ?: asmSymbolName(it.identifier)
|
||||
asmSymbolName(it.identifier)
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
it.firstStructVarName(program) ?: asmSymbolName(it)
|
||||
asmSymbolName(it)
|
||||
}
|
||||
else -> throw AssemblyError("weird array elt dt")
|
||||
}
|
||||
@@ -502,28 +494,19 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
internal fun asmSymbolName(identifier: IdentifierReference): String {
|
||||
return if(identifier.memberOfStruct(program)!=null) {
|
||||
val name = identifier.targetVarDecl(program)!!.name
|
||||
fixNameSymbols(name)
|
||||
} else {
|
||||
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
||||
}
|
||||
val target = identifier.targetStatement(program)
|
||||
val prefix = if(target is Label) "_" else ""
|
||||
return fixNameSymbols(prefix+identifier.nameInSource.joinToString("."))
|
||||
}
|
||||
|
||||
internal fun asmSymbolName(regs: RegisterOrPair): String =
|
||||
if(regs in Cx16VirtualRegisters)
|
||||
"cx16." + regs.toString().toLowerCase()
|
||||
if (regs in Cx16VirtualRegisters)
|
||||
"cx16." + regs.toString().lowercase()
|
||||
else
|
||||
throw AssemblyError("no symbol name for register $regs")
|
||||
|
||||
internal fun asmVariableName(identifier: IdentifierReference): String {
|
||||
return if(identifier.memberOfStruct(program)!=null) {
|
||||
val name = identifier.targetVarDecl(program)!!.name
|
||||
fixNameSymbols(name)
|
||||
} else {
|
||||
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
||||
}
|
||||
}
|
||||
internal fun asmVariableName(identifier: IdentifierReference) =
|
||||
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
||||
|
||||
internal fun asmSymbolName(name: String) = fixNameSymbols(name)
|
||||
internal fun asmVariableName(name: String) = fixNameSymbols(name)
|
||||
@@ -671,7 +654,7 @@ internal class AsmGen(private val program: Program,
|
||||
when(stmt) {
|
||||
is ParameterVarDecl -> { /* subroutine parameter vardecls don't get any special treatment here */ }
|
||||
is VarDecl -> translate(stmt)
|
||||
is StructDecl, is NopStatement -> {}
|
||||
is NopStatement -> {}
|
||||
is Directive -> translate(stmt)
|
||||
is Return -> translate(stmt)
|
||||
is Subroutine -> translateSubroutine(stmt)
|
||||
@@ -708,14 +691,16 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
internal fun loadScaledArrayIndexIntoRegister(expr: ArrayIndexedExpression,
|
||||
elementDt: DataType,
|
||||
register: CpuRegister,
|
||||
addOneExtra: Boolean=false) {
|
||||
val reg = register.toString().toLowerCase()
|
||||
internal fun loadScaledArrayIndexIntoRegister(
|
||||
expr: ArrayIndexedExpression,
|
||||
elementDt: DataType,
|
||||
register: CpuRegister,
|
||||
addOneExtra: Boolean = false
|
||||
) {
|
||||
val reg = register.toString().lowercase()
|
||||
val indexnum = expr.indexer.constIndex()
|
||||
if(indexnum!=null) {
|
||||
val indexValue = indexnum * compTarget.memorySize(elementDt) + if(addOneExtra) 1 else 0
|
||||
if (indexnum != null) {
|
||||
val indexValue = indexnum * compTarget.memorySize(elementDt) + if (addOneExtra) 1 else 0
|
||||
out(" ld$reg #$indexValue")
|
||||
return
|
||||
}
|
||||
@@ -724,35 +709,40 @@ internal class AsmGen(private val program: Program,
|
||||
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.indexer.position}")
|
||||
|
||||
val indexName = asmVariableName(indexVar)
|
||||
if(addOneExtra) {
|
||||
if (addOneExtra) {
|
||||
// add 1 to the result
|
||||
when(elementDt) {
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
out(" ldy $indexName | iny")
|
||||
when(register) {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" tya")
|
||||
CpuRegister.X -> out(" tyx")
|
||||
CpuRegister.Y -> {}
|
||||
CpuRegister.Y -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
out(" lda $indexName | sec | rol a")
|
||||
when(register) {
|
||||
CpuRegister.A -> {}
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(compTarget.memorySize(DataType.FLOAT)==5)
|
||||
out("""
|
||||
require(compTarget.memorySize(DataType.FLOAT) == 5)
|
||||
out(
|
||||
"""
|
||||
lda $indexName
|
||||
asl a
|
||||
asl a
|
||||
sec
|
||||
adc $indexName""")
|
||||
when(register) {
|
||||
CpuRegister.A -> {}
|
||||
adc $indexName"""
|
||||
)
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
@@ -760,26 +750,30 @@ internal class AsmGen(private val program: Program,
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
} else {
|
||||
when(elementDt) {
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> out(" ld$reg $indexName")
|
||||
in WordDatatypes -> {
|
||||
out(" lda $indexName | asl a")
|
||||
when(register) {
|
||||
CpuRegister.A -> {}
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(compTarget.memorySize(DataType.FLOAT)==5)
|
||||
out("""
|
||||
require(compTarget.memorySize(DataType.FLOAT) == 5)
|
||||
out(
|
||||
"""
|
||||
lda $indexName
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc $indexName""")
|
||||
when(register) {
|
||||
CpuRegister.A -> {}
|
||||
adc $indexName"""
|
||||
)
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
@@ -886,7 +880,15 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
out("; variables")
|
||||
out("; register saves")
|
||||
for((dt, name, addr) in sub.asmGenInfo.extraVars) {
|
||||
if(addr!=null)
|
||||
out("$name = $addr")
|
||||
else when(dt) {
|
||||
DataType.UBYTE -> out("$name .byte 0")
|
||||
DataType.UWORD -> out("$name .word 0")
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
if(sub.asmGenInfo.usedRegsaveA)
|
||||
out("_prog8_regsaveA .byte 0")
|
||||
if(sub.asmGenInfo.usedRegsaveX)
|
||||
@@ -894,9 +896,9 @@ internal class AsmGen(private val program: Program,
|
||||
if(sub.asmGenInfo.usedRegsaveY)
|
||||
out("_prog8_regsaveY .byte 0")
|
||||
if(sub.asmGenInfo.usedFloatEvalResultVar1)
|
||||
out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
|
||||
out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
|
||||
if(sub.asmGenInfo.usedFloatEvalResultVar2)
|
||||
out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
|
||||
out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
|
||||
vardecls2asm(sub.statements)
|
||||
out(" .pend\n")
|
||||
}
|
||||
@@ -981,11 +983,11 @@ internal class AsmGen(private val program: Program,
|
||||
iterations == 0 -> {}
|
||||
iterations <= 256 -> {
|
||||
out(" lda #${iterations and 255}")
|
||||
repeatByteCountInA(iterations, repeatLabel, endLabel, stmt.body)
|
||||
repeatByteCountInA(iterations, repeatLabel, endLabel, stmt)
|
||||
}
|
||||
else -> {
|
||||
out(" lda #<${iterations} | ldy #>${iterations}")
|
||||
repeatWordCountInAY(iterations, repeatLabel, endLabel, stmt.body)
|
||||
repeatWordCountInAY(iterations, repeatLabel, endLabel, stmt)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -995,11 +997,11 @@ internal class AsmGen(private val program: Program,
|
||||
when(vardecl.datatype) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
assignVariableToRegister(name, RegisterOrPair.A)
|
||||
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
|
||||
repeatByteCountInA(null, repeatLabel, endLabel, stmt)
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
assignVariableToRegister(name, RegisterOrPair.AY)
|
||||
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
|
||||
repeatWordCountInAY(null, repeatLabel, endLabel, stmt)
|
||||
}
|
||||
else -> throw AssemblyError("invalid loop variable datatype $vardecl")
|
||||
}
|
||||
@@ -1008,14 +1010,14 @@ internal class AsmGen(private val program: Program,
|
||||
val dt = stmt.iterations!!.inferType(program)
|
||||
if(!dt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes -> {
|
||||
assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.A)
|
||||
repeatByteCountInA(null, repeatLabel, endLabel, stmt.body)
|
||||
repeatByteCountInA(null, repeatLabel, endLabel, stmt)
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
assignExpressionToRegister(stmt.iterations!!, RegisterOrPair.AY)
|
||||
repeatWordCountInAY(null, repeatLabel, endLabel, stmt.body)
|
||||
repeatWordCountInAY(null, repeatLabel, endLabel, stmt)
|
||||
}
|
||||
else -> throw AssemblyError("invalid loop expression datatype $dt")
|
||||
}
|
||||
@@ -1025,13 +1027,13 @@ internal class AsmGen(private val program: Program,
|
||||
loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, body: AnonymousScope) {
|
||||
private fun repeatWordCountInAY(constIterations: Int?, repeatLabel: String, endLabel: String, stmt: RepeatLoop) {
|
||||
// note: A/Y must have been loaded with the number of iterations!
|
||||
if(constIterations==0)
|
||||
return
|
||||
// no need to explicitly test for 0 iterations as this is done in the count down logic below
|
||||
|
||||
val counterVar = makeLabel("repeatcounter")
|
||||
val counterVar: String = createRepeatCounterVar(DataType.UWORD, constIterations, stmt)
|
||||
out("""
|
||||
sta $counterVar
|
||||
sty $counterVar+1
|
||||
@@ -1044,44 +1046,67 @@ $repeatLabel lda $counterVar
|
||||
dec $counterVar+1
|
||||
+ dec $counterVar
|
||||
""")
|
||||
translate(body)
|
||||
translate(stmt.body)
|
||||
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) {
|
||||
private fun repeatByteCountInA(constIterations: Int?, repeatLabel: String, endLabel: String, stmt: RepeatLoop) {
|
||||
// note: A must have been loaded with the number of iterations!
|
||||
if(constIterations==0)
|
||||
return
|
||||
|
||||
if(constIterations==null)
|
||||
out(" beq $endLabel ; skip loop if zero iters")
|
||||
val counterVar = makeLabel("repeatcounter")
|
||||
val counterVar = createRepeatCounterVar(DataType.UBYTE, constIterations, stmt)
|
||||
out(" sta $counterVar")
|
||||
out(repeatLabel)
|
||||
translate(body)
|
||||
out("""
|
||||
dec $counterVar
|
||||
bne $repeatLabel
|
||||
beq $endLabel""")
|
||||
translate(stmt.body)
|
||||
out(" dec $counterVar | bne $repeatLabel")
|
||||
if(constIterations==null)
|
||||
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 createRepeatCounterVar(dt: DataType, constIterations: Int?, stmt: RepeatLoop): String {
|
||||
val asmInfo = stmt.definingSubroutine()!!.asmGenInfo
|
||||
var parent = stmt.parent
|
||||
while(parent !is ParentSentinel) {
|
||||
if(parent is RepeatLoop)
|
||||
break
|
||||
parent = parent.parent
|
||||
}
|
||||
val isNested = parent is RepeatLoop
|
||||
|
||||
if(!isNested) {
|
||||
// we can re-use a counter var from the subroutine if it already has one for that datatype
|
||||
val existingVar = asmInfo.extraVars.firstOrNull { it.first==dt }
|
||||
if(existingVar!=null)
|
||||
return existingVar.second
|
||||
}
|
||||
|
||||
out(endLabel)
|
||||
val counterVar = makeLabel("repeatcounter")
|
||||
when(dt) {
|
||||
DataType.UBYTE -> {
|
||||
if(constIterations!=null && constIterations>=16 && zeropage.hasByteAvailable()) {
|
||||
// allocate count var on ZP
|
||||
val zpAddr = zeropage.allocate(counterVar, DataType.UBYTE, stmt.position, errors)
|
||||
asmInfo.extraVars.add(Triple(DataType.UBYTE, counterVar, zpAddr))
|
||||
} else {
|
||||
asmInfo.extraVars.add(Triple(DataType.UBYTE, counterVar, null))
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(constIterations!=null && constIterations>=16 && zeropage.hasWordAvailable()) {
|
||||
// allocate count var on ZP
|
||||
val zpAddr = zeropage.allocate(counterVar, DataType.UWORD, stmt.position, errors)
|
||||
asmInfo.extraVars.add(Triple(DataType.UWORD, counterVar, zpAddr))
|
||||
} else {
|
||||
asmInfo.extraVars.add(Triple(DataType.UWORD, counterVar, null))
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("invalidt dt")
|
||||
}
|
||||
return counterVar
|
||||
}
|
||||
|
||||
private fun translate(stmt: WhileLoop) {
|
||||
@@ -1242,12 +1267,7 @@ $repeatLabel lda $counterVar
|
||||
when(stmt.directive) {
|
||||
"%asminclude" -> {
|
||||
val sourcecode = loadAsmIncludeFile(stmt.args[0].str!!, stmt.definingModule().source)
|
||||
val scopeprefix = stmt.args[1].str ?: ""
|
||||
if(scopeprefix.isNotBlank())
|
||||
out("$scopeprefix\t.proc")
|
||||
assemblyLines.add(sourcecode.trimEnd().trimStart('\n'))
|
||||
if(scopeprefix.isNotBlank())
|
||||
out(" .pend\n")
|
||||
}
|
||||
"%asmbinary" -> {
|
||||
val offset = if(stmt.args.size>1) ", ${stmt.args[1].int}" else ""
|
||||
@@ -1273,14 +1293,7 @@ $label nop""")
|
||||
val label = jump.generatedLabel
|
||||
val addr = jump.address
|
||||
return when {
|
||||
ident!=null -> {
|
||||
val target = ident.targetStatement(program)
|
||||
val asmName = asmSymbolName(ident)
|
||||
if(target is Label)
|
||||
"_$asmName" // prefix with underscore to jump to local label
|
||||
else
|
||||
asmName
|
||||
}
|
||||
ident!=null -> asmSymbolName(ident)
|
||||
label!=null -> label
|
||||
addr!=null -> addr.toHex()
|
||||
else -> "????"
|
||||
|
||||
@@ -13,6 +13,7 @@ import prog8.ast.toHex
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.functions.FSignature
|
||||
import prog8.compiler.target.CpuType
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.compiler.target.cpu6502.codegen.assignment.*
|
||||
import prog8.compiler.target.subroutineFloatEvalResultVar2
|
||||
|
||||
@@ -51,7 +52,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)
|
||||
"fastrnd8", "rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
||||
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
||||
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
||||
"rol" -> funcRol(fcall)
|
||||
"rol2" -> funcRol2(fcall)
|
||||
@@ -65,15 +66,119 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
"pokew" -> funcPokeW(fcall)
|
||||
"poke" -> throw AssemblyError("poke() should have been replaced by @()")
|
||||
"cmp" -> funcCmp(fcall)
|
||||
"callfar" -> funcCallFar(fcall)
|
||||
"callrom" -> funcCallRom(fcall)
|
||||
else -> throw AssemblyError("missing asmgen for builtin func ${func.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcCallFar(fcall: IFunctionCall) {
|
||||
if(asmgen.options.compTarget !is Cx16Target)
|
||||
throw AssemblyError("callfar only works on cx16 target at this time")
|
||||
|
||||
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
||||
val address = fcall.args[1].constValue(program)?.number?.toInt()
|
||||
if(bank==null || address==null)
|
||||
throw AssemblyError("callfar (jsrfar) requires constant arguments")
|
||||
|
||||
if(address !in 0xa000..0xbfff)
|
||||
throw AssemblyError("callfar done on address outside of cx16 banked ram")
|
||||
if(bank==0)
|
||||
throw AssemblyError("callfar done on bank 0 which is reserved for the kernal")
|
||||
|
||||
val argAddrArg = fcall.args[2]
|
||||
if(argAddrArg.constValue(program)?.number == 0) {
|
||||
asmgen.out("""
|
||||
jsr cx16.jsrfar
|
||||
.word ${address.toHex()}
|
||||
.byte ${bank.toHex()}""")
|
||||
} else {
|
||||
when(argAddrArg) {
|
||||
is AddressOf -> {
|
||||
if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE)
|
||||
throw AssemblyError("callfar done with 'arg' pointer to variable that's not UBYTE")
|
||||
asmgen.out("""
|
||||
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
||||
jsr cx16.jsrfar
|
||||
.word ${address.toHex()}
|
||||
.byte ${bank.toHex()}
|
||||
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
|
||||
}
|
||||
is NumericLiteralValue -> {
|
||||
asmgen.out("""
|
||||
lda ${argAddrArg.number.toHex()}
|
||||
jsr cx16.jsrfar
|
||||
.word ${address.toHex()}
|
||||
.byte ${bank.toHex()}
|
||||
sta ${argAddrArg.number.toHex()}""")
|
||||
}
|
||||
else -> throw AssemblyError("callfar only accepts pointer-of a (ubyte) variable or constant memory address for the 'arg' parameter")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcCallRom(fcall: IFunctionCall) {
|
||||
if(asmgen.options.compTarget !is Cx16Target)
|
||||
throw AssemblyError("callrom only works on cx16 target at this time")
|
||||
|
||||
val bank = fcall.args[0].constValue(program)?.number?.toInt()
|
||||
val address = fcall.args[1].constValue(program)?.number?.toInt()
|
||||
if(bank==null || address==null)
|
||||
throw AssemblyError("callrom requires constant arguments")
|
||||
|
||||
if(address !in 0xc000..0xffff)
|
||||
throw AssemblyError("callrom done on address outside of cx16 banked rom")
|
||||
if(bank>=32)
|
||||
throw AssemblyError("callrom bank must be <32")
|
||||
|
||||
val argAddrArg = fcall.args[2]
|
||||
if(argAddrArg.constValue(program)?.number == 0) {
|
||||
asmgen.out("""
|
||||
lda $01
|
||||
pha
|
||||
lda #${bank}
|
||||
sta $01
|
||||
jsr ${address.toHex()}
|
||||
pla
|
||||
sta $01""")
|
||||
} else {
|
||||
when(argAddrArg) {
|
||||
is AddressOf -> {
|
||||
if(argAddrArg.identifier.targetVarDecl(program)?.datatype != DataType.UBYTE)
|
||||
throw AssemblyError("callrom done with 'arg' pointer to variable that's not UBYTE")
|
||||
asmgen.out("""
|
||||
lda $01
|
||||
pha
|
||||
lda #${bank}
|
||||
sta $01
|
||||
lda ${asmgen.asmVariableName(argAddrArg.identifier)}
|
||||
jsr ${address.toHex()}
|
||||
sta ${asmgen.asmVariableName(argAddrArg.identifier)}
|
||||
pla
|
||||
sta $01""")
|
||||
}
|
||||
is NumericLiteralValue -> {
|
||||
asmgen.out("""
|
||||
lda $01
|
||||
pha
|
||||
lda #${bank}
|
||||
sta $01
|
||||
lda ${argAddrArg.number.toHex()}
|
||||
jsr ${address.toHex()}
|
||||
sta ${argAddrArg.number.toHex()}
|
||||
pla
|
||||
sta $01""")
|
||||
}
|
||||
else -> throw AssemblyError("callrom only accepts pointer-of a (ubyte) variable or constant memory address for the 'arg' parameter")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
val dt1 = arg1.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
val dt2 = arg2.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
if(dt1 in ByteDatatypes) {
|
||||
if(dt2 in ByteDatatypes) {
|
||||
when (arg2) {
|
||||
@@ -263,7 +368,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
private fun funcRor2(fcall: IFunctionCall) {
|
||||
val what = fcall.args.single()
|
||||
val dt = what.inferType(program)
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is ArrayIndexedExpression -> {
|
||||
@@ -306,7 +411,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
private fun funcRor(fcall: IFunctionCall) {
|
||||
val what = fcall.args.single()
|
||||
val dt = what.inferType(program)
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is ArrayIndexedExpression -> {
|
||||
@@ -364,7 +469,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
private fun funcRol2(fcall: IFunctionCall) {
|
||||
val what = fcall.args.single()
|
||||
val dt = what.inferType(program)
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is ArrayIndexedExpression -> {
|
||||
@@ -407,7 +512,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
private fun funcRol(fcall: IFunctionCall) {
|
||||
val what = fcall.args.single()
|
||||
val dt = what.inferType(program)
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE -> {
|
||||
when (what) {
|
||||
is ArrayIndexedExpression -> {
|
||||
@@ -481,7 +586,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
translateArguments(fcall.args, func, scope)
|
||||
val dt = fcall.args.single().inferType(program)
|
||||
if(resultToStack) {
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_stack")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_stack")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_stack")
|
||||
@@ -490,7 +595,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
} else {
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE -> asmgen.out(" jsr prog8_lib.func_sign_ub_into_A")
|
||||
DataType.BYTE -> asmgen.out(" jsr prog8_lib.func_sign_b_into_A")
|
||||
DataType.UWORD -> asmgen.out(" jsr prog8_lib.func_sign_uw_into_A")
|
||||
@@ -506,14 +611,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||
val dt = fcall.args.single().inferType(program)
|
||||
if(resultToStack) {
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_stack")
|
||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_stack")
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
} else {
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_A")
|
||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A")
|
||||
@@ -527,7 +632,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||
val dt = fcall.args.single().inferType(program)
|
||||
if(resultToStack) {
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${function.name}_ub_stack")
|
||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${function.name}_b_stack")
|
||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${function.name}_uw_stack")
|
||||
@@ -536,7 +641,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
} else {
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.ARRAY_UB, DataType.STR -> {
|
||||
asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A")
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
||||
@@ -566,7 +671,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
outputAddressAndLenghtOfArray(fcall.args[0])
|
||||
val dt = fcall.args.single().inferType(program)
|
||||
if(resultToStack) {
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_sum_ub_stack")
|
||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_sum_b_stack")
|
||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_sum_uw_stack")
|
||||
@@ -575,7 +680,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
else -> throw AssemblyError("weird type $dt")
|
||||
}
|
||||
} else {
|
||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||
when (dt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.ARRAY_UB, DataType.STR -> {
|
||||
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
|
||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
||||
@@ -645,7 +750,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
|
||||
// optimized simple case: swap two memory locations
|
||||
if(first is DirectMemoryRead && second is DirectMemoryRead) {
|
||||
// TODO optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+1), @(ptr+2))
|
||||
// TODO optimize swap of two memread values with index, using the same pointer expression/variable, like swap(@(ptr+i1), @(ptr+i2))
|
||||
val addr1 = (first.addressExpression as? NumericLiteralValue)?.number?.toHex()
|
||||
val addr2 = (second.addressExpression as? NumericLiteralValue)?.number?.toHex()
|
||||
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null
|
||||
@@ -677,7 +782,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
val elementIDt = first.inferType(program)
|
||||
if(!elementIDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
|
||||
val elementDt = elementIDt.typeOrElse(DataType.UNDEFINED)
|
||||
|
||||
val firstNum = first.indexer.indexExpr as? NumericLiteralValue
|
||||
val firstVar = first.indexer.indexExpr as? IdentifierReference
|
||||
@@ -710,7 +815,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
}
|
||||
|
||||
val datatype = first.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val datatype = first.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
when(datatype) {
|
||||
in ByteDatatypes, in WordDatatypes -> {
|
||||
asmgen.assignExpressionToVariable(first, "P8ZP_SCRATCH_W1", datatype, null)
|
||||
@@ -981,7 +1086,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
|
||||
private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||
translateArguments(fcall.args, func, scope)
|
||||
val dt = fcall.args.single().inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val dt = fcall.args.single().inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
if(resultToStack) {
|
||||
when (dt) {
|
||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b_stack")
|
||||
@@ -1010,14 +1115,6 @@ 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")
|
||||
@@ -1072,18 +1169,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine()!!)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
||||
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
|
||||
asmgen.out("""
|
||||
ldy #$index
|
||||
sta ($varname),y
|
||||
txa
|
||||
iny
|
||||
sta ($varname),y""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
return
|
||||
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine()!!)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
|
||||
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
|
||||
asmgen.out("""
|
||||
ldy #$index
|
||||
sta ($varname),y
|
||||
txa
|
||||
iny
|
||||
sta ($varname),y""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1127,15 +1227,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
is BinaryExpression -> {
|
||||
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
|
||||
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
|
||||
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
|
||||
asmgen.out("""
|
||||
ldy #$index
|
||||
lda ($varname),y
|
||||
pha
|
||||
iny
|
||||
lda ($varname),y
|
||||
tay
|
||||
pla""")
|
||||
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
|
||||
// pointervar is already in the zero page, no need to copy
|
||||
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
|
||||
asmgen.out("""
|
||||
ldy #$index
|
||||
lda ($varname),y
|
||||
pha
|
||||
iny
|
||||
lda ($varname),y
|
||||
tay
|
||||
pla""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
||||
}
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out(" jsr prog8_lib.func_peekw")
|
||||
@@ -1154,7 +1260,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
RegisterOrPair.AY -> {}
|
||||
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
|
||||
RegisterOrPair.XY -> asmgen.out(" tax")
|
||||
in Cx16VirtualRegisters -> asmgen.out(" sta cx16.${resultRegister.toString().toLowerCase()} | sty cx16.${resultRegister.toString().toLowerCase()}+1")
|
||||
in Cx16VirtualRegisters -> asmgen.out(
|
||||
" sta cx16.${
|
||||
resultRegister.toString().lowercase()
|
||||
} | sty cx16.${resultRegister.toString().lowercase()}+1")
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
}
|
||||
@@ -1204,9 +1313,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.out(" sta cx16.${reg.toString().toLowerCase()}")
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||
asmgen.out(" sta cx16.${reg.toString().toLowerCase()}+1")
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid mkword target reg")
|
||||
}
|
||||
@@ -1215,7 +1324,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
|
||||
private fun funcMsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||
if (!arg.inferType(program).isWords())
|
||||
throw AssemblyError("msb required word argument")
|
||||
if (arg is NumericLiteralValue)
|
||||
throw AssemblyError("msb(const) should have been const-folded away")
|
||||
@@ -1259,7 +1368,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
|
||||
private fun funcLsb(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
val arg = fcall.args.single()
|
||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||
if (!arg.inferType(program).isWords())
|
||||
throw AssemblyError("lsb required word argument")
|
||||
if (arg is NumericLiteralValue)
|
||||
throw AssemblyError("lsb(const) should have been const-folded away")
|
||||
@@ -1323,7 +1432,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
|
||||
private fun translateArguments(args: MutableList<Expression>, signature: FSignature, scope: Subroutine?) {
|
||||
val callConv = signature.callConvention(args.map { it.inferType(program).typeOrElse(DataType.STRUCT) })
|
||||
val callConv = signature.callConvention(args.map { it.inferType(program).typeOrElse(DataType.UNDEFINED) })
|
||||
|
||||
fun getSourceForFloat(value: Expression): AsmAssignSource {
|
||||
return when (value) {
|
||||
|
||||
@@ -57,7 +57,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
val idt = left.inferType(program)
|
||||
if(!idt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val dt = idt.typeOrElse(DataType.STRUCT)
|
||||
val dt = idt.typeOrElse(DataType.UNDEFINED)
|
||||
when (operator) {
|
||||
"==" -> {
|
||||
// if the left operand is an expression, and the right is 0, we can just evaluate that expression,
|
||||
@@ -1573,10 +1573,11 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> {
|
||||
asmgen.out("""
|
||||
lda cx16.${reg.registerOrPair.toString().toLowerCase()}
|
||||
asmgen.out(
|
||||
"""
|
||||
lda cx16.${reg.registerOrPair.toString().lowercase()}
|
||||
sta P8ESTACK_LO,x
|
||||
lda cx16.${reg.registerOrPair.toString().toLowerCase()}+1
|
||||
lda cx16.${reg.registerOrPair.toString().lowercase()}+1
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
""")
|
||||
@@ -1625,7 +1626,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
|
||||
private fun translateExpression(typecast: TypecastExpression) {
|
||||
translateExpression(typecast.expression)
|
||||
when(typecast.expression.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||
when(typecast.expression.inferType(program).typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE -> {
|
||||
when(typecast.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> {}
|
||||
@@ -1756,7 +1757,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
|
||||
private fun translateExpression(expr: IdentifierReference) {
|
||||
val varname = asmgen.asmVariableName(expr)
|
||||
when(expr.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||
when(expr.inferType(program).typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" lda $varname | sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
@@ -1780,8 +1781,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||
throw AssemblyError("can't infer type of both expression operands")
|
||||
|
||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
||||
val leftDt = leftIDt.typeOrElse(DataType.UNDEFINED)
|
||||
val rightDt = rightIDt.typeOrElse(DataType.UNDEFINED)
|
||||
// see if we can apply some optimized routines
|
||||
// TODO avoid using evaluation on stack everywhere
|
||||
when(expr.operator) {
|
||||
@@ -2107,7 +2108,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
val itype = expr.inferType(program)
|
||||
if(!itype.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val type = itype.typeOrElse(DataType.STRUCT)
|
||||
val type = itype.typeOrElse(DataType.UNDEFINED)
|
||||
when(expr.operator) {
|
||||
"+" -> {}
|
||||
"-" -> {
|
||||
@@ -2145,7 +2146,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
val elementIDt = arrayExpr.inferType(program)
|
||||
if(!elementIDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val elementDt = elementIDt.typeOrElse(DataType.STRUCT)
|
||||
val elementDt = elementIDt.typeOrElse(DataType.UNDEFINED)
|
||||
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
|
||||
val constIndexNum = arrayExpr.indexer.constIndex()
|
||||
if(constIndexNum!=null) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package prog8.compiler.target.cpu6502.codegen
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ArrayElementTypes
|
||||
import prog8.ast.base.ArrayToElementTypes
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.RegisterOrPair
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
@@ -21,13 +21,13 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
||||
is RangeExpr -> {
|
||||
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
||||
if(range==null) {
|
||||
translateForOverNonconstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as RangeExpr)
|
||||
translateForOverNonconstRange(stmt, iterableDt.typeOrElse(DataType.UNDEFINED), stmt.iterable as RangeExpr)
|
||||
} else {
|
||||
translateForOverConstRange(stmt, iterableDt.typeOrElse(DataType.STRUCT), range)
|
||||
translateForOverConstRange(stmt, iterableDt.typeOrElse(DataType.UNDEFINED), range)
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.STRUCT), stmt.iterable as IdentifierReference)
|
||||
translateForOverIterableVar(stmt, iterableDt.typeOrElse(DataType.UNDEFINED), stmt.iterable as IdentifierReference)
|
||||
}
|
||||
else -> throw AssemblyError("can't iterate over ${stmt.iterable.javaClass} - should have been replaced by a variable")
|
||||
}
|
||||
@@ -40,6 +40,13 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val stepsize=range.step.constValue(program)!!.number.toInt()
|
||||
|
||||
if(stepsize < -1) {
|
||||
val limit = range.to.constValue(program)?.number?.toDouble()
|
||||
if(limit==0.0)
|
||||
throw AssemblyError("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping")
|
||||
}
|
||||
|
||||
when(iterableDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
if (stepsize==1 || stepsize==-1) {
|
||||
@@ -49,8 +56,8 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayElementTypes.getValue(iterableDt), null)
|
||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayElementTypes.getValue(iterableDt), null)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.body)
|
||||
asmgen.out("""
|
||||
@@ -67,8 +74,8 @@ $modifiedLabel cmp #0 ; modified
|
||||
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmVariableName(stmt.loopVar)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayElementTypes.getValue(iterableDt), null)
|
||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayElementTypes.getValue(iterableDt), null)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt), null)
|
||||
asmgen.assignExpressionToVariable(range.to, "$modifiedLabel+1", ArrayToElementTypes.getValue(iterableDt), null)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.body)
|
||||
if(stepsize>0) {
|
||||
@@ -281,7 +288,7 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16 && asmgen.zeropage.available() > 0) {
|
||||
if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
|
||||
// allocate index var on ZP
|
||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||
@@ -320,7 +327,7 @@ $loopLabel sty $indexVar
|
||||
bne $loopLabel
|
||||
beq $endLabel""")
|
||||
}
|
||||
if(length>=16 && asmgen.zeropage.available() > 0) {
|
||||
if(length>=16 && asmgen.zeropage.hasByteAvailable()) {
|
||||
// allocate index var on ZP
|
||||
val zpAddr = asmgen.zeropage.allocate(indexVar, DataType.UBYTE, stmt.position, asmgen.errors)
|
||||
asmgen.out("""$indexVar = $zpAddr ; auto zp UBYTE""")
|
||||
@@ -581,5 +588,5 @@ $loopLabel""")
|
||||
}
|
||||
|
||||
private fun assignLoopvar(stmt: ForLoop, range: RangeExpr) =
|
||||
asmgen.assignExpressionToVariable(range.from, asmgen.asmVariableName(stmt.loopVar), stmt.loopVarDt(program).typeOrElse(DataType.STRUCT), stmt.definingSubroutine())
|
||||
asmgen.assignExpressionToVariable(range.from, asmgen.asmVariableName(stmt.loopVar), stmt.loopVarDt(program).typeOrElse(DataType.UNDEFINED), stmt.definingSubroutine())
|
||||
}
|
||||
@@ -175,21 +175,31 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
when (sub.parameters[argi.index].type) {
|
||||
in ByteDatatypes -> {
|
||||
// only load the lsb of the virtual register
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda P8ESTACK_LO$plusIdxStr,x
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().lowercase()}
|
||||
""")
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
||||
asmgen.out(
|
||||
" stz cx16.${
|
||||
argi.value.second.registerOrPair.toString().lowercase()
|
||||
}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
||||
asmgen.out(
|
||||
" lda #0 | sta cx16.${
|
||||
argi.value.second.registerOrPair.toString().lowercase()
|
||||
}+1")
|
||||
}
|
||||
in WordDatatypes, in IterableDatatypes ->
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda P8ESTACK_LO$plusIdxStr,x
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().lowercase()}
|
||||
lda P8ESTACK_HI$plusIdxStr,x
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1
|
||||
sta cx16.${
|
||||
argi.value.second.registerOrPair.toString().lowercase()
|
||||
}+1
|
||||
""")
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
@@ -244,7 +254,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
val valueIDt = value.inferType(program)
|
||||
if(!valueIDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||
val valueDt = valueIDt.typeOrElse(DataType.UNDEFINED)
|
||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||
throw AssemblyError("argument type incompatible")
|
||||
|
||||
@@ -257,7 +267,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
val valueIDt = value.inferType(program)
|
||||
if(!valueIDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||
val valueDt = valueIDt.typeOrElse(DataType.UNDEFINED)
|
||||
if(!isArgumentTypeCompatible(valueDt, parameter.value.type))
|
||||
throw AssemblyError("argument type incompatible")
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
||||
when {
|
||||
targetIdent!=null -> {
|
||||
val what = asmgen.asmVariableName(targetIdent)
|
||||
when (stmt.target.inferType(program).typeOrElse(DataType.STRUCT)) {
|
||||
when (stmt.target.inferType(program).typeOrElse(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes -> asmgen.out(if (incr) " inc $what" else " dec $what")
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
@@ -65,7 +65,7 @@ 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)
|
||||
val elementDt = targetArrayIdx.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
val constIndex = targetArrayIdx.indexer.constIndex()
|
||||
if(constIndex!=null) {
|
||||
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||
|
||||
@@ -59,7 +59,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val idt = inferType(program)
|
||||
if(!idt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val dt = idt.typeOrElse(DataType.STRUCT)
|
||||
val dt = idt.typeOrElse(DataType.UNDEFINED)
|
||||
when {
|
||||
identifier != null -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, assign.definingSubroutine(), variableAsmName = asmgen.asmVariableName(identifier!!), origAstTarget = this)
|
||||
arrayindexed != null -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, dt, assign.definingSubroutine(), array = arrayindexed, origAstTarget = this)
|
||||
@@ -132,12 +132,12 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
|
||||
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
|
||||
is IdentifierReference -> {
|
||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val dt = value.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
val varName=asmgen.asmVariableName(value)
|
||||
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
||||
if(dt==DataType.UWORD && varName.toLowerCase().startsWith("cx16.r")) {
|
||||
val regStr = varName.toLowerCase().substring(5)
|
||||
val reg = RegisterOrPair.valueOf(regStr.toUpperCase())
|
||||
if(dt == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
|
||||
val regStr = varName.lowercase().substring(5)
|
||||
val reg = RegisterOrPair.valueOf(regStr.uppercase())
|
||||
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, dt, register = reg)
|
||||
} else {
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = varName)
|
||||
@@ -147,7 +147,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
AsmAssignSource(SourceStorageKind.MEMORY, program, asmgen, DataType.UBYTE, memory = value)
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
val dt = value.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val dt = value.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
AsmAssignSource(SourceStorageKind.ARRAY, program, asmgen, dt, array = value)
|
||||
}
|
||||
is FunctionCall -> {
|
||||
@@ -162,7 +162,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val returnType = value.inferType(program)
|
||||
if(!returnType.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.typeOrElse(DataType.STRUCT), expression = value)
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, returnType.typeOrElse(DataType.UNDEFINED), expression = value)
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird call")
|
||||
@@ -173,7 +173,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val dt = value.inferType(program)
|
||||
if(!dt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.typeOrElse(DataType.STRUCT), expression = value)
|
||||
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, dt.typeOrElse(DataType.UNDEFINED), expression = value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,7 +206,7 @@ internal class AsmAssignment(val source: AsmAssignSource,
|
||||
|
||||
init {
|
||||
if(target.register !in setOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
|
||||
require(source.datatype != DataType.STRUCT) { "must not be placeholder datatype" }
|
||||
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype" }
|
||||
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
|
||||
"source storage size must be less or equal to target datatype storage size"
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
when(val value = assign.source.expression!!) {
|
||||
is AddressOf -> {
|
||||
val sourceName = value.identifier.firstStructVarName(program) ?: asmgen.asmVariableName(value.identifier)
|
||||
val sourceName = asmgen.asmSymbolName(value.identifier)
|
||||
assignAddressOf(assign.target, sourceName)
|
||||
}
|
||||
is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber")
|
||||
@@ -217,7 +217,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
val returntype = builtinFunctionReturnType(sub.name, value.args, program)
|
||||
if(!returntype.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
when(returntype.typeOrElse(DataType.STRUCT)) {
|
||||
when(returntype.typeOrElse(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes -> assignRegisterByte(assign.target, CpuRegister.A) // function's byte result is in A
|
||||
in WordDatatypes -> assignRegisterpairWord(assign.target, RegisterOrPair.AY) // function's word result is in AY
|
||||
DataType.STR -> {
|
||||
@@ -299,7 +299,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
val valueIDt = value.inferType(program)
|
||||
if(!valueIDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val valueDt = valueIDt.typeOrElse(DataType.STRUCT)
|
||||
val valueDt = valueIDt.typeOrElse(DataType.UNDEFINED)
|
||||
if(valueDt==targetDt)
|
||||
throw AssemblyError("type cast to identical dt should have been removed")
|
||||
|
||||
@@ -359,7 +359,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
|
||||
// special case optimizations
|
||||
if(target.kind== TargetStorageKind.VARIABLE) {
|
||||
if(value is IdentifierReference && valueDt != DataType.STRUCT)
|
||||
if(value is IdentifierReference && valueDt != DataType.UNDEFINED)
|
||||
return assignTypeCastedIdentifier(target.asmVarname, targetDt, asmgen.asmVariableName(value), valueDt)
|
||||
|
||||
when (valueDt) {
|
||||
@@ -587,13 +587,19 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
DataType.UBYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
|
||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
asmgen.out(
|
||||
" st${
|
||||
regs.toString().lowercase()
|
||||
} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
else
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
asmgen.out(
|
||||
" st${
|
||||
regs.toString().lowercase()
|
||||
} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
when(regs) {
|
||||
@@ -615,13 +621,19 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
DataType.BYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
|
||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
asmgen.out(
|
||||
" st${
|
||||
regs.toString().lowercase()
|
||||
} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
else
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
asmgen.out(
|
||||
" st${
|
||||
regs.toString().lowercase()
|
||||
} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when(regs) {
|
||||
@@ -653,7 +665,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
DataType.UWORD -> {
|
||||
when(targetDt) {
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" st${regs.toString().toLowerCase().first()} $targetAsmVarName")
|
||||
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||
}
|
||||
DataType.WORD, DataType.UWORD -> {
|
||||
when(regs) {
|
||||
@@ -681,7 +693,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
DataType.WORD -> {
|
||||
when(targetDt) {
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" st${regs.toString().toLowerCase().first()} $targetAsmVarName")
|
||||
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||
}
|
||||
DataType.WORD, DataType.UWORD -> {
|
||||
when(regs) {
|
||||
@@ -822,12 +834,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AX -> asmgen.out(" inx | txy | ldx #0 | lda P8ESTACK_LO,y")
|
||||
RegisterOrPair.AY -> asmgen.out(" inx | ldy #0 | lda P8ESTACK_LO,x")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #0
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("can't assign byte from stack to register pair XY")
|
||||
@@ -839,12 +852,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AY-> asmgen.out(" inx | ldy P8ESTACK_HI,x | lda P8ESTACK_LO,x")
|
||||
RegisterOrPair.XY-> throw AssemblyError("can't load X from stack here - use intermediary var? ${target.origAstTarget?.position}")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda P8ESTACK_HI,x
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("can't assign word to single byte register")
|
||||
@@ -888,11 +902,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AY -> asmgen.out(" ldy #>$sourceName | lda #<$sourceName")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #>$sourceName | ldx #<$sourceName")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda #<$sourceName
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #>$sourceName
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("can't load address in a single 8-bit register")
|
||||
@@ -1029,11 +1044,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda $sourceName
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda $sourceName+1
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("can't load word in a single 8-bit register")
|
||||
@@ -1203,11 +1219,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName")
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda $sourceName
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #0
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
@@ -1355,7 +1372,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out(" st${register.name.toLowerCase()} ${target.asmVarname}")
|
||||
asmgen.out(" st${register.name.lowercase()} ${target.asmVarname}")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
when(register) {
|
||||
@@ -1395,7 +1412,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
// only assign a single byte to the virtual register's Lsb
|
||||
asmgen.out(" sta cx16.${target.register.toString().toLowerCase()}")
|
||||
asmgen.out(" sta cx16.${target.register.toString().lowercase()}")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
@@ -1409,7 +1426,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
// only assign a single byte to the virtual register's Lsb
|
||||
asmgen.out(" stx cx16.${target.register.toString().toLowerCase()}")
|
||||
asmgen.out(" stx cx16.${target.register.toString().lowercase()}")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
@@ -1423,7 +1440,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
// only assign a single byte to the virtual register's Lsb
|
||||
asmgen.out(" sty cx16.${target.register.toString().toLowerCase()}")
|
||||
asmgen.out(" sty cx16.${target.register.toString().lowercase()}")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
@@ -1513,9 +1530,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AX -> { }
|
||||
RegisterOrPair.XY -> { asmgen.out(" stx P8ZP_SCRATCH_REG | ldy P8ZP_SCRATCH_REG | tax") }
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
stx cx16.${target.register.toString().toLowerCase()}+1
|
||||
asmgen.out(
|
||||
"""
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
stx cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
||||
@@ -1525,9 +1543,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AX -> { asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
||||
RegisterOrPair.XY -> { asmgen.out(" tax") }
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sty cx16.${target.register.toString().toLowerCase()}+1
|
||||
asmgen.out(
|
||||
"""
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
sty cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
||||
@@ -1537,9 +1556,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AX -> { asmgen.out(" txa | sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
||||
RegisterOrPair.XY -> { }
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
stx cx16.${target.register.toString().toLowerCase()}
|
||||
sty cx16.${target.register.toString().toLowerCase()}+1
|
||||
asmgen.out(
|
||||
"""
|
||||
stx cx16.${target.register.toString().lowercase()}
|
||||
sty cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
||||
@@ -1606,7 +1626,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AY -> asmgen.out(" lda #0 | tay")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()} | stz cx16.${target.register.toString().toLowerCase()}+1")
|
||||
asmgen.out(
|
||||
" stz cx16.${
|
||||
target.register.toString().lowercase()
|
||||
} | stz cx16.${target.register.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid register for word value")
|
||||
}
|
||||
@@ -1656,11 +1679,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AY -> asmgen.out(" ldy #>${word.toHex()} | lda #<${word.toHex()}")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #>${word.toHex()} | ldx #<${word.toHex()}")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda #<${word.toHex()}
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #>${word.toHex()}
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("invalid register for word value")
|
||||
@@ -1707,7 +1731,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()} | stz cx16.${target.register.toString().toLowerCase()}+1")
|
||||
asmgen.out(
|
||||
" stz cx16.${
|
||||
target.register.toString().lowercase()
|
||||
} | stz cx16.${target.register.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
@@ -1747,11 +1774,17 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx #${byte.toHex()}")
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(" lda #${byte.toHex()} | sta cx16.${target.register.toString().toLowerCase()}")
|
||||
asmgen.out(
|
||||
" lda #${byte.toHex()} | sta cx16.${
|
||||
target.register.toString().lowercase()
|
||||
}")
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()}+1\n")
|
||||
asmgen.out(" stz cx16.${target.register.toString().lowercase()}+1\n")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta cx16.${target.register.toString().toLowerCase()}+1\n")
|
||||
asmgen.out(
|
||||
" lda #0 | sta cx16.${
|
||||
target.register.toString().lowercase()
|
||||
}+1\n")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
@@ -1927,11 +1960,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldy ${address.toHex()}")
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda ${address.toHex()}
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #0
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
@@ -1967,10 +2001,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.XY -> asmgen.out(" tax | ldy #0")
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
asmgen.out(
|
||||
"""
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #0
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
|
||||
+49
-19
@@ -25,7 +25,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
val itype = value.inferType(program)
|
||||
if(!itype.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val type = itype.typeOrElse(DataType.STRUCT)
|
||||
val type = itype.typeOrElse(DataType.UNDEFINED)
|
||||
when (value.operator) {
|
||||
"+" -> {}
|
||||
"-" -> inplaceNegate(assign.target, type)
|
||||
@@ -110,7 +110,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
val ident = value as? IdentifierReference
|
||||
val memread = value as? DirectMemoryRead
|
||||
|
||||
when(target.kind) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
@@ -204,7 +204,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
when {
|
||||
indexNum!=null -> {
|
||||
val targetVarName = "${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
|
||||
when(target.datatype) {
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
when {
|
||||
valueLv != null -> inplaceModification_byte_litval_to_variable(targetVarName, target.datatype, operator, valueLv.toInt())
|
||||
@@ -244,7 +244,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
indexVar!=null -> {
|
||||
when(target.datatype) {
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.A, null, program, asmgen)
|
||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||
@@ -280,7 +280,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
val childIDt = value.expression.inferType(program)
|
||||
if(!childIDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val childDt = childIDt.typeOrElse(DataType.STRUCT)
|
||||
val childDt = childIDt.typeOrElse(DataType.UNDEFINED)
|
||||
if (value.type!=DataType.FLOAT && (value.type.equalsSize(childDt) || value.type.largerThan(childDt))) {
|
||||
// this typecast is redundant here; the rest of the code knows how to deal with the uncasted value.
|
||||
// (works for integer types, not for float.)
|
||||
@@ -672,7 +672,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
private fun inplaceModification_byte_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
||||
when(operator) {
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||
asmgen.out("""
|
||||
@@ -689,6 +689,18 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
sbc P8ZP_SCRATCH_B1
|
||||
sta $name""")
|
||||
}
|
||||
"|", "or" -> {
|
||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||
asmgen.out(" ora $name | sta $name")
|
||||
}
|
||||
"&", "and" -> {
|
||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||
asmgen.out(" and $name | sta $name")
|
||||
}
|
||||
"^", "xor" -> {
|
||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||
asmgen.out(" eor $name | sta $name")
|
||||
}
|
||||
// TODO: tuned code for more operators
|
||||
else -> {
|
||||
inplaceModification_byte_value_to_variable(name, dt, operator, memread)
|
||||
@@ -697,7 +709,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
private fun inplaceModification_word_memread_to_variable(name: String, dt: DataType, operator: String, memread: DirectMemoryRead) {
|
||||
when(operator) {
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||
asmgen.out("""
|
||||
@@ -720,6 +732,24 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
dec $name+1
|
||||
+""")
|
||||
}
|
||||
"|", "or" -> {
|
||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||
asmgen.out(" ora $name | sta $name")
|
||||
}
|
||||
"&", "and" -> {
|
||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||
asmgen.out(" and $name | sta $name")
|
||||
if(dt in WordDatatypes) {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz $name+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta $name+1")
|
||||
}
|
||||
}
|
||||
"^", "xor" -> {
|
||||
exprAsmGen.translateDirectMemReadExpression(memread, false)
|
||||
asmgen.out(" eor $name | sta $name")
|
||||
}
|
||||
// TODO: tuned code for more operators
|
||||
else -> {
|
||||
inplaceModification_word_value_to_variable(name, dt, operator, memread)
|
||||
@@ -1225,7 +1255,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
val valueiDt = value.inferType(program)
|
||||
if(!valueiDt.isKnown)
|
||||
throw AssemblyError("unknown dt")
|
||||
val valueDt = valueiDt.typeOrElse(DataType.STRUCT)
|
||||
val valueDt = valueiDt.typeOrElse(DataType.UNDEFINED)
|
||||
|
||||
fun multiplyVarByWordInAY() {
|
||||
asmgen.out("""
|
||||
@@ -1274,7 +1304,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
""")
|
||||
}
|
||||
|
||||
when(valueDt) {
|
||||
when (valueDt) {
|
||||
in ByteDatatypes -> {
|
||||
// the other variable is a BYTE type so optimize for that
|
||||
when (operator) {
|
||||
@@ -1331,21 +1361,21 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
"*" -> {
|
||||
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
|
||||
// TODO use an optimized word * byte multiplication routine
|
||||
// TODO use an optimized word * byte multiplication routine?
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.signExtendAYlsb(valueDt)
|
||||
multiplyVarByWordInAY()
|
||||
}
|
||||
"/" -> {
|
||||
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
|
||||
// TODO use an optimized word / byte divmod routine
|
||||
// TODO use an optimized word / byte divmod routine?
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.signExtendAYlsb(valueDt)
|
||||
divideVarByWordInAY()
|
||||
}
|
||||
"%" -> {
|
||||
// stack contains (u) byte value, sign extend that and proceed with regular 16 bit operation
|
||||
// TODO use an optimized word / byte divmod routine
|
||||
// TODO use an optimized word / byte divmod routine?
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.signExtendAYlsb(valueDt)
|
||||
remainderVarByWordInAY()
|
||||
@@ -1671,7 +1701,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
when (outerCastDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
when(target.kind) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz ${target.asmVarname}+1")
|
||||
@@ -1715,7 +1745,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
private fun inplaceBooleanNot(target: AsmAssignTarget, dt: DataType) {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> {
|
||||
when(target.kind) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda ${target.asmVarname}
|
||||
@@ -1765,7 +1795,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when(target.kind) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda ${target.asmVarname}
|
||||
@@ -1790,7 +1820,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
private fun inplaceInvert(target: AsmAssignTarget, dt: DataType) {
|
||||
when (dt) {
|
||||
DataType.UBYTE -> {
|
||||
when(target.kind) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda ${target.asmVarname}
|
||||
@@ -1831,7 +1861,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when(target.kind) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda ${target.asmVarname}
|
||||
@@ -1869,7 +1899,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when(target.kind) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
@@ -1887,7 +1917,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
when(target.kind) {
|
||||
when (target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
// simply flip the sign bit in the float
|
||||
asmgen.out("""
|
||||
|
||||
@@ -3,7 +3,8 @@ package prog8.optimizer
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.augmentAssignmentOperators
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.walk.AstWalker
|
||||
|
||||
@@ -3,7 +3,6 @@ package prog8.optimizer
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.AddressOf
|
||||
@@ -175,14 +174,14 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
}
|
||||
|
||||
fun unused(decl: VarDecl): Boolean {
|
||||
if(decl.type!=VarDeclType.VAR || decl.datatype==DataType.STRUCT || decl.autogeneratedDontRemove || decl.parent is StructDecl)
|
||||
if(decl.type!=VarDeclType.VAR || decl.autogeneratedDontRemove || decl.sharedWithAsm)
|
||||
return false
|
||||
|
||||
if(decl.definingBlock() !in usedBlocks)
|
||||
return false
|
||||
|
||||
val allReferencedVardecls = allIdentifiersAndTargets.filter { it.value is VarDecl }.map { it.value }.toSet()
|
||||
return decl !in allReferencedVardecls && !nameInAssemblyCode(decl.name)
|
||||
return decl !in allReferencedVardecls // Don't check assembly just for occurrences of variables, if they're not used in prog8 itself, just kill them
|
||||
}
|
||||
|
||||
private fun nameInAssemblyCode(name: String) = allAssemblyNodes.any { it.assembly.contains(name) }
|
||||
|
||||
@@ -106,7 +106,7 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
||||
// optimize various simple cases of ** :
|
||||
// optimize away 1 ** x into just 1 and 0 ** x into just 0
|
||||
// optimize 2 ** x into (1<<x) if both operands are integer.
|
||||
val leftDt = leftconst.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val leftDt = leftconst.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
when (leftconst.number.toDouble()) {
|
||||
0.0 -> {
|
||||
val value = NumericLiteralValue(leftDt, 0, expr.position)
|
||||
@@ -121,11 +121,11 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
||||
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number.toDouble()), expr.position)
|
||||
modifications += IAstModification.ReplaceNode(expr, value, parent)
|
||||
} else {
|
||||
val rightDt = expr.right.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val rightDt = expr.right.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||
val targetDt =
|
||||
when (parent) {
|
||||
is Assignment -> parent.target.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
is Assignment -> parent.target.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
is VarDecl -> parent.datatype
|
||||
else -> leftDt
|
||||
}
|
||||
@@ -187,7 +187,7 @@ internal class ConstantFoldingOptimizer(private val program: Program, private va
|
||||
} else {
|
||||
val arrayDt = array.guessDatatype(program)
|
||||
if (arrayDt.isKnown) {
|
||||
val newArray = array.cast(arrayDt.typeOrElse(DataType.STRUCT))
|
||||
val newArray = array.cast(arrayDt.typeOrElse(DataType.UNDEFINED))
|
||||
if (newArray != null && newArray != array)
|
||||
return listOf(IAstModification.ReplaceNode(array, newArray, parent))
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.ArrayIndex
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.ForLoop
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.compiler.IErrorReporter
|
||||
@@ -30,6 +28,28 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
|
||||
errors.err(x.message, x.position)
|
||||
}
|
||||
|
||||
// move vardecl to the containing subroutine and add initialization assignment in its place if needed
|
||||
if(decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||
val subroutine = decl.definingSubroutine() as? INameScope
|
||||
if(subroutine!=null && subroutine!==parent) {
|
||||
val declValue = decl.value
|
||||
decl.value = null
|
||||
decl.allowInitializeWithZero = false
|
||||
return if (declValue == null) {
|
||||
listOf(
|
||||
IAstModification.Remove(decl, parent as INameScope),
|
||||
IAstModification.InsertFirst(decl, subroutine)
|
||||
)
|
||||
} else {
|
||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||
val assign = Assignment(target, declValue, decl.position)
|
||||
listOf(
|
||||
IAstModification.ReplaceNode(decl, assign, parent),
|
||||
IAstModification.InsertFirst(decl, subroutine)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
@@ -152,7 +172,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
else -> {}
|
||||
}
|
||||
// create the array itself, filled with the fillvalue.
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) }.toTypedArray<Expression>()
|
||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayToElementTypes.getValue(decl.datatype), it, numericLv.position) }.toTypedArray<Expression>()
|
||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
|
||||
}
|
||||
@@ -189,7 +209,6 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
}
|
||||
else -> {
|
||||
// nothing to do for this type
|
||||
// this includes strings and structs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,8 +134,8 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
))
|
||||
}
|
||||
|
||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
||||
val leftDt = leftIDt.typeOrElse(DataType.UNDEFINED)
|
||||
val rightDt = rightIDt.typeOrElse(DataType.UNDEFINED)
|
||||
|
||||
if (expr.operator == "+" || expr.operator == "-"
|
||||
&& leftVal == null && rightVal == null
|
||||
@@ -489,7 +489,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
val idt = expr.inferType(program)
|
||||
if(!idt.isKnown)
|
||||
throw FatalAstException("unknown dt")
|
||||
return NumericLiteralValue(idt.typeOrElse(DataType.STRUCT), 0, expr.position)
|
||||
return NumericLiteralValue(idt.typeOrElse(DataType.UNDEFINED), 0, expr.position)
|
||||
} else if (cv in powersOfTwo) {
|
||||
expr.operator = "&"
|
||||
expr.right = NumericLiteralValue.optimalInteger(cv!!.toInt()-1, expr.position)
|
||||
@@ -513,7 +513,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
val leftIDt = expr.left.inferType(program)
|
||||
if (!leftIDt.isKnown)
|
||||
return null
|
||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||
val leftDt = leftIDt.typeOrElse(DataType.UNDEFINED)
|
||||
when (cv) {
|
||||
-1.0 -> {
|
||||
// '/' -> -left
|
||||
@@ -590,14 +590,14 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
return expr2.left
|
||||
}
|
||||
in powersOfTwo -> {
|
||||
if (leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
||||
if (leftValue.inferType(program).isInteger()) {
|
||||
// times a power of two => shift left
|
||||
val numshifts = log2(cv).toInt()
|
||||
return BinaryExpression(expr2.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
in negativePowersOfTwo -> {
|
||||
if (leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
||||
if (leftValue.inferType(program).isInteger()) {
|
||||
// times a negative power of two => negate, then shift left
|
||||
val numshifts = log2(-cv).toInt()
|
||||
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||
@@ -621,7 +621,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
val targetIDt = expr.left.inferType(program)
|
||||
if(!targetIDt.isKnown)
|
||||
throw FatalAstException("unknown dt")
|
||||
when (val targetDt = targetIDt.typeOrElse(DataType.STRUCT)) {
|
||||
when (val targetDt = targetIDt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
if (amount >= 8) {
|
||||
return NumericLiteralValue(targetDt, 0, expr.position)
|
||||
@@ -656,7 +656,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
val idt = expr.left.inferType(program)
|
||||
if(!idt.isKnown)
|
||||
throw FatalAstException("unknown dt")
|
||||
when (idt.typeOrElse(DataType.STRUCT)) {
|
||||
when (idt.typeOrElse(DataType.UNDEFINED)) {
|
||||
DataType.UBYTE -> {
|
||||
if (amount >= 8) {
|
||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||
|
||||
@@ -27,7 +27,12 @@ internal class StatementOptimizer(private val program: Program,
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
for(returnvar in subsThatNeedReturnVariable) {
|
||||
val decl = VarDecl(VarDeclType.VAR, returnvar.second, ZeropageWish.DONTCARE, null, retvarName, null, null, false, true, returnvar.third)
|
||||
val decl = VarDecl(VarDeclType.VAR, returnvar.second, ZeropageWish.DONTCARE, null, retvarName, null,
|
||||
isArray = false,
|
||||
autogeneratedDontRemove = true,
|
||||
sharedWithAsm = false,
|
||||
position = returnvar.third
|
||||
)
|
||||
returnvar.first.statements.add(0, decl)
|
||||
}
|
||||
subsThatNeedReturnVariable.clear()
|
||||
@@ -44,6 +49,7 @@ internal class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
|
||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
||||
// only do this optimization if the arg is a known-constant string literal instead of a user defined variable.
|
||||
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
|
||||
val arg = functionCallStatement.args.single()
|
||||
val stringVar: IdentifierReference? = if(arg is AddressOf) {
|
||||
@@ -51,30 +57,29 @@ internal class StatementOptimizer(private val program: Program,
|
||||
} else {
|
||||
arg as? IdentifierReference
|
||||
}
|
||||
if(stringVar!=null) {
|
||||
val vardecl = stringVar.targetVarDecl(program)!!
|
||||
val string = vardecl.value as? StringLiteralValue
|
||||
if(stringVar!=null && stringVar.wasStringLiteral(program)) {
|
||||
val string = stringVar.targetVarDecl(program)?.value as? StringLiteralValue
|
||||
if(string!=null) {
|
||||
val pos = functionCallStatement.position
|
||||
if (string.value.length == 1) {
|
||||
val firstCharEncoded = compTarget.encodeString(string.value, string.altEncoding)[0]
|
||||
val chrout = FunctionCallStatement(
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||
} else if (string.value.length == 2) {
|
||||
val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.altEncoding)
|
||||
val chrout1 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val chrout2 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(functionCallStatement, chrout1, parent as INameScope),
|
||||
@@ -333,7 +338,7 @@ internal class StatementOptimizer(private val program: Program,
|
||||
throw FatalAstException("can't infer type of assignment target")
|
||||
|
||||
// optimize binary expressions a bit
|
||||
val targetDt = targetIDt.typeOrElse(DataType.STRUCT)
|
||||
val targetDt = targetIDt.typeOrElse(DataType.UNDEFINED)
|
||||
val bexpr=assignment.value as? BinaryExpression
|
||||
if(bexpr!=null) {
|
||||
val rightCv = bexpr.right.constValue(program)?.number?.toDouble()
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.FunctionCall
|
||||
@@ -52,7 +49,7 @@ internal class UnusedCodeRemover(private val program: Program,
|
||||
|
||||
private fun reportUnreachable(stmt: Statement, parent: INameScope) {
|
||||
when(val next = parent.nextSibling(stmt)) {
|
||||
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine, is StructDecl -> {}
|
||||
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine -> {}
|
||||
else -> errors.warn("unreachable code", next.position)
|
||||
}
|
||||
}
|
||||
@@ -65,7 +62,7 @@ internal class UnusedCodeRemover(private val program: Program,
|
||||
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
|
||||
if("force_output" !in block.options()) {
|
||||
if (block.containsNoCodeNorVars()) {
|
||||
if(block.name != program.internedStringsModuleName)
|
||||
if(block.name != internedStringsModuleName)
|
||||
errors.warn("removing unused block '${block.name}'", block.position)
|
||||
return listOf(IAstModification.Remove(block, parent as INameScope))
|
||||
}
|
||||
@@ -104,12 +101,14 @@ internal class UnusedCodeRemover(private val program: Program,
|
||||
}
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||
if(!forceOutput && !decl.autogeneratedDontRemove && callgraph.unused(decl)) {
|
||||
if(decl.type == VarDeclType.VAR && !decl.definingBlock().isInLibrary)
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
|
||||
return listOf(IAstModification.Remove(decl, decl.definingScope()))
|
||||
if(decl.type==VarDeclType.VAR) {
|
||||
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||
if (!forceOutput && !decl.autogeneratedDontRemove && !decl.sharedWithAsm && !decl.definingBlock().isInLibrary) {
|
||||
if (callgraph.unused(decl)) {
|
||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||
return listOf(IAstModification.Remove(decl, decl.definingScope()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
|
||||
+79
-32
@@ -21,7 +21,6 @@ import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
||||
import prog8.compiler.target.cbm.Petscii
|
||||
import prog8.compiler.target.cx16.CX16MachineDefinition.CX16Zeropage
|
||||
import java.io.CharConversionException
|
||||
import java.nio.file.Path
|
||||
import kotlin.test.*
|
||||
|
||||
@@ -180,28 +179,48 @@ class TestC64Zeropage {
|
||||
fun testZpDontuse() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
|
||||
println(zp.free)
|
||||
assertEquals(0, zp.available())
|
||||
assertEquals(0, zp.availableBytes())
|
||||
assertFailsWith<CompilerException> {
|
||||
zp.allocate("", DataType.BYTE, null, errors)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFreeSpaces() {
|
||||
fun testFreeSpacesBytes() {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||
assertEquals(18, zp1.available())
|
||||
assertEquals(18, zp1.availableBytes())
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
|
||||
assertEquals(85, zp2.available())
|
||||
assertEquals(85, zp2.availableBytes())
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
|
||||
assertEquals(125, zp3.available())
|
||||
assertEquals(125, zp3.availableBytes())
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||
assertEquals(238, zp4.available())
|
||||
assertEquals(238, zp4.availableBytes())
|
||||
zp4.allocate("test", DataType.UBYTE, null, errors)
|
||||
assertEquals(237, zp4.availableBytes())
|
||||
zp4.allocate("test2", DataType.UBYTE, null, errors)
|
||||
assertEquals(236, zp4.availableBytes())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFreeSpacesWords() {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||
assertEquals(6, zp1.availableWords())
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
|
||||
assertEquals(38, zp2.availableWords())
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
|
||||
assertEquals(57, zp3.availableWords())
|
||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||
assertEquals(116, zp4.availableWords())
|
||||
zp4.allocate("test", DataType.UWORD, null, errors)
|
||||
assertEquals(115, zp4.availableWords())
|
||||
zp4.allocate("test2", DataType.UWORD, null, errors)
|
||||
assertEquals(114, zp4.availableWords())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReservedSpace() {
|
||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||
assertEquals(238, zp1.available())
|
||||
assertEquals(238, zp1.availableBytes())
|
||||
assertTrue(50 in zp1.free)
|
||||
assertTrue(100 in zp1.free)
|
||||
assertTrue(49 in zp1.free)
|
||||
@@ -210,7 +229,7 @@ class TestC64Zeropage {
|
||||
assertTrue(255 in zp1.free)
|
||||
assertTrue(199 in zp1.free)
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false, C64Target))
|
||||
assertEquals(139, zp2.available())
|
||||
assertEquals(139, zp2.availableBytes())
|
||||
assertFalse(50 in zp2.free)
|
||||
assertFalse(100 in zp2.free)
|
||||
assertTrue(49 in zp2.free)
|
||||
@@ -223,18 +242,22 @@ class TestC64Zeropage {
|
||||
@Test
|
||||
fun testBasicsafeAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||
assertEquals(18, zp.available())
|
||||
assertEquals(18, zp.availableBytes())
|
||||
assertTrue(zp.hasByteAvailable())
|
||||
assertTrue(zp.hasWordAvailable())
|
||||
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
// in regular zp there aren't 5 sequential bytes free
|
||||
zp.allocate("", DataType.FLOAT, null, errors)
|
||||
}
|
||||
|
||||
for (i in 0 until zp.available()) {
|
||||
for (i in 0 until zp.availableBytes()) {
|
||||
val loc = zp.allocate("", DataType.UBYTE, null, errors)
|
||||
assertTrue(loc > 0)
|
||||
}
|
||||
assertEquals(0, zp.available())
|
||||
assertEquals(0, zp.availableBytes())
|
||||
assertFalse(zp.hasByteAvailable())
|
||||
assertFalse(zp.hasWordAvailable())
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
zp.allocate("", DataType.UBYTE, null, errors)
|
||||
}
|
||||
@@ -246,16 +269,18 @@ class TestC64Zeropage {
|
||||
@Test
|
||||
fun testFullAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||
assertEquals(238, zp.available())
|
||||
assertEquals(238, zp.availableBytes())
|
||||
assertTrue(zp.hasByteAvailable())
|
||||
assertTrue(zp.hasWordAvailable())
|
||||
val loc = zp.allocate("", DataType.UWORD, null, errors)
|
||||
assertTrue(loc > 3)
|
||||
assertFalse(loc in zp.free)
|
||||
val num = zp.available() / 2
|
||||
val num = zp.availableBytes() / 2
|
||||
|
||||
for(i in 0..num-4) {
|
||||
zp.allocate("", DataType.UWORD, null, errors)
|
||||
}
|
||||
assertEquals(6,zp.available())
|
||||
assertEquals(6,zp.availableBytes())
|
||||
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
// can't allocate because no more sequential bytes, only fragmented
|
||||
@@ -266,7 +291,9 @@ class TestC64Zeropage {
|
||||
zp.allocate("", DataType.UBYTE, null, errors)
|
||||
}
|
||||
|
||||
assertEquals(0, zp.available())
|
||||
assertEquals(0, zp.availableBytes())
|
||||
assertFalse(zp.hasByteAvailable())
|
||||
assertFalse(zp.hasWordAvailable())
|
||||
assertFailsWith<ZeropageDepletedError> {
|
||||
// no more space
|
||||
zp.allocate("", DataType.UBYTE, null, errors)
|
||||
@@ -276,7 +303,7 @@ class TestC64Zeropage {
|
||||
@Test
|
||||
fun testEfficientAllocation() {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||
assertEquals(18, zp.available())
|
||||
assertEquals(18, zp.availableBytes())
|
||||
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
||||
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
||||
assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null, errors))
|
||||
@@ -289,7 +316,7 @@ class TestC64Zeropage {
|
||||
assertEquals(0x92, zp.allocate("", DataType.UBYTE, null, errors))
|
||||
assertEquals(0x96, zp.allocate("", DataType.UBYTE, null, errors))
|
||||
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null, errors))
|
||||
assertEquals(0, zp.available())
|
||||
assertEquals(0, zp.availableBytes())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -302,6 +329,8 @@ class TestC64Zeropage {
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class TestCx16Zeropage {
|
||||
private val errors = ErrorReporter()
|
||||
|
||||
@Test
|
||||
fun testReservedLocations() {
|
||||
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, Cx16Target))
|
||||
@@ -309,19 +338,37 @@ class TestCx16Zeropage {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFreeSpaces() {
|
||||
fun testFreeSpacesBytes() {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
|
||||
assertEquals(88, zp1.available())
|
||||
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(175, zp3.available())
|
||||
val zp4 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(216, zp4.available())
|
||||
assertEquals(88, zp1.availableBytes())
|
||||
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(175, zp2.availableBytes())
|
||||
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(216, zp3.availableBytes())
|
||||
zp3.allocate("test", DataType.UBYTE, null, errors)
|
||||
assertEquals(215, zp3.availableBytes())
|
||||
zp3.allocate("test2", DataType.UBYTE, null, errors)
|
||||
assertEquals(214, zp3.availableBytes())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFreeSpacesWords() {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(108, zp1.availableWords())
|
||||
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(87, zp2.availableWords())
|
||||
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
|
||||
assertEquals(44, zp3.availableWords())
|
||||
zp3.allocate("test", DataType.UWORD, null, errors)
|
||||
assertEquals(43, zp3.availableWords())
|
||||
zp3.allocate("test2", DataType.UWORD, null, errors)
|
||||
assertEquals(42, zp3.availableWords())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testReservedSpace() {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
|
||||
assertEquals(216, zp1.available())
|
||||
assertEquals(216, zp1.availableBytes())
|
||||
assertTrue(0x22 in zp1.free)
|
||||
assertTrue(0x80 in zp1.free)
|
||||
assertTrue(0xff in zp1.free)
|
||||
@@ -503,7 +550,7 @@ class TestMemory {
|
||||
|
||||
@Test
|
||||
private fun createTestProgramForMemoryRefViaVar(address: Int, vartype: VarDeclType): AssignTarget {
|
||||
val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val decl = VarDecl(vartype, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
|
||||
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||
@@ -523,7 +570,7 @@ class TestMemory {
|
||||
|
||||
@Test
|
||||
fun testInValidRamC64_variable() {
|
||||
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
|
||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||
@@ -536,7 +583,7 @@ class TestMemory {
|
||||
@Test
|
||||
fun testInValidRamC64_memmap_variable() {
|
||||
val address = 0x1000
|
||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||
@@ -549,7 +596,7 @@ class TestMemory {
|
||||
@Test
|
||||
fun testNotInValidRamC64_memmap_variable() {
|
||||
val address = 0xd020
|
||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||
@@ -561,7 +608,7 @@ class TestMemory {
|
||||
|
||||
@Test
|
||||
fun testInValidRamC64_array() {
|
||||
val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, null, false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.VAR, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
|
||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||
@@ -575,7 +622,7 @@ class TestMemory {
|
||||
@Test
|
||||
fun testInValidRamC64_array_memmapped() {
|
||||
val address = 0x1000
|
||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||
@@ -589,7 +636,7 @@ class TestMemory {
|
||||
@Test
|
||||
fun testNotValidRamC64_array_memmapped() {
|
||||
val address = 0xe000
|
||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, Position.DUMMY)
|
||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||
|
||||
@@ -1,993 +0,0 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import diskio
|
||||
%import string
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
; Prog8 adaptation of the Text-Elite galaxy10 system trading simulation engine.
|
||||
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
perf10 {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet10 in galaxy10 one
|
||||
const ubyte numforZaonce = 129
|
||||
const ubyte numforDiso = 147
|
||||
const ubyte numforRiedquat = 46
|
||||
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
txt.print("\u000c\n --- TextElite v1.1 ---\n")
|
||||
|
||||
galaxy10.travel_to(1, numforLave)
|
||||
market10.init(0) ; Lave's market10 is seeded with 0
|
||||
ship10.init()
|
||||
planet10.display(false)
|
||||
|
||||
repeat {
|
||||
; test_stack.test()
|
||||
|
||||
str input = "????????"
|
||||
txt.print("\nCash: ")
|
||||
util10.print_10s(ship10.cash)
|
||||
txt.print("\nCommand (?=help): ")
|
||||
ubyte num_chars = txt.input_chars(input)
|
||||
txt.nl()
|
||||
if num_chars {
|
||||
when input[0] {
|
||||
'?' -> {
|
||||
txt.print("\nCommands are:\n"+
|
||||
"buy jump info cash >=save\n"+
|
||||
"sell teleport market10 hold <=load\n"+
|
||||
"fuel galhyp local quit\n")
|
||||
}
|
||||
'q' -> break
|
||||
'b' -> trader10.do_buy()
|
||||
's' -> trader10.do_sell()
|
||||
'f' -> trader10.do_fuel()
|
||||
'j' -> trader10.do_jump()
|
||||
't' -> trader10.do_teleport()
|
||||
'g' -> trader10.do_next_galaxy10()
|
||||
'i' -> trader10.do_info()
|
||||
'm' -> trader10.do_show_market10()
|
||||
'l' -> trader10.do_local()
|
||||
'c' -> trader10.do_cash()
|
||||
'h' -> trader10.do_hold()
|
||||
'<' -> trader10.do_load()
|
||||
'>' -> trader10.do_save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trader10 {
|
||||
str Savegame = "↑commander.save"
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
struct SaveData {
|
||||
ubyte galaxy10
|
||||
ubyte planet10
|
||||
ubyte cargo0
|
||||
ubyte cargo1
|
||||
ubyte cargo2
|
||||
ubyte cargo3
|
||||
ubyte cargo4
|
||||
ubyte cargo5
|
||||
ubyte cargo6
|
||||
ubyte cargo7
|
||||
ubyte cargo8
|
||||
ubyte cargo9
|
||||
ubyte cargo10
|
||||
ubyte cargo11
|
||||
ubyte cargo12
|
||||
ubyte cargo13
|
||||
ubyte cargo14
|
||||
ubyte cargo15
|
||||
ubyte cargo16
|
||||
uword cash
|
||||
ubyte max_cargo
|
||||
ubyte fuel
|
||||
}
|
||||
SaveData savedata
|
||||
|
||||
sub do_load() {
|
||||
txt.print("\nLoading universe...")
|
||||
if diskio.load(8, Savegame, &savedata) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
return
|
||||
}
|
||||
|
||||
ship10.cash = savedata.cash
|
||||
ship10.Max_cargo = savedata.max_cargo
|
||||
ship10.fuel = savedata.fuel
|
||||
sys.memcopy(&savedata.cargo0, ship10.cargohold, len(ship10.cargohold))
|
||||
galaxy10.travel_to(savedata.galaxy10, savedata.planet10)
|
||||
|
||||
planet10.display(false)
|
||||
}
|
||||
|
||||
sub do_save() {
|
||||
savedata.galaxy10 = galaxy10.number
|
||||
savedata.planet10 = planet10.number
|
||||
savedata.cash = ship10.cash
|
||||
savedata.max_cargo = ship10.Max_cargo
|
||||
savedata.fuel = ship10.fuel
|
||||
sys.memcopy(ship10.cargohold, &savedata.cargo0, len(ship10.cargohold))
|
||||
|
||||
txt.print("\nSaving universe...")
|
||||
diskio.delete(8, Savegame)
|
||||
if diskio.save(8, Savegame, &savedata, sizeof(savedata)) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub do_jump() {
|
||||
txt.print("\nJump to what system? ")
|
||||
jump_to_system()
|
||||
}
|
||||
|
||||
sub do_teleport() {
|
||||
txt.print("\nCheat! Teleport to what system? ")
|
||||
ubyte fuel = ship10.fuel
|
||||
ship10.fuel = 255
|
||||
jump_to_system()
|
||||
ship10.fuel = fuel
|
||||
}
|
||||
|
||||
sub jump_to_system() {
|
||||
void txt.input_chars(input)
|
||||
ubyte current_planet10 = planet10.number
|
||||
ubyte x = planet10.x
|
||||
ubyte y = planet10.y
|
||||
if galaxy10.search_closest_planet10(input) {
|
||||
ubyte distance = planet10.distance(x, y)
|
||||
if distance <= ship10.fuel {
|
||||
galaxy10.init_market10_for_planet10()
|
||||
ship10.fuel -= distance
|
||||
txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||
planet10.display(true)
|
||||
return
|
||||
}
|
||||
txt.print("Insufficient fuel\n")
|
||||
} else {
|
||||
txt.print(" Not found!\n")
|
||||
}
|
||||
galaxy10.travel_to(galaxy10.number, current_planet10)
|
||||
}
|
||||
|
||||
sub do_buy() {
|
||||
txt.print("\nBuy what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market10.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if market10.current_quantity[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market10.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util10.print_10s(price)
|
||||
if price > ship10.cash {
|
||||
txt.print(" Not enough cash!\n")
|
||||
} else {
|
||||
ship10.cash -= price
|
||||
ship10.cargohold[ci] += amount
|
||||
market10.current_quantity[ci] -= amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_sell() {
|
||||
txt.print("\nSell what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market10.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if ship10.cargohold[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market10.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util10.print_10s(price)
|
||||
ship10.cash += price
|
||||
ship10.cargohold[ci] -= amount
|
||||
market10.current_quantity[ci] += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_fuel() {
|
||||
txt.print("\nBuy fuel. Amount? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||
ubyte max_fuel = ship10.Max_fuel - ship10.fuel
|
||||
if buy_fuel > max_fuel
|
||||
buy_fuel = max_fuel
|
||||
uword price = buy_fuel as uword * ship10.Fuel_cost
|
||||
if price > ship10.cash {
|
||||
txt.print("Not enough cash!\n")
|
||||
} else {
|
||||
ship10.cash -= price
|
||||
ship10.fuel += buy_fuel
|
||||
}
|
||||
}
|
||||
|
||||
sub do_cash() {
|
||||
txt.print("\nCheat! Set cash amount: ")
|
||||
void txt.input_chars(input)
|
||||
ship10.cash = conv.str2uword(input)
|
||||
}
|
||||
|
||||
sub do_hold() {
|
||||
txt.print("\nCheat! Set cargohold size: ")
|
||||
void txt.input_chars(input)
|
||||
ship10.Max_cargo = conv.str2ubyte(input)
|
||||
}
|
||||
|
||||
sub do_next_galaxy10() {
|
||||
galaxy10.travel_to(galaxy10.number+1, planet10.number)
|
||||
planet10.display(false)
|
||||
}
|
||||
|
||||
sub do_info() {
|
||||
txt.print("\nSystem name (empty=current): ")
|
||||
num_chars = txt.input_chars(input)
|
||||
if num_chars {
|
||||
ubyte current_planet10 = planet10.number
|
||||
if galaxy10.search_closest_planet10(input) {
|
||||
planet10.display(false)
|
||||
} else {
|
||||
txt.print(" Not found!")
|
||||
}
|
||||
galaxy10.travel_to(galaxy10.number, current_planet10)
|
||||
} else {
|
||||
planet10.display(false)
|
||||
}
|
||||
}
|
||||
|
||||
sub do_local() {
|
||||
galaxy10.local_area()
|
||||
}
|
||||
|
||||
sub do_show_market10() {
|
||||
market10.display()
|
||||
txt.print("\nFuel: ")
|
||||
util10.print_10s(ship10.fuel)
|
||||
txt.print(" Cargohold space: ")
|
||||
txt.print_ub(ship10.cargo_free())
|
||||
txt.print("t\n")
|
||||
}
|
||||
}
|
||||
|
||||
ship10 {
|
||||
const ubyte Max_fuel = 70
|
||||
const ubyte Fuel_cost = 2
|
||||
ubyte Max_cargo = 20
|
||||
|
||||
ubyte fuel = Max_fuel
|
||||
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
|
||||
ubyte[17] cargohold = 0
|
||||
|
||||
sub init() {
|
||||
sys.memset(cargohold, len(cargohold), 0)
|
||||
}
|
||||
|
||||
sub cargo_free() -> ubyte {
|
||||
ubyte ci
|
||||
ubyte total = 0
|
||||
for ci in 0 to len(cargohold)-1 {
|
||||
if market10.units[ci]==0 ; tonnes only
|
||||
total += cargohold[ci]
|
||||
}
|
||||
return Max_cargo - total
|
||||
}
|
||||
}
|
||||
|
||||
market10 {
|
||||
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
|
||||
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||
|
||||
ubyte[17] current_quantity = 0
|
||||
uword[17] current_price = 0
|
||||
|
||||
sub init(ubyte fluct) {
|
||||
; Prices and availabilities are influenced by the planet10's economy type
|
||||
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||
; commander position to keep the market10 prices constant over gamesaves.
|
||||
; Availabilities must be saved with the game since the player alters them
|
||||
; by buying (and selling(?))
|
||||
;
|
||||
; Almost all operations are one byte only and overflow "errors" are
|
||||
; extremely frequent and exploited.
|
||||
;
|
||||
; Trade Item prices are held internally in a single byte=true value/4.
|
||||
; The decimal point in prices is introduced only when printing them.
|
||||
; Internally, all prices are integers.
|
||||
; The player's cash is held in four bytes.
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
word product
|
||||
byte changing
|
||||
product = planet10.economy as word * gradients[ci]
|
||||
changing = fluct & maskbytes[ci] as byte
|
||||
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||
if q & $80
|
||||
q = 0 ; clip to positive 8-bit
|
||||
current_quantity[ci] = q & $3f
|
||||
q = (baseprices[ci] + changing + product) as ubyte
|
||||
current_price[ci] = q * $0004
|
||||
}
|
||||
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||
}
|
||||
|
||||
sub display() {
|
||||
ubyte ci
|
||||
txt.nl()
|
||||
planet10.print_name_uppercase()
|
||||
txt.print(" trade market10:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||
for ci in 0 to len(names)-1 {
|
||||
util10.print_right(13, names[ci])
|
||||
txt.print(" ")
|
||||
util10.print_10s(current_price[ci])
|
||||
txt.print(" ")
|
||||
txt.print_ub(current_quantity[ci])
|
||||
when units[ci] {
|
||||
0 -> txt.chrout('t')
|
||||
1 -> txt.print("kg")
|
||||
2 -> txt.chrout('g')
|
||||
}
|
||||
txt.print(" ")
|
||||
txt.print_ub(ship10.cargohold[ci])
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub match(uword nameptr) -> ubyte {
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
if util10.prefix_matches(nameptr, names[ci])
|
||||
return ci
|
||||
}
|
||||
return 255
|
||||
}
|
||||
}
|
||||
|
||||
galaxy10 {
|
||||
const uword GALSIZE = 256
|
||||
const uword base0 = $5A4A ; seeds for the first galaxy10
|
||||
const uword base1 = $0248
|
||||
const uword base2 = $B753
|
||||
|
||||
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||
|
||||
ubyte number
|
||||
|
||||
uword[3] seed
|
||||
|
||||
sub init(ubyte galaxy10num) {
|
||||
number = 1
|
||||
planet10.number = 255
|
||||
seed[0] = base0
|
||||
seed[1] = base1
|
||||
seed[2] = base2
|
||||
repeat galaxy10num-1 {
|
||||
nextgalaxy10()
|
||||
}
|
||||
}
|
||||
|
||||
sub nextgalaxy10() {
|
||||
seed[0] = twist(seed[0])
|
||||
seed[1] = twist(seed[1])
|
||||
seed[2] = twist(seed[2])
|
||||
number++
|
||||
if number==9
|
||||
number = 1
|
||||
}
|
||||
|
||||
sub travel_to(ubyte galaxy10num, ubyte system) {
|
||||
init(galaxy10num)
|
||||
generate_next_planet10() ; always at least planet10 0 (separate to avoid repeat ubyte overflow)
|
||||
repeat system {
|
||||
generate_next_planet10()
|
||||
}
|
||||
planet10.name = make_current_planet10_name()
|
||||
init_market10_for_planet10()
|
||||
}
|
||||
|
||||
sub init_market10_for_planet10() {
|
||||
market10.init(lsb(seed[0])+msb(seed[2]))
|
||||
}
|
||||
|
||||
sub search_closest_planet10(uword nameptr) -> ubyte {
|
||||
ubyte x = planet10.x
|
||||
ubyte y = planet10.y
|
||||
ubyte current_planet10_num = planet10.number
|
||||
|
||||
init(number)
|
||||
ubyte found = false
|
||||
ubyte current_closest_pi
|
||||
ubyte current_distance = 127
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet10()
|
||||
planet10.name = make_current_planet10_name()
|
||||
if util10.prefix_matches(nameptr, planet10.name) {
|
||||
ubyte distance = planet10.distance(x, y)
|
||||
if distance < current_distance {
|
||||
current_distance = distance
|
||||
current_closest_pi = pi
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found
|
||||
travel_to(number, current_closest_pi)
|
||||
else
|
||||
travel_to(number, current_planet10_num)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
sub local_area() {
|
||||
ubyte current_planet10 = planet10.number
|
||||
ubyte px = planet10.x
|
||||
ubyte py = planet10.y
|
||||
ubyte pn = 0
|
||||
|
||||
init(number)
|
||||
txt.print("\nGalaxy #")
|
||||
txt.print_ub(number)
|
||||
txt.print(" - systems in vicinity:\n")
|
||||
do {
|
||||
generate_next_planet10()
|
||||
ubyte distance = planet10.distance(px, py)
|
||||
if distance <= ship10.Max_fuel {
|
||||
if distance <= ship10.fuel
|
||||
txt.chrout('*')
|
||||
else
|
||||
txt.chrout('-')
|
||||
txt.spc()
|
||||
planet10.name = make_current_planet10_name()
|
||||
planet10.display(true)
|
||||
txt.print(" (")
|
||||
util10.print_10s(distance)
|
||||
txt.print(" LY)\n")
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
travel_to(number, current_planet10)
|
||||
}
|
||||
|
||||
ubyte pn_pair1
|
||||
ubyte pn_pair2
|
||||
ubyte pn_pair3
|
||||
ubyte pn_pair4
|
||||
ubyte longname
|
||||
|
||||
sub generate_next_planet10() {
|
||||
determine_planet10_properties()
|
||||
longname = lsb(seed[0]) & 64
|
||||
|
||||
; Always four iterations of random number
|
||||
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
}
|
||||
|
||||
sub make_current_planet10_name() -> str {
|
||||
ubyte ni = 0
|
||||
str name = " " ; max 8
|
||||
|
||||
if pn_pairs[pn_pair1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair1+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3+1]
|
||||
ni++
|
||||
}
|
||||
|
||||
if longname {
|
||||
if pn_pairs[pn_pair4] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair4+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4+1]
|
||||
ni++
|
||||
}
|
||||
}
|
||||
|
||||
name[ni] = 0
|
||||
return name
|
||||
}
|
||||
|
||||
sub determine_planet10_properties() {
|
||||
; create the planet10's characteristics
|
||||
planet10.number++
|
||||
planet10.x = msb(seed[1])
|
||||
planet10.y = msb(seed[0])
|
||||
planet10.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||
planet10.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||
if planet10.govtype <= 1
|
||||
planet10.economy = (planet10.economy | 2)
|
||||
planet10.techlevel = (msb(seed[1]) & 3) + (planet10.economy ^ 7)
|
||||
planet10.techlevel += planet10.govtype >> 1
|
||||
if planet10.govtype & 1
|
||||
planet10.techlevel++
|
||||
planet10.population = 4 * planet10.techlevel + planet10.economy
|
||||
planet10.population += planet10.govtype + 1
|
||||
planet10.productivity = ((planet10.economy ^ 7) + 3) * (planet10.govtype + 4)
|
||||
planet10.productivity *= planet10.population * 8
|
||||
ubyte seed2_msb = msb(seed[2])
|
||||
planet10.radius = mkword((seed2_msb & 15) + 11, planet10.x)
|
||||
planet10.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
|
||||
if planet10.species_is_alien {
|
||||
planet10.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||
planet10.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||
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[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() {
|
||||
uword temp = seed[0] + seed[1] + seed[2]
|
||||
seed[0] = seed[1]
|
||||
seed[1] = seed[2]
|
||||
seed[2] = temp
|
||||
}
|
||||
|
||||
sub twist(uword x) -> uword {
|
||||
ubyte xh = msb(x)
|
||||
ubyte xl = lsb(x)
|
||||
rol(xh)
|
||||
rol(xl)
|
||||
return mkword(xh, xl)
|
||||
}
|
||||
|
||||
sub debug_seed() {
|
||||
txt.print("\ngalaxy10 #")
|
||||
txt.print_ub(number)
|
||||
txt.print("\ngalaxy10 seed0=")
|
||||
txt.print_uwhex(galaxy10.seed[0], true)
|
||||
txt.print("\ngalaxy10 seed1=")
|
||||
txt.print_uwhex(galaxy10.seed[1], true)
|
||||
txt.print("\ngalaxy10 seed2=")
|
||||
txt.print_uwhex(galaxy10.seed[2], true)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
planet10 {
|
||||
%option force_output
|
||||
|
||||
str[] species_sizes = ["Large", "Fierce", "Small"]
|
||||
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
|
||||
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
|
||||
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
|
||||
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship10", "Communist", "Confederacy", "Democracy", "Corporate State"]
|
||||
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
|
||||
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
|
||||
|
||||
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||
str[] words8F = ["\xB0", "The planet10 \xB0", "The world \xB0", "This planet10", "This world"]
|
||||
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||
str[] words91 = ["planet10", "world", "place", "little planet10", "dump"]
|
||||
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||
|
||||
uword[] wordlists = [
|
||||
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||
|
||||
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||
|
||||
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||
|
||||
str name = " " ; 8 max
|
||||
ubyte number ; starts at 0 in new galaxy10, then increases by 1 for each generated planet10
|
||||
ubyte x
|
||||
ubyte y
|
||||
ubyte economy
|
||||
ubyte govtype
|
||||
ubyte techlevel
|
||||
ubyte population
|
||||
uword productivity
|
||||
uword radius
|
||||
ubyte species_is_alien ; otherwise "Human Colonials"
|
||||
ubyte species_size
|
||||
ubyte species_color
|
||||
ubyte species_look
|
||||
ubyte species_kind
|
||||
|
||||
sub set_seed(uword s1, uword s2) {
|
||||
goatsoup_seed[0] = lsb(s1)
|
||||
goatsoup_seed[1] = msb(s1)
|
||||
goatsoup_seed[2] = lsb(s2)
|
||||
goatsoup_seed[3] = msb(s2)
|
||||
reset_rnd()
|
||||
}
|
||||
|
||||
sub reset_rnd() {
|
||||
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||
}
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str name = " " ; 8 chars max
|
||||
ubyte nx = 0
|
||||
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||
ubyte x = goatsoup_rnd_number() & $3e
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
x++
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
}
|
||||
name[nx] = 0
|
||||
name[0] |= 32 ; uppercase first letter
|
||||
return name
|
||||
}
|
||||
|
||||
sub goatsoup_rnd_number() -> ubyte {
|
||||
ubyte x = goatsoup_rnd[0] * 2
|
||||
uword a = x as uword + goatsoup_rnd[2]
|
||||
if goatsoup_rnd[0] > 127
|
||||
a ++
|
||||
goatsoup_rnd[0] = lsb(a)
|
||||
goatsoup_rnd[2] = x
|
||||
x = goatsoup_rnd[1]
|
||||
ubyte ac = x + goatsoup_rnd[3] + msb(a)
|
||||
goatsoup_rnd[1] = ac
|
||||
goatsoup_rnd[3] = x
|
||||
return ac
|
||||
}
|
||||
|
||||
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||
uword ax
|
||||
uword ay
|
||||
if px>x
|
||||
ax=px-x
|
||||
else
|
||||
ax=x-px
|
||||
if py>y
|
||||
ay=py-y
|
||||
else
|
||||
ay=y-py
|
||||
ay /= 2
|
||||
ubyte d = sqrt16(ax*ax + ay*ay)
|
||||
if d>63
|
||||
return 255
|
||||
return d*4
|
||||
}
|
||||
|
||||
sub soup() -> str {
|
||||
str planet10_result = " " * 160
|
||||
uword[6] source_stack
|
||||
ubyte stack_ptr = 0
|
||||
str start_source = "\x8F is \x97."
|
||||
uword source_ptr = &start_source
|
||||
uword result_ptr = &planet10_result
|
||||
|
||||
reset_rnd()
|
||||
recursive_soup()
|
||||
return planet10_result
|
||||
|
||||
sub recursive_soup() {
|
||||
repeat {
|
||||
ubyte c = @(source_ptr)
|
||||
source_ptr++
|
||||
if c == $00 {
|
||||
@(result_ptr) = 0
|
||||
return
|
||||
}
|
||||
else if c <= $80 {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
else {
|
||||
if c <= $a4 {
|
||||
ubyte rnr = goatsoup_rnd_number()
|
||||
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
|
||||
source_stack[stack_ptr] = source_ptr
|
||||
stack_ptr++
|
||||
source_ptr = getword(c, wordNr)
|
||||
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||
stack_ptr--
|
||||
source_ptr = source_stack[stack_ptr]
|
||||
} else {
|
||||
if c == $b0 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
concat_string(&name + 1)
|
||||
}
|
||||
else if c == $b1 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
ubyte ni
|
||||
for ni in 1 to len(name) {
|
||||
ubyte cc = name[ni]
|
||||
if cc=='e' or cc=='o' or cc==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = cc
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
@(result_ptr) = 'i'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'a'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'n'
|
||||
result_ptr++
|
||||
}
|
||||
else if c == $b2 {
|
||||
concat_string(random_name())
|
||||
}
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub concat_string(uword str_ptr) {
|
||||
repeat {
|
||||
ubyte c = @(str_ptr)
|
||||
if c==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
str_ptr++
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub display(ubyte compressed) {
|
||||
if compressed {
|
||||
print_name_uppercase()
|
||||
txt.print(" TL:")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.spc()
|
||||
txt.print(econnames[economy])
|
||||
txt.spc()
|
||||
txt.print(govnames[govtype])
|
||||
} else {
|
||||
txt.print("\n\nSystem: ")
|
||||
print_name_uppercase()
|
||||
txt.print("\nPosition: ")
|
||||
txt.print_ub(x)
|
||||
txt.chrout('\'')
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
txt.chrout('#')
|
||||
txt.print_ub(number)
|
||||
txt.print("\nEconomy: ")
|
||||
txt.print(econnames[economy])
|
||||
txt.print("\nGovernment: ")
|
||||
txt.print(govnames[govtype])
|
||||
txt.print("\nTech Level: ")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.print("\nTurnover: ")
|
||||
txt.print_uw(productivity)
|
||||
txt.print("\nRadius: ")
|
||||
txt.print_uw(radius)
|
||||
txt.print("\nPopulation: ")
|
||||
txt.print_ub(population >> 3)
|
||||
txt.print(" Billion\nSpecies: ")
|
||||
if species_is_alien {
|
||||
if species_size < len(species_sizes) {
|
||||
txt.print(species_sizes[species_size])
|
||||
txt.spc()
|
||||
}
|
||||
if species_color < len(species_colors) {
|
||||
txt.print(species_colors[species_color])
|
||||
txt.spc()
|
||||
}
|
||||
if species_look < len(species_looks) {
|
||||
txt.print(species_looks[species_look])
|
||||
txt.spc()
|
||||
}
|
||||
if species_kind < len(species_kinds) {
|
||||
txt.print(species_kinds[species_kind])
|
||||
}
|
||||
} else {
|
||||
txt.print("Human Colonials")
|
||||
}
|
||||
txt.nl()
|
||||
txt.print(soup())
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub print_name_uppercase() {
|
||||
ubyte c
|
||||
for c in name
|
||||
txt.chrout(c | 32)
|
||||
}
|
||||
|
||||
asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY {
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc #$81
|
||||
asl a
|
||||
tay
|
||||
lda wordlists,y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda wordlists+1,y
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
util10 {
|
||||
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
|
||||
repeat {
|
||||
ubyte pc = @(prefixptr)
|
||||
ubyte sc = @(stringptr)
|
||||
if pc == 0
|
||||
return true
|
||||
; to lowercase for case insensitive compare:
|
||||
pc &= 127
|
||||
sc &= 127
|
||||
if pc != sc
|
||||
return false
|
||||
prefixptr++
|
||||
stringptr++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub print_right(ubyte width, uword st) {
|
||||
repeat width - string.length(st) {
|
||||
txt.spc()
|
||||
}
|
||||
txt.print(st)
|
||||
}
|
||||
|
||||
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
lda conv.uword2decimal.decTenThousands
|
||||
ldy #0 ; have we started printing?
|
||||
cmp #'0'
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decThousands
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decHundreds
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decTens
|
||||
jsr c64.CHROUT
|
||||
lda #'.'
|
||||
jsr c64.CHROUT
|
||||
lda conv.uword2decimal.decOnes
|
||||
jsr c64.CHROUT
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,994 +0,0 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import diskio
|
||||
%import string
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
; Prog8 adaptation of the Text-Elite galaxy2 system trading simulation engine.
|
||||
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
perf2 {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet2 in galaxy2 one
|
||||
const ubyte numforZaonce = 129
|
||||
const ubyte numforDiso = 147
|
||||
const ubyte numforRiedquat = 46
|
||||
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
txt.print("\u000c\n --- TextElite v1.1 ---\n")
|
||||
|
||||
galaxy2.travel_to(1, numforLave)
|
||||
market2.init(0) ; Lave's market2 is seeded with 0
|
||||
ship2.init()
|
||||
planet2.display(false)
|
||||
|
||||
repeat {
|
||||
; test_stack.test()
|
||||
|
||||
str input = "????????"
|
||||
txt.print("\nCash: ")
|
||||
util2.print_10s(ship2.cash)
|
||||
txt.print("\nCommand (?=help): ")
|
||||
ubyte num_chars = txt.input_chars(input)
|
||||
txt.nl()
|
||||
if num_chars {
|
||||
when input[0] {
|
||||
'?' -> {
|
||||
txt.print("\nCommands are:\n"+
|
||||
"buy jump info cash >=save\n"+
|
||||
"sell teleport market2 hold <=load\n"+
|
||||
"fuel galhyp local quit\n")
|
||||
}
|
||||
'q' -> break
|
||||
'b' -> trader2.do_buy()
|
||||
's' -> trader2.do_sell()
|
||||
'f' -> trader2.do_fuel()
|
||||
'j' -> trader2.do_jump()
|
||||
't' -> trader2.do_teleport()
|
||||
'g' -> trader2.do_next_galaxy2()
|
||||
'i' -> trader2.do_info()
|
||||
'm' -> trader2.do_show_market2()
|
||||
'l' -> trader2.do_local()
|
||||
'c' -> trader2.do_cash()
|
||||
'h' -> trader2.do_hold()
|
||||
'<' -> trader2.do_load()
|
||||
'>' -> trader2.do_save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trader2 {
|
||||
str Savegame = "↑commander.save"
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
struct SaveData {
|
||||
ubyte galaxy2
|
||||
ubyte planet2
|
||||
ubyte cargo0
|
||||
ubyte cargo1
|
||||
ubyte cargo2
|
||||
ubyte cargo3
|
||||
ubyte cargo4
|
||||
ubyte cargo5
|
||||
ubyte cargo6
|
||||
ubyte cargo7
|
||||
ubyte cargo8
|
||||
ubyte cargo9
|
||||
ubyte cargo10
|
||||
ubyte cargo11
|
||||
ubyte cargo12
|
||||
ubyte cargo13
|
||||
ubyte cargo14
|
||||
ubyte cargo15
|
||||
ubyte cargo16
|
||||
uword cash
|
||||
ubyte max_cargo
|
||||
ubyte fuel
|
||||
}
|
||||
SaveData savedata
|
||||
|
||||
sub do_load() {
|
||||
txt.print("\nLoading universe...")
|
||||
if diskio.load(8, Savegame, &savedata) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
return
|
||||
}
|
||||
|
||||
ship2.cash = savedata.cash
|
||||
ship2.Max_cargo = savedata.max_cargo
|
||||
ship2.fuel = savedata.fuel
|
||||
sys.memcopy(&savedata.cargo0, ship2.cargohold, len(ship2.cargohold))
|
||||
galaxy2.travel_to(savedata.galaxy2, savedata.planet2)
|
||||
|
||||
planet2.display(false)
|
||||
}
|
||||
|
||||
sub do_save() {
|
||||
savedata.galaxy2 = galaxy2.number
|
||||
savedata.planet2 = planet2.number
|
||||
savedata.cash = ship2.cash
|
||||
savedata.max_cargo = ship2.Max_cargo
|
||||
savedata.fuel = ship2.fuel
|
||||
sys.memcopy(ship2.cargohold, &savedata.cargo0, len(ship2.cargohold))
|
||||
|
||||
txt.print("\nSaving universe...")
|
||||
diskio.delete(8, Savegame)
|
||||
if diskio.save(8, Savegame, &savedata, sizeof(savedata)) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub do_jump() {
|
||||
txt.print("\nJump to what system? ")
|
||||
jump_to_system()
|
||||
}
|
||||
|
||||
sub do_teleport() {
|
||||
txt.print("\nCheat! Teleport to what system? ")
|
||||
ubyte fuel = ship2.fuel
|
||||
ship2.fuel = 255
|
||||
jump_to_system()
|
||||
ship2.fuel = fuel
|
||||
}
|
||||
|
||||
sub jump_to_system() {
|
||||
void txt.input_chars(input)
|
||||
ubyte current_planet2 = planet2.number
|
||||
ubyte x = planet2.x
|
||||
ubyte y = planet2.y
|
||||
if galaxy2.search_closest_planet2(input) {
|
||||
ubyte distance = planet2.distance(x, y)
|
||||
if distance <= ship2.fuel {
|
||||
galaxy2.init_market2_for_planet2()
|
||||
ship2.fuel -= distance
|
||||
txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||
planet2.display(true)
|
||||
return
|
||||
}
|
||||
txt.print("Insufficient fuel\n")
|
||||
} else {
|
||||
txt.print(" Not found!\n")
|
||||
}
|
||||
galaxy2.travel_to(galaxy2.number, current_planet2)
|
||||
}
|
||||
|
||||
sub do_buy() {
|
||||
txt.print("\nBuy what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market2.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if market2.current_quantity[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market2.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util2.print_10s(price)
|
||||
if price > ship2.cash {
|
||||
txt.print(" Not enough cash!\n")
|
||||
} else {
|
||||
ship2.cash -= price
|
||||
ship2.cargohold[ci] += amount
|
||||
market2.current_quantity[ci] -= amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_sell() {
|
||||
txt.print("\nSell what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market2.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if ship2.cargohold[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market2.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util2.print_10s(price)
|
||||
ship2.cash += price
|
||||
ship2.cargohold[ci] -= amount
|
||||
market2.current_quantity[ci] += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_fuel() {
|
||||
txt.print("\nBuy fuel. Amount? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||
ubyte max_fuel = ship2.Max_fuel - ship2.fuel
|
||||
if buy_fuel > max_fuel
|
||||
buy_fuel = max_fuel
|
||||
uword price = buy_fuel as uword * ship2.Fuel_cost
|
||||
if price > ship2.cash {
|
||||
txt.print("Not enough cash!\n")
|
||||
} else {
|
||||
ship2.cash -= price
|
||||
ship2.fuel += buy_fuel
|
||||
}
|
||||
}
|
||||
|
||||
sub do_cash() {
|
||||
txt.print("\nCheat! Set cash amount: ")
|
||||
void txt.input_chars(input)
|
||||
ship2.cash = conv.str2uword(input)
|
||||
}
|
||||
|
||||
sub do_hold() {
|
||||
txt.print("\nCheat! Set cargohold size: ")
|
||||
void txt.input_chars(input)
|
||||
ship2.Max_cargo = conv.str2ubyte(input)
|
||||
}
|
||||
|
||||
sub do_next_galaxy2() {
|
||||
galaxy2.travel_to(galaxy2.number+1, planet2.number)
|
||||
planet2.display(false)
|
||||
}
|
||||
|
||||
sub do_info() {
|
||||
txt.print("\nSystem name (empty=current): ")
|
||||
num_chars = txt.input_chars(input)
|
||||
if num_chars {
|
||||
ubyte current_planet2 = planet2.number
|
||||
if galaxy2.search_closest_planet2(input) {
|
||||
planet2.display(false)
|
||||
} else {
|
||||
txt.print(" Not found!")
|
||||
}
|
||||
galaxy2.travel_to(galaxy2.number, current_planet2)
|
||||
} else {
|
||||
planet2.display(false)
|
||||
}
|
||||
}
|
||||
|
||||
sub do_local() {
|
||||
galaxy2.local_area()
|
||||
}
|
||||
|
||||
sub do_show_market2() {
|
||||
market2.display()
|
||||
txt.print("\nFuel: ")
|
||||
util2.print_10s(ship2.fuel)
|
||||
txt.print(" Cargohold space: ")
|
||||
txt.print_ub(ship2.cargo_free())
|
||||
txt.print("t\n")
|
||||
}
|
||||
}
|
||||
|
||||
ship2 {
|
||||
const ubyte Max_fuel = 70
|
||||
const ubyte Fuel_cost = 2
|
||||
ubyte Max_cargo = 20
|
||||
|
||||
ubyte fuel = Max_fuel
|
||||
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
|
||||
ubyte[17] cargohold = 0
|
||||
|
||||
sub init() {
|
||||
sys.memset(cargohold, len(cargohold), 0)
|
||||
}
|
||||
|
||||
sub cargo_free() -> ubyte {
|
||||
ubyte ci
|
||||
ubyte total = 0
|
||||
for ci in 0 to len(cargohold)-1 {
|
||||
if market2.units[ci]==0 ; tonnes only
|
||||
total += cargohold[ci]
|
||||
}
|
||||
return Max_cargo - total
|
||||
}
|
||||
}
|
||||
|
||||
market2 {
|
||||
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
|
||||
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||
|
||||
ubyte[17] current_quantity = 0
|
||||
uword[17] current_price = 0
|
||||
|
||||
sub init(ubyte fluct) {
|
||||
; Prices and availabilities are influenced by the planet2's economy type
|
||||
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||
; commander position to keep the market2 prices constant over gamesaves.
|
||||
; Availabilities must be saved with the game since the player alters them
|
||||
; by buying (and selling(?))
|
||||
;
|
||||
; Almost all operations are one byte only and overflow "errors" are
|
||||
; extremely frequent and exploited.
|
||||
;
|
||||
; Trade Item prices are held internally in a single byte=true value/4.
|
||||
; The decimal point in prices is introduced only when printing them.
|
||||
; Internally, all prices are integers.
|
||||
; The player's cash is held in four bytes.
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
word product
|
||||
byte changing
|
||||
product = planet2.economy as word * gradients[ci]
|
||||
changing = fluct & maskbytes[ci] as byte
|
||||
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||
if q & $80
|
||||
q = 0 ; clip to positive 8-bit
|
||||
current_quantity[ci] = q & $3f
|
||||
q = (baseprices[ci] + changing + product) as ubyte
|
||||
current_price[ci] = q * $0004
|
||||
}
|
||||
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||
}
|
||||
|
||||
sub display() {
|
||||
ubyte ci
|
||||
txt.nl()
|
||||
planet2.print_name_uppercase()
|
||||
txt.print(" trade market2:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||
for ci in 0 to len(names)-1 {
|
||||
util2.print_right(13, names[ci])
|
||||
txt.print(" ")
|
||||
util2.print_10s(current_price[ci])
|
||||
txt.print(" ")
|
||||
txt.print_ub(current_quantity[ci])
|
||||
when units[ci] {
|
||||
0 -> txt.chrout('t')
|
||||
1 -> txt.print("kg")
|
||||
2 -> txt.chrout('g')
|
||||
}
|
||||
txt.print(" ")
|
||||
txt.print_ub(ship2.cargohold[ci])
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub match(uword nameptr) -> ubyte {
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
if util2.prefix_matches(nameptr, names[ci])
|
||||
return ci
|
||||
}
|
||||
return 255
|
||||
}
|
||||
}
|
||||
|
||||
galaxy2 {
|
||||
const uword GALSIZE = 256
|
||||
const uword base0 = $5A4A ; seeds for the first galaxy2
|
||||
const uword base1 = $0248
|
||||
const uword base2 = $B753
|
||||
|
||||
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||
|
||||
ubyte number
|
||||
|
||||
uword[3] seed
|
||||
|
||||
sub init(ubyte galaxy2num) {
|
||||
number = 1
|
||||
planet2.number = 255
|
||||
seed[0] = base0
|
||||
seed[1] = base1
|
||||
seed[2] = base2
|
||||
repeat galaxy2num-1 {
|
||||
nextgalaxy2()
|
||||
}
|
||||
}
|
||||
|
||||
sub nextgalaxy2() {
|
||||
seed[0] = twist(seed[0])
|
||||
seed[1] = twist(seed[1])
|
||||
seed[2] = twist(seed[2])
|
||||
number++
|
||||
if number==9
|
||||
number = 1
|
||||
}
|
||||
|
||||
sub travel_to(ubyte galaxy2num, ubyte system) {
|
||||
init(galaxy2num)
|
||||
generate_next_planet2() ; always at least planet2 0 (separate to avoid repeat ubyte overflow)
|
||||
repeat system {
|
||||
generate_next_planet2()
|
||||
}
|
||||
planet2.name = make_current_planet2_name()
|
||||
init_market2_for_planet2()
|
||||
}
|
||||
|
||||
sub init_market2_for_planet2() {
|
||||
market2.init(lsb(seed[0])+msb(seed[2]))
|
||||
}
|
||||
|
||||
sub search_closest_planet2(uword nameptr) -> ubyte {
|
||||
ubyte x = planet2.x
|
||||
ubyte y = planet2.y
|
||||
ubyte current_planet2_num = planet2.number
|
||||
|
||||
init(number)
|
||||
ubyte found = false
|
||||
ubyte current_closest_pi
|
||||
ubyte current_distance = 127
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet2()
|
||||
planet2.name = make_current_planet2_name()
|
||||
if util2.prefix_matches(nameptr, planet2.name) {
|
||||
ubyte distance = planet2.distance(x, y)
|
||||
if distance < current_distance {
|
||||
current_distance = distance
|
||||
current_closest_pi = pi
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found
|
||||
travel_to(number, current_closest_pi)
|
||||
else
|
||||
travel_to(number, current_planet2_num)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
sub local_area() {
|
||||
ubyte current_planet2 = planet2.number
|
||||
ubyte px = planet2.x
|
||||
ubyte py = planet2.y
|
||||
ubyte pn = 0
|
||||
|
||||
init(number)
|
||||
txt.print("\nGalaxy #")
|
||||
txt.print_ub(number)
|
||||
txt.print(" - systems in vicinity:\n")
|
||||
do {
|
||||
generate_next_planet2()
|
||||
ubyte distance = planet2.distance(px, py)
|
||||
if distance <= ship2.Max_fuel {
|
||||
if distance <= ship2.fuel
|
||||
txt.chrout('*')
|
||||
else
|
||||
txt.chrout('-')
|
||||
txt.spc()
|
||||
planet2.name = make_current_planet2_name()
|
||||
planet2.display(true)
|
||||
txt.print(" (")
|
||||
util2.print_10s(distance)
|
||||
txt.print(" LY)\n")
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
travel_to(number, current_planet2)
|
||||
}
|
||||
|
||||
ubyte pn_pair1
|
||||
ubyte pn_pair2
|
||||
ubyte pn_pair3
|
||||
ubyte pn_pair4
|
||||
ubyte longname
|
||||
|
||||
sub generate_next_planet2() {
|
||||
determine_planet2_properties()
|
||||
longname = lsb(seed[0]) & 64
|
||||
|
||||
; Always four iterations of random number
|
||||
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
}
|
||||
|
||||
sub make_current_planet2_name() -> str {
|
||||
ubyte ni = 0
|
||||
str name = " " ; max 8
|
||||
|
||||
if pn_pairs[pn_pair1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair1+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3+1]
|
||||
ni++
|
||||
}
|
||||
|
||||
if longname {
|
||||
if pn_pairs[pn_pair4] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair4+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4+1]
|
||||
ni++
|
||||
}
|
||||
}
|
||||
|
||||
name[ni] = 0
|
||||
return name
|
||||
}
|
||||
|
||||
sub determine_planet2_properties() {
|
||||
; create the planet2's characteristics
|
||||
planet2.number++
|
||||
planet2.x = msb(seed[1])
|
||||
planet2.y = msb(seed[0])
|
||||
planet2.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||
planet2.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||
if planet2.govtype <= 1
|
||||
planet2.economy = (planet2.economy | 2)
|
||||
planet2.techlevel = (msb(seed[1]) & 3) + (planet2.economy ^ 7)
|
||||
planet2.techlevel += planet2.govtype >> 1
|
||||
if planet2.govtype & 1
|
||||
planet2.techlevel++
|
||||
planet2.population = 4 * planet2.techlevel + planet2.economy
|
||||
planet2.population += planet2.govtype + 1
|
||||
planet2.productivity = ((planet2.economy ^ 7) + 3) * (planet2.govtype + 4)
|
||||
planet2.productivity *= planet2.population * 8
|
||||
ubyte seed2_msb = msb(seed[2])
|
||||
planet2.radius = mkword((seed2_msb & 15) + 11, planet2.x)
|
||||
planet2.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
|
||||
if planet2.species_is_alien {
|
||||
planet2.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||
planet2.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||
planet2.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||
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[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() {
|
||||
uword temp = seed[0] + seed[1] + seed[2]
|
||||
seed[0] = seed[1]
|
||||
seed[1] = seed[2]
|
||||
seed[2] = temp
|
||||
}
|
||||
|
||||
sub twist(uword x) -> uword {
|
||||
ubyte xh = msb(x)
|
||||
ubyte xl = lsb(x)
|
||||
rol(xh)
|
||||
rol(xl)
|
||||
return mkword(xh, xl)
|
||||
}
|
||||
|
||||
sub debug_seed() {
|
||||
txt.print("\ngalaxy2 #")
|
||||
txt.print_ub(number)
|
||||
txt.print("\ngalaxy2 seed0=")
|
||||
txt.print_uwhex(galaxy2.seed[0], true)
|
||||
txt.print("\ngalaxy2 seed1=")
|
||||
txt.print_uwhex(galaxy2.seed[1], true)
|
||||
txt.print("\ngalaxy2 seed2=")
|
||||
txt.print_uwhex(galaxy2.seed[2], true)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
planet2 {
|
||||
%option force_output
|
||||
|
||||
str[] species_sizes = ["Large", "Fierce", "Small"]
|
||||
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
|
||||
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
|
||||
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
|
||||
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship2", "Communist", "Confederacy", "Democracy", "Corporate State"]
|
||||
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
|
||||
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
|
||||
|
||||
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||
str[] words8F = ["\xB0", "The planet2 \xB0", "The world \xB0", "This planet2", "This world"]
|
||||
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||
str[] words91 = ["planet2", "world", "place", "little planet2", "dump"]
|
||||
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||
|
||||
uword[] wordlists = [
|
||||
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||
|
||||
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||
|
||||
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||
|
||||
str name = " " ; 8 max
|
||||
ubyte number ; starts at 0 in new galaxy2, then increases by 1 for each generated planet2
|
||||
ubyte x
|
||||
ubyte y
|
||||
ubyte economy
|
||||
ubyte govtype
|
||||
ubyte techlevel
|
||||
ubyte population
|
||||
uword productivity
|
||||
uword radius
|
||||
ubyte species_is_alien ; otherwise "Human Colonials"
|
||||
ubyte species_size
|
||||
ubyte species_color
|
||||
ubyte species_look
|
||||
ubyte species_kind
|
||||
|
||||
sub set_seed(uword s1, uword s2) {
|
||||
goatsoup_seed[0] = lsb(s1)
|
||||
goatsoup_seed[1] = msb(s1)
|
||||
goatsoup_seed[2] = lsb(s2)
|
||||
goatsoup_seed[3] = msb(s2)
|
||||
reset_rnd()
|
||||
}
|
||||
|
||||
sub reset_rnd() {
|
||||
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||
}
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str name = " " ; 8 chars max
|
||||
ubyte nx = 0
|
||||
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||
ubyte x = goatsoup_rnd_number() & $3e
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
x++
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
}
|
||||
name[nx] = 0
|
||||
name[0] |= 32 ; uppercase first letter
|
||||
return name
|
||||
}
|
||||
|
||||
sub goatsoup_rnd_number() -> ubyte {
|
||||
ubyte x = goatsoup_rnd[0] * 2
|
||||
uword a = x as uword + goatsoup_rnd[2]
|
||||
if goatsoup_rnd[0] > 127
|
||||
a ++
|
||||
goatsoup_rnd[0] = lsb(a)
|
||||
goatsoup_rnd[2] = x
|
||||
x = goatsoup_rnd[1]
|
||||
ubyte ac = x + goatsoup_rnd[3] + msb(a)
|
||||
goatsoup_rnd[1] = ac
|
||||
goatsoup_rnd[3] = x
|
||||
return ac
|
||||
}
|
||||
|
||||
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||
uword ax
|
||||
uword ay
|
||||
if px>x
|
||||
ax=px-x
|
||||
else
|
||||
ax=x-px
|
||||
if py>y
|
||||
ay=py-y
|
||||
else
|
||||
ay=y-py
|
||||
ay /= 2
|
||||
ubyte d = sqrt16(ax*ax + ay*ay)
|
||||
if d>63
|
||||
return 255
|
||||
return d*4
|
||||
}
|
||||
|
||||
sub soup() -> str {
|
||||
str planet2_result = " " * 160
|
||||
uword[6] source_stack
|
||||
ubyte stack_ptr = 0
|
||||
str start_source = "\x8F is \x97."
|
||||
uword source_ptr = &start_source
|
||||
uword result_ptr = &planet2_result
|
||||
|
||||
reset_rnd()
|
||||
recursive_soup()
|
||||
return planet2_result
|
||||
|
||||
sub recursive_soup() {
|
||||
repeat {
|
||||
ubyte c = @(source_ptr)
|
||||
source_ptr++
|
||||
if c == $00 {
|
||||
@(result_ptr) = 0
|
||||
return
|
||||
}
|
||||
else if c <= $80 {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
else {
|
||||
if c <= $a4 {
|
||||
ubyte rnr = goatsoup_rnd_number()
|
||||
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
|
||||
source_stack[stack_ptr] = source_ptr
|
||||
stack_ptr++
|
||||
source_ptr = getword(c, wordNr)
|
||||
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||
stack_ptr--
|
||||
source_ptr = source_stack[stack_ptr]
|
||||
} else {
|
||||
if c == $b0 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
concat_string(&name + 1)
|
||||
}
|
||||
else if c == $b1 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
ubyte ni
|
||||
for ni in 1 to len(name) {
|
||||
ubyte cc = name[ni]
|
||||
if cc=='e' or cc=='o' or cc==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = cc
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
@(result_ptr) = 'i'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'a'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'n'
|
||||
result_ptr++
|
||||
}
|
||||
else if c == $b2 {
|
||||
concat_string(random_name())
|
||||
}
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub concat_string(uword str_ptr) {
|
||||
repeat {
|
||||
ubyte c = @(str_ptr)
|
||||
if c==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
str_ptr++
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub display(ubyte compressed) {
|
||||
if compressed {
|
||||
print_name_uppercase()
|
||||
txt.print(" TL:")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.spc()
|
||||
txt.print(econnames[economy])
|
||||
txt.spc()
|
||||
txt.print(govnames[govtype])
|
||||
} else {
|
||||
txt.print("\n\nSystem: ")
|
||||
print_name_uppercase()
|
||||
txt.print("\nPosition: ")
|
||||
txt.print_ub(x)
|
||||
txt.chrout('\'')
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
txt.chrout('#')
|
||||
txt.print_ub(number)
|
||||
txt.print("\nEconomy: ")
|
||||
txt.print(econnames[economy])
|
||||
txt.print("\nGovernment: ")
|
||||
txt.print(govnames[govtype])
|
||||
txt.print("\nTech Level: ")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.print("\nTurnover: ")
|
||||
txt.print_uw(productivity)
|
||||
txt.print("\nRadius: ")
|
||||
txt.print_uw(radius)
|
||||
txt.print("\nPopulation: ")
|
||||
txt.print_ub(population >> 3)
|
||||
txt.print(" Billion\nSpecies: ")
|
||||
if species_is_alien {
|
||||
if species_size < len(species_sizes) {
|
||||
txt.print(species_sizes[species_size])
|
||||
txt.spc()
|
||||
}
|
||||
if species_color < len(species_colors) {
|
||||
txt.print(species_colors[species_color])
|
||||
txt.spc()
|
||||
}
|
||||
if species_look < len(species_looks) {
|
||||
txt.print(species_looks[species_look])
|
||||
txt.spc()
|
||||
}
|
||||
if species_kind < len(species_kinds) {
|
||||
txt.print(species_kinds[species_kind])
|
||||
}
|
||||
} else {
|
||||
txt.print("Human Colonials")
|
||||
}
|
||||
txt.nl()
|
||||
txt.print(soup())
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub print_name_uppercase() {
|
||||
ubyte c
|
||||
for c in name
|
||||
txt.chrout(c | 32)
|
||||
}
|
||||
|
||||
asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY {
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc #$81
|
||||
asl a
|
||||
tay
|
||||
lda wordlists,y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda wordlists+1,y
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
util2 {
|
||||
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
|
||||
repeat {
|
||||
ubyte pc = @(prefixptr)
|
||||
ubyte sc = @(stringptr)
|
||||
if pc == 0
|
||||
return true
|
||||
; to lowercase for case insensitive compare:
|
||||
pc &= 127
|
||||
sc &= 127
|
||||
if pc != sc
|
||||
return false
|
||||
prefixptr++
|
||||
stringptr++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub print_right(ubyte width, uword st) {
|
||||
repeat width - string.length(st) {
|
||||
txt.spc()
|
||||
}
|
||||
txt.print(st)
|
||||
}
|
||||
|
||||
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
lda conv.uword2decimal.decTenThousands
|
||||
ldy #0 ; have we started printing?
|
||||
cmp #'0'
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decThousands
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decHundreds
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decTens
|
||||
jsr c64.CHROUT
|
||||
lda #'.'
|
||||
jsr c64.CHROUT
|
||||
lda conv.uword2decimal.decOnes
|
||||
jsr c64.CHROUT
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,994 +0,0 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import diskio
|
||||
%import string
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
; Prog8 adaptation of the Text-Elite galaxy3 system trading simulation engine.
|
||||
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
perf3 {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet3 in galaxy3 one
|
||||
const ubyte numforZaonce = 129
|
||||
const ubyte numforDiso = 147
|
||||
const ubyte numforRiedquat = 46
|
||||
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
txt.print("\u000c\n --- TextElite v1.1 ---\n")
|
||||
|
||||
galaxy3.travel_to(1, numforLave)
|
||||
market3.init(0) ; Lave's market3 is seeded with 0
|
||||
ship3.init()
|
||||
planet3.display(false)
|
||||
|
||||
repeat {
|
||||
; test_stack.test()
|
||||
|
||||
str input = "????????"
|
||||
txt.print("\nCash: ")
|
||||
util3.print_10s(ship3.cash)
|
||||
txt.print("\nCommand (?=help): ")
|
||||
ubyte num_chars = txt.input_chars(input)
|
||||
txt.nl()
|
||||
if num_chars {
|
||||
when input[0] {
|
||||
'?' -> {
|
||||
txt.print("\nCommands are:\n"+
|
||||
"buy jump info cash >=save\n"+
|
||||
"sell teleport market3 hold <=load\n"+
|
||||
"fuel galhyp local quit\n")
|
||||
}
|
||||
'q' -> break
|
||||
'b' -> trader3.do_buy()
|
||||
's' -> trader3.do_sell()
|
||||
'f' -> trader3.do_fuel()
|
||||
'j' -> trader3.do_jump()
|
||||
't' -> trader3.do_teleport()
|
||||
'g' -> trader3.do_next_galaxy3()
|
||||
'i' -> trader3.do_info()
|
||||
'm' -> trader3.do_show_market3()
|
||||
'l' -> trader3.do_local()
|
||||
'c' -> trader3.do_cash()
|
||||
'h' -> trader3.do_hold()
|
||||
'<' -> trader3.do_load()
|
||||
'>' -> trader3.do_save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trader3 {
|
||||
str Savegame = "↑commander.save"
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
struct SaveData {
|
||||
ubyte galaxy3
|
||||
ubyte planet3
|
||||
ubyte cargo0
|
||||
ubyte cargo1
|
||||
ubyte cargo2
|
||||
ubyte cargo3
|
||||
ubyte cargo4
|
||||
ubyte cargo5
|
||||
ubyte cargo6
|
||||
ubyte cargo7
|
||||
ubyte cargo8
|
||||
ubyte cargo9
|
||||
ubyte cargo10
|
||||
ubyte cargo11
|
||||
ubyte cargo12
|
||||
ubyte cargo13
|
||||
ubyte cargo14
|
||||
ubyte cargo15
|
||||
ubyte cargo16
|
||||
uword cash
|
||||
ubyte max_cargo
|
||||
ubyte fuel
|
||||
}
|
||||
SaveData savedata
|
||||
|
||||
sub do_load() {
|
||||
txt.print("\nLoading universe...")
|
||||
if diskio.load(8, Savegame, &savedata) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
return
|
||||
}
|
||||
|
||||
ship3.cash = savedata.cash
|
||||
ship3.Max_cargo = savedata.max_cargo
|
||||
ship3.fuel = savedata.fuel
|
||||
sys.memcopy(&savedata.cargo0, ship3.cargohold, len(ship3.cargohold))
|
||||
galaxy3.travel_to(savedata.galaxy3, savedata.planet3)
|
||||
|
||||
planet3.display(false)
|
||||
}
|
||||
|
||||
sub do_save() {
|
||||
savedata.galaxy3 = galaxy3.number
|
||||
savedata.planet3 = planet3.number
|
||||
savedata.cash = ship3.cash
|
||||
savedata.max_cargo = ship3.Max_cargo
|
||||
savedata.fuel = ship3.fuel
|
||||
sys.memcopy(ship3.cargohold, &savedata.cargo0, len(ship3.cargohold))
|
||||
|
||||
txt.print("\nSaving universe...")
|
||||
diskio.delete(8, Savegame)
|
||||
if diskio.save(8, Savegame, &savedata, sizeof(savedata)) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub do_jump() {
|
||||
txt.print("\nJump to what system? ")
|
||||
jump_to_system()
|
||||
}
|
||||
|
||||
sub do_teleport() {
|
||||
txt.print("\nCheat! Teleport to what system? ")
|
||||
ubyte fuel = ship3.fuel
|
||||
ship3.fuel = 255
|
||||
jump_to_system()
|
||||
ship3.fuel = fuel
|
||||
}
|
||||
|
||||
sub jump_to_system() {
|
||||
void txt.input_chars(input)
|
||||
ubyte current_planet3 = planet3.number
|
||||
ubyte x = planet3.x
|
||||
ubyte y = planet3.y
|
||||
if galaxy3.search_closest_planet3(input) {
|
||||
ubyte distance = planet3.distance(x, y)
|
||||
if distance <= ship3.fuel {
|
||||
galaxy3.init_market3_for_planet3()
|
||||
ship3.fuel -= distance
|
||||
txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||
planet3.display(true)
|
||||
return
|
||||
}
|
||||
txt.print("Insufficient fuel\n")
|
||||
} else {
|
||||
txt.print(" Not found!\n")
|
||||
}
|
||||
galaxy3.travel_to(galaxy3.number, current_planet3)
|
||||
}
|
||||
|
||||
sub do_buy() {
|
||||
txt.print("\nBuy what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market3.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if market3.current_quantity[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market3.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util3.print_10s(price)
|
||||
if price > ship3.cash {
|
||||
txt.print(" Not enough cash!\n")
|
||||
} else {
|
||||
ship3.cash -= price
|
||||
ship3.cargohold[ci] += amount
|
||||
market3.current_quantity[ci] -= amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_sell() {
|
||||
txt.print("\nSell what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market3.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if ship3.cargohold[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market3.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util3.print_10s(price)
|
||||
ship3.cash += price
|
||||
ship3.cargohold[ci] -= amount
|
||||
market3.current_quantity[ci] += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_fuel() {
|
||||
txt.print("\nBuy fuel. Amount? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||
ubyte max_fuel = ship3.Max_fuel - ship3.fuel
|
||||
if buy_fuel > max_fuel
|
||||
buy_fuel = max_fuel
|
||||
uword price = buy_fuel as uword * ship3.Fuel_cost
|
||||
if price > ship3.cash {
|
||||
txt.print("Not enough cash!\n")
|
||||
} else {
|
||||
ship3.cash -= price
|
||||
ship3.fuel += buy_fuel
|
||||
}
|
||||
}
|
||||
|
||||
sub do_cash() {
|
||||
txt.print("\nCheat! Set cash amount: ")
|
||||
void txt.input_chars(input)
|
||||
ship3.cash = conv.str2uword(input)
|
||||
}
|
||||
|
||||
sub do_hold() {
|
||||
txt.print("\nCheat! Set cargohold size: ")
|
||||
void txt.input_chars(input)
|
||||
ship3.Max_cargo = conv.str2ubyte(input)
|
||||
}
|
||||
|
||||
sub do_next_galaxy3() {
|
||||
galaxy3.travel_to(galaxy3.number+1, planet3.number)
|
||||
planet3.display(false)
|
||||
}
|
||||
|
||||
sub do_info() {
|
||||
txt.print("\nSystem name (empty=current): ")
|
||||
num_chars = txt.input_chars(input)
|
||||
if num_chars {
|
||||
ubyte current_planet3 = planet3.number
|
||||
if galaxy3.search_closest_planet3(input) {
|
||||
planet3.display(false)
|
||||
} else {
|
||||
txt.print(" Not found!")
|
||||
}
|
||||
galaxy3.travel_to(galaxy3.number, current_planet3)
|
||||
} else {
|
||||
planet3.display(false)
|
||||
}
|
||||
}
|
||||
|
||||
sub do_local() {
|
||||
galaxy3.local_area()
|
||||
}
|
||||
|
||||
sub do_show_market3() {
|
||||
market3.display()
|
||||
txt.print("\nFuel: ")
|
||||
util3.print_10s(ship3.fuel)
|
||||
txt.print(" Cargohold space: ")
|
||||
txt.print_ub(ship3.cargo_free())
|
||||
txt.print("t\n")
|
||||
}
|
||||
}
|
||||
|
||||
ship3 {
|
||||
const ubyte Max_fuel = 70
|
||||
const ubyte Fuel_cost = 2
|
||||
ubyte Max_cargo = 20
|
||||
|
||||
ubyte fuel = Max_fuel
|
||||
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
|
||||
ubyte[17] cargohold = 0
|
||||
|
||||
sub init() {
|
||||
sys.memset(cargohold, len(cargohold), 0)
|
||||
}
|
||||
|
||||
sub cargo_free() -> ubyte {
|
||||
ubyte ci
|
||||
ubyte total = 0
|
||||
for ci in 0 to len(cargohold)-1 {
|
||||
if market3.units[ci]==0 ; tonnes only
|
||||
total += cargohold[ci]
|
||||
}
|
||||
return Max_cargo - total
|
||||
}
|
||||
}
|
||||
|
||||
market3 {
|
||||
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
|
||||
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||
|
||||
ubyte[17] current_quantity = 0
|
||||
uword[17] current_price = 0
|
||||
|
||||
sub init(ubyte fluct) {
|
||||
; Prices and availabilities are influenced by the planet3's economy type
|
||||
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||
; commander position to keep the market3 prices constant over gamesaves.
|
||||
; Availabilities must be saved with the game since the player alters them
|
||||
; by buying (and selling(?))
|
||||
;
|
||||
; Almost all operations are one byte only and overflow "errors" are
|
||||
; extremely frequent and exploited.
|
||||
;
|
||||
; Trade Item prices are held internally in a single byte=true value/4.
|
||||
; The decimal point in prices is introduced only when printing them.
|
||||
; Internally, all prices are integers.
|
||||
; The player's cash is held in four bytes.
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
word product
|
||||
byte changing
|
||||
product = planet3.economy as word * gradients[ci]
|
||||
changing = fluct & maskbytes[ci] as byte
|
||||
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||
if q & $80
|
||||
q = 0 ; clip to positive 8-bit
|
||||
current_quantity[ci] = q & $3f
|
||||
q = (baseprices[ci] + changing + product) as ubyte
|
||||
current_price[ci] = q * $0004
|
||||
}
|
||||
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||
}
|
||||
|
||||
sub display() {
|
||||
ubyte ci
|
||||
txt.nl()
|
||||
planet3.print_name_uppercase()
|
||||
txt.print(" trade market3:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||
for ci in 0 to len(names)-1 {
|
||||
util3.print_right(13, names[ci])
|
||||
txt.print(" ")
|
||||
util3.print_10s(current_price[ci])
|
||||
txt.print(" ")
|
||||
txt.print_ub(current_quantity[ci])
|
||||
when units[ci] {
|
||||
0 -> txt.chrout('t')
|
||||
1 -> txt.print("kg")
|
||||
2 -> txt.chrout('g')
|
||||
}
|
||||
txt.print(" ")
|
||||
txt.print_ub(ship3.cargohold[ci])
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub match(uword nameptr) -> ubyte {
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
if util3.prefix_matches(nameptr, names[ci])
|
||||
return ci
|
||||
}
|
||||
return 255
|
||||
}
|
||||
}
|
||||
|
||||
galaxy3 {
|
||||
const uword GALSIZE = 256
|
||||
const uword base0 = $5A4A ; seeds for the first galaxy3
|
||||
const uword base1 = $0248
|
||||
const uword base2 = $B753
|
||||
|
||||
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||
|
||||
ubyte number
|
||||
|
||||
uword[3] seed
|
||||
|
||||
sub init(ubyte galaxy3num) {
|
||||
number = 1
|
||||
planet3.number = 255
|
||||
seed[0] = base0
|
||||
seed[1] = base1
|
||||
seed[2] = base2
|
||||
repeat galaxy3num-1 {
|
||||
nextgalaxy3()
|
||||
}
|
||||
}
|
||||
|
||||
sub nextgalaxy3() {
|
||||
seed[0] = twist(seed[0])
|
||||
seed[1] = twist(seed[1])
|
||||
seed[2] = twist(seed[2])
|
||||
number++
|
||||
if number==9
|
||||
number = 1
|
||||
}
|
||||
|
||||
sub travel_to(ubyte galaxy3num, ubyte system) {
|
||||
init(galaxy3num)
|
||||
generate_next_planet3() ; always at least planet3 0 (separate to avoid repeat ubyte overflow)
|
||||
repeat system {
|
||||
generate_next_planet3()
|
||||
}
|
||||
planet3.name = make_current_planet3_name()
|
||||
init_market3_for_planet3()
|
||||
}
|
||||
|
||||
sub init_market3_for_planet3() {
|
||||
market3.init(lsb(seed[0])+msb(seed[2]))
|
||||
}
|
||||
|
||||
sub search_closest_planet3(uword nameptr) -> ubyte {
|
||||
ubyte x = planet3.x
|
||||
ubyte y = planet3.y
|
||||
ubyte current_planet3_num = planet3.number
|
||||
|
||||
init(number)
|
||||
ubyte found = false
|
||||
ubyte current_closest_pi
|
||||
ubyte current_distance = 127
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet3()
|
||||
planet3.name = make_current_planet3_name()
|
||||
if util3.prefix_matches(nameptr, planet3.name) {
|
||||
ubyte distance = planet3.distance(x, y)
|
||||
if distance < current_distance {
|
||||
current_distance = distance
|
||||
current_closest_pi = pi
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found
|
||||
travel_to(number, current_closest_pi)
|
||||
else
|
||||
travel_to(number, current_planet3_num)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
sub local_area() {
|
||||
ubyte current_planet3 = planet3.number
|
||||
ubyte px = planet3.x
|
||||
ubyte py = planet3.y
|
||||
ubyte pn = 0
|
||||
|
||||
init(number)
|
||||
txt.print("\nGalaxy #")
|
||||
txt.print_ub(number)
|
||||
txt.print(" - systems in vicinity:\n")
|
||||
do {
|
||||
generate_next_planet3()
|
||||
ubyte distance = planet3.distance(px, py)
|
||||
if distance <= ship3.Max_fuel {
|
||||
if distance <= ship3.fuel
|
||||
txt.chrout('*')
|
||||
else
|
||||
txt.chrout('-')
|
||||
txt.spc()
|
||||
planet3.name = make_current_planet3_name()
|
||||
planet3.display(true)
|
||||
txt.print(" (")
|
||||
util3.print_10s(distance)
|
||||
txt.print(" LY)\n")
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
travel_to(number, current_planet3)
|
||||
}
|
||||
|
||||
ubyte pn_pair1
|
||||
ubyte pn_pair2
|
||||
ubyte pn_pair3
|
||||
ubyte pn_pair4
|
||||
ubyte longname
|
||||
|
||||
sub generate_next_planet3() {
|
||||
determine_planet3_properties()
|
||||
longname = lsb(seed[0]) & 64
|
||||
|
||||
; Always four iterations of random number
|
||||
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
}
|
||||
|
||||
sub make_current_planet3_name() -> str {
|
||||
ubyte ni = 0
|
||||
str name = " " ; max 8
|
||||
|
||||
if pn_pairs[pn_pair1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair1+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3+1]
|
||||
ni++
|
||||
}
|
||||
|
||||
if longname {
|
||||
if pn_pairs[pn_pair4] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair4+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4+1]
|
||||
ni++
|
||||
}
|
||||
}
|
||||
|
||||
name[ni] = 0
|
||||
return name
|
||||
}
|
||||
|
||||
sub determine_planet3_properties() {
|
||||
; create the planet3's characteristics
|
||||
planet3.number++
|
||||
planet3.x = msb(seed[1])
|
||||
planet3.y = msb(seed[0])
|
||||
planet3.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||
planet3.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||
if planet3.govtype <= 1
|
||||
planet3.economy = (planet3.economy | 2)
|
||||
planet3.techlevel = (msb(seed[1]) & 3) + (planet3.economy ^ 7)
|
||||
planet3.techlevel += planet3.govtype >> 1
|
||||
if planet3.govtype & 1
|
||||
planet3.techlevel++
|
||||
planet3.population = 4 * planet3.techlevel + planet3.economy
|
||||
planet3.population += planet3.govtype + 1
|
||||
planet3.productivity = ((planet3.economy ^ 7) + 3) * (planet3.govtype + 4)
|
||||
planet3.productivity *= planet3.population * 8
|
||||
ubyte seed2_msb = msb(seed[2])
|
||||
planet3.radius = mkword((seed2_msb & 15) + 11, planet3.x)
|
||||
planet3.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
|
||||
if planet3.species_is_alien {
|
||||
planet3.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||
planet3.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||
planet3.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||
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[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() {
|
||||
uword temp = seed[0] + seed[1] + seed[2]
|
||||
seed[0] = seed[1]
|
||||
seed[1] = seed[2]
|
||||
seed[2] = temp
|
||||
}
|
||||
|
||||
sub twist(uword x) -> uword {
|
||||
ubyte xh = msb(x)
|
||||
ubyte xl = lsb(x)
|
||||
rol(xh)
|
||||
rol(xl)
|
||||
return mkword(xh, xl)
|
||||
}
|
||||
|
||||
sub debug_seed() {
|
||||
txt.print("\ngalaxy3 #")
|
||||
txt.print_ub(number)
|
||||
txt.print("\ngalaxy3 seed0=")
|
||||
txt.print_uwhex(galaxy3.seed[0], true)
|
||||
txt.print("\ngalaxy3 seed1=")
|
||||
txt.print_uwhex(galaxy3.seed[1], true)
|
||||
txt.print("\ngalaxy3 seed2=")
|
||||
txt.print_uwhex(galaxy3.seed[2], true)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
planet3 {
|
||||
%option force_output
|
||||
|
||||
str[] species_sizes = ["Large", "Fierce", "Small"]
|
||||
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
|
||||
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
|
||||
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
|
||||
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship3", "Communist", "Confederacy", "Democracy", "Corporate State"]
|
||||
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
|
||||
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
|
||||
|
||||
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||
str[] words8F = ["\xB0", "The planet3 \xB0", "The world \xB0", "This planet3", "This world"]
|
||||
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||
str[] words91 = ["planet3", "world", "place", "little planet3", "dump"]
|
||||
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||
|
||||
uword[] wordlists = [
|
||||
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||
|
||||
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||
|
||||
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||
|
||||
str name = " " ; 8 max
|
||||
ubyte number ; starts at 0 in new galaxy3, then increases by 1 for each generated planet3
|
||||
ubyte x
|
||||
ubyte y
|
||||
ubyte economy
|
||||
ubyte govtype
|
||||
ubyte techlevel
|
||||
ubyte population
|
||||
uword productivity
|
||||
uword radius
|
||||
ubyte species_is_alien ; otherwise "Human Colonials"
|
||||
ubyte species_size
|
||||
ubyte species_color
|
||||
ubyte species_look
|
||||
ubyte species_kind
|
||||
|
||||
sub set_seed(uword s1, uword s2) {
|
||||
goatsoup_seed[0] = lsb(s1)
|
||||
goatsoup_seed[1] = msb(s1)
|
||||
goatsoup_seed[2] = lsb(s2)
|
||||
goatsoup_seed[3] = msb(s2)
|
||||
reset_rnd()
|
||||
}
|
||||
|
||||
sub reset_rnd() {
|
||||
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||
}
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str name = " " ; 8 chars max
|
||||
ubyte nx = 0
|
||||
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||
ubyte x = goatsoup_rnd_number() & $3e
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
x++
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
}
|
||||
name[nx] = 0
|
||||
name[0] |= 32 ; uppercase first letter
|
||||
return name
|
||||
}
|
||||
|
||||
sub goatsoup_rnd_number() -> ubyte {
|
||||
ubyte x = goatsoup_rnd[0] * 2
|
||||
uword a = x as uword + goatsoup_rnd[2]
|
||||
if goatsoup_rnd[0] > 127
|
||||
a ++
|
||||
goatsoup_rnd[0] = lsb(a)
|
||||
goatsoup_rnd[2] = x
|
||||
x = goatsoup_rnd[1]
|
||||
ubyte ac = x + goatsoup_rnd[3] + msb(a)
|
||||
goatsoup_rnd[1] = ac
|
||||
goatsoup_rnd[3] = x
|
||||
return ac
|
||||
}
|
||||
|
||||
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||
uword ax
|
||||
uword ay
|
||||
if px>x
|
||||
ax=px-x
|
||||
else
|
||||
ax=x-px
|
||||
if py>y
|
||||
ay=py-y
|
||||
else
|
||||
ay=y-py
|
||||
ay /= 2
|
||||
ubyte d = sqrt16(ax*ax + ay*ay)
|
||||
if d>63
|
||||
return 255
|
||||
return d*4
|
||||
}
|
||||
|
||||
sub soup() -> str {
|
||||
str planet3_result = " " * 160
|
||||
uword[6] source_stack
|
||||
ubyte stack_ptr = 0
|
||||
str start_source = "\x8F is \x97."
|
||||
uword source_ptr = &start_source
|
||||
uword result_ptr = &planet3_result
|
||||
|
||||
reset_rnd()
|
||||
recursive_soup()
|
||||
return planet3_result
|
||||
|
||||
sub recursive_soup() {
|
||||
repeat {
|
||||
ubyte c = @(source_ptr)
|
||||
source_ptr++
|
||||
if c == $00 {
|
||||
@(result_ptr) = 0
|
||||
return
|
||||
}
|
||||
else if c <= $80 {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
else {
|
||||
if c <= $a4 {
|
||||
ubyte rnr = goatsoup_rnd_number()
|
||||
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
|
||||
source_stack[stack_ptr] = source_ptr
|
||||
stack_ptr++
|
||||
source_ptr = getword(c, wordNr)
|
||||
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||
stack_ptr--
|
||||
source_ptr = source_stack[stack_ptr]
|
||||
} else {
|
||||
if c == $b0 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
concat_string(&name + 1)
|
||||
}
|
||||
else if c == $b1 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
ubyte ni
|
||||
for ni in 1 to len(name) {
|
||||
ubyte cc = name[ni]
|
||||
if cc=='e' or cc=='o' or cc==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = cc
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
@(result_ptr) = 'i'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'a'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'n'
|
||||
result_ptr++
|
||||
}
|
||||
else if c == $b2 {
|
||||
concat_string(random_name())
|
||||
}
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub concat_string(uword str_ptr) {
|
||||
repeat {
|
||||
ubyte c = @(str_ptr)
|
||||
if c==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
str_ptr++
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub display(ubyte compressed) {
|
||||
if compressed {
|
||||
print_name_uppercase()
|
||||
txt.print(" TL:")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.spc()
|
||||
txt.print(econnames[economy])
|
||||
txt.spc()
|
||||
txt.print(govnames[govtype])
|
||||
} else {
|
||||
txt.print("\n\nSystem: ")
|
||||
print_name_uppercase()
|
||||
txt.print("\nPosition: ")
|
||||
txt.print_ub(x)
|
||||
txt.chrout('\'')
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
txt.chrout('#')
|
||||
txt.print_ub(number)
|
||||
txt.print("\nEconomy: ")
|
||||
txt.print(econnames[economy])
|
||||
txt.print("\nGovernment: ")
|
||||
txt.print(govnames[govtype])
|
||||
txt.print("\nTech Level: ")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.print("\nTurnover: ")
|
||||
txt.print_uw(productivity)
|
||||
txt.print("\nRadius: ")
|
||||
txt.print_uw(radius)
|
||||
txt.print("\nPopulation: ")
|
||||
txt.print_ub(population >> 3)
|
||||
txt.print(" Billion\nSpecies: ")
|
||||
if species_is_alien {
|
||||
if species_size < len(species_sizes) {
|
||||
txt.print(species_sizes[species_size])
|
||||
txt.spc()
|
||||
}
|
||||
if species_color < len(species_colors) {
|
||||
txt.print(species_colors[species_color])
|
||||
txt.spc()
|
||||
}
|
||||
if species_look < len(species_looks) {
|
||||
txt.print(species_looks[species_look])
|
||||
txt.spc()
|
||||
}
|
||||
if species_kind < len(species_kinds) {
|
||||
txt.print(species_kinds[species_kind])
|
||||
}
|
||||
} else {
|
||||
txt.print("Human Colonials")
|
||||
}
|
||||
txt.nl()
|
||||
txt.print(soup())
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub print_name_uppercase() {
|
||||
ubyte c
|
||||
for c in name
|
||||
txt.chrout(c | 32)
|
||||
}
|
||||
|
||||
asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY {
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc #$81
|
||||
asl a
|
||||
tay
|
||||
lda wordlists,y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda wordlists+1,y
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
util3 {
|
||||
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
|
||||
repeat {
|
||||
ubyte pc = @(prefixptr)
|
||||
ubyte sc = @(stringptr)
|
||||
if pc == 0
|
||||
return true
|
||||
; to lowercase for case insensitive compare:
|
||||
pc &= 127
|
||||
sc &= 127
|
||||
if pc != sc
|
||||
return false
|
||||
prefixptr++
|
||||
stringptr++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub print_right(ubyte width, uword st) {
|
||||
repeat width - string.length(st) {
|
||||
txt.spc()
|
||||
}
|
||||
txt.print(st)
|
||||
}
|
||||
|
||||
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
lda conv.uword2decimal.decTenThousands
|
||||
ldy #0 ; have we started printing?
|
||||
cmp #'0'
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decThousands
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decHundreds
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decTens
|
||||
jsr c64.CHROUT
|
||||
lda #'.'
|
||||
jsr c64.CHROUT
|
||||
lda conv.uword2decimal.decOnes
|
||||
jsr c64.CHROUT
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,994 +0,0 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import diskio
|
||||
%import string
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
; Prog8 adaptation of the Text-Elite galaxy4 system trading simulation engine.
|
||||
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
perf4 {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet4 in galaxy4 one
|
||||
const ubyte numforZaonce = 129
|
||||
const ubyte numforDiso = 147
|
||||
const ubyte numforRiedquat = 46
|
||||
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
txt.print("\u000c\n --- TextElite v1.1 ---\n")
|
||||
|
||||
galaxy4.travel_to(1, numforLave)
|
||||
market4.init(0) ; Lave's market4 is seeded with 0
|
||||
ship4.init()
|
||||
planet4.display(false)
|
||||
|
||||
repeat {
|
||||
; test_stack.test()
|
||||
|
||||
str input = "????????"
|
||||
txt.print("\nCash: ")
|
||||
util4.print_10s(ship4.cash)
|
||||
txt.print("\nCommand (?=help): ")
|
||||
ubyte num_chars = txt.input_chars(input)
|
||||
txt.nl()
|
||||
if num_chars {
|
||||
when input[0] {
|
||||
'?' -> {
|
||||
txt.print("\nCommands are:\n"+
|
||||
"buy jump info cash >=save\n"+
|
||||
"sell teleport market4 hold <=load\n"+
|
||||
"fuel galhyp local quit\n")
|
||||
}
|
||||
'q' -> break
|
||||
'b' -> trader4.do_buy()
|
||||
's' -> trader4.do_sell()
|
||||
'f' -> trader4.do_fuel()
|
||||
'j' -> trader4.do_jump()
|
||||
't' -> trader4.do_teleport()
|
||||
'g' -> trader4.do_next_galaxy4()
|
||||
'i' -> trader4.do_info()
|
||||
'm' -> trader4.do_show_market4()
|
||||
'l' -> trader4.do_local()
|
||||
'c' -> trader4.do_cash()
|
||||
'h' -> trader4.do_hold()
|
||||
'<' -> trader4.do_load()
|
||||
'>' -> trader4.do_save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trader4 {
|
||||
str Savegame = "↑commander.save"
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
struct SaveData {
|
||||
ubyte galaxy4
|
||||
ubyte planet4
|
||||
ubyte cargo0
|
||||
ubyte cargo1
|
||||
ubyte cargo2
|
||||
ubyte cargo3
|
||||
ubyte cargo4
|
||||
ubyte cargo5
|
||||
ubyte cargo6
|
||||
ubyte cargo7
|
||||
ubyte cargo8
|
||||
ubyte cargo9
|
||||
ubyte cargo10
|
||||
ubyte cargo11
|
||||
ubyte cargo12
|
||||
ubyte cargo13
|
||||
ubyte cargo14
|
||||
ubyte cargo15
|
||||
ubyte cargo16
|
||||
uword cash
|
||||
ubyte max_cargo
|
||||
ubyte fuel
|
||||
}
|
||||
SaveData savedata
|
||||
|
||||
sub do_load() {
|
||||
txt.print("\nLoading universe...")
|
||||
if diskio.load(8, Savegame, &savedata) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
return
|
||||
}
|
||||
|
||||
ship4.cash = savedata.cash
|
||||
ship4.Max_cargo = savedata.max_cargo
|
||||
ship4.fuel = savedata.fuel
|
||||
sys.memcopy(&savedata.cargo0, ship4.cargohold, len(ship4.cargohold))
|
||||
galaxy4.travel_to(savedata.galaxy4, savedata.planet4)
|
||||
|
||||
planet4.display(false)
|
||||
}
|
||||
|
||||
sub do_save() {
|
||||
savedata.galaxy4 = galaxy4.number
|
||||
savedata.planet4 = planet4.number
|
||||
savedata.cash = ship4.cash
|
||||
savedata.max_cargo = ship4.Max_cargo
|
||||
savedata.fuel = ship4.fuel
|
||||
sys.memcopy(ship4.cargohold, &savedata.cargo0, len(ship4.cargohold))
|
||||
|
||||
txt.print("\nSaving universe...")
|
||||
diskio.delete(8, Savegame)
|
||||
if diskio.save(8, Savegame, &savedata, sizeof(savedata)) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub do_jump() {
|
||||
txt.print("\nJump to what system? ")
|
||||
jump_to_system()
|
||||
}
|
||||
|
||||
sub do_teleport() {
|
||||
txt.print("\nCheat! Teleport to what system? ")
|
||||
ubyte fuel = ship4.fuel
|
||||
ship4.fuel = 255
|
||||
jump_to_system()
|
||||
ship4.fuel = fuel
|
||||
}
|
||||
|
||||
sub jump_to_system() {
|
||||
void txt.input_chars(input)
|
||||
ubyte current_planet4 = planet4.number
|
||||
ubyte x = planet4.x
|
||||
ubyte y = planet4.y
|
||||
if galaxy4.search_closest_planet4(input) {
|
||||
ubyte distance = planet4.distance(x, y)
|
||||
if distance <= ship4.fuel {
|
||||
galaxy4.init_market4_for_planet4()
|
||||
ship4.fuel -= distance
|
||||
txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||
planet4.display(true)
|
||||
return
|
||||
}
|
||||
txt.print("Insufficient fuel\n")
|
||||
} else {
|
||||
txt.print(" Not found!\n")
|
||||
}
|
||||
galaxy4.travel_to(galaxy4.number, current_planet4)
|
||||
}
|
||||
|
||||
sub do_buy() {
|
||||
txt.print("\nBuy what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market4.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if market4.current_quantity[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market4.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util4.print_10s(price)
|
||||
if price > ship4.cash {
|
||||
txt.print(" Not enough cash!\n")
|
||||
} else {
|
||||
ship4.cash -= price
|
||||
ship4.cargohold[ci] += amount
|
||||
market4.current_quantity[ci] -= amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_sell() {
|
||||
txt.print("\nSell what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market4.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if ship4.cargohold[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market4.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util4.print_10s(price)
|
||||
ship4.cash += price
|
||||
ship4.cargohold[ci] -= amount
|
||||
market4.current_quantity[ci] += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_fuel() {
|
||||
txt.print("\nBuy fuel. Amount? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||
ubyte max_fuel = ship4.Max_fuel - ship4.fuel
|
||||
if buy_fuel > max_fuel
|
||||
buy_fuel = max_fuel
|
||||
uword price = buy_fuel as uword * ship4.Fuel_cost
|
||||
if price > ship4.cash {
|
||||
txt.print("Not enough cash!\n")
|
||||
} else {
|
||||
ship4.cash -= price
|
||||
ship4.fuel += buy_fuel
|
||||
}
|
||||
}
|
||||
|
||||
sub do_cash() {
|
||||
txt.print("\nCheat! Set cash amount: ")
|
||||
void txt.input_chars(input)
|
||||
ship4.cash = conv.str2uword(input)
|
||||
}
|
||||
|
||||
sub do_hold() {
|
||||
txt.print("\nCheat! Set cargohold size: ")
|
||||
void txt.input_chars(input)
|
||||
ship4.Max_cargo = conv.str2ubyte(input)
|
||||
}
|
||||
|
||||
sub do_next_galaxy4() {
|
||||
galaxy4.travel_to(galaxy4.number+1, planet4.number)
|
||||
planet4.display(false)
|
||||
}
|
||||
|
||||
sub do_info() {
|
||||
txt.print("\nSystem name (empty=current): ")
|
||||
num_chars = txt.input_chars(input)
|
||||
if num_chars {
|
||||
ubyte current_planet4 = planet4.number
|
||||
if galaxy4.search_closest_planet4(input) {
|
||||
planet4.display(false)
|
||||
} else {
|
||||
txt.print(" Not found!")
|
||||
}
|
||||
galaxy4.travel_to(galaxy4.number, current_planet4)
|
||||
} else {
|
||||
planet4.display(false)
|
||||
}
|
||||
}
|
||||
|
||||
sub do_local() {
|
||||
galaxy4.local_area()
|
||||
}
|
||||
|
||||
sub do_show_market4() {
|
||||
market4.display()
|
||||
txt.print("\nFuel: ")
|
||||
util4.print_10s(ship4.fuel)
|
||||
txt.print(" Cargohold space: ")
|
||||
txt.print_ub(ship4.cargo_free())
|
||||
txt.print("t\n")
|
||||
}
|
||||
}
|
||||
|
||||
ship4 {
|
||||
const ubyte Max_fuel = 70
|
||||
const ubyte Fuel_cost = 2
|
||||
ubyte Max_cargo = 20
|
||||
|
||||
ubyte fuel = Max_fuel
|
||||
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
|
||||
ubyte[17] cargohold = 0
|
||||
|
||||
sub init() {
|
||||
sys.memset(cargohold, len(cargohold), 0)
|
||||
}
|
||||
|
||||
sub cargo_free() -> ubyte {
|
||||
ubyte ci
|
||||
ubyte total = 0
|
||||
for ci in 0 to len(cargohold)-1 {
|
||||
if market4.units[ci]==0 ; tonnes only
|
||||
total += cargohold[ci]
|
||||
}
|
||||
return Max_cargo - total
|
||||
}
|
||||
}
|
||||
|
||||
market4 {
|
||||
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
|
||||
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||
|
||||
ubyte[17] current_quantity = 0
|
||||
uword[17] current_price = 0
|
||||
|
||||
sub init(ubyte fluct) {
|
||||
; Prices and availabilities are influenced by the planet4's economy type
|
||||
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||
; commander position to keep the market4 prices constant over gamesaves.
|
||||
; Availabilities must be saved with the game since the player alters them
|
||||
; by buying (and selling(?))
|
||||
;
|
||||
; Almost all operations are one byte only and overflow "errors" are
|
||||
; extremely frequent and exploited.
|
||||
;
|
||||
; Trade Item prices are held internally in a single byte=true value/4.
|
||||
; The decimal point in prices is introduced only when printing them.
|
||||
; Internally, all prices are integers.
|
||||
; The player's cash is held in four bytes.
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
word product
|
||||
byte changing
|
||||
product = planet4.economy as word * gradients[ci]
|
||||
changing = fluct & maskbytes[ci] as byte
|
||||
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||
if q & $80
|
||||
q = 0 ; clip to positive 8-bit
|
||||
current_quantity[ci] = q & $3f
|
||||
q = (baseprices[ci] + changing + product) as ubyte
|
||||
current_price[ci] = q * $0004
|
||||
}
|
||||
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||
}
|
||||
|
||||
sub display() {
|
||||
ubyte ci
|
||||
txt.nl()
|
||||
planet4.print_name_uppercase()
|
||||
txt.print(" trade market4:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||
for ci in 0 to len(names)-1 {
|
||||
util4.print_right(13, names[ci])
|
||||
txt.print(" ")
|
||||
util4.print_10s(current_price[ci])
|
||||
txt.print(" ")
|
||||
txt.print_ub(current_quantity[ci])
|
||||
when units[ci] {
|
||||
0 -> txt.chrout('t')
|
||||
1 -> txt.print("kg")
|
||||
2 -> txt.chrout('g')
|
||||
}
|
||||
txt.print(" ")
|
||||
txt.print_ub(ship4.cargohold[ci])
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub match(uword nameptr) -> ubyte {
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
if util4.prefix_matches(nameptr, names[ci])
|
||||
return ci
|
||||
}
|
||||
return 255
|
||||
}
|
||||
}
|
||||
|
||||
galaxy4 {
|
||||
const uword GALSIZE = 256
|
||||
const uword base0 = $5A4A ; seeds for the first galaxy4
|
||||
const uword base1 = $0248
|
||||
const uword base2 = $B753
|
||||
|
||||
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||
|
||||
ubyte number
|
||||
|
||||
uword[3] seed
|
||||
|
||||
sub init(ubyte galaxy4num) {
|
||||
number = 1
|
||||
planet4.number = 255
|
||||
seed[0] = base0
|
||||
seed[1] = base1
|
||||
seed[2] = base2
|
||||
repeat galaxy4num-1 {
|
||||
nextgalaxy4()
|
||||
}
|
||||
}
|
||||
|
||||
sub nextgalaxy4() {
|
||||
seed[0] = twist(seed[0])
|
||||
seed[1] = twist(seed[1])
|
||||
seed[2] = twist(seed[2])
|
||||
number++
|
||||
if number==9
|
||||
number = 1
|
||||
}
|
||||
|
||||
sub travel_to(ubyte galaxy4num, ubyte system) {
|
||||
init(galaxy4num)
|
||||
generate_next_planet4() ; always at least planet4 0 (separate to avoid repeat ubyte overflow)
|
||||
repeat system {
|
||||
generate_next_planet4()
|
||||
}
|
||||
planet4.name = make_current_planet4_name()
|
||||
init_market4_for_planet4()
|
||||
}
|
||||
|
||||
sub init_market4_for_planet4() {
|
||||
market4.init(lsb(seed[0])+msb(seed[2]))
|
||||
}
|
||||
|
||||
sub search_closest_planet4(uword nameptr) -> ubyte {
|
||||
ubyte x = planet4.x
|
||||
ubyte y = planet4.y
|
||||
ubyte current_planet4_num = planet4.number
|
||||
|
||||
init(number)
|
||||
ubyte found = false
|
||||
ubyte current_closest_pi
|
||||
ubyte current_distance = 127
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet4()
|
||||
planet4.name = make_current_planet4_name()
|
||||
if util4.prefix_matches(nameptr, planet4.name) {
|
||||
ubyte distance = planet4.distance(x, y)
|
||||
if distance < current_distance {
|
||||
current_distance = distance
|
||||
current_closest_pi = pi
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found
|
||||
travel_to(number, current_closest_pi)
|
||||
else
|
||||
travel_to(number, current_planet4_num)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
sub local_area() {
|
||||
ubyte current_planet4 = planet4.number
|
||||
ubyte px = planet4.x
|
||||
ubyte py = planet4.y
|
||||
ubyte pn = 0
|
||||
|
||||
init(number)
|
||||
txt.print("\nGalaxy #")
|
||||
txt.print_ub(number)
|
||||
txt.print(" - systems in vicinity:\n")
|
||||
do {
|
||||
generate_next_planet4()
|
||||
ubyte distance = planet4.distance(px, py)
|
||||
if distance <= ship4.Max_fuel {
|
||||
if distance <= ship4.fuel
|
||||
txt.chrout('*')
|
||||
else
|
||||
txt.chrout('-')
|
||||
txt.spc()
|
||||
planet4.name = make_current_planet4_name()
|
||||
planet4.display(true)
|
||||
txt.print(" (")
|
||||
util4.print_10s(distance)
|
||||
txt.print(" LY)\n")
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
travel_to(number, current_planet4)
|
||||
}
|
||||
|
||||
ubyte pn_pair1
|
||||
ubyte pn_pair2
|
||||
ubyte pn_pair3
|
||||
ubyte pn_pair4
|
||||
ubyte longname
|
||||
|
||||
sub generate_next_planet4() {
|
||||
determine_planet4_properties()
|
||||
longname = lsb(seed[0]) & 64
|
||||
|
||||
; Always four iterations of random number
|
||||
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
}
|
||||
|
||||
sub make_current_planet4_name() -> str {
|
||||
ubyte ni = 0
|
||||
str name = " " ; max 8
|
||||
|
||||
if pn_pairs[pn_pair1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair1+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3+1]
|
||||
ni++
|
||||
}
|
||||
|
||||
if longname {
|
||||
if pn_pairs[pn_pair4] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair4+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4+1]
|
||||
ni++
|
||||
}
|
||||
}
|
||||
|
||||
name[ni] = 0
|
||||
return name
|
||||
}
|
||||
|
||||
sub determine_planet4_properties() {
|
||||
; create the planet4's characteristics
|
||||
planet4.number++
|
||||
planet4.x = msb(seed[1])
|
||||
planet4.y = msb(seed[0])
|
||||
planet4.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||
planet4.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||
if planet4.govtype <= 1
|
||||
planet4.economy = (planet4.economy | 2)
|
||||
planet4.techlevel = (msb(seed[1]) & 3) + (planet4.economy ^ 7)
|
||||
planet4.techlevel += planet4.govtype >> 1
|
||||
if planet4.govtype & 1
|
||||
planet4.techlevel++
|
||||
planet4.population = 4 * planet4.techlevel + planet4.economy
|
||||
planet4.population += planet4.govtype + 1
|
||||
planet4.productivity = ((planet4.economy ^ 7) + 3) * (planet4.govtype + 4)
|
||||
planet4.productivity *= planet4.population * 8
|
||||
ubyte seed2_msb = msb(seed[2])
|
||||
planet4.radius = mkword((seed2_msb & 15) + 11, planet4.x)
|
||||
planet4.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
|
||||
if planet4.species_is_alien {
|
||||
planet4.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||
planet4.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||
planet4.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||
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[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() {
|
||||
uword temp = seed[0] + seed[1] + seed[2]
|
||||
seed[0] = seed[1]
|
||||
seed[1] = seed[2]
|
||||
seed[2] = temp
|
||||
}
|
||||
|
||||
sub twist(uword x) -> uword {
|
||||
ubyte xh = msb(x)
|
||||
ubyte xl = lsb(x)
|
||||
rol(xh)
|
||||
rol(xl)
|
||||
return mkword(xh, xl)
|
||||
}
|
||||
|
||||
sub debug_seed() {
|
||||
txt.print("\ngalaxy4 #")
|
||||
txt.print_ub(number)
|
||||
txt.print("\ngalaxy4 seed0=")
|
||||
txt.print_uwhex(galaxy4.seed[0], true)
|
||||
txt.print("\ngalaxy4 seed1=")
|
||||
txt.print_uwhex(galaxy4.seed[1], true)
|
||||
txt.print("\ngalaxy4 seed2=")
|
||||
txt.print_uwhex(galaxy4.seed[2], true)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
planet4 {
|
||||
%option force_output
|
||||
|
||||
str[] species_sizes = ["Large", "Fierce", "Small"]
|
||||
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
|
||||
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
|
||||
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
|
||||
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship4", "Communist", "Confederacy", "Democracy", "Corporate State"]
|
||||
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
|
||||
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
|
||||
|
||||
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||
str[] words8F = ["\xB0", "The planet4 \xB0", "The world \xB0", "This planet4", "This world"]
|
||||
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||
str[] words91 = ["planet4", "world", "place", "little planet4", "dump"]
|
||||
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||
|
||||
uword[] wordlists = [
|
||||
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||
|
||||
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||
|
||||
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||
|
||||
str name = " " ; 8 max
|
||||
ubyte number ; starts at 0 in new galaxy4, then increases by 1 for each generated planet4
|
||||
ubyte x
|
||||
ubyte y
|
||||
ubyte economy
|
||||
ubyte govtype
|
||||
ubyte techlevel
|
||||
ubyte population
|
||||
uword productivity
|
||||
uword radius
|
||||
ubyte species_is_alien ; otherwise "Human Colonials"
|
||||
ubyte species_size
|
||||
ubyte species_color
|
||||
ubyte species_look
|
||||
ubyte species_kind
|
||||
|
||||
sub set_seed(uword s1, uword s2) {
|
||||
goatsoup_seed[0] = lsb(s1)
|
||||
goatsoup_seed[1] = msb(s1)
|
||||
goatsoup_seed[2] = lsb(s2)
|
||||
goatsoup_seed[3] = msb(s2)
|
||||
reset_rnd()
|
||||
}
|
||||
|
||||
sub reset_rnd() {
|
||||
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||
}
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str name = " " ; 8 chars max
|
||||
ubyte nx = 0
|
||||
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||
ubyte x = goatsoup_rnd_number() & $3e
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
x++
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
}
|
||||
name[nx] = 0
|
||||
name[0] |= 32 ; uppercase first letter
|
||||
return name
|
||||
}
|
||||
|
||||
sub goatsoup_rnd_number() -> ubyte {
|
||||
ubyte x = goatsoup_rnd[0] * 2
|
||||
uword a = x as uword + goatsoup_rnd[2]
|
||||
if goatsoup_rnd[0] > 127
|
||||
a ++
|
||||
goatsoup_rnd[0] = lsb(a)
|
||||
goatsoup_rnd[2] = x
|
||||
x = goatsoup_rnd[1]
|
||||
ubyte ac = x + goatsoup_rnd[3] + msb(a)
|
||||
goatsoup_rnd[1] = ac
|
||||
goatsoup_rnd[3] = x
|
||||
return ac
|
||||
}
|
||||
|
||||
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||
uword ax
|
||||
uword ay
|
||||
if px>x
|
||||
ax=px-x
|
||||
else
|
||||
ax=x-px
|
||||
if py>y
|
||||
ay=py-y
|
||||
else
|
||||
ay=y-py
|
||||
ay /= 2
|
||||
ubyte d = sqrt16(ax*ax + ay*ay)
|
||||
if d>63
|
||||
return 255
|
||||
return d*4
|
||||
}
|
||||
|
||||
sub soup() -> str {
|
||||
str planet4_result = " " * 160
|
||||
uword[6] source_stack
|
||||
ubyte stack_ptr = 0
|
||||
str start_source = "\x8F is \x97."
|
||||
uword source_ptr = &start_source
|
||||
uword result_ptr = &planet4_result
|
||||
|
||||
reset_rnd()
|
||||
recursive_soup()
|
||||
return planet4_result
|
||||
|
||||
sub recursive_soup() {
|
||||
repeat {
|
||||
ubyte c = @(source_ptr)
|
||||
source_ptr++
|
||||
if c == $00 {
|
||||
@(result_ptr) = 0
|
||||
return
|
||||
}
|
||||
else if c <= $80 {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
else {
|
||||
if c <= $a4 {
|
||||
ubyte rnr = goatsoup_rnd_number()
|
||||
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
|
||||
source_stack[stack_ptr] = source_ptr
|
||||
stack_ptr++
|
||||
source_ptr = getword(c, wordNr)
|
||||
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||
stack_ptr--
|
||||
source_ptr = source_stack[stack_ptr]
|
||||
} else {
|
||||
if c == $b0 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
concat_string(&name + 1)
|
||||
}
|
||||
else if c == $b1 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
ubyte ni
|
||||
for ni in 1 to len(name) {
|
||||
ubyte cc = name[ni]
|
||||
if cc=='e' or cc=='o' or cc==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = cc
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
@(result_ptr) = 'i'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'a'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'n'
|
||||
result_ptr++
|
||||
}
|
||||
else if c == $b2 {
|
||||
concat_string(random_name())
|
||||
}
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub concat_string(uword str_ptr) {
|
||||
repeat {
|
||||
ubyte c = @(str_ptr)
|
||||
if c==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
str_ptr++
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub display(ubyte compressed) {
|
||||
if compressed {
|
||||
print_name_uppercase()
|
||||
txt.print(" TL:")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.spc()
|
||||
txt.print(econnames[economy])
|
||||
txt.spc()
|
||||
txt.print(govnames[govtype])
|
||||
} else {
|
||||
txt.print("\n\nSystem: ")
|
||||
print_name_uppercase()
|
||||
txt.print("\nPosition: ")
|
||||
txt.print_ub(x)
|
||||
txt.chrout('\'')
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
txt.chrout('#')
|
||||
txt.print_ub(number)
|
||||
txt.print("\nEconomy: ")
|
||||
txt.print(econnames[economy])
|
||||
txt.print("\nGovernment: ")
|
||||
txt.print(govnames[govtype])
|
||||
txt.print("\nTech Level: ")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.print("\nTurnover: ")
|
||||
txt.print_uw(productivity)
|
||||
txt.print("\nRadius: ")
|
||||
txt.print_uw(radius)
|
||||
txt.print("\nPopulation: ")
|
||||
txt.print_ub(population >> 3)
|
||||
txt.print(" Billion\nSpecies: ")
|
||||
if species_is_alien {
|
||||
if species_size < len(species_sizes) {
|
||||
txt.print(species_sizes[species_size])
|
||||
txt.spc()
|
||||
}
|
||||
if species_color < len(species_colors) {
|
||||
txt.print(species_colors[species_color])
|
||||
txt.spc()
|
||||
}
|
||||
if species_look < len(species_looks) {
|
||||
txt.print(species_looks[species_look])
|
||||
txt.spc()
|
||||
}
|
||||
if species_kind < len(species_kinds) {
|
||||
txt.print(species_kinds[species_kind])
|
||||
}
|
||||
} else {
|
||||
txt.print("Human Colonials")
|
||||
}
|
||||
txt.nl()
|
||||
txt.print(soup())
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub print_name_uppercase() {
|
||||
ubyte c
|
||||
for c in name
|
||||
txt.chrout(c | 32)
|
||||
}
|
||||
|
||||
asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY {
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc #$81
|
||||
asl a
|
||||
tay
|
||||
lda wordlists,y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda wordlists+1,y
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
util4 {
|
||||
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
|
||||
repeat {
|
||||
ubyte pc = @(prefixptr)
|
||||
ubyte sc = @(stringptr)
|
||||
if pc == 0
|
||||
return true
|
||||
; to lowercase for case insensitive compare:
|
||||
pc &= 127
|
||||
sc &= 127
|
||||
if pc != sc
|
||||
return false
|
||||
prefixptr++
|
||||
stringptr++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub print_right(ubyte width, uword st) {
|
||||
repeat width - string.length(st) {
|
||||
txt.spc()
|
||||
}
|
||||
txt.print(st)
|
||||
}
|
||||
|
||||
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
lda conv.uword2decimal.decTenThousands
|
||||
ldy #0 ; have we started printing?
|
||||
cmp #'0'
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decThousands
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decHundreds
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decTens
|
||||
jsr c64.CHROUT
|
||||
lda #'.'
|
||||
jsr c64.CHROUT
|
||||
lda conv.uword2decimal.decOnes
|
||||
jsr c64.CHROUT
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,994 +0,0 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import diskio
|
||||
%import string
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
; Prog8 adaptation of the Text-Elite galaxy5 system trading simulation engine.
|
||||
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
perf5 {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet5 in galaxy5 one
|
||||
const ubyte numforZaonce = 129
|
||||
const ubyte numforDiso = 147
|
||||
const ubyte numforRiedquat = 46
|
||||
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
txt.print("\u000c\n --- TextElite v1.1 ---\n")
|
||||
|
||||
galaxy5.travel_to(1, numforLave)
|
||||
market5.init(0) ; Lave's market5 is seeded with 0
|
||||
ship5.init()
|
||||
planet5.display(false)
|
||||
|
||||
repeat {
|
||||
; test_stack.test()
|
||||
|
||||
str input = "????????"
|
||||
txt.print("\nCash: ")
|
||||
util5.print_10s(ship5.cash)
|
||||
txt.print("\nCommand (?=help): ")
|
||||
ubyte num_chars = txt.input_chars(input)
|
||||
txt.nl()
|
||||
if num_chars {
|
||||
when input[0] {
|
||||
'?' -> {
|
||||
txt.print("\nCommands are:\n"+
|
||||
"buy jump info cash >=save\n"+
|
||||
"sell teleport market5 hold <=load\n"+
|
||||
"fuel galhyp local quit\n")
|
||||
}
|
||||
'q' -> break
|
||||
'b' -> trader5.do_buy()
|
||||
's' -> trader5.do_sell()
|
||||
'f' -> trader5.do_fuel()
|
||||
'j' -> trader5.do_jump()
|
||||
't' -> trader5.do_teleport()
|
||||
'g' -> trader5.do_next_galaxy5()
|
||||
'i' -> trader5.do_info()
|
||||
'm' -> trader5.do_show_market5()
|
||||
'l' -> trader5.do_local()
|
||||
'c' -> trader5.do_cash()
|
||||
'h' -> trader5.do_hold()
|
||||
'<' -> trader5.do_load()
|
||||
'>' -> trader5.do_save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trader5 {
|
||||
str Savegame = "↑commander.save"
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
struct SaveData {
|
||||
ubyte galaxy5
|
||||
ubyte planet5
|
||||
ubyte cargo0
|
||||
ubyte cargo1
|
||||
ubyte cargo2
|
||||
ubyte cargo3
|
||||
ubyte cargo4
|
||||
ubyte cargo5
|
||||
ubyte cargo6
|
||||
ubyte cargo7
|
||||
ubyte cargo8
|
||||
ubyte cargo9
|
||||
ubyte cargo10
|
||||
ubyte cargo11
|
||||
ubyte cargo12
|
||||
ubyte cargo13
|
||||
ubyte cargo14
|
||||
ubyte cargo15
|
||||
ubyte cargo16
|
||||
uword cash
|
||||
ubyte max_cargo
|
||||
ubyte fuel
|
||||
}
|
||||
SaveData savedata
|
||||
|
||||
sub do_load() {
|
||||
txt.print("\nLoading universe...")
|
||||
if diskio.load(8, Savegame, &savedata) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
return
|
||||
}
|
||||
|
||||
ship5.cash = savedata.cash
|
||||
ship5.Max_cargo = savedata.max_cargo
|
||||
ship5.fuel = savedata.fuel
|
||||
sys.memcopy(&savedata.cargo0, ship5.cargohold, len(ship5.cargohold))
|
||||
galaxy5.travel_to(savedata.galaxy5, savedata.planet5)
|
||||
|
||||
planet5.display(false)
|
||||
}
|
||||
|
||||
sub do_save() {
|
||||
savedata.galaxy5 = galaxy5.number
|
||||
savedata.planet5 = planet5.number
|
||||
savedata.cash = ship5.cash
|
||||
savedata.max_cargo = ship5.Max_cargo
|
||||
savedata.fuel = ship5.fuel
|
||||
sys.memcopy(ship5.cargohold, &savedata.cargo0, len(ship5.cargohold))
|
||||
|
||||
txt.print("\nSaving universe...")
|
||||
diskio.delete(8, Savegame)
|
||||
if diskio.save(8, Savegame, &savedata, sizeof(savedata)) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub do_jump() {
|
||||
txt.print("\nJump to what system? ")
|
||||
jump_to_system()
|
||||
}
|
||||
|
||||
sub do_teleport() {
|
||||
txt.print("\nCheat! Teleport to what system? ")
|
||||
ubyte fuel = ship5.fuel
|
||||
ship5.fuel = 255
|
||||
jump_to_system()
|
||||
ship5.fuel = fuel
|
||||
}
|
||||
|
||||
sub jump_to_system() {
|
||||
void txt.input_chars(input)
|
||||
ubyte current_planet5 = planet5.number
|
||||
ubyte x = planet5.x
|
||||
ubyte y = planet5.y
|
||||
if galaxy5.search_closest_planet5(input) {
|
||||
ubyte distance = planet5.distance(x, y)
|
||||
if distance <= ship5.fuel {
|
||||
galaxy5.init_market5_for_planet5()
|
||||
ship5.fuel -= distance
|
||||
txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||
planet5.display(true)
|
||||
return
|
||||
}
|
||||
txt.print("Insufficient fuel\n")
|
||||
} else {
|
||||
txt.print(" Not found!\n")
|
||||
}
|
||||
galaxy5.travel_to(galaxy5.number, current_planet5)
|
||||
}
|
||||
|
||||
sub do_buy() {
|
||||
txt.print("\nBuy what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market5.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if market5.current_quantity[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market5.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util5.print_10s(price)
|
||||
if price > ship5.cash {
|
||||
txt.print(" Not enough cash!\n")
|
||||
} else {
|
||||
ship5.cash -= price
|
||||
ship5.cargohold[ci] += amount
|
||||
market5.current_quantity[ci] -= amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_sell() {
|
||||
txt.print("\nSell what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market5.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if ship5.cargohold[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market5.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util5.print_10s(price)
|
||||
ship5.cash += price
|
||||
ship5.cargohold[ci] -= amount
|
||||
market5.current_quantity[ci] += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_fuel() {
|
||||
txt.print("\nBuy fuel. Amount? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||
ubyte max_fuel = ship5.Max_fuel - ship5.fuel
|
||||
if buy_fuel > max_fuel
|
||||
buy_fuel = max_fuel
|
||||
uword price = buy_fuel as uword * ship5.Fuel_cost
|
||||
if price > ship5.cash {
|
||||
txt.print("Not enough cash!\n")
|
||||
} else {
|
||||
ship5.cash -= price
|
||||
ship5.fuel += buy_fuel
|
||||
}
|
||||
}
|
||||
|
||||
sub do_cash() {
|
||||
txt.print("\nCheat! Set cash amount: ")
|
||||
void txt.input_chars(input)
|
||||
ship5.cash = conv.str2uword(input)
|
||||
}
|
||||
|
||||
sub do_hold() {
|
||||
txt.print("\nCheat! Set cargohold size: ")
|
||||
void txt.input_chars(input)
|
||||
ship5.Max_cargo = conv.str2ubyte(input)
|
||||
}
|
||||
|
||||
sub do_next_galaxy5() {
|
||||
galaxy5.travel_to(galaxy5.number+1, planet5.number)
|
||||
planet5.display(false)
|
||||
}
|
||||
|
||||
sub do_info() {
|
||||
txt.print("\nSystem name (empty=current): ")
|
||||
num_chars = txt.input_chars(input)
|
||||
if num_chars {
|
||||
ubyte current_planet5 = planet5.number
|
||||
if galaxy5.search_closest_planet5(input) {
|
||||
planet5.display(false)
|
||||
} else {
|
||||
txt.print(" Not found!")
|
||||
}
|
||||
galaxy5.travel_to(galaxy5.number, current_planet5)
|
||||
} else {
|
||||
planet5.display(false)
|
||||
}
|
||||
}
|
||||
|
||||
sub do_local() {
|
||||
galaxy5.local_area()
|
||||
}
|
||||
|
||||
sub do_show_market5() {
|
||||
market5.display()
|
||||
txt.print("\nFuel: ")
|
||||
util5.print_10s(ship5.fuel)
|
||||
txt.print(" Cargohold space: ")
|
||||
txt.print_ub(ship5.cargo_free())
|
||||
txt.print("t\n")
|
||||
}
|
||||
}
|
||||
|
||||
ship5 {
|
||||
const ubyte Max_fuel = 70
|
||||
const ubyte Fuel_cost = 2
|
||||
ubyte Max_cargo = 20
|
||||
|
||||
ubyte fuel = Max_fuel
|
||||
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
|
||||
ubyte[17] cargohold = 0
|
||||
|
||||
sub init() {
|
||||
sys.memset(cargohold, len(cargohold), 0)
|
||||
}
|
||||
|
||||
sub cargo_free() -> ubyte {
|
||||
ubyte ci
|
||||
ubyte total = 0
|
||||
for ci in 0 to len(cargohold)-1 {
|
||||
if market5.units[ci]==0 ; tonnes only
|
||||
total += cargohold[ci]
|
||||
}
|
||||
return Max_cargo - total
|
||||
}
|
||||
}
|
||||
|
||||
market5 {
|
||||
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
|
||||
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||
|
||||
ubyte[17] current_quantity = 0
|
||||
uword[17] current_price = 0
|
||||
|
||||
sub init(ubyte fluct) {
|
||||
; Prices and availabilities are influenced by the planet5's economy type
|
||||
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||
; commander position to keep the market5 prices constant over gamesaves.
|
||||
; Availabilities must be saved with the game since the player alters them
|
||||
; by buying (and selling(?))
|
||||
;
|
||||
; Almost all operations are one byte only and overflow "errors" are
|
||||
; extremely frequent and exploited.
|
||||
;
|
||||
; Trade Item prices are held internally in a single byte=true value/4.
|
||||
; The decimal point in prices is introduced only when printing them.
|
||||
; Internally, all prices are integers.
|
||||
; The player's cash is held in four bytes.
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
word product
|
||||
byte changing
|
||||
product = planet5.economy as word * gradients[ci]
|
||||
changing = fluct & maskbytes[ci] as byte
|
||||
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||
if q & $80
|
||||
q = 0 ; clip to positive 8-bit
|
||||
current_quantity[ci] = q & $3f
|
||||
q = (baseprices[ci] + changing + product) as ubyte
|
||||
current_price[ci] = q * $0004
|
||||
}
|
||||
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||
}
|
||||
|
||||
sub display() {
|
||||
ubyte ci
|
||||
txt.nl()
|
||||
planet5.print_name_uppercase()
|
||||
txt.print(" trade market5:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||
for ci in 0 to len(names)-1 {
|
||||
util5.print_right(13, names[ci])
|
||||
txt.print(" ")
|
||||
util5.print_10s(current_price[ci])
|
||||
txt.print(" ")
|
||||
txt.print_ub(current_quantity[ci])
|
||||
when units[ci] {
|
||||
0 -> txt.chrout('t')
|
||||
1 -> txt.print("kg")
|
||||
2 -> txt.chrout('g')
|
||||
}
|
||||
txt.print(" ")
|
||||
txt.print_ub(ship5.cargohold[ci])
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub match(uword nameptr) -> ubyte {
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
if util5.prefix_matches(nameptr, names[ci])
|
||||
return ci
|
||||
}
|
||||
return 255
|
||||
}
|
||||
}
|
||||
|
||||
galaxy5 {
|
||||
const uword GALSIZE = 256
|
||||
const uword base0 = $5A4A ; seeds for the first galaxy5
|
||||
const uword base1 = $0248
|
||||
const uword base2 = $B753
|
||||
|
||||
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||
|
||||
ubyte number
|
||||
|
||||
uword[3] seed
|
||||
|
||||
sub init(ubyte galaxy5num) {
|
||||
number = 1
|
||||
planet5.number = 255
|
||||
seed[0] = base0
|
||||
seed[1] = base1
|
||||
seed[2] = base2
|
||||
repeat galaxy5num-1 {
|
||||
nextgalaxy5()
|
||||
}
|
||||
}
|
||||
|
||||
sub nextgalaxy5() {
|
||||
seed[0] = twist(seed[0])
|
||||
seed[1] = twist(seed[1])
|
||||
seed[2] = twist(seed[2])
|
||||
number++
|
||||
if number==9
|
||||
number = 1
|
||||
}
|
||||
|
||||
sub travel_to(ubyte galaxy5num, ubyte system) {
|
||||
init(galaxy5num)
|
||||
generate_next_planet5() ; always at least planet5 0 (separate to avoid repeat ubyte overflow)
|
||||
repeat system {
|
||||
generate_next_planet5()
|
||||
}
|
||||
planet5.name = make_current_planet5_name()
|
||||
init_market5_for_planet5()
|
||||
}
|
||||
|
||||
sub init_market5_for_planet5() {
|
||||
market5.init(lsb(seed[0])+msb(seed[2]))
|
||||
}
|
||||
|
||||
sub search_closest_planet5(uword nameptr) -> ubyte {
|
||||
ubyte x = planet5.x
|
||||
ubyte y = planet5.y
|
||||
ubyte current_planet5_num = planet5.number
|
||||
|
||||
init(number)
|
||||
ubyte found = false
|
||||
ubyte current_closest_pi
|
||||
ubyte current_distance = 127
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet5()
|
||||
planet5.name = make_current_planet5_name()
|
||||
if util5.prefix_matches(nameptr, planet5.name) {
|
||||
ubyte distance = planet5.distance(x, y)
|
||||
if distance < current_distance {
|
||||
current_distance = distance
|
||||
current_closest_pi = pi
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found
|
||||
travel_to(number, current_closest_pi)
|
||||
else
|
||||
travel_to(number, current_planet5_num)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
sub local_area() {
|
||||
ubyte current_planet5 = planet5.number
|
||||
ubyte px = planet5.x
|
||||
ubyte py = planet5.y
|
||||
ubyte pn = 0
|
||||
|
||||
init(number)
|
||||
txt.print("\nGalaxy #")
|
||||
txt.print_ub(number)
|
||||
txt.print(" - systems in vicinity:\n")
|
||||
do {
|
||||
generate_next_planet5()
|
||||
ubyte distance = planet5.distance(px, py)
|
||||
if distance <= ship5.Max_fuel {
|
||||
if distance <= ship5.fuel
|
||||
txt.chrout('*')
|
||||
else
|
||||
txt.chrout('-')
|
||||
txt.spc()
|
||||
planet5.name = make_current_planet5_name()
|
||||
planet5.display(true)
|
||||
txt.print(" (")
|
||||
util5.print_10s(distance)
|
||||
txt.print(" LY)\n")
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
travel_to(number, current_planet5)
|
||||
}
|
||||
|
||||
ubyte pn_pair1
|
||||
ubyte pn_pair2
|
||||
ubyte pn_pair3
|
||||
ubyte pn_pair4
|
||||
ubyte longname
|
||||
|
||||
sub generate_next_planet5() {
|
||||
determine_planet5_properties()
|
||||
longname = lsb(seed[0]) & 64
|
||||
|
||||
; Always four iterations of random number
|
||||
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
}
|
||||
|
||||
sub make_current_planet5_name() -> str {
|
||||
ubyte ni = 0
|
||||
str name = " " ; max 8
|
||||
|
||||
if pn_pairs[pn_pair1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair1+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3+1]
|
||||
ni++
|
||||
}
|
||||
|
||||
if longname {
|
||||
if pn_pairs[pn_pair4] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair4+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4+1]
|
||||
ni++
|
||||
}
|
||||
}
|
||||
|
||||
name[ni] = 0
|
||||
return name
|
||||
}
|
||||
|
||||
sub determine_planet5_properties() {
|
||||
; create the planet5's characteristics
|
||||
planet5.number++
|
||||
planet5.x = msb(seed[1])
|
||||
planet5.y = msb(seed[0])
|
||||
planet5.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||
planet5.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||
if planet5.govtype <= 1
|
||||
planet5.economy = (planet5.economy | 2)
|
||||
planet5.techlevel = (msb(seed[1]) & 3) + (planet5.economy ^ 7)
|
||||
planet5.techlevel += planet5.govtype >> 1
|
||||
if planet5.govtype & 1
|
||||
planet5.techlevel++
|
||||
planet5.population = 4 * planet5.techlevel + planet5.economy
|
||||
planet5.population += planet5.govtype + 1
|
||||
planet5.productivity = ((planet5.economy ^ 7) + 3) * (planet5.govtype + 4)
|
||||
planet5.productivity *= planet5.population * 8
|
||||
ubyte seed2_msb = msb(seed[2])
|
||||
planet5.radius = mkword((seed2_msb & 15) + 11, planet5.x)
|
||||
planet5.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
|
||||
if planet5.species_is_alien {
|
||||
planet5.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||
planet5.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||
planet5.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||
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[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() {
|
||||
uword temp = seed[0] + seed[1] + seed[2]
|
||||
seed[0] = seed[1]
|
||||
seed[1] = seed[2]
|
||||
seed[2] = temp
|
||||
}
|
||||
|
||||
sub twist(uword x) -> uword {
|
||||
ubyte xh = msb(x)
|
||||
ubyte xl = lsb(x)
|
||||
rol(xh)
|
||||
rol(xl)
|
||||
return mkword(xh, xl)
|
||||
}
|
||||
|
||||
sub debug_seed() {
|
||||
txt.print("\ngalaxy5 #")
|
||||
txt.print_ub(number)
|
||||
txt.print("\ngalaxy5 seed0=")
|
||||
txt.print_uwhex(galaxy5.seed[0], true)
|
||||
txt.print("\ngalaxy5 seed1=")
|
||||
txt.print_uwhex(galaxy5.seed[1], true)
|
||||
txt.print("\ngalaxy5 seed2=")
|
||||
txt.print_uwhex(galaxy5.seed[2], true)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
planet5 {
|
||||
%option force_output
|
||||
|
||||
str[] species_sizes = ["Large", "Fierce", "Small"]
|
||||
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
|
||||
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
|
||||
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
|
||||
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship5", "Communist", "Confederacy", "Democracy", "Corporate State"]
|
||||
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
|
||||
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
|
||||
|
||||
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||
str[] words8F = ["\xB0", "The planet5 \xB0", "The world \xB0", "This planet5", "This world"]
|
||||
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||
str[] words91 = ["planet5", "world", "place", "little planet5", "dump"]
|
||||
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||
|
||||
uword[] wordlists = [
|
||||
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||
|
||||
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||
|
||||
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||
|
||||
str name = " " ; 8 max
|
||||
ubyte number ; starts at 0 in new galaxy5, then increases by 1 for each generated planet5
|
||||
ubyte x
|
||||
ubyte y
|
||||
ubyte economy
|
||||
ubyte govtype
|
||||
ubyte techlevel
|
||||
ubyte population
|
||||
uword productivity
|
||||
uword radius
|
||||
ubyte species_is_alien ; otherwise "Human Colonials"
|
||||
ubyte species_size
|
||||
ubyte species_color
|
||||
ubyte species_look
|
||||
ubyte species_kind
|
||||
|
||||
sub set_seed(uword s1, uword s2) {
|
||||
goatsoup_seed[0] = lsb(s1)
|
||||
goatsoup_seed[1] = msb(s1)
|
||||
goatsoup_seed[2] = lsb(s2)
|
||||
goatsoup_seed[3] = msb(s2)
|
||||
reset_rnd()
|
||||
}
|
||||
|
||||
sub reset_rnd() {
|
||||
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||
}
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str name = " " ; 8 chars max
|
||||
ubyte nx = 0
|
||||
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||
ubyte x = goatsoup_rnd_number() & $3e
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
x++
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
}
|
||||
name[nx] = 0
|
||||
name[0] |= 32 ; uppercase first letter
|
||||
return name
|
||||
}
|
||||
|
||||
sub goatsoup_rnd_number() -> ubyte {
|
||||
ubyte x = goatsoup_rnd[0] * 2
|
||||
uword a = x as uword + goatsoup_rnd[2]
|
||||
if goatsoup_rnd[0] > 127
|
||||
a ++
|
||||
goatsoup_rnd[0] = lsb(a)
|
||||
goatsoup_rnd[2] = x
|
||||
x = goatsoup_rnd[1]
|
||||
ubyte ac = x + goatsoup_rnd[3] + msb(a)
|
||||
goatsoup_rnd[1] = ac
|
||||
goatsoup_rnd[3] = x
|
||||
return ac
|
||||
}
|
||||
|
||||
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||
uword ax
|
||||
uword ay
|
||||
if px>x
|
||||
ax=px-x
|
||||
else
|
||||
ax=x-px
|
||||
if py>y
|
||||
ay=py-y
|
||||
else
|
||||
ay=y-py
|
||||
ay /= 2
|
||||
ubyte d = sqrt16(ax*ax + ay*ay)
|
||||
if d>63
|
||||
return 255
|
||||
return d*4
|
||||
}
|
||||
|
||||
sub soup() -> str {
|
||||
str planet5_result = " " * 160
|
||||
uword[6] source_stack
|
||||
ubyte stack_ptr = 0
|
||||
str start_source = "\x8F is \x97."
|
||||
uword source_ptr = &start_source
|
||||
uword result_ptr = &planet5_result
|
||||
|
||||
reset_rnd()
|
||||
recursive_soup()
|
||||
return planet5_result
|
||||
|
||||
sub recursive_soup() {
|
||||
repeat {
|
||||
ubyte c = @(source_ptr)
|
||||
source_ptr++
|
||||
if c == $00 {
|
||||
@(result_ptr) = 0
|
||||
return
|
||||
}
|
||||
else if c <= $80 {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
else {
|
||||
if c <= $a4 {
|
||||
ubyte rnr = goatsoup_rnd_number()
|
||||
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
|
||||
source_stack[stack_ptr] = source_ptr
|
||||
stack_ptr++
|
||||
source_ptr = getword(c, wordNr)
|
||||
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||
stack_ptr--
|
||||
source_ptr = source_stack[stack_ptr]
|
||||
} else {
|
||||
if c == $b0 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
concat_string(&name + 1)
|
||||
}
|
||||
else if c == $b1 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
ubyte ni
|
||||
for ni in 1 to len(name) {
|
||||
ubyte cc = name[ni]
|
||||
if cc=='e' or cc=='o' or cc==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = cc
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
@(result_ptr) = 'i'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'a'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'n'
|
||||
result_ptr++
|
||||
}
|
||||
else if c == $b2 {
|
||||
concat_string(random_name())
|
||||
}
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub concat_string(uword str_ptr) {
|
||||
repeat {
|
||||
ubyte c = @(str_ptr)
|
||||
if c==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
str_ptr++
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub display(ubyte compressed) {
|
||||
if compressed {
|
||||
print_name_uppercase()
|
||||
txt.print(" TL:")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.spc()
|
||||
txt.print(econnames[economy])
|
||||
txt.spc()
|
||||
txt.print(govnames[govtype])
|
||||
} else {
|
||||
txt.print("\n\nSystem: ")
|
||||
print_name_uppercase()
|
||||
txt.print("\nPosition: ")
|
||||
txt.print_ub(x)
|
||||
txt.chrout('\'')
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
txt.chrout('#')
|
||||
txt.print_ub(number)
|
||||
txt.print("\nEconomy: ")
|
||||
txt.print(econnames[economy])
|
||||
txt.print("\nGovernment: ")
|
||||
txt.print(govnames[govtype])
|
||||
txt.print("\nTech Level: ")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.print("\nTurnover: ")
|
||||
txt.print_uw(productivity)
|
||||
txt.print("\nRadius: ")
|
||||
txt.print_uw(radius)
|
||||
txt.print("\nPopulation: ")
|
||||
txt.print_ub(population >> 3)
|
||||
txt.print(" Billion\nSpecies: ")
|
||||
if species_is_alien {
|
||||
if species_size < len(species_sizes) {
|
||||
txt.print(species_sizes[species_size])
|
||||
txt.spc()
|
||||
}
|
||||
if species_color < len(species_colors) {
|
||||
txt.print(species_colors[species_color])
|
||||
txt.spc()
|
||||
}
|
||||
if species_look < len(species_looks) {
|
||||
txt.print(species_looks[species_look])
|
||||
txt.spc()
|
||||
}
|
||||
if species_kind < len(species_kinds) {
|
||||
txt.print(species_kinds[species_kind])
|
||||
}
|
||||
} else {
|
||||
txt.print("Human Colonials")
|
||||
}
|
||||
txt.nl()
|
||||
txt.print(soup())
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub print_name_uppercase() {
|
||||
ubyte c
|
||||
for c in name
|
||||
txt.chrout(c | 32)
|
||||
}
|
||||
|
||||
asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY {
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc #$81
|
||||
asl a
|
||||
tay
|
||||
lda wordlists,y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda wordlists+1,y
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
util5 {
|
||||
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
|
||||
repeat {
|
||||
ubyte pc = @(prefixptr)
|
||||
ubyte sc = @(stringptr)
|
||||
if pc == 0
|
||||
return true
|
||||
; to lowercase for case insensitive compare:
|
||||
pc &= 127
|
||||
sc &= 127
|
||||
if pc != sc
|
||||
return false
|
||||
prefixptr++
|
||||
stringptr++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub print_right(ubyte width, uword st) {
|
||||
repeat width - string.length(st) {
|
||||
txt.spc()
|
||||
}
|
||||
txt.print(st)
|
||||
}
|
||||
|
||||
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
lda conv.uword2decimal.decTenThousands
|
||||
ldy #0 ; have we started printing?
|
||||
cmp #'0'
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decThousands
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decHundreds
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decTens
|
||||
jsr c64.CHROUT
|
||||
lda #'.'
|
||||
jsr c64.CHROUT
|
||||
lda conv.uword2decimal.decOnes
|
||||
jsr c64.CHROUT
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,994 +0,0 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import diskio
|
||||
%import string
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
; Prog8 adaptation of the Text-Elite galaxy6 system trading simulation engine.
|
||||
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
perf6 {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet6 in galaxy6 one
|
||||
const ubyte numforZaonce = 129
|
||||
const ubyte numforDiso = 147
|
||||
const ubyte numforRiedquat = 46
|
||||
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
txt.print("\u000c\n --- TextElite v1.1 ---\n")
|
||||
|
||||
galaxy6.travel_to(1, numforLave)
|
||||
market6.init(0) ; Lave's market6 is seeded with 0
|
||||
ship6.init()
|
||||
planet6.display(false)
|
||||
|
||||
repeat {
|
||||
; test_stack.test()
|
||||
|
||||
str input = "????????"
|
||||
txt.print("\nCash: ")
|
||||
util6.print_10s(ship6.cash)
|
||||
txt.print("\nCommand (?=help): ")
|
||||
ubyte num_chars = txt.input_chars(input)
|
||||
txt.nl()
|
||||
if num_chars {
|
||||
when input[0] {
|
||||
'?' -> {
|
||||
txt.print("\nCommands are:\n"+
|
||||
"buy jump info cash >=save\n"+
|
||||
"sell teleport market6 hold <=load\n"+
|
||||
"fuel galhyp local quit\n")
|
||||
}
|
||||
'q' -> break
|
||||
'b' -> trader6.do_buy()
|
||||
's' -> trader6.do_sell()
|
||||
'f' -> trader6.do_fuel()
|
||||
'j' -> trader6.do_jump()
|
||||
't' -> trader6.do_teleport()
|
||||
'g' -> trader6.do_next_galaxy6()
|
||||
'i' -> trader6.do_info()
|
||||
'm' -> trader6.do_show_market6()
|
||||
'l' -> trader6.do_local()
|
||||
'c' -> trader6.do_cash()
|
||||
'h' -> trader6.do_hold()
|
||||
'<' -> trader6.do_load()
|
||||
'>' -> trader6.do_save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trader6 {
|
||||
str Savegame = "↑commander.save"
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
struct SaveData {
|
||||
ubyte galaxy6
|
||||
ubyte planet6
|
||||
ubyte cargo0
|
||||
ubyte cargo1
|
||||
ubyte cargo2
|
||||
ubyte cargo3
|
||||
ubyte cargo4
|
||||
ubyte cargo5
|
||||
ubyte cargo6
|
||||
ubyte cargo7
|
||||
ubyte cargo8
|
||||
ubyte cargo9
|
||||
ubyte cargo10
|
||||
ubyte cargo11
|
||||
ubyte cargo12
|
||||
ubyte cargo13
|
||||
ubyte cargo14
|
||||
ubyte cargo15
|
||||
ubyte cargo16
|
||||
uword cash
|
||||
ubyte max_cargo
|
||||
ubyte fuel
|
||||
}
|
||||
SaveData savedata
|
||||
|
||||
sub do_load() {
|
||||
txt.print("\nLoading universe...")
|
||||
if diskio.load(8, Savegame, &savedata) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
return
|
||||
}
|
||||
|
||||
ship6.cash = savedata.cash
|
||||
ship6.Max_cargo = savedata.max_cargo
|
||||
ship6.fuel = savedata.fuel
|
||||
sys.memcopy(&savedata.cargo0, ship6.cargohold, len(ship6.cargohold))
|
||||
galaxy6.travel_to(savedata.galaxy6, savedata.planet6)
|
||||
|
||||
planet6.display(false)
|
||||
}
|
||||
|
||||
sub do_save() {
|
||||
savedata.galaxy6 = galaxy6.number
|
||||
savedata.planet6 = planet6.number
|
||||
savedata.cash = ship6.cash
|
||||
savedata.max_cargo = ship6.Max_cargo
|
||||
savedata.fuel = ship6.fuel
|
||||
sys.memcopy(ship6.cargohold, &savedata.cargo0, len(ship6.cargohold))
|
||||
|
||||
txt.print("\nSaving universe...")
|
||||
diskio.delete(8, Savegame)
|
||||
if diskio.save(8, Savegame, &savedata, sizeof(savedata)) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub do_jump() {
|
||||
txt.print("\nJump to what system? ")
|
||||
jump_to_system()
|
||||
}
|
||||
|
||||
sub do_teleport() {
|
||||
txt.print("\nCheat! Teleport to what system? ")
|
||||
ubyte fuel = ship6.fuel
|
||||
ship6.fuel = 255
|
||||
jump_to_system()
|
||||
ship6.fuel = fuel
|
||||
}
|
||||
|
||||
sub jump_to_system() {
|
||||
void txt.input_chars(input)
|
||||
ubyte current_planet6 = planet6.number
|
||||
ubyte x = planet6.x
|
||||
ubyte y = planet6.y
|
||||
if galaxy6.search_closest_planet6(input) {
|
||||
ubyte distance = planet6.distance(x, y)
|
||||
if distance <= ship6.fuel {
|
||||
galaxy6.init_market6_for_planet6()
|
||||
ship6.fuel -= distance
|
||||
txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||
planet6.display(true)
|
||||
return
|
||||
}
|
||||
txt.print("Insufficient fuel\n")
|
||||
} else {
|
||||
txt.print(" Not found!\n")
|
||||
}
|
||||
galaxy6.travel_to(galaxy6.number, current_planet6)
|
||||
}
|
||||
|
||||
sub do_buy() {
|
||||
txt.print("\nBuy what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market6.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if market6.current_quantity[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market6.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util6.print_10s(price)
|
||||
if price > ship6.cash {
|
||||
txt.print(" Not enough cash!\n")
|
||||
} else {
|
||||
ship6.cash -= price
|
||||
ship6.cargohold[ci] += amount
|
||||
market6.current_quantity[ci] -= amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_sell() {
|
||||
txt.print("\nSell what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market6.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if ship6.cargohold[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market6.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util6.print_10s(price)
|
||||
ship6.cash += price
|
||||
ship6.cargohold[ci] -= amount
|
||||
market6.current_quantity[ci] += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_fuel() {
|
||||
txt.print("\nBuy fuel. Amount? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||
ubyte max_fuel = ship6.Max_fuel - ship6.fuel
|
||||
if buy_fuel > max_fuel
|
||||
buy_fuel = max_fuel
|
||||
uword price = buy_fuel as uword * ship6.Fuel_cost
|
||||
if price > ship6.cash {
|
||||
txt.print("Not enough cash!\n")
|
||||
} else {
|
||||
ship6.cash -= price
|
||||
ship6.fuel += buy_fuel
|
||||
}
|
||||
}
|
||||
|
||||
sub do_cash() {
|
||||
txt.print("\nCheat! Set cash amount: ")
|
||||
void txt.input_chars(input)
|
||||
ship6.cash = conv.str2uword(input)
|
||||
}
|
||||
|
||||
sub do_hold() {
|
||||
txt.print("\nCheat! Set cargohold size: ")
|
||||
void txt.input_chars(input)
|
||||
ship6.Max_cargo = conv.str2ubyte(input)
|
||||
}
|
||||
|
||||
sub do_next_galaxy6() {
|
||||
galaxy6.travel_to(galaxy6.number+1, planet6.number)
|
||||
planet6.display(false)
|
||||
}
|
||||
|
||||
sub do_info() {
|
||||
txt.print("\nSystem name (empty=current): ")
|
||||
num_chars = txt.input_chars(input)
|
||||
if num_chars {
|
||||
ubyte current_planet6 = planet6.number
|
||||
if galaxy6.search_closest_planet6(input) {
|
||||
planet6.display(false)
|
||||
} else {
|
||||
txt.print(" Not found!")
|
||||
}
|
||||
galaxy6.travel_to(galaxy6.number, current_planet6)
|
||||
} else {
|
||||
planet6.display(false)
|
||||
}
|
||||
}
|
||||
|
||||
sub do_local() {
|
||||
galaxy6.local_area()
|
||||
}
|
||||
|
||||
sub do_show_market6() {
|
||||
market6.display()
|
||||
txt.print("\nFuel: ")
|
||||
util6.print_10s(ship6.fuel)
|
||||
txt.print(" Cargohold space: ")
|
||||
txt.print_ub(ship6.cargo_free())
|
||||
txt.print("t\n")
|
||||
}
|
||||
}
|
||||
|
||||
ship6 {
|
||||
const ubyte Max_fuel = 70
|
||||
const ubyte Fuel_cost = 2
|
||||
ubyte Max_cargo = 20
|
||||
|
||||
ubyte fuel = Max_fuel
|
||||
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
|
||||
ubyte[17] cargohold = 0
|
||||
|
||||
sub init() {
|
||||
sys.memset(cargohold, len(cargohold), 0)
|
||||
}
|
||||
|
||||
sub cargo_free() -> ubyte {
|
||||
ubyte ci
|
||||
ubyte total = 0
|
||||
for ci in 0 to len(cargohold)-1 {
|
||||
if market6.units[ci]==0 ; tonnes only
|
||||
total += cargohold[ci]
|
||||
}
|
||||
return Max_cargo - total
|
||||
}
|
||||
}
|
||||
|
||||
market6 {
|
||||
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
|
||||
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||
|
||||
ubyte[17] current_quantity = 0
|
||||
uword[17] current_price = 0
|
||||
|
||||
sub init(ubyte fluct) {
|
||||
; Prices and availabilities are influenced by the planet6's economy type
|
||||
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||
; commander position to keep the market6 prices constant over gamesaves.
|
||||
; Availabilities must be saved with the game since the player alters them
|
||||
; by buying (and selling(?))
|
||||
;
|
||||
; Almost all operations are one byte only and overflow "errors" are
|
||||
; extremely frequent and exploited.
|
||||
;
|
||||
; Trade Item prices are held internally in a single byte=true value/4.
|
||||
; The decimal point in prices is introduced only when printing them.
|
||||
; Internally, all prices are integers.
|
||||
; The player's cash is held in four bytes.
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
word product
|
||||
byte changing
|
||||
product = planet6.economy as word * gradients[ci]
|
||||
changing = fluct & maskbytes[ci] as byte
|
||||
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||
if q & $80
|
||||
q = 0 ; clip to positive 8-bit
|
||||
current_quantity[ci] = q & $3f
|
||||
q = (baseprices[ci] + changing + product) as ubyte
|
||||
current_price[ci] = q * $0004
|
||||
}
|
||||
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||
}
|
||||
|
||||
sub display() {
|
||||
ubyte ci
|
||||
txt.nl()
|
||||
planet6.print_name_uppercase()
|
||||
txt.print(" trade market6:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||
for ci in 0 to len(names)-1 {
|
||||
util6.print_right(13, names[ci])
|
||||
txt.print(" ")
|
||||
util6.print_10s(current_price[ci])
|
||||
txt.print(" ")
|
||||
txt.print_ub(current_quantity[ci])
|
||||
when units[ci] {
|
||||
0 -> txt.chrout('t')
|
||||
1 -> txt.print("kg")
|
||||
2 -> txt.chrout('g')
|
||||
}
|
||||
txt.print(" ")
|
||||
txt.print_ub(ship6.cargohold[ci])
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub match(uword nameptr) -> ubyte {
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
if util6.prefix_matches(nameptr, names[ci])
|
||||
return ci
|
||||
}
|
||||
return 255
|
||||
}
|
||||
}
|
||||
|
||||
galaxy6 {
|
||||
const uword GALSIZE = 256
|
||||
const uword base0 = $5A4A ; seeds for the first galaxy6
|
||||
const uword base1 = $0248
|
||||
const uword base2 = $B753
|
||||
|
||||
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||
|
||||
ubyte number
|
||||
|
||||
uword[3] seed
|
||||
|
||||
sub init(ubyte galaxy6num) {
|
||||
number = 1
|
||||
planet6.number = 255
|
||||
seed[0] = base0
|
||||
seed[1] = base1
|
||||
seed[2] = base2
|
||||
repeat galaxy6num-1 {
|
||||
nextgalaxy6()
|
||||
}
|
||||
}
|
||||
|
||||
sub nextgalaxy6() {
|
||||
seed[0] = twist(seed[0])
|
||||
seed[1] = twist(seed[1])
|
||||
seed[2] = twist(seed[2])
|
||||
number++
|
||||
if number==9
|
||||
number = 1
|
||||
}
|
||||
|
||||
sub travel_to(ubyte galaxy6num, ubyte system) {
|
||||
init(galaxy6num)
|
||||
generate_next_planet6() ; always at least planet6 0 (separate to avoid repeat ubyte overflow)
|
||||
repeat system {
|
||||
generate_next_planet6()
|
||||
}
|
||||
planet6.name = make_current_planet6_name()
|
||||
init_market6_for_planet6()
|
||||
}
|
||||
|
||||
sub init_market6_for_planet6() {
|
||||
market6.init(lsb(seed[0])+msb(seed[2]))
|
||||
}
|
||||
|
||||
sub search_closest_planet6(uword nameptr) -> ubyte {
|
||||
ubyte x = planet6.x
|
||||
ubyte y = planet6.y
|
||||
ubyte current_planet6_num = planet6.number
|
||||
|
||||
init(number)
|
||||
ubyte found = false
|
||||
ubyte current_closest_pi
|
||||
ubyte current_distance = 127
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet6()
|
||||
planet6.name = make_current_planet6_name()
|
||||
if util6.prefix_matches(nameptr, planet6.name) {
|
||||
ubyte distance = planet6.distance(x, y)
|
||||
if distance < current_distance {
|
||||
current_distance = distance
|
||||
current_closest_pi = pi
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found
|
||||
travel_to(number, current_closest_pi)
|
||||
else
|
||||
travel_to(number, current_planet6_num)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
sub local_area() {
|
||||
ubyte current_planet6 = planet6.number
|
||||
ubyte px = planet6.x
|
||||
ubyte py = planet6.y
|
||||
ubyte pn = 0
|
||||
|
||||
init(number)
|
||||
txt.print("\nGalaxy #")
|
||||
txt.print_ub(number)
|
||||
txt.print(" - systems in vicinity:\n")
|
||||
do {
|
||||
generate_next_planet6()
|
||||
ubyte distance = planet6.distance(px, py)
|
||||
if distance <= ship6.Max_fuel {
|
||||
if distance <= ship6.fuel
|
||||
txt.chrout('*')
|
||||
else
|
||||
txt.chrout('-')
|
||||
txt.spc()
|
||||
planet6.name = make_current_planet6_name()
|
||||
planet6.display(true)
|
||||
txt.print(" (")
|
||||
util6.print_10s(distance)
|
||||
txt.print(" LY)\n")
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
travel_to(number, current_planet6)
|
||||
}
|
||||
|
||||
ubyte pn_pair1
|
||||
ubyte pn_pair2
|
||||
ubyte pn_pair3
|
||||
ubyte pn_pair4
|
||||
ubyte longname
|
||||
|
||||
sub generate_next_planet6() {
|
||||
determine_planet6_properties()
|
||||
longname = lsb(seed[0]) & 64
|
||||
|
||||
; Always four iterations of random number
|
||||
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
}
|
||||
|
||||
sub make_current_planet6_name() -> str {
|
||||
ubyte ni = 0
|
||||
str name = " " ; max 8
|
||||
|
||||
if pn_pairs[pn_pair1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair1+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3+1]
|
||||
ni++
|
||||
}
|
||||
|
||||
if longname {
|
||||
if pn_pairs[pn_pair4] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair4+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4+1]
|
||||
ni++
|
||||
}
|
||||
}
|
||||
|
||||
name[ni] = 0
|
||||
return name
|
||||
}
|
||||
|
||||
sub determine_planet6_properties() {
|
||||
; create the planet6's characteristics
|
||||
planet6.number++
|
||||
planet6.x = msb(seed[1])
|
||||
planet6.y = msb(seed[0])
|
||||
planet6.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||
planet6.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||
if planet6.govtype <= 1
|
||||
planet6.economy = (planet6.economy | 2)
|
||||
planet6.techlevel = (msb(seed[1]) & 3) + (planet6.economy ^ 7)
|
||||
planet6.techlevel += planet6.govtype >> 1
|
||||
if planet6.govtype & 1
|
||||
planet6.techlevel++
|
||||
planet6.population = 4 * planet6.techlevel + planet6.economy
|
||||
planet6.population += planet6.govtype + 1
|
||||
planet6.productivity = ((planet6.economy ^ 7) + 3) * (planet6.govtype + 4)
|
||||
planet6.productivity *= planet6.population * 8
|
||||
ubyte seed2_msb = msb(seed[2])
|
||||
planet6.radius = mkword((seed2_msb & 15) + 11, planet6.x)
|
||||
planet6.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
|
||||
if planet6.species_is_alien {
|
||||
planet6.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||
planet6.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||
planet6.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||
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[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() {
|
||||
uword temp = seed[0] + seed[1] + seed[2]
|
||||
seed[0] = seed[1]
|
||||
seed[1] = seed[2]
|
||||
seed[2] = temp
|
||||
}
|
||||
|
||||
sub twist(uword x) -> uword {
|
||||
ubyte xh = msb(x)
|
||||
ubyte xl = lsb(x)
|
||||
rol(xh)
|
||||
rol(xl)
|
||||
return mkword(xh, xl)
|
||||
}
|
||||
|
||||
sub debug_seed() {
|
||||
txt.print("\ngalaxy6 #")
|
||||
txt.print_ub(number)
|
||||
txt.print("\ngalaxy6 seed0=")
|
||||
txt.print_uwhex(galaxy6.seed[0], true)
|
||||
txt.print("\ngalaxy6 seed1=")
|
||||
txt.print_uwhex(galaxy6.seed[1], true)
|
||||
txt.print("\ngalaxy6 seed2=")
|
||||
txt.print_uwhex(galaxy6.seed[2], true)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
planet6 {
|
||||
%option force_output
|
||||
|
||||
str[] species_sizes = ["Large", "Fierce", "Small"]
|
||||
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
|
||||
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
|
||||
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
|
||||
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship6", "Communist", "Confederacy", "Democracy", "Corporate State"]
|
||||
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
|
||||
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
|
||||
|
||||
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||
str[] words8F = ["\xB0", "The planet6 \xB0", "The world \xB0", "This planet6", "This world"]
|
||||
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||
str[] words91 = ["planet6", "world", "place", "little planet6", "dump"]
|
||||
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||
|
||||
uword[] wordlists = [
|
||||
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||
|
||||
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||
|
||||
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||
|
||||
str name = " " ; 8 max
|
||||
ubyte number ; starts at 0 in new galaxy6, then increases by 1 for each generated planet6
|
||||
ubyte x
|
||||
ubyte y
|
||||
ubyte economy
|
||||
ubyte govtype
|
||||
ubyte techlevel
|
||||
ubyte population
|
||||
uword productivity
|
||||
uword radius
|
||||
ubyte species_is_alien ; otherwise "Human Colonials"
|
||||
ubyte species_size
|
||||
ubyte species_color
|
||||
ubyte species_look
|
||||
ubyte species_kind
|
||||
|
||||
sub set_seed(uword s1, uword s2) {
|
||||
goatsoup_seed[0] = lsb(s1)
|
||||
goatsoup_seed[1] = msb(s1)
|
||||
goatsoup_seed[2] = lsb(s2)
|
||||
goatsoup_seed[3] = msb(s2)
|
||||
reset_rnd()
|
||||
}
|
||||
|
||||
sub reset_rnd() {
|
||||
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||
}
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str name = " " ; 8 chars max
|
||||
ubyte nx = 0
|
||||
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||
ubyte x = goatsoup_rnd_number() & $3e
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
x++
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
}
|
||||
name[nx] = 0
|
||||
name[0] |= 32 ; uppercase first letter
|
||||
return name
|
||||
}
|
||||
|
||||
sub goatsoup_rnd_number() -> ubyte {
|
||||
ubyte x = goatsoup_rnd[0] * 2
|
||||
uword a = x as uword + goatsoup_rnd[2]
|
||||
if goatsoup_rnd[0] > 127
|
||||
a ++
|
||||
goatsoup_rnd[0] = lsb(a)
|
||||
goatsoup_rnd[2] = x
|
||||
x = goatsoup_rnd[1]
|
||||
ubyte ac = x + goatsoup_rnd[3] + msb(a)
|
||||
goatsoup_rnd[1] = ac
|
||||
goatsoup_rnd[3] = x
|
||||
return ac
|
||||
}
|
||||
|
||||
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||
uword ax
|
||||
uword ay
|
||||
if px>x
|
||||
ax=px-x
|
||||
else
|
||||
ax=x-px
|
||||
if py>y
|
||||
ay=py-y
|
||||
else
|
||||
ay=y-py
|
||||
ay /= 2
|
||||
ubyte d = sqrt16(ax*ax + ay*ay)
|
||||
if d>63
|
||||
return 255
|
||||
return d*4
|
||||
}
|
||||
|
||||
sub soup() -> str {
|
||||
str planet6_result = " " * 160
|
||||
uword[6] source_stack
|
||||
ubyte stack_ptr = 0
|
||||
str start_source = "\x8F is \x97."
|
||||
uword source_ptr = &start_source
|
||||
uword result_ptr = &planet6_result
|
||||
|
||||
reset_rnd()
|
||||
recursive_soup()
|
||||
return planet6_result
|
||||
|
||||
sub recursive_soup() {
|
||||
repeat {
|
||||
ubyte c = @(source_ptr)
|
||||
source_ptr++
|
||||
if c == $00 {
|
||||
@(result_ptr) = 0
|
||||
return
|
||||
}
|
||||
else if c <= $80 {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
else {
|
||||
if c <= $a4 {
|
||||
ubyte rnr = goatsoup_rnd_number()
|
||||
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
|
||||
source_stack[stack_ptr] = source_ptr
|
||||
stack_ptr++
|
||||
source_ptr = getword(c, wordNr)
|
||||
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||
stack_ptr--
|
||||
source_ptr = source_stack[stack_ptr]
|
||||
} else {
|
||||
if c == $b0 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
concat_string(&name + 1)
|
||||
}
|
||||
else if c == $b1 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
ubyte ni
|
||||
for ni in 1 to len(name) {
|
||||
ubyte cc = name[ni]
|
||||
if cc=='e' or cc=='o' or cc==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = cc
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
@(result_ptr) = 'i'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'a'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'n'
|
||||
result_ptr++
|
||||
}
|
||||
else if c == $b2 {
|
||||
concat_string(random_name())
|
||||
}
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub concat_string(uword str_ptr) {
|
||||
repeat {
|
||||
ubyte c = @(str_ptr)
|
||||
if c==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
str_ptr++
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub display(ubyte compressed) {
|
||||
if compressed {
|
||||
print_name_uppercase()
|
||||
txt.print(" TL:")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.spc()
|
||||
txt.print(econnames[economy])
|
||||
txt.spc()
|
||||
txt.print(govnames[govtype])
|
||||
} else {
|
||||
txt.print("\n\nSystem: ")
|
||||
print_name_uppercase()
|
||||
txt.print("\nPosition: ")
|
||||
txt.print_ub(x)
|
||||
txt.chrout('\'')
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
txt.chrout('#')
|
||||
txt.print_ub(number)
|
||||
txt.print("\nEconomy: ")
|
||||
txt.print(econnames[economy])
|
||||
txt.print("\nGovernment: ")
|
||||
txt.print(govnames[govtype])
|
||||
txt.print("\nTech Level: ")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.print("\nTurnover: ")
|
||||
txt.print_uw(productivity)
|
||||
txt.print("\nRadius: ")
|
||||
txt.print_uw(radius)
|
||||
txt.print("\nPopulation: ")
|
||||
txt.print_ub(population >> 3)
|
||||
txt.print(" Billion\nSpecies: ")
|
||||
if species_is_alien {
|
||||
if species_size < len(species_sizes) {
|
||||
txt.print(species_sizes[species_size])
|
||||
txt.spc()
|
||||
}
|
||||
if species_color < len(species_colors) {
|
||||
txt.print(species_colors[species_color])
|
||||
txt.spc()
|
||||
}
|
||||
if species_look < len(species_looks) {
|
||||
txt.print(species_looks[species_look])
|
||||
txt.spc()
|
||||
}
|
||||
if species_kind < len(species_kinds) {
|
||||
txt.print(species_kinds[species_kind])
|
||||
}
|
||||
} else {
|
||||
txt.print("Human Colonials")
|
||||
}
|
||||
txt.nl()
|
||||
txt.print(soup())
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub print_name_uppercase() {
|
||||
ubyte c
|
||||
for c in name
|
||||
txt.chrout(c | 32)
|
||||
}
|
||||
|
||||
asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY {
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc #$81
|
||||
asl a
|
||||
tay
|
||||
lda wordlists,y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda wordlists+1,y
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
util6 {
|
||||
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
|
||||
repeat {
|
||||
ubyte pc = @(prefixptr)
|
||||
ubyte sc = @(stringptr)
|
||||
if pc == 0
|
||||
return true
|
||||
; to lowercase for case insensitive compare:
|
||||
pc &= 127
|
||||
sc &= 127
|
||||
if pc != sc
|
||||
return false
|
||||
prefixptr++
|
||||
stringptr++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub print_right(ubyte width, uword st) {
|
||||
repeat width - string.length(st) {
|
||||
txt.spc()
|
||||
}
|
||||
txt.print(st)
|
||||
}
|
||||
|
||||
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
lda conv.uword2decimal.decTenThousands
|
||||
ldy #0 ; have we started printing?
|
||||
cmp #'0'
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decThousands
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decHundreds
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decTens
|
||||
jsr c64.CHROUT
|
||||
lda #'.'
|
||||
jsr c64.CHROUT
|
||||
lda conv.uword2decimal.decOnes
|
||||
jsr c64.CHROUT
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,994 +0,0 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import diskio
|
||||
%import string
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
; Prog8 adaptation of the Text-Elite galaxy7 system trading simulation engine.
|
||||
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
perf7 {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet7 in galaxy7 one
|
||||
const ubyte numforZaonce = 129
|
||||
const ubyte numforDiso = 147
|
||||
const ubyte numforRiedquat = 46
|
||||
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
txt.print("\u000c\n --- TextElite v1.1 ---\n")
|
||||
|
||||
galaxy7.travel_to(1, numforLave)
|
||||
market7.init(0) ; Lave's market7 is seeded with 0
|
||||
ship7.init()
|
||||
planet7.display(false)
|
||||
|
||||
repeat {
|
||||
; test_stack.test()
|
||||
|
||||
str input = "????????"
|
||||
txt.print("\nCash: ")
|
||||
util7.print_10s(ship7.cash)
|
||||
txt.print("\nCommand (?=help): ")
|
||||
ubyte num_chars = txt.input_chars(input)
|
||||
txt.nl()
|
||||
if num_chars {
|
||||
when input[0] {
|
||||
'?' -> {
|
||||
txt.print("\nCommands are:\n"+
|
||||
"buy jump info cash >=save\n"+
|
||||
"sell teleport market7 hold <=load\n"+
|
||||
"fuel galhyp local quit\n")
|
||||
}
|
||||
'q' -> break
|
||||
'b' -> trader7.do_buy()
|
||||
's' -> trader7.do_sell()
|
||||
'f' -> trader7.do_fuel()
|
||||
'j' -> trader7.do_jump()
|
||||
't' -> trader7.do_teleport()
|
||||
'g' -> trader7.do_next_galaxy7()
|
||||
'i' -> trader7.do_info()
|
||||
'm' -> trader7.do_show_market7()
|
||||
'l' -> trader7.do_local()
|
||||
'c' -> trader7.do_cash()
|
||||
'h' -> trader7.do_hold()
|
||||
'<' -> trader7.do_load()
|
||||
'>' -> trader7.do_save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trader7 {
|
||||
str Savegame = "↑commander.save"
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
struct SaveData {
|
||||
ubyte galaxy7
|
||||
ubyte planet7
|
||||
ubyte cargo0
|
||||
ubyte cargo1
|
||||
ubyte cargo2
|
||||
ubyte cargo3
|
||||
ubyte cargo4
|
||||
ubyte cargo5
|
||||
ubyte cargo6
|
||||
ubyte cargo7
|
||||
ubyte cargo8
|
||||
ubyte cargo9
|
||||
ubyte cargo10
|
||||
ubyte cargo11
|
||||
ubyte cargo12
|
||||
ubyte cargo13
|
||||
ubyte cargo14
|
||||
ubyte cargo15
|
||||
ubyte cargo16
|
||||
uword cash
|
||||
ubyte max_cargo
|
||||
ubyte fuel
|
||||
}
|
||||
SaveData savedata
|
||||
|
||||
sub do_load() {
|
||||
txt.print("\nLoading universe...")
|
||||
if diskio.load(8, Savegame, &savedata) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
return
|
||||
}
|
||||
|
||||
ship7.cash = savedata.cash
|
||||
ship7.Max_cargo = savedata.max_cargo
|
||||
ship7.fuel = savedata.fuel
|
||||
sys.memcopy(&savedata.cargo0, ship7.cargohold, len(ship7.cargohold))
|
||||
galaxy7.travel_to(savedata.galaxy7, savedata.planet7)
|
||||
|
||||
planet7.display(false)
|
||||
}
|
||||
|
||||
sub do_save() {
|
||||
savedata.galaxy7 = galaxy7.number
|
||||
savedata.planet7 = planet7.number
|
||||
savedata.cash = ship7.cash
|
||||
savedata.max_cargo = ship7.Max_cargo
|
||||
savedata.fuel = ship7.fuel
|
||||
sys.memcopy(ship7.cargohold, &savedata.cargo0, len(ship7.cargohold))
|
||||
|
||||
txt.print("\nSaving universe...")
|
||||
diskio.delete(8, Savegame)
|
||||
if diskio.save(8, Savegame, &savedata, sizeof(savedata)) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub do_jump() {
|
||||
txt.print("\nJump to what system? ")
|
||||
jump_to_system()
|
||||
}
|
||||
|
||||
sub do_teleport() {
|
||||
txt.print("\nCheat! Teleport to what system? ")
|
||||
ubyte fuel = ship7.fuel
|
||||
ship7.fuel = 255
|
||||
jump_to_system()
|
||||
ship7.fuel = fuel
|
||||
}
|
||||
|
||||
sub jump_to_system() {
|
||||
void txt.input_chars(input)
|
||||
ubyte current_planet7 = planet7.number
|
||||
ubyte x = planet7.x
|
||||
ubyte y = planet7.y
|
||||
if galaxy7.search_closest_planet7(input) {
|
||||
ubyte distance = planet7.distance(x, y)
|
||||
if distance <= ship7.fuel {
|
||||
galaxy7.init_market7_for_planet7()
|
||||
ship7.fuel -= distance
|
||||
txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||
planet7.display(true)
|
||||
return
|
||||
}
|
||||
txt.print("Insufficient fuel\n")
|
||||
} else {
|
||||
txt.print(" Not found!\n")
|
||||
}
|
||||
galaxy7.travel_to(galaxy7.number, current_planet7)
|
||||
}
|
||||
|
||||
sub do_buy() {
|
||||
txt.print("\nBuy what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market7.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if market7.current_quantity[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market7.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util7.print_10s(price)
|
||||
if price > ship7.cash {
|
||||
txt.print(" Not enough cash!\n")
|
||||
} else {
|
||||
ship7.cash -= price
|
||||
ship7.cargohold[ci] += amount
|
||||
market7.current_quantity[ci] -= amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_sell() {
|
||||
txt.print("\nSell what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market7.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if ship7.cargohold[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market7.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util7.print_10s(price)
|
||||
ship7.cash += price
|
||||
ship7.cargohold[ci] -= amount
|
||||
market7.current_quantity[ci] += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_fuel() {
|
||||
txt.print("\nBuy fuel. Amount? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||
ubyte max_fuel = ship7.Max_fuel - ship7.fuel
|
||||
if buy_fuel > max_fuel
|
||||
buy_fuel = max_fuel
|
||||
uword price = buy_fuel as uword * ship7.Fuel_cost
|
||||
if price > ship7.cash {
|
||||
txt.print("Not enough cash!\n")
|
||||
} else {
|
||||
ship7.cash -= price
|
||||
ship7.fuel += buy_fuel
|
||||
}
|
||||
}
|
||||
|
||||
sub do_cash() {
|
||||
txt.print("\nCheat! Set cash amount: ")
|
||||
void txt.input_chars(input)
|
||||
ship7.cash = conv.str2uword(input)
|
||||
}
|
||||
|
||||
sub do_hold() {
|
||||
txt.print("\nCheat! Set cargohold size: ")
|
||||
void txt.input_chars(input)
|
||||
ship7.Max_cargo = conv.str2ubyte(input)
|
||||
}
|
||||
|
||||
sub do_next_galaxy7() {
|
||||
galaxy7.travel_to(galaxy7.number+1, planet7.number)
|
||||
planet7.display(false)
|
||||
}
|
||||
|
||||
sub do_info() {
|
||||
txt.print("\nSystem name (empty=current): ")
|
||||
num_chars = txt.input_chars(input)
|
||||
if num_chars {
|
||||
ubyte current_planet7 = planet7.number
|
||||
if galaxy7.search_closest_planet7(input) {
|
||||
planet7.display(false)
|
||||
} else {
|
||||
txt.print(" Not found!")
|
||||
}
|
||||
galaxy7.travel_to(galaxy7.number, current_planet7)
|
||||
} else {
|
||||
planet7.display(false)
|
||||
}
|
||||
}
|
||||
|
||||
sub do_local() {
|
||||
galaxy7.local_area()
|
||||
}
|
||||
|
||||
sub do_show_market7() {
|
||||
market7.display()
|
||||
txt.print("\nFuel: ")
|
||||
util7.print_10s(ship7.fuel)
|
||||
txt.print(" Cargohold space: ")
|
||||
txt.print_ub(ship7.cargo_free())
|
||||
txt.print("t\n")
|
||||
}
|
||||
}
|
||||
|
||||
ship7 {
|
||||
const ubyte Max_fuel = 70
|
||||
const ubyte Fuel_cost = 2
|
||||
ubyte Max_cargo = 20
|
||||
|
||||
ubyte fuel = Max_fuel
|
||||
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
|
||||
ubyte[17] cargohold = 0
|
||||
|
||||
sub init() {
|
||||
sys.memset(cargohold, len(cargohold), 0)
|
||||
}
|
||||
|
||||
sub cargo_free() -> ubyte {
|
||||
ubyte ci
|
||||
ubyte total = 0
|
||||
for ci in 0 to len(cargohold)-1 {
|
||||
if market7.units[ci]==0 ; tonnes only
|
||||
total += cargohold[ci]
|
||||
}
|
||||
return Max_cargo - total
|
||||
}
|
||||
}
|
||||
|
||||
market7 {
|
||||
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
|
||||
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||
|
||||
ubyte[17] current_quantity = 0
|
||||
uword[17] current_price = 0
|
||||
|
||||
sub init(ubyte fluct) {
|
||||
; Prices and availabilities are influenced by the planet7's economy type
|
||||
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||
; commander position to keep the market7 prices constant over gamesaves.
|
||||
; Availabilities must be saved with the game since the player alters them
|
||||
; by buying (and selling(?))
|
||||
;
|
||||
; Almost all operations are one byte only and overflow "errors" are
|
||||
; extremely frequent and exploited.
|
||||
;
|
||||
; Trade Item prices are held internally in a single byte=true value/4.
|
||||
; The decimal point in prices is introduced only when printing them.
|
||||
; Internally, all prices are integers.
|
||||
; The player's cash is held in four bytes.
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
word product
|
||||
byte changing
|
||||
product = planet7.economy as word * gradients[ci]
|
||||
changing = fluct & maskbytes[ci] as byte
|
||||
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||
if q & $80
|
||||
q = 0 ; clip to positive 8-bit
|
||||
current_quantity[ci] = q & $3f
|
||||
q = (baseprices[ci] + changing + product) as ubyte
|
||||
current_price[ci] = q * $0004
|
||||
}
|
||||
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||
}
|
||||
|
||||
sub display() {
|
||||
ubyte ci
|
||||
txt.nl()
|
||||
planet7.print_name_uppercase()
|
||||
txt.print(" trade market7:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||
for ci in 0 to len(names)-1 {
|
||||
util7.print_right(13, names[ci])
|
||||
txt.print(" ")
|
||||
util7.print_10s(current_price[ci])
|
||||
txt.print(" ")
|
||||
txt.print_ub(current_quantity[ci])
|
||||
when units[ci] {
|
||||
0 -> txt.chrout('t')
|
||||
1 -> txt.print("kg")
|
||||
2 -> txt.chrout('g')
|
||||
}
|
||||
txt.print(" ")
|
||||
txt.print_ub(ship7.cargohold[ci])
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub match(uword nameptr) -> ubyte {
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
if util7.prefix_matches(nameptr, names[ci])
|
||||
return ci
|
||||
}
|
||||
return 255
|
||||
}
|
||||
}
|
||||
|
||||
galaxy7 {
|
||||
const uword GALSIZE = 256
|
||||
const uword base0 = $5A4A ; seeds for the first galaxy7
|
||||
const uword base1 = $0248
|
||||
const uword base2 = $B753
|
||||
|
||||
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||
|
||||
ubyte number
|
||||
|
||||
uword[3] seed
|
||||
|
||||
sub init(ubyte galaxy7num) {
|
||||
number = 1
|
||||
planet7.number = 255
|
||||
seed[0] = base0
|
||||
seed[1] = base1
|
||||
seed[2] = base2
|
||||
repeat galaxy7num-1 {
|
||||
nextgalaxy7()
|
||||
}
|
||||
}
|
||||
|
||||
sub nextgalaxy7() {
|
||||
seed[0] = twist(seed[0])
|
||||
seed[1] = twist(seed[1])
|
||||
seed[2] = twist(seed[2])
|
||||
number++
|
||||
if number==9
|
||||
number = 1
|
||||
}
|
||||
|
||||
sub travel_to(ubyte galaxy7num, ubyte system) {
|
||||
init(galaxy7num)
|
||||
generate_next_planet7() ; always at least planet7 0 (separate to avoid repeat ubyte overflow)
|
||||
repeat system {
|
||||
generate_next_planet7()
|
||||
}
|
||||
planet7.name = make_current_planet7_name()
|
||||
init_market7_for_planet7()
|
||||
}
|
||||
|
||||
sub init_market7_for_planet7() {
|
||||
market7.init(lsb(seed[0])+msb(seed[2]))
|
||||
}
|
||||
|
||||
sub search_closest_planet7(uword nameptr) -> ubyte {
|
||||
ubyte x = planet7.x
|
||||
ubyte y = planet7.y
|
||||
ubyte current_planet7_num = planet7.number
|
||||
|
||||
init(number)
|
||||
ubyte found = false
|
||||
ubyte current_closest_pi
|
||||
ubyte current_distance = 127
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet7()
|
||||
planet7.name = make_current_planet7_name()
|
||||
if util7.prefix_matches(nameptr, planet7.name) {
|
||||
ubyte distance = planet7.distance(x, y)
|
||||
if distance < current_distance {
|
||||
current_distance = distance
|
||||
current_closest_pi = pi
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found
|
||||
travel_to(number, current_closest_pi)
|
||||
else
|
||||
travel_to(number, current_planet7_num)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
sub local_area() {
|
||||
ubyte current_planet7 = planet7.number
|
||||
ubyte px = planet7.x
|
||||
ubyte py = planet7.y
|
||||
ubyte pn = 0
|
||||
|
||||
init(number)
|
||||
txt.print("\nGalaxy #")
|
||||
txt.print_ub(number)
|
||||
txt.print(" - systems in vicinity:\n")
|
||||
do {
|
||||
generate_next_planet7()
|
||||
ubyte distance = planet7.distance(px, py)
|
||||
if distance <= ship7.Max_fuel {
|
||||
if distance <= ship7.fuel
|
||||
txt.chrout('*')
|
||||
else
|
||||
txt.chrout('-')
|
||||
txt.spc()
|
||||
planet7.name = make_current_planet7_name()
|
||||
planet7.display(true)
|
||||
txt.print(" (")
|
||||
util7.print_10s(distance)
|
||||
txt.print(" LY)\n")
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
travel_to(number, current_planet7)
|
||||
}
|
||||
|
||||
ubyte pn_pair1
|
||||
ubyte pn_pair2
|
||||
ubyte pn_pair3
|
||||
ubyte pn_pair4
|
||||
ubyte longname
|
||||
|
||||
sub generate_next_planet7() {
|
||||
determine_planet7_properties()
|
||||
longname = lsb(seed[0]) & 64
|
||||
|
||||
; Always four iterations of random number
|
||||
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
}
|
||||
|
||||
sub make_current_planet7_name() -> str {
|
||||
ubyte ni = 0
|
||||
str name = " " ; max 8
|
||||
|
||||
if pn_pairs[pn_pair1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair1+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3+1]
|
||||
ni++
|
||||
}
|
||||
|
||||
if longname {
|
||||
if pn_pairs[pn_pair4] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair4+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4+1]
|
||||
ni++
|
||||
}
|
||||
}
|
||||
|
||||
name[ni] = 0
|
||||
return name
|
||||
}
|
||||
|
||||
sub determine_planet7_properties() {
|
||||
; create the planet7's characteristics
|
||||
planet7.number++
|
||||
planet7.x = msb(seed[1])
|
||||
planet7.y = msb(seed[0])
|
||||
planet7.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||
planet7.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||
if planet7.govtype <= 1
|
||||
planet7.economy = (planet7.economy | 2)
|
||||
planet7.techlevel = (msb(seed[1]) & 3) + (planet7.economy ^ 7)
|
||||
planet7.techlevel += planet7.govtype >> 1
|
||||
if planet7.govtype & 1
|
||||
planet7.techlevel++
|
||||
planet7.population = 4 * planet7.techlevel + planet7.economy
|
||||
planet7.population += planet7.govtype + 1
|
||||
planet7.productivity = ((planet7.economy ^ 7) + 3) * (planet7.govtype + 4)
|
||||
planet7.productivity *= planet7.population * 8
|
||||
ubyte seed2_msb = msb(seed[2])
|
||||
planet7.radius = mkword((seed2_msb & 15) + 11, planet7.x)
|
||||
planet7.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
|
||||
if planet7.species_is_alien {
|
||||
planet7.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||
planet7.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||
planet7.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||
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[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() {
|
||||
uword temp = seed[0] + seed[1] + seed[2]
|
||||
seed[0] = seed[1]
|
||||
seed[1] = seed[2]
|
||||
seed[2] = temp
|
||||
}
|
||||
|
||||
sub twist(uword x) -> uword {
|
||||
ubyte xh = msb(x)
|
||||
ubyte xl = lsb(x)
|
||||
rol(xh)
|
||||
rol(xl)
|
||||
return mkword(xh, xl)
|
||||
}
|
||||
|
||||
sub debug_seed() {
|
||||
txt.print("\ngalaxy7 #")
|
||||
txt.print_ub(number)
|
||||
txt.print("\ngalaxy7 seed0=")
|
||||
txt.print_uwhex(galaxy7.seed[0], true)
|
||||
txt.print("\ngalaxy7 seed1=")
|
||||
txt.print_uwhex(galaxy7.seed[1], true)
|
||||
txt.print("\ngalaxy7 seed2=")
|
||||
txt.print_uwhex(galaxy7.seed[2], true)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
planet7 {
|
||||
%option force_output
|
||||
|
||||
str[] species_sizes = ["Large", "Fierce", "Small"]
|
||||
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
|
||||
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
|
||||
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
|
||||
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship7", "Communist", "Confederacy", "Democracy", "Corporate State"]
|
||||
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
|
||||
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
|
||||
|
||||
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||
str[] words8F = ["\xB0", "The planet7 \xB0", "The world \xB0", "This planet7", "This world"]
|
||||
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||
str[] words91 = ["planet7", "world", "place", "little planet7", "dump"]
|
||||
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||
|
||||
uword[] wordlists = [
|
||||
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||
|
||||
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||
|
||||
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||
|
||||
str name = " " ; 8 max
|
||||
ubyte number ; starts at 0 in new galaxy7, then increases by 1 for each generated planet7
|
||||
ubyte x
|
||||
ubyte y
|
||||
ubyte economy
|
||||
ubyte govtype
|
||||
ubyte techlevel
|
||||
ubyte population
|
||||
uword productivity
|
||||
uword radius
|
||||
ubyte species_is_alien ; otherwise "Human Colonials"
|
||||
ubyte species_size
|
||||
ubyte species_color
|
||||
ubyte species_look
|
||||
ubyte species_kind
|
||||
|
||||
sub set_seed(uword s1, uword s2) {
|
||||
goatsoup_seed[0] = lsb(s1)
|
||||
goatsoup_seed[1] = msb(s1)
|
||||
goatsoup_seed[2] = lsb(s2)
|
||||
goatsoup_seed[3] = msb(s2)
|
||||
reset_rnd()
|
||||
}
|
||||
|
||||
sub reset_rnd() {
|
||||
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||
}
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str name = " " ; 8 chars max
|
||||
ubyte nx = 0
|
||||
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||
ubyte x = goatsoup_rnd_number() & $3e
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
x++
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
}
|
||||
name[nx] = 0
|
||||
name[0] |= 32 ; uppercase first letter
|
||||
return name
|
||||
}
|
||||
|
||||
sub goatsoup_rnd_number() -> ubyte {
|
||||
ubyte x = goatsoup_rnd[0] * 2
|
||||
uword a = x as uword + goatsoup_rnd[2]
|
||||
if goatsoup_rnd[0] > 127
|
||||
a ++
|
||||
goatsoup_rnd[0] = lsb(a)
|
||||
goatsoup_rnd[2] = x
|
||||
x = goatsoup_rnd[1]
|
||||
ubyte ac = x + goatsoup_rnd[3] + msb(a)
|
||||
goatsoup_rnd[1] = ac
|
||||
goatsoup_rnd[3] = x
|
||||
return ac
|
||||
}
|
||||
|
||||
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||
uword ax
|
||||
uword ay
|
||||
if px>x
|
||||
ax=px-x
|
||||
else
|
||||
ax=x-px
|
||||
if py>y
|
||||
ay=py-y
|
||||
else
|
||||
ay=y-py
|
||||
ay /= 2
|
||||
ubyte d = sqrt16(ax*ax + ay*ay)
|
||||
if d>63
|
||||
return 255
|
||||
return d*4
|
||||
}
|
||||
|
||||
sub soup() -> str {
|
||||
str planet7_result = " " * 160
|
||||
uword[6] source_stack
|
||||
ubyte stack_ptr = 0
|
||||
str start_source = "\x8F is \x97."
|
||||
uword source_ptr = &start_source
|
||||
uword result_ptr = &planet7_result
|
||||
|
||||
reset_rnd()
|
||||
recursive_soup()
|
||||
return planet7_result
|
||||
|
||||
sub recursive_soup() {
|
||||
repeat {
|
||||
ubyte c = @(source_ptr)
|
||||
source_ptr++
|
||||
if c == $00 {
|
||||
@(result_ptr) = 0
|
||||
return
|
||||
}
|
||||
else if c <= $80 {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
else {
|
||||
if c <= $a4 {
|
||||
ubyte rnr = goatsoup_rnd_number()
|
||||
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
|
||||
source_stack[stack_ptr] = source_ptr
|
||||
stack_ptr++
|
||||
source_ptr = getword(c, wordNr)
|
||||
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||
stack_ptr--
|
||||
source_ptr = source_stack[stack_ptr]
|
||||
} else {
|
||||
if c == $b0 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
concat_string(&name + 1)
|
||||
}
|
||||
else if c == $b1 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
ubyte ni
|
||||
for ni in 1 to len(name) {
|
||||
ubyte cc = name[ni]
|
||||
if cc=='e' or cc=='o' or cc==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = cc
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
@(result_ptr) = 'i'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'a'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'n'
|
||||
result_ptr++
|
||||
}
|
||||
else if c == $b2 {
|
||||
concat_string(random_name())
|
||||
}
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub concat_string(uword str_ptr) {
|
||||
repeat {
|
||||
ubyte c = @(str_ptr)
|
||||
if c==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
str_ptr++
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub display(ubyte compressed) {
|
||||
if compressed {
|
||||
print_name_uppercase()
|
||||
txt.print(" TL:")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.spc()
|
||||
txt.print(econnames[economy])
|
||||
txt.spc()
|
||||
txt.print(govnames[govtype])
|
||||
} else {
|
||||
txt.print("\n\nSystem: ")
|
||||
print_name_uppercase()
|
||||
txt.print("\nPosition: ")
|
||||
txt.print_ub(x)
|
||||
txt.chrout('\'')
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
txt.chrout('#')
|
||||
txt.print_ub(number)
|
||||
txt.print("\nEconomy: ")
|
||||
txt.print(econnames[economy])
|
||||
txt.print("\nGovernment: ")
|
||||
txt.print(govnames[govtype])
|
||||
txt.print("\nTech Level: ")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.print("\nTurnover: ")
|
||||
txt.print_uw(productivity)
|
||||
txt.print("\nRadius: ")
|
||||
txt.print_uw(radius)
|
||||
txt.print("\nPopulation: ")
|
||||
txt.print_ub(population >> 3)
|
||||
txt.print(" Billion\nSpecies: ")
|
||||
if species_is_alien {
|
||||
if species_size < len(species_sizes) {
|
||||
txt.print(species_sizes[species_size])
|
||||
txt.spc()
|
||||
}
|
||||
if species_color < len(species_colors) {
|
||||
txt.print(species_colors[species_color])
|
||||
txt.spc()
|
||||
}
|
||||
if species_look < len(species_looks) {
|
||||
txt.print(species_looks[species_look])
|
||||
txt.spc()
|
||||
}
|
||||
if species_kind < len(species_kinds) {
|
||||
txt.print(species_kinds[species_kind])
|
||||
}
|
||||
} else {
|
||||
txt.print("Human Colonials")
|
||||
}
|
||||
txt.nl()
|
||||
txt.print(soup())
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub print_name_uppercase() {
|
||||
ubyte c
|
||||
for c in name
|
||||
txt.chrout(c | 32)
|
||||
}
|
||||
|
||||
asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY {
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc #$81
|
||||
asl a
|
||||
tay
|
||||
lda wordlists,y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda wordlists+1,y
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
util7 {
|
||||
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
|
||||
repeat {
|
||||
ubyte pc = @(prefixptr)
|
||||
ubyte sc = @(stringptr)
|
||||
if pc == 0
|
||||
return true
|
||||
; to lowercase for case insensitive compare:
|
||||
pc &= 127
|
||||
sc &= 127
|
||||
if pc != sc
|
||||
return false
|
||||
prefixptr++
|
||||
stringptr++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub print_right(ubyte width, uword st) {
|
||||
repeat width - string.length(st) {
|
||||
txt.spc()
|
||||
}
|
||||
txt.print(st)
|
||||
}
|
||||
|
||||
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
lda conv.uword2decimal.decTenThousands
|
||||
ldy #0 ; have we started printing?
|
||||
cmp #'0'
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decThousands
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decHundreds
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decTens
|
||||
jsr c64.CHROUT
|
||||
lda #'.'
|
||||
jsr c64.CHROUT
|
||||
lda conv.uword2decimal.decOnes
|
||||
jsr c64.CHROUT
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,994 +0,0 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import diskio
|
||||
%import string
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
; Prog8 adaptation of the Text-Elite galaxy8 system trading simulation engine.
|
||||
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
perf8 {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet8 in galaxy8 one
|
||||
const ubyte numforZaonce = 129
|
||||
const ubyte numforDiso = 147
|
||||
const ubyte numforRiedquat = 46
|
||||
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
txt.print("\u000c\n --- TextElite v1.1 ---\n")
|
||||
|
||||
galaxy8.travel_to(1, numforLave)
|
||||
market8.init(0) ; Lave's market8 is seeded with 0
|
||||
ship8.init()
|
||||
planet8.display(false)
|
||||
|
||||
repeat {
|
||||
; test_stack.test()
|
||||
|
||||
str input = "????????"
|
||||
txt.print("\nCash: ")
|
||||
util8.print_10s(ship8.cash)
|
||||
txt.print("\nCommand (?=help): ")
|
||||
ubyte num_chars = txt.input_chars(input)
|
||||
txt.nl()
|
||||
if num_chars {
|
||||
when input[0] {
|
||||
'?' -> {
|
||||
txt.print("\nCommands are:\n"+
|
||||
"buy jump info cash >=save\n"+
|
||||
"sell teleport market8 hold <=load\n"+
|
||||
"fuel galhyp local quit\n")
|
||||
}
|
||||
'q' -> break
|
||||
'b' -> trader8.do_buy()
|
||||
's' -> trader8.do_sell()
|
||||
'f' -> trader8.do_fuel()
|
||||
'j' -> trader8.do_jump()
|
||||
't' -> trader8.do_teleport()
|
||||
'g' -> trader8.do_next_galaxy8()
|
||||
'i' -> trader8.do_info()
|
||||
'm' -> trader8.do_show_market8()
|
||||
'l' -> trader8.do_local()
|
||||
'c' -> trader8.do_cash()
|
||||
'h' -> trader8.do_hold()
|
||||
'<' -> trader8.do_load()
|
||||
'>' -> trader8.do_save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trader8 {
|
||||
str Savegame = "↑commander.save"
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
struct SaveData {
|
||||
ubyte galaxy8
|
||||
ubyte planet8
|
||||
ubyte cargo0
|
||||
ubyte cargo1
|
||||
ubyte cargo2
|
||||
ubyte cargo3
|
||||
ubyte cargo4
|
||||
ubyte cargo5
|
||||
ubyte cargo6
|
||||
ubyte cargo7
|
||||
ubyte cargo8
|
||||
ubyte cargo9
|
||||
ubyte cargo10
|
||||
ubyte cargo11
|
||||
ubyte cargo12
|
||||
ubyte cargo13
|
||||
ubyte cargo14
|
||||
ubyte cargo15
|
||||
ubyte cargo16
|
||||
uword cash
|
||||
ubyte max_cargo
|
||||
ubyte fuel
|
||||
}
|
||||
SaveData savedata
|
||||
|
||||
sub do_load() {
|
||||
txt.print("\nLoading universe...")
|
||||
if diskio.load(8, Savegame, &savedata) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
return
|
||||
}
|
||||
|
||||
ship8.cash = savedata.cash
|
||||
ship8.Max_cargo = savedata.max_cargo
|
||||
ship8.fuel = savedata.fuel
|
||||
sys.memcopy(&savedata.cargo0, ship8.cargohold, len(ship8.cargohold))
|
||||
galaxy8.travel_to(savedata.galaxy8, savedata.planet8)
|
||||
|
||||
planet8.display(false)
|
||||
}
|
||||
|
||||
sub do_save() {
|
||||
savedata.galaxy8 = galaxy8.number
|
||||
savedata.planet8 = planet8.number
|
||||
savedata.cash = ship8.cash
|
||||
savedata.max_cargo = ship8.Max_cargo
|
||||
savedata.fuel = ship8.fuel
|
||||
sys.memcopy(ship8.cargohold, &savedata.cargo0, len(ship8.cargohold))
|
||||
|
||||
txt.print("\nSaving universe...")
|
||||
diskio.delete(8, Savegame)
|
||||
if diskio.save(8, Savegame, &savedata, sizeof(savedata)) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub do_jump() {
|
||||
txt.print("\nJump to what system? ")
|
||||
jump_to_system()
|
||||
}
|
||||
|
||||
sub do_teleport() {
|
||||
txt.print("\nCheat! Teleport to what system? ")
|
||||
ubyte fuel = ship8.fuel
|
||||
ship8.fuel = 255
|
||||
jump_to_system()
|
||||
ship8.fuel = fuel
|
||||
}
|
||||
|
||||
sub jump_to_system() {
|
||||
void txt.input_chars(input)
|
||||
ubyte current_planet8 = planet8.number
|
||||
ubyte x = planet8.x
|
||||
ubyte y = planet8.y
|
||||
if galaxy8.search_closest_planet8(input) {
|
||||
ubyte distance = planet8.distance(x, y)
|
||||
if distance <= ship8.fuel {
|
||||
galaxy8.init_market8_for_planet8()
|
||||
ship8.fuel -= distance
|
||||
txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||
planet8.display(true)
|
||||
return
|
||||
}
|
||||
txt.print("Insufficient fuel\n")
|
||||
} else {
|
||||
txt.print(" Not found!\n")
|
||||
}
|
||||
galaxy8.travel_to(galaxy8.number, current_planet8)
|
||||
}
|
||||
|
||||
sub do_buy() {
|
||||
txt.print("\nBuy what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market8.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if market8.current_quantity[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market8.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util8.print_10s(price)
|
||||
if price > ship8.cash {
|
||||
txt.print(" Not enough cash!\n")
|
||||
} else {
|
||||
ship8.cash -= price
|
||||
ship8.cargohold[ci] += amount
|
||||
market8.current_quantity[ci] -= amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_sell() {
|
||||
txt.print("\nSell what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market8.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if ship8.cargohold[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market8.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util8.print_10s(price)
|
||||
ship8.cash += price
|
||||
ship8.cargohold[ci] -= amount
|
||||
market8.current_quantity[ci] += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_fuel() {
|
||||
txt.print("\nBuy fuel. Amount? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||
ubyte max_fuel = ship8.Max_fuel - ship8.fuel
|
||||
if buy_fuel > max_fuel
|
||||
buy_fuel = max_fuel
|
||||
uword price = buy_fuel as uword * ship8.Fuel_cost
|
||||
if price > ship8.cash {
|
||||
txt.print("Not enough cash!\n")
|
||||
} else {
|
||||
ship8.cash -= price
|
||||
ship8.fuel += buy_fuel
|
||||
}
|
||||
}
|
||||
|
||||
sub do_cash() {
|
||||
txt.print("\nCheat! Set cash amount: ")
|
||||
void txt.input_chars(input)
|
||||
ship8.cash = conv.str2uword(input)
|
||||
}
|
||||
|
||||
sub do_hold() {
|
||||
txt.print("\nCheat! Set cargohold size: ")
|
||||
void txt.input_chars(input)
|
||||
ship8.Max_cargo = conv.str2ubyte(input)
|
||||
}
|
||||
|
||||
sub do_next_galaxy8() {
|
||||
galaxy8.travel_to(galaxy8.number+1, planet8.number)
|
||||
planet8.display(false)
|
||||
}
|
||||
|
||||
sub do_info() {
|
||||
txt.print("\nSystem name (empty=current): ")
|
||||
num_chars = txt.input_chars(input)
|
||||
if num_chars {
|
||||
ubyte current_planet8 = planet8.number
|
||||
if galaxy8.search_closest_planet8(input) {
|
||||
planet8.display(false)
|
||||
} else {
|
||||
txt.print(" Not found!")
|
||||
}
|
||||
galaxy8.travel_to(galaxy8.number, current_planet8)
|
||||
} else {
|
||||
planet8.display(false)
|
||||
}
|
||||
}
|
||||
|
||||
sub do_local() {
|
||||
galaxy8.local_area()
|
||||
}
|
||||
|
||||
sub do_show_market8() {
|
||||
market8.display()
|
||||
txt.print("\nFuel: ")
|
||||
util8.print_10s(ship8.fuel)
|
||||
txt.print(" Cargohold space: ")
|
||||
txt.print_ub(ship8.cargo_free())
|
||||
txt.print("t\n")
|
||||
}
|
||||
}
|
||||
|
||||
ship8 {
|
||||
const ubyte Max_fuel = 70
|
||||
const ubyte Fuel_cost = 2
|
||||
ubyte Max_cargo = 20
|
||||
|
||||
ubyte fuel = Max_fuel
|
||||
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
|
||||
ubyte[17] cargohold = 0
|
||||
|
||||
sub init() {
|
||||
sys.memset(cargohold, len(cargohold), 0)
|
||||
}
|
||||
|
||||
sub cargo_free() -> ubyte {
|
||||
ubyte ci
|
||||
ubyte total = 0
|
||||
for ci in 0 to len(cargohold)-1 {
|
||||
if market8.units[ci]==0 ; tonnes only
|
||||
total += cargohold[ci]
|
||||
}
|
||||
return Max_cargo - total
|
||||
}
|
||||
}
|
||||
|
||||
market8 {
|
||||
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
|
||||
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||
|
||||
ubyte[17] current_quantity = 0
|
||||
uword[17] current_price = 0
|
||||
|
||||
sub init(ubyte fluct) {
|
||||
; Prices and availabilities are influenced by the planet8's economy type
|
||||
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||
; commander position to keep the market8 prices constant over gamesaves.
|
||||
; Availabilities must be saved with the game since the player alters them
|
||||
; by buying (and selling(?))
|
||||
;
|
||||
; Almost all operations are one byte only and overflow "errors" are
|
||||
; extremely frequent and exploited.
|
||||
;
|
||||
; Trade Item prices are held internally in a single byte=true value/4.
|
||||
; The decimal point in prices is introduced only when printing them.
|
||||
; Internally, all prices are integers.
|
||||
; The player's cash is held in four bytes.
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
word product
|
||||
byte changing
|
||||
product = planet8.economy as word * gradients[ci]
|
||||
changing = fluct & maskbytes[ci] as byte
|
||||
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||
if q & $80
|
||||
q = 0 ; clip to positive 8-bit
|
||||
current_quantity[ci] = q & $3f
|
||||
q = (baseprices[ci] + changing + product) as ubyte
|
||||
current_price[ci] = q * $0004
|
||||
}
|
||||
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||
}
|
||||
|
||||
sub display() {
|
||||
ubyte ci
|
||||
txt.nl()
|
||||
planet8.print_name_uppercase()
|
||||
txt.print(" trade market8:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||
for ci in 0 to len(names)-1 {
|
||||
util8.print_right(13, names[ci])
|
||||
txt.print(" ")
|
||||
util8.print_10s(current_price[ci])
|
||||
txt.print(" ")
|
||||
txt.print_ub(current_quantity[ci])
|
||||
when units[ci] {
|
||||
0 -> txt.chrout('t')
|
||||
1 -> txt.print("kg")
|
||||
2 -> txt.chrout('g')
|
||||
}
|
||||
txt.print(" ")
|
||||
txt.print_ub(ship8.cargohold[ci])
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub match(uword nameptr) -> ubyte {
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
if util8.prefix_matches(nameptr, names[ci])
|
||||
return ci
|
||||
}
|
||||
return 255
|
||||
}
|
||||
}
|
||||
|
||||
galaxy8 {
|
||||
const uword GALSIZE = 256
|
||||
const uword base0 = $5A4A ; seeds for the first galaxy8
|
||||
const uword base1 = $0248
|
||||
const uword base2 = $B753
|
||||
|
||||
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||
|
||||
ubyte number
|
||||
|
||||
uword[3] seed
|
||||
|
||||
sub init(ubyte galaxy8num) {
|
||||
number = 1
|
||||
planet8.number = 255
|
||||
seed[0] = base0
|
||||
seed[1] = base1
|
||||
seed[2] = base2
|
||||
repeat galaxy8num-1 {
|
||||
nextgalaxy8()
|
||||
}
|
||||
}
|
||||
|
||||
sub nextgalaxy8() {
|
||||
seed[0] = twist(seed[0])
|
||||
seed[1] = twist(seed[1])
|
||||
seed[2] = twist(seed[2])
|
||||
number++
|
||||
if number==9
|
||||
number = 1
|
||||
}
|
||||
|
||||
sub travel_to(ubyte galaxy8num, ubyte system) {
|
||||
init(galaxy8num)
|
||||
generate_next_planet8() ; always at least planet8 0 (separate to avoid repeat ubyte overflow)
|
||||
repeat system {
|
||||
generate_next_planet8()
|
||||
}
|
||||
planet8.name = make_current_planet8_name()
|
||||
init_market8_for_planet8()
|
||||
}
|
||||
|
||||
sub init_market8_for_planet8() {
|
||||
market8.init(lsb(seed[0])+msb(seed[2]))
|
||||
}
|
||||
|
||||
sub search_closest_planet8(uword nameptr) -> ubyte {
|
||||
ubyte x = planet8.x
|
||||
ubyte y = planet8.y
|
||||
ubyte current_planet8_num = planet8.number
|
||||
|
||||
init(number)
|
||||
ubyte found = false
|
||||
ubyte current_closest_pi
|
||||
ubyte current_distance = 127
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet8()
|
||||
planet8.name = make_current_planet8_name()
|
||||
if util8.prefix_matches(nameptr, planet8.name) {
|
||||
ubyte distance = planet8.distance(x, y)
|
||||
if distance < current_distance {
|
||||
current_distance = distance
|
||||
current_closest_pi = pi
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found
|
||||
travel_to(number, current_closest_pi)
|
||||
else
|
||||
travel_to(number, current_planet8_num)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
sub local_area() {
|
||||
ubyte current_planet8 = planet8.number
|
||||
ubyte px = planet8.x
|
||||
ubyte py = planet8.y
|
||||
ubyte pn = 0
|
||||
|
||||
init(number)
|
||||
txt.print("\nGalaxy #")
|
||||
txt.print_ub(number)
|
||||
txt.print(" - systems in vicinity:\n")
|
||||
do {
|
||||
generate_next_planet8()
|
||||
ubyte distance = planet8.distance(px, py)
|
||||
if distance <= ship8.Max_fuel {
|
||||
if distance <= ship8.fuel
|
||||
txt.chrout('*')
|
||||
else
|
||||
txt.chrout('-')
|
||||
txt.spc()
|
||||
planet8.name = make_current_planet8_name()
|
||||
planet8.display(true)
|
||||
txt.print(" (")
|
||||
util8.print_10s(distance)
|
||||
txt.print(" LY)\n")
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
travel_to(number, current_planet8)
|
||||
}
|
||||
|
||||
ubyte pn_pair1
|
||||
ubyte pn_pair2
|
||||
ubyte pn_pair3
|
||||
ubyte pn_pair4
|
||||
ubyte longname
|
||||
|
||||
sub generate_next_planet8() {
|
||||
determine_planet8_properties()
|
||||
longname = lsb(seed[0]) & 64
|
||||
|
||||
; Always four iterations of random number
|
||||
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
}
|
||||
|
||||
sub make_current_planet8_name() -> str {
|
||||
ubyte ni = 0
|
||||
str name = " " ; max 8
|
||||
|
||||
if pn_pairs[pn_pair1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair1+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3+1]
|
||||
ni++
|
||||
}
|
||||
|
||||
if longname {
|
||||
if pn_pairs[pn_pair4] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair4+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4+1]
|
||||
ni++
|
||||
}
|
||||
}
|
||||
|
||||
name[ni] = 0
|
||||
return name
|
||||
}
|
||||
|
||||
sub determine_planet8_properties() {
|
||||
; create the planet8's characteristics
|
||||
planet8.number++
|
||||
planet8.x = msb(seed[1])
|
||||
planet8.y = msb(seed[0])
|
||||
planet8.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||
planet8.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||
if planet8.govtype <= 1
|
||||
planet8.economy = (planet8.economy | 2)
|
||||
planet8.techlevel = (msb(seed[1]) & 3) + (planet8.economy ^ 7)
|
||||
planet8.techlevel += planet8.govtype >> 1
|
||||
if planet8.govtype & 1
|
||||
planet8.techlevel++
|
||||
planet8.population = 4 * planet8.techlevel + planet8.economy
|
||||
planet8.population += planet8.govtype + 1
|
||||
planet8.productivity = ((planet8.economy ^ 7) + 3) * (planet8.govtype + 4)
|
||||
planet8.productivity *= planet8.population * 8
|
||||
ubyte seed2_msb = msb(seed[2])
|
||||
planet8.radius = mkword((seed2_msb & 15) + 11, planet8.x)
|
||||
planet8.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
|
||||
if planet8.species_is_alien {
|
||||
planet8.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||
planet8.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||
planet8.species_look = (seed2_msb ^ msb(seed[1])) & 7 ;bits 0-2 of (w0_hi EOR w1_hi)
|
||||
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[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() {
|
||||
uword temp = seed[0] + seed[1] + seed[2]
|
||||
seed[0] = seed[1]
|
||||
seed[1] = seed[2]
|
||||
seed[2] = temp
|
||||
}
|
||||
|
||||
sub twist(uword x) -> uword {
|
||||
ubyte xh = msb(x)
|
||||
ubyte xl = lsb(x)
|
||||
rol(xh)
|
||||
rol(xl)
|
||||
return mkword(xh, xl)
|
||||
}
|
||||
|
||||
sub debug_seed() {
|
||||
txt.print("\ngalaxy8 #")
|
||||
txt.print_ub(number)
|
||||
txt.print("\ngalaxy8 seed0=")
|
||||
txt.print_uwhex(galaxy8.seed[0], true)
|
||||
txt.print("\ngalaxy8 seed1=")
|
||||
txt.print_uwhex(galaxy8.seed[1], true)
|
||||
txt.print("\ngalaxy8 seed2=")
|
||||
txt.print_uwhex(galaxy8.seed[2], true)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
planet8 {
|
||||
%option force_output
|
||||
|
||||
str[] species_sizes = ["Large", "Fierce", "Small"]
|
||||
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
|
||||
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
|
||||
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
|
||||
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship8", "Communist", "Confederacy", "Democracy", "Corporate State"]
|
||||
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
|
||||
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
|
||||
|
||||
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||
str[] words8F = ["\xB0", "The planet8 \xB0", "The world \xB0", "This planet8", "This world"]
|
||||
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||
str[] words91 = ["planet8", "world", "place", "little planet8", "dump"]
|
||||
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||
|
||||
uword[] wordlists = [
|
||||
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||
|
||||
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||
|
||||
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||
|
||||
str name = " " ; 8 max
|
||||
ubyte number ; starts at 0 in new galaxy8, then increases by 1 for each generated planet8
|
||||
ubyte x
|
||||
ubyte y
|
||||
ubyte economy
|
||||
ubyte govtype
|
||||
ubyte techlevel
|
||||
ubyte population
|
||||
uword productivity
|
||||
uword radius
|
||||
ubyte species_is_alien ; otherwise "Human Colonials"
|
||||
ubyte species_size
|
||||
ubyte species_color
|
||||
ubyte species_look
|
||||
ubyte species_kind
|
||||
|
||||
sub set_seed(uword s1, uword s2) {
|
||||
goatsoup_seed[0] = lsb(s1)
|
||||
goatsoup_seed[1] = msb(s1)
|
||||
goatsoup_seed[2] = lsb(s2)
|
||||
goatsoup_seed[3] = msb(s2)
|
||||
reset_rnd()
|
||||
}
|
||||
|
||||
sub reset_rnd() {
|
||||
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||
}
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str name = " " ; 8 chars max
|
||||
ubyte nx = 0
|
||||
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||
ubyte x = goatsoup_rnd_number() & $3e
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
x++
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
}
|
||||
name[nx] = 0
|
||||
name[0] |= 32 ; uppercase first letter
|
||||
return name
|
||||
}
|
||||
|
||||
sub goatsoup_rnd_number() -> ubyte {
|
||||
ubyte x = goatsoup_rnd[0] * 2
|
||||
uword a = x as uword + goatsoup_rnd[2]
|
||||
if goatsoup_rnd[0] > 127
|
||||
a ++
|
||||
goatsoup_rnd[0] = lsb(a)
|
||||
goatsoup_rnd[2] = x
|
||||
x = goatsoup_rnd[1]
|
||||
ubyte ac = x + goatsoup_rnd[3] + msb(a)
|
||||
goatsoup_rnd[1] = ac
|
||||
goatsoup_rnd[3] = x
|
||||
return ac
|
||||
}
|
||||
|
||||
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||
uword ax
|
||||
uword ay
|
||||
if px>x
|
||||
ax=px-x
|
||||
else
|
||||
ax=x-px
|
||||
if py>y
|
||||
ay=py-y
|
||||
else
|
||||
ay=y-py
|
||||
ay /= 2
|
||||
ubyte d = sqrt16(ax*ax + ay*ay)
|
||||
if d>63
|
||||
return 255
|
||||
return d*4
|
||||
}
|
||||
|
||||
sub soup() -> str {
|
||||
str planet8_result = " " * 160
|
||||
uword[6] source_stack
|
||||
ubyte stack_ptr = 0
|
||||
str start_source = "\x8F is \x97."
|
||||
uword source_ptr = &start_source
|
||||
uword result_ptr = &planet8_result
|
||||
|
||||
reset_rnd()
|
||||
recursive_soup()
|
||||
return planet8_result
|
||||
|
||||
sub recursive_soup() {
|
||||
repeat {
|
||||
ubyte c = @(source_ptr)
|
||||
source_ptr++
|
||||
if c == $00 {
|
||||
@(result_ptr) = 0
|
||||
return
|
||||
}
|
||||
else if c <= $80 {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
else {
|
||||
if c <= $a4 {
|
||||
ubyte rnr = goatsoup_rnd_number()
|
||||
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
|
||||
source_stack[stack_ptr] = source_ptr
|
||||
stack_ptr++
|
||||
source_ptr = getword(c, wordNr)
|
||||
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||
stack_ptr--
|
||||
source_ptr = source_stack[stack_ptr]
|
||||
} else {
|
||||
if c == $b0 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
concat_string(&name + 1)
|
||||
}
|
||||
else if c == $b1 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
ubyte ni
|
||||
for ni in 1 to len(name) {
|
||||
ubyte cc = name[ni]
|
||||
if cc=='e' or cc=='o' or cc==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = cc
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
@(result_ptr) = 'i'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'a'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'n'
|
||||
result_ptr++
|
||||
}
|
||||
else if c == $b2 {
|
||||
concat_string(random_name())
|
||||
}
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub concat_string(uword str_ptr) {
|
||||
repeat {
|
||||
ubyte c = @(str_ptr)
|
||||
if c==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
str_ptr++
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub display(ubyte compressed) {
|
||||
if compressed {
|
||||
print_name_uppercase()
|
||||
txt.print(" TL:")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.spc()
|
||||
txt.print(econnames[economy])
|
||||
txt.spc()
|
||||
txt.print(govnames[govtype])
|
||||
} else {
|
||||
txt.print("\n\nSystem: ")
|
||||
print_name_uppercase()
|
||||
txt.print("\nPosition: ")
|
||||
txt.print_ub(x)
|
||||
txt.chrout('\'')
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
txt.chrout('#')
|
||||
txt.print_ub(number)
|
||||
txt.print("\nEconomy: ")
|
||||
txt.print(econnames[economy])
|
||||
txt.print("\nGovernment: ")
|
||||
txt.print(govnames[govtype])
|
||||
txt.print("\nTech Level: ")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.print("\nTurnover: ")
|
||||
txt.print_uw(productivity)
|
||||
txt.print("\nRadius: ")
|
||||
txt.print_uw(radius)
|
||||
txt.print("\nPopulation: ")
|
||||
txt.print_ub(population >> 3)
|
||||
txt.print(" Billion\nSpecies: ")
|
||||
if species_is_alien {
|
||||
if species_size < len(species_sizes) {
|
||||
txt.print(species_sizes[species_size])
|
||||
txt.spc()
|
||||
}
|
||||
if species_color < len(species_colors) {
|
||||
txt.print(species_colors[species_color])
|
||||
txt.spc()
|
||||
}
|
||||
if species_look < len(species_looks) {
|
||||
txt.print(species_looks[species_look])
|
||||
txt.spc()
|
||||
}
|
||||
if species_kind < len(species_kinds) {
|
||||
txt.print(species_kinds[species_kind])
|
||||
}
|
||||
} else {
|
||||
txt.print("Human Colonials")
|
||||
}
|
||||
txt.nl()
|
||||
txt.print(soup())
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub print_name_uppercase() {
|
||||
ubyte c
|
||||
for c in name
|
||||
txt.chrout(c | 32)
|
||||
}
|
||||
|
||||
asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY {
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc #$81
|
||||
asl a
|
||||
tay
|
||||
lda wordlists,y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda wordlists+1,y
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
util8 {
|
||||
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
|
||||
repeat {
|
||||
ubyte pc = @(prefixptr)
|
||||
ubyte sc = @(stringptr)
|
||||
if pc == 0
|
||||
return true
|
||||
; to lowercase for case insensitive compare:
|
||||
pc &= 127
|
||||
sc &= 127
|
||||
if pc != sc
|
||||
return false
|
||||
prefixptr++
|
||||
stringptr++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub print_right(ubyte width, uword st) {
|
||||
repeat width - string.length(st) {
|
||||
txt.spc()
|
||||
}
|
||||
txt.print(st)
|
||||
}
|
||||
|
||||
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
lda conv.uword2decimal.decTenThousands
|
||||
ldy #0 ; have we started printing?
|
||||
cmp #'0'
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decThousands
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decHundreds
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decTens
|
||||
jsr c64.CHROUT
|
||||
lda #'.'
|
||||
jsr c64.CHROUT
|
||||
lda conv.uword2decimal.decOnes
|
||||
jsr c64.CHROUT
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,993 +0,0 @@
|
||||
%import textio
|
||||
%import conv
|
||||
%import diskio
|
||||
%import string
|
||||
%import test_stack
|
||||
%option no_sysinit
|
||||
%zeropage basicsafe
|
||||
|
||||
; Prog8 adaptation of the Text-Elite galaxy9 system trading simulation engine.
|
||||
; Original C-version obtained from: http://www.elitehomepage.org/text/index.htm
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
perf9 {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet9 in galaxy9 one
|
||||
const ubyte numforZaonce = 129
|
||||
const ubyte numforDiso = 147
|
||||
const ubyte numforRiedquat = 46
|
||||
|
||||
sub start() {
|
||||
txt.lowercase()
|
||||
txt.print("\u000c\n --- TextElite v1.1 ---\n")
|
||||
|
||||
galaxy9.travel_to(1, numforLave)
|
||||
market9.init(0) ; Lave's market9 is seeded with 0
|
||||
ship9.init()
|
||||
planet9.display(false)
|
||||
|
||||
repeat {
|
||||
; test_stack.test()
|
||||
|
||||
str input = "????????"
|
||||
txt.print("\nCash: ")
|
||||
util9.print_10s(ship9.cash)
|
||||
txt.print("\nCommand (?=help): ")
|
||||
ubyte num_chars = txt.input_chars(input)
|
||||
txt.nl()
|
||||
if num_chars {
|
||||
when input[0] {
|
||||
'?' -> {
|
||||
txt.print("\nCommands are:\n"+
|
||||
"buy jump info cash >=save\n"+
|
||||
"sell teleport market9 hold <=load\n"+
|
||||
"fuel galhyp local quit\n")
|
||||
}
|
||||
'q' -> break
|
||||
'b' -> trader9.do_buy()
|
||||
's' -> trader9.do_sell()
|
||||
'f' -> trader9.do_fuel()
|
||||
'j' -> trader9.do_jump()
|
||||
't' -> trader9.do_teleport()
|
||||
'g' -> trader9.do_next_galaxy9()
|
||||
'i' -> trader9.do_info()
|
||||
'm' -> trader9.do_show_market9()
|
||||
'l' -> trader9.do_local()
|
||||
'c' -> trader9.do_cash()
|
||||
'h' -> trader9.do_hold()
|
||||
'<' -> trader9.do_load()
|
||||
'>' -> trader9.do_save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trader9 {
|
||||
str Savegame = "↑commander.save"
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
struct SaveData {
|
||||
ubyte galaxy9
|
||||
ubyte planet9
|
||||
ubyte cargo0
|
||||
ubyte cargo1
|
||||
ubyte cargo2
|
||||
ubyte cargo3
|
||||
ubyte cargo4
|
||||
ubyte cargo5
|
||||
ubyte cargo6
|
||||
ubyte cargo7
|
||||
ubyte cargo8
|
||||
ubyte cargo9
|
||||
ubyte cargo10
|
||||
ubyte cargo11
|
||||
ubyte cargo12
|
||||
ubyte cargo13
|
||||
ubyte cargo14
|
||||
ubyte cargo15
|
||||
ubyte cargo16
|
||||
uword cash
|
||||
ubyte max_cargo
|
||||
ubyte fuel
|
||||
}
|
||||
SaveData savedata
|
||||
|
||||
sub do_load() {
|
||||
txt.print("\nLoading universe...")
|
||||
if diskio.load(8, Savegame, &savedata) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
return
|
||||
}
|
||||
|
||||
ship9.cash = savedata.cash
|
||||
ship9.Max_cargo = savedata.max_cargo
|
||||
ship9.fuel = savedata.fuel
|
||||
sys.memcopy(&savedata.cargo0, ship9.cargohold, len(ship9.cargohold))
|
||||
galaxy9.travel_to(savedata.galaxy9, savedata.planet9)
|
||||
|
||||
planet9.display(false)
|
||||
}
|
||||
|
||||
sub do_save() {
|
||||
savedata.galaxy9 = galaxy9.number
|
||||
savedata.planet9 = planet9.number
|
||||
savedata.cash = ship9.cash
|
||||
savedata.max_cargo = ship9.Max_cargo
|
||||
savedata.fuel = ship9.fuel
|
||||
sys.memcopy(ship9.cargohold, &savedata.cargo0, len(ship9.cargohold))
|
||||
|
||||
txt.print("\nSaving universe...")
|
||||
diskio.delete(8, Savegame)
|
||||
if diskio.save(8, Savegame, &savedata, sizeof(savedata)) {
|
||||
txt.print("ok\n")
|
||||
} else {
|
||||
txt.print("\ni/o error: ")
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub do_jump() {
|
||||
txt.print("\nJump to what system? ")
|
||||
jump_to_system()
|
||||
}
|
||||
|
||||
sub do_teleport() {
|
||||
txt.print("\nCheat! Teleport to what system? ")
|
||||
ubyte fuel = ship9.fuel
|
||||
ship9.fuel = 255
|
||||
jump_to_system()
|
||||
ship9.fuel = fuel
|
||||
}
|
||||
|
||||
sub jump_to_system() {
|
||||
void txt.input_chars(input)
|
||||
ubyte current_planet9 = planet9.number
|
||||
ubyte x = planet9.x
|
||||
ubyte y = planet9.y
|
||||
if galaxy9.search_closest_planet9(input) {
|
||||
ubyte distance = planet9.distance(x, y)
|
||||
if distance <= ship9.fuel {
|
||||
galaxy9.init_market9_for_planet9()
|
||||
ship9.fuel -= distance
|
||||
txt.print("\n\nHyperspace jump! Arrived at:\n")
|
||||
planet9.display(true)
|
||||
return
|
||||
}
|
||||
txt.print("Insufficient fuel\n")
|
||||
} else {
|
||||
txt.print(" Not found!\n")
|
||||
}
|
||||
galaxy9.travel_to(galaxy9.number, current_planet9)
|
||||
}
|
||||
|
||||
sub do_buy() {
|
||||
txt.print("\nBuy what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market9.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if market9.current_quantity[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market9.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util9.print_10s(price)
|
||||
if price > ship9.cash {
|
||||
txt.print(" Not enough cash!\n")
|
||||
} else {
|
||||
ship9.cash -= price
|
||||
ship9.cargohold[ci] += amount
|
||||
market9.current_quantity[ci] -= amount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_sell() {
|
||||
txt.print("\nSell what commodity? ")
|
||||
str commodity = "???????????????"
|
||||
void txt.input_chars(commodity)
|
||||
ubyte ci = market9.match(commodity)
|
||||
if ci & 128 {
|
||||
txt.print("Unknown\n")
|
||||
} else {
|
||||
txt.print("\nHow much? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte amount = conv.str2ubyte(input)
|
||||
if ship9.cargohold[ci] < amount {
|
||||
txt.print(" Insufficient supply!\n")
|
||||
} else {
|
||||
uword price = market9.current_price[ci] * amount
|
||||
txt.print(" Total price: ")
|
||||
util9.print_10s(price)
|
||||
ship9.cash += price
|
||||
ship9.cargohold[ci] -= amount
|
||||
market9.current_quantity[ci] += amount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub do_fuel() {
|
||||
txt.print("\nBuy fuel. Amount? ")
|
||||
void txt.input_chars(input)
|
||||
ubyte buy_fuel = 10*conv.str2ubyte(input)
|
||||
ubyte max_fuel = ship9.Max_fuel - ship9.fuel
|
||||
if buy_fuel > max_fuel
|
||||
buy_fuel = max_fuel
|
||||
uword price = buy_fuel as uword * ship9.Fuel_cost
|
||||
if price > ship9.cash {
|
||||
txt.print("Not enough cash!\n")
|
||||
} else {
|
||||
ship9.cash -= price
|
||||
ship9.fuel += buy_fuel
|
||||
}
|
||||
}
|
||||
|
||||
sub do_cash() {
|
||||
txt.print("\nCheat! Set cash amount: ")
|
||||
void txt.input_chars(input)
|
||||
ship9.cash = conv.str2uword(input)
|
||||
}
|
||||
|
||||
sub do_hold() {
|
||||
txt.print("\nCheat! Set cargohold size: ")
|
||||
void txt.input_chars(input)
|
||||
ship9.Max_cargo = conv.str2ubyte(input)
|
||||
}
|
||||
|
||||
sub do_next_galaxy9() {
|
||||
galaxy9.travel_to(galaxy9.number+1, planet9.number)
|
||||
planet9.display(false)
|
||||
}
|
||||
|
||||
sub do_info() {
|
||||
txt.print("\nSystem name (empty=current): ")
|
||||
num_chars = txt.input_chars(input)
|
||||
if num_chars {
|
||||
ubyte current_planet9 = planet9.number
|
||||
if galaxy9.search_closest_planet9(input) {
|
||||
planet9.display(false)
|
||||
} else {
|
||||
txt.print(" Not found!")
|
||||
}
|
||||
galaxy9.travel_to(galaxy9.number, current_planet9)
|
||||
} else {
|
||||
planet9.display(false)
|
||||
}
|
||||
}
|
||||
|
||||
sub do_local() {
|
||||
galaxy9.local_area()
|
||||
}
|
||||
|
||||
sub do_show_market9() {
|
||||
market9.display()
|
||||
txt.print("\nFuel: ")
|
||||
util9.print_10s(ship9.fuel)
|
||||
txt.print(" Cargohold space: ")
|
||||
txt.print_ub(ship9.cargo_free())
|
||||
txt.print("t\n")
|
||||
}
|
||||
}
|
||||
|
||||
ship9 {
|
||||
const ubyte Max_fuel = 70
|
||||
const ubyte Fuel_cost = 2
|
||||
ubyte Max_cargo = 20
|
||||
|
||||
ubyte fuel = Max_fuel
|
||||
uword cash = 1000 ; actually has to be 4 bytes for the ultra rich....
|
||||
ubyte[17] cargohold = 0
|
||||
|
||||
sub init() {
|
||||
sys.memset(cargohold, len(cargohold), 0)
|
||||
}
|
||||
|
||||
sub cargo_free() -> ubyte {
|
||||
ubyte ci
|
||||
ubyte total = 0
|
||||
for ci in 0 to len(cargohold)-1 {
|
||||
if market9.units[ci]==0 ; tonnes only
|
||||
total += cargohold[ci]
|
||||
}
|
||||
return Max_cargo - total
|
||||
}
|
||||
}
|
||||
|
||||
market9 {
|
||||
ubyte[17] baseprices = [$13, $14, $41, $28, $53, $C4, $EB, $9A, $75, $4E, $7C, $B0, $20, $61, $AB, $2D, $35]
|
||||
byte[17] gradients = [-$02, -$01, -$03, -$05, -$05, $08, $1D, $0E, $06, $01, $0d, -$09, -$01, -$01, -$02, -$01, $0F]
|
||||
ubyte[17] basequants = [$06, $0A, $02, $E2, $FB, $36, $08, $38, $28, $11, $1D, $DC, $35, $42, $37, $FA, $C0]
|
||||
ubyte[17] maskbytes = [$01, $03, $07, $1F, $0F, $03, $78, $03, $07, $1F, $07, $3F, $03, $07, $1F, $0F, $07]
|
||||
ubyte[17] units = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0]
|
||||
str[17] names = ["Food", "Textiles", "Radioactives", "Slaves", "Liquor/Wines", "Luxuries", "Narcotics", "Computers",
|
||||
"Machinery", "Alloys", "Firearms", "Furs", "Minerals", "Gold", "Platinum", "Gem-Stones", "Alien Items"]
|
||||
|
||||
ubyte[17] current_quantity = 0
|
||||
uword[17] current_price = 0
|
||||
|
||||
sub init(ubyte fluct) {
|
||||
; Prices and availabilities are influenced by the planet9's economy type
|
||||
; (0-7) and a random "fluctuation" byte that was kept within the saved
|
||||
; commander position to keep the market9 prices constant over gamesaves.
|
||||
; Availabilities must be saved with the game since the player alters them
|
||||
; by buying (and selling(?))
|
||||
;
|
||||
; Almost all operations are one byte only and overflow "errors" are
|
||||
; extremely frequent and exploited.
|
||||
;
|
||||
; Trade Item prices are held internally in a single byte=true value/4.
|
||||
; The decimal point in prices is introduced only when printing them.
|
||||
; Internally, all prices are integers.
|
||||
; The player's cash is held in four bytes.
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
word product
|
||||
byte changing
|
||||
product = planet9.economy as word * gradients[ci]
|
||||
changing = fluct & maskbytes[ci] as byte
|
||||
ubyte q = (basequants[ci] as word + changing - product) as ubyte
|
||||
if q & $80
|
||||
q = 0 ; clip to positive 8-bit
|
||||
current_quantity[ci] = q & $3f
|
||||
q = (baseprices[ci] + changing + product) as ubyte
|
||||
current_price[ci] = q * $0004
|
||||
}
|
||||
current_quantity[16] = 0 ; force nonavailability of Alien Items
|
||||
}
|
||||
|
||||
sub display() {
|
||||
ubyte ci
|
||||
txt.nl()
|
||||
planet9.print_name_uppercase()
|
||||
txt.print(" trade market9:\n COMMODITY / PRICE / AVAIL / IN HOLD\n")
|
||||
for ci in 0 to len(names)-1 {
|
||||
util9.print_right(13, names[ci])
|
||||
txt.print(" ")
|
||||
util9.print_10s(current_price[ci])
|
||||
txt.print(" ")
|
||||
txt.print_ub(current_quantity[ci])
|
||||
when units[ci] {
|
||||
0 -> txt.chrout('t')
|
||||
1 -> txt.print("kg")
|
||||
2 -> txt.chrout('g')
|
||||
}
|
||||
txt.print(" ")
|
||||
txt.print_ub(ship9.cargohold[ci])
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub match(uword nameptr) -> ubyte {
|
||||
ubyte ci
|
||||
for ci in 0 to len(names)-1 {
|
||||
if util9.prefix_matches(nameptr, names[ci])
|
||||
return ci
|
||||
}
|
||||
return 255
|
||||
}
|
||||
}
|
||||
|
||||
galaxy9 {
|
||||
const uword GALSIZE = 256
|
||||
const uword base0 = $5A4A ; seeds for the first galaxy9
|
||||
const uword base1 = $0248
|
||||
const uword base2 = $B753
|
||||
|
||||
str pn_pairs = "..lexegezacebisousesarmaindirea.eratenberalavetiedorquanteisrion"
|
||||
|
||||
ubyte number
|
||||
|
||||
uword[3] seed
|
||||
|
||||
sub init(ubyte galaxy9num) {
|
||||
number = 1
|
||||
planet9.number = 255
|
||||
seed[0] = base0
|
||||
seed[1] = base1
|
||||
seed[2] = base2
|
||||
repeat galaxy9num-1 {
|
||||
nextgalaxy9()
|
||||
}
|
||||
}
|
||||
|
||||
sub nextgalaxy9() {
|
||||
seed[0] = twist(seed[0])
|
||||
seed[1] = twist(seed[1])
|
||||
seed[2] = twist(seed[2])
|
||||
number++
|
||||
if number==9
|
||||
number = 1
|
||||
}
|
||||
|
||||
sub travel_to(ubyte galaxy9num, ubyte system) {
|
||||
init(galaxy9num)
|
||||
generate_next_planet9() ; always at least planet9 0 (separate to avoid repeat ubyte overflow)
|
||||
repeat system {
|
||||
generate_next_planet9()
|
||||
}
|
||||
planet9.name = make_current_planet9_name()
|
||||
init_market9_for_planet9()
|
||||
}
|
||||
|
||||
sub init_market9_for_planet9() {
|
||||
market9.init(lsb(seed[0])+msb(seed[2]))
|
||||
}
|
||||
|
||||
sub search_closest_planet9(uword nameptr) -> ubyte {
|
||||
ubyte x = planet9.x
|
||||
ubyte y = planet9.y
|
||||
ubyte current_planet9_num = planet9.number
|
||||
|
||||
init(number)
|
||||
ubyte found = false
|
||||
ubyte current_closest_pi
|
||||
ubyte current_distance = 127
|
||||
ubyte pi
|
||||
for pi in 0 to 255 {
|
||||
generate_next_planet9()
|
||||
planet9.name = make_current_planet9_name()
|
||||
if util9.prefix_matches(nameptr, planet9.name) {
|
||||
ubyte distance = planet9.distance(x, y)
|
||||
if distance < current_distance {
|
||||
current_distance = distance
|
||||
current_closest_pi = pi
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found
|
||||
travel_to(number, current_closest_pi)
|
||||
else
|
||||
travel_to(number, current_planet9_num)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
sub local_area() {
|
||||
ubyte current_planet9 = planet9.number
|
||||
ubyte px = planet9.x
|
||||
ubyte py = planet9.y
|
||||
ubyte pn = 0
|
||||
|
||||
init(number)
|
||||
txt.print("\nGalaxy #")
|
||||
txt.print_ub(number)
|
||||
txt.print(" - systems in vicinity:\n")
|
||||
do {
|
||||
generate_next_planet9()
|
||||
ubyte distance = planet9.distance(px, py)
|
||||
if distance <= ship9.Max_fuel {
|
||||
if distance <= ship9.fuel
|
||||
txt.chrout('*')
|
||||
else
|
||||
txt.chrout('-')
|
||||
txt.spc()
|
||||
planet9.name = make_current_planet9_name()
|
||||
planet9.display(true)
|
||||
txt.print(" (")
|
||||
util9.print_10s(distance)
|
||||
txt.print(" LY)\n")
|
||||
}
|
||||
pn++
|
||||
} until pn==0
|
||||
|
||||
travel_to(number, current_planet9)
|
||||
}
|
||||
|
||||
ubyte pn_pair1
|
||||
ubyte pn_pair2
|
||||
ubyte pn_pair3
|
||||
ubyte pn_pair4
|
||||
ubyte longname
|
||||
|
||||
sub generate_next_planet9() {
|
||||
determine_planet9_properties()
|
||||
longname = lsb(seed[0]) & 64
|
||||
|
||||
; Always four iterations of random number
|
||||
pn_pair1 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair2 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair3 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
pn_pair4 = (msb(seed[2]) & 31) * 2
|
||||
tweakseed()
|
||||
}
|
||||
|
||||
sub make_current_planet9_name() -> str {
|
||||
ubyte ni = 0
|
||||
str name = " " ; max 8
|
||||
|
||||
if pn_pairs[pn_pair1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair1+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair1+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair2+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair2+1]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair3+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair3+1]
|
||||
ni++
|
||||
}
|
||||
|
||||
if longname {
|
||||
if pn_pairs[pn_pair4] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4]
|
||||
ni++
|
||||
}
|
||||
if pn_pairs[pn_pair4+1] != '.' {
|
||||
name[ni] = pn_pairs[pn_pair4+1]
|
||||
ni++
|
||||
}
|
||||
}
|
||||
|
||||
name[ni] = 0
|
||||
return name
|
||||
}
|
||||
|
||||
sub determine_planet9_properties() {
|
||||
; create the planet9's characteristics
|
||||
planet9.number++
|
||||
planet9.x = msb(seed[1])
|
||||
planet9.y = msb(seed[0])
|
||||
planet9.govtype = lsb(seed[1]) >> 3 & 7 ; bits 3,4 &5 of w1
|
||||
planet9.economy = msb(seed[0]) & 7 ; bits 8,9 &A of w0
|
||||
if planet9.govtype <= 1
|
||||
planet9.economy = (planet9.economy | 2)
|
||||
planet9.techlevel = (msb(seed[1]) & 3) + (planet9.economy ^ 7)
|
||||
planet9.techlevel += planet9.govtype >> 1
|
||||
if planet9.govtype & 1
|
||||
planet9.techlevel++
|
||||
planet9.population = 4 * planet9.techlevel + planet9.economy
|
||||
planet9.population += planet9.govtype + 1
|
||||
planet9.productivity = ((planet9.economy ^ 7) + 3) * (planet9.govtype + 4)
|
||||
planet9.productivity *= planet9.population * 8
|
||||
ubyte seed2_msb = msb(seed[2])
|
||||
planet9.radius = mkword((seed2_msb & 15) + 11, planet9.x)
|
||||
planet9.species_is_alien = lsb(seed[2]) & 128 ; bit 7 of w2_lo
|
||||
if planet9.species_is_alien {
|
||||
planet9.species_size = (seed2_msb >> 2) & 7 ; bits 2-4 of w2_hi
|
||||
planet9.species_color = seed2_msb >> 5 ; bits 5-7 of w2_hi
|
||||
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[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() {
|
||||
uword temp = seed[0] + seed[1] + seed[2]
|
||||
seed[0] = seed[1]
|
||||
seed[1] = seed[2]
|
||||
seed[2] = temp
|
||||
}
|
||||
|
||||
sub twist(uword x) -> uword {
|
||||
ubyte xh = msb(x)
|
||||
ubyte xl = lsb(x)
|
||||
rol(xh)
|
||||
rol(xl)
|
||||
return mkword(xh, xl)
|
||||
}
|
||||
|
||||
sub debug_seed() {
|
||||
txt.print("\ngalaxy9 #")
|
||||
txt.print_ub(number)
|
||||
txt.print("\ngalaxy9 seed0=")
|
||||
txt.print_uwhex(galaxy9.seed[0], true)
|
||||
txt.print("\ngalaxy9 seed1=")
|
||||
txt.print_uwhex(galaxy9.seed[1], true)
|
||||
txt.print("\ngalaxy9 seed2=")
|
||||
txt.print_uwhex(galaxy9.seed[2], true)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
planet9 {
|
||||
%option force_output
|
||||
|
||||
str[] species_sizes = ["Large", "Fierce", "Small"]
|
||||
str[] species_colors = ["Green", "Red", "Yellow", "Blue", "Black", "Harmless"]
|
||||
str[] species_looks = ["Slimy", "Bug-Eyed", "Horned", "Bony", "Fat", "Furry"]
|
||||
str[] species_kinds = ["Rodents", "Frogs", "Lizards", "Lobsters", "Birds", "Humanoids", "Felines", "Insects"]
|
||||
str[] govnames = ["Anarchy", "Feudal", "Multi-gov", "Dictatorship9", "Communist", "Confederacy", "Democracy", "Corporate State"]
|
||||
str[] econnames = ["Rich Industrial", "Average Industrial", "Poor Industrial", "Mainly Industrial",
|
||||
"Mainly Agricultural", "Rich Agricultural", "Average Agricultural", "Poor Agricultural"]
|
||||
|
||||
str[] words81 = ["fabled", "notable", "well known", "famous", "noted"]
|
||||
str[] words82 = ["very", "mildly", "most", "reasonably", ""]
|
||||
str[] words83 = ["ancient", "\x95", "great", "vast", "pink"]
|
||||
str[] words84 = ["\x9E \x9D plantations", "mountains", "\x9C", "\x94 forests", "oceans"]
|
||||
str[] words85 = ["shyness", "silliness", "mating traditions", "loathing of \x86", "love for \x86"]
|
||||
str[] words86 = ["food blenders", "tourists", "poetry", "discos", "\x8E"]
|
||||
str[] words87 = ["talking tree", "crab", "bat", "lobst", "\xB2"]
|
||||
str[] words88 = ["beset", "plagued", "ravaged", "cursed", "scourged"]
|
||||
str[] words89 = ["\x96 civil war", "\x9B \x98 \x99s", "a \x9B disease", "\x96 earthquakes", "\x96 solar activity"]
|
||||
str[] words8A = ["its \x83 \x84", "the \xB1 \x98 \x99", "its inhabitants' \x9A \x85", "\xA1", "its \x8D \x8E"]
|
||||
str[] words8B = ["juice", "brandy", "water", "brew", "gargle blasters"]
|
||||
str[] words8C = ["\xB2", "\xB1 \x99", "\xB1 \xB2", "\xB1 \x9B", "\x9B \xB2"]
|
||||
str[] words8D = ["fabulous", "exotic", "hoopy", "unusual", "exciting"]
|
||||
str[] words8E = ["cuisine", "night life", "casinos", "sit coms", " \xA1 "]
|
||||
str[] words8F = ["\xB0", "The planet9 \xB0", "The world \xB0", "This planet9", "This world"]
|
||||
str[] words90 = ["n unremarkable", " boring", " dull", " tedious", " revolting"]
|
||||
str[] words91 = ["planet9", "world", "place", "little planet9", "dump"]
|
||||
str[] words92 = ["wasp", "moth", "grub", "ant", "\xB2"]
|
||||
str[] words93 = ["poet", "arts graduate", "yak", "snail", "slug"]
|
||||
str[] words94 = ["tropical", "dense", "rain", "impenetrable", "exuberant"]
|
||||
str[] words95 = ["funny", "wierd", "unusual", "strange", "peculiar"]
|
||||
str[] words96 = ["frequent", "occasional", "unpredictable", "dreadful", "deadly"]
|
||||
str[] words97 = ["\x82 \x81 for \x8A", "\x82 \x81 for \x8A and \x8A", "\x88 by \x89", "\x82 \x81 for \x8A but \x88 by \x89", "a\x90 \x91"]
|
||||
str[] words98 = ["\x9B", "mountain", "edible", "tree", "spotted"]
|
||||
str[] words99 = ["\x9F", "\xA0", "\x87oid", "\x93", "\x92"]
|
||||
str[] words9A = ["ancient", "exceptional", "eccentric", "ingrained", "\x95"]
|
||||
str[] words9B = ["killer", "deadly", "evil", "lethal", "vicious"]
|
||||
str[] words9C = ["parking meters", "dust clouds", "ice bergs", "rock formations", "volcanoes"]
|
||||
str[] words9D = ["plant", "tulip", "banana", "corn", "\xB2weed"]
|
||||
str[] words9E = ["\xB2", "\xB1 \xB2", "\xB1 \x9B", "inhabitant", "\xB1 \xB2"]
|
||||
str[] words9F = ["shrew", "beast", "bison", "snake", "wolf"]
|
||||
str[] wordsA0 = ["leopard", "cat", "monkey", "goat", "fish"]
|
||||
str[] wordsA1 = ["\x8C \x8B", "\xB1 \x9F \xA2", "its \x8D \xA0 \xA2", "\xA3 \xA4", "\x8C \x8B"]
|
||||
str[] wordsA2 = ["meat", "cutlet", "steak", "burgers", "soup"]
|
||||
str[] wordsA3 = ["ice", "mud", "Zero-G", "vacuum", "\xB1 ultra"]
|
||||
str[] wordsA4 = ["hockey", "cricket", "karate", "polo", "tennis"]
|
||||
|
||||
uword[] wordlists = [
|
||||
words81, words82, words83, words84, words85, words86, words87, words88,
|
||||
words89, words8A, words8B, words8C, words8D, words8E, words8F, words90,
|
||||
words91, words92, words93, words94, words95, words96, words97, words98,
|
||||
words99, words9A, words9B, words9C, words9D, words9E, words9F, wordsA0,
|
||||
wordsA1, wordsA2, wordsA3, wordsA4]
|
||||
|
||||
str pairs0 = "abouseitiletstonlonuthnoallexegezacebisousesarmaindirea.eratenbe"
|
||||
|
||||
ubyte[4] goatsoup_rnd = [0, 0, 0, 0]
|
||||
ubyte[4] goatsoup_seed = [0, 0, 0, 0]
|
||||
|
||||
str name = " " ; 8 max
|
||||
ubyte number ; starts at 0 in new galaxy9, then increases by 1 for each generated planet9
|
||||
ubyte x
|
||||
ubyte y
|
||||
ubyte economy
|
||||
ubyte govtype
|
||||
ubyte techlevel
|
||||
ubyte population
|
||||
uword productivity
|
||||
uword radius
|
||||
ubyte species_is_alien ; otherwise "Human Colonials"
|
||||
ubyte species_size
|
||||
ubyte species_color
|
||||
ubyte species_look
|
||||
ubyte species_kind
|
||||
|
||||
sub set_seed(uword s1, uword s2) {
|
||||
goatsoup_seed[0] = lsb(s1)
|
||||
goatsoup_seed[1] = msb(s1)
|
||||
goatsoup_seed[2] = lsb(s2)
|
||||
goatsoup_seed[3] = msb(s2)
|
||||
reset_rnd()
|
||||
}
|
||||
|
||||
sub reset_rnd() {
|
||||
goatsoup_rnd[0] = goatsoup_seed[0]
|
||||
goatsoup_rnd[1] = goatsoup_seed[1]
|
||||
goatsoup_rnd[2] = goatsoup_seed[2]
|
||||
goatsoup_rnd[3] = goatsoup_seed[3]
|
||||
}
|
||||
|
||||
sub random_name() -> str {
|
||||
ubyte ii
|
||||
str name = " " ; 8 chars max
|
||||
ubyte nx = 0
|
||||
for ii in 0 to goatsoup_rnd_number() & 3 {
|
||||
ubyte x = goatsoup_rnd_number() & $3e
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
x++
|
||||
if pairs0[x] != '.' {
|
||||
name[nx] = pairs0[x]
|
||||
nx++
|
||||
}
|
||||
}
|
||||
name[nx] = 0
|
||||
name[0] |= 32 ; uppercase first letter
|
||||
return name
|
||||
}
|
||||
|
||||
sub goatsoup_rnd_number() -> ubyte {
|
||||
ubyte x = goatsoup_rnd[0] * 2
|
||||
uword a = x as uword + goatsoup_rnd[2]
|
||||
if goatsoup_rnd[0] > 127
|
||||
a ++
|
||||
goatsoup_rnd[0] = lsb(a)
|
||||
goatsoup_rnd[2] = x
|
||||
x = goatsoup_rnd[1]
|
||||
ubyte ac = x + goatsoup_rnd[3] + msb(a)
|
||||
goatsoup_rnd[1] = ac
|
||||
goatsoup_rnd[3] = x
|
||||
return ac
|
||||
}
|
||||
|
||||
sub distance(ubyte px, ubyte py) -> ubyte {
|
||||
uword ax
|
||||
uword ay
|
||||
if px>x
|
||||
ax=px-x
|
||||
else
|
||||
ax=x-px
|
||||
if py>y
|
||||
ay=py-y
|
||||
else
|
||||
ay=y-py
|
||||
ay /= 2
|
||||
ubyte d = sqrt16(ax*ax + ay*ay)
|
||||
if d>63
|
||||
return 255
|
||||
return d*4
|
||||
}
|
||||
|
||||
sub soup() -> str {
|
||||
str planet9_result = " " * 160
|
||||
uword[6] source_stack
|
||||
ubyte stack_ptr = 0
|
||||
str start_source = "\x8F is \x97."
|
||||
uword source_ptr = &start_source
|
||||
uword result_ptr = &planet9_result
|
||||
|
||||
reset_rnd()
|
||||
recursive_soup()
|
||||
return planet9_result
|
||||
|
||||
sub recursive_soup() {
|
||||
repeat {
|
||||
ubyte c = @(source_ptr)
|
||||
source_ptr++
|
||||
if c == $00 {
|
||||
@(result_ptr) = 0
|
||||
return
|
||||
}
|
||||
else if c <= $80 {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
else {
|
||||
if c <= $a4 {
|
||||
ubyte rnr = goatsoup_rnd_number()
|
||||
ubyte wordNr = (rnr >= $33) + (rnr >= $66) + (rnr >= $99) + (rnr >= $CC)
|
||||
source_stack[stack_ptr] = source_ptr
|
||||
stack_ptr++
|
||||
source_ptr = getword(c, wordNr)
|
||||
recursive_soup() ; RECURSIVE CALL - ignore the warning message from the compiler; we don't use local variables or parameters so we're safe in this case
|
||||
stack_ptr--
|
||||
source_ptr = source_stack[stack_ptr]
|
||||
} else {
|
||||
if c == $b0 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
concat_string(&name + 1)
|
||||
}
|
||||
else if c == $b1 {
|
||||
@(result_ptr) = name[0] | 32
|
||||
result_ptr++
|
||||
ubyte ni
|
||||
for ni in 1 to len(name) {
|
||||
ubyte cc = name[ni]
|
||||
if cc=='e' or cc=='o' or cc==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = cc
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
@(result_ptr) = 'i'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'a'
|
||||
result_ptr++
|
||||
@(result_ptr) = 'n'
|
||||
result_ptr++
|
||||
}
|
||||
else if c == $b2 {
|
||||
concat_string(random_name())
|
||||
}
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub concat_string(uword str_ptr) {
|
||||
repeat {
|
||||
ubyte c = @(str_ptr)
|
||||
if c==0
|
||||
break
|
||||
else {
|
||||
@(result_ptr) = c
|
||||
str_ptr++
|
||||
result_ptr++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub display(ubyte compressed) {
|
||||
if compressed {
|
||||
print_name_uppercase()
|
||||
txt.print(" TL:")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.spc()
|
||||
txt.print(econnames[economy])
|
||||
txt.spc()
|
||||
txt.print(govnames[govtype])
|
||||
} else {
|
||||
txt.print("\n\nSystem: ")
|
||||
print_name_uppercase()
|
||||
txt.print("\nPosition: ")
|
||||
txt.print_ub(x)
|
||||
txt.chrout('\'')
|
||||
txt.print_ub(y)
|
||||
txt.spc()
|
||||
txt.chrout('#')
|
||||
txt.print_ub(number)
|
||||
txt.print("\nEconomy: ")
|
||||
txt.print(econnames[economy])
|
||||
txt.print("\nGovernment: ")
|
||||
txt.print(govnames[govtype])
|
||||
txt.print("\nTech Level: ")
|
||||
txt.print_ub(techlevel+1)
|
||||
txt.print("\nTurnover: ")
|
||||
txt.print_uw(productivity)
|
||||
txt.print("\nRadius: ")
|
||||
txt.print_uw(radius)
|
||||
txt.print("\nPopulation: ")
|
||||
txt.print_ub(population >> 3)
|
||||
txt.print(" Billion\nSpecies: ")
|
||||
if species_is_alien {
|
||||
if species_size < len(species_sizes) {
|
||||
txt.print(species_sizes[species_size])
|
||||
txt.spc()
|
||||
}
|
||||
if species_color < len(species_colors) {
|
||||
txt.print(species_colors[species_color])
|
||||
txt.spc()
|
||||
}
|
||||
if species_look < len(species_looks) {
|
||||
txt.print(species_looks[species_look])
|
||||
txt.spc()
|
||||
}
|
||||
if species_kind < len(species_kinds) {
|
||||
txt.print(species_kinds[species_kind])
|
||||
}
|
||||
} else {
|
||||
txt.print("Human Colonials")
|
||||
}
|
||||
txt.nl()
|
||||
txt.print(soup())
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub print_name_uppercase() {
|
||||
ubyte c
|
||||
for c in name
|
||||
txt.chrout(c | 32)
|
||||
}
|
||||
|
||||
asmsub getword(ubyte list @A, ubyte wordidx @Y) -> uword @AY {
|
||||
%asm {{
|
||||
sty P8ZP_SCRATCH_REG
|
||||
sec
|
||||
sbc #$81
|
||||
asl a
|
||||
tay
|
||||
lda wordlists,y
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda wordlists+1,y
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
tay
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
pha
|
||||
iny
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
tay
|
||||
pla
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
util9 {
|
||||
sub prefix_matches(uword prefixptr, uword stringptr) -> ubyte {
|
||||
repeat {
|
||||
ubyte pc = @(prefixptr)
|
||||
ubyte sc = @(stringptr)
|
||||
if pc == 0
|
||||
return true
|
||||
; to lowercase for case insensitive compare:
|
||||
pc &= 127
|
||||
sc &= 127
|
||||
if pc != sc
|
||||
return false
|
||||
prefixptr++
|
||||
stringptr++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
sub print_right(ubyte width, uword st) {
|
||||
repeat width - string.length(st) {
|
||||
txt.spc()
|
||||
}
|
||||
txt.print(st)
|
||||
}
|
||||
|
||||
asmsub print_10s(uword value @AY) clobbers(A, X, Y) {
|
||||
%asm {{
|
||||
jsr conv.uword2decimal
|
||||
lda conv.uword2decimal.decTenThousands
|
||||
ldy #0 ; have we started printing?
|
||||
cmp #'0'
|
||||
beq +
|
||||
jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decThousands
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decHundreds
|
||||
cmp #'0'
|
||||
bne +
|
||||
cpy #0
|
||||
beq ++
|
||||
+ jsr c64.CHROUT
|
||||
iny
|
||||
+ lda conv.uword2decimal.decTens
|
||||
jsr c64.CHROUT
|
||||
lda #'.'
|
||||
jsr c64.CHROUT
|
||||
lda conv.uword2decimal.decOnes
|
||||
jsr c64.CHROUT
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'antlr'
|
||||
id 'java'
|
||||
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||
id "org.jetbrains.kotlin.jvm" version "1.5.10"
|
||||
}
|
||||
|
||||
targetCompatibility = 11
|
||||
|
||||
@@ -78,31 +78,18 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
}
|
||||
|
||||
private fun datatypeString(dt: DataType): String {
|
||||
return when(dt) {
|
||||
in NumericDatatypes -> dt.toString().toLowerCase()
|
||||
DataType.STR -> dt.toString().toLowerCase()
|
||||
return when (dt) {
|
||||
in NumericDatatypes -> dt.toString().lowercase()
|
||||
DataType.STR -> dt.toString().lowercase()
|
||||
DataType.ARRAY_UB -> "ubyte["
|
||||
DataType.ARRAY_B -> "byte["
|
||||
DataType.ARRAY_UW -> "uword["
|
||||
DataType.ARRAY_W -> "word["
|
||||
DataType.ARRAY_F -> "float["
|
||||
DataType.STRUCT -> "" // the name of the struct is enough
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(structDecl: StructDecl) {
|
||||
outputln("struct ${structDecl.name} {")
|
||||
scopelevel++
|
||||
for(decl in structDecl.statements) {
|
||||
outputi("")
|
||||
decl.accept(this)
|
||||
output("\n")
|
||||
}
|
||||
scopelevel--
|
||||
outputlni("}")
|
||||
}
|
||||
|
||||
override fun visit(decl: VarDecl) {
|
||||
|
||||
// if the vardecl is a parameter of a subroutine, don't output it again
|
||||
@@ -116,9 +103,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
VarDeclType.MEMORY -> output("&")
|
||||
}
|
||||
|
||||
if(decl.datatype==DataType.STRUCT && decl.struct!=null)
|
||||
output(decl.struct!!.name)
|
||||
|
||||
output(datatypeString(decl.datatype))
|
||||
if(decl.arraysize!=null) {
|
||||
decl.arraysize!!.indexExpr.accept(this)
|
||||
@@ -251,7 +235,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
}
|
||||
|
||||
override fun visit(branchStatement: BranchStatement) {
|
||||
output("if_${branchStatement.condition.toString().toLowerCase()} ")
|
||||
output("if_${branchStatement.condition.toString().lowercase()} ")
|
||||
branchStatement.truepart.accept(this)
|
||||
if(branchStatement.elsepart.statements.isNotEmpty()) {
|
||||
output(" else ")
|
||||
|
||||
@@ -8,6 +8,8 @@ import prog8.ast.walk.IAstVisitor
|
||||
import java.nio.file.Path
|
||||
import kotlin.math.abs
|
||||
|
||||
const val internedStringsModuleName = "prog8_interned_strings"
|
||||
|
||||
interface IStringEncoding {
|
||||
fun encodeString(str: String, altEncoding: Boolean): List<Short>
|
||||
fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
|
||||
@@ -124,18 +126,7 @@ interface INameScope {
|
||||
|
||||
fun lookup(scopedName: List<String>, localContext: Node) : Statement? {
|
||||
if(scopedName.size>1) {
|
||||
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
|
||||
// try the struct first.
|
||||
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
|
||||
val struct = thing?.struct
|
||||
if (struct != null) {
|
||||
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
|
||||
// return ref to the mangled name variable
|
||||
val mangled = mangledStructMemberName(thing.name, scopedName.last())
|
||||
return thing.definingScope().getLabelOrVariable(mangled)
|
||||
}
|
||||
}
|
||||
|
||||
// a scoped name refers to a name in another module.
|
||||
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)
|
||||
for(module in localContext.definingModule().program.modules) {
|
||||
var scope: INameScope? = module
|
||||
@@ -267,8 +258,7 @@ class Program(val name: String,
|
||||
get() = mainModule.loadAddress
|
||||
|
||||
var actualLoadAddress: Int = 0
|
||||
private val internedStrings = mutableMapOf<Pair<String, Boolean>, List<String>>()
|
||||
val internedStringsModuleName = "prog8_interned_strings"
|
||||
private val internedStringsUnique = mutableMapOf<Pair<String, Boolean>, List<String>>()
|
||||
|
||||
init {
|
||||
// insert a container module for all interned strings later
|
||||
@@ -292,21 +282,40 @@ class Program(val name: String,
|
||||
}
|
||||
|
||||
fun internString(string: StringLiteralValue): List<String> {
|
||||
// Move a string literal into the internal, deduplicated, string pool
|
||||
// replace it with a variable declaration that points to the entry in the pool.
|
||||
|
||||
if(string.parent is VarDecl) {
|
||||
// deduplication can only be performed safely for known-const strings (=string literals OUTSIDE OF A VARDECL)!
|
||||
throw FatalAstException("cannot intern a string literal that's part of a vardecl")
|
||||
}
|
||||
|
||||
fun getScopedName(string: StringLiteralValue): List<String> {
|
||||
val internedStringsBlock = modules
|
||||
.first { it.name == internedStringsModuleName }.statements
|
||||
.first { it is Block && it.name == internedStringsModuleName } as Block
|
||||
val varName = "string_${internedStringsBlock.statements.size}"
|
||||
val decl = VarDecl(
|
||||
VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, string,
|
||||
isArray = false, autogeneratedDontRemove = true, sharedWithAsm = false, position = string.position
|
||||
)
|
||||
internedStringsBlock.statements.add(decl)
|
||||
decl.linkParents(internedStringsBlock)
|
||||
return listOf(internedStringsModuleName, decl.name)
|
||||
}
|
||||
|
||||
val key = Pair(string.value, string.altEncoding)
|
||||
val existing = internedStrings[key]
|
||||
if(existing!=null)
|
||||
val existing = internedStringsUnique[key]
|
||||
if (existing != null)
|
||||
return existing
|
||||
|
||||
val decl = VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, "string_${internedStrings.size}", null, string,
|
||||
isArray = false, autogeneratedDontRemove = true, position = string.position)
|
||||
val internedStringsBlock = modules.first { it.name==internedStringsModuleName }.statements.first { it is Block && it.name == internedStringsModuleName}
|
||||
(internedStringsBlock as Block).statements.add(decl)
|
||||
decl.linkParents(internedStringsBlock)
|
||||
val scopedName = listOf(internedStringsModuleName, decl.name)
|
||||
internedStrings[key] = scopedName
|
||||
val scopedName = getScopedName(string)
|
||||
internedStringsUnique[key] = scopedName
|
||||
return scopedName
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
||||
|
||||
override val position: Position = Position.DUMMY
|
||||
@@ -384,20 +393,6 @@ class GlobalNamespace(val modules: List<Module>, private val builtinFunctionName
|
||||
return builtinPlaceholder
|
||||
}
|
||||
|
||||
if(scopedName.size>1) {
|
||||
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
|
||||
// try the struct first.
|
||||
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
|
||||
val struct = thing?.struct
|
||||
if (struct != null) {
|
||||
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
|
||||
// return ref to the mangled name variable
|
||||
val mangled = mangledStructMemberName(thing.name, scopedName.last())
|
||||
return thing.definingScope().getLabelOrVariable(mangled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// special case: the do....until statement can also look INSIDE the anonymous scope
|
||||
if(localContext.parent.parent is UntilLoop) {
|
||||
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.lookup(scopedName, localContext)
|
||||
@@ -407,7 +402,7 @@ class GlobalNamespace(val modules: List<Module>, private val builtinFunctionName
|
||||
|
||||
// lookup something from the module.
|
||||
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
||||
is Label, is VarDecl, is Block, is Subroutine, is StructDecl -> stmt
|
||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
||||
null -> null
|
||||
else -> throw SyntaxError("invalid identifier target type", stmt.position)
|
||||
}
|
||||
@@ -423,10 +418,6 @@ object BuiltinFunctionScopePlaceholder : INameScope {
|
||||
}
|
||||
|
||||
|
||||
// prefix for struct member variables
|
||||
fun mangledStructMemberName(varName: String, memberName: String) = "prog8struct_${varName}_$memberName"
|
||||
|
||||
|
||||
fun Number.toHex(): String {
|
||||
// 0..15 -> "0".."15"
|
||||
// 16..255 -> "$10".."$ff"
|
||||
|
||||
@@ -45,7 +45,8 @@ private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean, encoding: IStri
|
||||
it.subroutinedeclaration()!=null -> it.subroutinedeclaration().toAst(encoding)
|
||||
it.directive()!=null -> it.directive().toAst()
|
||||
it.inlineasm()!=null -> it.inlineasm().toAst()
|
||||
else -> throw FatalAstException("weird block statement $it")
|
||||
it.labeldef()!=null -> it.labeldef().toAst()
|
||||
else -> throw FatalAstException("weird block node $it")
|
||||
}
|
||||
}
|
||||
return Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), blockstatements.toMutableList(), isInLibrary, toPosition())
|
||||
@@ -61,62 +62,31 @@ private fun prog8Parser.VariabledeclarationContext.toAst(encoding: IStringEncodi
|
||||
val vd = it.vardecl()
|
||||
return VarDecl(
|
||||
VarDeclType.VAR,
|
||||
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
||||
vd.datatype()?.toAst() ?: DataType.UNDEFINED,
|
||||
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||
vd.arrayindex()?.toAst(encoding),
|
||||
vd.varname.text,
|
||||
null,
|
||||
it.expression().toAst(encoding),
|
||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||
false,
|
||||
vd.SHARED()!=null,
|
||||
it.toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
structvarinitializer()?.let {
|
||||
val vd = it.structvardecl()
|
||||
return VarDecl(
|
||||
VarDeclType.VAR,
|
||||
DataType.STRUCT,
|
||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
null,
|
||||
vd.varname.text,
|
||||
vd.structname.text,
|
||||
it.expression().toAst(encoding),
|
||||
isArray = false,
|
||||
autogeneratedDontRemove = false,
|
||||
position = it.toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
structvardecl()?.let {
|
||||
return VarDecl(
|
||||
VarDeclType.VAR,
|
||||
DataType.STRUCT,
|
||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
null,
|
||||
it.varname.text,
|
||||
it.structname.text,
|
||||
null,
|
||||
isArray = false,
|
||||
autogeneratedDontRemove = false,
|
||||
position = it.toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
constdecl()?.let {
|
||||
val cvarinit = it.varinitializer()
|
||||
val vd = cvarinit.vardecl()
|
||||
return VarDecl(
|
||||
VarDeclType.CONST,
|
||||
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
||||
vd.datatype()?.toAst() ?: DataType.UNDEFINED,
|
||||
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||
vd.arrayindex()?.toAst(encoding),
|
||||
vd.varname.text,
|
||||
null,
|
||||
cvarinit.expression().toAst(encoding),
|
||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||
false,
|
||||
vd.SHARED() != null,
|
||||
cvarinit.toPosition()
|
||||
)
|
||||
}
|
||||
@@ -126,24 +96,18 @@ private fun prog8Parser.VariabledeclarationContext.toAst(encoding: IStringEncodi
|
||||
val vd = mvarinit.vardecl()
|
||||
return VarDecl(
|
||||
VarDeclType.MEMORY,
|
||||
vd.datatype()?.toAst() ?: DataType.STRUCT,
|
||||
vd.datatype()?.toAst() ?: DataType.UNDEFINED,
|
||||
if (vd.ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||
vd.arrayindex()?.toAst(encoding),
|
||||
vd.varname.text,
|
||||
null,
|
||||
mvarinit.expression().toAst(encoding),
|
||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||
false,
|
||||
vd.SHARED()!=null,
|
||||
mvarinit.toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
structdecl()?.let {
|
||||
return StructDecl(it.identifier().text,
|
||||
it.vardecl().map { vd->vd.toAst(encoding) }.toMutableList(),
|
||||
toPosition())
|
||||
}
|
||||
|
||||
throw FatalAstException("weird variable decl $this")
|
||||
}
|
||||
|
||||
@@ -294,7 +258,7 @@ private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
||||
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
||||
= asmsub_param().map {
|
||||
val vardecl = it.vardecl()
|
||||
val datatype = vardecl.datatype()?.toAst() ?: DataType.STRUCT
|
||||
val datatype = vardecl.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||
val register = it.register().text
|
||||
var registerorpair: RegisterOrPair? = null
|
||||
var statusregister: Statusflag? = null
|
||||
@@ -360,7 +324,7 @@ private fun prog8Parser.Sub_return_partContext.toAst(): List<DataType> {
|
||||
|
||||
private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||
vardecl().map {
|
||||
val datatype = it.datatype()?.toAst() ?: DataType.STRUCT
|
||||
val datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||
SubroutineParameter(it.varname.text, datatype, it.toPosition())
|
||||
}
|
||||
|
||||
@@ -379,7 +343,7 @@ private fun prog8Parser.ClobberContext.toAst() : Set<CpuRegister> {
|
||||
return names.map { CpuRegister.valueOf(it) }.toSet()
|
||||
}
|
||||
|
||||
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
|
||||
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.uppercase())
|
||||
|
||||
private fun prog8Parser.ArrayindexContext.toAst(encoding: IStringEncoding) : ArrayIndex =
|
||||
ArrayIndex(expression().toAst(encoding), toPosition())
|
||||
@@ -575,7 +539,9 @@ private fun prog8Parser.Branch_stmtContext.toAst(encoding: IStringEncoding): Bra
|
||||
return BranchStatement(branchcondition, trueScope, elseScope, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
||||
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(
|
||||
text.substringAfter('_').uppercase()
|
||||
)
|
||||
|
||||
private fun prog8Parser.ForloopContext.toAst(encoding: IStringEncoding): ForLoop {
|
||||
val loopvar = identifier().toAst()
|
||||
@@ -633,14 +599,14 @@ private fun prog8Parser.When_choiceContext.toAst(encoding: IStringEncoding): Whe
|
||||
private fun prog8Parser.VardeclContext.toAst(encoding: IStringEncoding): VarDecl {
|
||||
return VarDecl(
|
||||
VarDeclType.VAR,
|
||||
datatype()?.toAst() ?: DataType.STRUCT,
|
||||
datatype()?.toAst() ?: DataType.UNDEFINED,
|
||||
if(ZEROPAGE() != null) ZeropageWish.PREFER_ZEROPAGE else ZeropageWish.DONTCARE,
|
||||
arrayindex()?.toAst(encoding),
|
||||
varname.text,
|
||||
null,
|
||||
null,
|
||||
ARRAYSIG() != null || arrayindex() != null,
|
||||
false,
|
||||
SHARED()!=null,
|
||||
toPosition()
|
||||
)
|
||||
}
|
||||
@@ -10,9 +10,9 @@ fun escape(str: String): String {
|
||||
'\n' -> "\\n"
|
||||
'\r' -> "\\r"
|
||||
'"' -> "\\\""
|
||||
in '\u8000'..'\u80ff' -> "\\x" + (it.toInt() - 0x8000).toString(16).padStart(2, '0')
|
||||
in '\u8000'..'\u80ff' -> "\\x" + (it.code - 0x8000).toString(16).padStart(2, '0')
|
||||
in '\u0000'..'\u00ff' -> it.toString()
|
||||
else -> "\\u" + it.toInt().toString(16).padStart(4, '0')
|
||||
else -> "\\u" + it.code.toString(16).padStart(4, '0')
|
||||
}
|
||||
}
|
||||
return es.joinToString("")
|
||||
|
||||
@@ -17,7 +17,7 @@ enum class DataType {
|
||||
ARRAY_UW, // pass by reference
|
||||
ARRAY_W, // pass by reference
|
||||
ARRAY_F, // pass by reference
|
||||
STRUCT; // pass by reference
|
||||
UNDEFINED;
|
||||
|
||||
/**
|
||||
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
|
||||
@@ -140,8 +140,8 @@ val IterableDatatypes = setOf(
|
||||
DataType.ARRAY_F
|
||||
)
|
||||
val PassByValueDatatypes = NumericDatatypes
|
||||
val PassByReferenceDatatypes = IterableDatatypes.plus(DataType.STRUCT)
|
||||
val ArrayElementTypes = mapOf(
|
||||
val PassByReferenceDatatypes = IterableDatatypes
|
||||
val ArrayToElementTypes = mapOf(
|
||||
DataType.STR to DataType.UBYTE,
|
||||
DataType.ARRAY_B to DataType.BYTE,
|
||||
DataType.ARRAY_UB to DataType.UBYTE,
|
||||
@@ -149,7 +149,7 @@ val ArrayElementTypes = mapOf(
|
||||
DataType.ARRAY_UW to DataType.UWORD,
|
||||
DataType.ARRAY_F to DataType.FLOAT
|
||||
)
|
||||
val ElementArrayTypes = mapOf(
|
||||
val ElementToArrayTypes = mapOf(
|
||||
DataType.BYTE to DataType.ARRAY_B,
|
||||
DataType.UBYTE to DataType.ARRAY_UB,
|
||||
DataType.WORD to DataType.ARRAY_W,
|
||||
|
||||
@@ -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.Objects
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
|
||||
|
||||
@@ -89,14 +89,14 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
||||
return when(operator) {
|
||||
"+" -> inferred
|
||||
"~", "not" -> {
|
||||
when(inferred.typeOrElse(DataType.STRUCT)) {
|
||||
when(inferred.typeOrElse(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes -> InferredTypes.knownFor(DataType.UBYTE)
|
||||
in WordDatatypes -> InferredTypes.knownFor(DataType.UWORD)
|
||||
else -> inferred
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
when(inferred.typeOrElse(DataType.STRUCT)) {
|
||||
when(inferred.typeOrElse(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes -> InferredTypes.knownFor(DataType.BYTE)
|
||||
in WordDatatypes -> InferredTypes.knownFor(DataType.WORD)
|
||||
else -> inferred
|
||||
@@ -268,7 +268,7 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
||||
if (target is VarDecl) {
|
||||
return when (target.datatype) {
|
||||
DataType.STR -> InferredTypes.knownFor(DataType.UBYTE)
|
||||
in ArrayDatatypes -> InferredTypes.knownFor(ArrayElementTypes.getValue(target.datatype))
|
||||
in ArrayDatatypes -> InferredTypes.knownFor(ArrayToElementTypes.getValue(target.datatype))
|
||||
else -> InferredTypes.unknown()
|
||||
}
|
||||
}
|
||||
@@ -567,7 +567,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
||||
|
||||
fun memsize(memsizer: IMemSizer): Int {
|
||||
if(type.isKnown) {
|
||||
val eltType = ArrayElementTypes.getValue(type.typeOrElse(DataType.STRUCT))
|
||||
val eltType = ArrayToElementTypes.getValue(type.typeOrElse(DataType.UNDEFINED))
|
||||
return memsizer.memorySize(eltType) * value.size
|
||||
}
|
||||
else throw IllegalArgumentException("array datatype is not yet known")
|
||||
@@ -580,17 +580,17 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
||||
if(forloop != null) {
|
||||
val loopvarDt = forloop.loopVarDt(program)
|
||||
if(loopvarDt.isKnown) {
|
||||
return if(loopvarDt.typeOrElse(DataType.STRUCT) !in ElementArrayTypes)
|
||||
return if(!loopvarDt.isArrayElement())
|
||||
InferredTypes.InferredType.unknown()
|
||||
else
|
||||
InferredTypes.InferredType.known(ElementArrayTypes.getValue(loopvarDt.typeOrElse(DataType.STRUCT)))
|
||||
InferredTypes.InferredType.known(ElementToArrayTypes.getValue(loopvarDt.typeOrElse(DataType.UNDEFINED)))
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, select the "biggegst" datatype based on the elements in the array.
|
||||
val datatypesInArray = value.map { it.inferType(program) }
|
||||
require(datatypesInArray.isNotEmpty() && datatypesInArray.all { it.isKnown }) { "can't determine type of empty array" }
|
||||
val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) }
|
||||
val dts = datatypesInArray.map { it.typeOrElse(DataType.UNDEFINED) }
|
||||
return when {
|
||||
DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F)
|
||||
DataType.STR in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
||||
@@ -602,8 +602,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
||||
DataType.ARRAY_W in dts ||
|
||||
DataType.ARRAY_UB in dts ||
|
||||
DataType.ARRAY_B in dts ||
|
||||
DataType.ARRAY_F in dts ||
|
||||
DataType.STRUCT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
||||
DataType.ARRAY_F in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW)
|
||||
else -> InferredTypes.InferredType.unknown()
|
||||
}
|
||||
}
|
||||
@@ -612,7 +611,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
||||
if(type.istype(targettype))
|
||||
return this
|
||||
if(targettype in ArrayDatatypes) {
|
||||
val elementType = ArrayElementTypes.getValue(targettype)
|
||||
val elementType = ArrayToElementTypes.getValue(targettype)
|
||||
val castArray = value.map{
|
||||
val num = it as? NumericLiteralValue
|
||||
if(num==null) {
|
||||
@@ -677,12 +676,12 @@ class RangeExpr(var from: Expression,
|
||||
fromDt istype DataType.WORD || toDt istype DataType.WORD -> InferredTypes.knownFor(DataType.ARRAY_W)
|
||||
fromDt istype DataType.BYTE || toDt istype DataType.BYTE -> InferredTypes.knownFor(DataType.ARRAY_B)
|
||||
else -> {
|
||||
val fdt = fromDt.typeOrElse(DataType.STRUCT)
|
||||
val tdt = toDt.typeOrElse(DataType.STRUCT)
|
||||
val fdt = fromDt.typeOrElse(DataType.UNDEFINED)
|
||||
val tdt = toDt.typeOrElse(DataType.UNDEFINED)
|
||||
if(fdt largerThan tdt)
|
||||
InferredTypes.knownFor(ElementArrayTypes.getValue(fdt))
|
||||
InferredTypes.knownFor(ElementToArrayTypes.getValue(fdt))
|
||||
else
|
||||
InferredTypes.knownFor(ElementArrayTypes.getValue(tdt))
|
||||
InferredTypes.knownFor(ElementToArrayTypes.getValue(tdt))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -786,26 +785,17 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||
return when (val targetStmt = targetStatement(program)) {
|
||||
is VarDecl -> InferredTypes.knownFor(targetStmt.datatype)
|
||||
is StructDecl -> InferredTypes.knownFor(DataType.STRUCT)
|
||||
else -> InferredTypes.InferredType.unknown()
|
||||
}
|
||||
}
|
||||
|
||||
fun memberOfStruct(program: Program) = this.targetVarDecl(program)?.struct
|
||||
fun wasStringLiteral(program: Program): Boolean {
|
||||
val decl = targetVarDecl(program)
|
||||
if(decl == null || !decl.autogeneratedDontRemove)
|
||||
return false
|
||||
|
||||
fun firstStructVarName(program: Program): String? {
|
||||
// take the name of the first struct member of the structvariable instead
|
||||
// if it's just a regular variable, return null.
|
||||
val struct = memberOfStruct(program) ?: return null
|
||||
val decl = targetVarDecl(program)!!
|
||||
if(decl.datatype!=DataType.STRUCT)
|
||||
return null
|
||||
|
||||
val firstStructMember = struct.nameOfFirstMember()
|
||||
// find the flattened var that belongs to this first struct member
|
||||
val firstVarName = listOf(decl.name, firstStructMember)
|
||||
val firstVar = definingScope().lookup(firstVarName, this) as VarDecl
|
||||
return firstVar.name
|
||||
val scope=decl.definingModule()
|
||||
return scope.name==internedStringsModuleName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package prog8.ast.expressions
|
||||
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.*
|
||||
import java.util.*
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ object InferredTypes {
|
||||
require(!(datatype!=null && (isUnknown || isVoid))) { "invalid combination of args" }
|
||||
}
|
||||
|
||||
val isKnown = datatype!=null
|
||||
val isKnown = datatype!=null && datatype!=DataType.UNDEFINED
|
||||
fun typeOrElse(alternative: DataType) = if(isUnknown || isVoid) alternative else datatype!!
|
||||
infix fun istype(type: DataType): Boolean = if(isUnknown || isVoid) false else this.datatype==type
|
||||
|
||||
@@ -42,6 +42,17 @@ object InferredTypes {
|
||||
isKnown && (datatype!! isAssignableTo targetDt)
|
||||
infix fun isNotAssignableTo(targetDt: InferredType): Boolean = !this.isAssignableTo(targetDt)
|
||||
infix fun isNotAssignableTo(targetDt: DataType): Boolean = !this.isAssignableTo(targetDt)
|
||||
|
||||
fun isBytes() = datatype in ByteDatatypes
|
||||
fun isWords() = datatype in WordDatatypes
|
||||
fun isInteger() = datatype in IntegerDatatypes
|
||||
fun isNumeric() = datatype in NumericDatatypes
|
||||
fun isArray() = datatype in ArrayDatatypes
|
||||
fun isString() = datatype in StringlyDatatypes
|
||||
fun isIterable() = datatype in IterableDatatypes
|
||||
fun isPassByReference() = datatype in PassByReferenceDatatypes
|
||||
fun isPassByValue() = datatype in PassByValueDatatypes
|
||||
fun isArrayElement() = datatype in ElementToArrayTypes
|
||||
}
|
||||
|
||||
private val unknownInstance = InferredType.unknown()
|
||||
@@ -57,8 +68,7 @@ object InferredTypes {
|
||||
DataType.ARRAY_B to InferredType.known(DataType.ARRAY_B),
|
||||
DataType.ARRAY_UW to InferredType.known(DataType.ARRAY_UW),
|
||||
DataType.ARRAY_W to InferredType.known(DataType.ARRAY_W),
|
||||
DataType.ARRAY_F to InferredType.known(DataType.ARRAY_F),
|
||||
DataType.STRUCT to InferredType.known(DataType.STRUCT)
|
||||
DataType.ARRAY_F to InferredType.known(DataType.ARRAY_F)
|
||||
)
|
||||
|
||||
fun void() = voidInstance
|
||||
|
||||
@@ -161,16 +161,12 @@ open class VarDecl(val type: VarDeclType,
|
||||
val zeropage: ZeropageWish,
|
||||
var arraysize: ArrayIndex?,
|
||||
override val name: String,
|
||||
private val structName: String?,
|
||||
var value: Expression?,
|
||||
val isArray: Boolean,
|
||||
val autogeneratedDontRemove: Boolean,
|
||||
val sharedWithAsm: Boolean,
|
||||
override val position: Position) : Statement(), ISymbolStatement {
|
||||
override lateinit var parent: Node
|
||||
var struct: StructDecl? = null // set later (because at parse time, we only know the name)
|
||||
private set
|
||||
var structHasBeenFlattened = false // set later
|
||||
private set
|
||||
var allowInitializeWithZero = true
|
||||
|
||||
// prefix for literal values that are turned into a variable on the heap
|
||||
@@ -184,11 +180,11 @@ open class VarDecl(val type: VarDeclType,
|
||||
if(!array.type.isKnown)
|
||||
throw FatalAstException("unknown dt")
|
||||
else
|
||||
array.type.typeOrElse(DataType.STRUCT)
|
||||
val declaredType = ArrayElementTypes.getValue(arrayDt)
|
||||
array.type.typeOrElse(DataType.UNDEFINED)
|
||||
val declaredType = ArrayToElementTypes.getValue(arrayDt)
|
||||
val arraysize = ArrayIndex.forArray(array)
|
||||
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, array,
|
||||
isArray = true, autogeneratedDontRemove = true, position = array.position)
|
||||
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, array,
|
||||
isArray = true, autogeneratedDontRemove = true, sharedWithAsm = false, position = array.position)
|
||||
}
|
||||
|
||||
fun defaultZero(dt: DataType, position: Position) = when(dt) {
|
||||
@@ -221,16 +217,6 @@ open class VarDecl(val type: VarDeclType,
|
||||
this.parent = parent
|
||||
arraysize?.linkParents(this)
|
||||
value?.linkParents(this)
|
||||
if(structName!=null) {
|
||||
val structStmt = definingScope().lookup(listOf(structName), this)
|
||||
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) {
|
||||
@@ -243,7 +229,7 @@ open class VarDecl(val type: VarDeclType,
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
|
||||
override fun toString(): String {
|
||||
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, struct=$structName, value=$value, pos=$position)"
|
||||
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, value=$value, pos=$position)"
|
||||
}
|
||||
|
||||
fun zeroElementValue(): NumericLiteralValue {
|
||||
@@ -253,45 +239,16 @@ open class VarDecl(val type: VarDeclType,
|
||||
throw IllegalArgumentException("attempt to get zero value for vardecl that shouldn't get it")
|
||||
}
|
||||
|
||||
fun flattenStructMembers(): MutableList<Statement> {
|
||||
val result = struct!!.statements.mapIndexed { index, statement ->
|
||||
val member = statement as VarDecl
|
||||
val initvalueOrig = if(value!=null) (value as ArrayLiteralValue).value[index] else null
|
||||
val initvalue = when(initvalueOrig) {
|
||||
is AddressOf -> initvalueOrig.copy()
|
||||
is ArrayIndexedExpression -> initvalueOrig.copy()
|
||||
is DirectMemoryRead -> initvalueOrig.copy()
|
||||
is IdentifierReference -> initvalueOrig.copy()
|
||||
else -> initvalueOrig
|
||||
}
|
||||
VarDecl(
|
||||
VarDeclType.VAR,
|
||||
member.datatype,
|
||||
ZeropageWish.NOT_IN_ZEROPAGE,
|
||||
member.arraysize,
|
||||
mangledStructMemberName(name, member.name),
|
||||
struct!!.name,
|
||||
initvalue,
|
||||
member.isArray,
|
||||
true,
|
||||
member.position
|
||||
)
|
||||
}.toMutableList<Statement>()
|
||||
structHasBeenFlattened = true
|
||||
return result
|
||||
}
|
||||
|
||||
fun copy(): VarDecl {
|
||||
val c = VarDecl(type, declaredDatatype, zeropage, arraysize, name, structName, value, isArray, autogeneratedDontRemove, position)
|
||||
val c = VarDecl(type, declaredDatatype, zeropage, arraysize, name, value, isArray, autogeneratedDontRemove, sharedWithAsm, position)
|
||||
c.allowInitializeWithZero = this.allowInitializeWithZero
|
||||
c.structHasBeenFlattened = this.structHasBeenFlattened
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
// a vardecl used only for subroutine parameters
|
||||
class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Position)
|
||||
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, null, false, true, position)
|
||||
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.DONTCARE, null, name, null, false, true, false, position)
|
||||
|
||||
class ArrayIndex(var indexExpr: Expression,
|
||||
override val position: Position) : Node {
|
||||
@@ -581,7 +538,7 @@ class InlineAssembly(val assembly: String, override val position: Position) : St
|
||||
|
||||
class AnonymousScope(override var statements: MutableList<Statement>,
|
||||
override val position: Position) : INameScope, Statement() {
|
||||
override val name: String
|
||||
override val name: String = "<anon-$sequenceNumber>"
|
||||
override lateinit var parent: Node
|
||||
|
||||
companion object {
|
||||
@@ -589,7 +546,7 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
||||
}
|
||||
|
||||
init {
|
||||
name = "<anon-$sequenceNumber>" // make sure it's an invalid soruce code identifier so user source code can never produce it
|
||||
// make sure it's an invalid soruce code identifier so user source code can never produce it
|
||||
sequenceNumber++
|
||||
}
|
||||
|
||||
@@ -631,6 +588,8 @@ class AsmGenInfo {
|
||||
var usedRegsaveY = false
|
||||
var usedFloatEvalResultVar1 = false
|
||||
var usedFloatEvalResultVar2 = false
|
||||
|
||||
val extraVars = mutableListOf<Triple<DataType, String, Int?>>()
|
||||
}
|
||||
|
||||
// the subroutine class covers both the normal user-defined subroutines,
|
||||
@@ -955,36 +914,6 @@ class WhenChoice(var values: MutableList<Expression>?, // if null, th
|
||||
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
}
|
||||
|
||||
class StructDecl(override val name: String,
|
||||
override var statements: MutableList<Statement>, // actually, only vardecls here
|
||||
override val position: Position): Statement(), INameScope {
|
||||
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
this.statements.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
require(replacement is Statement)
|
||||
val idx = statements.indexOfFirst { it===node }
|
||||
statements[idx] = replacement
|
||||
replacement.parent = this
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
fun nameOfFirstMember() = (statements.first() as VarDecl).name
|
||||
}
|
||||
|
||||
class DirectMemoryWrite(var addressExpression: Expression, override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
|
||||
|
||||
@@ -111,7 +111,6 @@ abstract class AstWalker {
|
||||
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
||||
@@ -152,7 +151,6 @@ abstract class AstWalker {
|
||||
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
|
||||
@@ -177,7 +175,7 @@ abstract class AstWalker {
|
||||
fun applyModifications(): Int {
|
||||
// check if there are double removes, keep only the last one
|
||||
val removals = modifications.filter { it.first is IAstModification.Remove }
|
||||
if(removals.size>0) {
|
||||
if(removals.isNotEmpty()) {
|
||||
val doubles = removals.groupBy { (it.first as IAstModification.Remove).node }.filter { it.value.size>1 }
|
||||
doubles.forEach {
|
||||
for(doubleRemove in it.value.dropLast(1)) {
|
||||
@@ -235,7 +233,6 @@ abstract class AstWalker {
|
||||
track(before(decl, parent), decl, parent)
|
||||
decl.value?.accept(this, decl)
|
||||
decl.arraysize?.accept(this, decl)
|
||||
decl.struct?.accept(this, decl)
|
||||
track(after(decl, parent), decl, parent)
|
||||
}
|
||||
|
||||
@@ -435,11 +432,5 @@ abstract class AstWalker {
|
||||
whenChoice.statements.accept(this, whenChoice)
|
||||
track(after(whenChoice, parent), whenChoice, parent)
|
||||
}
|
||||
|
||||
fun visit(structDecl: StructDecl, parent: Node) {
|
||||
track(before(structDecl, parent), structDecl, parent)
|
||||
structDecl.statements.forEach { it.accept(this, structDecl) }
|
||||
track(after(structDecl, parent), structDecl, parent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ interface IAstVisitor {
|
||||
fun visit(decl: VarDecl) {
|
||||
decl.value?.accept(this)
|
||||
decl.arraysize?.accept(this)
|
||||
decl.struct?.accept(this)
|
||||
}
|
||||
|
||||
fun visit(subroutine: Subroutine) {
|
||||
@@ -170,8 +169,4 @@ interface IAstVisitor {
|
||||
whenChoice.values?.forEach { it.accept(this) }
|
||||
whenChoice.statements.accept(this)
|
||||
}
|
||||
|
||||
fun visit(structDecl: StructDecl) {
|
||||
structDecl.statements.forEach { it.accept(this) }
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,12 @@ fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
||||
internal fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDefault().getPath(stringPath, *rest)
|
||||
|
||||
|
||||
class ModuleImporter {
|
||||
class ModuleImporter(private val program: Program,
|
||||
private val encoder: IStringEncoding,
|
||||
private val compilationTargetName: String,
|
||||
private val libdirs: List<String>) {
|
||||
|
||||
fun importModule(program: Program, filePath: Path, encoder: IStringEncoding, compilationTargetName: String): Module {
|
||||
fun importModule(filePath: Path): Module {
|
||||
print("importing '${moduleName(filePath.fileName)}'")
|
||||
if(filePath.parent!=null) {
|
||||
var importloc = filePath.toString()
|
||||
@@ -41,16 +44,18 @@ class ModuleImporter {
|
||||
if(!Files.isReadable(filePath))
|
||||
throw ParsingFailedError("No such file: $filePath")
|
||||
|
||||
val content = filePath.toFile().readText().replace("\r\n", "\n")
|
||||
return importModule(program, CharStreams.fromString(content), filePath, false, encoder, compilationTargetName)
|
||||
var content = filePath.toFile().readText().replace("\r\n", "\n") // normalize line endings
|
||||
if(content.last()!='\n')
|
||||
content+='\n' // grammar requires blocks (and thus module files) to end in an EOL
|
||||
|
||||
return importModule(CharStreams.fromString(content), filePath, false)
|
||||
}
|
||||
|
||||
fun importLibraryModule(program: Program, name: String,
|
||||
encoder: IStringEncoding, compilationTargetName: String): Module? {
|
||||
fun importLibraryModule(name: String): Module? {
|
||||
val import = Directive("%import", listOf(
|
||||
DirectiveArg("", name, 42, position = Position("<<<implicit-import>>>", 0, 0, 0))
|
||||
), Position("<<<implicit-import>>>", 0, 0, 0))
|
||||
return executeImportDirective(program, import, Paths.get(""), encoder, compilationTargetName)
|
||||
return executeImportDirective(import, Paths.get(""))
|
||||
}
|
||||
|
||||
private class MyErrorListener: ConsoleErrorListener() {
|
||||
@@ -67,8 +72,7 @@ class ModuleImporter {
|
||||
}
|
||||
}
|
||||
|
||||
private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean,
|
||||
encoder: IStringEncoding, compilationTargetName: String): Module {
|
||||
private fun importModule(stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
||||
val moduleName = moduleName(modulePath.fileName)
|
||||
val lexer = CustomLexer(modulePath, stream)
|
||||
lexer.removeErrorListeners()
|
||||
@@ -97,14 +101,13 @@ class ModuleImporter {
|
||||
lines.asSequence()
|
||||
.mapIndexed { i, it -> i to it }
|
||||
.filter { (it.second as? Directive)?.directive == "%import" }
|
||||
.forEach { executeImportDirective(program, it.second as Directive, modulePath, encoder, compilationTargetName) }
|
||||
.forEach { executeImportDirective(it.second as Directive, modulePath) }
|
||||
|
||||
moduleAst.statements = lines
|
||||
return moduleAst
|
||||
}
|
||||
|
||||
private fun executeImportDirective(program: Program, import: Directive, source: Path,
|
||||
encoder: IStringEncoding, compilationTargetName: String): Module? {
|
||||
private fun executeImportDirective(import: Directive, source: Path): Module? {
|
||||
if(import.directive!="%import" || import.args.size!=1 || import.args[0].name==null)
|
||||
throw SyntaxError("invalid import directive", import.position)
|
||||
val moduleName = import.args[0].name!!
|
||||
@@ -123,12 +126,11 @@ class ModuleImporter {
|
||||
resource.use {
|
||||
println("importing '$moduleName' (library)")
|
||||
val content = it.reader().readText().replace("\r\n", "\n")
|
||||
importModule(program, CharStreams.fromString(content), Paths.get("@embedded@/$resourcePath"),
|
||||
true, encoder, compilationTargetName)
|
||||
importModule(CharStreams.fromString(content), Paths.get("@embedded@/$resourcePath"), true)
|
||||
}
|
||||
} else {
|
||||
val modulePath = tryGetModuleFromFile(moduleName, source, import.position)
|
||||
importModule(program, modulePath, encoder, compilationTargetName)
|
||||
importModule(modulePath)
|
||||
}
|
||||
|
||||
removeDirectivesFromImportedModule(importedModule)
|
||||
@@ -160,15 +162,10 @@ class ModuleImporter {
|
||||
|
||||
private fun tryGetModuleFromFile(name: String, source: Path, position: Position?): Path {
|
||||
val fileName = "$name.p8"
|
||||
val locations = if(source.toString().isEmpty()) mutableListOf<Path>() else mutableListOf(source.parent ?: Path.of("."))
|
||||
|
||||
val propPath = System.getProperty("prog8.libdir")
|
||||
if(propPath!=null)
|
||||
locations.add(pathFrom(propPath))
|
||||
val envPath = System.getenv("PROG8_LIBDIR")
|
||||
if(envPath!=null)
|
||||
locations.add(pathFrom(envPath))
|
||||
locations.add(Paths.get(Paths.get("").toAbsolutePath().toString(), "prog8lib"))
|
||||
val libpaths = libdirs.map {Path.of(it)}
|
||||
val locations =
|
||||
(if(source.toString().isEmpty()) libpaths else libpaths.drop(1) + listOf(source.parent ?: Path.of("."))) +
|
||||
listOf(Paths.get(Paths.get("").toAbsolutePath().toString(), "prog8lib"))
|
||||
|
||||
locations.forEach {
|
||||
val file = pathFrom(it.toString(), fileName)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||
id "org.jetbrains.kotlin.jvm" version "1.5.10"
|
||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ Start the compiler with the ``-watch`` argument to enable this.
|
||||
It will compile your program and then instead of exiting, it waits for any changes in the module source files.
|
||||
As soon as a change happens, the program gets compiled again.
|
||||
It is possible to use the watch mode with multiple modules as well, but it will
|
||||
recompile everything in that list even if only of the files got updated.
|
||||
recompile everything in that list even if only one of the files got updated.
|
||||
|
||||
Other options
|
||||
^^^^^^^^^^^^^
|
||||
@@ -117,11 +117,11 @@ They are embedded into the packaged release version of the compiler so you don't
|
||||
where they are, but their names are still reserved.
|
||||
|
||||
|
||||
User defined library files
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
User defined library files and -location
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
You can create library files yourself too that can be shared among programs.
|
||||
You can tell the compiler where it should look for these files, by setting the java command line property ``prog8.libdir``
|
||||
or by setting the ``PROG8_LIBDIR`` environment variable to the correct directory.
|
||||
You can tell the compiler where it should look for these files, by using
|
||||
the libdirs command line option.
|
||||
|
||||
|
||||
.. _debugging:
|
||||
|
||||
@@ -52,10 +52,12 @@ Language features
|
||||
still able to directly use memory addresses and ROM subroutines,
|
||||
and inline assembly to have full control when every register, cycle or byte matters
|
||||
- Subroutines with parameters and return values
|
||||
- complex nested expressions are possible
|
||||
- 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.
|
||||
- Variable data types include signed and unsigned bytes and words, arrays, strings.
|
||||
- Floating point math also supported if the target system provides floating point library routines (C64 and Cx16 both do).
|
||||
- Strings can contain escaped characters but also many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest petscii equivalents.
|
||||
- High-level code optimizations, such as const-folding, expression and statement simplifications/rewriting.
|
||||
- Many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``sort`` and ``reverse``
|
||||
- Programs can be run multiple times without reloading because of automatic variable (re)initializations.
|
||||
|
||||
+47
-49
@@ -67,8 +67,6 @@ Label
|
||||
This is a named position in your code where you can jump to from another place.
|
||||
You can jump to it with a jump statement elsewhere. It is also possible to use a
|
||||
subroutine call to a label (but without parameters and return value).
|
||||
Labels can only be defined in a block or in another subroutine, so you can't define a label
|
||||
inside a loop statement block for instance.
|
||||
|
||||
Scope
|
||||
Also known as 'namespace', this is a named box around the symbols defined in it.
|
||||
@@ -204,6 +202,16 @@ Example::
|
||||
byte @zp zeropageCounter = 42
|
||||
|
||||
|
||||
*shared tag:*
|
||||
If you add the ``@shared`` tag to the variable declaration, the compiler will know that this variable
|
||||
is a prog8 variable shared with some assembly code elsewhere. This means that the assembly code can
|
||||
refer to the variable even if it's otherwise not used in prog8 code itself.
|
||||
(usually, these kinds of 'unused' variables are optimized away by the compiler, resulting in an error
|
||||
when assembling the rest of the code). Example::
|
||||
|
||||
byte @shared assemblyVariable = 42
|
||||
|
||||
|
||||
Integers
|
||||
^^^^^^^^
|
||||
|
||||
@@ -320,47 +328,22 @@ read the syntax reference on strings.
|
||||
For regular assignments you still need to use an explicit ``&`` (address-of) to take
|
||||
the address of the string or array.
|
||||
|
||||
.. caution::
|
||||
It's probably best to avoid changing the contents in strings and treat them as static.
|
||||
This includes changing certain letters by index, or by assigning a new value, or by
|
||||
modifying the string via other means for example ``substr`` function and its cousins.
|
||||
This is because the changes persist in memory. If your program exits and is restarted
|
||||
(without reloading it from disk), it will then start working with the modified strings
|
||||
instead of the original ones!
|
||||
The same is true for arrays! So be careful to (re)initialize them if needed.
|
||||
.. note:: Strings and their (im)mutability
|
||||
|
||||
*String literals outside of a string variable's initialization value*,
|
||||
are considered to be "constant", i.e. the string isn't going to change
|
||||
during the execution of the program. The compiler takes advantage of this in certain
|
||||
ways. For instance, multiple identical occurrences of a string literal are folded into
|
||||
just one string allocation in memory. Examples of such strings are the string literals
|
||||
passed to a subroutine as arguments.
|
||||
|
||||
Structs
|
||||
^^^^^^^
|
||||
|
||||
A struct is a group of one or more other variables.
|
||||
This allows you to reuse the definition and manipulate it as a whole.
|
||||
Individual variables in the struct are accessed as you would expect, just
|
||||
use a scoped name to refer to them: ``structvariable.membername``.
|
||||
|
||||
Structs are a bit limited in Prog8: you can only use numerical variables
|
||||
as member of a struct, so strings and arrays and other structs can not be part of a struct.
|
||||
Also, it is not possible to use a struct itself inside an array.
|
||||
Structs are mainly syntactic sugar for repeated groups of vardecls
|
||||
and assignments that belong together. However,
|
||||
*they are layed out in sequence in memory as the members are defined*
|
||||
which may be usefulif you want to pass pointers around.
|
||||
|
||||
To create a variable of a struct type you need to define the struct itself,
|
||||
and then create a variable with it::
|
||||
|
||||
struct Color {
|
||||
ubyte red
|
||||
ubyte green
|
||||
ubyte blue
|
||||
}
|
||||
|
||||
Color rgb = [255,122,0] ; note that struct initializer value is same as an array
|
||||
Color another ; the init value is optional, like arrays
|
||||
|
||||
another = rgb ; assign all of the values of rgb to another
|
||||
another.blue = 255 ; set a single member
|
||||
|
||||
*Strings that aren't such string literals are considered to be unique*, even if they
|
||||
are the same as a string defined elsewhere. This includes the strings assigned to
|
||||
a string variable in its declaration! These kind of strings are not deduplicated and
|
||||
are just copied into the program in their own unique part of memory. This means that
|
||||
it is okay to treat those strings as mutable; you can safely change the contents
|
||||
of such a string without destroying other occurrences (as long as you stay within
|
||||
the size of the allocated string!)
|
||||
|
||||
|
||||
Special types: const and memory-mapped
|
||||
@@ -845,9 +828,6 @@ 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,
|
||||
@@ -878,11 +858,6 @@ sizeof(name)
|
||||
For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes).
|
||||
Note: usually you will be interested in the number of elements in an array, use len() for that.
|
||||
|
||||
offsetof(membername)
|
||||
Number of bytes from the start of a struct variable that this member variable is located.
|
||||
For now, this only works on members of a declared struct variable and not yet on members
|
||||
referenced from the struct type itself. This might be improved in a future version of the language.
|
||||
|
||||
swap(x, y)
|
||||
Swap the values of numerical variables (or memory locations) x and y in a fast way.
|
||||
|
||||
@@ -897,6 +872,29 @@ memory(name, size)
|
||||
The return value is just a simple uword address so it cannot be used as an array in your program.
|
||||
You can only treat it as a pointer or use it in inline assembly.
|
||||
|
||||
callfar(bank, address, argumentaddress) ; NOTE: specific to cx16 compiler target for now
|
||||
Calls an assembly routine in another ram-bank on the CommanderX16 (using the ``jsrfar`` routine)
|
||||
The banked RAM is located in the address range $A000-$BFFF (8 kilobyte).
|
||||
Notice that bank $00 is used by the Kernal and should not be used by user code.
|
||||
The third argument can be used to designate the memory address
|
||||
of an argument for the routine; it will be loaded into the A register and will
|
||||
receive the result value returned by the routine in the A register. If you leave this at zero,
|
||||
no argument passing will be done.
|
||||
If the routine requires different arguments or return values, ``callfar`` cannot be used
|
||||
and you'll have to set up a call to ``jsrfar`` yourself to process this.
|
||||
|
||||
callrom(bank, address, argumentaddress) ; NOTE: specific to cx16 compiler target for now
|
||||
Calls an assembly routine in another rom-bank on the CommanderX16
|
||||
The banked ROM is located in the address range $C000-$FFFF (16 kilobyte).
|
||||
There are 32 banks (0 to 31).
|
||||
The third argument can be used to designate the memory address
|
||||
of an argument for the routine; it will be loaded into the A register and will
|
||||
receive the result value returned by the routine in the A register. If you leave this at zero,
|
||||
no argument passing will be done.
|
||||
If the routine requires different arguments or return values, ``callrom`` cannot be used
|
||||
and you'll have to set up a call in assembly code yourself that handles the banking and
|
||||
argument/returnvalues.
|
||||
|
||||
|
||||
Library routines
|
||||
----------------
|
||||
|
||||
@@ -108,7 +108,7 @@ Directives
|
||||
|
||||
.. data:: %import <name>
|
||||
|
||||
Level: module, block.
|
||||
Level: module.
|
||||
This reads and compiles the named module source file as part of your current program.
|
||||
Symbols from the imported module become available in your code,
|
||||
without a module or filename prefix.
|
||||
@@ -136,32 +136,32 @@ Directives
|
||||
|
||||
.. data:: %asmbinary "<filename>" [, <offset>[, <length>]]
|
||||
|
||||
Level: block.
|
||||
Level: not at module scope.
|
||||
This directive can only be used inside a block.
|
||||
The assembler will include the file as binary bytes at this point, prog8 will not process this at all.
|
||||
The optional offset and length can be used to select a particular piece of the file.
|
||||
The file is located relative to the current working directory!
|
||||
|
||||
.. data:: %asminclude "<filename>", "scopelabel"
|
||||
.. data:: %asminclude "<filename>"
|
||||
|
||||
Level: block.
|
||||
Level: not at module scope.
|
||||
This directive can only be used inside a block.
|
||||
The assembler will include the file as raw assembly source text at this point,
|
||||
prog8 will not process this at all, with one exception: the labels.
|
||||
The scopelabel argument will be used as a prefix to access the labels from the included source code,
|
||||
otherwise you would risk symbol redefinitions or duplications.
|
||||
If you know what you are doing you can leave it as an empty string to not have a scope prefix.
|
||||
prog8 will not process this at all. Symbols defined in the included assembly can not be referenced
|
||||
from prog8 code. However they can be referenced from other assembly code if properly prefixed.
|
||||
Be careful: you risk symbol redefinitions or duplications if you include a piece of
|
||||
assembly into a prog8 block that already defines symbols itself.
|
||||
The compiler first looks for the file relative to the same directory as the module containing this statement is in,
|
||||
if the file can't be found there it is searched relative to the current directory.
|
||||
|
||||
.. data:: %breakpoint
|
||||
|
||||
Level: block, subroutine.
|
||||
Level: not at module scope.
|
||||
Defines a debugging breakpoint at this location. See :ref:`debugging`
|
||||
|
||||
.. data:: %asm {{ ... }}
|
||||
|
||||
Level: block, subroutine.
|
||||
Level: not at module scope.
|
||||
Declares that there is *inline assembly code* in the lines enclosed by the curly braces.
|
||||
This code will be written as-is into the generated output file.
|
||||
The assembler syntax used should be for the 3rd party cross assembler tool that Prog8 uses.
|
||||
@@ -235,9 +235,11 @@ for them. You can give them an initial value as well. That value can be a simple
|
||||
or an expression. If you don't provide an intial value yourself, zero will be used.
|
||||
You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
|
||||
when selecting variables to be put into zeropage.
|
||||
You can add a ``@shared`` shared-tag, to tell the compiler that the variable is shared
|
||||
with some assembly code and that it should not be optimized away if not used elsewhere.
|
||||
The syntax is::
|
||||
|
||||
<datatype> [ @zp ] <variable name> [ = <initial value> ]
|
||||
<datatype> [ @shared ] [ @zp ] <variable name> [ = <initial value> ]
|
||||
|
||||
Various examples::
|
||||
|
||||
@@ -252,8 +254,8 @@ Various examples::
|
||||
byte[5] values ; array of 5 bytes, initially set to zero
|
||||
byte[5] values = 255 ; initialize with five 255 bytes
|
||||
|
||||
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
|
||||
Color rgb = {1,255,0} ; a struct variable with initial values
|
||||
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
|
||||
word @shared asmvar ; variable is used in assembly code but not elsewhere
|
||||
|
||||
|
||||
Data types
|
||||
@@ -387,27 +389,6 @@ Syntax is familiar with brackets: ``arrayvar[x]`` ::
|
||||
string[4] ; the fifth character (=byte) in the string
|
||||
|
||||
|
||||
Struct
|
||||
^^^^^^
|
||||
|
||||
A *struct* has to be defined to specify what its member variables are.
|
||||
There are one or more members::
|
||||
|
||||
struct <structname> {
|
||||
<vardecl>
|
||||
[ <vardecl> ...]
|
||||
}
|
||||
|
||||
You can only use numerical variables as member of a struct, so strings and arrays
|
||||
and other structs can not be part of a struct. Vice versa, a struct can not occur in an array.
|
||||
|
||||
After defining a struct you can use the name of the struct as a data type to declare variables with.
|
||||
|
||||
Struct variables can be assigned a struct literal value (also in their declaration as initial value)::
|
||||
|
||||
Color rgb = [255, 100, 0] ; note that the value is an array
|
||||
|
||||
|
||||
String
|
||||
^^^^^^
|
||||
|
||||
@@ -425,6 +406,9 @@ There are several escape sequences available to put special characters into your
|
||||
- ``\uHHHH`` - a unicode codepoint \u0000 - \uffff (16-bit hexadecimal)
|
||||
- ``\xHH`` - 8-bit hex value that will be copied verbatim *without encoding*
|
||||
|
||||
- String literals can contain many symbols directly if they have a petscii equivalent, such as "♠♥♣♦π▚●○╳".
|
||||
Characters like ^, _, \\, {, } and | (that have no direct PETSCII counterpart) are still accepted and converted to the closest PETSCII equivalents. (Make sure you save the source file in UTF-8 encoding if you use this.)
|
||||
|
||||
|
||||
Operators
|
||||
---------
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- 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)
|
||||
- test all examples (including imgviewer, assembler and petaxian) before release of the new version
|
||||
|
||||
- simplify cx16.joystick_get2() once this cx16 rom issue is resolved: https://github.com/commanderx16/x16-rom/issues/203
|
||||
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
|
||||
- get rid of all other TODO's in the code ;-)
|
||||
|
||||
|
||||
Low prio
|
||||
^^^^^^^^
|
||||
- see if we can remove more ".typeOrElse(DataType.UNDEFINED)"
|
||||
- optimize several inner loops in gfx2 even further?
|
||||
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)?
|
||||
- add a flood fill routine to gfx2?
|
||||
|
||||
+6
-8
@@ -8,8 +8,8 @@
|
||||
main {
|
||||
const ubyte database_size = 100
|
||||
|
||||
uword animal_names_buf
|
||||
uword questions_buf
|
||||
uword animal_names_buf = memory("animalnames", 500)
|
||||
uword questions_buf = memory("questions", 2000)
|
||||
uword animal_names_ptr
|
||||
uword questions_ptr
|
||||
|
||||
@@ -23,8 +23,6 @@ main {
|
||||
|
||||
sub start() {
|
||||
; initialize the database
|
||||
animal_names_buf = memory("animalnames", 500)
|
||||
questions_buf = memory("questions", 2000)
|
||||
animal_names_ptr = animal_names_buf
|
||||
questions_ptr = questions_buf
|
||||
|
||||
@@ -93,7 +91,7 @@ main {
|
||||
txt.print("is it a ")
|
||||
txt.print(animals[animal_number])
|
||||
txt.print("? ")
|
||||
txt.input_chars(userinput)
|
||||
void txt.input_chars(userinput)
|
||||
if userinput[0] == 'y' {
|
||||
txt.print("\n\nsee, i knew it!\n")
|
||||
return
|
||||
@@ -101,18 +99,18 @@ main {
|
||||
|
||||
str name = "x"*30
|
||||
txt.print("\n\ni give up. what is it? ")
|
||||
txt.input_chars(name)
|
||||
void txt.input_chars(name)
|
||||
txt.print("\nwhat yes-no question would best articulate the difference\nbetween a ")
|
||||
txt.print(animals[animal_number])
|
||||
txt.print(" and a ")
|
||||
txt.print(name)
|
||||
txt.print("? ")
|
||||
txt.input_chars(userinput)
|
||||
void txt.input_chars(userinput)
|
||||
txt.print("\nfor a ")
|
||||
txt.print(name)
|
||||
txt.print(", what is the answer to that; yes or no? ")
|
||||
str answer = "x"*10
|
||||
txt.input_chars(answer)
|
||||
void txt.input_chars(answer)
|
||||
|
||||
animals[new_animal_number] = animal_names_ptr
|
||||
questions[new_question_number] = questions_ptr
|
||||
|
||||
@@ -42,7 +42,7 @@ main {
|
||||
active_height--
|
||||
upwards = false
|
||||
} else {
|
||||
target_height = 8 + fastrnd8() % 16
|
||||
target_height = 8 + rnd() % 16
|
||||
if upwards
|
||||
mountain = 233
|
||||
else
|
||||
@@ -57,7 +57,7 @@ main {
|
||||
txt.scroll_left(true)
|
||||
|
||||
; float the balloon
|
||||
if fastrnd8() & %10000
|
||||
if rnd() & %10000
|
||||
c64.SPXY[1] ++
|
||||
else
|
||||
c64.SPXY[1] --
|
||||
@@ -71,10 +71,10 @@ main {
|
||||
txt.setcc(39, yy, 160, 8) ; draw mountain
|
||||
}
|
||||
|
||||
yy = fastrnd8()
|
||||
yy = rnd()
|
||||
if yy > 100 {
|
||||
; draw a star
|
||||
txt.setcc(39, yy % (active_height-1), '.', fastrnd8())
|
||||
txt.setcc(39, yy % (active_height-1), '.', rnd())
|
||||
}
|
||||
|
||||
if yy > 200 {
|
||||
@@ -85,7 +85,7 @@ main {
|
||||
tree = 88
|
||||
else if yy & %00100000 != 0
|
||||
tree = 65
|
||||
if fastrnd8() > 130
|
||||
if rnd() > 130
|
||||
treecolor = 13
|
||||
txt.setcc(39, active_height, tree, treecolor)
|
||||
}
|
||||
|
||||
+5
-6
@@ -24,12 +24,11 @@ main {
|
||||
; Setup Starting Ball Positions
|
||||
ubyte lp
|
||||
for lp in 0 to ballCount-1 {
|
||||
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()
|
||||
BX[lp] = rnd() % txt.DEFAULT_WIDTH
|
||||
BY[lp] = rnd() % txt.DEFAULT_HEIGHT
|
||||
BC[lp] = rnd() & 15
|
||||
DX[lp] = rnd() & 1
|
||||
DY[lp] = rnd() & 1
|
||||
}
|
||||
|
||||
; start clock
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
%import gfx2
|
||||
%import palette
|
||||
%import textio
|
||||
|
||||
|
||||
; TODO WORK IN PROGRESS...
|
||||
; want to make Amiga 'copper' bars color cycling effects
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
palette.set_all_black()
|
||||
gfx2.screen_mode(4)
|
||||
|
||||
ubyte yy
|
||||
for yy in 0 to 239
|
||||
gfx2.horizontal_line(0, yy, 320, yy & 63)
|
||||
|
||||
repeat {
|
||||
colors.random_bar()
|
||||
colors.set_palette()
|
||||
repeat 20
|
||||
sys.waitvsync()
|
||||
}
|
||||
|
||||
repeat {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
colors {
|
||||
ubyte cr
|
||||
ubyte cg
|
||||
ubyte cb
|
||||
ubyte[48+16] reds
|
||||
ubyte[48+16] greens
|
||||
ubyte[48+16] blues
|
||||
ubyte bar_size
|
||||
|
||||
sub random_rgb12() {
|
||||
do {
|
||||
uword rr = rndw()
|
||||
cr = msb(rr) & 15
|
||||
cg = lsb(rr)
|
||||
cb = cg & 15
|
||||
cg >>= 4
|
||||
} until cr+cg+cb >= 12
|
||||
}
|
||||
|
||||
sub random_bar() {
|
||||
; fade black -> color then fade color -> white
|
||||
random_rgb12()
|
||||
ubyte r=0
|
||||
ubyte g=0
|
||||
ubyte b=0
|
||||
ubyte different
|
||||
bar_size = 0
|
||||
|
||||
repeat {
|
||||
different = false
|
||||
if r != cr {
|
||||
different = true
|
||||
r++
|
||||
}
|
||||
if g != cg {
|
||||
different = true
|
||||
g++
|
||||
}
|
||||
if b != cb {
|
||||
different = true
|
||||
b++
|
||||
}
|
||||
if not different
|
||||
break
|
||||
reds[bar_size] = r
|
||||
greens[bar_size] = g
|
||||
blues[bar_size] = b
|
||||
bar_size++
|
||||
}
|
||||
repeat {
|
||||
different = false
|
||||
if r != 15 {
|
||||
different = true
|
||||
r++
|
||||
}
|
||||
if g != 15 {
|
||||
different = true
|
||||
g++
|
||||
}
|
||||
if b != 15 {
|
||||
different = true
|
||||
b++
|
||||
}
|
||||
if not different
|
||||
break
|
||||
reds[bar_size] = r
|
||||
greens[bar_size] = g
|
||||
blues[bar_size] = b
|
||||
bar_size++
|
||||
}
|
||||
; mirror bottom half from top half
|
||||
ubyte mi = bar_size-1
|
||||
repeat mi {
|
||||
reds[bar_size] = reds[mi]
|
||||
greens[bar_size] = greens[mi]
|
||||
blues[bar_size] = blues[mi]
|
||||
bar_size++
|
||||
mi--
|
||||
}
|
||||
; make rest of bar black (bars are not always the same length using the simplistic algorithm above...)
|
||||
while bar_size != 48+16 {
|
||||
reds[bar_size] = $0
|
||||
greens[bar_size] = $0
|
||||
blues[bar_size] = $0
|
||||
bar_size++
|
||||
}
|
||||
}
|
||||
|
||||
sub set_palette() {
|
||||
ubyte ix
|
||||
for ix in 0 to 48+15 {
|
||||
uword color = mkword(reds[ix], (greens[ix] << 4) | blues[ix] )
|
||||
palette.set_color(ix, color)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -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] = fastrnd8()
|
||||
c64.SPXY[i*2+1] = rnd()
|
||||
}
|
||||
|
||||
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 = fastrnd8()
|
||||
ubyte @zp r = rnd()
|
||||
if r>200
|
||||
c64.SPXY[i]++
|
||||
else if r<40
|
||||
|
||||
+7
-11
@@ -6,21 +6,17 @@
|
||||
|
||||
main {
|
||||
|
||||
struct Ball {
|
||||
float t
|
||||
ubyte color
|
||||
}
|
||||
|
||||
sub start() {
|
||||
|
||||
Ball ball
|
||||
float ball_t
|
||||
ubyte ball_color
|
||||
|
||||
repeat {
|
||||
ubyte xx=(sin(ball.t) * txt.DEFAULT_WIDTH/2.1) + txt.DEFAULT_WIDTH/2.0 as ubyte
|
||||
ubyte yy=(cos(ball.t*1.1356) * txt.DEFAULT_HEIGHT/2.1) + txt.DEFAULT_HEIGHT/2.0 as ubyte
|
||||
txt.setcc(xx, yy, 81, ball.color)
|
||||
ball.t += 0.08
|
||||
ball.color++
|
||||
ubyte xx=(sin(ball_t) * txt.DEFAULT_WIDTH/2.1) + txt.DEFAULT_WIDTH/2.0 as ubyte
|
||||
ubyte yy=(cos(ball_t*1.1356) * txt.DEFAULT_HEIGHT/2.1) + txt.DEFAULT_HEIGHT/2.0 as ubyte
|
||||
txt.setcc(xx, yy, 81, ball_color)
|
||||
ball_t += 0.08
|
||||
ball_color++
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-12
@@ -6,21 +6,18 @@
|
||||
main {
|
||||
const uword screenwidth = txt.DEFAULT_WIDTH
|
||||
const uword screenheight = txt.DEFAULT_HEIGHT
|
||||
struct Ball {
|
||||
uword anglex
|
||||
uword angley
|
||||
ubyte color
|
||||
}
|
||||
uword anglex
|
||||
uword angley
|
||||
ubyte color
|
||||
|
||||
sub start() {
|
||||
Ball ball
|
||||
repeat {
|
||||
ubyte x = msb(sin8u(msb(ball.anglex)) * screenwidth)
|
||||
ubyte y = msb(cos8u(msb(ball.angley)) * screenheight)
|
||||
txt.setcc(x, y, 81, ball.color)
|
||||
ball.anglex+=366
|
||||
ball.angley+=291
|
||||
ball.color++
|
||||
ubyte x = msb(sin8u(msb(anglex)) * screenwidth)
|
||||
ubyte y = msb(cos8u(msb(angley)) * screenheight)
|
||||
txt.setcc(x, y, 81, color)
|
||||
anglex+=366
|
||||
angley+=291
|
||||
color++
|
||||
}
|
||||
}
|
||||
}
|
||||
+101
-9
@@ -1,17 +1,109 @@
|
||||
%import textio
|
||||
%import gfx2
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
repeat {
|
||||
sys.waitvsync()
|
||||
ubyte joy = lsb(cx16.joystick_get2(0))
|
||||
txt.print_ubbin(joy,1)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
repeat {
|
||||
; test program for the optimization of repeat var allocation (asmgen.createRepeatCounterVar)
|
||||
; output must be: 60 6164 6224 12328
|
||||
|
||||
uword xx
|
||||
|
||||
sub start() {
|
||||
|
||||
xx=0
|
||||
|
||||
repeat 10 {
|
||||
xx++
|
||||
}
|
||||
repeat 10 {
|
||||
xx++
|
||||
}
|
||||
repeat 10 {
|
||||
xx++
|
||||
}
|
||||
repeat 10 {
|
||||
xx++
|
||||
}
|
||||
repeat 5 {
|
||||
repeat 4 {
|
||||
xx++
|
||||
}
|
||||
}
|
||||
txt.print_uw(xx)
|
||||
txt.nl()
|
||||
|
||||
repeat 1000 {
|
||||
xx++
|
||||
}
|
||||
repeat 1000 {
|
||||
xx++
|
||||
}
|
||||
repeat 1000 {
|
||||
xx++
|
||||
}
|
||||
repeat 260 {
|
||||
repeat 4 {
|
||||
xx++
|
||||
}
|
||||
}
|
||||
repeat 260 {
|
||||
repeat 260 {
|
||||
xx++
|
||||
}
|
||||
}
|
||||
txt.print_uw(xx)
|
||||
txt.nl()
|
||||
|
||||
sub2()
|
||||
|
||||
if xx!=12328
|
||||
txt.print("\n!fail!\n")
|
||||
else
|
||||
txt.print("\nok\n")
|
||||
}
|
||||
|
||||
sub sub2() {
|
||||
|
||||
repeat 10 {
|
||||
xx++
|
||||
}
|
||||
repeat 10 {
|
||||
xx++
|
||||
}
|
||||
repeat 10 {
|
||||
xx++
|
||||
}
|
||||
repeat 10 {
|
||||
xx++
|
||||
}
|
||||
repeat 5 {
|
||||
repeat 4 {
|
||||
xx++
|
||||
}
|
||||
}
|
||||
txt.print_uw(xx)
|
||||
txt.nl()
|
||||
|
||||
repeat 1000 {
|
||||
xx++
|
||||
}
|
||||
repeat 1000 {
|
||||
xx++
|
||||
}
|
||||
repeat 1000 {
|
||||
xx++
|
||||
}
|
||||
repeat 260 {
|
||||
repeat 4 {
|
||||
xx++
|
||||
}
|
||||
}
|
||||
repeat 260 {
|
||||
repeat 260 {
|
||||
xx++
|
||||
}
|
||||
}
|
||||
txt.print_uw(xx)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
+21
-37
@@ -74,31 +74,14 @@ trader {
|
||||
str input = "??????????"
|
||||
ubyte num_chars
|
||||
|
||||
struct SaveData {
|
||||
ubyte galaxy
|
||||
ubyte planet
|
||||
ubyte cargo0
|
||||
ubyte cargo1
|
||||
ubyte cargo2
|
||||
ubyte cargo3
|
||||
ubyte cargo4
|
||||
ubyte cargo5
|
||||
ubyte cargo6
|
||||
ubyte cargo7
|
||||
ubyte cargo8
|
||||
ubyte cargo9
|
||||
ubyte cargo10
|
||||
ubyte cargo11
|
||||
ubyte cargo12
|
||||
ubyte cargo13
|
||||
ubyte cargo14
|
||||
ubyte cargo15
|
||||
ubyte cargo16
|
||||
uword cash
|
||||
ubyte max_cargo
|
||||
ubyte fuel
|
||||
}
|
||||
SaveData savedata
|
||||
ubyte[23] savedata
|
||||
; format:
|
||||
; 0 ubyte galaxy
|
||||
; 1 ubyte planet
|
||||
; 2-18 ubyte cargo0..cargo16
|
||||
; 19 uword cash
|
||||
; 21 ubyte max_cargo
|
||||
; 22 ubyte fuel
|
||||
|
||||
sub do_load() {
|
||||
txt.print("\nLoading universe...")
|
||||
@@ -111,22 +94,23 @@ trader {
|
||||
return
|
||||
}
|
||||
|
||||
ship.cash = savedata.cash
|
||||
ship.Max_cargo = savedata.max_cargo
|
||||
ship.fuel = savedata.fuel
|
||||
sys.memcopy(&savedata.cargo0, ship.cargohold, len(ship.cargohold))
|
||||
galaxy.travel_to(savedata.galaxy, savedata.planet)
|
||||
ship.cash = mkword(savedata[20], savedata[19])
|
||||
ship.Max_cargo = savedata[21]
|
||||
ship.fuel = savedata[22]
|
||||
sys.memcopy(&savedata + 2, ship.cargohold, len(ship.cargohold))
|
||||
galaxy.travel_to(savedata[0], savedata[1])
|
||||
|
||||
planet.display(false, 0)
|
||||
}
|
||||
|
||||
sub do_save() {
|
||||
savedata.galaxy = galaxy.number
|
||||
savedata.planet = planet.number
|
||||
savedata.cash = ship.cash
|
||||
savedata.max_cargo = ship.Max_cargo
|
||||
savedata.fuel = ship.fuel
|
||||
sys.memcopy(ship.cargohold, &savedata.cargo0, len(ship.cargohold))
|
||||
savedata[0] = galaxy.number
|
||||
savedata[1] = planet.number
|
||||
savedata[19] = lsb(ship.cash)
|
||||
savedata[20] = msb(ship.cash)
|
||||
savedata[21] = ship.Max_cargo
|
||||
savedata[22] = ship.fuel
|
||||
sys.memcopy(ship.cargohold, &savedata + 2, len(ship.cargohold))
|
||||
|
||||
txt.print("\nSaving universe...")
|
||||
diskio.delete(8, Savegame)
|
||||
@@ -508,7 +492,7 @@ galaxy {
|
||||
ubyte current_planet = planet.number
|
||||
ubyte px = planet.x
|
||||
ubyte py = planet.y
|
||||
uword current_name = planet.name
|
||||
uword current_name = &planet.name
|
||||
ubyte pn = 0
|
||||
uword scaling = 8
|
||||
if local
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||
id "org.jetbrains.kotlin.jvm" version "1.5.10"
|
||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class RequestParser : Take {
|
||||
val form = RqFormBase(request)
|
||||
val names = form.names()
|
||||
val a = form.param("a").single()
|
||||
val compilationResult = compileProgram(Path.of(a), true, true, true, "c64", Path.of("."))
|
||||
val compilationResult = compileProgram(Path.of(a), true, true, true, "c64", emptyList<String>(), Path.of("."))
|
||||
return RsJson(Jsonding())
|
||||
}
|
||||
}
|
||||
|
||||
+6
-11
@@ -62,11 +62,14 @@ ZEROPAGE :
|
||||
'@zp'
|
||||
;
|
||||
|
||||
SHARED :
|
||||
'@shared'
|
||||
;
|
||||
|
||||
ARRAYSIG :
|
||||
'[]'
|
||||
;
|
||||
|
||||
|
||||
cpuregister: 'A' | 'X' | 'Y';
|
||||
register: 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' | 'Pc' | 'Pz' | 'Pn' | 'Pv' | 'R0' | 'R1' | 'R2' | 'R3' | 'R4' | 'R5' | 'R6' | 'R7' | 'R8' | 'R9' | 'R10' | 'R11' | 'R12' | 'R13' | 'R14' | 'R15';
|
||||
|
||||
@@ -80,6 +83,7 @@ block_statement:
|
||||
| variabledeclaration
|
||||
| subroutinedeclaration
|
||||
| inlineasm
|
||||
| labeldef
|
||||
;
|
||||
|
||||
|
||||
@@ -108,12 +112,9 @@ statement :
|
||||
|
||||
variabledeclaration :
|
||||
varinitializer
|
||||
| structvarinitializer
|
||||
| vardecl
|
||||
| structvardecl
|
||||
| constdecl
|
||||
| memoryvardecl
|
||||
| structdecl
|
||||
;
|
||||
|
||||
|
||||
@@ -136,20 +137,14 @@ directive :
|
||||
|
||||
directivearg : stringliteral | identifier | integerliteral ;
|
||||
|
||||
vardecl: datatype ZEROPAGE? (arrayindex | ARRAYSIG) ? varname=identifier ;
|
||||
|
||||
structvardecl: structname=identifier varname=identifier ;
|
||||
vardecl: datatype SHARED? ZEROPAGE? (arrayindex | ARRAYSIG) ? varname=identifier ;
|
||||
|
||||
varinitializer : vardecl '=' expression ;
|
||||
|
||||
structvarinitializer : structvardecl '=' expression ;
|
||||
|
||||
constdecl: 'const' varinitializer ;
|
||||
|
||||
memoryvardecl: ADDRESS_OF varinitializer;
|
||||
|
||||
structdecl: 'struct' identifier '{' EOL vardecl ( EOL vardecl)* EOL? '}' EOL;
|
||||
|
||||
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'float' | 'str' ;
|
||||
|
||||
arrayindex: '[' expression ']' ;
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
</options>
|
||||
<keywords keywords="&;->;@;\$;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;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" />
|
||||
<keywords3 keywords="byte;const;float;shared;str;ubyte;uword;void;word;zp" />
|
||||
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;callfar;callrom;ceil;cmp;cos;cos16;cos16u;cos8;cos8u;deg;floor;len;ln;log2;lsb;lsl;lsr;max;memory;min;mkword;msb;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" />
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<NotepadPlus>
|
||||
<UserLang name="prog8" ext="p8 prog8" udlVersion="2.1">
|
||||
<Settings>
|
||||
<Global caseIgnored="no" allowFoldOfComments="yes" foldCompact="yes" forcePureLC="0" decimalSeparator="0" />
|
||||
<Prefix Keywords1="no" Keywords2="yes" Keywords3="no" Keywords4="no" Keywords5="no" Keywords6="no" Keywords7="no" Keywords8="no" />
|
||||
</Settings>
|
||||
<KeywordLists>
|
||||
<Keywords name="Comments">00; 01 02 03 04</Keywords>
|
||||
<Keywords name="Numbers, prefix1">%</Keywords>
|
||||
<Keywords name="Numbers, prefix2">$</Keywords>
|
||||
<Keywords name="Numbers, extras1">A B C D E F a b c d e f</Keywords>
|
||||
<Keywords name="Numbers, extras2"></Keywords>
|
||||
<Keywords name="Numbers, suffix1"></Keywords>
|
||||
<Keywords name="Numbers, suffix2"></Keywords>
|
||||
<Keywords name="Numbers, range"></Keywords>
|
||||
<Keywords name="Operators1">@ & | ^ ~ >> << += -= *= = **= &= |= ^= <<= >>= -> = == != < > <= >= , + ++ - -- * ** / ( ) [ ]</Keywords>
|
||||
<Keywords name="Operators2"></Keywords>
|
||||
<Keywords name="Folders in code1, open">{ {{</Keywords>
|
||||
<Keywords name="Folders in code1, middle"></Keywords>
|
||||
<Keywords name="Folders in code1, close">} }}</Keywords>
|
||||
<Keywords name="Folders in code2, open"></Keywords>
|
||||
<Keywords name="Folders in code2, middle"></Keywords>
|
||||
<Keywords name="Folders in code2, close"></Keywords>
|
||||
<Keywords name="Folders in comment, open"></Keywords>
|
||||
<Keywords name="Folders in comment, middle"></Keywords>
|
||||
<Keywords name="Folders in comment, close"></Keywords>
|
||||
<Keywords name="Keywords1">void const
str
byte ubyte
word uword
float
zp shared</Keywords>
|
||||
<Keywords name="Keywords2">%address
%asm
%asmbinary
%asminclude
%breakpoint
%import
%launcher
%option
%output
%target
%zeropage
%zpreserved</Keywords>
|
||||
<Keywords name="Keywords3">inline sub asmsub romsub
clobbers
asm
if
when else
if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z
for in step do while repeat
break return goto</Keywords>
|
||||
<Keywords name="Keywords4">abs acos all any asin atan avg callfar callrom ceil cmp cos cos16 cos16u cos8 cos8u deg floor len ln log2 lsb lsl lsr max memory min mkword msb 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
</Keywords>
|
||||
<Keywords name="Keywords5">true false
not and or xor
as to downto</Keywords>
|
||||
<Keywords name="Keywords6"></Keywords>
|
||||
<Keywords name="Keywords7"></Keywords>
|
||||
<Keywords name="Keywords8"></Keywords>
|
||||
<Keywords name="Delimiters">00TODO 00FIXME 01 02((EOL)) 02((EOL)) 02((EOL)) 03" 04\ 05" 06 07 08 09' 10\ 11' 12 13 14 15 16 17 18 19 20 21 22 23</Keywords>
|
||||
</KeywordLists>
|
||||
<Styles>
|
||||
<WordsStyle name="DEFAULT" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="COMMENTS" fgColor="0080FF" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="LINE COMMENTS" fgColor="008040" bgColor="C1FFC1" fontName="" fontStyle="2" nesting="1" />
|
||||
<WordsStyle name="NUMBERS" fgColor="800080" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
|
||||
<WordsStyle name="KEYWORDS1" fgColor="FF8000" bgColor="FFFFFF" fontName="" fontStyle="3" nesting="0" />
|
||||
<WordsStyle name="KEYWORDS2" fgColor="8080FF" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="KEYWORDS3" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="KEYWORDS4" fgColor="804040" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="KEYWORDS5" fgColor="400080" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
|
||||
<WordsStyle name="KEYWORDS6" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="KEYWORDS7" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="KEYWORDS8" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="OPERATORS" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="FOLDER IN CODE1" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="FOLDER IN CODE2" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="FOLDER IN COMMENT" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="DELIMITERS1" fgColor="FF0000" bgColor="C1FFC1" fontName="" fontStyle="3" nesting="0" />
|
||||
<WordsStyle name="DELIMITERS2" fgColor="800040" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="DELIMITERS3" fgColor="808080" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="DELIMITERS4" fgColor="800040" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
|
||||
<WordsStyle name="DELIMITERS5" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="DELIMITERS6" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="DELIMITERS7" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
<WordsStyle name="DELIMITERS8" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
|
||||
</Styles>
|
||||
</UserLang>
|
||||
</NotepadPlus>
|
||||
@@ -0,0 +1,14 @@
|
||||
Prog8 syntax highlighting file for [Notepad++](https://notepad-plus-plus.org/).
|
||||
|
||||
Created by meisl on Github. Thanks!
|
||||
|
||||
|
||||
To install:
|
||||
|
||||
* Open Notepad++
|
||||
* Menu `Language` | `User Defined Language` | `Define your language...`
|
||||
* Click `Import...`, then choose the xml file `Prog8.xml`
|
||||
|
||||
Notes:
|
||||
* After an update of Notepad++ you will have to import the xml file again.
|
||||
* Alternatively, you can select `Language` | `User Defined Language` | `Open User Defined Language folder...` and put the xml file there.
|
||||
@@ -1 +0,0 @@
|
||||
TODO
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,54 @@
|
||||
%target cx16
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
; simple line comment; consecutive lines can be folded
|
||||
; TODO: something
|
||||
; FIXME #31
|
||||
main {
|
||||
str input = "string literal\r\n\"\\"
|
||||
ubyte c = 'x' ; character literal in bold
|
||||
ubyte decimal = 0 + 1 - 2 * 3
|
||||
float pi = 3.1415
|
||||
ubyte boolean = true or false and true xor not false
|
||||
str temp = "?"
|
||||
word[] numbers = [$80ea, %0101011, 23]
|
||||
inline asmsub foo(ubyte char @A) clobbers(Y) {
|
||||
asm {{
|
||||
a_label:
|
||||
nop ; comment inside asm
|
||||
bcc _done
|
||||
sec
|
||||
_done: rts
|
||||
}}
|
||||
}
|
||||
inline sub start(ubyte char @A) -> void {
|
||||
ubyte @zp ch = min(max, n, (x, 5))
|
||||
if (true) {
|
||||
goto nirvana
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
repeat {
|
||||
ch = input[index+5]
|
||||
when ch {
|
||||
0 -> {
|
||||
break
|
||||
}
|
||||
else -> {
|
||||
temp[0] = ch
|
||||
txt.print(temp) ; wrongly optimized to
|
||||
; lda #$3f
|
||||
; jsr txt.chrout
|
||||
|
||||
; with -noopt the above line is correctly turned into
|
||||
; ldy #>temp
|
||||
; ldy #<temp
|
||||
; jsr txt.print
|
||||
}
|
||||
}
|
||||
index++
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -43,8 +43,7 @@ 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 match prog8StorageClass "\(^\|\s\)\(@zp\|@shared\)\>"
|
||||
|
||||
syn region prog8Block start="{" end="}" transparent
|
||||
syn region prog8Expression start="(" end=")" transparent
|
||||
|
||||
@@ -16,8 +16,8 @@ syn keyword prog8BuiltInFunc any all len max min reverse sum sort
|
||||
|
||||
" Miscellaneous functions
|
||||
syn keyword prog8BuiltInFunc cmp lsb msb mkword peek peekw poke pokew rnd rndw
|
||||
syn keyword prog8BuiltInFunc rndf fastrnd8 rol rol2 ror ror2 sizeof offsetof
|
||||
syn keyword prog8BuiltInFunc swap memory
|
||||
syn keyword prog8BuiltInFunc rndf rol rol2 ror ror2 sizeof
|
||||
syn keyword prog8BuiltInFunc swap memory callfar callrom
|
||||
|
||||
|
||||
" c64/floats.p8
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
Vim syntax highlighting definitions.
|
||||
Created by Elektron72 on github
|
||||
|
||||
Created by Elektron72 on github. Thanks!
|
||||
|
||||
|
||||
To install:
|
||||
|
||||
Reference in new issue
Block a user