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 on 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". We are using a '+' operator, and the two groups we get are fed into the Tokenizer again to see what they are, namely a variable: 'v' (a numerical one at that) 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, [1] => 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

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.