Skip to content

Instantly share code, notes, and snippets.

@lancejpollard
Last active August 30, 2020 16:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lancejpollard/e19e7164e6ee09542aa583c6a0ed387e to your computer and use it in GitHub Desktop.
Save lancejpollard/e19e7164e6ee09542aa583c6a0ed387e to your computer and use it in GitHub Desktop.
//
// THIS IS PSEUDOCODE half JavaScript, a little C.
//
//
// All variables are 32-bit integers for JavaScript.
//
struct fiber {
int32 pointer
int32 waiting
int32 next
int32 stack
}
// global memory store
const store = new Uint32Array(65546)
// global "native JS object" store.
const native_store = []
// all the syscalls, of which there
// are dozens of variations.
// it is an array for fast lookup by index.
const syscalls = [
move1_syscall,
call_syscall
]
// start the vm
main()
// entrypoint function
function main() {
loop()
}
// This is the main looping function, the main while loop.
function loop() {
const threshold_time = 16
const threshold_i = 10000
const start = Date.now()
let met_threshold_time = false
let i = 0
// break this first loop every 16ms
// so it has some room to breath,
// so async events can interrupt.
while (!met_threshold_time) {
// allow 10000 iterations
// before we check the time again
// so if some functionality is very intensive
// it gets interrupted without going too far over 16ms.
while (i < threshold_i) {
i++
step()
}
let end = Date.now()
// check if time difference from last run
// is over the threshold.
met_threshold_time = end - start > threshold_time
}
// wait until next frame.
setImmediate(loop)
}
function step() {
// the start of the fiber linked list
// is at the 0 position in the global store
// by convention.
let fiber = store[0]
// this fiber is theoretically a struct as seen above.
let waiting = store[fiber + 1]
// if the fiber made an async call,
// then it is paused essentially.
if (waiting) {
jump_to_next_fiber(fiber)
} else {
process_fiber_instruction(fiber)
}
}
// move the pointer to the next fiber.
function jump_to_next_fiber(fiber) {
const next = store[fiber + 2]
store[0] = next
}
// this is where the meat is.
// this assumes each low-level "syscall"
// of type `type` takes MAX 3 inputs.
// it calls the syscall, of which there are many types.
// the syscall then updates where
// the current `fiber` points to.
function process_fiber_instruction(fiber) {
const call = store[fiber]
const stack = store[fiber + 3] // the tip of the fiber stack
const type = store[call]
const a = store[call + 1]
const b = store[call + 2]
const c = store[call + 3]
const syscall = syscalls[type]
// the syscall takes a `fiber`
// and a stack, so it can update
// where the current fiber points to.
syscall(fiber, stack, a, b, c)
}
// a = position relative to the stack
// b = value
//
// in reality, there are `move` syscalls
// that take absolute addresses,
// some that take relative address offsets,
// others that just take literal number values,
// and others that contain combinations of these.
//
// only showing one basic example here.
//
// NOTE: one of the `move1` syscalls will be setting
// the `return address` just before the following
// `call` syscall is called.
function move1_syscall(fiber, stack, offset, value) {
// stack + offset == absolute address from
// calculating offset `offset` relative to `stack`.
store[stack + offset] = value
// now for the next loop step,
// make it so fiber points to next instruction
// which is 4 32-bit integers away.
store[fiber] = store[fiber] + 4
// now the next iteration,
// when it grabs the fiber
// it will be taking the next instruction
// in the chain.
}
// this shifts the fiber pointer
// to a new VM function
// (not a native syscall function).
function call_syscall(fiber, stack, a) {
store[fiber] = a
}
function handle_some_async_event(event) {
const current_fiber = store[0]
const next_fiber = store[current_fiber + 2]
// 4 32-bit for the fiber struct, let's say
// returns the index in the store.
const new_fiber = allocate(4)
store[0] = new_fiber
// merge into the linked list.
store[new_fiber + 2] = current_fiber
// TODO: maybe also have a `previous` fiber link,
// so we can properly insert it into the linked list.
//
// but now, in the next main loop iteration,
// it will be pointing to this fiber for evaluation.
//
// TODO: push the native `event` into the stack somehow.
const native_index = native_store.push(event)
// this sorta thing, pushing onto the stack.
store[new_fiber + 3] = native_index
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment