alan-if / alan

ALAN IF compilers and interpreters

Home Page:https://alanif.se

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Alan v2 interpreter license

cspiegel opened this issue · comments

While Alan v2 is obsolete, Gargoyle still includes an interpreter for older games that were created using it, thus my interest here.

The Alan v2 interpreter (in the v2_x-updates branch) doesn't work on modern 64-bit systems, as it assumes pointers are 32 bits. Gargoyle made some changes a while back to fix issues with pointers on the stack, but there are still problems: the clock.alan regression test, for example, segfaults under Gargoyle due to another pointer issue.

I was working on making it 64-bit clean, planning on getting some minimal fixes into the v2 branch here, and then into Gargoyle. But I got kind of carried away and rewrote large chunks of the interpreter, some necessary for the 64-bit fixes, which were a bit difficult to get done, some not necessary for the fixes.

Anyway, since I know you're not maintaining v2 given that v3's been out forever, I plan on maintaining this version myself, but I see that the v2 source contains no license (neither in the Git branch nor the interpreter287src.tgz archive), at least as far as I can tell.

Can you clarify what license the v2 source code is under, please? I don't want to distribute something I have no right to.

Thanks for you interest in Alan!

I will add a license file in the v2 branch to clarify that it is actually the same license as the later versions.

If you are interested in joining the very small Alan team I would be happy to have you maintain the v2 branch from within the AlanIF organisation. That way we could keep a unified branch and perhaps also have v3 benefit from some of your cleanups. Just a thought.

I'd be happy to do maintenance under the AlanIF organization. However, when I said I rewrote large chunks, that's probably an understatement at this point. A lot of the core logic is still the same (I mean, it has to be, obviously), but it's really a very different beast at this point. I'll list some of the major things I did, so you can get a sense of the state of my version of the interpreter, and how you'd feel about it being at least a semi-official part of Alan.

  • Whereas the original code loads the ACD file into memory and directly manipulates it (treating it as an array of words and directly indexing into it), the new code loads various tables into data structures up front. The main impetus for this was to avoid the need to know the endianness of the target machine, since words are now read byte-by-byte and stored in the tables.
  • I converted to C++ from C: this was driven mainly by the fact that C++'s STL provides data structures that I could use for the above-mentioned tables, though I do make a lot more use of C++ than just for the tables. At the same time, I changed up the formatting of the code to match my personal style, given that I'd already hacked up so much of it.
  • I removed platform-specific code. IIRC the platform-specific stuff was tied to character set encoding, so I guess if DOS is used with non-ASCII characters, they may look wrong... but this was also driven by the fact that I wasn't sure how exactly character sets work with Alan, and I wanted to streamline the code. That being said, I'd absolutely be fine with resurrecting character set stuff if that's desired.
  • I removed the line input editing (e.g. using arrow keys to go through history), in part because I was having a couple issues with it, and in (larger) part because I was more focused on Glk than anything else. As with character set encoding, I have no issues bringing it back (I tried using readline and editline as replacements, but they both require to you provide the prompt, and while > is usually the prompt, from what I can tell, the game can provide its own prompts in some circumstances, which would be very difficult to pull out and pass to readline). I do fully support non-Glk, though, just not with line editing.
  • I removed the ability for games to call outside commands (i.e. the SYSTEM instruction). This is one I'd be fine with adding back, but only behind a conditional macro: I can't distribute an interpreter with Gargoyle that allows games to run arbitrary commands on the user's machine, but I'm fine giving whoever is building the interpreter the choice to allow it. I didn't actually remove the instruction itself, but made it a no-op.
  • I added a new save file format (while still supporting reading of old saves). This was because the original save format is platform-dependent: it writes out Awords directly with fwrite(), meaning it's tied to the existing machine architecture (realistically it means that saves from a big endian machine won't work on a little endian machine, and vice versa). To be a bit more friendly to sharing save files, I also removed the check of the file path (as the original code aborts the restore if the file paths don't match), but instead use the CRC to do a quick verification that the original game file is probably the same.
  • I removed the header check for debugging so I could debug anything I wanted to, making development easier. I have zero problems putting it back, so this is more a reminder than anything else.
  • The code includes two third-party header-only libraries: variant-lite and optional-lite. These are backports of features from C++17. I've got the code targeting C++14 now because Gargoyle needs to build on older Macs, which don't support C++17. This is kind of a double-edged sword, I guess. I'd like to use C++17, and may switch Gargoyle over, since Apple isn't supporting the older macOS releases which require C++14, but you very probably don't want to tie the interpreter to standards that are too new. So it's C++14 and a couple large-ish headers, or C++17 and less support for older systems.
  • I did not touch the compiler at all. This is solely work on the interpreter.

There are likely other relatively large things I've forgotten (my Git history is too scattered to easily scan for the major changes).

I'll also quickly explain the 64-bit problems that I was original addressing:

  • The stack is an array of words, but the getstr() function (in exe.c) pushes a string onto the stack, and does so by pushing the address of an allocation. Since the stack is an array of (32-bit) words, the conversion of a 64-bit pointer is clearly not going to work. I fixed this by having the stack store variants (i.e. tagged unions) of Aword and C++ strings. This has the advantage, too, of runtime type checking when the stack is popped: if the wrong type is requested, the program will abort.
  • A more difficult problem: In main.c, the initstrings() function pulls out string values and stores them at specific addresses in memory (which will be read through attributes later on). These are fixed locations in memory where the strings are stored. The code does this: memory[init->adr] = pop(); where pop() returns a string. As with the first point, this string doesn't fit, but the difficult part is that when you have an attribute later on, it "knows" this fixed location in memory, and tells you to look up a string there. It's simply impossible to fit a 64-bit pointer there. Making memory an array of 64-bit words is just kicking the can down the road. This I fixed by storing a parallel mapping of ACode addresses to strings (so, outside of Alan's "main memory"), and when strings are looked up via attributes, the associated attribute table remembers its original ACode address, and uses that to find the address in the parallel mapping. I don't know if that makes any sense written out, but long story short, it works.

My original goal was to ensure the interpreter works fine in Gargoyle, and as such, I was only really concerned with supporting systems on which Gargoyle runs (which is to say, really, anything with a C++14 compiler). Once I started doing a lot of the renovations, I wasn't concerned with older/more obscure target systems. So that may not really be in line with what you're looking for, but on the other hand, I didn't go out of my way to make it unportable. Anyway, if you'd like, I can at least toss up a repository somewhere with these changes, if you don't mind it being public, or if you'd prefer, I can privately send you a copy of the latest version I've got, so you can peruse it. I'm happy to work with you to ensure it has everything you need, but I completely understand if this is just a bridge too far, after all the changes I've made.