Browse Source

First full coding pass of NESNameTable complete.

dev
Bryan Miller 5 years ago
parent
commit
f504f93fbe
1 changed files with 196 additions and 16 deletions
  1. +196
    -16
      app/js/models/NESNameTable.js

+ 196
- 16
app/js/models/NESNameTable.js View File

@@ -4,6 +4,26 @@ import NESBank from "/app/js/models/NESBank.js";
import NESPalette from "/app/js/models/NESPalette.js";


function NumToHex(n){
var h = n.toString(16);
if (h.length %2)
h = '0' + h;
return '$' + h;
}

function CompileAttribs(attrib){
return (attrib[3] << 6) | (attrib[2] << 4) | (attrib[1] << 2) | (attrib[0] << 0)
}

function DecompileAttribs(v){
return [
v & 0x00000011,
(v & 0x00001100) >> 2,
(v & 0x00110000) >> 4,
(v & 0x11000000) >> 6
];
}

export default class NESNameTable extends ISurface{
constructor(){
super();
@@ -12,18 +32,49 @@ export default class NESNameTable extends ISurface{
this.__tiles = [];
this.__attribs = [];

this.__undos = [];
this.__redos = [];

for (let i=0; i < 960; i++)
this.__tiles[i] = 0;
for (let i=0; i < 64; i++)
this.__attribs[i] = [0,0,0,0];
}

get base64(){
var b = "";
for (let i = 0; i < this.__tiles.length; i++)
b += String.fromCharCode(this.__tiles[i]);
for (let i = 0; i < this.__attribs.length; i++)
b += String.fromCharCode(CompileAttribs(this.__attribs[i]));
return window.btoa(b);
}

set base64(s){
var b = window.atob(s);
var len = b.length;
if (b.length !== 1024){
throw new Error("Base64 string contains invalid byte count.");
}
b = new Uint8Array(b.split("").map(function(c){
return c.charCodeAt(0);
}));
for (let i=0; i < b.length; i++){
if (i < 960){
this.__tiles[i] = b[i];
} else {
this.__attribs[i-960] = DecompileAttribs(b[i]);
}
}
this.emit("data_changed");
}

get bank(){return this.__bank;}
set bank(b){
if (b !== null and !(b instanceof NESBank))
throw new TypeError("Expected a NESBank object.");
this.__bank = b;
this.emit("data_changed");
}

get palette(){return this.__palette;}
@@ -31,13 +82,14 @@ export default class NESNameTable extends ISurface{
if (p !== null && !(p instanceof NESPalette))
throw new TypeError("Expected a NESPalette object.");
this.__palette = p;
this.emit("data_changed");
}

get width(){return 256;}
get height(){return 240;}
get length(){return 0;}
get undos(){return 0;}
get redos(){return 0;}
get length(){return this.width * this.height;}
get undos(){return this.__undos.length;}
get redos(){return this.__redos.length;}


copy(b){
@@ -53,6 +105,7 @@ export default class NESNameTable extends ISurface{
b.__attribs[i][3]
];
}
this.emit("data_changed");
return this;
}

@@ -60,13 +113,52 @@ export default class NESNameTable extends ISurface{
return (new NESNameTable()).clone(this);
}

snapshot(){return this;}
undo(){return this;}
redo(){return this;}
clearUndos(){return this;}
clearRedos(){return this;}
clearHistory(){
return this.clearUndos().clearRedos();
snapshot(){
if (this.__redos.length > 0) // Remove the redo history. We're adding a new snapshot.
this.__redos = [];

var snap = this.base64;
if (this.__undos.length === this.__historyLength){
this.__undos.pop();
}
this.__undos.splice(0,0,snap);
return this;
}
undo(){
if (this.__undos.length > 0){
var usnap = this.__undos.splice(0, 1)[0];
var rsnap = this.base64;
this.base64 = usnap;
if (this.__redos.length === this.__historyLength){
this.__redos.pop();
}
this.__redos.splice(0,0,rsnap);
}
return this;
}

redo(){
if (this.__redos.length > 0){
var rsnap = this.__redos.splice(0,1)[0];
var usnap = this.base64;
this.base64 = rsnap;
if (this.__undos.length === this.__historyLength){
this.__undos.pop();
}
this.__undos.splice(0,0,usnap);
}
return this;
}

clearUndos(){
this.__undos = [];
return this;
}

clearRedos(){
this.__redos = [];
return this;
}

getColor(x, y){
@@ -96,9 +188,8 @@ export default class NESNameTable extends ISurface{

var tileX = Math.floor(x / 32);
var tileY = Math.floor(y / 32);
var tileIndex = 256 + this.__tiles[(tileY * 32) + tileX];

ci = this.__bank.rp[tileIndex].getPixelIndex(_x, _y);
ci = this.__bank.rp[this.__tiles[(tileY * 32) + tileX]].getPixelIndex(_x, _y);
pi = this._PaletteFromCoords(x, y);
}
return {pi:pi, ci:ci};
@@ -106,22 +197,111 @@ export default class NESNameTable extends ISurface{


setColorIndex(x, y, ci, pi){
if (x < 0 || x >= this.width || y < 0 || y >= this.height)
throw new RangeError("Coordinates are out of bounds.");
if (pi < 0 || pi >= 4)
throw new RangeError("Palette index is out of bounds.");

// NOTE: This method (setColorIndex) is called by CTRLPainter, which doesn't know about painting tile
// indicies... therefore, CTRLPainter will still call this method for NESNameTable surfaces, but all it
// will do is change palette indicies.
// To paint the actual tile index, however, we'll use this emit that will be watched by the CTRLNameTable class
// and call this class's setTileIndex() method for tile painting.
// YAY to cheating!!
this.emit("paint_nametable");

// Then business as usual!
var bp = this._GetAttribBlockPalette(x,y);
if (this.__attribs[bp.bindex][bp.pindex] !== pi){
this.__attribs[bp.bindex][bp.pindex] = pi;
this.emit("data_changed");
}
return this;
}

setTileIndex(x, y, ti){
if (ti < 0 || ti >= 256)
throw new RangeError("Tile index is out of bounds.");
if (x < 0 || x >= this.width || y < 0 || y >= this.height)
throw new RangeError("Coordinates are out of bounds.");

var _x = Math.floor(x / 8);
var _y = Math.floor(y / 8);
var tindex = (_y * 32) + _x;
if (this.__tiles[tindex] !== ti){
this.__tiles[tindex] = ti;
this.emit("data_changed");
}
}


/**
* Generates a small 6502 assembly block string containing the current nametable data.
* @param {string} [ntname="NameTableData"] The label name under which to store the data.
* @returns {string}
*/
nametable_asm(ntname="NameTableData"){
var s = ntname + ":";

for (let i=0; i < this.__tiles.length; i++){
if (i % 32 === 0)
s += "\n\t.db";
s += " " + NumToHex(this.__tiles[i]);
}

return s;
}


/**
* Generates a small 6502 assembly block string containing the current attribute table data.
* @param {string} [atname="AttribTableData"] The label name under which to store the data.
* @returns {string}
*/
attribtable_asm(atname="AttribTableData"){
var s = atname + ":";

for (let i=0; i < this.__attribs.length; i++){
if (i % 32 === 0)
s += "\n\t.db";
s += " " + NumToHex(CompileAttribs(this.__attribs[i]));
}

return s;
}


/**
* Generates a small 6502 assembly block string containing the current name and attribute table data.
* @param {string} [ntname="NameTableData"] The label name under which to store the Nametable data.
* @param {string} [atname="AttribTableData"] the label name under which to store the Attribute table data.
* @returns {string}
*/
to_asm(ntname="NameTableData", atname="AttribTableData"){
return this.nametable_asm(ntname) + "\n\n" + this.attribtable_asm(atname);
}

_GetAttribBlockPalette(x,y){
var bp = {bindex:0, pindex:0};

_PaletteFromCoords(x,y){
var blockX = Math.floor(x / 32);
var blockY = Math.floor(y / 32);
var bIndex = (blockY * 8) + blockX;
bp.bindex = (blockY * 8) + blockX;

var palX = Math.floor(x % 16);
var palY = Math.floor(y % 16);
var pIndex = ((palX < 8) ? 0 : 1) + ((palY >= 8) ? 2 : 0);
bp.pindex = ((palX < 8) ? 0 : 1) + ((palY >= 8) ? 2 : 0);

return this.__attribs[bIndex][pIndex];
return bp;
}

_PaletteFromCoords(x,y){
var bp = this._GetAttribBlockPalette(x,y);
return this.__attribs[bp.bindex][bp.pindex];
}
}






Loading…
Cancel
Save