Compare commits

..
82 Commits
Author SHA1 Message Date
Irmen de Jong d4b69ac79c improved repeat counter vars allocation (re-use var if possible) 2021-05-30 15:30:34 +02:00
Irmen de Jong e61a2d7083 slightly optimized repeat loop asmgen 2021-05-30 13:10:05 +02:00
Irmen de Jong c03f6604af added free words counting method to zeropage 2021-05-30 00:55:11 +02:00
Irmen de Jong 572bb38ddb update to kotlin 1.5.10 2021-05-29 15:25:17 +02:00
Irmen de Jong 42c5c0cb9f start of cx16 colorbars example 2021-05-26 22:13:23 +02:00
Irmen de Jong e145d2255e added palette.set_all_black() and set_all_white() 2021-05-26 21:33:18 +02:00
Irmen de Jong 442fa07dd4 relax name conflict rule regarding block names vs subroutine params 2021-05-26 21:32:54 +02:00
Irmen de Jong 31ae9e1243 refactor repeat loop counter var creation into single routine 2021-05-22 13:01:51 +02:00
Irmen de Jong d7f83f8df2 version bump 2021-05-20 23:38:41 +02:00
Irmen de Jong 29e2d4e0c8 give error when passing invalid command line option 2021-05-20 23:34:20 +02:00
Irmen de Jong 2732d2c844 exclude d64 files 2021-05-19 18:49:38 +02:00
Irmen de Jong c4a037b277 added '@shared' to syntax files 2021-05-19 18:48:18 +02:00
Irmen de Jong 0e614ad6fc added @shared flag to vardecl to mark variable as shared with assembly code elsewhere, to not have it optimized away 2021-05-19 01:19:25 +02:00
Irmen de Jong ca1a8cd617 improve doc about string (im)mutability 2021-05-19 00:15:17 +02:00
Irmen de Jong ba96a637be remove strdedup compiler argument again
(string deduplication is the default again but only for known-const strings, i.e. string literals)
2021-05-18 23:52:43 +02:00
Irmen de Jong c2cac772e3 validate string interning 2021-05-18 23:37:52 +02:00
Irmen de Jong 6b7216f4ec todo 2021-05-17 19:00:20 +02:00
Irmen de Jong e4fb5946dd optimize cx16 sys.wait and sys.waitvsync to use WAI instruction 2021-05-17 18:44:42 +02:00
Irmen de Jong ca61248861 printing 2-letter strings is now only optimized into direct CHROUT if it's a const string literal 2021-05-16 15:00:40 +02:00
Irmen de Jong 68d7b4649e label and directive location docs 2021-05-16 12:32:08 +02:00
Irmen de Jong 0416aacbbd fix %asminclude by removing scopelabel argument and improving docs to remove false promise about labels 2021-05-16 00:14:57 +02:00
Irmen de Jong bc731e6f8e fix compiler crash when taking address of label 2021-05-16 00:07:48 +02:00
Irmen de Jong ae5d7705bb allow correct parsing of source files that don't end in a EOL character. Fixes #40 2021-05-14 17:14:44 +02:00
Irmen de Jong b9bd541532 restored optimization of printing short strings into just CHROUT
but added comment about known-constness still to be resolved
2021-05-13 01:46:43 +02:00
Irmen de Jong 83639c2535 code style 2021-05-13 01:00:19 +02:00
Irmen de Jong 25d80f4df1 added compiler option to choose string literal deduplication yes/no -- default changed to NO 2021-05-13 00:35:22 +02:00
Irmen de Jong 74f918d911 fix crashes for string encoding errors: give normal compiler error instead 2021-05-11 21:33:11 +02:00
Irmen de Jong a20efa56eb print unmappable character in escaped form in errormessage 2021-05-11 18:09:09 +02:00
Irmen de Jong f4d83075be Merge pull request #35 from meisl/master
Notepad++ syntax-file: add notes re update / alt installation
2021-05-07 21:44:58 +02:00
Irmen de Jong 254592c383 Merge pull request #36 from meisl/pull36
docs: fix typo
2021-05-07 20:04:00 +02:00
meisl ee23ac0537 * docs: fix typo 2021-05-07 15:28:22 +02:00
meisl a48cf0bb24 + #23 Notepad++ syntax-file: add notes re update / alt installation 2021-05-07 15:12:01 +02:00
Irmen de Jong dae59238cd fix array type checking crash when attempting to use str literal to initialize a byte array.
Fixes #34
2021-05-07 00:04:29 +02:00
Irmen de Jong 8736da1a21 strings of 1 and 2 length no longer optimized into one call to CHROUT - also upgrade to kotlin 1.5.0 2021-05-06 23:46:18 +02:00
Irmen de Jong 09a1de69e7 Merge pull request #33 from meisl/master
+ docs: add missing word
2021-05-06 23:45:44 +02:00
meisl 63d67bc6cb + docs: add missing word 2021-05-06 15:49:58 +02:00
Irmen de Jong 7099245204 Notepad++ syntax file contributor added 2021-05-05 00:39:19 +02:00
Irmen de Jong 4d097d2139 Merge pull request #32 from meisl/master
syntax file for Notepad++

