UNPKG

broken-neees

Version:

A really broken NEEES emulator that introduces glitches and random bugs on purpose!

103 lines (102 loc) 6.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _Tile = _interopRequireDefault(require("./Tile")); var _byte = _interopRequireDefault(require("../lib/byte")); 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 SCREEN_WIDTH = 256; var SCREEN_HEIGHT = 240; var TILE_SIZE_PIXELS = 8; var TILES_PER_ROW = SCREEN_WIDTH / TILE_SIZE_PIXELS; var MEM_NAME_TABLES = 0x2000; var NAME_TABLE_SIZE_BYTES = 1024; var ATTRIBUTE_TABLE_SIZE_BYTES = 64; var ATTRIBUTE_TABLE_BLOCK_SIZE = 32; var ATTRIBUTE_TABLE_REGION_SIZE = 16; var ATTRIBUTE_TABLE_TOTAL_BLOCKS_X = SCREEN_WIDTH / ATTRIBUTE_TABLE_BLOCK_SIZE; var ATTRIBUTE_TABLE_TOTAL_REGIONS_X = ATTRIBUTE_TABLE_BLOCK_SIZE / ATTRIBUTE_TABLE_REGION_SIZE; var ATTRIBUTE_TABLE_REGION_SIZE_BITS = 2; function xxx(time, period, radius) { return radius + Math.round(radius * Math.cos(2 * Math.PI * time / period)); } function yyy(time, period, radius) { return radius + Math.round(radius * Math.sin(2 * Math.PI * time / period)); } var PERIOD = 60; var RADIUS = 6; var BackgroundRenderer = /*#__PURE__*/function () { function BackgroundRenderer(ppu) { _classCallCheck(this, BackgroundRenderer); this.ppu = ppu; } _createClass(BackgroundRenderer, [{ key: "renderScanline", value: function renderScanline() { var y = this.ppu.scanline; var scrolledY; if (this.ppu.cpu.unbroken) { scrolledY = this.ppu.loopy.scrolledY(); } else { // [!!!] scrolledY = (this.ppu.loopy.scrolledY() + (y > 31 ? yyy(this.ppu.frame, PERIOD, RADIUS) : 0)) % 240; } var backgroundColor = this.ppu.getColor(0, 0); for (var x = 0; x < SCREEN_WIDTH; x++) { if (!this.ppu.registers.ppuMask.showBackgroundInFirst8Pixels && x < 8) { this.ppu.plotBG(x, y, backgroundColor, 0); continue; } var scrolledX = void 0; if (this.ppu.cpu.unbroken) { scrolledX = this.ppu.loopy.scrolledX(x); } else { // [!!!] scrolledX = (this.ppu.loopy.scrolledX(x) + xxx(this.ppu.frame, PERIOD, RADIUS)) % 256; } var nameTableId = this.ppu.loopy.nameTableId(scrolledX); var nameTableX = scrolledX % SCREEN_WIDTH; var nameTableY = scrolledY % SCREEN_HEIGHT; var tileX = Math.floor(nameTableX / TILE_SIZE_PIXELS); var tileY = Math.floor(nameTableY / TILE_SIZE_PIXELS); var tileIndex = tileY * TILES_PER_ROW + tileX; var tileId = this.ppu.memory.read(MEM_NAME_TABLES + nameTableId * NAME_TABLE_SIZE_BYTES + tileIndex); var paletteId = this._getBackgroundPaletteId(nameTableId, nameTableX, nameTableY); var paletteColors = this.ppu.getPaletteColors(paletteId); var patternTableId = this.ppu.registers.ppuCtrl.backgroundPatternTableId; var tileStartX = nameTableX % TILE_SIZE_PIXELS; var tileInsideY = nameTableY % TILE_SIZE_PIXELS; var tile = new _Tile.default(this.ppu, patternTableId, tileId, tileInsideY); var tilePixels = Math.min(TILE_SIZE_PIXELS - tileStartX, SCREEN_WIDTH - nameTableX); for (var xx = 0; xx < tilePixels; xx++) { var colorIndex = tile.getColorIndex(tileStartX + xx); var color = colorIndex > 0 ? paletteColors[colorIndex] : backgroundColor; this.ppu.plotBG(x + xx, y, color, colorIndex); } x += tilePixels - 1; } } }, { key: "_getBackgroundPaletteId", value: function _getBackgroundPaletteId(nameTableId, x, y) { var startAddress = MEM_NAME_TABLES + (nameTableId + 1) * NAME_TABLE_SIZE_BYTES - ATTRIBUTE_TABLE_SIZE_BYTES; var blockX = Math.floor(x / ATTRIBUTE_TABLE_BLOCK_SIZE); var blockY = Math.floor(y / ATTRIBUTE_TABLE_BLOCK_SIZE); var blockIndex = blockY * ATTRIBUTE_TABLE_TOTAL_BLOCKS_X + blockX; var regionX = Math.floor(x % ATTRIBUTE_TABLE_BLOCK_SIZE / ATTRIBUTE_TABLE_REGION_SIZE); var regionY = Math.floor(y % ATTRIBUTE_TABLE_BLOCK_SIZE / ATTRIBUTE_TABLE_REGION_SIZE); var regionIndex = regionY * ATTRIBUTE_TABLE_TOTAL_REGIONS_X + regionX; var block = this.ppu.memory.read(startAddress + blockIndex); return _byte.default.getBits(block, regionIndex * ATTRIBUTE_TABLE_REGION_SIZE_BITS, ATTRIBUTE_TABLE_REGION_SIZE_BITS); } }]); return BackgroundRenderer; }(); exports.default = BackgroundRenderer;