broken-neees
Version:
A really broken NEEES emulator that introduces glitches and random bugs on purpose!
128 lines (127 loc) • 5.95 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _PPUMemory = _interopRequireDefault(require("./PPUMemory"));
var _VideoRegisters = _interopRequireDefault(require("./VideoRegisters"));
var _BackgroundRenderer = _interopRequireDefault(require("./BackgroundRenderer"));
var _SpriteRenderer = _interopRequireDefault(require("./SpriteRenderer"));
var _LoopyRegister = _interopRequireDefault(require("../lib/ppu/LoopyRegister"));
var _masterPalette = _interopRequireDefault(require("../lib/ppu/masterPalette"));
var _interrupts = _interopRequireDefault(require("../lib/cpu/interrupts"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
var MEM_PALETTE = 0x3f00;
var PALETTE_SIZE_BYTES = 4;
var PPU = /*#__PURE__*/function () {
function PPU(cpu) {
_classCallCheck(this, PPU);
this.cpu = cpu;
this.cycle = 0;
this.scanline = -1;
this.frame = 0;
this.memory = new _PPUMemory.default();
this.registers = new _VideoRegisters.default(this);
this.loopy = new _LoopyRegister.default();
this.frameBuffer = new Uint32Array(256 * 240);
this.colorIndexes = new Uint8Array(256 * 240);
this.backgroundRenderer = new _BackgroundRenderer.default(this);
this.spriteRenderer = new _SpriteRenderer.default(this);
}
_createClass(PPU, [{
key: "onLoad",
value: function onLoad(mapper) {
this.mapper = mapper;
}
}, {
key: "step",
value: function step(onFrame, onInterrupt) {
if (this.scanline === -1) this._onPreLine();else if (this.scanline >= 0 && this.scanline < 240) this._onVisibleLine();else if (this.scanline === 241) this._onVBlankLine(onInterrupt);
this._incrementCounters(onFrame);
}
}, {
key: "plotBG",
value: function plotBG(x, y, color, colorIndex) {
this.colorIndexes[y * 256 + x] = colorIndex;
this.plot(x, y, color);
this.loopy.onPlot(x);
}
}, {
key: "plot",
value: function plot(x, y, color) {
this.frameBuffer[y * 256 + x] = this.registers.ppuMask.transform(color);
}
}, {
key: "isBackgroundPixelOpaque",
value: function isBackgroundPixelOpaque(x, y) {
return this.colorIndexes[y * 256 + x] > 0;
}
}, {
key: "getPaletteColors",
value: function getPaletteColors(paletteId) {
return [this.getColor(paletteId, 0), this.getColor(paletteId, 1), this.getColor(paletteId, 2), this.getColor(paletteId, 3)];
}
}, {
key: "getColor",
value: function getColor(paletteId, paletteIndex) {
var startAddress = MEM_PALETTE + paletteId * PALETTE_SIZE_BYTES;
var colorIndex = this.memory.read(startAddress + paletteIndex);
return _masterPalette.default[colorIndex];
}
}, {
key: "_onPreLine",
value: function _onPreLine() {
if (!this.registers.ppuMask.isRenderingEnabled()) return;
if (this.cycle === 1) {
this.registers.ppuStatus.spriteOverflow = 0;
this.registers.ppuStatus.sprite0Hit = 0;
this.registers.ppuStatus.isInVBlankInterval = 0;
}
this.loopy.onPreLine(this.cycle);
if (this.cycle === 260) this.mapper.tick();
}
}, {
key: "_onVisibleLine",
value: function _onVisibleLine() {
if (!this.registers.ppuMask.isRenderingEnabled()) return;
if (this.cycle === 0) {
if (this.registers.ppuMask.showBackground) this.backgroundRenderer.renderScanline();
if (this.registers.ppuMask.showSprites) this.spriteRenderer.renderScanline();
}
this.loopy.onVisibleLine(this.cycle);
if (this.cycle === 260) this.mapper.tick();
}
}, {
key: "_onVBlankLine",
value: function _onVBlankLine(onInterrupt) {
if (this.cycle === 1) {
this.registers.ppuStatus.isInVBlankInterval = 1;
if (this.registers.ppuCtrl.generateNMIOnVBlank) {
onInterrupt(_interrupts.default.NMI);
}
}
}
}, {
key: "_incrementCounters",
value: function _incrementCounters(onFrame) {
this.cycle++;
if (this.cycle >= 341) {
this.cycle = 0;
this.scanline++;
if (this.scanline >= 261) {
this.scanline = -1;
this.frame++;
onFrame(this.frameBuffer);
}
}
}
}]);
return PPU;
}();
exports.default = PPU;