mz700-js
Version:
MZ-700 Full JavaScript Emulator
317 lines (291 loc) • 11 kB
text/typescript
"use strict";
import mz700cg from "./mz700-cg";
/* tslint:disable: no-bitwise */
export default class MZ700CanvasRenderer {
static colors = {
"black":0,
"blue":1,
"red":2,
"magenta":3,
"green":4,
"cyan":5,
"yellow":6,
"white":7,
};
//
// default screen size by character
//
static size = {"cols":40, "rows":25};
//
// Dot size of a character
//
static charSize = {"dotWidth":8, "dotHeight":8};
// A canvas element
_canvas:HTMLCanvasElement;
// A canvas context to draw
_ctx:CanvasRenderingContext2D = null;
opt:{
canvas?:HTMLCanvasElement,
cols?:number,
rows?:number,
CG?:mz700cg,
color?:number,
backgroundColor?:number,
width?:string,
alt?:string,
title?:string,
} = {
canvas: null,
cols: MZ700CanvasRenderer.size.cols,
rows: MZ700CanvasRenderer.size.rows,
CG: null,
color: MZ700CanvasRenderer.colors.white,
backgroundColor: MZ700CanvasRenderer.colors.blue,
};
vramText:number[] = [];
vramAttr:number[] = [];
_font:mz700cg;
idxloc:{x:number, y:number}[];
constructor() { /* empty */ }
//
// Create screen
//
create(opt:{
canvas?:HTMLCanvasElement,
CG?:mz700cg,
cols?:number,
rows?:number,
color?:number,
backgroundColor?:number,
}):void {
opt = opt || {};
Object.keys(this.opt).forEach(key => {
if (key in opt) {
this.opt[key] = opt[key];
}
});
this._canvas = this.opt.canvas;
if (this.opt.CG == null) {
this.opt.CG = new mz700cg(mz700cg.ROM, 8, 8);
}
this._font = this.opt.CG;
// Create text/attr vram
this.vramText = [];
this.vramAttr = [];
for (let i = 0; i < this.opt.cols * this.opt.rows; i++) {
this.vramText.push(0x00);
this.vramAttr.push(0x71);
}
//
// A translation table to convert an address index on the VRAM
// to the X-Y pixel position where a character shown.
//
this.idxloc = ((idxloc, cols, rows) => {
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
idxloc.push({
x: MZ700CanvasRenderer.charSize.dotWidth * x,
y: MZ700CanvasRenderer.charSize.dotHeight * y
});
}
}
return idxloc;
})([], this.opt.cols, this.opt.rows);
}
setupRendering():void {
// Save canvas context
this._ctx = this._canvas.getContext('2d') as CanvasRenderingContext2D;
// (this._ctx as any).mozImageSmoothingEnabled = true;
// (this._ctx as any).webkitImageSmoothingEnabled = true;
// (this._ctx as any).msImageSmoothingEnabled = true;
this._ctx.imageSmoothingEnabled = true;
}
/**
* Redraw specified characters on the screen.
* @param {number} atb attribute bit to select CG page.
* @param {number} dispCode The display code to redraw.
* @returns {undefined}
*/
redrawChar(atb:number, dispCode:number):void {
const abit = atb << 7;
const n = this.opt.cols * this.opt.rows;
for (let i = 0; i < n; i++) {
const attr = this.vramAttr[i];
if (this.vramText[i] === dispCode && (attr & 0x80) === abit) {
this.writeVram(i, attr, dispCode);
}
}
}
/**
* Write a character to relative address with attribute.
* @param {number} addr A relative address index from the top left of screen to put.
* @param {number} attr An attribute value for MZ-700.
* @param {number} dispcode A display code of character to put.
* @returns {undefined}
*/
writeVram(addr:number, attr:number, dispcode:number):void {
this._writeVram(addr, attr, dispcode);
this.vramText[addr] = dispcode;
this.vramAttr[addr] = attr;
}
private _writeVram(addr:number, attr:number, dispcode:number):void {
const loc:{x:number, y:number} = this.idxloc[addr];
if(!this._ctx) {
return;
}
this._ctx.putImageData(
this._font.get(attr, dispcode).getImageData(),
loc.x, loc.y);
}
// Redraw VRAM
redraw():void {
const n:number = this.opt.cols * this.opt.rows;
for (let i = 0; i < n; i++) {
this._writeVram(i, this.vramAttr[i], this.vramText[i]);
}
}
/**
* @returns {ImageData} canvas image data of screen.
*/
getImageData():ImageData {
return this._ctx.getImageData(0, 0, 320, 200);
}
// Change Character Generator
changeCG(cgData:mz700cg):void {
this._font = cgData;
}
// Restore Character Generator
restoreCG():void {
this._font = this.opt.CG;
}
/**
* Clear the screen
* @returns {undefined}
*/
clear():void {
const limit:number = this.opt.rows * this.opt.cols;
const chars:string[] = MZ700CanvasRenderer.str2chars(' ');
for (let relAddr = 0; relAddr < limit; relAddr++) {
this.putChars(chars, relAddr, 0);
}
}
/**
* Put string
* @param {string} s strings to put
* @param {number} x horizontal starting position
* @param {number} y vertical starting position
* @returns {number} The length of the letters that outputed
*/
puts(s:string, x:number, y:number):number {
const chars = MZ700CanvasRenderer.str2chars(s);
return this.putChars(chars, x, y);
}
putChars(chars:string[], x:number, y:number):number {
const limit = this.opt.rows * this.opt.cols;
const colorSpec = this.opt.color << 4 | this.opt.backgroundColor;
let n = 0;
let relAddr = y * this.opt.cols + x;
chars.forEach(function (c) {
if (relAddr < limit) {
const data = MZ700CanvasRenderer.char2dispcode(c);
this.writeVram(relAddr, (data.attr << 7) | colorSpec, data.dispcode);
relAddr++;
n++;
}
}, this);
return n;
}
private static TableDispCode2Char:string[][][] = [
[
[" ","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O"],
["P","Q","R","S","T","U","V","W","X","Y","Z","┼","└","┘","├","┴"],
["0","1","2","3","4","5","6","7","8","9","-","=",";","/",".",","],
["","","","","","","","","","","","","","","",""],
["→","SPADE","","","DIA","←","CLUB","●","○","?","●反転","","","","","",":"],
["↑","<","[","HEART","]","@","",">","","BACKSLASH","HATCH","","","","",""],
["π","!", '"', "#", "$", "%", "AMP", "'", "(", ")","+","*","","","",""],
["","","","","","","","","","","","","","","",""],
["↓", "チ","コ","ソ","シ","イ","ハ","キ","ク","ニ","マ","ノ","リ","モ","ミ","ラ"],
["セ","タ","ス","ト","カ","ナ","ヒ","テ","サ","ン","ツ","ロ","ケ","「","ァ","ャ"],
["ワ","ヌ","フ","ア","ウ","エ","オ","ヤ","ユ","ヨ","ホ","ヘ","レ","メ","ル","ネ"],
["ム","」","ィ","ュ","ヲ","、","ゥ","ョ","゜","・","ェ","ッ","゛","。","ォ","ー"],
["PUSHDOWN","~DOWN","~UP","~RIGHT","~LEFT","~HOME","~CLEAR","UFO","CARRIGHT","CARUP","HUMAN","LHUMAN","RHUMAN","DHUMAN","FILLEDFACE","FACE"],
["日","月","火","水","木","金","土","生","年","時","分","秒","円","¥","£","蛇"],
[" ","","","","","","","","","","","","","","",""],
[" ","","","","","","","","","","","","","","",""]
],
[
[" ","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o"],
["p","q","r","s","t","u","v","w","x","y","z","", "", "", "", "", ],
["","","","","","","","","","","","","","","",""],
["","","","","","","","","","","","","","","",""],
["","","","","","","","","","","","","","","",""],
["","","","","","","","","","","","","","","",""],
["","","","","","","","","","","","","","","",""],
["","","","","","","","","","","","","","","",""],
["", "ち","こ","そ","し","い","は","き","く","に","ま","の","り","も","み","ら"],
["せ","た","す","と","か","な","ひ","て","さ","ん","つ","ろ","け","", "ぁ","ゃ"],
["わ","ぬ","ふ","あ","う","え","お","や","ゆ","よ","ほ","へ","れ","め","る","ね"],
["む","", "ぃ","ゅ","を","", "ぅ","ょ","", "", "ぇ","っ","", "", "ぉ",""],
["","","","","","","","","","","","","","","",""],
["","","","","","","","","","","","","","","",""],
["","","","","","","","","","","","","","","",""],
["","","","","","","","","","","","","","","",""],
]
];
/*
* Create a dictionary to retrieve the display code of MZ-700
* by one normal character or string of entity reference.
*/
static MapChar2DispCode = {};
/**
* A dummy field like a static constructor
*/
static initializer = (() => {
MZ700CanvasRenderer.TableDispCode2Char.forEach((table, attr) => {
table.forEach((line, upper) => {
line.forEach((c, lower) => {
if(!(c in MZ700CanvasRenderer.MapChar2DispCode)) {
MZ700CanvasRenderer.MapChar2DispCode[c] = {
"attr": attr,
"dispcode" : upper << 4 | lower,
};
}
});
});
});
})();
static char2dispcode(c:string):{attr:number, dispcode:number} {
const charData = MZ700CanvasRenderer.MapChar2DispCode[c];
if(!charData) {
return { attr:0, dispcode : 0xef };
}
return charData;
}
static str2chars(s:string):string[] {
const chars = s.split('');
const entities = [];
let entityRef = false;
let entity = "";
chars.forEach((c) => {
if(!entityRef) {
if(c === '&') {
entityRef = true;
entity = '';
} else {
entities.push(c);
}
} else {
if(c === ';') {
entities.push(entity.toUpperCase());
entityRef = false;
entity = '';
} else {
entity += c;
}
}
});
return entities;
}
}
module.exports = MZ700CanvasRenderer;