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 environment, 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 really allow you to do blocking input, and you can't really do multithreaded programming. I tried many ways to get around this but eventually I found a way to simulate blocking string input. I hit upon the idea of using the terminal idea to simulate string input by marking where we began input and keeping track of which characters were being pressed when we were in 'input' mode. Then, when the user hit ENTER, the string could be sent to a queue for processing (ex. SET_NAME <value>). In Javascript it looked like this:

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

However, I had never managed to solve the problem of context; I was not able to store and use the value of the input locally ex. “a = input()”. 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.

Best first shot

The best first shot I came up with was having terminal mode and run mode, But this didn't really mean anything to the javascript host. Then I came up with the idea of a GET_KEY command. It would loop until it detected a keystroke, change it to a KEY command, and then it would be processed. This simulated the blocking part, and the string input part, but I still lost context. So I put the result of the function in a variable and modified a flag. But in the end, I still had to start a new context whenever the event was detected, so I still couldn't read the input and use it in-context. I felt it would become a huge problem if I had to break up my code un-naturally.

Yet, without realizing it I had already created a sort of virtual CPU. As I started adding state, and adding commands, it began to feel like an old-time operating system. I played around with letting the user enter programs – if the line began with a number, it would add it to an array, indexed by the line number, and could run a small interpreter.

How it Works

Stuff like keys are always being processed, there's no 'command mode'. However, there is a terminal mode, in which the terminal processes commands when ENTER is pressed. If it doesn't recognize the command it sends it back to the event queue as an ENTER (or CONSOLE) command.

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

FIXME

The Same Queue

If all events are atomic they will eventually be on the same queue. Like a message queue. This has benefits. A running program would be the addition of a 'RUN' command to the atomic queue, or 'ISC' (instruction set cycle). This would pull an instruction, add it to the queue, and it would execute. IO commands on the same queue would be executed in-sequence alongside “CPU” or “PROGRAM” commands. The execution model for a 'RUN' could be that the instruction is pulled from memory, added to the queue, and then another RUN (or ISC) is added after it in sequence, causing a loop but also maintaining the control of the “main” command queue.

The downside is that it will be very difficult to maintain a very large switch statement, a lot of IFs, or a jump table. Hopefully it can be refactored later.

Multiple Queues

Say, a 'system' queue (i/o), an 'os' queue, a 'program' queue, a 'terminal' queue, a 'cpu' queue, and so forth. The problem with this is scheduling, Ideally you would have a priority queue for things like cpu register value changes. Or would you? If you enter a write command on a priority queue followed by a read on a nonpriority queue, its fine. But if you enter a priority read followed by a priority write there could be a problem. Ultimately it's a pipeline problem because we are in a single threaded environment; i.e. we cannot determine (at speed) whether or not one command is necessarily dependant on another. We do know however that if there are 100 commands on the execution stack that a value change must be inserted before the next command. This insinuates a priority queue for things that must be done in direct support of the current command. For example, setting a zero flag in the case where multiple instructions are in the queue. We cannot append it, if a subsequent command relies on the zero flag.

The very idea of a program stored in memory is already a queue. We do not need to modify it while it is actually running. So we would only ever be running one command (ISC) at a time.

The question is then what if we implement an opcode (for example) as a series of 'other' opcodes? In such a case there is a CPU state which must be modified immediately no matter what. Ultimately, the idea of a priority queue is inferior to the idea of a single threaded system supporting a monolithic atomic queue.

However, not all events are equal. A user typing 'HELP' should never reach the queue if we are in some kind of ISC or RUN mode and we did, for example, A = INPUT(). So we can turn off console mode during run and so forth.

This is a mid-way simulation, and it only points in one way.

It's really a CPU

Ultimately, all of this should be done in the instruction set we write for a cpu system. That is really the end goal. Maybe we should start with commands like HELP and just throw them onto the queue and slowly start to add basic instructions and then start writing a ROM and moving everything into the ROM. Yes, there will be a performance hit, but, it is an incremental thing, and there can actually be multiple instruction sets available at the same time. Just because one instruction set is used does not mean the other doesn't work. This would allow us to program in assembly as easily as in BASIC on a C64 – consider the beauty of 10 LDA #56 and mixing basic with assembly. It could work.

Program Simulator

main.js is getting too big and it might make sense to start refactoring things a little. But starting out with a full CPU simulator is too big. To demonstrate the principle of how the new system works, a small BASIC interpreter is most likely what is called for. Also, we will continue to use a monolithic atomic queue for everything, although, as the ROM is written this will begin to be just a CPU simulator. For now, it takes the role of program simulator or program support. The game/program will be written in Javascript, but the system will be controlled by queue commands.

Will it work?

Today's computers are many millions of times faster than a C64, but even a C64 (6502 processor) can run Linux. Even an 8086 can run Minix. Yeah, I never tried it, but the point is that today's processors are over 100,000 times faster than a 486 and that's ignoring more efficient instructions, more efficient instructions per cycle, and multithreading. Even if I just wrote a whole new VM from scratch it would run fast enough for any kind of yesteryear game. I mean, look at modern emulators. You can do dreamcast at 60fps on anything these days.

An 80486 had five pipelines and ran at 100mhz. Today's computers could easily simulate a handful of those. We are far beyond the technical specifications for this kind of virtual system by more than an order of magnitude. A JavaScript implementation of this would be expected to run on anything (even a phone) quite easily. It might even work on an early 2020's Apple Watch.

Existing Projects

I don't think anything like this really exists. Web assembly looks like it was designed to be a failure. It can't fork(), it can't getkey(), and it can't access the DOM. They dropped the ball – if you're going to have a programming language in the browser, then make it a real programming language. All of the artificial restrictions and poor design choices need to be fixed.

javascript_terminal_v3.1701233722.txt.gz · Last modified: 2023/11/29 04:55 by appledog

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki