diff --git a/js/core/graphics/graphics-renderer.js b/js/core/graphics/graphics-renderer.js new file mode 100644 index 000000000..83feb24b7 --- /dev/null +++ b/js/core/graphics/graphics-renderer.js @@ -0,0 +1,43 @@ +// Copyright 2016-present runtime.js project authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const nameSymbol = Symbol('name'); + +class GraphicsRenderer { + constructor(name = '') { + this[nameSymbol] = name; + this.ongetbuffer = null; + this.onenablegraphics = null; + this.constants = {}; + } + get name() { + return this[nameSymbol]; + } + get displayBuffer() { + if (!this.ongetbuffer) { + throw new Error('renderer not initialized'); + } + return this.ongetbuffer(); + } + enableGraphics(width, height, bitDepth) { + if (!this.onenablegraphics) { + throw new Error('renderer not initialized'); + } + this.onenablegraphics(width, height, bitDepth); + } +} + +module.exports = GraphicsRenderer; diff --git a/js/core/graphics/index.js b/js/core/graphics/index.js new file mode 100644 index 000000000..6a9744625 --- /dev/null +++ b/js/core/graphics/index.js @@ -0,0 +1,29 @@ +'use strict'; + +const GraphicsRenderer = require('./graphics-renderer'); +const { Screen, symbols: screenSymbols } = require('./screen'); +const renderers = require('./renderers'); +const screen = new Screen(); + +module.exports = { + graphicsAvailable() { + return renderers.renderersAvailable(); + }, + enableGraphics(width, height, bitDepth) { + const renderer = renderers.getDefaultRenderer(); + renderer.enableGraphics(width, height, bitDepth); + screen[screenSymbols.reset](); + screen[screenSymbols.init](width, height, bitDepth, renderer); + }, + GraphicsRenderer, + get screen() { + return screen; + }, + get displayBuffer() { + return renderers.getDefaultRenderer().displayBuffer; + }, + addRenderer: renderers.addRenderer, + get constants() { + return renderers.getConstants(); + }, +}; diff --git a/js/core/graphics/renderers.js b/js/core/graphics/renderers.js new file mode 100644 index 000000000..8841dfb06 --- /dev/null +++ b/js/core/graphics/renderers.js @@ -0,0 +1,41 @@ +// Copyright 2016-present runtime.js project authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +let defaultRenderer = null; +const availableRenderers = new Map(); + +module.exports = { + addRenderer(renderer) { + availableRenderers.set(renderer.name, renderer); + + console.log(`[graphics] using renderer ${renderer.name}`); + + defaultRenderer = renderer; + }, + renderersAvailable() { + return availableRenderers.size !== 0; + }, + getDefaultRenderer() { + return defaultRenderer; + }, + getConstants() { + const consts = {}; + for (const renderer of availableRenderers.values()) { + Object.assign(consts, renderer.constants); + } + return consts; + }, +}; diff --git a/js/core/graphics/screen.js b/js/core/graphics/screen.js new file mode 100644 index 000000000..8f767a8ba --- /dev/null +++ b/js/core/graphics/screen.js @@ -0,0 +1,64 @@ +// Copyright 2016-present runtime.js project authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// The properties `width`, `height`, and `bitDepth` are +// Symbols because they shouldn't be set. +const symbols = { + width: Symbol('width'), + height: Symbol('height'), + bitDepth: Symbol('bitDepth'), + init: Symbol('init'), + reset: Symbol('reset'), + initialized: Symbol('initialized'), + renderer: Symbol('renderer'), +}; + +class Screen { + constructor() { + this[symbols.reset](); + } + [symbols.init](width, height, bitDepth, renderer) { + this[symbols.width] = width; + this[symbols.height] = height; + this[symbols.bitDepth] = bitDepth; + this[symbols.renderer] = renderer; + this[symbols.initialized] = true; + } + [symbols.reset]() { + this[symbols.width] = 0; + this[symbols.height] = 0; + this[symbols.bitDepth] = 0; + this[symbols.renderer] = null; + this[symbols.initialized] = false; + } + get width() { + return this[symbols.width]; + } + get height() { + return this[symbols.height]; + } + get bitDepth() { + return this[symbols.bitDepth]; + } + get bufferFormat() { + return this[symbols.renderer].name; + } +} + +module.exports = { + Screen, + symbols, +}; diff --git a/js/core/index.js b/js/core/index.js index ebddb3600..647f65af3 100644 --- a/js/core/index.js +++ b/js/core/index.js @@ -23,6 +23,7 @@ const ps2 = require('./ps2'); const pci = require('./pci'); const net = require('./net'); const stdio = require('./stdio'); +const graphics = require('./graphics'); class Runtime { constructor() { @@ -34,6 +35,7 @@ class Runtime { allocator, net, stdio, + graphics, machine: { reboot: __SYSCALL.reboot, shutdown: () => __SYSCALL.acpiEnterSleepState(5), diff --git a/js/driver/bga/constants.js b/js/driver/bga/constants.js new file mode 100644 index 000000000..a8d55f6e7 --- /dev/null +++ b/js/driver/bga/constants.js @@ -0,0 +1,35 @@ +'use strict'; + +module.exports = { + VBE_DISPI_IOPORT_INDEX: 0x01CE, + VBE_DISPI_IOPORT_DATA: 0x01CF, + VBE_DISPI_INDEX_ID: 0x0, + VBE_DISPI_INDEX_XRES: 0x1, + VBE_DISPI_INDEX_YRES: 0x2, + VBE_DISPI_INDEX_BPP: 0x3, + VBE_DISPI_INDEX_ENABLE: 0x4, + VBE_DISPI_INDEX_BANK: 0x5, + VBE_DISPI_INDEX_VIRT_WIDTH: 0x6, + VBE_DISPI_INDEX_VIRT_HEIGHT: 0x7, + VBE_DISPI_INDEX_X_OFFSET: 0x8, + VBE_DISPI_INDEX_Y_OFFSET: 0x9, + VBE_DISPI_INDEX_VIDEO_MEMORY_64K: 0xa, + VBE_DISPI_BPP_4: 0x04, + VBE_DISPI_BPP_8: 0x08, + VBE_DISPI_BPP_15: 0x0F, + VBE_DISPI_BPP_16: 0x10, + VBE_DISPI_BPP_24: 0x18, + VBE_DISPI_BPP_32: 0x20, + VBE_DISPI_NOCLEARMEM: 0x80, + VBE_DISPI_DISABLED: 0x00, + VBE_DISPI_ENABLED: 0x01, + VBE_DISPI_LFB_ENABLED: 0x40, + VBE_DISPI_ID0: 0xB0C0, + VBE_DISPI_ID1: 0xB0C1, + VBE_DISPI_ID2: 0xB0C2, + VBE_DISPI_ID3: 0xB0C3, + VBE_DISPI_ID4: 0xB0C4, + VBE_DISPI_ID5: 0xB0C5, + VBE_DISPI_GETCAP: 0x02, + VBE_DISPI_8BIT_DAC: 0x20, +}; diff --git a/js/driver/bga/index.js b/js/driver/bga/index.js new file mode 100644 index 000000000..ce6c8ded3 --- /dev/null +++ b/js/driver/bga/index.js @@ -0,0 +1,46 @@ +'use strict'; + +const { ioPort } = require('../../core/driver-utils'); +const constants = require('./constants'); +const vbeDispiIoportIndex = ioPort(constants.VBE_DISPI_IOPORT_INDEX); +const vbeDispiIoportData = ioPort(constants.VBE_DISPI_IOPORT_DATA); + +function writeBgaRegister(index, data) { + vbeDispiIoportIndex.write16(index >>> 0); + vbeDispiIoportData.write16(data >>> 0); +} +function readBgaRegister(index) { + vbeDispiIoportIndex.write16(index >>> 0); + return vbeDispiIoportData.read16(); +} +function bgaAvailable() { + const bgaVersion = readBgaRegister(constants.VBE_DISPI_INDEX_ID); + for (const i of [0, 1, 2, 3, 4, 5]) { + if (bgaVersion === constants[`VBE_DISPI_ID${i}`]) { + return true; + } + } + return false; +} + +if (bgaAvailable()) { + const driver = { + init(device) { + const buf = new Uint8Array(device.bars[0].resource.buffer()); + const renderer = new runtime.graphics.GraphicsRenderer('bga'); + renderer.onenablegraphics = (width, height, bitDepth) => { + writeBgaRegister(constants.VBE_DISPI_INDEX_ENABLE, constants.VBE_DISPI_DISABLED); + writeBgaRegister(constants.VBE_DISPI_INDEX_XRES, width); + writeBgaRegister(constants.VBE_DISPI_INDEX_YRES, height); + writeBgaRegister(constants.VBE_DISPI_INDEX_BPP, bitDepth); + writeBgaRegister(constants.VBE_DISPI_INDEX_ENABLE, constants.VBE_DISPI_ENABLED | constants.VBE_DISPI_LFB_ENABLED); + }; + renderer.ongetbuffer = () => buf; + renderer.constants = constants; + runtime.graphics.addRenderer(renderer); + }, + reset() {}, + }; + + runtime.pci.addDriver(0x1234, 0x1111, driver); +} diff --git a/js/index.js b/js/index.js index fd76ddecd..cb49cbbef 100644 --- a/js/index.js +++ b/js/index.js @@ -63,6 +63,7 @@ runtime.shell.setCommand('reboot', (args, env, cb) => { // Start device drivers require('./driver/ps2'); require('./driver/virtio'); +require('./driver/bga'); // Set time require('./core/cmos-time'); // load cmos