egonolsen71 / basicv2 Goto Github PK
View Code? Open in Web Editor NEWA Commodore (CBM) BASIC V2 interpreter/compiler written in Java
Home Page: https://egonolsen71.github.io/basicv2/
License: The Unlicense
A Commodore (CBM) BASIC V2 interpreter/compiler written in Java
Home Page: https://egonolsen71.github.io/basicv2/
License: The Unlicense
There is a problem with Integer optimization and basicv2.jar with date 2022/07/17 and newer.
When compiling this code:
200 n%=9
210 dim d%(9)
220 for i=1 to 9: d%(i)=i: next
300 t1%=1
310 i1%=d%(t1%):d%(t1%)=d%(1):d%(1)=i1%: s2=10*i1%
320 rem further swaps
485 d%(1)=d%(t1%):d%(t1%)=i1%
490 t1%=t1%+2:if t1%<=n% goto 310
There is following error when compiling it with actual basicv2.jar:
...
Total optimizations applied in this pass: 4
Integer pass 1...
!!! Failed to apply integer optimizations: Index 17 out of bounds for length 17
java.lang.IndexOutOfBoundsException: Index 17 out of bounds for length 17
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
at java.base/java.util.Objects.checkIndex(Objects.java:372)
at java.base/java.util.ArrayList.get(ArrayList.java:459)
at com.sixtyfour.cbmnative.mos6502.IntOptimizer$33.modify(IntOptimizer.java:967)
at com.sixtyfour.cbmnative.mos6502.IntPattern.modify(IntPattern.java:23)
at com.sixtyfour.cbmnative.mos6502.IntOptimizer.applyIntOptimizations(IntOptimizer.java:1532)
at com.sixtyfour.cbmnative.mos6502.Optimizer6502.applySpecialRules(Optimizer6502.java:267)
at com.sixtyfour.cbmnative.mos6502.Optimizer6502.optimize(Optimizer6502.java:44)
at com.sixtyfour.cbmnative.NativeCompiler.compile(NativeCompiler.java:190)
at com.sixtyfour.cbmnative.shell.MoSpeedCL.main(MoSpeedCL.java:338)
Total optimizations applied in this pass: 0
...
This is a suggestion for improving the case
IF B=C THEN ...
IF B<>C THEN ...
when B
and C
are real variables or constants (no expression). It's a very specific case, but occurs quite often in BASIC programs, so optimizing it might be lead to speed and size improvement.
The rationale is that real variables can be compared directly without passing from the FAC
.
Currently:
LDA #<VAR_B
LDY #>VAR_B
JSR REALFAC
LDA #<VAR_C
LDY #>VAR_C
JSR CMPFAC
BNE SKIP
; 16 bytes long
Suggested:
ldx #4
loop:
lda VAR_B, x
cmp VAR_C, x
bne skip ; beq skip for IF B<>C THEN ...
dex
bpl loop
equals:
...
skip:
...
; 13 bytes
It's 3 bytes shorter and a lot faster.
The only problem could be the case when B
or C
are zero but with "dirty" mantissa bytes, (e.g. $80
+ 4 random bytes) because of the early exit in the ROM CMPFAC
routine. I have been discussing about this on the Denial Forum and it seems that this case should never occur; when the ROM returns a "0" number it's always $80,0,0,0,0
, so direct byte compare for real numbers is formally correct.
I normally use text files as input for compiler/interpreter.
But I have a problem when I want to use the character for PI (chr$(126) in Commodore Basic) as function (result 3.1415...), e. g. for print or as part of a calculation.
When I try the usual solution as "~", ALT-960 or ALT-227, then there is either a syntax error or zero is returned.
Look here: https://github.com/nietoperz809/OlsenBasic/tree/master/src/main/java/com/sixtyfour/basicshell
Add this package to your code and run the ShellFrame Class.
It works almost like an original C64-Editor :)
This line of code triggers a Java exception:
1 print "{cm-*}"
Running assembler...
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at java.lang.String.charAt(Unknown Source)
at com.sixtyfour.parser.assembly.AssemblyParser.getBinaryData(AssemblyParser.java:249)
at com.sixtyfour.Assembler.compile(Assembler.java:206)
at com.sixtyfour.cbmnative.shell.MoSpeedCL.main(MoSpeedCL.java:201)
cm-asterisk
does work though.
Hi
I have read the doc "MOSPEED native compiler.txt", which only mentions the C64.
I would like to use it for the unexpanded Vic 20 and possibly use it from any directory.
How can I select the Vic20 binary format: unexpanded vs +3k vs +8k/16k/24k/32k ?
When a colon is missing in a program, e. g.
...
400 t9=t9+2 if t9<=n goto 360
...
There is following output when compiling it:
Loading source file...
Checking source file...
!!! Error compiling BASIC program: Syntax error: 2IFT9
With this error message it is hard to find the error in the program, because there is no linenumber.
Also the text editor finds no "2IFT9"
Please add the linenumber to the error message.
I get the following error when trying to compile:
!!! Error compiling: Failed to run optimizer!
*Error at: 10130 data 315,890,0,-1,0,0,-1,0
Caused by: java.lang.IllegalArgumentException: fromIndex(467) > toIndex(335)
Running on latest master.
I adapted the Python program from Prof. Weitz for finding the Superzahl (see https://www.youtube.com/watch?v=kO91PkkkD0o at 13:39) to C64 Basic.
Running with C64 it shows the correct result for n=6 (123654 and 321654), but it is very slow (it tries all permutations and uses the power function). Therefore I compiled it with MOSpeed and started it with C64 Emulation (C64 JavaScript Emulator and CCS64).
But some seconds after finding the first number the emulated C64 crashes with a blank blue screen.
With a little modification the compiled program runs without crash (btw: a additional int() is necessary since there is a problem with the precision of the power function in Commodore Basic):
1310 gosub 1400: if z-int(z/i)*i then return
1410 z=int(d(i-i2+1)*10^(i2-1)+z)
I was surprised about the crash because I compiled many optimized versions of the program and the code was always stable. The bbcompiler causes more trouble.
It would be better if the runtime system (not the basic prg itself) dispense with lower case letters.
They may look very ugly if the environment tries to mimic a true C64 screen. :)
On youtube there is following video from "8-Bit Show And Tell":
https://www.youtube.com/watch?v=VC-lbd8mTOs&t=526s
With compiler and emulator I have no problem with the shown code, but with interpreter the output is other than expected. To see the differences I wrote following code:
0 poke53281,12
1 poke 199,0: rem rvs off
2 rem print chr$(146);
3 print chr$(155)+chr$(169)+chr$(151)+chr$(223)
5 poke 199,1: rem rvs on
6 rem print chr$(18);
7 print chr$(155)+chr$(169)+chr$(151)+chr$(127)
There are two problems:
I know that POKE 199 is not documented here, but it very near to 198 ;-)
And the output of Robin's (better said: Ian's) code is pretty !
currently the output file does include the 2 bytes for the loading address at the start -- which is fine for loading in the emulator but inconvenient if you want to include it as a binary for another assembly source file.
Can be it turned off in some way?
Background:
BASIC 4.0 for PET has a bug, that a program is stopped after a INPUT command without input.
There exists several workarounds (see https://www.youtube.com/watch?v=M1leqcEyYT8). They are not necessary for C64, but they still works and are a good test for basic compatibility.
One possibility is to use INPUT# instead of INPUT (see 4:34 in video).
I modified the program, so that also PRINT# is tested:
10 open 2,0: open 3,3
20 print#3,"? ";: input#2,a$
30 print: print a$
40 print "ende"
50 close 2: close 3
With compiler it works, but BasicShell and console output (run_gui.cmd) have problems with line 20:
Hi! I thought I would compile a bunch of BASIC programs for fun :-) I tried http://www.gb64.com/game.php?id=01291 but it does something weird when drawing the screen, then crashes :-(
Input/output: castle.zip
I opened a new issue#1 for project C64Screen:
When there is a INPUT for a numeric variable, e. g. 100 INPUT A, after pressing return there is always following error although the input was numeric, e. g. 1:
?REDO FROM START
This error is probably caused by BasicShell.
While trying compile some .bas
of mine, I discovered the parser doesn't detect the errors in 160
and 170
below:
160 a=peek(56320) rem // legge joystick in porta 2
170 a=peek(56320) // legge joystick in porta 2
180 print a
P.S. this is truly an amazing project, I'm impressed.
I just switched from an old version of the compiler (2018 circa pre X16) to the latest release and I noticed an increase in size in the generated output. What before was 12794 bytes is now 13650 (with the same command line arguments).
I wonder if it's because a change in compactlevel
default value, or if it's something else.
The statement B=5
is rendered as (opts=true):
LDA #<CONST_0R
LDY #>CONST_0R
JSR REALFAC
LDX #<VAR_B
LDY #>VAR_B
JSR FACMEM
that is: load constant into FAC and then from FAC to var B (if my understanding is right)
I think that could be optimized with the equivalent:
ldx #4
loop:
lda CONST_0R, x
sta VAR_B, x
dex
bpl loop
which is faster and also shorter (11 vs 14 bytes). The only downside would be the use of the X
register.
I'm looking into how an IF
is compiled down to assembly.
I noticed the optimizer rule "Highly simplified loading for CMP/6" uses the integer values #0
and #1
instead of REAL_CONST_ZERO
and REAL_CONST_MINUS_ONE
which is good, but I think the loading into A
is not necessary and it could be further optimized avoiding it, doing the BRANCHES directly.
Example (current):
JSR CMPFAC
; Optimizer rule: Highly simplified loading for CMP/6
BNE NEQ_NEQ9
LDA #0
JMP NEQ_SKIP9
NEQ_NEQ9:
LDA #$1
NEQ_SKIP9:
COMP_SKP9:
BEQ LINE_SKIP10
; Simplified conditional branch
;
LINE_NSKIP10:
;
;
LINE_SKIP10:
Suggested (something like this):
JSR CMPFAC
; Optimizer rule: Highly simplified loading for CMP/6
BNE LINE_SKIP10
LINE_NSKIP10:
;
;
LINE_SKIP10:
I guess it's much more complex than this because all operators (<=
, >=
etc..) should be taken into account, perhaps that can be optimized as an additional step in the optimizer?
Also notice that
LDA #0
JMP NEQ_SKIP9
can be shortened by one byte with
LDA #0
BEQ NEQ_SKIP9 ; Z=0 for sure
The compiler is able to work with PRG files, but console and basicshell have problems:
console: the program is loaded, but there is a syntax error shown when starting it
basicshell: there is a READY message when loading it, but there is no line shown with LIST.
PRG files have two advantages:
Therefore it were good when console and basicshell could load PRG files. Perhaps you can reuse code written for the compiler.
Two important control codes are missing in Console Support, which are used in many basic programs for commodore (PET, VC20, C64, ...):
chr$(14): switch to lower case
chr$(142): switch to upper case
So it make sense to add these control codes to Console Support, although Console Support should not replace an emulator.
Test if statement with following code:
100 input a
110 if a then print "true": goto 100
120 print "false": goto 100
With Commodore Basic or with compiler "true" is printed when a is not 0, e. g. a is 1
With Interpreter the behavior is different: here "true" is only printed when a is -1
I have a strange case where a BASIC program results in almost no speed improvement when compiled with Mospeed. I'm trying to figure out the reason... my suspect it's the floating point ops being the bottleneck, but I am not sure.
It's a 3d-plot of a circular sine function. It's a mixture of BASIC and ML (for plotting on the screen). I wrote it with asmproc, a language tool of my own.
You can run the two side by side in VICE and see no gain:
Also:
Do you have any clue?
Edit: this is what is given to mospeed:
100 poke 53272,peek(53272) or 8
105 poke 53265,peek(53265) or 32
106 sys 6660
110 dim m(192)
120 fort=0to192:m(t)=184:next
130 fory=35to-35step-1
140 foro=0to191
150 x=(o+y*4-96)/7
160 h=sqr(x*x+y*y)
170 ifh<>0thenz=-sin(h)/h
180 v=84+y*2+z*70
190 ifv>m(o)thenv=m(o):goto 200
191 m(o)=v
200 poke 6656,o and 255:poke 6656+1,int(o/256)
210 poke 6658,v
220 sys 6723
230 next
240 next
250 goto 250
Hi,
I would like to have peek/poke listeners. That enables others (like me) to develop plugins that mimic the hardware of real CBM machines.
Hello, and thank you for the work you put into BasicV2 compiler.
This is the X16Tests.java main method and //*
means that these lines do give errors with master
branch. Sadly it is well ahead of my paygrade, but maybe it is just a regression so it can be reverted back:
testVpoke();
testVpeek();
testVpokePeek();
testColors();
testData();
testCharfractal();
//* testFrog();
//* testTi();
testBalls();
testCards();
//* testDos();
//* testMon();
testDivide();
testVload();
testSave();
testLoad();
//* testGeos();
testBitmapfill();
testLine();
testRaytracer();
testLine640();
testFractal256();
testExpresso();
testAffine();
testVloadTest();
testPeek();
//* testRamPeek();
// testBank();
testKoala();
testTeapots();
testInput();
testFastFor();
testScreen();
testPset();
testXLine();
testFrame();
testRect();
testChar();
testMouse();
//* testSignum();
testFractalLand();
testXtris();
testJoy();
testColor();
//* testCls();
//*testReset();
testCircles();
testEllipse();
testHashMap();
Kind regards!
I believe JUnit or similar should be used.
I have a file for the vic 20 that is 8k pre compiled and 21k post.
The code works and I am happy as long as it functions. I just thought it was odd that the compiled code was over doubled in size.
Here is the repository for the code.
https://github.com/rzo42/Vic-20-Mini-Synth
Thank you
The following generates a syntax error with mospeed but not on a real C64:
19080 sm = 50 : se = 0 : dim stable$(sm)
This appears to be due to mospeed seeing the 'TAB' in there, when C64 basic only considers TAB valid in things like PRINT.
I ran into this issue trying to compile the 'Mastercode' assembler. It references this variable as 'ST$' everywhere except the DIM part. So changing the dim line and I can compile further (size issue now), but it does seem to be a syntax difference from real C64 basic.
ready.
tab(1)
?syntax error
ready.
?tab(1)
ready.
10 tab(1)
run
?syntax error in 10
ready.
C64 basic v2 is perfectly happy with this construct. As long as it is used as st$ (or sta$), it is valid on my C64.
ready.
10 dim stable$(50)
run
ready.
Hello,
I'd like to build a multiplatform visual interface for MoSpeed.
As for the compiler there are no problems because can use it from the command line.
But, before compiling (.prg file) it would be useful to run the Basic interpreter to test the source code.
There is a problem though: the VisualRuntime script not loading and running a file from command line.
Is there any workaround?
Thanks in advance.
-j
Background:
BASIC 4.0 for PET has a bug, that a program is stopped after a INPUT command without input.
There exists several workarounds (see https://www.youtube.com/watch?v=M1leqcEyYT8). They are not necessary for C64, but they still works and are a good test for basic compatibility.
One possibility is to use SHIFT+TAB and Controlcodes for Cursor movement (10:02 in video).
There are several problems.
Compiler:
Using a PRG-Files is no problem. But is there a easy way for editing a text file, so that following control codes can be used ?
CTRL-160 SHIFT+SPACE
CTRL-157 CURSOR LEFT
CTRL-145 CURSOR UP
For a text file I used following modification of the program:
1 s$=chr$(160): l$=chr$(157): u$=chr$(145)
10 print "answer:"s$s$s$s$l$l$l$;: input a$
20 if asc(a$)=160 then print u$;: goto 10
30 print a$
Console support (run_gui.cmd):
The controlcodes works, but the evaluation with SHIFT+SPACE doesn't work
BasicShell:
The controlcodes are not working.
The evaluation with SHIFT+SPACE couldn't be tested because of problems with controlcodes, but probably it also doesn't work.
Some prgs written for the C64 delay execution using loops. That's almost ineffective in your BASIC system.
I suggest an argument in the constructor (e.g. boolean slowMode), which slows down the BASIC VM to a level like that of the original C64.
This program is a howling siren:
300 FOR I = 0 TO 24 : POKE 54272 + I, 0 : NEXT
310 POKE 54276, 33 : REM SAWTOOTH WAVE OSC1
320 POKE 54286, 3 : REM CONTROL FREQ. OSC3
330 POKE 54290, 16 : REM TRIANGLE WAVE 0SC3
340 POKE 54296, 175 : REM FULL VOL. & SELECT B AND-PASS & DISC. OSC3 FROM AUDIO
350 POKE 54295, 1 : REM NO RES'NCE & CHOOSE OSCI FOR FILTER
360 POKE 54293, 255 : POKE 54294, 78 : REM CUTOFF FREQUENCY
370 POKE 54278, 240 : REM FULL SUSTAIN/FASTEST RELEASE RATE
375 FOR T = 1 TO 3000000
380 F = 20000 + PEEK (54299) * 20 : REM ADD OSC3 OUTPUT TO BASE FREQUENCY
390 HF = INT (F/256) : LF = F-256 * HF : REM SPLIT NEW FREQUENCY INTO HIGH/LOW BYTES
400 POKE 54272, LF : POKE 54273, HF : REM SET NEWOSCI FREQUENCY
410 NEXT : POKE 54276, 32 : POKE 54296, 0
420 END
In the original code, line 375 counts only up to 300.
Endless loops are unstoppable, e.g. 10 goto 10
Insert at the top of the for loop (Interpreter, line 495) the following code: if (stop) return;
Then it will break if someone calls runStop()
๐
IF cond1 AND cond2 THEN ...
IF cond3 OR cond4 THEN ...
If cond1
is false there is no need to evaluate cond2
, it will be always false. Similarly if cond3
is true no need to evaluate cond4
as it will be always true.
That could provide some speed increase. The only caution is that cond2
and cond4
should not contain call to impure functions (functions that have side effects) but to my knowledge almost all BASIC V2 functions are pure (except RND
and FN
?).
When trying to compile a C64 basic program that loads a binary part using something like
10 IFA>0THENGOTO190
180 A=1:LOADNA$,8,1
I get all kind of problems. My understanding is that using LOAD in a BASIC program for binary parts (located above basic memory) will reset the executing program to its first line (thus the skip mechanism using variable A). In the actual BASIC program this works fine, but in the compiled program it somehow doesn't.
E.g. I get IO errors even though the program was loaded and screen corruption.
Admittedly, this is a borderline case and didn't really work with AustroComp either, but is there any chance to get this running without some dedicated load routine?
Integer constants are created as .WORD
but I guess they can be inlined directly using the 6502 immediate mode (#
), saving the bytes and making it a little faster.
E.g. the statement K%=32
:
; current
LDY CONST_6
LDA CONST_6+1
; Optimizer rule: INT to FAC, FAC to INT/2
STY VAR_K%
STA VAR_K%+1
; suggested
LDY #32
LDA #0
; Optimizer rule: INT to FAC, FAC to INT/2
STY VAR_K%
STA VAR_K%+1
When PRINT with comma is used with BasicShell, e.g "PRINT 1,2" then following is printed:
1I 2
But instead an "I" a tab should be printed:
1 2
Another variant of the basic program for finding the Superzahl of Prof. Weitz (see issue #65):
In issue #65 the performance was improved by replacing real variables by integer.
This I applied also to this program:
But then the compiled program is slower: instead of 0,15s it needs now 0,167s.
With bbcompiler it is as expected with same files: instead 0,15s it needs 0,1s.
If I run it outside the dist directory I get: "Error: Could not find or load main class com.sixtyfour.cbmnative.shell.MoSpeedCL"
Is there a suggested clean way to install the compiler in a way that it can be called from anywhere?
Since Mospeed is written in Java it works fine unter Linux or OSX.
The arg parser is just a little bit too easy.
Around this line here:
Every argument starting with a -
or a /
makes is a parameter, the first argument not starting with one of them ist da source file.
This results in 2 problems:
a Filename cannot start with -
You can't use an absolute File path like tmp/test.bas
Maybe just make the last argument the sourcefile?
Or use just -
as a parameter prefix and make --
stop parameter parsing.
One of those are implemented by most cli tools.
I'm not firm in Java, but at least the first suggestion seems to be easy.
Just pop the last element, set it as source file and don't set a sourcefile in the loop.
PS: there is no way to see which version my mospeed is, maybe also add a /version?
Thanks for your work
Schorsch3000
This compiles without error
10 3 = 1 : PRINT 3
15 3% = 1 : PRINT 3%
Output is 3 and 1 :(
I have a basic program which plots some reversed characters on the screen. The program was written in CBM Prg Studio and plots the characters in two ways:-
and
Both methods work when run natively in basic and when compiled with Blitz of Basic Boss, however when compiled in MOSSpeed method 1 works but method 2 seems to ignore anything printed using chr$(), i.e. the colours and characters.
I am using method 1 for presenting fixed graphics which will never change but need method two to dynamically change what it is displayed according some values in an array. I have confirmed that the values in the array are correct by modifying the statement in 2) to
The expected values are displayed
MoSpeed already accepts labels in place of line numbers, why not extend the syntax making it recognize structured loops? It would be great to write structured BASIC and then compile down to machine language.
Just these would be enough:
if
then
else
do
... loop while condition
/ do
... loop until condition
while condition
... wend
continue
exit for
, exit do
, exit while
They can all be traslated easily into BASIC v2 statements, they are just IFs and GOTOs. The only problem is in the IF-THEN-ELSE because you have to consider the dangling else problem in your grammar.
It would be nice if the compiler, once has parsed the basic source file, would emit an optimized basic V2 source, along with the machine language output.
Optimizations might include:
0
constants with .
Think of it as a minifier, similar to JavaScript uglify
if you know about it.
The implementation of tab for console output is not correct:
There seems to be a simple implemenation which only prints two blanks.
I compile my BASIC program with the following command:
./mospeed.sh speed_test.bas /target=speed_test.prg /platform=x16 -generatesrc=true /addressheader=true
When I then open up the X16 enumerator r32:
./x16emu -echo -debug -run -prg peed_test.prg
The screen just go blank but i don't see my output.
Maybe an error in the generated prg ?
After starting console or basicshell there is the problem, that the GUI can have different screen sizes.
E. g. console can have two diffent sizes
With console it is only a cosmetic problem, but basicshell is often so small that it must be retarted to be able to work with it.
I'm using Ubuntu with WSL and VcSrv X Server.
With C64Screen there is no such problem.
Apologies but I am missing something. Where is the MOSSpeed jar file in the dist directory? I do not see it there.
This is not critical as I appear to be able to work around this by compiling with the "-xfloatopt=false" argument.
I'm testing compiling the 'Mastercode' assembler ( https://github.com/gillham/C64/tree/main/MachineCodeMaster ) with your compiler. This is a large program that uses a lot of memory. It might just be running out of memory, but hard for me to tell right now.
During the execution of the compiled binary I have it load in assembler source code from a seq file on disk. Then i tell it to assemble it. During assembly of a large file it errors out like this:
?illegal quantity error in 0
ready.
My full compile command is:
./mospeed.sh -compactlevel=3 -xfloatopt=false -target=mc.prg mastercode.bas
Do you have any suggestions for debugging this to see if it is just low on memory or some other bug? Is 'print fre(0)' going to give me a useful number for a compiled program?
Again, I seem to be working around it by disabling 'xfloatopt' which might just be freeing up enough memory to make it work. Even if it is just a low memory situation it would be nice to know a good way to tell how much memory is free at different points during execution.
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.