├── .npmignore ├── .gitignore ├── prepublish.js ├── demo ├── banner.txt ├── init.js ├── keymap.js └── screen.js ├── usage.txt ├── package.json ├── README.md ├── scaffolding ├── keymap.js ├── screen.js └── init.js └── runtime-playground.js /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | kernel.bin 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /prepublish.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs') 4 | 5 | fs.statSync('kernel.bin') 6 | -------------------------------------------------------------------------------- /demo/banner.txt: -------------------------------------------------------------------------------- 1 | _____ _ _ _ _____ 2 | | __ \ | | (_) | |/ ____| 3 | | |__) | _ _ __ | |_ _ _ __ ___ ___ | | (___ 4 | | _ / | | | '_ \| __| | '_ ` _ \ / _ \_ | |\___ \ 5 | | | \ \ |_| | | | | |_| | | | | | | __/ |__| |____) | 6 | |_| \_\__,_|_| |_|\__|_|_| |_| |_|\___|\____/|_____/ 7 | -------------------------------------------------------------------------------- /usage.txt: -------------------------------------------------------------------------------- 1 | usage: runtime-playground COMMAND 2 | 3 | Commands: 4 | 5 | doctor check your system 6 | demo run the demo 7 | init create a scaffolding 8 | run run a custom build 9 | 10 | Create a project: 11 | 12 | Create a scaffolding with the `init` command. 13 | 14 | $ runtime-playground init 15 | 16 | Running Custom Builds: 17 | 18 | Create and run your own JS bundle with the `run` command. 19 | Your module and its dependencies will be bunndled with 20 | browserify and executed on load. 21 | 22 | $ runtime-playground run myfile.js 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "runtime-playground", 3 | "version": "0.1.2", 4 | "description": "runtimejs quick start", 5 | "scripts": { 6 | "prepublish": "node prepublish.js" 7 | }, 8 | "bin": { 9 | "runtime-playground": "runtime-playground.js" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "brfs": "^1.2.0", 15 | "browserify": "^5.10.0", 16 | "colors": "^0.6.2", 17 | "minimist": "^1.1.0", 18 | "readline-sync": "^0.4.5", 19 | "shelljs": "^0.3.0", 20 | "tempfile": "^1.0.0" 21 | }, 22 | "devDependencies": {}, 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/groundwater/runtime-playground.git" 26 | }, 27 | "bugs": { 28 | "url": "https://github.com/groundwater/runtime-playground/issues" 29 | }, 30 | "homepage": "https://github.com/groundwater/runtime-playground" 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # runtimejs playground 2 | 3 | > runtime playground lets you write kernel-space javascript, 4 | > powered by npm and browserify 5 | 6 | ## install 7 | 8 | 1. get pre-requisites (on osx) 9 | 10 | ```bash 11 | brew install qemu 12 | ``` 13 | 2. install runtime playground 14 | 15 | ```bash 16 | npm install -g runtime-playground 17 | ``` 18 | 19 | ## usage 20 | 21 | 1. check if your system is ready 22 | 23 | ```bash 24 | runtime-playground doctor 25 | ``` 26 | 27 | 2. run the demo 28 | 29 | ```bash 30 | runtime-playground demo 31 | ``` 32 | 33 | 3. initialize a new project 34 | 35 | ```bash 36 | runtime-playground init 37 | ``` 38 | 39 | this creates a scaffolding in your current directory. 40 | you can edit these files and run them with the `run` command. 41 | 42 | 4. run your project 43 | 44 | ```bash 45 | runtime-playground run init.js 46 | ``` 47 | -------------------------------------------------------------------------------- /demo/init.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var Screen = require('./screen.js') 3 | var map = require('./keymap.js') 4 | 5 | // location and dimensions of VGA frame buffer 6 | var start = 0xB8000 7 | var bytes = 2 8 | var cols = 80 9 | var rows = 25 10 | var size = cols * rows * bytes 11 | 12 | // initialize display based on frame buffer 13 | var display = new Uint16Array(buff(start, size)) 14 | var screen = new Screen(display) 15 | 16 | var banner = fs.readFileSync(__dirname + '/banner.txt', 'utf8') 17 | screen.write(banner) 18 | screen.newline() 19 | 20 | // welcome message 21 | screen.write(' Welcome to Runtime') 22 | prompt() 23 | 24 | // run loop 25 | while(true) { 26 | var num 27 | , key 28 | 29 | if (1 === poll()) // check for keyboard events 30 | if (num = inb(0x60)) // get key pressed 31 | if (key = map(num)) // convert key code to character 32 | if (key === '\n') prompt() // special handle newline 33 | else if (key === '\b') screen.backspace() // special handle backspace 34 | else if (key) screen.write(key) // write key to screen 35 | else screen.write('.') // write unknown characters as '.' 36 | } 37 | 38 | //--- 39 | 40 | function prompt() { 41 | screen.newline() 42 | screen.write(' >') 43 | } 44 | -------------------------------------------------------------------------------- /demo/keymap.js: -------------------------------------------------------------------------------- 1 | // right-shift-down 0x36 2 | // right-shift-up 0xb6 3 | // left-shift-down 0x2a 4 | // left-shift-up 0xaa 5 | // delete-down 0x14 6 | // delete-up 0x142 7 | // up-arrow down=0xe0 up=0x48 8 | 9 | var shift = false 10 | var keymap = [ 11 | '', '', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '', '\t', 12 | 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', '', 'a', 's', 13 | 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', '', '\\', 'z', 'x', 'c', 'v', 14 | 'b', 'n', 'm', ',', '.', '/', '', '', '', ' ', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 15 | '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' 16 | ]; 17 | 18 | var keymapShift = [ 19 | '', '', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '', '\t', 20 | 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', '', 'A', 'S', 21 | 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', '', '|', 'Z', 'X', 'C', 'V', 22 | 'B', 'N', 'M', '<', '>', '?', '', '', '', ' ', '', '', '', '', '', '', '', '', '', '', 23 | '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' 24 | ]; 25 | 26 | function key(code) { 27 | if (code === 0x36 || code === 0x2a) { 28 | // shift down 29 | shift = true 30 | } else if(code === 0xb6 || code === 0xaa) { 31 | // shift up 32 | shift = false 33 | } else if(code === 0xe) { 34 | // backspace 35 | return '\b' 36 | } 37 | 38 | if (code & 0x80) return; 39 | 40 | code &= 0x7F; 41 | 42 | return shift ? keymapShift[code] : keymap[code]; 43 | } 44 | 45 | module.exports = key 46 | -------------------------------------------------------------------------------- /scaffolding/keymap.js: -------------------------------------------------------------------------------- 1 | // right-shift-down 0x36 2 | // right-shift-up 0xb6 3 | // left-shift-down 0x2a 4 | // left-shift-up 0xaa 5 | // delete-down 0x14 6 | // delete-up 0x142 7 | // up-arrow down=0xe0 up=0x48 8 | 9 | var shift = false 10 | var keymap = [ 11 | '', '', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '', '\t', 12 | 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', '', 'a', 's', 13 | 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', '', '\\', 'z', 'x', 'c', 'v', 14 | 'b', 'n', 'm', ',', '.', '/', '', '', '', ' ', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 15 | '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' 16 | ]; 17 | 18 | var keymapShift = [ 19 | '', '', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '', '\t', 20 | 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', '', 'A', 'S', 21 | 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', '', '|', 'Z', 'X', 'C', 'V', 22 | 'B', 'N', 'M', '<', '>', '?', '', '', '', ' ', '', '', '', '', '', '', '', '', '', '', 23 | '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' 24 | ]; 25 | 26 | function key(code) { 27 | if (code === 0x36 || code === 0x2a) { 28 | // shift down 29 | shift = true 30 | } else if(code === 0xb6 || code === 0xaa) { 31 | // shift up 32 | shift = false 33 | } else if(code === 0xe) { 34 | // backspace 35 | return '\b' 36 | } 37 | 38 | if (code & 0x80) return; 39 | 40 | code &= 0x7F; 41 | 42 | return shift ? keymapShift[code] : keymap[code]; 43 | } 44 | 45 | module.exports = key 46 | -------------------------------------------------------------------------------- /demo/screen.js: -------------------------------------------------------------------------------- 1 | function Screen(buffer){ 2 | this.buffer = buffer 3 | this.bytes = 2 4 | this.cols = 80 5 | this.rows = 25 6 | this.color = 0x0A 7 | // row, col 8 | this.cursor = [ 0 , 0 ] 9 | } 10 | 11 | Screen.prototype.clear = function(){ 12 | var b = this.buffer 13 | for(var i=0; i= this.rows - 1) { 48 | this.clear() 49 | this.startChar() 50 | this.cursor[0] = 0 51 | } else { 52 | this.newline() 53 | } 54 | } 55 | 56 | Screen.prototype.startChar = function(){ 57 | this.cursor[1] = 0 58 | } 59 | 60 | Screen.prototype.putChar = function (c) { 61 | var pos = this.linearChar() 62 | this.buffer[pos] = this.color << 8 | c.charCodeAt(0) 63 | } 64 | 65 | Screen.prototype.writeChar = function (c) { 66 | var pos = this.linearChar() 67 | this.buffer[pos] = this.color << 8 | c.charCodeAt(0) 68 | this.nextChar() 69 | } 70 | 71 | Screen.prototype.write = function (line) { 72 | for(var i=0; i= this.rows - 1) { 48 | this.clear() 49 | this.startChar() 50 | this.cursor[0] = 0 51 | } else { 52 | this.returnChar() 53 | } 54 | } 55 | 56 | Screen.prototype.startChar = function(){ 57 | this.cursor[1] = 0 58 | } 59 | 60 | Screen.prototype.putChar = function (c) { 61 | var pos = this.linearChar() 62 | this.buffer[pos] = this.color << 8 | c.charCodeAt(0) 63 | } 64 | 65 | Screen.prototype.writeChar = function (c) { 66 | var pos = this.linearChar() 67 | this.buffer[pos] = this.color << 8 | c.charCodeAt(0) 68 | this.nextChar() 69 | } 70 | 71 | Screen.prototype.write = function (line) { 72 | for(var i=0; i ArrayBuffer 10 | 11 | Allocates a buffer at address `start` of length `len`. 12 | You can allocate buffers anywhere in memory, and write to them. 13 | This _can_ mess up your system; you have access to everything. 14 | 15 | - eval(code: String) -> Value 16 | 17 | Execute code in the current context, returning the result. 18 | 19 | - inb(code: Integer) -> Int 20 | 21 | Issue an `in` assembly command. 22 | To query the keyboard for the last keypress use `inb(0x60)`. 23 | 24 | - poll() -> Int 25 | 26 | Grab any outstanding interrupts, or undefined. 27 | 28 | 3. The VGA frame buffer is at address 0xB8000, 29 | has 80 columns, 25 rows, and is 2 bytes per cell. 30 | 31 | To allocate a TypedArray backed by the vga frame buffer, use: 32 | 33 | ``` 34 | var start = 0xB8000 35 | var bytes = 2 36 | var cols = 80 37 | var rows = 25 38 | var size = cols * rows * bytes 39 | var display = new Uint16Array(buff(start, size)) 40 | ``` 41 | 42 | I hope you have fun! 43 | 44 | */ 45 | 46 | var Screen = require('./screen.js') 47 | var map = require('./keymap.js') 48 | 49 | // location and dimensions of VGA frame buffer 50 | var start = 0xB8000 51 | var bytes = 2 52 | var cols = 80 53 | var rows = 25 54 | var size = cols * rows * bytes 55 | 56 | // initialize display based on frame buffer 57 | var display = new Uint16Array(buff(start, size)) 58 | var screen = new Screen(display) 59 | 60 | // welcome message 61 | screen.write('Welcome to Runtime') 62 | prompt() 63 | 64 | // run loop 65 | while(true) { 66 | var num 67 | , key 68 | 69 | if (1 === poll()) // check for keyboard events 70 | if (num = inb(0x60)) // get key pressed 71 | if (key = map(num)) // convert key code to character 72 | if (key === '\n') prompt() // special handle newline 73 | else if (key === '\b') screen.backspace() // special handle backspace 74 | else if (key) screen.write(key) // write key to screen 75 | else screen.write('.') // write unknown characters as '.' 76 | } 77 | 78 | //--- 79 | 80 | function prompt() { 81 | screen.newline() 82 | screen.write(' >') 83 | } 84 | -------------------------------------------------------------------------------- /runtime-playground.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('shelljs/global') 4 | require('colors') 5 | 6 | config.fatal = true 7 | 8 | var read = require('readline-sync') 9 | var temp = require('tempfile') 10 | var argv = require('minimist')(process.argv.slice(2)) 11 | var cmd = argv._.shift() 12 | 13 | if (cmd === 'doctor') { 14 | if (!which('qemu-system-x86_64')) { 15 | echo('please install qemu'.red) 16 | echo('on osx try: brew install qemu'.grey) 17 | echo('not ok'.red) 18 | } 19 | else { 20 | echo('ready to run'.green) 21 | echo('try: runtime-playground demo'.grey) 22 | exit(0) 23 | } 24 | exit(1) 25 | } 26 | else if (cmd === 'init') { 27 | var dir = argv._.pop() || process.cwd() 28 | ok = read.question('Create scaffolding in ' + dir.grey + '\nY/n > ').trim() 29 | if (ok !== 'n' && ok !== 'N') { 30 | doInit() 31 | } 32 | else { 33 | echo('Abort: Nothing Created'.yellow) 34 | } 35 | } 36 | else if (cmd === 'demo') { 37 | echo('switch to qemu to view your system'.green) 38 | echo('next: generate scaffolding with `runtime-playground init .`'.grey) 39 | doQemu(__dirname + '/demo/init.js') 40 | } 41 | else if (cmd === 'run') { 42 | var file = argv._.shift() 43 | if (!file) { 44 | echo('Please specify init file'.red) 45 | exit(1) 46 | } 47 | else if (!test('-e', file)) { 48 | echo(('File ' + file + ' does not exist').red) 49 | exit(1) 50 | } 51 | else { 52 | doQemu(file) 53 | } 54 | } 55 | else { 56 | echo(cat(__dirname + '/usage.txt').grey) 57 | exit(1) 58 | } 59 | 60 | function doQemu(file) { 61 | var tempfile = temp('.js') 62 | var brfs = __dirname + '/node_modules/brfs' 63 | var browserify = __dirname + '/node_modules/.bin/browserify -t ' + brfs + ' ' + file + ' -o ' + tempfile 64 | 65 | if (exec(browserify).code !== 0) { 66 | echo('browserify error'.red) 67 | exit(1) 68 | } 69 | 70 | var qemu = [ 71 | 'qemu-system-x86_64', 72 | '-m 512', 73 | '-smp 1', 74 | '-s', 75 | '-boot order=d', 76 | '-netdev user,id=mynet0,hostfwd=tcp::5555-:80', 77 | '-device rtl8139,netdev=mynet0,mac=1a:46:0b:ca:bc:7c', 78 | '-kernel ' + __dirname + '/kernel.bin', 79 | '-initrd ' + tempfile, 80 | '-serial stdio', 81 | '-localtime', 82 | '-M pc', 83 | ].join(' ') 84 | 85 | exec(qemu) 86 | } 87 | 88 | function doInit() { 89 | echo('creating scaffolding'.green) 90 | cp('-R', __dirname + '/scaffolding/*', process.cwd()) 91 | } 92 | --------------------------------------------------------------------------------