thanks for your contribution!!
2021-05-05 00:33:06 +02:00
meisl 6485bf9ad9 +/- #23 add test file and screenshot; fix: remove if/else as "Folding in code 2", just keywords 2021-05-04 21:55:12 +02:00
meisl b7c5b1bfc7 * #23 rename to .md for nicer link to homepage 2021-05-04 21:34:04 +02:00
meisl 2b7546e827 + #23 syntax file for Notepad++ 2021-05-04 21:30:53 +02:00
Irmen de Jong 3549ccf4b3 software license 2021-05-02 15:31:14 +02:00
Irmen de Jong e2f5752d9a add f_open_w, f_write, f_close_w to diskio to be able to save parts of memory sequentially 2021-05-01 19:13:56 +02:00
Irmen de Jong 1a59019fc8 add generic error in diskio.status() if drive status can't be read 2021-05-01 15:39:39 +02:00
Irmen de Jong 7bac7bdc3e more precise 2021-05-01 13:39:02 +02:00
Irmen de Jong 19fe58dbac fix regression bug that left variables uninitialized 2021-05-01 01:35:03 +02:00
Irmen de Jong 0a5b30e21c added fast code for x*640 2021-04-30 22:30:21 +02:00
Irmen de Jong 664818fd29 try fixing a weird problem with pointervar[idx] -> memread rewriting
this was introduced in the removal of structs somehow
2021-04-30 01:34:03 +02:00
Irmen de Jong d5214e2505 fix import paths 2021-04-30 00:16:36 +02:00
Irmen de Jong d906fcea0e refactor some type checks 2021-04-30 00:09:15 +02:00
Irmen de Jong 29c8e8b740 doc 2021-04-29 19:57:14 +02:00
Irmen de Jong 71fec4c555 added a few more simple special codegen segements for the logic operators on a memmory-read 2021-04-29 19:38:42 +02:00
Irmen de Jong 5ee36c897d todo 2021-04-29 00:57:32 +02:00
Irmen de Jong 4aba0c7405 unused variables are removed more aggressively (no longer checking asm blocks for their names) 2021-04-29 00:48:16 +02:00
Irmen de Jong ed7479c854 version 7 due to removal of structs and v39 cx16 support changes 2021-04-29 00:15:54 +02:00
Irmen de Jong 8d3d5f726a removed Datatype.STRUCT 2021-04-29 00:13:17 +02:00
Irmen de Jong a9a7068818 removed support for structs. It was too much hassle and complexity and subtle bugs. 2021-04-29 00:01:20 +02:00
Irmen de Jong 1bde7c7718 ver 2021-04-28 20:05:56 +02:00
Irmen de Jong 17068130bb removed PROG8_LIBDIR env variables and replaced with -libdirs command line option 2021-04-28 20:04:23 +02:00
Irmen de Jong 81a91d62cb improved horizontal_line in highres 4c 2021-04-28 02:55:49 +02:00
Irmen de Jong 2575263438 optimized gfx2.plot() for hires-4c 2021-04-28 02:49:25 +02:00
Irmen de Jong 7f0e25cb50 optimized gfx2.plot() for hires-monochrome 2021-04-28 02:32:11 +02:00
Irmen de Jong a1e4e9c50f optimized gfx2.plot() for lores-256c 2021-04-28 02:22:21 +02:00
Irmen de Jong 98eff2701b optimized gfx2.plot() for lores-monochrome 2021-04-28 02:15:07 +02:00
Irmen de Jong 8b84f87217 removed fastrnd8() because it was hilariously bad, just use rnd() 2021-04-28 01:53:12 +02:00
Irmen de Jong 306a1b7bc2 optimized gfx2.vertical_line for hires monochrome mode 2021-04-28 01:19:10 +02:00
Irmen de Jong 481214c46e optimized gfx2.vertical_line for lores monochrome mode 2021-04-28 01:02:29 +02:00
Irmen de Jong a5961cbeab optimized gfx2.vertical_line for highres 4c mode 2021-04-28 00:29:21 +02:00
Irmen de Jong 3bf335e0a0 todo 2021-04-27 23:13:46 +02:00
Irmen de Jong 68f696d165 added 'callrom' builtin function (for cx16 target) that calls a routine in banked ROM 2021-04-25 18:04:56 +02:00
Irmen de Jong 1170aed026 added 'callfar' builtin function (for cx16 target) that uses jsrfar to call a routine in banked RAM 2021-04-25 17:47:13 +02:00
Irmen de Jong bf1b2066b6 fix crashes in peekw() and pokew() 2021-04-22 18:26:46 +02:00
Irmen de Jong 4c080afb76 added compiler check against impossible for loop range (unsigned downto exactly 0 with non-const startvalue and step != -1) 2021-04-21 23:03:29 +02:00
Irmen de Jong ee1c43ca91 improved scanning for return statement in routines that should return a value. 2021-04-21 20:31:29 +02:00
Irmen de Jong 1c2e6f9e4c lower() and upper() now also return the lenght of the processed string. 2021-04-21 20:21:58 +02:00
Irmen de Jong dd379430d9 added docs on flexible string character mapping to petscii 2021-04-20 01:22:49 +02:00
Irmen de Jong 42033ebd35 added petscii mappings for ^, _, \, {, } and | 2021-04-19 02:18:55 +02:00
Irmen de Jong a086d6e009 allow labels also in blocks instead of only in subroutines 2021-04-18 23:03:18 +02:00
Irmen de Jong c70bbdab26 fixed missing type checking in vardecl initializer values. Fixes #29
Also fix wrong assert of 0 const check in assembly gen for if-statement comparisons.
2021-04-18 22:46:21 +02:00
Irmen de Jong 3d956ef554 fix wrong values for register used in array indexing expressions
added the L/H byte parts of the cx16 virtual registers
2021-04-18 13:53:02 +02:00
Irmen de Jong 329f491c30 fix compiler crash with scoped const vardecls 2021-04-18 01:56:26 +02:00
Irmen de Jong e93701f50e fix compiler error when initializing var with memory(...) in block scope instead of subroutine 2021-04-17 15:49:41 +02:00
99 changed files with 1911 additions and 11467 deletions

