-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathMOSPEED native compiler.txt
269 lines (171 loc) · 14.7 KB
/
MOSPEED native compiler.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
MOSpeed is an optimizing BASIC cross compiler. Being a cross compiler, it runs on a PC but generates
6502-Machine language for the Commodore 64.
Description
MOSpeed is controlled on a PC via the command line/shell. It expects a BASIC V2 program in text file format,
just like C64 Studio or CBM prg Studio are using it. It supports the usual placeholders like {clear}.
In addition, it can operate directly on prg files and converts them to text format before compiling them.
In this case, the file to compile has to end on .prg or the compiler won't recognize it as a prg file.
MOSpeed is written in Java. It requires a Java runtime environment version 11 or higher.
MOSpeed can be configured via command line parameters. It doesn't make use of compiler directives directly in
the source code like some other compilers for the C64 do!
The performance on average is between 10 and 20 % higher than the performance of a BASIC-BOSS compiled
program without any special variable declarations, but can be much higher in some special cases as well.
On average, MOSpeed increases the performance of BASIC V2 programs by a factor of 3 to 5.
Loading, startup, controls
MOSpeed is a command line application for the PC. Because MOSPeed is written in Java, the compiler is distributed
as a JAR-file (@ https://github.com/EgonOlsen71/basicv2/tree/master/dist ). This distribution contains two script files,
one for Windows (mospeed.cmd) and one for Linux (mospeed.sh), which can be used to start the compiler.
The compiler needs at least the name/path of the source file to run or /? to display the integrated help text.
Started with just the source file name, the compiler will create (assuming that the compile process went fine) a prg-file
for the C64 named like the source file but with two plus signs as prefix. For example: example.bas becomes ++example.prg
The source code can use line numbers (as usual) but also labels, which the interpreter doesn't support.
MOSpeed translates these label to line numbers at compile time.
The compilation process can be configured by using various command line parameters.
Command line parameters
These command line parameter can be appended directly to the call to start the compiler. Order doesn't matter.
All parameters start with a /, alternatively a -sign can be used.
In the following, true|false means that the feature can be enabled (true) or disables (false).
- /target=<target file>: Sets the name of the target file. If nothing is given, the name will be generated from the source
file's name (as described above). It's possible to use a complete path name.
- /platform=xxxx: the target platform. Options are c64 (for c64 compatible machine code), vic20 or vc20, x16 for x16-compatible machine language, js (for Javascript), ps (for Powershell/-script) and py (for Python), default is c64
- /generatesrc=true|false: If true, the generated source codes for the assembly like intermediate language as well as for the
6502-Assembler|Assembly will be written to disk as well. Default is false.
- /constfolding=true|false: If true, constants will be detected and replaced by their actual values.
Examples are the often used addresses for VIC or SID. Default is true.
- /constprop=true|false: If true, constant propagation will be used. I.e. constants won't only just detected directly,
but also if calculations result in constant values. Default is true.
- /pcodeopt=true|false: If true, inlining and other high level optimizations will be performed. Default is false.
- /ilangopt=true|false: If true, the generated intermediate code will be optimized. This increases the speed and usually
reduces the size. Default is true.
- /nlangopt=true|false: If true, the generated 6502-Assembly code will be optimized. This increases the speed and usually
reduces the size. This optimization can take some time, depending on the size of the program. Default is true.
- /smartlinker=true|false: If true, only used parts of the runtime will be linked to the actual program. If false,
the complete runtime will be linked. Default is true.
- /deadstoreopt=true|false: If true, dead, i.e. unused variable assigments will be detected and removed. In most cases,
this is just fine but in some cases (for example when calling but not using the result of RND) it can cause the results
of the compiled program to differ from the interpreted ones. This parameter affects integer and floating point variables only.
Default is true.
- /deadstoreoptstr=true|false: Just like /deadstoreopt, but for string variables. Default is true.
- /deadcodeopt=true|false - enables/disables dead code elimination. Default is false.
- /loopopt=true|false: Detects and removes empty loops. These loops don't have to be empty in the source code,
but if the values that are calculated inside them aren't actually used, they might get removed anyway. Default is true.
- /addressheader=true|false - enables/disables the writing of the two address header bytes. Settings this to false will
prevent the BASIC header from being written as well. Default is true.
- /floatopt=true|false: If true, various optimizations for floating point calculations will be used. In some case,
caused by inaccuracies in the calculations, this can lead to small differences but in most cases, this shouldn't be a
problem if it's even noticable. Default is false for compatibility reasons.
- /xfloatopt=true|false: enables/disables additional floating point optimizations. While they speed up some operations by up to 25%,
they need more memory. Default is true.
- /intopt=true|false: If true, certain integer operations will be optimized. In general MOSpeed treats integer variables
like the interpreter does for compatibility reasons. By using this parameter, this restriction can me loosened somewhat.
Default is true.
- /compactlevel=[0|3...]: If >= 3, the generated code will be optimized for size. This has an impact on performance,
but not in the same proportion as the size reduction. The higher the value, the less of a performance impact there will be,
but the size reduction might be smaller as well. 4 or 5 are usually a good compromise, which often leads to a size
reduction of 10 to 15 % with a performance impact of 0.5 to 1%. Default is 0, which disables this optimization.
- /progstart=xxxxx|$yyyy: Sets the start address of the compiled program. This value can be either decimal or hexadecimal.
If it's < 2100, a BASIC header to run the program will be added automatically, otherwise it won't. Default is 2072.
- /varstart=xxxxx|$yyyy: Sets the start address of the variable/constants memory. This value can be either decimal or hexadecimal.
If nothing is given, this memory will follow the memory used for the runtime. This is also the default.
- /varend=xxxxx|$yyyy: Sets the end of the variable memory. This value can be either decimal or hexadecimal.
Because the actual variable memory doesn't change at runtime, this actually sets the end of the string's heap memory.
Make sure that the memory is readable, i.e. that it isn't shadowed by the ROM. Default is 40960.
- /runtimestart=xxxxx|$yyyy: Sets the start address of the runtime code. This value can be either decimal or hexadecimal.
If nothing is given, it will follow the actual program's memory. This is also the default.
- /sysbuffer=xxxxx|$yyyy: Sets the start address of the buffer used to execute SYS commands with parameters.
Default is 820 for the CBM machines and 1024 for the X16. If set to 0, the runtime will reserve its own buffer in BASIC memory.
- /alloff=true|false: If true, all optimizations which are enabled by default will be turned off. Default is false.
- /vice=<path>: This sets the path to an instance of the VICE|VICE-Emulator (or some other with similar behaviour).
After a successful compilation, the compiled program will be started in that emulator. By default this doesn't happen.
- /memconfig=0|3|8 - the memory configuration in case of VIC20 as the target machine. 0 means 'unexpanded', 3 means with a 3k expansion,
8 means with an 8k or larger expansion. Default is 8.
- /symboltable=<file>: This only applies to the X16 target platform. It lets you specify a different symbol table to compile for
different ROM releases. The default symbol table should match the latest ROM release.
- /nondecimals=true|false: if true, hexadecimal and binary numbers can be indicated by & and %. Default is false, except for the X16
platform, where it's enabled by default.
- /tolower=true|false: if true, all strings in the source code will be treated as lower case. This can be useful when compiling BASIC
code copied directly from an emulator where all strings are given in upper case. Default is false.
- /flipcase=true|false: if true, the casing of strings in the source code will be reversed. This can be useful when compiling BASIC
code copied directly from an emulator. Default is false.
- /multipart=true|false: if false (default) the target file contains all binary data regardless of the address in memory. If true,
several files will be written if the addresses of the program's parts aren't adjacent.
- /memhole=<start1-end1>,<start2-end2>,...: Defines holes/locked regions in memory. The compiled program won't use these memory locations
for compiled code and variables. If a hole is located after the end of the compiled program, it will be ignored. Default is none.
- /boost=true|false: if true, a compiled C64 program will use the C128's 2 Mhz mode to increase performance up to 25%. This only works on
the C128 in C64 mode, it has no effect when run on a real C64. It might also not be compatible with all programs. Default is false.
- /bigram=true|false: - If true, the RAM under the C64's BASIC ROM as well as the higher 4K of RAM will be used for the
compiled program as well. This will reduce performance, especially when accessing memory under the ROM. Default is false.
- /compression=true|false: - If true, the compiled program will be compressed to achieve a smaller file size.
The compressed file will be saved in addition to the normal binary. Compression isn't always possible.
In that case, no compressed file will be written.
- /deploy=<ip/domain> - If set to an ip or domain on which an Ultimate64 is listening, the program will be transfered and started on that device. Default is empty.
- /inlineasm=true|false: - If true, inline assembly code can be used, marked by REM [...;...;...]. Default is false.
- /printopt=true|false - *Experimental* - If true, the compiler tries to rearrange texts in PRINT statements to save memory at the expense of speed. Default is false.
- /arrayopt=true|false - *Experimental* - If true, the compiler tries to optimize access speed of multi-dimensional arrays at the expense of memory usage. Default is false.
- /assignmentopt=true|false - *Experimental* - If true, the compiler tries to optimize assignments. Default is false.
- /varopt=true|false - *Experimental* - If true, the compiler tries to move integer variables into the zeropage if possible and applicable. Default is false.
Changes compared to BASIC 2.0
MOSpeed tries to stay compatible with the BASIC V2 interpreter when possible. However, there are some deviations.
- LIST: Will be ignored.
- RUN: Any line number after RUN will be ignored.
- NEW, STOP: Will be treated like END.
- DIM with variables: While this actually isn't supported, to might work to a degree if MOSpeed can detect
these variables as constants.
- Limited support for USR: This function is supported, but only a single floating point value is allowed as input.
Strings or multiple parameters are not allowed.
- SYS with parameters: This option is supported, but because it's actually an interpreter hack in the first place, the call itself
might be slower in the compiled code.
- INPUT with double quotes: When using INPUT MOSpeed is a little more forgiving with double quotes.
- Unified rounding: The interpreter chooses different approaches to rounding in different contexts.
The rounding differs inside of functions, for example. An example: A=145/3*3 assigns 145 to A, but PEEK(145/3*3) reads from 144,
not 145. MOSpeed unifies this behaviour, so that in this example, the outcome will always be 145 (just like in the
BASIC 7.0 of the C128).
- floating point accuracy: The results of floating point operations may show slight differences between the interpreter
and MOSpeed, depending on the applied optimizations. In almost all cases, this shouldn't be a problem at all. In addition,
MOSpeed tries to workaround a bug in the interpreter ROM, so that the results might turn out to be more precise.
- IF with Strings and without a comparison operator: BASIC V2 will print out OK for A$="":B$="YEAH":IFA$THEN?"OK", even though A$ is empty.
The reason is that it's not using the variable inside the IF (A$ in this case), but the last assigned one, in this case B$.
MOSpeed always tests against the variables given after the IF.
Runtime errors
Runtime errors in the program (for example divisions by zero so similar) will be catched and printed out in most cases,
but the Line number will always be 0.
Inline assembly support
MOSpeed supports inline assembly code, but it's disabled for compatibility reasons by default. You can enable it by setting /inlineasm=true.
When enabled, everything behind a REM in square brackets will be processed by the built-in assembler.
Multiple commands/labels//assignments can be written in one line separated by a semicolon. For example:
10 print "hello asm"
20 rem [ ldx #$ff ]
30 rem [ loopy; inc 53280 ]
40 rem [ dex; bne loopy ]
50 goto 10
This will print "hello asm" in an endless loop but will also make the border flash by using machine language code. You can define labels
as well as constants in the assembly code. So something like this will work as well:
10 print "hello asm"
20 rem [ numby=$ff; ldx #numby ]
30 rem [ loopy; inc 53280 ]
40 rem [ dex; bne loopy ]
50 goto 10
You can access BASIC variables and lines by adding ! as a postfix, like so:
10 print "hello asm": i%=255
20 rem [ ldx i%! ]
30 rem [ inc 53280 ]
40 rem [ dex; bne 30! ]
50 goto 10
You can also do something like this:
10 print "hello asm": i%=255
20 rem [ ldx numby ]
30 rem [ inc 53280 ]
40 rem [ dex; bne 30!; jmp 50! ]
45 rem [ numby; .byte 255 ]
50 goto 10
So, basically, you can do everything that you can do in the actual assembler that comes with this project. That includes screwing up the
compiled program and/or the runtime by using this option if you don't know, what you are doing.
Even evil hacks like this are possible:
10 print "hello asm"
20 rem [ldx #$ff]
30 rem [jmp 40!; *=$2000;]
40 rem [inc 53280]
50 rem [dex; bne 40!]
60 goto 10
With this, you are splitting compiled code into one part starting at $0801 (by default) and everything after line 30 continuing at $2000.
While this works, I suggest not to do things like this. Use the memhole-option instead...