User Tools

Site Tools


javascript_terminal_v3

This is an old revision of the document!


JavaScript Terminal v3

When I began to write NetWhack, I decided that the game would start out as a terminal emulator, and then the game would rely on the terminal emulator side of the software as if it were a game engine. I liked this 'old school' environment because it was nostalgic for me. It is how I learned to code, way back in the day, so I thought it would be a fun little project to write a interface that would look like a Commodore or a PC or some kind of monochrome terminal.

The main problem I encountered was that modern, event driven systems like Android, Javascript and iOS don't allow you to do blocking string input. Trying to do getkey() or input() is impossible. Eventually I found a way to simulate it within the environment of the terminal simulator by sending the line that the cursor was on to a “command processor”, which would interpret the line like a command. This was close to a shell running on an OS, but there were no clear definitions of what was the OS, what was the shell, what was the hardware. Then I wrote an actual input() function that allowed me to prompt the user and set it to a keyword which could be evaluated by the game engine later:

    input("SET_NAME", "What is your name? ");

When the user pressed ENTER, a console command “SET_NAME <value>” would be sent to the event queue (the game engine) for processing like a console command.

I then began experimenting with a kind of in-terminal scripting, like BASIC. The concept was simple, I would see if the first part of the string sent to the command processor was a number. If it was, it sent it to an array indexed by that number. This became a program listing which I could “RUN” with an interpreter.

This would finally solve the context problem; normally you cnanot store and use the value of the input locally ex. “a = input()”, because JavaScript (as does LibGDX, PyGame, and many others) runs its game engine code on the UI thread. So every time I accepted string input I would have to lose program context, then respond to the SET_NAME event later on. This was simulated string input, but not simulated blocking string input.

Tradeoffs

This is really the best you can do. If you try and expand the scripting aspect you will eventually find yourself rewriting the entire game in the virtual scripting language. It might not be worth it, if you can break up your code just a little. With that in mind I set out to make V3 the best damn terminal simulator it could be, but with a structure that would allow someone to turn it into a state machine very easily, if desired.

Refactoring gameState

gameState was removed and flags were added to control the terminal. As a result, we can run the event queue continuously.

For example, Terminal mode (when the terminal is reset):

  • echo on, cursor on, console mode on,
  • terminal mode on, input mode off, touchmode off,
  • runmode off

Now the question is over the queue. Do we use one monolithic atomic queue or try to use multiple queues?

Monolithic Atomic Queue

If all events are atomic they are effectively on the same queue so a single queue for all events would be used. Like a message queue. This has a lot of benefits, primarily keeping things simple. The big downside is that there is a massive jump table required, but this can be partially solved by using keywords for various processing modes. such as an ISC command which pulls opcodes, an ENTER command which deals with the user pressing ENTER in terminal “shell” mode, a LINE command which causes program lines to be put into the program listing array, and so forth. These could be separate functions in separate files to keep things neat and tidy.

Multiple Queues

Multiple queues won't work. If you need a value set it will be set by the host (in JavaScript). If you need to process an opcode as a subroutine of other opcodes (ex. ADD is a construct of other opcodes and runs on the CPU and not in JavaScript) you can just replace AND by inserting the replacement code directly. If you need to set a value immediately that can be inserted before the next command, too. Multiple queues will be a distraction and are unlikely to speed up emulation in a single threaded environment.

Note that if we are in a multi-threaded environment we can always have the game engine running in an off-UI thread. Or the implementation can launch copies in their own threads vs, manual task and context swithching which you would need to simulate in an (ex. JavaScript) implementation. But it would be transparent to the code written for the virtual machine.

It's really a CPU

A monolithic atomic queue is like a CPU. Since we don't have a ROM, we write high level commands in JavaScript. Slowly, we can implement opcodes and start working more with the state machine.

Jump Table in JavaScript

<Code:JavaScript> Define functions function function1() {

  console.log("Function 1");

}

function function2() {

  console.log("Function 2");

}

function function3() {

  console.log("Function 3");

}

Create a jump table const jumpTable = {

  "func1": function1,
  "func2": function2,
  "func3": function3

};

Example of calling a function dynamically based on a string parameter function callFunctionByName(name) {

  const selectedFunction = jumpTable[name];
  
  if (selectedFunction && typeof selectedFunction === 'function') {
      selectedFunction();
  } else {
      console.error("Function not found");
  }

}

This example is how to support a large number of opcodes/commands quickly.

The Goal of V3

A full featured terminal like V2 but refactored in the direction of a virtual machine, so the user can program that way if he needs.

It's kind of like writing a shell. The terminal mode is the shell. Your actual game can be written in JavaScript and you can run it there, and turn off terminal mode, or you can remain in terminal mode and try to use the scripting functionality there, like extending a game engine.

In most cases, the input method of v2 is going to be good enough. It is good enough to do an interactive fiction game (anything like a shell – and therefore we could write a whole computer system on top of V2). V3 is more like reorganizing things to make the computer system part more obvious and accessible. Why, so maybe we can just remove it and focus more on a barebones terminal. This might ultimately be the way, since gameState's terminal and command modes are now merged and it maybe doesn't make sense to have any kind of os-like commands (like HELP).

Final Thoughts

Today's computers are many millions of times faster than (ex. a C64). Even a C65 running at 3.5mhz can 'software' simulate a C64. The point is that today's processors are in many cases over a million times faster than a C64 or 8086 style processor and are overkill for this. Therefore this should run on anything. Maybe even a 2020s Apple Watch.

Second, the idea of a CPU simuator is very interesting, but we don't have a ROM/BIOS and we don't have an OS and that alone is way too big to worry about for a simple roguelike game. It's interesting, it would be fun, but it is perhaps a little too big for now.

Even an 80486 had five pipelines and ran at 100mhz, and this might have already been 500 to 1000 times faster than a 6502/6510/etc.

More Final Thoughts

I don't think anything similar to this really exists. There are almost ten different C64 emulators written in JavaScript here: https://github.com/fcambus/jsemu and this list includes many more for other systems (more than twenty Nintendo emulators for example). That page also lists many CPU emulators. So while there might be nothing really like this, there are very many similar things and we know the concept is tried and true.

Of particular interest will probably be ZZT.js and Parchment, as well as others – which are game engines written in JavaScript. In it's current state, Terminal V2 (using gameState) is already good enough to serve as a platform for interactive games, since enter-on-a-line is good enough. You could also use V2 as a MUD platform. So why V3? It just feels like the next step in development of the terminal, but we must be careful to step into the rabbithole of writing an entire virtual computer and OS, at least not just yet.

javascript_terminal_v3.1701245699.txt.gz · Last modified: 2023/11/29 08:14 by appledog

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki