Tales From The Code Front Stories in words and pictures

10 POKE53281,0

How #Genesis64 parses C64 BASIC (Part 2)

Or: how operators work and mathematical equations are solved

The last post was about translating your input into something that can be run in #Genesis64, this time I'll try to shed some light into the black art of using operators and math.

Let's start with a simple BASIC program:

10 v=53280
20 poke v+1,0
30 print 2+2*2

We can skip line 10 and look directly at the parameter part of the POKE command: v+1.
Anyone can understand it and the trick (as so often) is to make the program to understand it. The term v+1 is pushed into the tokenizer after splitting (see Part 1) and again I use a simple regex to see what it is we want to tokenize:

/^(.*)\+(.*)$/

This translates to: "Take everything until you hit a '+' and group it, then take the rest and group it as well". Ok, this seems to be using a '+' operator, and the two groups we get are fed in the Tokenizer again to see what they are, namely a variable: 'v' and a number '1'. 
So the token for '+' looks like:

When the program is run and the token for '+' is executed, it simply takes its two parameters and adds their Num value and then stores it in its Num value (which is then used by the poke token.

More complex expressions

2 + 2 * 2

Luckily these work exactly like the simple ones, we just have to keep an eye on the order. The Tokenizer will return two tokens: a "multiplication" token and an "addition" token.

(excuse the crude drawing)

#Genesis64 parses expressions in reverse order (so lower-order mathematical operations first), but executes them back in reverse again:

// parsing
2+2*2
-> [0] "+": 2, 2*2
2*2
--> [1] "*": 2, 2

// running
[1]: "*", 2, 2 => 4
[0]: "+" 2, [2] => 6

This way all other operators are parsed, too: and, or (but not: not), <, >, etc.

The next part(s) will be about the more complex commands like if/then, goto/gosub, and for/to/step as they all share the "problem" that they need to jump around the token list.

You can try out your C64 BASIC skills in #Genesis64. You can get a list of all working c64 BASIC commands and functions by typing "help" and hit enter.

Happy coding,
nGFX

10 POKE53280,0

How #Genesis64 parses C64 BASIC (Part 1)

First, let's get a glimpse of what the C64 BASIC (BASIC v2) consists of. There are 76 keywords the parser of a real C64 understands:

  • Operators for logic, arithmetics, and strings
  • Commands
  • Numerical functions
  • String manipulation
  • String expressions
  • Output methods
Operator Commands Numeric functions String manipulation Output
AND CLOSE GOSUB NEXT RUN ABS PEEK CHR$ SPC(
OR CLR GO ON SAVE ASC POS LEFT$ TAB(
NOT CONT GOTO OPEN STOP ATN RND MID$  
+ CMD IF POKE STEP COS SGN RIGHT$  
- DATA INPUT PRINT SYS EXP SIN STR$  
* DEF INPUT# PRINT# THEN FN SQR    
/ DIM LET READ TO FRE TAN    
END LIST REM VERIFY INT USR    
= FOR LOAD RESTORE WAIT LEN VAL    
< GET, GET# NEW RETURN   LOG      
>                

There are also 3 system variables and a constant:

ST (STATUS) TI (TIME) TI$ (TIME$) π (PI)

(Bold means that #Genesis64 understands it)

During the next couple of posts, I'll try to shed some light on how G64 handles these.

10 POKE53280,0

When I first had the idea of doing a C64 parser in javascript it was just this: parse C64 BASIC and I started of by reading through a good deal of articles describing how parsing is done and I read Let’s Build A Simple Interpreter. Part 1. Then I tried what I've learned by writing the first version of G64 and didn't like how it worked.

I had the morinic idea to try something a bit ... different.

In G64 parsing is done in multiple steps and the result is a list of Tokens that are then used to "run" the BASIC program. First, let's have a look at the Token structure:

Doesn't look pretty, but it does the job.

So everything in G64 that deals with BASIC either is a Token or returns one. The most important part of that structure is TokenType ... which is ... the type of the token :).

Let's do a very BASIC example program:

10 poke53280,0

(If you wonder why I didn't go with a simple: 10 PRINT "hello world", it is because PRINT is everything but nice and simple (but that is for another post))

As I mentioned, I decided to go the lazy way and use Regular Expressions as much as possible, so here's the first one:

/(\d+)\s*(.*)/

This returns two matches if the line has a line number, for the line above it returns 10 and pO53280,0.
For this post, I'm going to skip a few steps (I'll explain them in detail when dealing with PRINT and strings), for now, the next step is de-abbreviation and it simply converts pO to POKE.

Now that we have POKE53280,0 we can feed that into the Tokenizer.

/^poke\s*(.*)$/

All commands have this simple Regex attached and the first thing the Tokenizer does is to loop over that list of commands and try to match their Regex (there's more, but let's try to keep it simple).

This gives us a single match: 53280,0. As POKE expects 2 numerical parameters separated by a comma, we try to split that piece of code by the comma (",").
The results are 53280 and 0. Both are fed into the Tokenizer and (as they are numbers) returned as Token, type "number" and added to POKE Token's value list.


This is what the resulting Line (and the Tokens) look like in the debugger.

Easy, isn't it?

Run

Now that we have a Token, running it is quite easy and to make things short, here's the code for running the POKE Token:


I need to rewrite this sometime, it shows that this is one of the first commands I added.

As you can see we pass the Token itself and return a Token, which is either an EOP (EndOfPart) or ERR (as in error). If the returned value is EOP we carry on with the next Token or line, if it is an ERR the program is stopped and the error is printed.
There's also some sort of explanation added to the error Token and spit out to the console, which can be used to find an error a bit quicker.

Next time I'll explain how operators work and how mathematical equations are solved.

You can try out your C64 BASIC skills in #Genesis64. You can get a list of all working c64 BASIC commands and functions by typing "help" and hit enter.

Happy coding,
nGFX

And now: something completely different ...

Let's talk keyboards.

Specifically, about keyboards in #Genesis64.
More specifically, about what a headfuck they are.


My early take on a Genesis64 on-screen keyboard

Looking at the #C64 keyboard (with the image above being an exact copy of the layout), you'll notice a slight difference to modern keyboard layouts, like the C= (Commodore) key (although we are blessed with the Windows / Apple key), missing Alt, Tab, Esc ... you name it.


Basic input is easy ...

Getting input working is pretty easy, sort of.

The keyboard driver is a simple affair, capturing the keydown event on the <canvas/> element, everything is fine as long as you stick to the ASCII range (a-z, numbers).

The first tiny obstacle is that you get a Unicode character string back when reading out the KeyboardEvent.key property.
Obstacle, because #Genesis64 uses the same way to render text to screen the #C64 does, by reading out the screen-ram (stored at $0400 by default, and the color-ram from $D800, but let's ignore that).

Even though we can convert the Unicode to ASCII quite easily, getting this to PETSCII is a different matter. While PETSCII codes match with a huge part of the ASCII range, the screen-codes do NOT.

The #C64 uses PETSCII when dealing with strings, but screen-code when storing things in screen-ram. I honestly have no idea why that distinction was made.
Take the letter "A" for example, which resides at $01 as screen-code, but at $41 in PETSCII-verse (see: http://sta.c64.org/cbm64scrtopetext.html for some insights in the screen-code / PETSCII conversion, see also: https://style64.org/petscii/).

So how does it work in #Genesis64? In the laziest and un-heroic way possible, by using Javascript's .indexOf.

public TXTSCREENCODE: string = "@abcdefghijklmnopqrstuvwxyz[£]^_ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ";

If the KeyboardEvent.key length is 1 I can safely assume that it is a letter or a number and then simply:

let scr: number = this.m_G64Memory.TXTSCREENCODE.indexOf(key);

For everything >= 0, scr is now the screen-code of the pressed letter and the value can be stored directly in screen-ram (at cursor position, of course), should scr be <0 we have to investigate further ...

The fun begins when trying to reach the C= key combinations. While being conveniently shown under the letter on the #C64 keyboard there seems to be no logical connection between the symbol's screen code and the letter. You cannot simply capture the Alt key (as a replacement of the C= key) and add, say 111 to it (PETSCII values, "A" is at 65 and "" is at 176).

Adding to this particular flavor of fun is the fact that there are symbols located on keys that are in a different position on modern keyboards. I made an Excel table to see how to map keys around:

Never one for the easy way, I also use a German keyboard layout with those lovely umlauts and the § sign instead of the pound symbol (£, Shift & "3"). Just from looking at the table I'm getting a headache (as everyone who has looked into the way VICE handles keyboard layouts).

Here are just a few questions I need to find an answer for by myself:

  • Use symbolic or positional layout (ie: "Z"/"Y"), does it make more sense to keep the symbol (C= & key) on that key (hence symbolic C= & "Z" will still result in "", even though the key is in a different place on my keyboard)
  • Keys that have a different shifted representation (Shift & "+" is "*" on my keyboard, but "" on the #C64)
  • Keys not present, like "", "" or "", not to mention Runstop or Restore.
  • "ä", "ö", "ü"?

Right now I'm considering to say "fuck it" and map what can be mapped and resort for everything else to an on-screen keyboard (which is a whole different story).

And on this sober note ...
... have a good one and see you later.

Now playing ...

Right now I'm playing the damn best Fallout in ages - Outer Worlds ( by @Obsidian ). Everything screams Fallout, except it is almost better in almost every way (which isn't surprising as the original Fallout devs and the makers of Fallout: New Vegas made it).

[TLDR] It is bright, colorful and fun.

The space setting allows for more than the 50 shades of brown the usual Fallout comes in and the alien vegetation and monsters make heavy use of that freedom. Buildings and indoor environments have that nice mid-century-sci-fi vibe to it, which I adore a lot.


(Image from https://outerworlds.obsidian.net/en/media)

I'm mildly annoyed by the fact that hibernating doesn't work on Xbox (on the Xbox One X), so every time you fire up the machine and go to the game, it shows your last "screen" for a few milliseconds, then shows a loading screen and throws you back at the start screen from where you can continue (or load).

Luckily load times are short, so it isn't as annoying as with Elite. I usually save the before I turn off, so I'm not sure if you continue your game where you left or your last save (or the auto-save) - I may have to check that.

The UI-font on the other hand...
I'm playing on a 44'' 4k TV, on the couch roughly 4m away from it. I don't know how they test these games for consoles, but I imagine that they simply hook up the gear to a 22'' monitor and play on a desk - so they probably don't notice that the font is too DAMN SMALL! - alas, this is a problem the game shares with a few other games (like The Witcher 3 before they patched in a bigger font).

Most of the text is barely readable without too much squinting, but some of the descriptive texts (on weapons for instance) seem to be written in an 8px font (using an unholy muted color as a bonus).

The 1.1.1.0 patch promises a bigger font for conversations and subtitles, though.


That's just too damn small, worse on inventory/quest/info screens.

I'm not sure if 1.1.1.0 fixes the (minor) issues with inventory/item management, like comparing items. You have to activate it and it is lacking visual clues for "which item I'm comparing", ie: the item you activated compare on and the item you compare to, let alone showing which of these might be currently equipped.

The same goes for shops, so far I haven't discovered a way to compare the shop item to the item I currently have equipped. I've already bought a few items in the belief they might be better than what I have, only to discover that the new item had slightly worse stats.

So, is it worth my time?

I had a slightly sour taste after playing Fallout 4 and plain ignored Fallout 76, but I still was hoping for a new Fallout. I think this is as close as you could ever get to play a new (and good) one for a long time.

Dialogues are fun and witty (even bordering on sarcasm at times), the crew you assemble during the first hours is quite likable (except maybe the vicar, but I still can push him out of the air-lock ...) and the quests (even though nothing special so far) manage to keep me entertained.

Right now it is included in the Xbox Game Pass (not an affiliate link), which makes it a #1 choice to try it.

And on this note ...
... happy gaming, @nGFX

Hello Visitor, stay a while ...

So you found my personal little corner of the web, prepare to be disappointed.

As first posts go, here's what you can expect:

  • A lot of posts about #Genesis64 my WebGL powered, Typescript written stab at bringing C64 glory to the browser
  • A few mentions of C64 games
  • Some code
  • Some rants
  • Some photos (not of me, of course)
  • Some Lego / (non lego) bricks based building posts
  • Maybe some personal views of games I play

I think that's it for now and thanks for stopping by.

ps: if you have a blog that deals with 8bit stuff (C64, Atari, ...) let me know and I'll add you to the blogroll.
pps: if you're missing the inevitable "we're using cookies" banner, look no further, there is no need for it as far as I can see: no google analytics, no facebook pixels or any other tracking service on here, not even ads.