//======================================================================================== // processor.js // Copyright (C) 2018 Makoto Kamada // // This program is derived from XEiJ. Please read the XEiJ License. // このプログラムは XEiJ の派生物です。XEiJ 使用許諾条件をお読みください。 // http://stdkmd.com/xeij/ //======================================================================================== //XEiJ使用許諾条件 /******************************************************************************* -------------------------------------------------------------------------------- XEiJ License (2016-02-26) You can use, modify and redistribute the XEiJ (X68000 Emulator in Java) if the following conditions are met. 1. The XEiJ and its derivatives must be distributed free of charge. 2. The XEiJ and its derivatives must be distributed along with those source code that is necessary to recompile them. 3. You must attach this text to derivatives of the XEiJ. Copyright Source code and data of the XEiJ except for the following parts are copyrighted by Makoto Kamada. 1. Basic software products for X68000 that were distributed free of charge, such as IPLROM.DAT, HUMAN.SYS, and so on, are copyrighted by right-holder companies. http://retropc.net/x68000/software/sharp/ 2. Emulation algorithm of YM2151 is derived from the MAME. http://mamedev.org/ 3. Default font data is composed of fonts designed by Keitarou Hiraki and Num Kadoma. http://hp.vector.co.jp/authors/VA000874/ http://www.geocities.jp/littlimi/ 4. IPL programs that are written into 2HDE and 2HS floppy disk images in formatting are copyrighted by 6no8rou. Disclaimer XEiJ is provided as is. No warranty against disadvantages caused by using and/or inability to use this software is given regardless of whether it had been able to be foreseen. -------------------------------------------------------------------------------- XEiJ 使用許諾条件 (2016-02-26) XEiJ (X68000 Emulator in Java) は以下の条件で使用し、改変し、再配布することが できます。 1. XEiJ とその派生物は無償で配布されなければなりません。 2. XEiJ とその派生物はそれらを再コンパイルするために必要なソースコードと共に配 布されなければなりません。 3. XEiJ の派生物には本文書を添付しなければなりません。 著作権 以下の部分を除く XEiJ のソースコードとデータは Makoto Kamada の著作物です。 1. IPLROM.DAT、HUMAN.SYS などの無償公開された X68000 の基本ソフトウェア製品は 権利各社の著作物です。 http://retropc.net/x68000/software/sharp/ 2. YM2151 のエミュレーションアルゴリズムは MAME に由来します。 http://mamedev.org/ 3. デフォルトのフォントデータは平木敬太郎氏と門真なむ氏によってデザインされた フォントで構成されています。 http://hp.vector.co.jp/authors/VA000874/ http://www.geocities.jp/littlimi/ 4. 2HDE および 2HS フロッピーディスクイメージをフォーマットするときに書き込ま れる IPL プログラムは 6no8rou 氏の著作物です。 免責事項 XEiJ は現状のまま (AS-IS) 提供されます。このソフトウェアを用いたことあるいは用 いることができなかったことで不利益が生じたとしても、それが予見可能だったかどう かに関わらず補償されません。 -------------------------------------------------------------------------------- *******************************************************************************/ //======================================================================================== // // Math // //======================================================================================== //------------------------------------------------------------------------ //数学定数 let E = Math.E; let LN10 = Math.LN10; let LN2 = Math.LN2; let LOG10E = Math.LOG10E; let LOG2E = Math.LOG2E; let PI = Math.PI; let SQRT1_2 = Math.SQRT1_2; let SQRT2 = Math.SQRT2; let ONE_3 = 1 / 3; let TWO_3 = 2 / 3; let FOUR_3 = 4 / 3; let TWOPI = 2 * PI; let PI_2 = 0.5 * PI; let PI_4 = 0.25 * PI; let DEG_RAD = 180 / PI; let RAD_DEG = PI / 180; //------------------------------------------------------------------------ //y = abs (x) // 絶対値関数 absolute value function アブソリュートバリューファンクション let abs = Math.abs; //------------------------------------------------------------------------ //y = acos (x) // 逆余弦 inverse cosine インバースコサイン let acos = Math.acos; //------------------------------------------------------------------------ //y = acosh (x) // 逆双曲線余弦 inverse hyperbolic cosine インバースハイパボリックコサイン // function builtin_acosh (x) { return log (x + sqrt (x * x - 1.0)); } let acosh = Math.acosh || builtin_acosh; //ECMAScrpit 6 //------------------------------------------------------------------------ //y = acot (x) // 逆余接 inverse cotangent インバースコタンジェント function acot (x) { return atan (1.0 / x); } //------------------------------------------------------------------------ //y = acoth (x) // 逆双曲線余接 inverse hyperbolic cotangent インバースハイパボリックコタンジェント function acoth (x) { return 0.5 * sgn (x) * log1p (2.0 / (abs (x) - 1.0)); } //------------------------------------------------------------------------ //y = acsc (x) // 逆余割 inverse csc インバースコセカント function acsc (x) { return asin (1.0 / x); } //------------------------------------------------------------------------ //y = acsch (x) // 逆双曲線余割 inverse hyperbolic csc インバースハイパボリックコセカント function acsch (x) { return asinh (1.0 / x); } //------------------------------------------------------------------------ //y = asec (x) // 逆正割 inverse secant インバースセカント function asec (x) { return atan2 (sqrt (x * x - 1), sgn (x)); } //------------------------------------------------------------------------ //y = asech (x) // 逆双曲線正割 inverse hyperbolic secant インバースハイパボリックセカント function asech (x) { return acosh (1.0 / x); } //------------------------------------------------------------------------ //y = asin (x) // 逆正弦 inverse sine インバースサイン let asin = Math.asin; //------------------------------------------------------------------------ //y = asinh (x) // 逆双曲線正弦 inverse hyperbolic sine インバースハイパボリックサイン function builtin_asinh (x) { return sgn (x) * log (abs (x) + sqrt (x * x + 1.0)); } let asinh = Math.asinh || builtin_asinh; //------------------------------------------------------------------------ //y = atan (x) // 逆正接 inverse tangent インバースタンジェント let atan = Math.atan; //------------------------------------------------------------------------ //y = atan2 (y, x) // 逆正接 inverse tangent インバースタンジェント let atan2 = Math.atan2; //------------------------------------------------------------------------ //y = atanh (x) // 逆双曲線正接 inverse hyperbolic tangent インバースハイパボリックタンジェント function builtin_atanh (x) { return x == 0.0 ? x : 0.5 * log ((1.0 + x) / (1.0 - x)); } let atanh = Math.atanh || builtin_atanh; //------------------------------------------------------------------------ //y = cbrt (x) // 1/3乗(立方根) function builtin_cbrt (x) { return sgn (x) * pow (abs (x), ONE_3); } let cbrt = Math.cbrt || builtin_cbrt; //------------------------------------------------------------------------ //y = ceil (x) // 天井関数 ceiling function let ceil = Math.ceil; //------------------------------------------------------------------------ //n = clz32 (x) // count leading zeros function builtin_clz32 (x) { let n = ((x >>> 16) - 1) >> 12 & 16; n += ((x >>> (24 - n)) - 1) >> 5 & 8; n += ((x >>> (28 - n)) - 1) >> 2 & 4; x >>>= 28 - n; return n + ((0x55af >>> (x << 1) & 3) - ((x - 1) >> 4)); } let clz32 = Math.clz32 || builtin_clz32; //------------------------------------------------------------------------ //s = cmp (x, y) // 自然順序比較 function cmp (x, y) { return y < x ? 1.0 : x < y ? -1.0 : 0.0; } //------------------------------------------------------------------------ //s = cmpnum (x, y) // 数値比較 spaceship operator function cmpnum (x, y) { x = +x; y = +y; return isNaN (x) || isNaN (y) || x === y ? 0.0 : y < x ? 1.0 : -1.0; } //------------------------------------------------------------------------ //z = copysign (x, y) // 符号複写 function copysign (x, y) { x = abs (x); return y < 0.0 || y == 0.0 && 1.0 / y < 0.0 ? -x : x; //1/+0=+Infinity,1/-0=-Infinity。-NaNは作れない } //------------------------------------------------------------------------ //y = cos (x) // 余弦 cosine コサイン let cos = Math.cos; //------------------------------------------------------------------------ //y = cosh (x) // 双曲線余弦 hyperbolic cosine ハイパボリックコサイン function builtin_cosh (x) { x = exp (x); return 0.5 * (x + 1.0 / x); } let cosh = Math.cosh || builtin_cosh; //------------------------------------------------------------------------ //y = cot (x) // 余接 cotangent コタンジェント function cot (x) { return cos (x) / sin (x); } //------------------------------------------------------------------------ //y = coth (x) // 双曲線余接 hyperbolic cotangent ハイパボリックコタンジェント function coth (x) { return cosh (x) / sinh (x); } //------------------------------------------------------------------------ //y = csc (x) // 余割 csc コセカント function csc (x) { return 1.0 / sin (x); } //------------------------------------------------------------------------ //y = csch (x) // 双曲線余割 hyperbolic csc ハイパボリックコセカント function csch (x) { return 1.0 / sinh (x); } //------------------------------------------------------------------------ //y = ctz32 (x) // count trailing zeros const CTZ32_HASH = [0, 1, 2, 5, 3, 9, 6, 11, 15, 4, 8, 10, 14, 7, 13, 12]; function builtin_ctz32 (x) { x >>>= 0; //32bit符号なし整数にする if (0 < x) { //1以上かつNaNではないとき let n = ((x & 65535) - 1) >> 12 & 16; //x<<16?0:16 x >>>= n; // xの下位16bitは0ではない // (1)x&-xで最下位の1<<0..15を取り出す // (2)最小完全ハッシュ関数x*2479>>>12&15で1<<0..15を0..15に変換する // (3)配列参照で0..15を1<<0..15のbit番号に変換する // 配列参照のコストによっては見た目ほど速くないかも知れない return n + CTZ32_HASH[(x & -x) * 2479 >>> 12 & 15]; } else { return 32; } } let ctz32 = Math.ctz32 || builtin_ctz32; //------------------------------------------------------------------------ //y = cub (x) // 3乗 function cub (x) { return x * x * x; } //------------------------------------------------------------------------ //x = cubic (a, b, c, d) // 3次方程式 function cubic (a, b, c, d) { return (a != 0.0 ? (a = 1.0 / a, b *= a * ONE_3, c *= a, d *= a, a = b * b - c * ONE_3, c = 0.5 * (b * c - 2.0 * b * b * b - d), d = c * c - a * a * a, (0.0 <= d ? (d = sqrt (d), cbrt (c - d) + cbrt (c + d)) : 2.0 * sqrt (a) * cos (atan2 (sqrt (-d), c) * ONE_3)) - b) : b != 0.0 ? (b = 1.0 / b, c *= 0.5 * b, d = c * c - b * d, 0.0 <= d ? sqrt (d) - c : NaN) : c != 0.0 ? -d / c : d != 0.0 ? NaN : 0.0); } //------------------------------------------------------------------------ //b = defined (x) // x !== undefined function defined (x) { return x !== undefined; } //------------------------------------------------------------------------ //y = deg (x) // 180/pi倍(ラジアン) function deg (x) { return DEG_RAD * x; } //------------------------------------------------------------------------ //y = exp (x) // 指数関数 exponential let exp = Math.exp; //------------------------------------------------------------------------ //y = exp10 (x) // 底が10の指数関数 exponential with base 10 function exp10 (x) { return exp (LN10 * x); } //------------------------------------------------------------------------ //y = exp2 (x) // 底が2の指数関数 exponential with base 2 function exp2 (x) { return exp (LN2 * x); } //------------------------------------------------------------------------ //y = exp2m1 (x) // 0に近い数の底が2の指数関数 exponential of number being close to 0 with base 2 function exp2m1 (x) { return exp2 (x) - 1.0; } //------------------------------------------------------------------------ //y = expm1 (x) // 0に近い数の指数関数 exponential of number being close to 0 function builtin_expm1 (x) { return x == 0 ? x : exp (x) - 1.0; } let expm1 = Math.expm1 || builtin_expm1; //------------------------------------------------------------------------ //y = ext16s (x) // 符号拡張(16bit符号あり整数) // 小数点以下を切り捨ててから2^16で割った余りを-2^15..2^15-1の範囲で返す function ext16s (x) { x = trunc (x) + 2 ** 15; //0x1p+15 return x - floor (x * 2 ** -16) * 2 ** 16 - 2 ** 15; //0x1p-16,0x1p+16,0x1p+15 } //------------------------------------------------------------------------ //y = ext16u (x) // ゼロ拡張(16bit符号なし整数) // 小数点以下を切り捨ててから2^16で割った余りを0..2^16-1の範囲で返す function ext16u (x) { x = trunc (x); return x - floor (x * 2 ** -16) * 2 ** 16; //0x1p-16,0x1p+16 } //------------------------------------------------------------------------ //y = ext32s (x) // 符号拡張(32bit符号あり整数) // 小数点以下を切り捨ててから2^32で割った余りを-2^31..2^31-1の範囲で返す function ext32s (x) { x = trunc (x) + 2 ** 31; //0x1p+31 return x - floor (x * 2 ** -32) * 2 ** 32 - 2 ** 31; //0x1p-32,0x1p+32,0x1p+31 } //------------------------------------------------------------------------ //y = ext32u (x) // ゼロ拡張(32bit符号なし整数) // 小数点以下を切り捨ててから2^32で割った余りを0..2^32-1の範囲で返す function ext32u (x) { x = trunc (x); return x - floor (x * 2 ** -32) * 2 ** 32; //0x1p-32,0x1p+32 } //------------------------------------------------------------------------ //y = ext64s (x) // 符号拡張(64bit符号あり整数) // 小数点以下を切り捨ててから2^64で割った余りを-2^63..2^63-1の範囲で返す function ext64s (x) { x = trunc (x) + 2 ** 63; //0x1p+63 return x - floor (x * 2 ** -64) * 2 ** 64 - 2 ** 63; //0x1p-64,0x1p+64,0x1p+63 } //------------------------------------------------------------------------ //y = ext64u (x) // ゼロ拡張(64bit符号なし整数) // 小数点以下を切り捨ててから2^64で割った余りを0..2^64-1の範囲で返す function ext64u (x) { x = trunc (x); return x - floor (x * 2 ** -64) * 2 ** 64; //0x1p-64,0x1p+64 } //------------------------------------------------------------------------ //y = ext8s (x) // 符号拡張(8bit符号あり整数) // 小数点以下を切り捨ててから2^8で割った余りを-2^7..2^7-1の範囲で返す function ext8s (x) { x = trunc (x) + 2 ** 7; //0x1p+7 return x - floor (x * 2 ** -8) * 2 ** 8 - 2 ** 7; //0x1p-8,0x1p+8,0x1p+7 } //------------------------------------------------------------------------ //y = ext8u (x) // ゼロ拡張(8bit符号なし整数) // 小数点以下を切り捨ててから2^8で割った余りを0..2^8-1の範囲で返す function ext8u (x) { x = trunc (x); return x - floor (x * 2 ** -8) * 2 ** 8; //0x1p-8,0x1p+8 } //------------------------------------------------------------------------ //z = fdiv (x, y) // floor除算の商 function fdiv (x, y) { return floor (x / y); } //------------------------------------------------------------------------ //y = floor (x) // 床関数 floor function let floor = Math.floor; //------------------------------------------------------------------------ //z = floor2 (x, y) // 床倍数 function floor2 (x, y) { return floor (x / y) * y; } //------------------------------------------------------------------------ //y = fracf (x) // 床小数部 fractional part (floor) function fracf (x) { return x - floor (x); } //------------------------------------------------------------------------ //y = fract (x) // 幹小数部 fractional part (trunc) function fract (x) { return isFinite (x) ? copysign (x - trunc (x), x) : x; } //------------------------------------------------------------------------ //z = frem (x, y) // floor除算の余り function frem (x, y) { return x - floor (x / y) * y; } //------------------------------------------------------------------------ //y = fround (x) // 最も近いfloat値に丸める function builtin_fround (x) { return new Float32Array ([x]) [0]; } let fround = Math.fround || builtin_fround; //------------------------------------------------------------------------ //y = hypot2 (a, b) //y = hypot3 (a, b, c) //y = hypot4 (a, b, c, d) //y = hypotn (n, ...) // 斜辺 hypotenuse function builtin_hypot2 (a, b) { return sqrt (a * a + b * b); } function builtin_hypot3 (a, b, c) { return sqrt (a * a + b * b + c * c); } function builtin_hypot4 (a, b, c, d) { return sqrt (a * a + b * b + c * c + d * d); } function builtin_hypotn () { let y = 0.0; for (let i = 0, n = arguments.length; i < n; i++) { let x = arguments[i]; y += x * x; } return sqrt (y); } let hypot2 = Math.hypot || builtin_hypot2; let hypot3 = Math.hypot || builtin_hypot3; let hypot4 = Math.hypot || builtin_hypot4; let hypotn = Math.hypot || builtin_hypotn; //------------------------------------------------------------------------ //z = ieeerem (x, y) // 剰余(round-to-nearest) function ieeerem (x, y) { return !isFinite (x) || isNaN (y) ? NaN : !isFinite (y) ? x : x - roundn (x / y) * y; } //------------------------------------------------------------------------ //z = imul (x, y) // 32bit符号あり整数乗算(下位) // 64bit浮動小数点数を32bit符号あり整数に変換して掛け合わせ、 // 積の64bit符号あり整数の下位32bitを32bit符号あり整数として取り出して64bit浮動小数点数に変換して返す // // 64bit浮動小数点数から32bit符号なし整数を取り出すとき32bit符号あり整数を経由してはならない // 同じ32bit整数への変換でもdouble→intのキャストは-0x80000000..0x7fffffffで飽和するので、 // 0x80000000..0xffffffffのビットパターンが保存されない function builtin_imul (x, y) { let b = x & 65535, d = y & 65535; return ((x >> 16) * d + b * (y >> 16) << 16) + b * d >>> 0 >> 0; } let imul = Math.imul || builtin_imul; //------------------------------------------------------------------------ //z = imulh (x, y) // 32bit符号あり整数乗算(上位) // 64bit浮動小数点数を32bit符号あり整数に変換して掛け合わせ、 // 積の64bit符号あり整数の上位32bitを32bit符号あり整数として取り出して64bit浮動小数点数に変換して返す // // 64bit浮動小数点数から32bit符号なし整数を取り出すとき32bit符号あり整数を経由してはならない // 同じ32bit整数への変換でもdouble→intのキャストは-0x80000000..0x7fffffffで飽和するので、 // 0x80000000..0xffffffffのビットパターンが保存されない function imulh (x, y) { let a = x >> 16, b = x & 65535, c = y >> 16, d = y & 65535; return a * c + ((a * d + b * c + (b * d >>> 16)) * 2 ** -16 >> 0) >> 0; //0x1p-16 *0x1p-16>>0の部分は入力が33bitあるので>>16にしてはならない } //------------------------------------------------------------------------ //b = isFinite (x) // 有限か //------------------------------------------------------------------------ //b = isInfinity (x) // ±Infinityか function isInfinity (x) { return !isNaN (x) && !isFinite (x); } //------------------------------------------------------------------------ //b =isNaN (x) // NaNか //------------------------------------------------------------------------ //b = isalnum (x) function isalnum (x) { return isdigit (x) || isalpha (x); } //------------------------------------------------------------------------ //b = isalpha (x) function isalpha (x) { return isupper (x) || islower (x); } //------------------------------------------------------------------------ //b = isascii (x) function isascii (x) { return 0x00 <= x && x <= 0x7f; } //------------------------------------------------------------------------ //b = isbdigit (x) function isbdigit (x) { return 0x30 <= x && x <= 0x31; //'0'..'1' } //------------------------------------------------------------------------ //b = isblank (x) function isblank (x) { return x == 0x09 || x == 0x20; //'\t'||' ' } //------------------------------------------------------------------------ //b = iscntrl (x) function iscntrl (x) { return isascii (x) && !isprint (x); } //------------------------------------------------------------------------ //b = iscsym (x) function iscsym (x) { return isdigit (x) || iscsymf (x); } //------------------------------------------------------------------------ //b = iscsymf (x) function iscsymf (x) { return isalpha (x) || x == 0x5f; //||'_' } //------------------------------------------------------------------------ //b = isdigit (x) function isdigit (x) { return 0x30 <= x && x <= 0x39; //'0'..'9' } //------------------------------------------------------------------------ //b = isgraph (x) function isgraph (x) { return 0x21 <= x && x <= 0x7e; //'!'..'~' } //------------------------------------------------------------------------ //b = islower (x) function islower (x) { return 0x61 <= x && x <= 0x7a; //'a'..'z' } //------------------------------------------------------------------------ //b = isodigit (x) function isodigit (x) { return 0x30 <= x && x <= 0x37; //'0'..'7' } //------------------------------------------------------------------------ //b = isprint (x) function isprint (x) { return 0x20 <= x && x <= 0x7e; //' '..'~' } //------------------------------------------------------------------------ //b = ispunct (x) function ispunct (x) { return isgraph (x) && !isalnum (x); } //------------------------------------------------------------------------ //b = isspace (x) function isspace (x) { return (0x09 <= x && x <= 0x0d) || x == 0x20; //'\t'..'\r'||' ' } //------------------------------------------------------------------------ //b = isupper (x) function isupper (x) { return 0x41 <= x && x <= 0x5a; //'A'..'Z' } //------------------------------------------------------------------------ //b = isxdigit (x) function isxdigit (x) { return isdigit (x) || (0x41 <= x && x <= 0x46) || (0x61 <= x && x <= 0x66); //||'A'..'F'||'a'..'f' } //------------------------------------------------------------------------ //y = log (x) // 自然対数 natural logarithm let log = Math.log; //------------------------------------------------------------------------ //y = log10 (x) // 常用対数 common logarithm function builtin_log10 (x) { return log (x) * LOG10E; } let log10 = Math.log10 || builtin_log10; //------------------------------------------------------------------------ //y = log1p (x) // 1に近い数の自然対数 natural logarithm of number being close to 1 function builtin_log1p (x) { return x == 0 ? x : log (1.0 + x); } let log1p = Math.log1p || builtin_log1p; //------------------------------------------------------------------------ //y = log2 (x) // 二進対数 binary logarithm function builtin_log2 (x) { return log (x) * LOG2E; } let log2 = Math.log2 || builtin_log2; //------------------------------------------------------------------------ //y = max2 (a, b) //y = max3 (a, b, c) //y = max4 (a, b, c, d) //y = maxn (n, ...) // 最大値 let max2 = Math.max; let max3 = Math.max; let max4 = Math.max; let maxn = Math.max; //------------------------------------------------------------------------ //y = min2 (a, b) //y = min3 (a, b, c) //y = min4 (a, b, c, d) //y = minn (n, ...) // 最小値 let min2 = Math.min; let min3 = Math.min; let min4 = Math.min; let minn = Math.min; //------------------------------------------------------------------------ //z = pow (x, y) // 累乗 let pow = Math.pow; //------------------------------------------------------------------------ //y = rad (x) // pi/180倍(ラジアン) function rad (x) { return RAD_DEG * x; } //------------------------------------------------------------------------ //y = rounda (x) // 丸め(away-from-zero) function rounda (x) { return x == 0 ? x : 0.0 < x ? floor (x + 0.5) : ceil (x - 0.5); } //------------------------------------------------------------------------ //y = roundm (x) // 丸め(toward-minus-infinity) function roundm (x) { return !isFinite (x) ? x : 0.0 <= x && x <= 0.5 ? floor (x) : ceil (x - 0.5); } //------------------------------------------------------------------------ //y = roundn (x) // 丸め(nearest-even) function roundn (x) { let y = floor (x + 0.5), z = ceil (x - 0.5); return copysign (y == z ? y : floor (y * 0.5) * 2.0 == y ? y : z, x); } //------------------------------------------------------------------------ //y = roundp (x) // 丸め(toward-plus-infinity) function roundp (x) { return !isFinite (x) ? x : -0.5 <= x && x <= 0.0 ? ceil (x) : floor (x + 0.5); } //------------------------------------------------------------------------ //z = roundp2 (x, y) // 丸め(toward-plus-infinity) function roundp2 (x, y) { return roundp (x / y) * y; } //------------------------------------------------------------------------ //y = roundz (x) // 丸め(toward-zero) function roundz (x) { return !isFinite (x) || x == 0 ? x : (0.0 < x ? x <= 0.5 ? 0.0 : ceil (x - 0.5) : -0.5 <= x ? -0.0 : floor (x + 0.5)); } //------------------------------------------------------------------------ //y = sec (x) // 正割 secant セカント function sec (x) { return 1.0 / cos (x); } //------------------------------------------------------------------------ //y = sech (x) // 双曲線正割 hyperbolic secant ハイパボリックセカント function sech (x) { return 1.0 / cosh (x); } //------------------------------------------------------------------------ //y = sgn (x) // 符号 function builtin_sgn (x) { x = +x; return x === 0 || isNaN (x) ? x : 0.0 < x ? 1.0 : -1.0; } let sgn = Math.sign || builtin_sgn; //------------------------------------------------------------------------ //y = sin (x) // 正弦 sine サイン let sin = Math.sin; //------------------------------------------------------------------------ //y = sinh (x) // 双曲線正弦 hyperbolic sine ハイパボリックサイン function builtin_sinh (x) { return x == 0 ? x : x = exp (x), 0.5 * (x - 1.0 / x); } let sinh = Math.sinh || builtin_sinh; //------------------------------------------------------------------------ //y = sqrt (x) // 1/2乗(平方根) let sqrt = Math.sqrt; //------------------------------------------------------------------------ //y = squ (x) // 2乗 function squ (x) { return x * x; } //------------------------------------------------------------------------ //y = tan (x) // 正接 tangent タンジェント let tan = Math.tan; //------------------------------------------------------------------------ //y = tanh (x) // 双曲線正接 hyperbolic tangent ハイパボリックタンジェント function builtin_tanh (x) { return isNaN (x) || x == 0 ? x : isFinite (x) ? (x = exp (2.0 * x), (x - 1.0) / (x + 1.0)) : 0.0 <= x ? 1.0 : -1.0; } let tanh = Math.tanh || builtin_tanh; //------------------------------------------------------------------------ //b = tolower (x) function tolower (x) { return isupper (x) ? x | 0x20 : x; } //------------------------------------------------------------------------ //b = toupper (x) function toupper (x) { return islower (x) ? x & ~0x20 : x; } //------------------------------------------------------------------------ //y = trunc (x) // 切り落とし truncate function builtin_trunc (x) { return isNaN (x) ? NaN : 0.0 <= x ? floor (x) : ceil (x); } let trunc = Math.trunc || builtin_trunc; //------------------------------------------------------------------------ //z = umul (x, y) // 32bit符号なし整数乗算(下位) // 64bit浮動小数点数を32bit符号なし整数に変換して掛け合わせ、 // 積の64bit符号なし整数の下位32bitを32bit符号なし整数として取り出して64bit浮動小数点数に変換して返す // // 64bit浮動小数点数から32bit符号なし整数を取り出すとき32bit符号あり整数を経由してはならない // 同じ32bit整数への変換でもdouble→intのキャストは-0x80000000..0x7fffffffで飽和するので、 // 0x80000000..0xffffffffのビットパターンが保存されない function umul (x, y) { let b = x >>> 0 & 65535, d = y >>> 0 & 65535; return ((x >>> 16) * d + b * (y >>> 16) << 16) + b * d >>> 0; } //------------------------------------------------------------------------ //z = umulh (x, y) // 32bit符号なし整数乗算(上位) // 64bit浮動小数点数を32bit符号なし整数に変換して掛け合わせ、 // 積の64bit符号なし整数の上位32bitを32bit符号なし整数として取り出して64bit浮動小数点数に変換して返す // // 64bit浮動小数点数から32bit符号なし整数を取り出すとき32bit符号あり整数を経由してはならない // 同じ32bit整数への変換でもdouble→intのキャストは-0x80000000..0x7fffffffで飽和するので、 // 0x80000000..0xffffffffのビットパターンが保存されない function umulh (x, y) { let a = x >>> 16, b = x >>> 0 & 65535, c = y >>> 16, d = y >>> 0 & 65535; return a * c + ((a * d + b * c + (b * d >>> 16)) * 2 ** -16 >>> 0) >>> 0; //0x1p-16 *0x1p-16>>>0の部分は入力が33bitあるので>>>16にしてはならない } //------------------------------------------------------------------------ let opmpcmProcessor; //プロセッサ //内部バッファ const OPM_OSC_FREQ = 4000000; //int OPMのオシレータ周波数(Hz)。4000000Hz const INTERNAL_SAMPLE_RATE = (OPM_OSC_FREQ / 64); //int OPMのサンプリング周波数=内部サンプリング周波数(Hz)。62500Hz const INTERNAL_BUFFER_MSEC = 50; //int 内部バッファの長さ(ms)。50ms=(1/20)s const INTERNAL_BUFFER_FRAMES = INTERNAL_SAMPLE_RATE * INTERNAL_BUFFER_MSEC / 1000; //int 内部バッファのフレーム数。62500/20=3125 let internalBufferLeft; //Float32Array 内部バッファ左 let internalBufferRight; //Float32Array 内部バッファ右 let internalBufferTime; //double 内部バッファに次に充填する時刻 //外部バッファ // 外部バッファは内部サンプリング周波数で構築された内部バッファを外部サンプリング周波数に合わせて間引き・重複して長さを変更した架空のバッファ let externalSampleRate; //int 外部サンプリング周波数。48000Hzまたは44100Hz let externalBufferFrames; //int 外部バッファのフレーム数。48000Hzのとき480、44100Hzのとき441 let externalToInternal; //Int32Array 外部バッファのインデックスを内部バッファのインデックスに変換するテーブル let externalBufferPointer; //int 外部バッファから次に読み出す位置 //------------------------------------------------------------------------ //OPM const OPM_PORT_COUNT = 2; //OPMポートの数 let opmPortArray; //OPMポートの配列 const OPM_CHANNEL_COUNT = 8; //int 1ポートあたりのチャンネル数。8以上の2の累乗。8より大きいときはアドレスの上位バイトを使う const OPM_SLOT_COUNT = (4 * OPM_CHANNEL_COUNT); //int 1ポートあたりのスロット数。32以上の2の累乗 const OPM_PHASE_SHIFT = 16; //int const OPM_PHASE_MASK = ((1 << OPM_PHASE_SHIFT) - 1); //int 65535 const OPM_SIN_BITS = 10; //int const OPM_SIN_TABLE_SIZE = (1 << OPM_SIN_BITS); //int 1024 const OPM_SIN_MASK = (OPM_SIN_TABLE_SIZE - 1); //int 1023 const OPM_TL_BITS = 10; //int const OPM_TL_SIZE = (1 << OPM_TL_BITS); //int 1024 const OPM_TL_BLOCK_SIZE = (256 * 2); //int 512 const OPM_TL_TABLE_SIZE = (13 * OPM_TL_BLOCK_SIZE); //int 6656 const OPM_ENV_QUIET = (OPM_TL_TABLE_SIZE >> 3); //int 832 //タイマ // RESET,IRQEN,LOAD,ISTのビットの順序 // Inside X68000にISTのビットの順序が上位からA|Bと書かれているが、B|Aの誤り const OPM_RESETB = 0x20; //int 0b00100000 const OPM_RESETA = 0x10; //int 0b00010000 const OPM_IRQENB = 0x08; //int 0b00001000 const OPM_IRQENA = 0x04; //int 0b00000100 const OPM_LOADB = 0x02; //int 0b00000010 const OPM_LOADA = 0x01; //int 0b00000001 const OPM_ISTB = 0x02; //int 0b00000010 const OPM_ISTA = 0x01; //int 0b00000001 //PG // // YM2151は入力クロックとして3.58MHzを与えたときにラの音(キーコード0x4a)の周波数が440Hzになるように設計されている // (2062*2^(4-2))*3.58e+6/2^26=439.9991 // X68000では4MHzが与えられているため、およそ2度高い音が出る // (2062*2^(4-2))*4e+6/2^26=491.6191 // キーコードを2減らすことで本来のラの音に近い音が出る。キーコードを減らすときは欠番の跨ぎ方に注意する // (1837*2^(4-2))*4e+6/2^26=437.9749 // より正確な音を出すにはキーフラクションも調整する // log2(4/3.58)*(64*12)=122.9110 // キーフラクションを123減らすことで本来のラの音に近い音が出る。それでも3.58MHzを与えたときほど正確なラの音は出せない // (1845*2^(4-2))*4e+6/2^26=439.8823 // // 細かい波形はD/AやA/Dを通るときに変化してしまうので観測が難しいが、周波数はある程度の時間観測して振動回数を数えれば分かる // X68030の実機で観測し、1オクターブ(16*64=1024音)がすべて整数になる最小の係数を求めたところ、音が重複している箇所も含めてMAMEが持っているテーブルとまったく同じものが得られた // // KFが平均音階の1度を正確に64分割した結果を丸めたものであればテーブルの全体を自動生成できるはずだが、間隔にばらつきがあるため単純に分割しただけでは再現できない