sup39
898ea733ac
- Reduced parameters to struct pointer + format string + varargs - Rewrote QFT, Pattern Selector, Customized Display with the new drawText function - Added PAL font (TODO: NTSC-U) - Merged P/A/S Display and Speed Display to Customized Display - Provided background options to Pattern Selector and Customized Display
275 lines
6.6 KiB
JavaScript
275 lines
6.6 KiB
JavaScript
import * as Encoding from 'encoding-japanese';
|
|
|
|
/**
|
|
* @typedef {number[]} Inst
|
|
*
|
|
* @typedef {(
|
|
* rT: number,
|
|
* rA: number,
|
|
* D: number,
|
|
* ) => Inst} InstD
|
|
* @typedef {(
|
|
* rS: number,
|
|
* rA: number,
|
|
* D: number,
|
|
* ) => Inst} InstDS
|
|
*
|
|
* @typedef {(
|
|
* rA: number,
|
|
* rS: number,
|
|
* SH: number,
|
|
* MB: number,
|
|
* ME: number,
|
|
* Rc: number|boolean,
|
|
* ) => Inst} InstM
|
|
*
|
|
* @typedef {(
|
|
* rT: number,
|
|
* rA: number,
|
|
* rB: number,
|
|
* Rc: number|boolean,
|
|
* ) => Inst} InstX
|
|
* @typedef {(
|
|
* rS: number,
|
|
* rA: number,
|
|
* rB: number,
|
|
* Rc: number|boolean,
|
|
* ) => Inst} InstXS
|
|
*
|
|
* @typedef {(
|
|
* LL: number,
|
|
* LK: number|boolean,
|
|
* AA?: number|boolean,
|
|
* ) => Inst} InstI
|
|
*/
|
|
|
|
/** @param {number} inst */
|
|
export const makeInst = (inst) => {
|
|
// const buf = Inst.alloc(4);
|
|
// buf.writeUint32BE(inst >>> 0);
|
|
// return buf;
|
|
return [inst];
|
|
};
|
|
/**
|
|
* @param {number} op
|
|
* @param {number} rT
|
|
* @param {number} rA
|
|
* @param {number} D
|
|
*/
|
|
const InstD = (op, rT, rA, D) =>
|
|
makeInst(((op & 0x3f) << 26) | ((rT & 0x1f) << 21) | ((rA & 0x1f) << 16) | (D & 0xffff));
|
|
/**
|
|
* @param {number} op
|
|
* @param {number} rT
|
|
* @param {number} rA
|
|
* @param {number} rB
|
|
* @param {number} op2
|
|
* @param {number} Rc
|
|
*/
|
|
const InstX = (op, rT, rA, rB, op2, Rc) =>
|
|
makeInst(
|
|
((op & 0x3f) << 26) |
|
|
((rT & 0x1f) << 21) |
|
|
((rA & 0x1f) << 16) |
|
|
((rB & 0x1f) << 11) |
|
|
((op2 & 0x3ff) << 1) |
|
|
Rc,
|
|
);
|
|
/**
|
|
* @param {number} op
|
|
* @param {number} RS
|
|
* @param {number} RA
|
|
* @param {number} SH
|
|
* @param {number} MB
|
|
* @param {number} ME
|
|
* @param {number} Rc
|
|
*/
|
|
const InstM = (op, RA, RS, SH, MB, ME, Rc) =>
|
|
makeInst(
|
|
((op & 0x3f) << 26) |
|
|
((RS & 0x1f) << 21) |
|
|
((RA & 0x1f) << 16) |
|
|
((SH & 0x1f) << 11) |
|
|
((MB & 0x1f) << 6) |
|
|
((ME & 0x1f) << 1) |
|
|
Rc,
|
|
);
|
|
/**
|
|
* @param {number} op
|
|
* @param {number} LL
|
|
* @param {number} AA
|
|
* @param {number} LK
|
|
*/
|
|
const InstI = (op, LL, AA, LK) =>
|
|
makeInst(((op & 0x3f) << 26) | ((LL & 0xffffff) << 2) | ((AA & 1) << 1) | (LK & 1));
|
|
|
|
/** @type {(op: number) => InstD} */
|
|
const makeInstD = (op) => (rT, rA, D) => InstD(op, rT, rA, D);
|
|
/** @type {(op: number) => InstDS} */
|
|
const makeInstDS = (op) => (rA, rS, D) => InstD(op, rA, rS, D);
|
|
/** @type {(op: number, op2: number) => InstX} */
|
|
const makeInstX = (op, op2) => (rT, rA, rB, Rc) => InstX(op, rT, rA, rB, op2, +Rc);
|
|
/** @type {(op: number, op2: number) => InstXS} */
|
|
const makeInstXS = (op, op2) => (rA, rS, rB, Rc) => InstX(op, rA, rS, rB, op2, +Rc);
|
|
/** @type {(op: number) => InstM} */
|
|
const makeInstM = (op) => (rA, rS, SH, MB, ME, Rc) => InstM(op, rA, rS, SH, MB, ME, +Rc);
|
|
/** @type {(op: number) => InstI} */
|
|
const makeInstI =
|
|
(op) =>
|
|
(LL, LK, AA = 0) =>
|
|
InstI(op, LL >> 2, +AA, +LK);
|
|
|
|
export const ASM = {
|
|
// store rT, rA, D
|
|
stb: makeInstD(38),
|
|
sth: makeInstD(44),
|
|
stw: makeInstD(36),
|
|
stfs: makeInstD(52),
|
|
stfd: makeInstD(54),
|
|
stwu: makeInstD(37),
|
|
// load rS, rA, D
|
|
lbz: makeInstD(34),
|
|
lhz: makeInstD(40),
|
|
lwz: makeInstD(32),
|
|
lfs: makeInstD(48),
|
|
// li rT, D
|
|
addi: makeInstD(14),
|
|
li: (/**@type{number}*/ rT, /**@type{number}*/ D) => InstD(14, rT, 0, D),
|
|
addis: makeInstD(15),
|
|
lis: (/**@type{number}*/ rT, /**@type{number}*/ D) => InstD(15, rT, 0, D),
|
|
// ori rA, rS, D
|
|
ori: makeInstDS(24),
|
|
// or rA, rS, rB, flag
|
|
or: makeInstXS(31, 444),
|
|
mr: (/**@type{number}*/ rT, /**@type{number}*/ rA, flag = 0) => InstX(31, rA, rT, rA, 444, flag),
|
|
// mask
|
|
rlwinm: makeInstM(21),
|
|
// b
|
|
b: makeInstI(18),
|
|
// mflr
|
|
mflr: (/**@type{number}*/ rT) => InstX(31, rT, 8, 0, 339, 0),
|
|
mfctr: (/**@type{number}*/ rT) => InstX(31, rT, 9, 0, 339, 0),
|
|
// mtlr
|
|
mtlr: (/**@type{number}*/ rS) => InstX(31, rS, 8, 0, 467, 0),
|
|
mtctr: (/**@type{number}*/ rS) => InstX(31, rS, 9, 0, 467, 0),
|
|
// cr
|
|
crset: (/**@type{number}*/ B) => InstX(19, B, B, B, 289, 0),
|
|
crclr: (/**@type{number}*/ B) => InstX(19, B, B, B, 193, 0),
|
|
};
|
|
|
|
/**
|
|
* @param {number} rT
|
|
* @param {number} D
|
|
*/
|
|
export function liDX(rT, D) {
|
|
if (-0x8000 <= D && D < 0x8000) {
|
|
return ASM.li(rT, D);
|
|
} else if ((D & 0xffff) === 0) {
|
|
return ASM.lis(rT, D >>> 16);
|
|
} else {
|
|
const h = D >>> 16;
|
|
const l = D & 0xffff;
|
|
return [...ASM.lis(rT, h), ...ASM.ori(rT, rT, l)];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} s
|
|
* @param {string} version
|
|
*/
|
|
export function str2bytes(s, version) {
|
|
const enc = version.startsWith('GMSJ') ? 'SJIS' : '';
|
|
const fmtbuf = version.startsWith('GMSJ')
|
|
? Encoding.convert(Encoding.stringToCode(s), 'SJIS') // Shift-JIS
|
|
: Array.from(s, (c) => {
|
|
// latin1
|
|
const x = c.charCodeAt(0);
|
|
// replace the char with space if it is multi-byte
|
|
return x >= 0x100 ? 0x20 : x;
|
|
});
|
|
fmtbuf.push(0); // NUL terminated
|
|
return fmtbuf;
|
|
}
|
|
|
|
/**
|
|
* @param {string} s
|
|
* @param {string} version
|
|
*/
|
|
export const str2hex = (s, version) =>
|
|
str2bytes(s, version)
|
|
.map((x) => x.toString(16).toUpperCase().padStart(2, '0'))
|
|
.join('');
|
|
|
|
/**
|
|
* @param {string} s
|
|
* @param {string} version
|
|
*/
|
|
export function str2inst(s, version) {
|
|
const fmtbuf = str2bytes(s, version);
|
|
const fmtlen = fmtbuf.length;
|
|
const fmtlen3 = fmtlen & 3;
|
|
const pad = fmtlen3 ? 4 - fmtlen3 : 0;
|
|
fmtbuf.push(...Array(pad).fill(0));
|
|
const dv = new DataView(Uint8Array.from(fmtbuf).buffer);
|
|
const insts = Array((fmtlen + pad) >> 2)
|
|
.fill(0)
|
|
.map((_, i) => dv.getUint32(i << 2, false));
|
|
return insts;
|
|
}
|
|
|
|
/** @param {number} pc */
|
|
export function makeProgram(pc) {
|
|
/** @type {Inst[]} */
|
|
const bufs = [];
|
|
return {
|
|
pc,
|
|
/**
|
|
* @param {number} dst
|
|
* @param {boolean} LL
|
|
*/
|
|
b(dst, LL = false) {
|
|
// TODO check overflow
|
|
this.push(ASM.b(dst - this.pc, LL));
|
|
},
|
|
/** @param {number} dst */
|
|
bl(dst) {
|
|
this.b(dst, true);
|
|
},
|
|
/** @param {Inst[]} codes */
|
|
push(...codes) {
|
|
bufs.push(...codes);
|
|
this.pc += codes.reduce((a, e) => a + e.length, 0) << 2;
|
|
},
|
|
dump: () => bufs.flatMap((e) => e),
|
|
};
|
|
}
|
|
|
|
/** @param {number} x */
|
|
export const inst2gecko = (x) => (x >>> 0).toString(16).toUpperCase().padStart(8, '0');
|
|
|
|
/**
|
|
* @param {{
|
|
* x: number,
|
|
* y: number,
|
|
* fontSize: number,
|
|
* bgRGB: number,
|
|
* bgA: number,
|
|
* bgLeft: number,
|
|
* bgRight: number,
|
|
* bgTop: number,
|
|
* bgBot: number
|
|
* }} opt
|
|
* @param {{width: number, height: number}} size
|
|
**/
|
|
export const getFillRectParams = (
|
|
{ x, y, fontSize, bgRGB, bgA, bgLeft, bgRight, bgTop, bgBot },
|
|
{ width, height },
|
|
) => [
|
|
// rect
|
|
x - bgLeft, // x0
|
|
y - fontSize - bgTop, // y0
|
|
x + Math.ceil((width * fontSize) / 20) + bgRight, // x1
|
|
y - fontSize + Math.ceil((height * fontSize) / 20) + bgBot, // y1
|
|
// color
|
|
(bgRGB << 8) | bgA,
|
|
];
|