At the Mountains of Madness
Welcome to my (deeply disturbing?) series of articles where we will build a tool chain that will allow us to compile and run Pascal p-code with JavaScript. This introductory article will explain a bit about the history of Pascal in particular the p-code machine that allowed Pascal to take the world by storm in the late 70s. But first...
Why am I doing this madness? Probably because Pascal was my first love (sorry Basic, it was just a phase) and JavaScript is what I do now. Now you must be wondering how decrepit I must be. You can just keep on wondering. Nicklaus Wirth died recently so it's also a tribute to the great man. Hopefully he won't be rolling in his grave. Mostly I am doing this because it will be great fun and I will get to use everything JavaScript has to offer.
The Overall Plan
There are two major pieces to this project the p-code VM and a Pascal compiler. I won't be doing this in a vacuum. I will be basing my work on existing code written in Pascal.
A Bit of History
During the early seventies the folks at ETH Zurich wrote a series of Pascal compilers written in Pascal that generalized the output to run on a stack based VM much like Java and .Net (very forward thinking). They were numbered P1,P2,P3 and P4. These were freely distributed with only a small fee to cover the cost being charged. Some of this source code lives on thanks to Scott A. Franco at Standard Pascal. There is a very comprehensize discussion of this at Pascal-P: The portable Pascal compiler
The p-code VM
How will we accomplish all this? We will use ArrayBuffer, DataView and typed arrays to implement a byte addressable memory space where we can host our machine. Will it be cool? Yes, but never as cool as an entire PC emulated in JavaScript. I intend to document the entire process and that is where this project will hopefully benefit you, the reader. For example, the first thing we will implement is a class to tokenize the intermediate file and we will use generators because generators are cool.
p-code!
The intermediate p-code looks like this
i
!
! Pascal intermediate file Generated by P6 Pascal compiler vs. 0.2
!
o prtlab- lstcod+ chk+ varblk- experror+ list+ prttables- undestag+ chkvar+ prtlex- prtdisplay-
! program locals(input, output);
:1
!
! Program locals
!
b p locals
sfr l locals.6 ! Set function result
l locals.6=0
cup l locals.3 0 ! Call user procedure
ret ! Return code strip
! procedure addlocal;
:2
! var i: integer; (*The variable i is local to the procedure addlocal*)
b r addlocal@p
:3
s i l -24 i
! begin
! i:=i+1;
l locals.7
:4
:4
mst 1 l locals.8 l locals.9 ! Mark(frame) stack
:5
lodi 2 -24 ! Load local value(t)
! writeln(i:5);
ldci 1 ! Load constant(t)
adi ! Add integers
stri 2 -24 ! Store local(t)
:6
lodi 2 -24 ! Load local value(t)
lao 2 ! Load global address
swp 8 ! Swap tos with sos
ldci 5 ! Load constant(t)
csp wri ! Call system procedure/function: Write integer to text file decimal
! end; (*of addlocal*)
csp wln ! Call system procedure/function: Write next line to text file
dmp 8 ! Dump tos
:7
retp 0 ! Return from procedure/function(t)
l locals.8=8
l locals.9=24
! begin
! addlocal
e r
l locals.3
:8
:8
mst 0 l locals.11 l locals.12 ! Mark(frame) stack
! end.
:9
sfr l locals.14 ! Set function result
l locals.14=0
cup l locals.7 0 ! Call user procedure
:10
:10
!
retp 0 ! Return from procedure/function(t)
l locals.11=0
l locals.12=0
g 88
e p
f 0
q
The Pascal that produced this looks like this
program locals(input, output);
procedure addlocal;
var i: integer; (*The variable i is local to the procedure addlocal*)
begin
i:=i+1;
writeln(i:5);
end; (*of addlocal*)
begin
addlocal
end.
The compiler that produced this output is from this github repository
First Code
The code for our tokenizer can be found in the PascalJS Week 1 repository. In the source folder there is file name pint.js. We can run it with node or bun with bun run pint.js -a ../testdata/addlocal.pcode
. Right now all it does it console.log all the tokens in the intermediate file. Later we will actually have our assembler well...assemble. The flow is that we pass the path to the assembler and the assembler creates a PcodeTokenizer
object that the assembler can use to peel off tokens at it's leisure. The tokenizer lets us do things like check for eol
(end of line) and eof
(end of file) and peek to see what the next token is on a line (if there is one) and of course do a getToken()
to get the next token in the stream. Check out the mdn documentation about generators.
In Conclusion
This is the first of many installments in this long running project whose purpose is to create a toolchain for running Pascal programs in JavaScript. For now we will work in Node/Bun but later we will work on getting this doing stuff in the browser. Thanks for joining me on this crazy journey.
*This project is not meant to treat or diagnose any disease. It is meant for entertainment purposes and you should not use it make life choices. Definitely don't use it in medical or aeronautical equipment. Not safe at any speed.
Top comments (1)
Heh, looking forward to this madness Toby! 😁