No files matched your search

+1 -1
View File
@@ -30,4 +30,4 @@ parsetab.py
.gradle
/prog8compiler.jar
sd*.img
*.d64
+7
View File
@@ -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
+11 -7
View File
@@ -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 -1
View File
@@ -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'
}
+2 -2
View File
@@ -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"
}
+33
View File
@@ -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
}
+2 -2
View File
@@ -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
View File
@@ -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
+8
View File
@@ -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 {
+54 -19
View File
@@ -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
}}
}
+44 -2
View File
@@ -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)
+43 -56
View File
@@ -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 -----------
+1 -1
View File
@@ -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"
}
-8
View File
@@ -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
+2 -2
View File
@@ -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)
+5 -5
View File
@@ -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
View File
@@ -1 +1 @@
6.5-BETA
7.0-BETA2
+15 -6
View File
@@ -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))
+40 -34
View File
@@ -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()
}
+23 -1
View File
@@ -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")
@@ -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
+2 -3
View File
@@ -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
View File
@@ -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)
-993
View File
@@ -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
}}
}
}
-994
View File
@@ -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
}}
}
}
-994
View File
@@ -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
}}
}
}
-994
View File
@@ -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
}}
}
}
-994
View File
@@ -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
}}
}
}
-994
View File
@@ -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
}}
}
}
-994
View File
@@ -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
}}
}
}
-994
View File
@@ -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
}}
}
}
-993
View File
@@ -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 -1
View File
@@ -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
+4 -20
View File
@@ -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 ")
+33 -42
View File
@@ -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"
+16 -50
View File
@@ -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("")
+4 -4
View File
@@ -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
+1 -10
View File
@@ -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) }
}
}
+21 -24
View File
@@ -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)
+1 -1
View File
@@ -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'
}
+5 -5
View File
@@ -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:
+4 -2
View File
@@ -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
View File
@@ -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
----------------
+18 -34
View File
@@ -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
---------
+4 -1
View File
@@ -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
View File
@@ -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
+5 -5
View File
@@ -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
View File
@@ -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
+127
View File
@@ -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
View File
@@ -42,7 +42,7 @@ main {
for i in 0 to 7 {
c64.SPRPTR[i] = $0a00 / 64
c64.SPXY[i*2] = 50+25*i
c64.SPXY[i*2+1] = 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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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 ']' ;
+2 -2
View File
@@ -13,8 +13,8 @@
</options>
<keywords keywords="&amp;;-&gt;;@;\$;and;as;asmsub;break;clobbers;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%import;%launcher;%option;%output;%target;%zeropage;%zpreserved" />
<keywords3 keywords="byte;const;float;str;struct;ubyte;uword;void;word;zp" />
<keywords4 keywords="abs;acos;all;any;asin;atan;avg;ceil;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" />
+64
View File
@@ -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">@ &amp; | ^ ~ &gt;&gt; &lt;&lt; += -= *= = **= &amp;= |= ^= &lt;&lt;= &gt;&gt;= -&gt; = == != &lt; &gt; &lt;= &gt;= , + ++ - -- * ** / ( ) [ ]</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&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte&#x000D;&#x000A;word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%import&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%target&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved</Keywords>
<Keywords name="Keywords3">inline sub asmsub romsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat&#x000D;&#x000A;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&#x000D;&#x000A;</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;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&quot; 04\ 05&quot; 06 07 08 09&apos; 10\ 11&apos; 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>
+14
View File
@@ -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
View File
@@ -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
}
}
+1 -2
View File
@@ -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
+2 -2
View File
@@ -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
+2 -1
View File
@@ -1,5 +1,6 @@
Vim syntax highlighting definitions.
Created by Elektron72 on github
Created by Elektron72 on github. Thanks!
To install: