![]() |
<!DOCTYPE HTML> <HTML LANG="ja"> <HEAD> <META CHARSET="Shift_JIS"> <TITLE>IBM PC エミュレータ</TITLE> </HEAD> <BODY STYLE="background-color:#CCFFFF"> <CENTER> <B>IBM PC エミュレータ (Firefox 43)</B> <BR> <DIV ID="frm" STYLE="border:solid gray 4px"> <CANVAS ID="scr" WIDTH=1 HEIGHT=1 STYLE="image-rendering:-moz-crisp-edges; display:none"></CANVAS> <SPAN ID="dscr" STYLE="background-color:black; display:inline-block"> <SPAN STYLE="margin-top:5em; padding:20px; border-radius:3px; text-align:left; color:black; background-color:#EEEEEE; display:inline-block">起動するディスク イメージをマウントして<BR>リセット ボタンを押してください。</SPAN> </SPAN> </DIV> <BR> <FORM> <TABLE BORDER=0> <TR><TD NOWRAP> <SPAN STYLE="vertical-align:middle; margin-right:0.5em">FD ドライブ 1</SPAN> <SPAN STYLE="display:inline-block; line-height:0; vertical-align:middle"> <SPAN STYLE="display:inline-block; height:0; overflow:hidden; vertical-align:top"> <INPUT TYPE=BUTTON VALUE="マウント
取り外し" STYLE="visibility:hidden"> </SPAN> <BR> <INPUT TYPE=BUTTON NAME="mnt" VALUE="マウント" onClick="mount_fd(0)" STYLE="width:100%"> </SPAN> <SPAN NAME="nam" STYLE="white-space:pre; display:inline-block; width:10em; vertical-align:middle; padding-left:2px; padding-right:2px; overflow:hidden; text-overflow:ellipsis; color:black; background-color:white"> </SPAN> <LABEL><INPUT TYPE=CHECKBOX NAME="prt" STYLE="vertical-align:middle"> <SPAN STYLE="vertical-align:middle">書き込み禁止</SPAN></LABEL> <INPUT TYPE=BUTTON NAME="sav" VALUE="保存" DISABLED onClick="save_fd(0)" STYLE="vertical-align:middle"> </TD></TR> <TR><TD NOWRAP> <SPAN STYLE="vertical-align:middle; margin-right:0.5em">FD ドライブ 2</SPAN> <SPAN STYLE="display:inline-block; line-height:0; vertical-align:middle"> <SPAN STYLE="display:inline-block; height:0; overflow:hidden; vertical-align:top"> <INPUT TYPE=BUTTON VALUE="マウント
取り外し" STYLE="visibility:hidden"> </SPAN> <BR> <INPUT TYPE=BUTTON NAME="mnt" VALUE="マウント" onClick="mount_fd(1)" STYLE="width:100%"> </SPAN> <SPAN NAME="nam" STYLE="white-space:pre; display:inline-block; width:10em; vertical-align:middle; padding-left:2px; padding-right:2px; overflow:hidden; text-overflow:ellipsis; color:black; background-color:white"> </SPAN> <LABEL><INPUT TYPE=CHECKBOX NAME="prt" STYLE="vertical-align:middle"> <SPAN STYLE="vertical-align:middle">書き込み禁止</SPAN></LABEL> <INPUT TYPE=BUTTON NAME="sav" VALUE="保存" DISABLED onClick="save_fd(1)" STYLE="vertical-align:middle"> </TD></TR> <TR><TD NOWRAP> <SPAN STYLE="margin-right:1em">速度</SPAN> 遅い<INPUT TYPE=RANGE MIN="0" MAX="0.1" STEP="any" VALUE="0.1" onInput="run_delay = 0.1 - Number(this.value)">速い </TD></TR> <TR><TD NOWRAP> <LABEL><INPUT TYPE=CHECKBOX onClick="video_composite = this.checked" STYLE="vertical-align:middle"> <SPAN STYLE="vertical-align:middle">コンポジット モニタ(グラフィック モードのみ)</SPAN></LABEL> </TD></TR> <TR><TD NOWRAP> <BR> <INPUT TYPE=BUTTON ID="rst" VALUE="リセット" onClick="boot()"> <INPUT TYPE=BUTTON ID="hlt" VALUE="停止" onClick="halt()" STYLE="margin-left:1em"> <INPUT TYPE=BUTTON VALUE="空ディスク イメージ作成" onClick="new_fd()" STYLE="margin-left:5em"> </TD></TR> </TABLE> </FORM> </CENTER> <DIV ID="nfd" STYLE="position:absolute; left:0; top:0; padding:15px 20px; text-align:left; color:black; background-color:white; border:solid #990000 4px; border-radius:3px; visibility:hidden"> 新しいディスク イメージを作成します。<BR><BR> <FORM> ディスク容量 <SELECT ID="nfd_fmt" STYLE="margin-left:2px"> <OPTION SELECTED>160 KB <OPTION>180 KB <OPTION>200 KB <OPTION>320 KB <OPTION>360 KB <OPTION>400 KB <OPTION>720 KB <OPTION>1.44 MB </SELECT> <BR><BR> <LABEL><INPUT TYPE=RADIO NAME="nfd_grp" ID="nfd_drv1" CHECKED> FD ドライブ 1 にマウントする</LABEL><BR> <LABEL><INPUT TYPE=RADIO NAME="nfd_grp"> FD ドライブ 2 にマウントする</LABEL><BR> <LABEL><INPUT TYPE=RADIO NAME="nfd_grp" ID="nfd_fil"> ファイルに保存する</LABEL><BR> <BR> <INPUT TYPE=BUTTON VALUE="作成" onClick="new_fd_go()" STYLE="margin-left:2em"> <INPUT TYPE=BUTTON VALUE="キャンセル" onClick="new_fd_hide()" STYLE="margin-left:1em"> </FORM> </DIV> <A ID="dl" STYLE="display:none"></A><!--FD イメージ保存処理用--> <IFRAME ID="tmr1" WIDTH=1 HEIGHT=1 SRC="about:blank" STYLE="display:none"></IFRAME><!--新タイマー取得用--> <IFRAME ID="tmr2" WIDTH=1 HEIGHT=1 SRC="about:blank" STYLE="display:none"></IFRAME> <SCRIPT TYPE="text/javascript"> <!-- // IBM PC エミュレータ Firefox 43 以上用 // Firefox 59 以上では,Firefox の設定(about:config)の“privacy.reduceTimerPrecision” // の値を false に変更する. const INIT_MODE = 3; // 初期ビデオ モード const AUDIO_GAIN = 0.3; // オーディオ ゲイン // ディスク イメージ サイズ バイト数 disk_size = [ 163840, // 160K 184320, // 180K 204800, // 200K 327680, // 320K 368640, // 360K 409600, // 400K 737280, // 720K 1474560 // 1.44M ]; // ディスク シリンダ数,ヘッド数,セクタ数,セクタ長 disk_spec = [ [40, 1, 8, 512], // 160K [40, 1, 9, 512], // 180K [40, 1, 10, 512], // 200K [40, 2, 8, 512], // 320K [40, 2, 9, 512], // 360K [40, 2, 10, 512], // 400K [80, 2, 9, 512], // 720K [80, 2, 18, 512] // 1.44M ]; elem_frm = document.getElementById("frm"); elem_scr = document.getElementById("scr"); elem_dscr = document.getElementById("dscr"); elem_mnt = document.getElementsByName("mnt"); elem_nam = document.getElementsByName("nam"); elem_prt = document.getElementsByName("prt"); elem_sav = document.getElementsByName("sav"); elem_rst = document.getElementById("rst"); elem_hlt = document.getElementById("hlt"); elem_nfd = document.getElementById("nfd"); elem_dl = document.getElementById("dl"); elem_tmr1 = document.getElementById("tmr1"); elem_tmr2 = document.getElementById("tmr2"); // ページを再ロードしたときのため document.forms[0].reset(); elem_mnt[0].disabled = elem_mnt[1].disabled = false; elem_sav[0].disabled = elem_sav[1].disabled = true; elem_rst.disabled = elem_hlt.disabled = false; document.forms[1].reset(); // 画面 ctx_scr = elem_scr.getContext("2d"); ctx_scr.fillStyle = "black"; err = true; elem_scr.width = 1000; elem_scr.height = 1000; img_width = ctx_scr.getImageData(0, 0, 1000, 1).width; img_height = ctx_scr.getImageData(0, 0, 1, 1000).height; elem_scr.width = width320 = Math.round(320000 / img_width); elem_scr.height = height200 = Math.round(200000 / img_height); if(ctx_scr.getImageData(0, 0, width320, 1).width == 320 && ctx_scr.getImageData(0, 0, 1, height200).height == 200) { elem_scr.width = width640 = Math.round(640000 / img_width); if(ctx_scr.getImageData(0, 0, width640, 1).width == 640) { elem_scr.width = width720 = Math.round(720000 / img_width); elem_scr.height = height350 = Math.round(350000 / img_height); if(ctx_scr.getImageData(0, 0, width720, 1).width == 720 && ctx_scr.getImageData(0, 0, 1, height350).height == 350) err = false; } } delete img_width; delete img_height; if(err) { alert("画面サイズと同じ大きさのピクセル データを持つ Canvas を作成できません。"); elem_rst.disabled = elem_hlt.disabled = true; } else { if(INIT_MODE == 7) { // 初期ビデオ モード MDA width = width720; hheight = height350; } else { width = width640; height = height200 << 1; } elem_frm.style.width = elem_dscr.style.width = String(width) + "px"; elem_frm.style.height = elem_dscr.style.height = String(height) + "px"; delete width; delete height; } delete err; // オーディオ with(aud_ctx = new AudioContext()) { // ビープ音発生用 beep_osc = createOscillator(); beep_osc.type = "square"; beep_osc.start(0); beep_osc_gain = createGain(); beep_osc_gain.gain.value = AUDIO_GAIN; beep_osc.connect(beep_osc_gain); // スピーカー直接出力用 spk_out = createScriptProcessor(512, 0, 1); spk_out_gain = createGain(); spk_out_gain.gain.value = AUDIO_GAIN * 2; lpf1 = createBiquadFilter(); lpf1.type = "lowpass"; lpf1.frequency.value = 4000; lpf1.Q.value = 1; lpf1.connect(spk_out_gain); lpf2 = createBiquadFilter(); lpf2.type = "lowpass"; lpf2.frequency.value = 4000; lpf2.Q.value = 1; lpf2.connect(lpf1); spk_out.connect(lpf2); delete lpf1; delete lpf2; spk_out_rate = sampleRate / 1000; } spk_out_buff = new Float64Array(2048); spk_out_int = 512 / spk_out_rate; run_state = false; run_delay = 0; addEventListener("message", run, false); // FD イメージ マウント/取り外し function mount_fd(i_drive) { if(mnt_image[i_drive] == undefined) { // マウント mount_i_drive = i_drive; elem_mnt[0].disabled = elem_mnt[1].disabled = true; setTimeout(enable_mnt, 100); elem_file = document.createElement("INPUT"); elem_file.type = "FILE"; elem_file.accept = ".img,.ima"; elem_file.onchange = mount_fd_sel; elem_file.click(); } else { // 取り外し if(mnt_dirty[i_drive]) { if(!confirm("ディスク イメージが更新されていますが、取り外してもよいですか?")) return; mnt_dirty[i_drive] = false; elem_nam[i_drive].style.color = "black"; } mnt_image[i_drive] = undefined; elem_nam[i_drive].textContent = " "; elem_mnt[i_drive].value = "マウント"; elem_sav[i_drive].disabled = true; } } function enable_mnt() { elem_mnt[0].disabled = elem_mnt[1].disabled = false; } function mount_fd_sel() { file_reader = new FileReader(); file_reader.onload = mount_fd_sel2; file_reader.readAsArrayBuffer(elem_file.files.item(0)); elem_mnt[0].disabled = elem_mnt[1].disabled = true; } function mount_fd_sel2() { elem_mnt[0].disabled = elem_mnt[1].disabled = false; var image = file_reader.result; delete file_reader; var i_size; if((i_size = disk_size.indexOf(image.byteLength)) < 0) { alert("対応していないディスク形式です。"); return; } mount_image(mount_i_drive, new Uint8Array(image), i_size, elem_file.files.item(0).name); } // FD イメージ保存 function save_fd(i_drive) { with(elem_dl) { download = elem_nam[i_drive].textContent; if(href != "") URL.revokeObjectURL(href); href = URL.createObjectURL(new Blob([mnt_image[i_drive]], {type:"application/octet-stream"})); click(); } } // リセット function boot() { if(run_state) halt_wait(boot_2); else boot_2(); } function boot_2() { if(mnt_image[0] == undefined && mnt_image[1] == undefined) { elem_dscr.style.width = elem_frm.style.width; elem_dscr.style.height = elem_frm.style.height; elem_dscr.style.display = "inline-block"; elem_scr.style.display = "none"; return; } elem_tmr1.onload = boot_3; elem_tmr1.src = "about:blank"; elem_rst.disabled = true; } function boot_3() { elem_rst.disabled = false; var i_drive = (mnt_image[0] == undefined) ? 1 : 0; timer = elem_tmr1.contentWindow.performance; timer_sw = false; elem_tmr1.onload = elem_tmr2.onload = reset_timer; mem.fill(0); bios_init(); elem_scr.style.display = ""; elem_dscr.style.display = "none"; mem.set(new Uint8Array(mnt_image[i_drive].buffer, 0, mnt_spec[i_drive][3]), 0x7c00); // ブート セクタ es4 = ds4 = 0x00000; regs[I_DL] = i_drive; flag_o = flag_d = flag_t = flag_s = flag_z = flag_a = flag_p = flag_c = 0; flag_i = 1; seg4_ovr = -1; cs4 = 0x00000; ip = 0x7c00; // PC booter の中にはスタックを初期化しないものがあるので, // スタックをセットしておく. ss4 = 0x00300; set_reg2(I_SP, 0x0100); run_break = undefined; run_wait = 0; run_pause = false; run_halt = false; run_hwint_ss = -1; run_icnt = 0; postMessage(undefined, "*"); run_state = true; } // 停止 function halt() { if(run_state) halt_wait(halt_2); } function halt_2() { elem_dscr.style.width = elem_frm.style.width; elem_dscr.style.height = elem_frm.style.height; elem_dscr.style.display = "inline-block"; elem_scr.style.display = "none"; } // エミュレーション停止 function halt_wait(cb) { switch(sound_state) { case 1: // ビープ音発生中 beep_osc_gain.disconnect(); break; case 2: // スピーカー直接出力中 spk_out.onaudioprocess = null; spk_out_gain.disconnect(); break; } run_break = cb; elem_rst.disabled = elem_hlt.disabled = true; } // ディスク イメージ作成 function new_fd() { removeEventListener("keydown", keyboard_event, false); removeEventListener("keyup", keyboard_event, false); addEventListener("keydown", new_fd_modal, false); addEventListener("keyup", new_fd_modal, false); addEventListener("mousedown", new_fd_modal, false); addEventListener("mouseup", new_fd_modal, false); addEventListener("mousemove", new_fd_modal, false); addEventListener("click", new_fd_modal, false); with(elem_nfd.style) { left = String((document.documentElement.scrollWidth - elem_nfd.offsetWidth) >> 1) + "px"; top = String((document.documentElement.scrollHeight - elem_nfd.offsetHeight) >> 1) + "px"; visibility = ""; } } // 作成 function new_fd_go() { var i_size = document.getElementById("nfd_fmt").selectedIndex; var image = new Uint8Array(disk_size[i_size]); if(document.getElementById("nfd_fil").checked) { // ファイルに保存する with(elem_dl) { download = "unnamed.img"; if(href != "") URL.revokeObjectURL(href); href = URL.createObjectURL(new Blob([image], {type:"application/octet-stream"})); click(); } } else { // FD ドライブにマウントする var i_drive = (document.getElementById("nfd_drv1").checked) ? 0 : 1; if(mnt_image[i_drive] != undefined) { if(mnt_dirty[i_drive]) { if(!confirm("FD ドライブ " + String(i_drive + 1) + " のディスク イメージは更新されていますが、取り外してもよいですか?")) return; mnt_dirty[i_drive] = false; elem_nam[i_drive].style.color = "black"; } } mount_image(i_drive, image, i_size, "unnamed.img"); } new_fd_hide(); } // 閉じる function new_fd_hide() { with(elem_nfd.style) { visibility = "hidden"; left = top = "0"; } removeEventListener("keydown", new_fd_modal, false); removeEventListener("keyup", new_fd_modal, false); removeEventListener("mousedown", new_fd_modal, false); removeEventListener("mouseup", new_fd_modal, false); removeEventListener("mousemove", new_fd_modal, false); removeEventListener("click", new_fd_modal, false); addEventListener("keydown", keyboard_event, false); addEventListener("keyup", keyboard_event, false); } function new_fd_modal(e) { if(!elem_nfd.contains(e.target) && e.target != elem_dl) { e.stopPropagation(); e.preventDefault(); } } // FD イメージ マウント function mount_image(i_drive, image, i_size, name) { mnt_image[i_drive] = image; mnt_spec[i_drive] = disk_spec[i_size]; elem_nam[i_drive].textContent = name; elem_mnt[i_drive].value = "取り外し"; elem_sav[i_drive].disabled = false; } // エミュレーション実行 function run(e) { if(e.source == window) { var cnt = 512; for(; ; ) { for(; cnt; cnt--) { if(run_break != undefined) { run_state = false; elem_rst.disabled = elem_hlt.disabled = false; run_break(); return; } // ディレイ if(run_delay) { if(!(cnt & 0x7)) { for(var to = timer.now() + run_delay; timer.now() < to; ) ; } } timer_now = timer.now(); if(ss4 == run_hwint_ss && regs_2[I_SP_2] == run_hwint_sp) // ハードウェア割り込み処理呼び出し終了 run_hwint_ss = -1; // CRT 帰線区間のエミュレート & 画面更新 if(timer_now >= video_line_next) { if(!--video_line_cnt) { video_line_cnt = video_lines; video_frame_cnt = (video_frame_cnt + 1) & 0x1f; if(!(video_frame_cnt & 0x7)) // 8 フレーム毎に画面を定期更新する video_refresh_req1 = true; // 画面更新要求 } if(video_color_chg >= 0) { // カラー パレット変更中 video_refresh_1l(); if(video_line_cnt == video_color_chg) // カラー パレット変更終了 video_color_chg = -1; } // CGA の CRTC のクロックとタイマーのクロックは同じ発振器の出力を元にしているため, // 完全に同期する. // そのことを前提としているプログラムがあるので,浮動小数点数の誤差の累積によって // 両者の同期が次第にずれていくことが無いように注意する必要がある. video_line_osc_cnt += video_line_osc_n; video_line_next = video_line_org + video_line_osc_cnt * video_line_osc_int; } // キー リピート if(timer_now >= keyboard_rep_next) { if(keyboard_input.length < 4) keyboard_input.push(keyboard_rep_data); keyboard_rep_next += keyboard_rep_rate; } if(timer_now >= timer_0_next) { // システム タイマー割り込み要求 // 実際のハードウェアでは,前の割り込み要求が受け付けられていない状態では割り込み要求は // 捨てられると思う. // タイマー割り込みを取りこぼすと処理に影響があるようなプログラムは,次の割り込み要求が // 発生する前に割り込みを受け付けるように作られているはず. // しかし,このエミュレータでは,バックグラウンドでガーベッジ コレクションが走った場合 // など,次の割り込み要求までに割り込みが受け付けられないことが容易に起こり得る. // そのため,割り込み要求を受け付けることができない場合には,割り込み要求を保留するよう // にして,取りこぼしが起きないようにする. if(!(pic_m_irr & 0x01) // 遅延がどんどん累積していく場合はあきらめる || timer_now > timer_0_next + 200) { pic_m_irr |= 0x01; timer_0_update(); } } if(keyboard_input.length) // キーボード割り込み要求 pic_m_irr |= 0x02; if((pic_m_irr & 0x01) && !(pic_m_imr & 0x01) && !(pic_m_isr & 0x01) && flag_i) { // システム タイマー割り込み interval_int(); } else if((pic_m_irr & 0x02) && !(pic_m_imr & 0x02) && !(pic_m_isr & 0x03) && flag_i) { // キーボード割り込み if(keyboard_int()) continue; } if(run_hwint_ss < 0) { // ハードウェア割り込み処理呼び出し中でない // 画面更新 if(video_refresh_req1) { if(video_color_chg < 0) // カラー パレット変更中でない video_refresh(); video_refresh_req2 = true; video_refresh_req1 = false; } else { if(video_refresh_req2) { if(!video_disabled) ctx_scr.putImageData(video_data, 0, 0); video_refresh_req2 = false; } } if(run_pause) // Pause 中 continue; if(run_wait) { // 待機中 switch(run_wait) { case 0x10001: // BEL コードによるビープ終了 if(bell_ended()) continue; break; case 0x10002: // 入力待ち & バッファに入力あり run_wait = 0; op_popf(); regs[I_AH] = keyboard_wait_func; bios_keyboard(); if(run_wait) // 入力待ち continue; break; default: continue; } } } if(run_halt) // HLT 命令により停止中 continue; // デフォルトの割り込み処理 if(cs4 == 0xf0000) { switch(ip) { case INT_08_OFF: interval_int_def(); break; case INT_08_OFF_2: interval_int_def_2(); break; case INT_09_OFF: keyboard_int_def(); break; case INT_13_OFF: bios_disk_int_def(); break; } } var code; for(; ; ) { if(((code = get_code1()) & 0xe7) != 0x26) // セグメント オーバーライドでない break; seg4_ovr = code & 0x18; } op_tbl[code](); seg4_ovr = -1; } // 割り込み禁止状態のときは,ビジー ウェイトでタイミングを取っている場合があるので, // ブラウザに制御を戻すのを遅延させる. if(flag_i) break; if(run_icnt == 24) break; cnt = 64; run_icnt++; } if(timer_now >= 1000000) { // タイマー再取得 ((timer_sw) ? elem_tmr1 : elem_tmr2).src = "about:blank"; return; } postMessage(undefined, "*"); } } function reset_timer(e) { var new_timer = e.target.contentWindow.performance; var now; var delta = (now = timer.now()) - new_timer.now(); timer = new_timer; timer_sw = !timer_sw; video_line_next -= delta; video_line_org = video_line_next - video_line_osc_n * video_line_osc_int; video_line_osc_cnt = video_line_osc_n; if(keyboard_rep_next < Number.MAX_VALUE) keyboard_rep_next -= delta; if(timer_0_next < Number.MAX_VALUE) { timer_0_next -= delta; timer_0_org = timer_0_next - timer_0_count * OSC_INT; timer_0_osc_cnt = timer_0_count; } if(timer_2_org < Number.MAX_VALUE) timer_2_org = now - (now - timer_2_org) % (timer_2_count * OSC_INT) - delta; timer_2_gate_off -= delta; if(sound_state == 2) { // スピーカー直接出力中 spk_out_time -= delta; spk_out_put_prev -= delta; spk_out_get_prev -= delta; } postMessage(undefined, "*"); } // ---- 8086 エミュレーション ---- mem = new Uint8Array(0x100000 + 16); // メモリ + レジスタ mem_s = new Int8Array(mem.buffer); // メモリ/レジスタ 符号付きアクセス用 regs = new Uint8Array(mem.buffer, 0x100000, 16); // レジスタ アクセス用 regs_s = new Int8Array(mem.buffer, 0x100000, 16); // レジスタ 符号付きアクセス用 regs_2 = new Uint16Array(mem.buffer, 0x100000, 8); // レジスタ 2 バイト アクセス用 // (実行環境により endian は異なる) // レジスタ インデックス const I_AX = 0; const I_CX = 2; const I_DX = 4; const I_BX = 6; const I_SP = 8; const I_BP = 10; const I_SI = 12; const I_DI = 14; const I_AL = 0; const I_CL = 2; const I_DL = 4; const I_BL = 6; const I_AH = 1; const I_CH = 3; const I_DH = 5; const I_BH = 7; const I_SP_L = 8; const I_SP_H = 9; const I_BP_L = 10; const I_BP_H = 11; const I_SI_L = 12; const I_SI_H = 13; const I_DI_L = 14; const I_DI_H = 15; // レジスタ インデックス 2 バイト アクセス用 const I_AX_2 = 0; const I_CX_2 = 1; const I_DX_2 = 2; const I_BX_2 = 3; const I_SP_2 = 4; const I_BP_2 = 5; const I_SI_2 = 6; const I_DI_2 = 7; // 命令処理関数テーブル op_tbl = [ op_add_s1, op_add_s2, op_add_d1, op_add_d2, op_add_im1, op_add_im2, op_push_es, op_pop_es, op_or_s1, op_or_s2, op_or_d1, op_or_d2, op_or_im1, op_or_im2, op_push_cs, op_nop, op_adc_s1, op_adc_s2, op_adc_d1, op_adc_d2, op_adc_im1, op_adc_im2, op_push_ss, op_pop_ss, op_sbb_s1, op_sbb_s2, op_sbb_d1, op_sbb_d2, op_sbb_im1, op_sbb_im2, op_push_ds, op_pop_ds, op_and_s1, op_and_s2, op_and_d1, op_and_d2, op_and_im1, op_and_im2, , op_daa, op_sub_s1, op_sub_s2, op_sub_d1, op_sub_d2, op_sub_im1, op_sub_im2, , op_das, op_xor_s1, op_xor_s2, op_xor_d1, op_xor_d2, op_xor_im1, op_xor_im2, , op_aaa, op_cmp_s1, op_cmp_s2, op_cmp_d1, op_cmp_d2, op_cmp_im1, op_cmp_im2, , op_aas, op_inc_ax, op_inc_cx, op_inc_dx, op_inc_bx, op_inc_sp, op_inc_bp, op_inc_si, op_inc_di, op_dec_ax, op_dec_cx, op_dec_dx, op_dec_bx, op_dec_sp, op_dec_bp, op_dec_si, op_dec_di, op_push_ax, op_push_cx, op_push_dx, op_push_bx, op_push_sp, op_push_bp, op_push_si, op_push_di, op_pop_ax, op_pop_cx, op_pop_dx, op_pop_bx, op_pop_sp, op_pop_bp, op_pop_si, op_pop_di, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_jo, op_jno, op_jb, op_jnb, op_je, op_jne, op_jbe, op_jnbe, op_js, op_jns, op_jp, op_jnp, op_jl, op_jnl, op_jle, op_jnle, op_misc11, op_misc12, op_misc21, op_misc22, op_test1, op_test2, op_xchg1, op_xchg2, op_mov_s1, op_mov_s2, op_mov_d1, op_mov_d2, op_mov_seg_s, op_lea, op_mov_seg_d, op_misc3, op_nop, op_xchg_cx, op_xchg_dx, op_xchg_bx, op_xchg_sp, op_xchg_bp, op_xchg_si, op_xchg_di, op_cbw, op_cwd, op_call_far, op_nop, op_pushf, op_popf, op_sahf, op_lahf, op_mov_l1, op_mov_l2, op_mov_t1, op_mov_t2, op_movsb, op_movsw, op_cmpsb, op_cmpsw, op_test_im1, op_test_im2, op_stosb, op_stosw, op_lodsb, op_lodsw, op_scasb, op_scasw, op_mov_al_im, op_mov_cl_im, op_mov_dl_im, op_mov_bl_im, op_mov_ah_im, op_mov_ch_im, op_mov_dh_im, op_mov_bh_im, op_mov_ax_im, op_mov_cx_im, op_mov_dx_im, op_mov_bx_im, op_mov_sp_im, op_mov_bp_im, op_mov_si_im, op_mov_di_im, op_nop, op_nop, op_ret_n, op_ret, op_les, op_lds, op_mov_m_im1, op_mov_m_im2, op_nop, op_nop, op_retf_n, op_retf, op_int_3, op_int_n, op_into, op_iret, op_rs_1_1, op_rs_1_2, op_rs_n_1, op_rs_n_2, op_aam, op_aad, op_nop, op_xlat, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_nop, op_loopne, op_loope, op_loop, op_jcxz, op_in_1, op_in_2, op_out_1, op_out_2, op_call_near, op_jmp_near, op_jmp_far, op_jmp, op_in_dx1, op_in_dx2, op_out_dx1, op_out_dx2, op_nop, op_nop, op_repne, op_repe, op_hlt, op_cmc, op_misc4, op_misc5, op_clc, op_stc, op_cli, op_sti, op_cld, op_std, op_misc6, op_misc7 ]; function op_nop() { } // ADD REG8/MEM8,REG8 function op_add_s1() { decode_mrm(-1, 0); mem[dat_i_mem0] = add_1(mem[dat_i_mem0], regs[mrm_i_reg], 0); } // ADD REG16/MEM16,REG16 function op_add_s2() { decode_mrm(-1, 1); set_dat_mem2(add_2(get_dat_mem2(), get_mrm_reg2(), 0)); } // ADD REG8,REG8/MEM8 function op_add_d1() { decode_mrm(-1, 0); regs[mrm_i_reg] = add_1(regs[mrm_i_reg], mem[dat_i_mem0], 0); } // ADD REG16,REG16/MEM16 function op_add_d2() { decode_mrm(-1, 1); set_mrm_reg2(add_2(get_mrm_reg2(), get_dat_mem2(), 0)); } // ADD AL,IMMED8 function op_add_im1() { regs[I_AL] = add_1(regs[I_AL], get_code1(), 0); } // ADD AX,IMMED16 function op_add_im2() { set_reg2(I_AX, add_2(get_reg2(I_AX), get_code2(), 0)); } // PUSH ES function op_push_es() { push(es4 >> 4); } // POP ES function op_pop_es() { es4 = pop() << 4; } // OR REG8/MEM8,REG8 function op_or_s1() { decode_mrm(-1, 0); flags_log1(mem[dat_i_mem0] |= regs[mrm_i_reg]); } // OR REG16/MEM16,REG16 function op_or_s2() { decode_mrm(-1, 1); flags_log2(mem[dat_i_mem0] |= regs[mrm_i_reg], mem[dat_i_mem1] |= regs[mrm_i_reg | 1]); } // OR REG8,REG8/MEM8 function op_or_d1() { decode_mrm(-1, 0); flags_log1(regs[mrm_i_reg] |= mem[dat_i_mem0]); } // OR REG16,REG16/MEM16 function op_or_d2() { decode_mrm(-1, 1); flags_log2(regs[mrm_i_reg] |= mem[dat_i_mem0], regs[mrm_i_reg | 1] |= mem[dat_i_mem1]); } // OR AL,IMMED8 function op_or_im1() { flags_log1(regs[I_AL] |= get_code1()); } // OR AX,IMMED16 function op_or_im2() { get_code2_2(); flags_log2(regs[I_AL] |= mem[cod_i_mem0], regs[I_AH] |= mem[cod_i_mem1]); } // PUSH CS function op_push_cs() { push(cs4 >> 4); } // ADC REG8/MEM8,REG8 function op_adc_s1() { decode_mrm(-1, 0); mem[dat_i_mem0] = add_1(mem[dat_i_mem0], regs[mrm_i_reg], flag_c); } // ADC REG16/MEM16,REG16 function op_adc_s2() { decode_mrm(-1, 1); set_dat_mem2(add_2(get_dat_mem2(), get_mrm_reg2(), flag_c)); } // ADC REG8,REG8/MEM8 function op_adc_d1() { decode_mrm(-1, 0); regs[mrm_i_reg] = add_1(regs[mrm_i_reg], mem[dat_i_mem0], flag_c); } // ADC REG16,REG16/MEM16 function op_adc_d2() { decode_mrm(-1, 1); set_mrm_reg2(add_2(get_mrm_reg2(), get_dat_mem2(), flag_c)); } // ADC AL,IMMED8 function op_adc_im1() { regs[I_AL] = add_1(regs[I_AL], get_code1(), flag_c); } // ADC AX,IMMED16 function op_adc_im2() { set_reg2(I_AX, add_2(get_reg2(I_AX), get_code2(), flag_c)); } // PUSH SS function op_push_ss() { push(ss4 >> 4); } // POP SS function op_pop_ss() { ss4 = pop() << 4; } // SBB REG8/MEM8,REG8 function op_sbb_s1() { decode_mrm(-1, 0); mem[dat_i_mem0] = sub_1(mem[dat_i_mem0], regs[mrm_i_reg], flag_c); } // SBB REG16/MEM16,REG16 function op_sbb_s2() { decode_mrm(-1, 1); set_dat_mem2(sub_2(get_dat_mem2(), get_mrm_reg2(), flag_c)); } // SBB REG8,REG8/MEM8 function op_sbb_d1() { decode_mrm(-1, 0); regs[mrm_i_reg] = sub_1(regs[mrm_i_reg], mem[dat_i_mem0], flag_c); } // SBB REG16,REG16/MEM16 function op_sbb_d2() { decode_mrm(-1, 1); set_mrm_reg2(sub_2(get_mrm_reg2(), get_dat_mem2(), flag_c)); } // SBB AL,IMMED8 function op_sbb_im1() { regs[I_AL] = sub_1(regs[I_AL], get_code1(), flag_c); } // SBB AX,IMMED16 function op_sbb_im2() { set_reg2(I_AX, sub_2(get_reg2(I_AX), get_code2(), flag_c)); } // PUSH DS function op_push_ds() { push(ds4 >> 4); } // POP DS function op_pop_ds() { ds4 = pop() << 4; } // AND REG8/MEM8,REG8 function op_and_s1() { decode_mrm(-1, 0); flags_log1(mem[dat_i_mem0] &= regs[mrm_i_reg]); } // AND REG16/MEM16,REG16 function op_and_s2() { decode_mrm(-1, 1); flags_log2(mem[dat_i_mem0] &= regs[mrm_i_reg], mem[dat_i_mem1] &= regs[mrm_i_reg | 1]); } // AND REG8,REG8/MEM8 function op_and_d1() { decode_mrm(-1, 0); flags_log1(regs[mrm_i_reg] &= mem[dat_i_mem0]); } // AND REG16,REG16/MEM16 function op_and_d2() { decode_mrm(-1, 1); flags_log2(regs[mrm_i_reg] &= mem[dat_i_mem0], regs[mrm_i_reg | 1] &= mem[dat_i_mem1]); } // AND AL,IMMED8 function op_and_im1() { flags_log1(regs[I_AL] &= get_code1()); } // AND AX,IMMED16 function op_and_im2() { get_code2_2(); flags_log2(regs[I_AL] &= mem[cod_i_mem0], regs[I_AH] &= mem[cod_i_mem1]); } // DAA function op_daa() { var val = regs[I_AL]; if((val & 0x0f) > 0x09 || flag_a) { val = (val + 0x06) & 0xff; flag_a = 1; } if(val > 0x9f || flag_c) { val = (val + 0x60) & 0xff; flag_c = 1; } flags_szp1(regs[I_AL] = val); } // SUB REG8/MEM8,REG8 function op_sub_s1() { decode_mrm(-1, 0); mem[dat_i_mem0] = sub_1(mem[dat_i_mem0], regs[mrm_i_reg], 0); } // SUB REG16/MEM16,REG16 function op_sub_s2() { decode_mrm(-1, 1); set_dat_mem2(sub_2(get_dat_mem2(), get_mrm_reg2(), 0)); } // SUB REG8,REG8/MEM8 function op_sub_d1() { decode_mrm(-1, 0); regs[mrm_i_reg] = sub_1(regs[mrm_i_reg], mem[dat_i_mem0], 0); } // SUB REG16,REG16/MEM16 function op_sub_d2() { decode_mrm(-1, 1); set_mrm_reg2(sub_2(get_mrm_reg2(), get_dat_mem2(), 0)); } // SUB AL,IMMED8 function op_sub_im1() { regs[I_AL] = sub_1(regs[I_AL], get_code1(), 0); } // SUB AX,IMMED16 function op_sub_im2() { set_reg2(I_AX, sub_2(get_reg2(I_AX), get_code2(), 0)); } // DAS function op_das() { var val = regs[I_AL]; if((val & 0x0f) > 0x09 || flag_a) { val = (val - 0x06) & 0xff; flag_a = 1; } if(val > 0x9f || flag_c) { val = (val - 0x60) & 0xff; flag_c = 1; } flags_szp1(regs[I_AL] = val); } // XOR REG8/MEM8,REG8 function op_xor_s1() { decode_mrm(-1, 0); flags_log1(mem[dat_i_mem0] ^= regs[mrm_i_reg]); } // XOR REG16/MEM16,REG16 function op_xor_s2() { decode_mrm(-1, 1); flags_log2(mem[dat_i_mem0] ^= regs[mrm_i_reg], mem[dat_i_mem1] ^= regs[mrm_i_reg | 1]); } // XOR REG8,REG8/MEM8 function op_xor_d1() { decode_mrm(-1, 0); flags_log1(regs[mrm_i_reg] ^= mem[dat_i_mem0]); } // XOR REG16,REG16/MEM16 function op_xor_d2() { decode_mrm(-1, 1); flags_log2(regs[mrm_i_reg] ^= mem[dat_i_mem0], regs[mrm_i_reg | 1] ^= mem[dat_i_mem1]); } // XOR AL,IMMED8 function op_xor_im1() { flags_log1(regs[I_AL] ^= get_code1()); } // XOR AX,IMMED16 function op_xor_im2() { get_code2_2(); flags_log2(regs[I_AL] ^= mem[cod_i_mem0], regs[I_AH] ^= mem[cod_i_mem1]); } // AAA function op_aaa() { var val = regs[I_AL]; if((val & 0x0f) > 0x09 || flag_a) { val += 0x06; regs[I_AH] = (regs[I_AH] + 1) & 0xff; flag_a = 1; } flag_c = flag_a; regs[I_AL] = val & 0x0f; } // CMP REG8/MEM8,REG8 function op_cmp_s1() { decode_mrm(-1, 0); sub_1(mem[dat_i_mem0], regs[mrm_i_reg], 0); } // CMP REG16/MEM16,REG16 function op_cmp_s2() { decode_mrm(-1, 1); sub_2(get_dat_mem2(), get_mrm_reg2(), 0); } // CMP REG8,REG8/MEM8 function op_cmp_d1() { decode_mrm(-1, 0); sub_1(regs[mrm_i_reg], mem[dat_i_mem0], 0); } // CMP REG16,REG16/MEM16 function op_cmp_d2() { decode_mrm(-1, 1); sub_2(get_mrm_reg2(), get_dat_mem2(), 0); } // CMP AL,IMMED8 function op_cmp_im1() { sub_1(regs[I_AL], get_code1(), 0); } // CMP AX,IMMED16 function op_cmp_im2() { sub_2(get_reg2(I_AX), get_code2(), 0); } // AAS function op_aas() { var val = regs[I_AL]; if((val & 0x0f) > 0x09 || flag_a) { val -= 0x06; regs[I_AH] = (regs[I_AH] - 1) & 0xff; flag_a = 1; } flag_c = flag_a; regs[I_AL] = val & 0x0f; } // INC AX function op_inc_ax() { inc_reg2(I_AX); } // INC CX function op_inc_cx() { inc_reg2(I_CX); } // INC DX function op_inc_dx() { inc_reg2(I_DX); } // INC BX function op_inc_bx() { inc_reg2(I_BX); } // INC SP function op_inc_sp() { inc_reg2(I_SP); } // INC BP function op_inc_bp() { inc_reg2(I_BP); } // INC SI function op_inc_si() { inc_reg2(I_SI); } // INC DI function op_inc_di() { inc_reg2(I_DI); } function inc_reg2(i_reg) { var val = (get_reg2(i_reg) + 1) & 0xffff; flag_o = (val == 0x8000) ? 1 : 0; flag_a = (val & 0xf) ? 0 : 1; set_reg2(i_reg, val); flags_szp2(val); } // DEC AX function op_dec_ax() { dec_reg2(I_AX); } // DEC CX function op_dec_cx() { dec_reg2(I_CX); } // DEC DX function op_dec_dx() { dec_reg2(I_DX); } // DEC BX function op_dec_bx() { dec_reg2(I_BX); } // DEC SP function op_dec_sp() { dec_reg2(I_SP); } // DEC BP function op_dec_bp() { dec_reg2(I_BP); } // DEC SI function op_dec_si() { dec_reg2(I_SI); } // DEC DI function op_dec_di() { dec_reg2(I_DI); } function dec_reg2(i_reg) { var val = get_reg2(i_reg); flag_o = (val == 0x8000) ? 1 : 0; flag_a = (val & 0xf) ? 0 : 1; set_reg2(i_reg, val = (val - 1) & 0xffff); flags_szp2(val); } // PUSH AX function op_push_ax() { push_2(regs[I_AL], regs[I_AH]); } // PUSH CX function op_push_cx() { push_2(regs[I_CL], regs[I_CH]); } // PUSH DX function op_push_dx() { push_2(regs[I_DL], regs[I_DH]); } // PUSH BX function op_push_bx() { push_2(regs[I_BL], regs[I_BH]); } // PUSH SP function op_push_sp() { // 8086 では,SP - 2(PUSH 後の SP の値)が PUSH される var sp = (get_reg2(I_SP) - 2) & 0xffff; mem[(ss4 + sp) & 0xfffff] = regs[I_SP] = sp & 0xff; mem[(ss4 + ((sp + 1) & 0xffff)) & 0xfffff] = regs[I_SP | 1] = sp >> 8; } // PUSH BP function op_push_bp() { push_2(regs[I_BP_L], regs[I_BP_H]); } // PUSH SI function op_push_si() { push_2(regs[I_SI_L], regs[I_SI_H]); } // PUSH DI function op_push_di() { push_2(regs[I_DI_L], regs[I_DI_H]); } // POP AX function op_pop_ax() { pop_reg(I_AX); } // POP CX function op_pop_cx() { pop_reg(I_CX); } // POP DX function op_pop_dx() { pop_reg(I_DX); } // POP BX function op_pop_bx() { pop_reg(I_BX); } // POP SP function op_pop_sp() { pop_reg(I_SP); } // POP BP function op_pop_bp() { pop_reg(I_BP); } // POP SI function op_pop_si() { pop_reg(I_SI); } // POP DI function op_pop_di() { pop_reg(I_DI); } function pop_reg(i_reg) { var sp = get_reg2(I_SP); set_reg2(I_SP, (sp + 2) & 0xffff); regs[i_reg] = mem[(ss4 + sp) & 0xfffff]; regs[i_reg | 1] = mem[(ss4 + ((sp + 1) & 0xffff)) & 0xfffff]; } // JO function op_jo() { jmp_short(flag_o); } // JNO function op_jno() { jmp_short(!flag_o); } // JB function op_jb() { jmp_short(flag_c); } // JNB function op_jnb() { jmp_short(!flag_c); } // JE function op_je() { jmp_short(flag_z); } // JNE function op_jne() { jmp_short(!flag_z); } // JBE function op_jbe() { jmp_short(flag_c || flag_z); } // JNBE function op_jnbe() { jmp_short(!flag_c && !flag_z); } // JS function op_js() { jmp_short(flag_s); } // JNS function op_jns() { jmp_short(!flag_s); } // JP function op_jp() { jmp_short(flag_p); } // JNP function op_jnp() { jmp_short(!flag_p); } // JL function op_jl() { jmp_short(flag_s != flag_o); } // JNL function op_jnl() { jmp_short(flag_s == flag_o); } // JLE function op_jle() { jmp_short(flag_s != flag_o || flag_z); } // JNLE function op_jnle() { jmp_short(flag_s == flag_o && !flag_z); } // ADD/OR/ADC/SBB/AND/SUB/XOR/CMP REG8/MEM8,IMMED8 function op_misc11() { var op2; decode_mrm(op2 = get_code1(), 0); switch(op2 & 0x38) { case 0x00: // ADD mem[dat_i_mem0] = add_1(mem[dat_i_mem0], get_code1(), 0); break; case 0x08: // OR flags_log1(mem[dat_i_mem0] |= get_code1()); break; case 0x10: // ADC mem[dat_i_mem0] = add_1(mem[dat_i_mem0], get_code1(), flag_c); break; case 0x18: // SBB mem[dat_i_mem0] = sub_1(mem[dat_i_mem0], get_code1(), flag_c); break; case 0x20: // AND flags_log1(mem[dat_i_mem0] &= get_code1()); break; case 0x28: // SUB mem[dat_i_mem0] = sub_1(mem[dat_i_mem0], get_code1(), 0); break; case 0x30: // XOR flags_log1(mem[dat_i_mem0] ^= get_code1()); break; default: // CMP sub_1(mem[dat_i_mem0], get_code1(), 0); } } // ADD/OR/ADC/SBB/AND/SUB/XOR/CMP REG16/MEM16,IMMED16 function op_misc12() { var op2; decode_mrm(op2 = get_code1(), 1); switch(op2 & 0x38) { case 0x00: // ADD set_dat_mem2(add_2(get_dat_mem2(), get_code2(), 0)); break; case 0x08: // OR get_code2_2(); flags_log2(mem[dat_i_mem0] |= mem[cod_i_mem0], mem[dat_i_mem1] |= mem[cod_i_mem1]); break; case 0x10: // ADC set_dat_mem2(add_2(get_dat_mem2(), get_code2(), flag_c)); break; case 0x18: // SBB set_dat_mem2(sub_2(get_dat_mem2(), get_code2(), flag_c)); break; case 0x20: // AND get_code2_2(); flags_log2(mem[dat_i_mem0] &= mem[cod_i_mem0], mem[dat_i_mem1] &= mem[cod_i_mem1]); break; case 0x28: // SUB set_dat_mem2(sub_2(get_dat_mem2(), get_code2(), 0)); break; case 0x30: // XOR get_code2_2(); flags_log2(mem[dat_i_mem0] ^= mem[cod_i_mem0], mem[dat_i_mem1] ^= mem[cod_i_mem1]); break; default: // CMP sub_2(get_dat_mem2(), get_code2(), 0); } } // ADD/ADC/SBB/SUB/CMP REG8/MEM8,IMMED8 function op_misc21() { var op2; switch((op2 = get_code1()) & 0x38) { case 0x00: // ADD decode_mrm(op2, 0); mem[dat_i_mem0] = add_1(mem[dat_i_mem0], get_code1(), 0); break; case 0x10: // ADC decode_mrm(op2, 0); mem[dat_i_mem0] = add_1(mem[dat_i_mem0], get_code1(), flag_c); break; case 0x18: // SBB decode_mrm(op2, 0); mem[dat_i_mem0] = sub_1(mem[dat_i_mem0], get_code1(), flag_c); break; case 0x28: // SUB decode_mrm(op2, 0); mem[dat_i_mem0] = sub_1(mem[dat_i_mem0], get_code1(), 0); break; case 0x38: // CMP decode_mrm(op2, 0); sub_1(mem[dat_i_mem0], get_code1(), 0); break; } } // ADD/ADC/SBB/SUB/CMP REG16/MEM16,IMMED8 // OR/AND/XOR/ REG16/MEM16,IMMED8(命令表に無いが,使っているプログラムがある) function op_misc22() { var op2; decode_mrm(op2 = get_code1(), 1); var val; switch(op2 & 0x38) { case 0x00: // ADD set_dat_mem2(add_2(get_dat_mem2(), get_code_sx(), 0)); break; case 0x08: // OR val = get_code1(); flags_log2(mem[dat_i_mem0] |= val, (val & 0x80) ? mem[dat_i_mem1] = 0xff : mem[dat_i_mem1]); break; case 0x10: // ADC set_dat_mem2(add_2(get_dat_mem2(), get_code_sx(), flag_c)); break; case 0x18: // SBB set_dat_mem2(sub_2(get_dat_mem2(), get_code_sx(), flag_c)); break; case 0x20: // AND val = get_code1(); flags_log2(mem[dat_i_mem0] &= val, (val & 0x80) ? mem[dat_i_mem1] : mem[dat_i_mem1] = 0x00); break; case 0x28: // SUB set_dat_mem2(sub_2(get_dat_mem2(), get_code_sx(), 0)); break; case 0x30: // XOR val = get_code1(); flags_log2(mem[dat_i_mem0] ^= val, (val & 0x80) ? mem[dat_i_mem1] ^= 0xff : mem[dat_i_mem1]); break; default: // CMP sub_2(get_dat_mem2(), get_code_sx(), 0); } } // TEST REG8,REG8/MEM8 function op_test1() { decode_mrm(-1, 0); flags_log1(regs[mrm_i_reg] & mem[dat_i_mem0]); } // TEST REG16,REG16/MEM16 function op_test2() { decode_mrm(-1, 1); flags_log2(regs[mrm_i_reg] & mem[dat_i_mem0], regs[mrm_i_reg | 1] & mem[dat_i_mem1]); } // XCHG REG8,REG8/MEM8 function op_xchg1() { decode_mrm(-1, 0); var w = regs[mrm_i_reg]; regs[mrm_i_reg] = mem[dat_i_mem0]; mem[dat_i_mem0] = w; } // XCHG REG16,REG16/MEM16 function op_xchg2() { decode_mrm(-1, 1); var w = regs[mrm_i_reg]; regs[mrm_i_reg] = mem[dat_i_mem0]; mem[dat_i_mem0] = w; w = regs[mrm_i_reg | 1]; regs[mrm_i_reg | 1] = mem[dat_i_mem1]; mem[dat_i_mem1] = w; } // MOV REG8/MEM8,REG8 function op_mov_s1() { decode_mrm(-1, 0); mem[dat_i_mem0] = regs[mrm_i_reg]; } // MOV REG16/MEM16,REG16 function op_mov_s2() { decode_mrm(-1, 1); mem[dat_i_mem0] = regs[mrm_i_reg]; mem[dat_i_mem1] = regs[mrm_i_reg | 1]; } // MOV REG8,REG8/MEM8 function op_mov_d1() { decode_mrm(-1, 0); regs[mrm_i_reg] = mem[dat_i_mem0]; } // MOV REG16,REG16/MEM16 function op_mov_d2() { decode_mrm(-1, 1); regs[mrm_i_reg] = mem[dat_i_mem0]; regs[mrm_i_reg | 1] = mem[dat_i_mem1]; } // MOV REG16/MEM16,SEGREG function op_mov_seg_s() { var op2; var seg4; switch((op2 = get_code1()) & 0x38) { case 0x00: // ES seg4 = es4; break; case 0x08: // CS seg4 = cs4; break; case 0x10: // SS seg4 = ss4; break; case 0x18: // DS seg4 = ds4; break; default: return; } decode_mrm(op2, 1); set_dat_mem2(seg4 >> 4); } // LEA REG16,MEM16 function op_lea() { var mrm = get_code1(); var r_m = mrm & 0x07; var ea; switch(mrm & 0xc0) { case 0xc0: // レジスタ return; case 0x40: // 変位 1 バイト ea = get_code_sx(); break; case 0x80: // 変位 2 バイト ea = get_code2(); break; default: // 0x00 if(r_m == 0x6) { // 変位 2 バイト ea = get_code2(); r_m = 0x8; break; } // 変位なし ea = 0; } switch(r_m) { case 0x0: ea += get_reg2(I_BX) + get_reg2(I_SI); break; case 0x1: ea += get_reg2(I_BX) + get_reg2(I_DI); break; case 0x2: ea += get_reg2(I_BP) + get_reg2(I_SI); break; case 0x3: ea += get_reg2(I_BP) + get_reg2(I_DI); break; case 0x4: ea += get_reg2(I_SI); break; case 0x5: ea += get_reg2(I_DI); break; case 0x6: ea += get_reg2(I_BP); break; case 0x7: ea += get_reg2(I_BX); break; // 0x8: インデックスなし } set_reg2((mrm & 0x38) >> 2, ea & 0xffff); } // MOV SEGREG,REG16/MEM16 function op_mov_seg_d() { var op2; switch((op2 = get_code1()) & 0x38) { case 0x00: // ES decode_mrm(op2, 1); es4 = get_dat_mem2() << 4; break; case 0x08: // CS decode_mrm(op2, 1); cs4 = get_dat_mem2() << 4; break; case 0x10: // SS decode_mrm(op2, 1); ss4 = get_dat_mem2() << 4; break; case 0x18: // DS decode_mrm(op2, 1); ds4 = get_dat_mem2() << 4; break; } } // POP REG16/MEM16 function op_misc3() { var op2; if(((op2 = get_code1()) & 0x38) == 0x00) { // POP decode_mrm(op2, 1); set_dat_mem2(pop()); } } // XCHG AX,CX function op_xchg_cx() { var w = regs_2[I_AX_2]; regs_2[I_AX_2] = regs_2[I_CX_2]; regs_2[I_CX_2] = w; } // XCHG AX,DX function op_xchg_dx() { var w = regs_2[I_AX_2]; regs_2[I_AX_2] = regs_2[I_DX_2]; regs_2[I_DX_2] = w; } // XCHG AX,BX function op_xchg_bx() { var w = regs_2[I_AX_2]; regs_2[I_AX_2] = regs_2[I_BX_2]; regs_2[I_BX_2] = w; } // XCHG AX,SP function op_xchg_sp() { var w = regs_2[I_AX_2]; regs_2[I_AX_2] = regs_2[I_SP_2]; regs_2[I_SP_2] = w; } // XCHG AX,BP function op_xchg_bp() { var w = regs_2[I_AX_2]; regs_2[I_AX_2] = regs_2[I_BP_2]; regs_2[I_BP_2] = w; } // XCHG AX,SI function op_xchg_si() { var w = regs_2[I_AX_2]; regs_2[I_AX_2] = regs_2[I_SI_2]; regs_2[I_SI_2] = w; } // XCHG AX,DI function op_xchg_di() { var w = regs_2[I_AX_2]; regs_2[I_AX_2] = regs_2[I_DI_2]; regs_2[I_DI_2] = w; } // CBW function op_cbw() { regs[I_AH] = (regs[I_AL] & 0x80) ? 0xff : 0x00; } // CWD function op_cwd() { regs_2[I_DX_2] = (regs[I_AH] & 0x80) ? 0xffff : 0x0000; } // CALL FAR function op_call_far() { var new_ip = get_code2(); push(cs4 >> 4); cs4 = get_code2() << 4; push(ip); ip = new_ip; } // PUSHF function op_pushf() { push_2((flag_s << 7) | (flag_z << 6) | (flag_a << 4) | (flag_p << 2) | 0x02 | flag_c, (flag_o << 3) | (flag_d << 2) | (flag_i << 1) | flag_t); } // POPF function op_popf() { var flags = pop(); flag_o = (flags & 0x0800) >> 11; flag_d = (flags & 0x0400) >> 10; flag_i = (flags & 0x0200) >> 9; flag_t = (flags & 0x0100) >> 8; flag_s = (flags & 0x0080) >> 7; flag_z = (flags & 0x0040) >> 6; flag_a = (flags & 0x0010) >> 4; flag_p = (flags & 0x0004) >> 2; flag_c = flags & 0x0001; if(flag_i) run_icnt = 0; } // SAHF function op_sahf() { var flags = regs[I_AH]; flag_s = (flags & 0x80) >> 7; flag_z = (flags & 0x40) >> 6; flag_a = (flags & 0x10) >> 4; flag_p = (flags & 0x04) >> 2; flag_c = flags & 0x01; } // LAHF function op_lahf() { regs[I_AH] = (flag_s << 7) | (flag_z << 6) | (flag_a << 4) | (flag_p << 2) | 0x02 | flag_c; } // MOV AL,MEM8 function op_mov_l1() { data_addr(get_code2(), 0); regs[I_AL] = mem[dat_i_mem0]; } // MOV AX,MEM16 function op_mov_l2() { data_addr(get_code2(), 1); regs[I_AL] = mem[dat_i_mem0]; regs[I_AH] = mem[dat_i_mem1]; } // MOV MEM8,AL function op_mov_t1() { data_addr(get_code2(), 0); mem[dat_i_mem0] = regs[I_AL]; } // MOV MEM16,AX function op_mov_t2() { data_addr(get_code2(), 1); mem[dat_i_mem0] = regs[I_AL]; mem[dat_i_mem1] = regs[I_AH]; } // MOVSB function op_movsb() { var si = get_reg2(I_SI); var di = get_reg2(I_DI); data_addr(si, 0); mem[(es4 + di) & 0xfffff] = mem[dat_i_mem0]; if(flag_d) { set_reg2(I_SI, (si - 1) & 0xffff); set_reg2(I_DI, (di - 1) & 0xffff); } else { set_reg2(I_SI, (si + 1) & 0xffff); set_reg2(I_DI, (di + 1) & 0xffff); } } // MOVSW function op_movsw() { var si = get_reg2(I_SI); var di = get_reg2(I_DI); data_addr(si, 1); var w = mem[dat_i_mem1]; mem[(es4 + di) & 0xfffff] = mem[dat_i_mem0]; mem[(es4 + ((di + 1) & 0xffff)) & 0xfffff] = w; if(flag_d) { set_reg2(I_SI, (si - 2) & 0xffff); set_reg2(I_DI, (di - 2) & 0xffff); } else { set_reg2(I_SI, (si + 2) & 0xffff); set_reg2(I_DI, (di + 2) & 0xffff); } } // CMPSB function op_cmpsb() { var si = get_reg2(I_SI); var di = get_reg2(I_DI); data_addr(si, 0); sub_1(mem[dat_i_mem0], mem[(es4 + di) & 0xfffff], 0); if(flag_d) { set_reg2(I_SI, (si - 1) & 0xffff); set_reg2(I_DI, (di - 1) & 0xffff); } else { set_reg2(I_SI, (si + 1) & 0xffff); set_reg2(I_DI, (di + 1) & 0xffff); } } // CMPSW function op_cmpsw() { var si = get_reg2(I_SI); var di = get_reg2(I_DI); data_addr(si, 1); sub_2(get_dat_mem2(), get_mem2(es4, di), 0); if(flag_d) { set_reg2(I_SI, (si - 2) & 0xffff); set_reg2(I_DI, (di - 2) & 0xffff); } else { set_reg2(I_SI, (si + 2) & 0xffff); set_reg2(I_DI, (di + 2) & 0xffff); } } // TEST AL,IMMED8 function op_test_im1() { flags_log1(regs[I_AL] & get_code1()); } // TEST AX,IMMED16 function op_test_im2() { get_code2_2(); flags_log2(regs[I_AL] & mem[cod_i_mem0], regs[I_AH] & mem[cod_i_mem1]); } // STOSB function op_stosb() { var di = get_reg2(I_DI); mem[(es4 + di) & 0xfffff] = regs[I_AL]; set_reg2(I_DI, ((flag_d) ? (di - 1) : (di + 1)) & 0xffff); } // STOSW function op_stosw() { var di = get_reg2(I_DI); mem[(es4 + di) & 0xfffff] = regs[I_AL]; mem[(es4 + ((di + 1) & 0xffff)) & 0xfffff] = regs[I_AH]; set_reg2(I_DI, ((flag_d) ? (di - 2) : (di + 2)) & 0xffff); } // LODSB function op_lodsb() { var si = get_reg2(I_SI); data_addr(si, 0); regs[I_AL] = mem[dat_i_mem0]; set_reg2(I_SI, ((flag_d) ? (si - 1) : (si + 1)) & 0xffff); } // LODSW function op_lodsw() { var si = get_reg2(I_SI); data_addr(si, 1); regs[I_AL] = mem[dat_i_mem0]; regs[I_AH] = mem[dat_i_mem1]; set_reg2(I_SI, ((flag_d) ? (si - 2) : (si + 2)) & 0xffff); } // SCASB function op_scasb() { var di = get_reg2(I_DI); sub_1(regs[I_AL], mem[(es4 + di) & 0xfffff], 0); set_reg2(I_DI, ((flag_d) ? (di - 1) : (di + 1)) & 0xffff); } // SCASW function op_scasw() { var di = get_reg2(I_DI); sub_2(get_reg2(I_AX), get_mem2(es4, di), 0); set_reg2(I_DI, ((flag_d) ? (di - 2) : (di + 2)) & 0xffff); } // MOV AL,IMMED8 function op_mov_al_im() { regs[I_AL] = get_code1(); } // MOV CL,IMMED8 function op_mov_cl_im() { regs[I_CL] = get_code1(); } // MOV DL,IMMED8 function op_mov_dl_im() { regs[I_DL] = get_code1(); } // MOV BL,IMMED8 function op_mov_bl_im() { regs[I_BL] = get_code1(); } // MOV AH,IMMED8 function op_mov_ah_im() { regs[I_AH] = get_code1(); } // MOV CH,IMMED8 function op_mov_ch_im() { regs[I_CH] = get_code1(); } // MOV DH,IMMED8 function op_mov_dh_im() { regs[I_DH] = get_code1(); } // MOV BH,IMMED8 function op_mov_bh_im() { regs[I_BH] = get_code1(); } // MOV AX,IMMED16 function op_mov_ax_im() { get_code2_2(); regs[I_AL] = mem[cod_i_mem0]; regs[I_AH] = mem[cod_i_mem1]; } // MOV CX,IMMED16 function op_mov_cx_im() { get_code2_2(); regs[I_CL] = mem[cod_i_mem0]; regs[I_CH] = mem[cod_i_mem1]; } // MOV DX,IMMED16 function op_mov_dx_im() { get_code2_2(); regs[I_DL] = mem[cod_i_mem0]; regs[I_DH] = mem[cod_i_mem1]; } // MOV BX,IMMED16 function op_mov_bx_im() { get_code2_2(); regs[I_BL] = mem[cod_i_mem0]; regs[I_BH] = mem[cod_i_mem1]; } // MOV SP,IMMED16 function op_mov_sp_im() { get_code2_2(); regs[I_SP_L] = mem[cod_i_mem0]; regs[I_SP_H] = mem[cod_i_mem1]; } // MOV BP,IMMED16 function op_mov_bp_im() { get_code2_2(); regs[I_BP_L] = mem[cod_i_mem0]; regs[I_BP_H] = mem[cod_i_mem1]; } // MOV SI,IMMED16 function op_mov_si_im() { get_code2_2(); regs[I_SI_L] = mem[cod_i_mem0]; regs[I_SI_H] = mem[cod_i_mem1]; } // MOV DI,IMMED16 function op_mov_di_im() { get_code2_2(); regs[I_DI_L] = mem[cod_i_mem0]; regs[I_DI_H] = mem[cod_i_mem1]; } // RET IMMED16 function op_ret_n() { var inc = get_code2(); ip = pop(); set_reg2(I_SP, (get_reg2(I_SP) + inc) & 0xffff); } // RET function op_ret() { ip = pop(); } // LES REG16,MEM16 function op_les() { decode_mrm(-1, 2); if(!(dat_i_mem0 & 0x100000)) { // レジスタ以外 set_mrm_reg2(get_dat_mem2()); es4 = data_seg4; } } // LDS REG16,MEM16 function op_lds() { decode_mrm(-1, 2); if(!(dat_i_mem0 & 0x100000)) { // レジスタ以外 set_mrm_reg2(get_dat_mem2()); ds4 = data_seg4; } } // MOV MEM8,IMMED8 function op_mov_m_im1() { var op2; if(((op2 = get_code1()) & 0x38) == 0x00) { decode_mrm(op2, 0); mem[dat_i_mem0] = get_code1(); } } // MOV MEM16,IMMED16 function op_mov_m_im2() { var op2; if(((op2 = get_code1()) & 0x38) == 0x00) { decode_mrm(op2, 1); get_code2_2(); mem[dat_i_mem0] = mem[cod_i_mem0]; mem[dat_i_mem1] = mem[cod_i_mem1]; } } // RETF IMMED16 function op_retf_n() { var inc = get_code2(); ip = pop(); cs4 = pop() << 4; set_reg2(I_SP, (get_reg2(I_SP) + inc) & 0xffff); } // RETF function op_retf() { ip = pop(); cs4 = pop() << 4; } // INT 3 function op_int_3() { int(0x03); } // INT IMMED8 function op_int_n() { var n = get_code1(); // BIOS エミュレーション switch(n) { case 0x10: // Video Services bios_video(); return; case 0x11: // Equipment List bios_equip(); return; case 0x12: // Memory Size bios_memory(); return; // INT 13H 割り込みをフックするプログラムのため,Disk BIOS については実際に割り込みを実行する. //case 0x13: // Low Level Disk Services // bios_disk(); // return; case 0x14: // Serial Port I/O bios_serial(); return; case 0x15: // Miscellaneous Services bios_misc(); return; case 0x16: // Keyboard Services bios_keyboard(); return; case 0x17: // Printer Services bios_printer(); return; case 0x19: // Bootstrap Loader bios_boot(); return; case 0x1a: // Real Time Clock Services bios_clock(); return; } int(n); } // Disk BIOS 割り込み デフォルト処理 function bios_disk_int_def() { bios_disk(); // 結果のキャリー フラグ設定 var i_mem = (ss4 + ((get_reg2(I_SP) + 4) & 0xffff)) & 0xfffff; mem[i_mem] = mem[i_mem] & 0xfe | flag_c; } // INTO function op_into() { if(flag_o) int(0x04); } // IRET function op_iret() { ip = pop(); cs4 = pop() << 4; op_popf(); } // ROL/ROR/RCL/RCR/SHL(SAL)/SHR/SAR REG8/MEM8,1 function op_rs_1_1() { rs_1(1); } // ROL/ROR/RCL/RCR/SHL(SAL)/SHR/SAR REG16/MEM16,1 function op_rs_1_2() { rs_2(1); } // ROL/ROR/RCL/RCR/SAL(SHL)/SHR/SAR REG8/MEM8,CL function op_rs_n_1() { rs_1(regs[I_CL]); } // ROL/ROR/RCL/RCR/SAL(SHL)/SHR/SAR REG16/MEM16,CL function op_rs_n_2() { rs_2(regs[I_CL]); } // ローテート/シフト バイト用 function rs_1(n) { var op2; var op_sub; if(((op_sub = (op2 = get_code1()) & 0x38)) != 0x30) { decode_mrm(op2, 0); if(!n) return; var val = mem[dat_i_mem0]; switch(op_sub) { case 0x00: // ROL n &= 0x7; val = (val << n) & 0xff | (val >> (8 - n)); flag_c = val & 0x01; flag_o = ((val & 0x80) >> 7) ^ flag_c; break; case 0x08: // ROR n &= 0x7; val = (val >> n) | (val << (8 - n)) & 0xff; flag_o = ((val >> 7) ^ (val >> 6)) & 0x1; flag_c = (val & 0x80) >> 7; break; case 0x10: // RCL n %= 9; val |= flag_c << 8; val = (val << n) | (val >> (9 - n)); flag_c = (val & 0x100) >> 8; flag_o = ((val & 0x80) >> 7) ^ flag_c; val &= 0xff; break; case 0x18: // RCR n %= 9; val |= flag_c << 8; val = (val >> n) | (val << (9 - n)); flag_o = ((val >> 7) ^ (val >> 6)) & 0x1; flag_c = (val & 0x100) >> 8; val &= 0xff; break; case 0x20: // SHL(SAL) if(n > 9) n = 9; val <<= n; flag_c = (val & 0x100) >> 8; flag_o = ((val & 0x80) >> 7) ^ flag_c; flags_szp1(val &= 0xff); break; case 0x28: // SHR if(n > 9) n = 9; val >>= n - 1; flag_o = val >> 6; flag_c = val & 0x1; flags_szp1(val >>= 1); break; default: // SAR if(n > 9) n = 9; if(val & 0x80) val |= 0xffffff00; val >>= n - 1; flag_o = 0; flag_c = val & 0x1; flags_szp1(val = (val >> 1) & 0xff); } mem[dat_i_mem0] = val; } } // ローテート/シフト ワード用 function rs_2(n) { var op2; var op_sub; if(((op_sub = (op2 = get_code1()) & 0x38)) != 0x30) { decode_mrm(op2, 1); if(!n) return; var val = get_dat_mem2(); switch(op_sub) { case 0x00: // ROL n &= 0xf; val = (val << n) & 0xffff | (val >> (16 - n)); flag_c = val & 0x0001; flag_o = ((val & 0x8000) >> 15) ^ flag_c; break; case 0x08: // ROR n &= 0xf; val = (val >> n) | (val << (16 - n)) & 0xffff; flag_o = ((val >> 15) ^ (val >> 14)) & 0x1; flag_c = (val & 0x8000) >> 15; break; case 0x10: // RCL n %= 17; val |= flag_c << 16; val = (val << n) | (val >> (17 - n)); flag_c = (val & 0x10000) >> 16; flag_o = ((val & 0x8000) >> 15) ^ flag_c; val &= 0xffff; break; case 0x18: // RCR n %= 17; val |= flag_c << 16; val = (val >> n) | (val << (17 - n)); flag_o = ((val >> 15) ^ (val >> 14)) & 0x1; flag_c = (val & 0x10000) >> 16; val &= 0xffff; break; case 0x20: // SHL(SAL) if(n > 17) n = 17; val <<= n; flag_c = (val & 0x10000) >> 16; flag_o = ((val & 0x8000) >> 15) ^ flag_c; flags_szp2(val &= 0xffff); break; case 0x28: // SHR if(n > 17) n = 17; val >>= n - 1; flag_o = val >> 14; flag_c = val & 0x1; flags_szp2(val >>= 1); break; default: // SAR if(n > 17) n = 17; if(val & 0x8000) val |= 0xffff0000; val >>= n - 1; flag_o = 0; flag_c = val & 0x1; flags_szp2(val = (val >> 1) & 0xffff); } set_dat_mem2(val); } } // AAM function op_aam() { if(get_code1() == 0x0a) { var val = regs[I_AL]; var rem; flags_szp1(regs[I_AL] = rem = val % 10); regs[I_AH] = (val - rem) / 10; } } // AAD function op_aad() { if(get_code1() == 0x0a) { flags_szp1(regs[I_AL] = (regs[I_AH] * 10 + regs[I_AL]) & 0xff); regs[I_AH] = 0; } } // XLAT function op_xlat() { data_addr((get_reg2(I_BX) + regs[I_AL]) & 0xffff, 0); regs[I_AL] = mem[dat_i_mem0]; } // LOOPNE function op_loopne() { var cx; set_reg2(I_CX, cx = (get_reg2(I_CX) - 1) & 0xffff); jmp_short(cx && !flag_z); } // LOOPE function op_loope() { var cx; set_reg2(I_CX, cx = (get_reg2(I_CX) - 1) & 0xffff); jmp_short(cx && flag_z); } // LOOP function op_loop() { var cx; set_reg2(I_CX, cx = (get_reg2(I_CX) - 1) & 0xffff); jmp_short(cx); } // JCXZ function op_jcxz() { jmp_short(!regs_2[I_CX_2]); } // IN AL,IMMED8 function op_in_1() { in_1(get_code1()); } // IN AX,IMMED8 function op_in_2() { in_2(get_code1()); } // OUT IMMED8,AL function op_out_1() { out_1(get_code1()); } // OUT IMMED8,AX function op_out_2() { out_2(get_code1()); } // CALL NEAR function op_call_near() { var disp = get_code2(); push(ip); ip = (ip + disp) & 0xffff; } // JMP NEAR function op_jmp_near() { var disp = get_code2(); ip = (ip + disp) & 0xffff; } // JMP FAR function op_jmp_far() { var new_ip = get_code2(); cs4 = get_code2() << 4; ip = new_ip; } // JMP SHORT function op_jmp() { var disp = get_code_sx(); ip = (ip + disp) & 0xffff; } // IN AL,DX function op_in_dx1() { in_1(get_reg2(I_DX)); } // IN AX,DX function op_in_dx2() { in_2(get_reg2(I_DX)); } // OUT DX,AL function op_out_dx1() { out_1(get_reg2(I_DX)); } // OUT DX,AX function op_out_dx2() { out_2(get_reg2(I_DX)); } // REPNE function op_repne() { rep(1); } // REP/REPE function op_repe() { rep(0); } function rep(z) { var wip = ip; var code = mem[(cs4 + wip) & 0xfffff]; if(code == 0xf0) { // LOCK wip = (wip + 1) & 0xffff; code = mem[(cs4 + wip) & 0xfffff]; } if((code & 0xe7) == 0x26) { // セグメント オーバーライド seg4_ovr = code & 0x18; wip = (wip + 1) & 0xffff; code = mem[(cs4 + wip) & 0xfffff]; } // MOVSB,MOVSW,STOSB,STOSW,LODSB,LODSW については // REPNE は REP(REPE)と同じ動作になる var cx; var si, di; var dir; var seg4; var al; switch(code) { case 0xa4: // MOVSB src_addr(); di = get_reg2(I_DI); dir = (flag_d) ? -1 : 1; for(cx = get_reg2(I_CX); cx; cx--) { mem[(es4 + di) & 0xfffff] = mem[(seg4 + si) & 0xfffff]; si = (si + dir) & 0xffff; di = (di + dir) & 0xffff; } regs_2[I_CX_2] = 0; set_reg2(I_SI, si); set_reg2(I_DI, di); break; case 0xa5: // MOVSW src_addr(); di = get_reg2(I_DI); dir = (flag_d) ? -2 : 2; for(cx = get_reg2(I_CX); cx; cx--) { var w = mem[(seg4 + ((si + 1) & 0xffff)) & 0xfffff]; mem[(es4 + di) & 0xfffff] = mem[(seg4 + si) & 0xfffff]; mem[(es4 + ((di + 1) & 0xffff)) & 0xfffff] = w; si = (si + dir) & 0xffff; di = (di + dir) & 0xffff; } regs_2[I_CX_2] = 0; set_reg2(I_SI, si); set_reg2(I_DI, di); break; case 0xa6: // CMPSB src_addr(); di = get_reg2(I_DI); dir = (flag_d) ? -1 : 1; for(cx = get_reg2(I_CX); cx; ) { sub_1(mem[(seg4 + si) & 0xfffff], mem[(es4 + di) & 0xfffff], 0); si = (si + dir) & 0xffff; di = (di + dir) & 0xffff; cx--; if(flag_z == z) break; } set_reg2(I_CX, cx); set_reg2(I_SI, si); set_reg2(I_DI, di); break; case 0xa7: // CMPSW src_addr(); di = get_reg2(I_DI); dir = (flag_d) ? -2 : 2; for(cx = get_reg2(I_CX); cx; ) { sub_2(get_mem2(seg4, si), get_mem2(es4, di), 0); si = (si + dir) & 0xffff; di = (di + dir) & 0xffff; cx--; if(flag_z == z) break; } set_reg2(I_CX, cx); set_reg2(I_SI, si); set_reg2(I_DI, di); break; case 0xaa: // STOSB di = get_reg2(I_DI); dir = (flag_d) ? -1 : 1; al = regs[I_AL]; for(cx = get_reg2(I_CX); cx; cx--) { mem[(es4 + di) & 0xfffff] = al; di = (di + dir) & 0xffff; } regs_2[I_CX_2] = 0; set_reg2(I_DI, di); break; case 0xab: // STOSW di = get_reg2(I_DI); dir = (flag_d) ? -2 : 2; al = regs[I_AL]; var ah = regs[I_AH]; for(cx = get_reg2(I_CX); cx; cx--) { mem[(es4 + di) & 0xfffff] = al; mem[(es4 + ((di + 1) & 0xffff)) & 0xfffff] = ah; di = (di + dir) & 0xffff; } regs_2[I_CX_2] = 0; set_reg2(I_DI, di); break; case 0xac: // LODSB src_addr(); dir = (flag_d) ? -1 : 1; for(cx = get_reg2(I_CX); cx; cx--) { regs[I_AL] = mem[(seg4 + si) & 0xfffff]; si = (si + dir) & 0xffff; } regs_2[I_CX_2] = 0; set_reg2(I_SI, si); break; case 0xad: // LODSW src_addr(); dir = (flag_d) ? -2 : 2; for(cx = get_reg2(I_CX); cx; cx--) { regs[I_AL] = mem[(seg4 + si) & 0xfffff]; regs[I_AH] = mem[(seg4 + ((si + 1) & 0xffff)) & 0xfffff]; si = (si + dir) & 0xffff; } regs_2[I_CX_2] = 0; set_reg2(I_SI, si); break; case 0xae: // SCASB di = get_reg2(I_DI); dir = (flag_d) ? -1 : 1; al = regs[I_AL]; for(cx = get_reg2(I_CX); cx; ) { sub_1(al, mem[(es4 + di) & 0xfffff], 0); di = (di + dir) & 0xffff; cx--; if(flag_z == z) break; } set_reg2(I_CX, cx); set_reg2(I_DI, di); break; case 0xaf: // SCASW di = get_reg2(I_DI); dir = (flag_d) ? -2 : 2; var ax = get_reg2(I_AX); for(cx = get_reg2(I_CX); cx; ) { sub_2(ax, get_mem2(es4, di), 0); di = (di + dir) & 0xffff; cx--; if(flag_z == z) break; } set_reg2(I_CX, cx); set_reg2(I_DI, di); break; default: return; } ip = (wip + 1) & 0xffff; function src_addr() { switch(seg4_ovr) { case 0x00: seg4 = es4; break; case 0x08: seg4 = cs4; break; case 0x10: seg4 = ss4; break; default: seg4 = ds4; } si = get_reg2(I_SI); } } // HLT function op_hlt() { run_halt = true; } // CMC function op_cmc() { flag_c ^= 0x1; } // TEST REG8/MEM8,IMMED8 // NOT/NEG/MUL/IMUL/DIV/IDIV REG8/MEM8 function op_misc4() { var op2; var val; var div; var rem; switch((op2 = get_code1()) & 0x38) { case 0x00: // TEST decode_mrm(op2, 0); flags_log1(mem[dat_i_mem0] & get_code1()); break; case 0x10: // NOT decode_mrm(op2, 0); mem[dat_i_mem0] ^= 0xff; break; case 0x18: // NEG decode_mrm(op2, 0); val = mem_s[dat_i_mem0]; flag_o = (val == 0xffffff80) ? 1 : 0; flag_a = (val & 0xf) ? 1 : 0; flag_c = (val) ? 1 : 0; flags_szp1(mem[dat_i_mem0] = (- val) & 0xff); break; case 0x20: // MUL decode_mrm(op2, 0); set_reg2(I_AX, val = regs[I_AL] * mem[dat_i_mem0]); flag_o = flag_c = (val & 0xff00) ? 1 : 0; break; case 0x28: // IMUL decode_mrm(op2, 0); val = regs_s[I_AL] * mem_s[dat_i_mem0]; set_reg2(I_AX, val & 0xffff); flag_o = flag_c = (val < -128 || val > 127) ? 1 : 0; break; case 0x30: // DIV decode_mrm(op2, 0); val = get_reg2(I_AX); if(div = mem[dat_i_mem0]) { regs[I_AH] = rem = val % div; regs[I_AL] = (val = (val - rem) / div) & 0xff; if(!(val & 0xff00)) break; } int(0x00); return; case 0x38: // IDIV decode_mrm(op2, 0); val = get_reg2s(I_AX); if(div = mem_s[dat_i_mem0]) { regs[I_AH] = (rem = val % div) & 0xff; regs[I_AL] = (val = (val - rem) / div) & 0xff; if(val >= -128 && val <= 127) break; } int(0x00); return; } } // TEST REG16/MEM16,IMMED16 // NOT/NEG/MUL/IMUL/DIV/IDIV REG16/MEM16 function op_misc5() { var op2; var val; var div; var rem; switch((op2 = get_code1()) & 0x38) { case 0x00: // TEST decode_mrm(op2, 1); get_code2_2(); flags_log2(mem[dat_i_mem0] & mem[cod_i_mem0], mem[dat_i_mem1] & mem[cod_i_mem1]); break; case 0x10: // NOT decode_mrm(op2, 1); mem[dat_i_mem0] ^= 0xff; mem[dat_i_mem1] ^= 0xff; break; case 0x18: // NEG decode_mrm(op2, 1); val = get_dat_mem2s(); flag_o = (val == 0xffff8000) ? 1 : 0; flag_a = (val & 0xf) ? 1 : 0; flag_c = (val) ? 1 : 0; set_dat_mem2(val = (- val) & 0xffff); flags_szp2(val); break; case 0x20: // MUL decode_mrm(op2, 1); val = get_reg2(I_AX) * get_dat_mem2(); set_reg2(I_AX, rem = val % 0x10000); set_reg2(I_DX, val = (val - rem) / 0x10000); flag_o = flag_c = (val) ? 1 : 0; break; case 0x28: // IMUL decode_mrm(op2, 1); val = get_reg2s(I_AX) * get_dat_mem2s(); set_reg2(I_DX, val >>> 16); set_reg2(I_AX, val & 0xffff); flag_o = flag_c = (val > 32767 || val < -32768) ? 1 : 0; break; case 0x30: // DIV decode_mrm(op2, 1); val = get_reg2(I_DX) * 0x10000 + get_reg2(I_AX); if(div = get_dat_mem2()) { set_reg2(I_DX, rem = val % div); set_reg2(I_AX, (val = (val - rem) / div) % 0x10000); if(val < 0x10000) break; } int(0x00); return; case 0x38: // IDIV decode_mrm(op2, 1); val = (get_reg2s(I_DX) << 16) | get_reg2(I_AX); if(div = get_dat_mem2s()) { set_reg2(I_DX, (rem = val % div) & 0xffff); set_reg2(I_AX, (val = (val - rem) / div) & 0xffff); if(val >= -32768 && val <= 32767) break; } int(0x00); return; } } // CLC function op_clc() { flag_c = 0; } // STC function op_stc() { flag_c = 1; } // CLI function op_cli() { flag_i = 0; } // STI function op_sti() { flag_i = 1; run_icnt = 0; } // CLD function op_cld() { flag_d = 0; } // STD function op_std() { flag_d = 1; } // INC/DEC REG8/MEM8 function op_misc6() { var op2; var val; switch((op2 = get_code1()) & 0x38) { case 0x00: // INC decode_mrm(op2, 0); val = (mem[dat_i_mem0] + 1) & 0xff; flag_o = (val == 0x80) ? 1 : 0; flag_a = (val & 0xf) ? 0 : 1; flags_szp1(mem[dat_i_mem0] = val); break; case 0x08: // DEC decode_mrm(op2, 0); val = mem[dat_i_mem0]; flag_o = (val == 0x80) ? 1 : 0; flag_a = (val & 0xf) ? 0 : 1; flags_szp1(mem[dat_i_mem0] = (val - 1) & 0xff); break; } } // INC/DEC MEM16 // CALL/JMP REG16/MEM16 // CALL/JMP MEM16 // PUSH REG16/MEM16 function op_misc7() { var op2; var val; switch((op2 = get_code1()) & 0x38) { case 0x00: // INC decode_mrm(op2, 1); val = (get_dat_mem2() + 1) & 0xffff; flag_o = (val == 0x8000) ? 1 : 0; flag_a = (val & 0xf) ? 0 : 1; set_dat_mem2(val); flags_szp2(val); break; case 0x08: // DEC decode_mrm(op2, 1); val = get_dat_mem2(); flag_o = (val == 0x8000) ? 1 : 0; flag_a = (val & 0xf) ? 0 : 1; set_dat_mem2(val = (val - 1) & 0xffff); flags_szp2(val); break; case 0x10: // CALL decode_mrm(op2, 1); push(ip); ip = get_dat_mem2(); break; case 0x18: // CALL decode_mrm(op2, 2); if(!(dat_i_mem0 & 0x100000)) { // レジスタ以外 push(cs4 >> 4); push(ip); ip = get_dat_mem2(); cs4 = data_seg4; } break; case 0x20: // JMP decode_mrm(op2, 1); ip = get_dat_mem2(); break; case 0x28: // JMP decode_mrm(op2, 2); if(!(dat_i_mem0 & 0x100000)) { // レジスタ以外 ip = get_dat_mem2(); cs4 = data_seg4; } break; case 0x30: // PUSH decode_mrm(op2, 1); push_2(mem[dat_i_mem0], mem[dat_i_mem1]); break; } } function jmp_short(cond) { if(cond) { var disp = get_code_sx(); ip = (ip + disp) & 0xffff; } else { ip = (ip + 1) & 0xffff; } } function int(n) { n <<= 2; op_pushf(); push(cs4 >> 4); push(ip); flag_i = flag_t = 0; cs4 = (mem[n | 2] | (mem[n | 3] << 8)) << 4; ip = mem[n] | (mem[n | 1] << 8); } function decode_mrm(mrm, w) { if(mrm < 0) mrm = get_code1(); mrm_i_reg = (mrm & 0x38) >> 2; if(!w && (mrm & 0x20)) // AH 〜 BH mrm_i_reg -= 7; var r_m = mrm & 0x07; var disp; switch(mrm & 0xc0) { case 0xc0: // レジスタ dat_i_mem0 = 0x100000 + (r_m << 1); if(w) { dat_i_mem1 = dat_i_mem0 | 1; } else { if(r_m & 0x04) // AH 〜 BH dat_i_mem0 -= 7; } return; case 0x40: // 変位 1 バイト disp = get_code_sx(); break; case 0x80: // 変位 2 バイト disp = get_code2(); break; default: // 0x00 if(r_m == 0x6) { // 変位 2 バイト // インデックスなし data_addr(get_code2(), w); return; } // 変位なし disp = 0; } switch(r_m) { case 0x0: data_addr((disp + get_reg2(I_BX) + get_reg2(I_SI)) & 0xffff, w); break; case 0x1: data_addr((disp + get_reg2(I_BX) + get_reg2(I_DI)) & 0xffff, w); break; case 0x2: data_addr_ss((disp + get_reg2(I_BP) + get_reg2(I_SI)) & 0xffff, w); break; case 0x3: data_addr_ss((disp + get_reg2(I_BP) + get_reg2(I_DI)) & 0xffff, w); break; case 0x4: data_addr((disp + get_reg2(I_SI)) & 0xffff, w); break; case 0x5: data_addr((disp + get_reg2(I_DI)) & 0xffff, w); break; case 0x6: data_addr_ss((disp + get_reg2(I_BP)) & 0xffff, w); break; default: // 0x7 data_addr((disp + get_reg2(I_BX)) & 0xffff, w); } } function get_mrm_reg2() { return regs[mrm_i_reg] | (regs[mrm_i_reg | 1] << 8); } function set_mrm_reg2(val) { regs[mrm_i_reg] = val & 0xff; regs[mrm_i_reg | 1] = val >> 8; } function data_addr(off, w) { var seg4; switch(seg4_ovr) { case 0x00: seg4 = es4; break; case 0x08: seg4 = cs4; break; case 0x10: seg4 = ss4; break; default: seg4 = ds4; } dat_i_mem0 = (seg4 + off) & 0xfffff; if(w) { dat_i_mem1 = (seg4 + ((off + 1) & 0xffff)) & 0xfffff; if(w == 2) data_seg4 = get_mem2(seg4, off + 2) << 4; } } function data_addr_ss(off, w) { var seg4; switch(seg4_ovr) { case 0x00: seg4 = es4; break; case 0x08: seg4 = cs4; break; case 0x18: seg4 = ds4; break; default: seg4 = ss4; } dat_i_mem0 = (seg4 + off) & 0xfffff; if(w) { dat_i_mem1 = (seg4 + ((off + 1) & 0xffff)) & 0xfffff; if(w == 2) data_seg4 = get_mem2(seg4, off + 2) << 4; } } // 加算 バイト用 function add_1(val1, val2, cy) { var val = val1 + val2 + cy; var w = val1 ^ val2 ^ val; flag_o = ((w >> 7) ^ (val >> 8)) & 0x1; flag_a = (w >> 4) & 0x1; flag_c = (val >> 8) & 0x1; flags_szp1(val &= 0xff); return val; } // 加算 ワード用 function add_2(val1, val2, cy) { var val = val1 + val2 + cy; var w = val1 ^ val2 ^ val; flag_o = ((w >> 15) ^ (val >> 16)) & 0x1; flag_a = (w >> 4) & 0x1; flag_c = (val >> 16) & 0x1; flags_szp2(val &= 0xffff); return val; } // 減算 バイト用 function sub_1(val1, val2, cy) { val2 ^= 0xff; var val = val1 + val2 + (cy ^ 0x1); var w = val1 ^ val2 ^ val; flag_o = ((w >> 7) ^ (val >> 8)) & 0x1; flag_a = (w >> 4) & 0x1 ^ 0x1; flag_c = (val >> 8) & 0x1 ^ 0x1; flags_szp1(val &= 0xff); return val; } // 減算 ワード用 function sub_2(val1, val2, cy) { val2 ^= 0xffff; var val = val1 + val2 + (cy ^ 0x1); var w = val1 ^ val2 ^ val; flag_o = ((w >> 15) ^ (val >> 16)) & 0x1; flag_a = (w >> 4) & 0x1 ^ 0x1; flag_c = (val >> 16) & 0x1 ^ 0x1; flags_szp2(val &= 0xffff); return val; } function push(val) { var sp; set_reg2(I_SP, sp = (get_reg2(I_SP) - 2) & 0xffff); mem[(ss4 + sp) & 0xfffff] = val & 0xff; mem[(ss4 + ((sp + 1) & 0xffff)) & 0xfffff] = val >> 8; } function push_2(val_l, val_h) { var sp; set_reg2(I_SP, sp = (get_reg2(I_SP) - 2) & 0xffff); mem[(ss4 + sp) & 0xfffff] = val_l; mem[(ss4 + ((sp + 1) & 0xffff)) & 0xfffff] = val_h; } function pop() { var sp; var val = get_mem2(ss4, sp = get_reg2(I_SP)); set_reg2(I_SP, (sp + 2) & 0xffff); return val; } // 論理演算後フラグ セット バイト用 function flags_log1(val) { flag_o = flag_c = 0; flags_szp1(val); } // 論理演算後フラグ セット ワード用 function flags_log2(val_l, val_h) { flag_o = flag_c = 0; // サイン flag_s = val_h >> 7; // ゼロ flag_z = (val_l | val_h) ? 0 : 1; // パリティ // パリティは下位バイトのみ判定する var w = val_l ^ (val_l >> 4); w ^= w >> 2; flag_p = (w ^ (w >> 1) ^ 0x1) & 0x1; } // サイン,ゼロ,パリティ フラグ セット バイト用 function flags_szp1(val) { // サイン flag_s = val >> 7; // ゼロ flag_z = (val) ? 0 : 1; // パリティ var w = val ^ (val >> 4); w ^= w >> 2; flag_p = (w ^ (w >> 1) ^ 0x1) & 0x1; } // サイン,ゼロ,パリティ フラグ セット ワード用 function flags_szp2(val) { // サイン flag_s = val >> 15; // ゼロ flag_z = (val) ? 0 : 1; // パリティ // パリティは下位バイトのみ判定する var w = val ^ (val >> 4); w ^= w >> 2; flag_p = (w ^ (w >> 1) ^ 0x1) & 0x1; } function get_code1() { var val = mem[(cs4 + ip) & 0xfffff]; ip = (ip + 1) & 0xffff; return val; } function get_code2() { var val = mem[(cs4 + ip) & 0xfffff] | (mem[(cs4 + ((ip + 1) & 0xffff)) & 0xfffff] << 8); ip = (ip + 2) & 0xffff; return val; } function get_code2_2() { cod_i_mem0 = (cs4 + ip) & 0xfffff; cod_i_mem1 = (cs4 + ((ip + 1) & 0xffff)) & 0xfffff; ip = (ip + 2) & 0xffff; } function get_code_sx() { var val = mem_s[(cs4 + ip) & 0xfffff] & 0xffff; ip = (ip + 1) & 0xffff; return val; } function get_dat_mem2() { return mem[dat_i_mem0] | (mem[dat_i_mem1] << 8); } function get_dat_mem2s() { return mem[dat_i_mem0] | (mem_s[dat_i_mem1] << 8); } function set_dat_mem2(val) { mem[dat_i_mem0] = val & 0xff; mem[dat_i_mem1] = val >> 8; } function get_mem2(seg4, off) { return mem[(seg4 + off) & 0xfffff] | (mem[(seg4 + ((off + 1) & 0xffff)) & 0xfffff] << 8); } function get_reg2(i_reg) { return regs[i_reg] | (regs[i_reg | 1] << 8); } function get_reg2s(i_reg) { return regs[i_reg] | (regs_s[i_reg | 1] << 8); } function set_reg2(i_reg, val) { regs[i_reg] = val & 0xff; regs[i_reg | 1] = val >> 8; } // ---- BIOS/ハードウェア エミュレーション ---- const INT_08_OFF = 0xfea5; const INT_08_OFF_2 = 0xfea6; const INT_09_OFF = 0xe987; const INT_13_OFF = 0xec59; const SYS_DESC_SEG4 = 0xf0000; const SYS_DESC_OFF = 0x0000; const FD_PARAM_SEG4 = 0xf0000; const FD_PARAM_OFF = 0xefc7; const FD_PARAM_8_OFF = 0x0010; const FD_PARAM_9_OFF = 0x0020; const FD_PARAM_10_OFF = 0x0030; // BIOS データ領域アドレス const MEM_EQUIP_LIST_0 = 0x410; const MEM_EQUIP_LIST_1 = 0x411; const MEM_MEM_SIZE_0 = 0x413; const MEM_MEM_SIZE_1 = 0x414; const MEM_KB_STATUS_0 = 0x417; const MEM_KB_STATUS_1 = 0x418; const MEM_KB_BUFF_HEAD_0 = 0x41a; const MEM_KB_BUFF_HEAD_1 = 0x41b; const MEM_KB_BUFF_TAIL_0 = 0x41c; const MEM_KB_BUFF_TAIL_1 = 0x41d; const MEM_KB_BUFF = 0x41e; const MEM_MOTOR_OFF = 0x440; const MEM_DISK_STATUS = 0x441; const MEM_VIDEO_MODE = 0x449; const MEM_SCR_COLS_0 = 0x44a; const MEM_SCR_COLS_1 = 0x44b; const MEM_VBUF_SIZE_0 = 0x44c; const MEM_VBUF_SIZE_1 = 0x44d; const MEM_VBUF_START_0 = 0x44e; const MEM_VBUF_START_1 = 0x44f; const MEM_CURSOR_POS = 0x450; const MEM_CURSOR_E = 0x460; const MEM_CURSOR_S = 0x461; const MEM_ACTIVE_PAGE = 0x462; const MEM_CRTC_PORT_0 = 0x463; const MEM_CRTC_PORT_1 = 0x464; const MEM_CRTC_MODE = 0x465; const MEM_CRTC_COLOR = 0x466; const MEM_TIMER_COUNTER_0 = 0x46c; const MEM_TIMER_COUNTER_1 = 0x46d; const MEM_TIMER_COUNTER_2 = 0x46e; const MEM_TIMER_COUNTER_3 = 0x46f; const MEM_TIMER_OVERFLOW = 0x470; const MEM_NUM_HD = 0x475; const MEM_SCR_ROW_MAX = 0x484; const MEM_KB_FLAGS = 0x496; const KB_BUFF_OFF = 0x1e; // I/O アドレス const I_DMA_STATUS = 0x8; const I_PIC_M_STATUS_0 = 0x20; const O_PIC_M_COMMAND_0 = 0x20; const I_PIC_M_STATUS_1 = 0x21; const O_PIC_M_COMMAND_1 = 0x21; const I_TIMER_0_COUNT = 0x40; const O_TIMER_0_COUNT = 0x40; const I_TIMER_2_COUNT = 0x42; const O_TIMER_2_COUNT = 0x42; const O_TIMER_CONTROL = 0x43; const I_PARALLEL_A = 0x60; const I_PARALLEL_B = 0x61; const O_PARALLEL_B = 0x61; const I_KB_STATUS = 0x64; const I_JOYSTICK = 0x201; const IO_MDA_0 = 0xb4; const IO_MDA_1 = 0x3; const O_MDA_INDEX = 0x3b4; const I_MDA_DATA = 0x3b5; const O_MDA_DATA = 0x3b5; const O_MDA_MODE = 0x3b8; const I_MDA_STATUS = 0x3ba; const IO_CGA_0 = 0xd4; const IO_CGA_1 = 0x3; const O_CGA_INDEX = 0x3d4; const I_CGA_DATA = 0x3d5; const O_CGA_DATA = 0x3d5; const O_CGA_MODE = 0x3d8; const I_CGA_UNKNOWN = 0x3d9; const O_CGA_COLOR = 0x3d9; const I_CGA_STATUS = 0x3da; const O_CGA_LP_CLEAR = 0x3db; const O_CGA_LP_PRESET = 0x3dc; const I_FDC_STATUS = 0x3f4; const OSC_INT = 0.0008380953445200438; // 1000 / (14318180 / 12) const TIMER_FREQ = 1000 / OSC_INT; save_regs = new Uint16Array(mem.buffer, 0xe0000, 2304); // レジスタ退避用領域(メモリの未使用領域を流用) mnt_image = [undefined, undefined]; mnt_spec = [undefined, undefined]; mnt_dirty = [false, false]; // BIOS/ハードウェア初期化 function bios_init() { // 割り込みベクター初期化 mem[0xfff53] = 0xcf; // IRET for(i = 0; i < 0x7c; i += 4) { mem[i] = 0x53; mem[i | 1] = 0xff; mem[i | 2] = 0x00; mem[i | 3] = 0xf0; } // INT 08H mem[0xffea5] = 0x90; // NOP mem[0xffea6] = 0xcf; // IRET mem[0x20] = 0xa5; mem[0x21] = 0xfe; // INT 09H mem[0xfe987] = 0xcf; // IRET mem[0x24] = 0x87; mem[0x25] = 0xe9; // INT 13H mem[0xfec59] = 0xcf; // IRET mem[0x4c] = 0x59; mem[0x4d] = 0xec; // System Descriptor Table mem.set([8, 0, 0xff, 0, 0, 0x20, 0, 0, 0, 0], 0xf0000); // Model Number mem[0xffffe] = 0xff; // Video Parameters mem.set([ 56, 40, 45,10, 31, 6, 25, 28,2, 7, 6, 7,0x00,0x00,0x00,0x00, 113, 80, 90,10, 31, 6, 25, 28,2, 7, 6, 7,0x00,0x00,0x00,0x00, 56, 40, 45,10,127, 6,100,112,2, 1, 6, 7,0x00,0x00,0x00,0x00, 97, 80, 82,15, 25, 6, 25, 25,2,13,11,12,0x00,0x00,0x00,0x00, 0x00,0x08,0x00,0x10,0x00,0x40,0x00,0x40, 40,40,80,80,40,40,80,80, 0x2c,0x28,0x2d,0x29,0x2a,0x2e,0x1e,0x29 ], 0xff0a4); mem[0x74] = 0xa4; mem[0x75] = 0xf0; // Diskette Parameters var dp = [0xf1, 0x02, 0, 2, 18, 27, 0xff, 80, 0xf6, 0, 0]; mem.set(dp, 0xfefc7); mem[0x78] = 0xc7; mem[0x79] = 0xef; mem.set(dp, 0xf0010); mem[0xf0014] = 8; mem.set(dp, 0xf0020); mem[0xf0024] = 9; mem.set(dp, 0xf0030); mem[0xf0034] = 10; // CGA Graphics Character Font mem.set(new Uint8Array(video_font_8x8.buffer, 0, 1024), 0xffa6e); mem[0x7c] = mem[0x7d] = mem[0x7e] = mem[0x7f] = 0; // 最初の実行命令 // このジャンプ命令の飛び先で機種(?)を判定しているプログラムがある mem.set([0xea, 0x5b, 0xe0, 0x00, 0xf0], 0xffff0); // JMP F000:E05B // BIOS データ領域初期化 mem.fill(0, 0x400, 0x410); // COMn,LPTn ポート switch(INIT_MODE) { // 初期ビデオ モード case 2: case 3: case 6: conf_sw1 = 0x6d; break; case 7: conf_sw1 = 0x7d; break; default: // 0,1,4,5 conf_sw1 = 0x5d; } mem[MEM_EQUIP_LIST_0] = conf_sw1; mem[MEM_EQUIP_LIST_1] = 0x00; mem[MEM_MEM_SIZE_0] = 0x80; // 640 mem[MEM_MEM_SIZE_1] = 0x02; mem[MEM_MOTOR_OFF] = 0; mem[MEM_DISK_STATUS] = 0; mem[MEM_TIMER_COUNTER_0] = mem[MEM_TIMER_COUNTER_1] = mem[MEM_TIMER_COUNTER_2] = mem[MEM_TIMER_COUNTER_3] = 0; mem[MEM_TIMER_OVERFLOW] = 0; mem[MEM_NUM_HD] = 0; timer_now = timer.now(); // PIC pic_m_irr = pic_m_isr = pic_m_imr = 0x00; pic_m_ris = 0; // タイマー timer_0_org = timer_2_org = timer_now; timer_0_control = 0x36; timer_0_osc_cnt = timer_0_count = 0x10000; timer_0_next = timer_0_org + 0x10000 * OSC_INT; timer_2_control = 0xb6; timer_2_count = 1326; timer_0_o_lh = timer_0_i_lh = timer_2_o_lh = timer_2_i_lh = false; timer_0_latch = timer_2_latch = -1; timer_0_new_count = timer_2_new_count = 0; beep_osc.frequency.value = bell_freq = TIMER_FREQ / 1326; // 900 Hz io_parallel_b = 0x00; sound_state = 0; timer_2_gate_off = timer_now; io_dma_status = 0x04; // DMA チャネル 2 TC video_init(); // ビデオ初期化 keyboard_init(); // キーボード初期化 i_save_regs = 0; // レジスタ退避インデックス } // Video Services function bios_video() { var i_cur; var page; var char; var attr; var color; var val; var i_mem; var n; var x, y; var func; switch(func = regs[I_AH]) { case 0x00: // Set Video Mode var mode; if((mode = regs[I_AL] & 0x7f) > 7) return; mem[MEM_VIDEO_MODE] = video_adapter_mode = video_mode = mode; video_comp_color = false; switch(video_mode) { // ビデオ モード case 0: case 1: elem_scr.width = width320; elem_scr.height = height200; elem_scr.style.width = String(width320 << 1) + "px"; elem_scr.style.height = String(height200 << 1) + "px"; mem[MEM_CRTC_MODE] = (video_mode == 0) ? 0x2c : 0x28; mem[MEM_SCR_COLS_0] = video_cols = 40; mem[MEM_VBUF_SIZE_1] = 0x08; mem[MEM_CURSOR_S] = video_cursor_start = 6; mem[MEM_CURSOR_E] = video_cursor_end = 7; video_vertical_disp = 25; video_scan_lines = 8; video_i_buff_end = 64000/* 200*320 */; video_i_buff_adj = 2240/* 7*320 */; break; case 2: case 3: elem_scr.width = width640; elem_scr.height = height200; elem_scr.style.width = String(width640) + "px"; elem_scr.style.height = String(height200 << 1) + "px"; mem[MEM_CRTC_MODE] = (video_mode == 2) ? 0x2d : 0x29; mem[MEM_SCR_COLS_0] = video_cols = 80; mem[MEM_VBUF_SIZE_1] = 0x10; mem[MEM_CURSOR_S] = video_cursor_start = 6; mem[MEM_CURSOR_E] = video_cursor_end = 7; video_vertical_disp = 25; video_scan_lines = 8; video_i_buff_end = 128000/* 200*640 */; video_i_buff_adj = 4480/* 7*640 */; break; case 4: video_comp_color = true; // fall thru case 5: elem_scr.width = width320; elem_scr.height = height200; elem_scr.style.width = String(width320 << 1) + "px"; elem_scr.style.height = String(height200 << 1) + "px"; mem[MEM_CRTC_MODE] = (video_mode == 4) ? 0x2a : 0x2e; mem[MEM_SCR_COLS_0] = video_cols = 40; mem[MEM_VBUF_SIZE_1] = 0x40; mem[MEM_CURSOR_S] = mem[MEM_CURSOR_E] = 0; break; case 6: elem_scr.width = width640; elem_scr.height = height200; elem_scr.style.width = String(width640) + "px"; elem_scr.style.height = String(height200 << 1) + "px"; mem[MEM_CRTC_MODE] = 0x1e; mem[MEM_SCR_COLS_0] = video_cols = 80; mem[MEM_VBUF_SIZE_1] = 0x40; mem[MEM_CURSOR_S] = mem[MEM_CURSOR_E] = 0; break; default: // 7 elem_scr.width = width720; elem_scr.height = height350; elem_scr.style.width = String(width720) + "px"; elem_scr.style.height = String(height350) + "px"; mem[MEM_CRTC_MODE] = 0x29; mem[MEM_SCR_COLS_0] = video_cols = 80; mem[MEM_VBUF_SIZE_1] = 0x10; mem[MEM_CURSOR_S] = video_cursor_start = 11; mem[MEM_CURSOR_E] = video_cursor_end = 12; } elem_frm.style.width = elem_scr.style.width; elem_frm.style.height = elem_scr.style.height; if(video_mode == 7) { // MDA video_mem = new Uint8Array(mem.buffer, 0xb0000, 0x8000); mem[MEM_CRTC_PORT_0] = IO_MDA_0; mem[MEM_CRTC_PORT_1] = IO_MDA_1; video_lines = 370; video_line_osc_n = 1; video_line_osc_int = 0.05425355231592545; // 1000 / (16257000 / 9) * 98 } else { // CGA video_mem = new Uint8Array(mem.buffer, 0xb8000, 0x8000); video_mem_o = new Uint8Array(mem.buffer, 0xba000, 0x2000) mem[MEM_CRTC_PORT_0] = IO_CGA_0; mem[MEM_CRTC_PORT_1] = IO_CGA_1; if(video_mode == 6) { video_cga_palette[0] = video_cga_white; video_comp_palette_phase_0 = 0xff; video_comp_palette_int[0] = 0x8; } else { video_cga_palette[0] = video_cga_black; video_comp_palette_phase_0 = 0x00; video_comp_palette_int[0] = 0x0; } video_comp_palette_phase[0] = video_comp_palette_phase_0; video_cga_palette[1] = video_cga_cyan; video_cga_palette[2] = (video_mode == 5) ? video_cga_red : video_cga_magenta; video_cga_palette[3] = video_cga_lgray; if(video_comp_color) { video_comp_palette_phase[1] = video_comp_phase_cyan; video_comp_palette_phase[2] = video_comp_phase_magenta; } else { video_comp_palette_phase[1] = video_comp_palette_phase[2] = 0xff; } video_comp_palette_phase[3] = 0xff; video_comp_palette_int[1] = video_comp_palette_int[2] = video_comp_palette_int[3] = 0x0; video_palette = true; video_bright = false; mem[MEM_CRTC_COLOR] = (video_mode == 6) ? 0x2f : 0x20; video_lines = 262; video_line_osc_n = 76; // 16 * 57 / 12(8 * 114 / 12) video_line_osc_int = OSC_INT; } video_mem_2 = new Uint16Array(mem.buffer, video_mem.byteOffset, 0x4000) mem[MEM_ACTIVE_PAGE] = video_page = 0; video_start_addr = 0; mem[MEM_VBUF_START_1] = 0x00; mem.fill(0, MEM_CURSOR_POS, MEM_CURSOR_POS + 16); video_crtc_index = 0; video_cursor_mode = 0x40; video_cursor_addr = 0; video_lp_latch = 0; video_lp_trigger = 0x0; video_blink = true; video_disabled = false; video_line_org = timer_now; video_line_osc_cnt = video_line_osc_n; video_line_next = video_line_org + video_line_osc_n * video_line_osc_int; video_line_cnt = video_lines; video_color_chg = -1; ctx_scr.fillRect(0, 0, elem_scr.width, elem_scr.height); video_data = ctx_scr.getImageData(0, 0, elem_scr.width, elem_scr.height); video_buff = new Uint32Array(video_data.data.buffer); if(!(regs[I_AL] & 0x80)) { // ビデオ メモリ クリア switch(video_mode) { // ビデオ モード case 4: case 5: case 6: video_mem.fill(0, 0, 0x4000); break; default: // 0,1,2,3,7 video_mem_2.fill(video_text_clear); } } break; case 0x01: // Set Cursor Shape switch(video_mode) { // ビデオ モード case 4: case 5: case 6: break; default: mem[MEM_CURSOR_S] = video_cursor_start = regs[I_CH] & 0x1f; mem[MEM_CURSOR_E] = video_cursor_end = regs[I_CL] & 0x1f; // Bit 5,6 の設定は仕様外だが,カーソルを消すために使っているプログラムがある. // Bit 5: 1,Bit 6: 0 の場合のみ,カーソル非表示に設定する. video_cursor_mode = ((regs[I_CH] & 0x60) == 0x20) ? 0x20 : 0x40; } break; case 0x02: // Set Cursor Position var row = regs[I_DH]; var col = regs[I_DL]; switch(video_mode) { // ビデオ モード case 4: case 5: case 6: i_cur = MEM_CURSOR_POS; break; default: // 0,1,2,3,7 if((page = regs[I_BH]) >= 8) return; if(page == video_page) // 現在表示中のページ video_cursor_addr = (row >= 25 || col >= video_cols) ? 0x3fff : (page << ((video_mode < 2) ? 10 : 11)) + row * video_cols + col; i_cur = MEM_CURSOR_POS + (page << 1); } mem[i_cur] = col; mem[i_cur + 1] = row; break; case 0x03: // Get Cursor Position and Shape switch(video_mode) { // ビデオ モード case 4: case 5: case 6: regs[I_CH] = regs[I_CL] = 0; i_cur = MEM_CURSOR_POS; break; default: // 0,1,2,3,7 if((page = regs[I_BH]) >= 8) return; regs[I_CH] = video_cursor_start; regs[I_CL] = video_cursor_end; i_cur = MEM_CURSOR_POS + (page << 1); } regs[I_DL] = mem[i_cur]; regs[I_DH] = mem[i_cur + 1]; break; case 0x04: // Read Light Pen Position regs[I_AH] = 0; break; case 0x05: // Select Active Display Page switch(video_mode) { // ビデオ モード case 4: case 5: case 6: video_start_addr = 0; mem[MEM_VBUF_START_1] = 0x00; break; default: // 0,1,2,3,7 if((page = regs[I_AL]) >= 8) return; mem[MEM_ACTIVE_PAGE] = video_page = page; video_start_addr = page << ((video_mode < 2) ? 10 : 11); mem[MEM_VBUF_START_1] = page << ((video_mode < 2) ? 3 : 4); } break; case 0x06: // Clear/Scroll Up Window case 0x07: // Clear/Scroll Down Window n = regs[I_AL]; var row1 = regs[I_CH]; var col1 = regs[I_CL]; var row2 = regs[I_DH]; var col2 = regs[I_DL]; if(row1 >= 25 || col1 >= video_cols || row2 < row1 || col2 < col1) return; if(row2 >= 25) row2 = 24; if(col2 >= video_cols) col2 = video_cols - 1; var rows = row2 - row1 + 1; var i_mem2; var off; switch(video_mode) { // ビデオ モード case 4: case 5: case 6: var b2 = (video_mode == 6) ? 0 : 1; var cols_bytes = (col2 - col1 + 1) << b2; if(func == 0x06) { i_mem = row1 * 320 + (col1 << b2); if(n && n < rows) { off = n * 320; for(rows = (rows - n) << 2; rows; rows--) { video_mem.copyWithin(i_mem, i_mem + off, i_mem + off + cols_bytes); i_mem2 = i_mem + 0x2000; video_mem.copyWithin(i_mem2, i_mem2 + off, i_mem2 + off + cols_bytes); i_mem += 80; } } else { n = rows; } for(n <<= 2; n; n--) { video_mem.fill(0, i_mem, i_mem + cols_bytes); i_mem2 = i_mem + 0x2000; video_mem.fill(0, i_mem2, i_mem2 + cols_bytes); i_mem += 80; } } else { i_mem = row2 * 320 + 240 + (col1 << b2); if(n && n < rows) { off = n * 320; for(rows = (rows - n) << 2; rows; rows--) { video_mem.copyWithin(i_mem, i_mem - off, i_mem - off + cols_bytes); i_mem2 = i_mem + 0x2000; video_mem.copyWithin(i_mem2, i_mem2 - off, i_mem2 - off + cols_bytes); i_mem -= 80; } } else { n = rows; } for(n <<= 2; n; n--) { video_mem.fill(0, i_mem, i_mem + cols_bytes); i_mem2 = i_mem + 0x2000; video_mem.fill(0, i_mem2, i_mem2 + cols_bytes); i_mem -= 80; } } break; default: // 0,1,2,3,7 video_mem_work[0] = 0x20; video_mem_work[1] = regs[I_BH]; var cols = col2 - col1 + 1; if(func == 0x06) { i_mem = (video_page << ((video_mode < 2) ? 10 : 11)) + row1 * video_cols + col1; if(n && n < rows) { off = n * video_cols; for(rows -= n; rows; rows--) { video_mem_2.copyWithin(i_mem, i_mem + off, i_mem + off + cols); i_mem += video_cols; } } else { n = rows; } i_mem2 = i_mem << 1; for(; n; n--) { video_mem_2.fill(video_mem_work_2[0], i_mem, i_mem + cols); i_mem += video_cols; } } else { i_mem = (video_page << ((video_mode < 2) ? 10 : 11)) + row2 * video_cols + col1; if(n && n < rows) { off = n * video_cols; for(rows -= n; rows; rows--) { video_mem_2.copyWithin(i_mem, i_mem - off, i_mem - off + cols); i_mem -= video_cols; } } else { n = rows; } i_mem2 = i_mem << 1; for(; n; n--) { video_mem_2.fill(video_mem_work_2[0], i_mem, i_mem + cols); i_mem -= video_cols; } } } break; case 0x08: // Read Character and Attribute at Cursor switch(video_mode) { // ビデオ モード case 4: case 5: case 6: if((i_mem = cursor_pos()) < 0) return; regs[I_AL] = read_graphic_char(video_mode == 6); break; default: // 0,1,2,3,7 if((page = regs[I_BH]) >= 8) return; if((i_mem = cursor_pos()) < 0) return; regs[I_AH] = video_mem[i_mem + 1]; // 属性 regs[I_AL] = video_mem[i_mem]; // 文字 } break; case 0x09: // Write Character and Attribute at Cursor attr = regs[I_BL]; // 属性/色 // fall thru case 0x0a: // Write Character at Cursor var wa = (func == 0x09); char = regs[I_AL]; n = get_reg2(I_CX); var limit; switch(video_mode) { // ビデオ モード case 4: case 5: if((i_mem = cursor_pos()) < 0) return; limit = 40 - mem[MEM_CURSOR_POS]; if(n > limit) n = limit; attr = (wa) ? attr & 0x83 : 0x3; for(; n; n--) { write_graphic_char_4_5(attr); i_mem += 2; } break; case 6: if((i_mem = cursor_pos()) < 0) return; limit = 80 - mem[MEM_CURSOR_POS]; if(n > limit) n = limit; attr = (wa) ? attr & 0x80 : 0; for(; n; n--) { write_graphic_char_6(attr); i_mem++; } break; default: // 0,1,2,3,7 if((page = regs[I_BH]) >= 8) return; if((i_mem = cursor_pos()) < 0) return; limit = video_cols - mem[MEM_CURSOR_POS + (page << 1)]; if(n > limit) n = limit; for(; n; n--) { video_mem[i_mem] = char; // 文字 if(wa) video_mem[i_mem + 1] = attr; // 属性 i_mem += 2; } } break; case 0x0b: // Set Color Palette if(video_mode == 7) return; val = regs[I_BL]; switch(regs[I_BH]) { case 0: video_cga_palette[0] = video_cga_color[val &= 0xf]; video_comp_palette_phase_0 = video_comp_phase[val & 0x7]; if(video_comp_color) video_comp_palette_phase[0] = video_comp_palette_phase_0; else video_comp_palette_phase[0] = (video_comp_palette_phase_0) ? 0xff : 0x00; video_comp_palette_int[0] = val & 0x8; mem[MEM_CRTC_COLOR] = mem[MEM_CRTC_COLOR] & 0xf0 | val; break; case 1: if(video_palette = ((val & 0x1) != 0)) { if(video_adapter_mode != 5) { video_cga_palette[1] = video_cga_cyan; video_cga_palette[2] = video_cga_magenta; video_cga_palette[3] = video_cga_lgray; if(video_comp_color) { video_comp_palette_phase[1] = video_comp_phase_cyan; video_comp_palette_phase[2] = video_comp_phase_magenta; } else { video_comp_palette_phase[1] = video_comp_palette_phase[2] = 0xff; } video_comp_palette_phase[3] = 0xff; } mem[MEM_CRTC_COLOR] |= 0x20; } else { if(video_adapter_mode != 5) { video_cga_palette[1] = video_cga_green; video_cga_palette[2] = video_cga_red; video_cga_palette[3] = video_cga_brown; if(video_comp_color) { video_comp_palette_phase[1] = video_comp_phase_green; video_comp_palette_phase[2] = video_comp_phase_red; video_comp_palette_phase[3] = video_comp_phase_yellow; } else { video_comp_palette_phase[1] = video_comp_palette_phase[2] = video_comp_palette_phase[3] = 0xff; } } mem[MEM_CRTC_COLOR] &= 0xdf; } if(video_adapter_mode == 5) { video_cga_palette[1] = video_cga_cyan; video_cga_palette[2] = video_cga_red; video_cga_palette[3] = video_cga_lgray; video_comp_palette_phase[1] = video_comp_palette_phase[2] = video_comp_palette_phase[3] = 0xff; } video_bright = false; video_comp_palette_int[1] = video_comp_palette_int[2] = video_comp_palette_int[3] = 0x0; break; } switch(video_mode) { case 4: case 5: case 6: video_refresh_req1 = true; // 画面更新要求 break; } break; case 0x0c: // Write Graphics Pixel color = regs[I_AL]; x = get_reg2(I_CX); y = get_reg2(I_DX); var shift; switch(video_mode) { // ビデオ モード case 4: case 5: i_mem = (y >> 1) * 80 + (x >> 2); if(y & 0x1) i_mem += 0x2000; shift = (x & 0x3) << 1; val = (color & 0x3) << (6 - shift); if(color & 0x80) video_mem[i_mem] ^= val; else video_mem[i_mem] = video_mem[i_mem] & (0x3f3f >> shift) | val; break; case 6: i_mem = (y >> 1) * 80 + (x >> 3); if(y & 0x1) i_mem += 0x2000; shift = x & 0x7; val = (color & 0x1) << (7 - shift); if(color & 0x80) video_mem[i_mem] ^= val; else video_mem[i_mem] = video_mem[i_mem] & (0x7f7f >> shift) | val; break; } break; case 0x0d: // Read Graphics Pixel x = get_reg2(I_CX); y = get_reg2(I_DX); switch(video_mode) { // ビデオ モード case 4: case 5: i_mem = (y >> 1) * 80 + (x >> 2); if(y & 0x1) i_mem += 0x2000; regs[I_AL] = (video_mem[i_mem] >> (6 - ((x & 0x3) << 1))) & 0x3; break; case 6: i_mem = (y >> 1) * 80 + (x >> 3); if(y & 0x1) i_mem += 0x2000; regs[I_AL] = (video_mem[i_mem] >> (7 - (x & 0x7))) & 0x1; break; } break; case 0x0e: // Write Character in Teletype Mode switch(video_mode) { // ビデオ モード case 4: case 5: case 6: i_cur = MEM_CURSOR_POS; break; default: // 0,1,2,3,7 if((page = regs[I_BH]) >= 8) return; i_cur = MEM_CURSOR_POS + (page << 1); } if(mem[i_cur + 1] >= 25 || mem[i_cur] >= video_cols) return; switch(char = regs[I_AL]) { case 0x07: // BEL if(run_wait) // 待機中 return; bell(bell_ended_0e); return; case 0x08: // BS if(mem[i_cur]) // カラム mem[i_cur]--; break; case 0x0a: // LF if(mem[i_cur + 1] == 24) { // 行 // スクロール switch(video_mode) { // ビデオ モード case 4: case 5: case 6: tty_scroll_graphic(); break; default: // 0,1,2,3,7 tty_scroll_text(page); } } else { mem[i_cur + 1]++; // 行 } break; case 0x0d: // CR mem[i_cur] = 0; // カラム break; default: i_mem = cursor_pos(); switch(video_mode) { // ビデオ モード case 4: case 5: case 6: if(video_mode == 6) write_graphic_char_6(0); else write_graphic_char_4_5(regs[I_BL] & 0x3); if(mem[MEM_CURSOR_POS] == video_cols - 1) { if(mem[MEM_CURSOR_POS + 1] == 24) // スクロール tty_scroll_graphic(); else mem[MEM_CURSOR_POS + 1]++; mem[MEM_CURSOR_POS] = 0; } else { mem[MEM_CURSOR_POS]++; } break; default: // 0,1,2,3,7 video_mem[i_mem] = char; // 文字 if(mem[i_cur] == video_cols - 1) { // カラム if(mem[i_cur + 1] == 24) // 行 // スクロール tty_scroll_text(page); else mem[i_cur + 1]++; // 行 mem[i_cur] = 0; // カラム } else { mem[i_cur]++; // カラム } } } switch(video_mode) { // ビデオ モード case 4: case 5: case 6: break; default: // 0,1,2,3,7 if(page == video_page) // 現在表示中のページ video_cursor_addr = (page << ((video_mode < 2) ? 10 : 11)) + mem[i_cur + 1] * video_cols + mem[i_cur]; } break; case 0x0f: // Get Current Video Mode regs[I_AH] = video_cols; regs[I_AL] = video_mode; regs[I_BH] = video_page; break; case 0x13: // Write String switch(video_mode) { // ビデオ モード case 4: case 5: case 6: // グラフィック モードでは無効? return; } if((bios_ws_page = regs[I_BH]) >= 8 || (bios_ws_row = regs[I_DH]) >= 25 || (bios_ws_col = regs[I_DL]) >= video_cols) return; bios_ws_chars = get_reg2(I_CX); bios_ws_off = get_reg2(I_BP); bios_ws_upd_cur = true; switch(regs[I_AL]) { case 0x00: bios_ws_upd_cur = false; // fall thru case 0x01: bios_ws_attr = regs[I_BL]; break; case 0x02: bios_ws_upd_cur = false; // fall thru case 0x03: bios_ws_attr = -1; break; default: return; } write_string(); break; } function bell_ended_0e() { bell_stop(); return false; } // ファンクション 13H の処理 function write_string() { while(bios_ws_chars) { bios_ws_chars--; var char = mem[(es4 + bios_ws_off) & 0xfffff]; bios_ws_off = (bios_ws_off + 1) & 0xffff; switch(char) { case 0x07: // BEL if(run_wait) // 待機中 continue; bell(bell_ended_13); return; case 0x08: // BS if(bios_ws_col) bios_ws_col--; break; case 0x0a: // LF if(bios_ws_row == 24) // スクロール tty_scroll_text(bios_ws_page); else bios_ws_row++; break; case 0x0d: // CR bios_ws_col = 0; break; default: var attr; if(bios_ws_attr < 0) { attr = mem[(es4 + bios_ws_off) & 0xfffff]; bios_ws_off = (bios_ws_off + 1) & 0xffff; } else { attr = bios_ws_attr; } var i_mem = (bios_ws_page << ((video_mode < 2) ? 11 : 12)) + ((bios_ws_row * video_cols + bios_ws_col) << 1); video_mem[i_mem] = char; // 文字 video_mem[i_mem + 1] = attr; // 属性 if(bios_ws_col == video_cols - 1) { if(bios_ws_row == 24) // スクロール tty_scroll_text(bios_ws_page); else bios_ws_row++; bios_ws_col = 0; } else { bios_ws_col++; } } if(bios_ws_upd_cur) { var i_cur = MEM_CURSOR_POS + (bios_ws_page << 1); mem[i_cur] = bios_ws_col; mem[i_cur + 1] = bios_ws_row; if(bios_ws_page == video_page) // 現在表示中のページ video_cursor_addr = (bios_ws_page << ((video_mode < 2) ? 10 : 11)) + bios_ws_row * video_cols + bios_ws_col; } } function bell_ended_13() { bell_stop(); write_string(); return true; } } // グラフィック文字読み込み function read_graphic_char(resol) { for(var ext = false; ; ) { var ext_base; for(var char = 0; char < 128; char++) { var i_mem2 = i_mem; var i_font = (ext) ? ext_base + (char << 3) : char << 3; var i_line; for(i_line = 0; i_line < 8; i_line++) { var font1; if(resol) { font1 = video_mem[i_mem2]; } else { var p8 = (video_mem[i_mem2] << 8) | video_mem[i_mem2 + 1]; font1 = 0; for(var bits = 0xc000; bits; bits >>= 2) { font1 <<= 1; if(p8 & bits) font1 |= 0x1; } } if(font1 != ((ext) ? mem[(i_font + i_line) & 0xfffff] : video_font_8x8[i_font + i_line])) break; if(i_line & 0x1) i_mem2 -= 8112/* 0x2000-80 */; else i_mem2 += 0x2000; } if(i_line == 8) return (ext) ? 0x80 | char : char; } if(ext) return 0; ext_base = ((mem[0x7e] | (mem[0x7f] << 8)) << 4) + (mem[0x7c] | (mem[0x7d] << 8)); if(!ext_base) return 0; ext = true; } } // グラフィック文字書き込み ビデオ モード 4,5 function write_graphic_char_4_5(color) { var xor = color & 0x80; color &= 0x3; var i_font = (char & 0x80) ? ((mem[0x7e] | (mem[0x7f] << 8)) << 4) + (mem[0x7c] | (mem[0x7d] << 8)) + ((char & 0x7f) << 3) : char << 3; for(var i_line = 0; i_line < 8; i_line++) { var font1 = (char & 0x80) ? mem[(i_font + i_line) & 0xfffff] : video_font_8x8[i_font + i_line]; var p4_1 = (font1 & 0x80) ? color << 6 : 0; if(font1 & 0x40) p4_1 |= color << 4; if(font1 & 0x20) p4_1 |= color << 2; if(font1 & 0x10) p4_1 |= color; var p4_2 = (font1 & 0x08) ? color << 6 : 0; if(font1 & 0x04) p4_2 |= color << 4; if(font1 & 0x02) p4_2 |= color << 2; if(font1 & 0x01) p4_2 |= color; if(xor) { video_mem[i_mem] ^= p4_1; video_mem[i_mem + 1] ^= p4_2; } else { video_mem[i_mem] = p4_1; video_mem[i_mem + 1] = p4_2; } if(i_line & 0x1) i_mem -= 8112/* 0x2000-80 */; else i_mem += 0x2000; } } // グラフィック文字書き込み ビデオ モード 6 function write_graphic_char_6(xor) { var i_font = (char & 0x80) ? ((mem[0x7e] | (mem[0x7f] << 8)) << 4) + (mem[0x7c] | (mem[0x7d] << 8)) + ((char & 0x7f) << 3) : char << 3; for(var i_line = 0; i_line < 8; i_line++) { var font1 = (char & 0x80) ? mem[(i_font + i_line) & 0xfffff] : video_font_8x8[i_font + i_line]; if(xor) video_mem[i_mem] ^= font1; else video_mem[i_mem] = font1; if(i_line & 0x1) i_mem -= 8112/* 0x2000-80 */; else i_mem += 0x2000; } } // TTY スクロール テキスト モード function tty_scroll_text(page) { var i_mem = page << ((video_mode < 2) ? 10 : 11); video_mem_2.copyWithin(i_mem, i_mem + video_cols, i_mem + 25 * video_cols); i_mem += 24 * video_cols; video_mem[i_mem << 1] = 0x20; video_mem_2.fill(video_mem_2[i_mem], i_mem + 1, i_mem + video_cols); } // TTY スクロール グラフィック モード function tty_scroll_graphic() { video_mem.copyWithin( 0, 0x140/* 80*4 */, 0x1f40/* 25*80*4 */); video_mem.copyWithin(0x2000, 0x2140/* 0x2000+80*4 */, 0x3f40/* 0x2000+25*80*4 */); video_mem.fill(0, 0x1e00/* 24*80*4 */, 0x1f40/* 25*80*4 */); video_mem.fill(0, 0x3e00/* 0x2000+24*80*4 */, 0x3f40/* 0x2000+25*80*4 */); } // BEL コードによるビープ音発生 function bell(ended) { timer_2_control = 0xb6; timer_2_org = timer_now; timer_2_count = 1326; timer_2_o_lh = timer_2_i_lh = false; timer_2_latch = -1; timer_2_new_count = 0; beep_osc.frequency.value = bell_freq; speaker_old_state = io_parallel_b & 0x03; io_parallel_b |= 0x03; switch(sound_state) { case 1: // ビープ音発生中 break; case 2: // スピーカー直接出力中 spk_out.onaudioprocess = null; spk_out_gain.disconnect(); // fall thru default: // ビープ音発生中でない beep_osc_gain.connect(aud_ctx.destination); sound_state = 1; } bell_ended = ended; setTimeout(bell_timer, 500); run_wait = 1; op_pushf(); flag_i = 1; run_icnt = 0; function bell_timer() { run_wait = 0x10001; } } // BEL コードによるビープ終了 function bell_stop() { // BEL コードによるビープ音発生中に,割り込み処理でスピーカーの状態が // 変更されている可能性があるので,一応すべての状態を考慮しておく. if(speaker_old_state & 0x01) { // ゲート オン if(!(io_parallel_b & 0x01)) { // ゲート オフだった if(timer_2_org < Number.MAX_VALUE) timer_2_org += timer_now - timer_2_gate_off; } } else { if(io_parallel_b & 0x01) { // ゲート オンだった if(timer_2_org < Number.MAX_VALUE) timer_2_gate_off = timer_now; } } io_parallel_b = io_parallel_b & 0xfc | speaker_old_state; switch(speaker_old_state) { case 0x03: // ゲート オン,イネーブル if(sound_state != 1) { // ビープ音発生中でない if(sound_state == 2) { // スピーカー直接出力中 spk_out.onaudioprocess = null; spk_out_gain.disconnect(); } if((timer_2_control & 0x06) == 0x06) { // Mode 3 if(timer_2_org < Number.MAX_VALUE) { // ビープ音発生 beep_osc_gain.connect(aud_ctx.destination); sound_state = 1; break; } } sound_state = 0; } break; case 0x01: // ゲート オン,ディスエーブル switch(sound_state) { case 1: // ビープ音発生中 beep_osc_gain.disconnect(); break; case 2: // スピーカー直接出力中 spk_out.onaudioprocess = null; spk_out_gain.disconnect(); break; } sound_state = 0; break; default: // ゲート オフ if(sound_state != 2) { // スピーカー直接出力中でない if(sound_state == 1) // ビープ音発生中 beep_osc_gain.disconnect(); sound_state = 0; } } // BEL コードによるビープ終了後,ポート 61H の状態は復元されるが // カウンタ設定値は復元されない. // 8254 にはカウンタ設定値を読み取る機能が無いため,変更前のカウンタ // 設定値が不明なので復元できないものと思われる. run_wait = 0; op_popf(); } function cursor_pos() { var i_cur; switch(video_mode) { // ビデオ モード case 0: case 1: i_cur = MEM_CURSOR_POS + (page << 1); if(mem[i_cur + 1] < 25 && mem[i_cur] < 40) return (page << 11) + mem[i_cur + 1] * 80 + (mem[i_cur] << 1); break; case 4: case 5: if(mem[MEM_CURSOR_POS + 1] < 25 && mem[MEM_CURSOR_POS] < 40) return mem[MEM_CURSOR_POS + 1] * 320/* 80*4 */ + (mem[MEM_CURSOR_POS] << 1); break; case 6: if(mem[MEM_CURSOR_POS + 1] < 25 && mem[MEM_CURSOR_POS] < 80) return mem[MEM_CURSOR_POS + 1] * 320/* 80*4 */ + mem[MEM_CURSOR_POS]; break; default: // 2,3,7 i_cur = MEM_CURSOR_POS + (page << 1); if(mem[i_cur + 1] < 25 && mem[i_cur] < 80) return (page << 12) + mem[i_cur + 1] * 160 + (mem[i_cur] << 1); } return -1; } } // Equipment List function bios_equip() { regs[I_AL] = mem[MEM_EQUIP_LIST_0]; regs[I_AH] = mem[MEM_EQUIP_LIST_1]; } // Memory Size function bios_memory() { regs[I_AL] = mem[MEM_MEM_SIZE_0]; regs[I_AH] = mem[MEM_MEM_SIZE_1]; } // Low Level Disk Services function bios_disk() { var sts = 0; var i_drive; var n_cyl; var n_hed; var n_sec; var sec_bytes; var cyl; var hed; var sec; var i_image; switch(regs[I_AH]) { case 0x00: // Reset Disk System break; case 0x01: // Get Disk System Status flag_c = (regs[I_AH] = mem[MEM_DISK_STATUS]) ? 1 : 0; return; case 0x02: // Read Sectors case 0x03: // Write Sectors case 0x04: // Verify Sectors if(check_para(false)) { regs[I_AL] = 0; break; } var n = regs[I_AL]; if(!n) { sts = 0x01; break; } var bytes = n * sec_bytes; n -= n_sec - sec + 1; if(!hed && n_hed == 2) n -= n_sec; if(n > (n_cyl - cyl - 1) * n_hed * n_sec) { regs[I_AL] = 0; sts = 0x01; break; } if(regs[I_AH] == 0x04) // Verify Sectors break; var i_mem; if((i_mem = es4 + get_reg2(I_BX)) >= 0x100000) return; if(i_mem + bytes > 0x100000) { bytes = 0x100000 - i_mem; bytes -= bytes % sec_bytes; } i_image = ((cyl * n_hed + hed) * n_sec + sec - 1) * sec_bytes; if(regs[I_AH] == 0x02) { // Read Sectors mem.set(new Uint8Array(mnt_image[i_drive].buffer, i_image, bytes), i_mem); } else { if(elem_prt[i_drive].checked) { // 書き込み禁止 sts = 0x03; break; } mnt_image[i_drive].set(new Uint8Array(mem.buffer, i_mem, bytes), i_image); mnt_dirty[i_drive] = true; elem_nam[i_drive].style.color = "#CC0000"; } break; case 0x05: // Format Track if(check_para(true)) break; i_image = ((cyl * n_hed + hed) * n_sec) * sec_bytes; mnt_image[i_drive].fill (mem[(((mem[0x7a] | (mem[0x7b] << 8)) << 4) + (mem[0x78] | (mem[0x79] << 8)) + 8) & 0xfffff], // fill byte i_image, i_image + n_sec * sec_bytes); mnt_dirty[i_drive] = true; elem_nam[i_drive].style.color = "#CC0000"; break; case 0x08: // Get Drive Parameters switch(regs[I_DL]) { case 0: case 1: regs_2[I_AX_2] = 0; regs[I_BH] = 0; regs[I_CH] = 79; regs[I_CL] = 18; regs[I_DH] = 1; regs[I_BL] = 4; es4 = FD_PARAM_SEG4; set_reg2(I_DI, FD_PARAM_OFF); break; default: regs[I_AL] = regs[I_DH] = 0; regs_2[I_BX_2] = regs_2[I_CX_2] = regs_2[I_DI_2] = 0; es4 = 0; sts = 0x01; } regs[I_DL] = 2; break; case 0x17: // Set DASD Type for Format switch(regs[I_DL]) { case 0: case 1: break; default: sts = 0x01; } break; case 0x18: // Set Media Type for Format switch(regs[I_DL]) { case 0: case 1: var off; switch(get_reg2(I_CX)) { case 0x2708: off = FD_PARAM_8_OFF; break; case 0x2709: case 0x4f09: off = FD_PARAM_9_OFF; break; case 0x270a: off = FD_PARAM_10_OFF; break; case 0x4f12: off = FD_PARAM_OFF; break; default: sts = 0x0c; } if(!sts) { es4 = FD_PARAM_SEG4; set_reg2(I_DI, off); } break; default: sts = 0x01; } break; default: sts = 0x01; } flag_c = (regs[I_AH] = mem[MEM_DISK_STATUS] = sts) ? 1 : 0; function check_para(format) { switch(i_drive = regs[I_DL]) { case 0: case 1: break; default: sts = 0x01; return true; } if(mnt_image[i_drive] == undefined) { sts = 0x80; return true; } [n_cyl, n_hed, n_sec, sec_bytes] = mnt_spec[i_drive]; sec = regs[I_CL]; cyl = ((sec & 0xc0) << 2) | regs[I_CH]; sec &= 0x3f; hed = regs[I_DH]; if(format) { if(cyl >= n_cyl || hed >= n_hed) sts = 0x01; } else { if(cyl >= n_cyl || hed >= n_hed || sec == 0 || sec > n_sec) sts = 0x01; } if(sts) return true; return false; } } // Serial Port I/O function bios_serial() { switch(regs[I_AH]) { case 0x00: // Serial Port Initialization regs_2[I_AX_2] = 0; break; case 0x01: // Transmit Character regs[I_AH] = 0; break; case 0x02: // Receive Character regs_2[I_AX_2] = 0; break; case 0x03: // Status regs_2[I_AX_2] = 0; break; } } // Miscellaneous Services function bios_misc() { switch(regs[I_AH]) { case 0x88: // Get Extended Memory Size regs_2[I_AX_2] = 0; break; case 0x89: // Switch to Protected Mode regs[I_AH] = 0xff; break; case 0xc0: // Get System Parameters regs[I_AH] = 0; es4 = SYS_DESC_SEG4; set_reg2(I_BX, SYS_DESC_OFF); flag_c = 0; break; default: regs[I_AH] = 0x86; flag_c = 1; } } // Keyboard Services keyboard_code_remove = [ 0x0100,0x0e00,0x1a00,0x1b00,0x1c00,0x2700,0x2800,0x2900,0x2b00,0x3300,0x3400,0x3500, 0x3700,0x4a00,0x4cf0,0x4e00,0x8500,0x8600,0x8700,0x8800,0x8900,0x8a00,0x8b00,0x8c00,0x8d00, 0x8de0,0x8e00,0x8f00,0x9000,0x9100,0x91e0,0x9200,0x92e0,0x9300,0x93e0,0x9400,0x9500,0x9600, 0x9700,0x9800,0x9900,0x9b00,0x9d00,0x9f00,0xa000,0xa100,0xa200,0xa300,0xa400,0xa500,0xa600 ]; function bios_keyboard() { var off; switch(regs[I_AH]) { case 0x00: // Read Keyboard Input if(peek_83_key()) { if(run_wait) { // 待機中 regs_2[I_AX_2] = 0; break; } run_wait = 2; op_pushf(); flag_i = 1; run_icnt = 0; keyboard_wait_func = 0x00; break; } keyboard_i_buff_head = (keyboard_i_buff_head + 2) & 0x1f; off = KB_BUFF_OFF + keyboard_i_buff_head; mem[MEM_KB_BUFF_HEAD_0] = off & 0xff; mem[MEM_KB_BUFF_HEAD_1] = (off & 0xff00) >> 8; break; case 0x01: // Return Keyboard Status flag_z = peek_83_key(); flag_i = 1; run_icnt = 0; break; case 0x02: // Return Shift Flag Status regs[I_AL] = mem[MEM_KB_STATUS_0]; break; case 0x03: // Set Typematic Rate and Delay if(regs[I_AL] == 5) { keyboard_rep_delay = ((regs[I_BH] & 0x03) + 1) * 250; keyboard_rep_rate = keyboard_rep_rate_tbl[regs[I_BL] & 0x1f]; } break; case 0x05: // Store Key Data var new_tail = (keyboard_i_buff_tail + 2) & 0x1f; if(new_tail == keyboard_i_buff_head) { // バッファ一杯 regs[I_AL] = 1; break; } regs[I_AL] = 0; keyboard_buff[keyboard_i_buff_tail + 1] = regs[I_CH]; keyboard_buff[keyboard_i_buff_tail] = regs[I_CL]; keyboard_i_buff_tail = new_tail; off = KB_BUFF_OFF + keyboard_i_buff_tail; mem[MEM_KB_BUFF_TAIL_0] = off & 0xff; mem[MEM_KB_BUFF_TAIL_1] = (off & 0xff00) >> 8; break; case 0x10: // Read Extended Keyboard Input if(peek_101_key()) { if(run_wait) { // 待機中 regs_2[I_AX_2] = 0; break; } run_wait = 2; op_pushf(); flag_i = 1; run_icnt = 0; keyboard_wait_func = 0x10; break; } keyboard_i_buff_head = (keyboard_i_buff_head + 2) & 0x1f; off = KB_BUFF_OFF + keyboard_i_buff_head; mem[MEM_KB_BUFF_HEAD_0] = off & 0xff; mem[MEM_KB_BUFF_HEAD_1] = (off & 0xff00) >> 8; break; case 0x11: // Return Extended Keyboard Status flag_z = peek_101_key(); flag_i = 1; run_icnt = 0; break; case 0x12: // Return Extended Shift Flags Status regs[I_AL] = mem[MEM_KB_STATUS_0]; regs[I_AH] = mem[MEM_KB_STATUS_1] & 0x73 | mem[MEM_KB_FLAGS] & 0x0c | ((mem[MEM_KB_STATUS_1] & 0x04) << 5); break; } function peek_83_key() { for(; ; ) { if(keyboard_i_buff_head == keyboard_i_buff_tail) // バッファ空 return 1; var scan = keyboard_buff[keyboard_i_buff_head + 1]; var char = keyboard_buff[keyboard_i_buff_head]; if(keyboard_code_remove.indexOf((scan << 8) | char) < 0) break; keyboard_i_buff_head = (keyboard_i_buff_head + 2) & 0x1f; var off = KB_BUFF_OFF + keyboard_i_buff_head; mem[MEM_KB_BUFF_HEAD_0] = off & 0xff; mem[MEM_KB_BUFF_HEAD_1] = (off & 0xff00) >> 8; } if(scan == 0xe0) { switch(char) { case 0x0a: case 0x0d: scan = 0x1c; break; case 0x2f: scan = 0x35; break; } } else { if(char == 0xe0) char = 0x00; } regs[I_AH] = scan; regs[I_AL] = char; return 0; } function peek_101_key() { if(keyboard_i_buff_head == keyboard_i_buff_tail) // バッファ空 return 1; var scan; regs[I_AH] = scan = keyboard_buff[keyboard_i_buff_head + 1]; var char = keyboard_buff[keyboard_i_buff_head]; regs[I_AL] = (scan == 0x4c && char == 0xf0) ? 0x00 : char; return 0; } } // Printer Services function bios_printer() { switch(regs[I_AH]) { case 0x00: // Print Character to Printer case 0x01: // Initialize Printer case 0x02: // Check Printer Status regs[I_AH] = 0; break; } } // Bootstrap Loader Service function bios_boot() { boot(); } // Real Time Clock Services function bios_clock() { var now; switch(regs[I_AH]) { case 0x00: // Get System Timer Count regs[I_DL] = mem[MEM_TIMER_COUNTER_0]; regs[I_DH] = mem[MEM_TIMER_COUNTER_1]; regs[I_CL] = mem[MEM_TIMER_COUNTER_2]; regs[I_CH] = mem[MEM_TIMER_COUNTER_3]; regs[I_AL] = mem[MEM_TIMER_OVERFLOW]; mem[MEM_TIMER_OVERFLOW] = 0; break; case 0x01: // Set System Timer Count mem[MEM_TIMER_COUNTER_0] = regs[I_DL]; mem[MEM_TIMER_COUNTER_1] = regs[I_DH]; mem[MEM_TIMER_COUNTER_2] = regs[I_CL]; mem[MEM_TIMER_COUNTER_3] = regs[I_CH]; mem[MEM_TIMER_OVERFLOW] = 0; break; case 0x02: // Get RTC Time now = new Date(); regs[I_CH] = bcd(now.getHours()); regs[I_CL] = bcd(now.getMinutes()); regs[I_DH] = bcd(now.getSeconds()); regs[I_DL] = 0; flag_c = 0; break; case 0x04: // Get RTC Date now = new Date(); var year = now.getFullYear(); var year2; regs[I_CL] = bcd(year2 = year % 100); regs[I_CH] = bcd((year - year2) / 100); regs[I_DH] = bcd(now.getMonth() + 1); regs[I_DL] = bcd(now.getDate()); flag_c = 0; break; case 0x03: // Set RTC Time case 0x05: // Set RTC Date break; case 0x06: // Set RTC Alarm flag_c = 0; break; case 0x07: // Reset RTC Alarm break; } function bcd(val) { var d1 = val % 10; return (((val - d1) / 10) << 4) | d1; } } function timer_0_update() { if(timer_0_control & 0x04) { // Mode 2,3 // CGA の CRTC のクロックとタイマーのクロックは,同じ発振器の出力を元にしているため // 完全に同期する. // そのことを前提としているプログラムがあるので,浮動小数点数の誤差の累積によって // 両者の同期が次第にずれてゆくことが無いように注意する必要がある. if(timer_0_new_count) { // タイマー値が変わった timer_0_org = timer_0_next; timer_0_osc_cnt = timer_0_count = timer_0_new_count; timer_0_new_count = 0; } else { timer_0_osc_cnt += timer_0_count; } timer_0_next = timer_0_org + timer_0_osc_cnt * OSC_INT; } else { timer_0_next = Number.MAX_VALUE; } } // システム タイマー割り込み // INT 08H 割り込みをフックするプログラムのため,割り込みをエミュレートする. function interval_int() { pic_m_isr |= 0x01; pic_m_irr &= 0xfe; if(run_hwint_ss < 0) { run_hwint_ss = ss4; run_hwint_sp = regs_2[I_SP_2]; } run_halt = false; int(0x08); } // システム タイマー割り込み デフォルト処理 function interval_int_def() { var ticks; if(mem[MEM_TIMER_COUNTER_3]) { ticks = 0; mem[MEM_TIMER_OVERFLOW] = 1; } else { if((ticks = (mem[MEM_TIMER_COUNTER_0] | (mem[MEM_TIMER_COUNTER_1] << 8) | (mem[MEM_TIMER_COUNTER_2] << 16)) + 1) >= 1573040) { ticks = 0; mem[MEM_TIMER_OVERFLOW] = 1; } } mem[MEM_TIMER_COUNTER_0] = ticks & 0xff; mem[MEM_TIMER_COUNTER_1] = (ticks & 0xff00) >> 8; mem[MEM_TIMER_COUNTER_2] = (ticks & 0xff0000) >> 16; mem[MEM_TIMER_COUNTER_3] = 0; if(mem[MEM_MOTOR_OFF]) mem[MEM_MOTOR_OFF]--; // INT 1CH flag_i = 1; run_icnt = 0; // レジスタ退避 save_regs[i_save_regs] = ds4 >> 4; save_regs[i_save_regs + 1] = es4 >> 4; [save_regs[i_save_regs + 2], save_regs[i_save_regs + 3], save_regs[i_save_regs + 4], save_regs[i_save_regs + 5], , save_regs[i_save_regs + 6], save_regs[i_save_regs + 7], save_regs[i_save_regs + 8] ] = regs_2; i_save_regs += 9; ip++; int(0x1c); } // INT 1CH 終了 function interval_int_def_2() { pic_m_isr &= 0xfe; // EOI // レジスタ復旧 i_save_regs -= 9; ds4 = save_regs[i_save_regs] << 4; es4 = save_regs[i_save_regs + 1] << 4; regs_2[I_AX_2] = save_regs[i_save_regs + 2]; regs_2[I_CX_2] = save_regs[i_save_regs + 3]; regs_2[I_DX_2] = save_regs[i_save_regs + 4]; regs_2[I_BX_2] = save_regs[i_save_regs + 5]; regs_2[I_BP_2] = save_regs[i_save_regs + 6]; regs_2[I_SI_2] = save_regs[i_save_regs + 7]; regs_2[I_DI_2] = save_regs[i_save_regs + 8]; } // スピーカー直接出力 PCM データ作成 function spk_out_dummy_proc(e) { e.outputBuffer.getChannelData(0).fill(0); if(timer.now() >= spk_out_time + spk_out_int) spk_out.onaudioprocess = spk_out_proc; } function spk_out_proc(e) { var pcm = e.outputBuffer.getChannelData(0); var i_pcm = 0; var next = spk_out_time + spk_out_int; if(spk_out_i_get == spk_out_i_put) { // バッファ空 if(timer.now() >= spk_out_get_prev + spk_out_buff[(spk_out_i_get - 1) & 0x7ff] + 100) { spk_out.onaudioprocess = null; spk_out_gain.disconnect(); sound_state = 0; return; } } else { while(spk_out_i_get != spk_out_i_put) { var time = spk_out_get_prev + spk_out_buff[spk_out_i_get]; if(time > next) break; var i = Math.floor((time - spk_out_time) * spk_out_rate); for(; i_pcm < i; i_pcm++) pcm[i_pcm] = spk_out_val; spk_out_i_get = (spk_out_i_get + 1) & 0x7ff; spk_out_get_prev = time; spk_out_val = 1 - spk_out_val; } } for(; i_pcm < 512; i_pcm++) pcm[i_pcm] = spk_out_val; spk_out_time = next; } // ---- ビデオ エミュレーション ---- // 8x8 フォント video_font_8x8 = new Uint8Array([ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x7e,0x81,0xa5,0x81,0xbd,0x99,0x81,0x7e, 0x7e,0xff,0xdb,0xff,0xc3,0xe7,0xff,0x7e, 0x6c,0xfe,0xfe,0xfe,0x7c,0x38,0x10,0x00, 0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00, 0x38,0x7c,0x38,0xfe,0xfe,0x7c,0x38,0x7c, 0x10,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x7c, 0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00, 0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff, 0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00, 0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff, 0x0f,0x07,0x0f,0x7d,0xcc,0xcc,0xcc,0x78, 0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18, 0x3f,0x33,0x3f,0x30,0x30,0x70,0xf0,0xe0, 0x7f,0x63,0x7f,0x63,0x63,0x67,0xe6,0xc0, 0x99,0x5a,0x3c,0xe7,0xe7,0x3c,0x5a,0x99, 0x80,0xe0,0xf8,0xfe,0xf8,0xe0,0x80,0x00, 0x02,0x0e,0x3e,0xfe,0x3e,0x0e,0x02,0x00, 0x18,0x3c,0x7e,0x18,0x18,0x7e,0x3c,0x18, 0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x00, 0x7f,0xdb,0xdb,0x7b,0x1b,0x1b,0x1b,0x00, 0x3e,0x63,0x38,0x6c,0x6c,0x38,0xcc,0x78, 0x00,0x00,0x00,0x00,0x7e,0x7e,0x7e,0x00, 0x18,0x3c,0x7e,0x18,0x7e,0x3c,0x18,0xff, 0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x00, 0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00, 0x00,0x18,0x0c,0xfe,0x0c,0x18,0x00,0x00, 0x00,0x30,0x60,0xfe,0x60,0x30,0x00,0x00, 0x00,0x00,0xc0,0xc0,0xc0,0xfe,0x00,0x00, 0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00, 0x00,0x18,0x3c,0x7e,0xff,0xff,0x00,0x00, 0x00,0xff,0xff,0x7e,0x3c,0x18,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00, 0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00, 0x6c,0x6c,0xfe,0x6c,0xfe,0x6c,0x6c,0x00, 0x30,0x7c,0xc0,0x78,0x0c,0xf8,0x30,0x00, 0x00,0xc6,0xcc,0x18,0x30,0x66,0xc6,0x00, 0x38,0x6c,0x38,0x76,0xdc,0xcc,0x76,0x00, 0x60,0x60,0xc0,0x00,0x00,0x00,0x00,0x00, 0x18,0x30,0x60,0x60,0x60,0x30,0x18,0x00, 0x60,0x30,0x18,0x18,0x18,0x30,0x60,0x00, 0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00, 0x00,0x30,0x30,0xfc,0x30,0x30,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x60, 0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00, 0x06,0x0c,0x18,0x30,0x60,0xc0,0x80,0x00, 0x7c,0xc6,0xce,0xde,0xf6,0xe6,0x7c,0x00, 0x30,0x70,0x30,0x30,0x30,0x30,0xfc,0x00, 0x78,0xcc,0x0c,0x38,0x60,0xcc,0xfc,0x00, 0x78,0xcc,0x0c,0x38,0x0c,0xcc,0x78,0x00, 0x1c,0x3c,0x6c,0xcc,0xfe,0x0c,0x1e,0x00, 0xfc,0xc0,0xf8,0x0c,0x0c,0xcc,0x78,0x00, 0x38,0x60,0xc0,0xf8,0xcc,0xcc,0x78,0x00, 0xfc,0xcc,0x0c,0x18,0x30,0x30,0x30,0x00, 0x78,0xcc,0xcc,0x78,0xcc,0xcc,0x78,0x00, 0x78,0xcc,0xcc,0x7c,0x0c,0x18,0x70,0x00, 0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x00, 0x00,0x30,0x30,0x00,0x00,0x30,0x30,0x60, 0x18,0x30,0x60,0xc0,0x60,0x30,0x18,0x00, 0x00,0x00,0xfc,0x00,0x00,0xfc,0x00,0x00, 0x60,0x30,0x18,0x0c,0x18,0x30,0x60,0x00, 0x78,0xcc,0x0c,0x18,0x30,0x00,0x30,0x00, 0x7c,0xc6,0xde,0xde,0xde,0xc0,0x78,0x00, 0x30,0x78,0xcc,0xcc,0xfc,0xcc,0xcc,0x00, 0xfc,0x66,0x66,0x7c,0x66,0x66,0xfc,0x00, 0x3c,0x66,0xc0,0xc0,0xc0,0x66,0x3c,0x00, 0xf8,0x6c,0x66,0x66,0x66,0x6c,0xf8,0x00, 0xfe,0x62,0x68,0x78,0x68,0x62,0xfe,0x00, 0xfe,0x62,0x68,0x78,0x68,0x60,0xf0,0x00, 0x3c,0x66,0xc0,0xc0,0xce,0x66,0x3e,0x00, 0xcc,0xcc,0xcc,0xfc,0xcc,0xcc,0xcc,0x00, 0x78,0x30,0x30,0x30,0x30,0x30,0x78,0x00, 0x1e,0x0c,0x0c,0x0c,0xcc,0xcc,0x78,0x00, 0xe6,0x66,0x6c,0x78,0x6c,0x66,0xe6,0x00, 0xf0,0x60,0x60,0x60,0x62,0x66,0xfe,0x00, 0xc6,0xee,0xfe,0xfe,0xd6,0xc6,0xc6,0x00, 0xc6,0xe6,0xf6,0xde,0xce,0xc6,0xc6,0x00, 0x38,0x6c,0xc6,0xc6,0xc6,0x6c,0x38,0x00, 0xfc,0x66,0x66,0x7c,0x60,0x60,0xf0,0x00, 0x78,0xcc,0xcc,0xcc,0xdc,0x78,0x1c,0x00, 0xfc,0x66,0x66,0x7c,0x6c,0x66,0xe6,0x00, 0x78,0xcc,0xe0,0x70,0x1c,0xcc,0x78,0x00, 0xfc,0xb4,0x30,0x30,0x30,0x30,0x78,0x00, 0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xfc,0x00, 0xcc,0xcc,0xcc,0xcc,0xcc,0x78,0x30,0x00, 0xc6,0xc6,0xc6,0xd6,0xfe,0xee,0xc6,0x00, 0xc6,0xc6,0x6c,0x38,0x38,0x6c,0xc6,0x00, 0xcc,0xcc,0xcc,0x78,0x30,0x30,0x78,0x00, 0xfe,0xc6,0x8c,0x18,0x32,0x66,0xfe,0x00, 0x78,0x60,0x60,0x60,0x60,0x60,0x78,0x00, 0xc0,0x60,0x30,0x18,0x0c,0x06,0x02,0x00, 0x78,0x18,0x18,0x18,0x18,0x18,0x78,0x00, 0x10,0x38,0x6c,0xc6,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x78,0x0c,0x7c,0xcc,0x76,0x00, 0xe0,0x60,0x60,0x7c,0x66,0x66,0xdc,0x00, 0x00,0x00,0x78,0xcc,0xc0,0xcc,0x78,0x00, 0x1c,0x0c,0x0c,0x7c,0xcc,0xcc,0x76,0x00, 0x00,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00, 0x38,0x6c,0x60,0xf0,0x60,0x60,0xf0,0x00, 0x00,0x00,0x76,0xcc,0xcc,0x7c,0x0c,0xf8, 0xe0,0x60,0x6c,0x76,0x66,0x66,0xe6,0x00, 0x30,0x00,0x70,0x30,0x30,0x30,0x78,0x00, 0x0c,0x00,0x0c,0x0c,0x0c,0xcc,0xcc,0x78, 0xe0,0x60,0x66,0x6c,0x78,0x6c,0xe6,0x00, 0x70,0x30,0x30,0x30,0x30,0x30,0x78,0x00, 0x00,0x00,0xcc,0xfe,0xfe,0xd6,0xc6,0x00, 0x00,0x00,0xf8,0xcc,0xcc,0xcc,0xcc,0x00, 0x00,0x00,0x78,0xcc,0xcc,0xcc,0x78,0x00, 0x00,0x00,0xdc,0x66,0x66,0x7c,0x60,0xf0, 0x00,0x00,0x76,0xcc,0xcc,0x7c,0x0c,0x1e, 0x00,0x00,0xdc,0x76,0x66,0x60,0xf0,0x00, 0x00,0x00,0x7c,0xc0,0x78,0x0c,0xf8,0x00, 0x10,0x30,0x7c,0x30,0x30,0x34,0x18,0x00, 0x00,0x00,0xcc,0xcc,0xcc,0xcc,0x76,0x00, 0x00,0x00,0xcc,0xcc,0xcc,0x78,0x30,0x00, 0x00,0x00,0xc6,0xd6,0xfe,0xfe,0x6c,0x00, 0x00,0x00,0xc6,0x6c,0x38,0x6c,0xc6,0x00, 0x00,0x00,0xcc,0xcc,0xcc,0x7c,0x0c,0xf8, 0x00,0x00,0xfc,0x98,0x30,0x64,0xfc,0x00, 0x1c,0x30,0x30,0xe0,0x30,0x30,0x1c,0x00, 0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00, 0xe0,0x30,0x30,0x1c,0x30,0x30,0xe0,0x00, 0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0x00, 0x78,0xcc,0xc0,0xcc,0x78,0x18,0x0c,0x78, 0x00,0xcc,0x00,0xcc,0xcc,0xcc,0x7e,0x00, 0x1c,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00, 0x7e,0xc3,0x3c,0x06,0x3e,0x66,0x3f,0x00, 0xcc,0x00,0x78,0x0c,0x7c,0xcc,0x7e,0x00, 0xe0,0x00,0x78,0x0c,0x7c,0xcc,0x7e,0x00, 0x30,0x30,0x78,0x0c,0x7c,0xcc,0x7e,0x00, 0x00,0x00,0x78,0xc0,0xc0,0x78,0x0c,0x38, 0x7e,0xc3,0x3c,0x66,0x7e,0x60,0x3c,0x00, 0xcc,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00, 0xe0,0x00,0x78,0xcc,0xfc,0xc0,0x78,0x00, 0xcc,0x00,0x70,0x30,0x30,0x30,0x78,0x00, 0x7c,0xc6,0x38,0x18,0x18,0x18,0x3c,0x00, 0xe0,0x00,0x70,0x30,0x30,0x30,0x78,0x00, 0xc6,0x38,0x6c,0xc6,0xfe,0xc6,0xc6,0x00, 0x30,0x30,0x00,0x78,0xcc,0xfc,0xcc,0x00, 0x1c,0x00,0xfc,0x60,0x78,0x60,0xfc,0x00, 0x00,0x00,0x7f,0x0c,0x7f,0xcc,0x7f,0x00, 0x3e,0x6c,0xcc,0xfe,0xcc,0xcc,0xce,0x00, 0x78,0xcc,0x00,0x78,0xcc,0xcc,0x78,0x00, 0x00,0xcc,0x00,0x78,0xcc,0xcc,0x78,0x00, 0x00,0xe0,0x00,0x78,0xcc,0xcc,0x78,0x00, 0x78,0xcc,0x00,0xcc,0xcc,0xcc,0x7e,0x00, 0x00,0xe0,0x00,0xcc,0xcc,0xcc,0x7e,0x00, 0x00,0xcc,0x00,0xcc,0xcc,0x7c,0x0c,0xf8, 0xc3,0x18,0x3c,0x66,0x66,0x3c,0x18,0x00, 0xcc,0x00,0xcc,0xcc,0xcc,0xcc,0x78,0x00, 0x18,0x18,0x7e,0xc0,0xc0,0x7e,0x18,0x18, 0x38,0x6c,0x64,0xf0,0x60,0xe6,0xfc,0x00, 0xcc,0xcc,0x78,0xfc,0x30,0xfc,0x30,0x30, 0xf8,0xcc,0xcc,0xfa,0xc6,0xcf,0xc6,0xc7, 0x0e,0x1b,0x18,0x3c,0x18,0x18,0xd8,0x70, 0x1c,0x00,0x78,0x0c,0x7c,0xcc,0x7e,0x00, 0x38,0x00,0x70,0x30,0x30,0x30,0x78,0x00, 0x00,0x1c,0x00,0x78,0xcc,0xcc,0x78,0x00, 0x00,0x1c,0x00,0xcc,0xcc,0xcc,0x7e,0x00, 0x00,0xf8,0x00,0xf8,0xcc,0xcc,0xcc,0x00, 0xfc,0x00,0xcc,0xec,0xfc,0xdc,0xcc,0x00, 0x3c,0x6c,0x6c,0x3e,0x00,0x7e,0x00,0x00, 0x38,0x6c,0x6c,0x38,0x00,0x7c,0x00,0x00, 0x30,0x00,0x30,0x60,0xc0,0xcc,0x78,0x00, 0x00,0x00,0x00,0xfc,0xc0,0xc0,0x00,0x00, 0x00,0x00,0x00,0xfc,0x0c,0x0c,0x00,0x00, 0xc3,0xc6,0xcc,0xde,0x33,0x66,0xcc,0x0f, 0xc3,0xc6,0xcc,0xdb,0x37,0x6f,0xcf,0x03, 0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x00, 0x00,0x33,0x66,0xcc,0x66,0x33,0x00,0x00, 0x00,0xcc,0x66,0x33,0x66,0xcc,0x00,0x00, 0x22,0x88,0x22,0x88,0x22,0x88,0x22,0x88, 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, 0xdb,0x77,0xdb,0xee,0xdb,0x77,0xdb,0xee, 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18, 0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18, 0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36, 0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36, 0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18, 0x36,0x36,0xf6,0x06,0xf6,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, 0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36, 0x36,0x36,0xf6,0x06,0xfe,0x00,0x00,0x00, 0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00, 0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00, 0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18, 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00, 0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18, 0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18, 0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36, 0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00, 0x00,0x00,0x3f,0x30,0x37,0x36,0x36,0x36, 0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00, 0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36, 0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36, 0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00, 0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36, 0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00, 0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00, 0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18, 0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0x3f,0x00,0x00,0x00, 0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00, 0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18, 0x00,0x00,0x00,0x00,0x3f,0x36,0x36,0x36, 0x36,0x36,0x36,0x36,0xff,0x36,0x36,0x36, 0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0, 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, 0x00,0x00,0x76,0xdc,0xc8,0xdc,0x76,0x00, 0x00,0x78,0xcc,0xf8,0xcc,0xf8,0xc0,0xc0, 0x00,0xfc,0xcc,0xc0,0xc0,0xc0,0xc0,0x00, 0x00,0xfe,0x6c,0x6c,0x6c,0x6c,0x6c,0x00, 0xfc,0xcc,0x60,0x30,0x60,0xcc,0xfc,0x00, 0x00,0x00,0x7e,0xd8,0xd8,0xd8,0x70,0x00, 0x00,0x66,0x66,0x66,0x66,0x7c,0x60,0xc0, 0x00,0x76,0xdc,0x18,0x18,0x18,0x18,0x00, 0xfc,0x30,0x78,0xcc,0xcc,0x78,0x30,0xfc, 0x38,0x6c,0xc6,0xfe,0xc6,0x6c,0x38,0x00, 0x38,0x6c,0xc6,0xc6,0x6c,0x6c,0xee,0x00, 0x1c,0x30,0x18,0x7c,0xcc,0xcc,0x78,0x00, 0x00,0x00,0x7e,0xdb,0xdb,0x7e,0x00,0x00, 0x06,0x0c,0x7e,0xdb,0xdb,0x7e,0x60,0xc0, 0x38,0x60,0xc0,0xf8,0xc0,0x60,0x38,0x00, 0x78,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x00, 0x00,0xfc,0x00,0xfc,0x00,0xfc,0x00,0x00, 0x30,0x30,0xfc,0x30,0x30,0x00,0xfc,0x00, 0x60,0x30,0x18,0x30,0x60,0x00,0xfc,0x00, 0x18,0x30,0x60,0x30,0x18,0x00,0xfc,0x00, 0x0e,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0x70, 0x30,0x30,0x00,0xfc,0x00,0x30,0x30,0x00, 0x00,0x76,0xdc,0x00,0x76,0xdc,0x00,0x00, 0x38,0x6c,0x6c,0x38,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00, 0x0f,0x0c,0x0c,0x0c,0xec,0x6c,0x3c,0x1c, 0x78,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00, 0x70,0x18,0x30,0x60,0x78,0x00,0x00,0x00, 0x00,0x00,0x3c,0x3c,0x3c,0x3c,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ]); // 9x14 フォント video_font_9x14 = new Uint16Array([ 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x0fc,0x102,0x14a,0x102,0x102,0x17a,0x132,0x102,0x0fc,0x000,0x000,0x000, 0x000,0x000,0x0fc,0x1fe,0x1b6,0x1fe,0x1fe,0x186,0x1ce,0x1fe,0x0fc,0x000,0x000,0x000, 0x000,0x000,0x000,0x06c,0x0fe,0x0fe,0x0fe,0x0fe,0x07c,0x038,0x010,0x000,0x000,0x000, 0x000,0x000,0x000,0x010,0x038,0x07c,0x0fe,0x07c,0x038,0x010,0x000,0x000,0x000,0x000, 0x000,0x000,0x030,0x078,0x078,0x1ce,0x1ce,0x1ce,0x030,0x030,0x078,0x000,0x000,0x000, 0x000,0x000,0x030,0x078,0x0fc,0x1fe,0x1fe,0x0fc,0x030,0x030,0x078,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x030,0x078,0x078,0x030,0x000,0x000,0x000,0x000,0x000, 0x1fe,0x1fe,0x1fe,0x1fe,0x1fe,0x1ce,0x186,0x186,0x1ce,0x1fe,0x1fe,0x1fe,0x1fe,0x1fe, 0x000,0x000,0x000,0x000,0x078,0x0cc,0x084,0x084,0x0cc,0x078,0x000,0x000,0x000,0x000, 0x1fe,0x1fe,0x1fe,0x1fe,0x186,0x132,0x17a,0x17a,0x132,0x186,0x1fe,0x1fe,0x1fe,0x1fe, 0x000,0x000,0x01e,0x00e,0x01a,0x032,0x078,0x0cc,0x0cc,0x0cc,0x078,0x000,0x000,0x000, 0x000,0x000,0x078,0x0cc,0x0cc,0x0cc,0x078,0x030,0x0fc,0x030,0x030,0x000,0x000,0x000, 0x000,0x000,0x07e,0x066,0x07e,0x060,0x060,0x060,0x0e0,0x1e0,0x1c0,0x000,0x000,0x000, 0x000,0x000,0x0fe,0x0c6,0x0fe,0x0c6,0x0c6,0x0c6,0x0ce,0x1ce,0x1cc,0x180,0x000,0x000, 0x000,0x000,0x030,0x030,0x1b6,0x078,0x1ce,0x078,0x1b6,0x030,0x030,0x000,0x000,0x000, 0x000,0x000,0x080,0x0c0,0x0e0,0x0f8,0x0fe,0x0f8,0x0e0,0x0c0,0x080,0x000,0x000,0x000, 0x000,0x000,0x002,0x006,0x00e,0x03e,0x0fe,0x03e,0x00e,0x006,0x002,0x000,0x000,0x000, 0x000,0x000,0x030,0x078,0x0fc,0x030,0x030,0x030,0x0fc,0x078,0x030,0x000,0x000,0x000, 0x000,0x000,0x066,0x066,0x066,0x066,0x066,0x066,0x000,0x066,0x066,0x000,0x000,0x000, 0x000,0x000,0x0fe,0x1b6,0x1b6,0x1b6,0x0f6,0x036,0x036,0x036,0x036,0x000,0x000,0x000, 0x000,0x07c,0x0c6,0x060,0x038,0x06c,0x0c6,0x0c6,0x06c,0x038,0x00c,0x0c6,0x07c,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x0fe,0x0fe,0x0fe,0x000,0x000,0x000, 0x000,0x000,0x030,0x078,0x0fc,0x030,0x030,0x030,0x0fc,0x078,0x030,0x0fc,0x000,0x000, 0x000,0x000,0x030,0x078,0x0fc,0x030,0x030,0x030,0x030,0x030,0x030,0x000,0x000,0x000, 0x000,0x000,0x030,0x030,0x030,0x030,0x030,0x030,0x0fc,0x078,0x030,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x018,0x00c,0x0fe,0x00c,0x018,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x030,0x060,0x0fe,0x060,0x030,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x0c0,0x0c0,0x0c0,0x0fe,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x048,0x0cc,0x1fe,0x0cc,0x048,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x010,0x038,0x038,0x07c,0x07c,0x0fe,0x0fe,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x0fe,0x0fe,0x07c,0x07c,0x038,0x038,0x010,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x030,0x078,0x078,0x078,0x030,0x030,0x000,0x030,0x030,0x000,0x000,0x000, 0x000,0x0c6,0x0c6,0x0c6,0x044,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x06c,0x06c,0x0fe,0x06c,0x06c,0x06c,0x0fe,0x06c,0x06c,0x000,0x000,0x000, 0x018,0x018,0x07c,0x0c6,0x0c2,0x0c0,0x07c,0x006,0x086,0x0c6,0x07c,0x018,0x018,0x000, 0x000,0x000,0x000,0x000,0x0c2,0x0c6,0x00c,0x018,0x030,0x066,0x0c6,0x000,0x000,0x000, 0x000,0x000,0x038,0x06c,0x06c,0x038,0x076,0x0dc,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x060,0x060,0x060,0x0c0,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x018,0x030,0x060,0x060,0x060,0x060,0x060,0x030,0x018,0x000,0x000,0x000, 0x000,0x000,0x030,0x018,0x00c,0x00c,0x00c,0x00c,0x00c,0x018,0x030,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x0cc,0x078,0x1fe,0x078,0x0cc,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x030,0x030,0x030,0x1fe,0x030,0x030,0x030,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x030,0x030,0x030,0x060,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x1fe,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x030,0x030,0x000,0x000,0x000, 0x000,0x000,0x002,0x006,0x00c,0x018,0x030,0x060,0x0c0,0x080,0x000,0x000,0x000,0x000, 0x000,0x000,0x07c,0x0c6,0x0ce,0x0de,0x0f6,0x0e6,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x018,0x038,0x078,0x018,0x018,0x018,0x018,0x018,0x07e,0x000,0x000,0x000, 0x000,0x000,0x07c,0x0c6,0x006,0x00c,0x018,0x030,0x060,0x0c6,0x0fe,0x000,0x000,0x000, 0x000,0x000,0x07c,0x0c6,0x006,0x006,0x03c,0x006,0x006,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x00c,0x01c,0x03c,0x06c,0x0cc,0x0fe,0x00c,0x00c,0x01e,0x000,0x000,0x000, 0x000,0x000,0x0fe,0x0c0,0x0c0,0x0c0,0x0fc,0x006,0x006,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x038,0x060,0x0c0,0x0c0,0x0fc,0x0c6,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x0fe,0x0c6,0x006,0x00c,0x018,0x030,0x030,0x030,0x030,0x000,0x000,0x000, 0x000,0x000,0x07c,0x0c6,0x0c6,0x0c6,0x07c,0x0c6,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x07c,0x0c6,0x0c6,0x0c6,0x07e,0x006,0x006,0x00c,0x078,0x000,0x000,0x000, 0x000,0x000,0x000,0x030,0x030,0x000,0x000,0x000,0x030,0x030,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x030,0x030,0x000,0x000,0x000,0x030,0x030,0x060,0x000,0x000,0x000, 0x000,0x000,0x00c,0x018,0x030,0x060,0x0c0,0x060,0x030,0x018,0x00c,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x0fc,0x000,0x000,0x0fc,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x0c0,0x060,0x030,0x018,0x00c,0x018,0x030,0x060,0x0c0,0x000,0x000,0x000, 0x000,0x000,0x07c,0x0c6,0x0c6,0x00c,0x018,0x018,0x000,0x018,0x018,0x000,0x000,0x000, 0x000,0x000,0x07c,0x0c6,0x0c6,0x0de,0x0de,0x0de,0x0dc,0x0c0,0x07c,0x000,0x000,0x000, 0x000,0x000,0x010,0x038,0x06c,0x0c6,0x0c6,0x0fe,0x0c6,0x0c6,0x0c6,0x000,0x000,0x000, 0x000,0x000,0x0fc,0x066,0x066,0x066,0x07c,0x066,0x066,0x066,0x0fc,0x000,0x000,0x000, 0x000,0x000,0x03c,0x066,0x0c2,0x0c0,0x0c0,0x0c0,0x0c2,0x066,0x03c,0x000,0x000,0x000, 0x000,0x000,0x0f8,0x06c,0x066,0x066,0x066,0x066,0x066,0x06c,0x0f8,0x000,0x000,0x000, 0x000,0x000,0x0fe,0x066,0x062,0x068,0x078,0x068,0x062,0x066,0x0fe,0x000,0x000,0x000, 0x000,0x000,0x0fe,0x066,0x062,0x068,0x078,0x068,0x060,0x060,0x0f0,0x000,0x000,0x000, 0x000,0x000,0x03c,0x066,0x0c2,0x0c0,0x0c0,0x0de,0x0c6,0x066,0x03a,0x000,0x000,0x000, 0x000,0x000,0x0c6,0x0c6,0x0c6,0x0c6,0x0fe,0x0c6,0x0c6,0x0c6,0x0c6,0x000,0x000,0x000, 0x000,0x000,0x078,0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x078,0x000,0x000,0x000, 0x000,0x000,0x01e,0x00c,0x00c,0x00c,0x00c,0x00c,0x0cc,0x0cc,0x078,0x000,0x000,0x000, 0x000,0x000,0x0e6,0x066,0x06c,0x06c,0x078,0x06c,0x06c,0x066,0x0e6,0x000,0x000,0x000, 0x000,0x000,0x0f0,0x060,0x060,0x060,0x060,0x060,0x062,0x066,0x0fe,0x000,0x000,0x000, 0x000,0x000,0x186,0x1ce,0x1fe,0x1b6,0x186,0x186,0x186,0x186,0x186,0x000,0x000,0x000, 0x000,0x000,0x0c6,0x0e6,0x0f6,0x0fe,0x0de,0x0ce,0x0c6,0x0c6,0x0c6,0x000,0x000,0x000, 0x000,0x000,0x038,0x06c,0x0c6,0x0c6,0x0c6,0x0c6,0x0c6,0x06c,0x038,0x000,0x000,0x000, 0x000,0x000,0x0fc,0x066,0x066,0x066,0x07c,0x060,0x060,0x060,0x0f0,0x000,0x000,0x000, 0x000,0x000,0x07c,0x0c6,0x0c6,0x0c6,0x0c6,0x0d6,0x0de,0x07c,0x00c,0x00e,0x000,0x000, 0x000,0x000,0x0fc,0x066,0x066,0x066,0x07c,0x06c,0x066,0x066,0x0e6,0x000,0x000,0x000, 0x000,0x000,0x07c,0x0c6,0x0c6,0x060,0x038,0x00c,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x1fe,0x1b6,0x132,0x030,0x030,0x030,0x030,0x030,0x078,0x000,0x000,0x000, 0x000,0x000,0x0c6,0x0c6,0x0c6,0x0c6,0x0c6,0x0c6,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x186,0x186,0x186,0x186,0x186,0x186,0x0cc,0x078,0x030,0x000,0x000,0x000, 0x000,0x000,0x186,0x186,0x186,0x186,0x1b6,0x1b6,0x1fe,0x0cc,0x0cc,0x000,0x000,0x000, 0x000,0x000,0x186,0x186,0x0cc,0x078,0x030,0x078,0x0cc,0x186,0x186,0x000,0x000,0x000, 0x000,0x000,0x186,0x186,0x186,0x0cc,0x078,0x030,0x030,0x030,0x078,0x000,0x000,0x000, 0x000,0x000,0x1fe,0x186,0x10c,0x018,0x030,0x060,0x0c2,0x186,0x1fe,0x000,0x000,0x000, 0x000,0x000,0x078,0x060,0x060,0x060,0x060,0x060,0x060,0x060,0x078,0x000,0x000,0x000, 0x000,0x000,0x080,0x0c0,0x0e0,0x070,0x038,0x01c,0x00e,0x006,0x002,0x000,0x000,0x000, 0x000,0x000,0x078,0x018,0x018,0x018,0x018,0x018,0x018,0x018,0x078,0x000,0x000,0x000, 0x010,0x038,0x06c,0x0c6,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x1fe,0x000, 0x030,0x030,0x018,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x078,0x00c,0x07c,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x000,0x0e0,0x060,0x060,0x078,0x06c,0x066,0x066,0x066,0x0dc,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x07c,0x0c6,0x0c0,0x0c0,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x01c,0x00c,0x00c,0x03c,0x06c,0x0cc,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x07c,0x0c6,0x0fe,0x0c0,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x038,0x06c,0x064,0x060,0x0f8,0x060,0x060,0x060,0x0f0,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x076,0x0cc,0x0cc,0x0cc,0x07c,0x00c,0x0cc,0x078,0x000, 0x000,0x000,0x0e0,0x060,0x060,0x06c,0x076,0x066,0x066,0x066,0x0e6,0x000,0x000,0x000, 0x000,0x000,0x018,0x018,0x000,0x038,0x018,0x018,0x018,0x018,0x03c,0x000,0x000,0x000, 0x000,0x000,0x00c,0x00c,0x000,0x01c,0x00c,0x00c,0x00c,0x00c,0x0cc,0x0cc,0x078,0x000, 0x000,0x000,0x0e0,0x060,0x060,0x066,0x06c,0x078,0x06c,0x066,0x0e6,0x000,0x000,0x000, 0x000,0x000,0x038,0x018,0x018,0x018,0x018,0x018,0x018,0x018,0x03c,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x1cc,0x1fe,0x1b6,0x1b6,0x1b6,0x1b6,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x0dc,0x066,0x066,0x066,0x066,0x066,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x07c,0x0c6,0x0c6,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x0dc,0x066,0x066,0x066,0x07c,0x060,0x060,0x0f0,0x000, 0x000,0x000,0x000,0x000,0x000,0x076,0x0cc,0x0cc,0x0cc,0x07c,0x00c,0x00c,0x01e,0x000, 0x000,0x000,0x000,0x000,0x000,0x0dc,0x076,0x066,0x060,0x060,0x0f0,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x07c,0x0c6,0x070,0x01c,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x010,0x030,0x030,0x0fc,0x030,0x030,0x030,0x036,0x01c,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x0cc,0x0cc,0x0cc,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x186,0x186,0x186,0x0cc,0x078,0x030,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x186,0x186,0x1b6,0x1b6,0x1fe,0x0cc,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x0c6,0x06c,0x038,0x038,0x06c,0x0c6,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x0c6,0x0c6,0x0c6,0x0c6,0x07e,0x006,0x00c,0x078,0x000, 0x000,0x000,0x000,0x000,0x000,0x0fe,0x0cc,0x018,0x030,0x066,0x0fe,0x000,0x000,0x000, 0x000,0x000,0x01c,0x030,0x030,0x030,0x0e0,0x030,0x030,0x030,0x01c,0x000,0x000,0x000, 0x000,0x000,0x030,0x030,0x030,0x030,0x000,0x030,0x030,0x030,0x030,0x000,0x000,0x000, 0x000,0x000,0x0e0,0x030,0x030,0x030,0x01c,0x030,0x030,0x030,0x0e0,0x000,0x000,0x000, 0x000,0x000,0x076,0x0dc,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x010,0x038,0x06c,0x0c6,0x0c6,0x0fe,0x000,0x000,0x000,0x000, 0x000,0x000,0x03c,0x066,0x0c2,0x0c0,0x0c0,0x0c2,0x066,0x03c,0x00c,0x006,0x07c,0x000, 0x000,0x000,0x0cc,0x0cc,0x000,0x0cc,0x0cc,0x0cc,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x00c,0x018,0x030,0x000,0x07c,0x0c6,0x0fe,0x0c0,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x010,0x038,0x06c,0x000,0x078,0x00c,0x07c,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x000,0x0cc,0x0cc,0x000,0x078,0x00c,0x07c,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x060,0x030,0x018,0x000,0x078,0x00c,0x07c,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x038,0x06c,0x038,0x000,0x078,0x00c,0x07c,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x078,0x0cc,0x0c0,0x0cc,0x078,0x018,0x00c,0x078,0x000,0x000, 0x000,0x010,0x038,0x06c,0x000,0x07c,0x0c6,0x0fe,0x0c0,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x0cc,0x0cc,0x000,0x07c,0x0c6,0x0fe,0x0c0,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x060,0x030,0x018,0x000,0x07c,0x0c6,0x0fe,0x0c0,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x0cc,0x0cc,0x000,0x070,0x030,0x030,0x030,0x030,0x078,0x000,0x000,0x000, 0x000,0x030,0x078,0x0cc,0x000,0x070,0x030,0x030,0x030,0x030,0x078,0x000,0x000,0x000, 0x000,0x0c0,0x060,0x030,0x000,0x070,0x030,0x030,0x030,0x030,0x078,0x000,0x000,0x000, 0x000,0x0c6,0x0c6,0x010,0x038,0x06c,0x0c6,0x0c6,0x0fe,0x0c6,0x0c6,0x000,0x000,0x000, 0x038,0x06c,0x038,0x000,0x038,0x06c,0x0c6,0x0c6,0x0fe,0x0c6,0x0c6,0x000,0x000,0x000, 0x018,0x030,0x060,0x000,0x0fe,0x066,0x060,0x07c,0x060,0x066,0x0fe,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x0dc,0x076,0x036,0x0fc,0x1b0,0x1b8,0x0ee,0x000,0x000,0x000, 0x000,0x000,0x03e,0x06c,0x0cc,0x0cc,0x0fe,0x0cc,0x0cc,0x0cc,0x0ce,0x000,0x000,0x000, 0x000,0x010,0x038,0x06c,0x000,0x07c,0x0c6,0x0c6,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x0c6,0x0c6,0x000,0x07c,0x0c6,0x0c6,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x060,0x030,0x018,0x000,0x07c,0x0c6,0x0c6,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x030,0x078,0x0cc,0x000,0x0cc,0x0cc,0x0cc,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x060,0x030,0x018,0x000,0x0cc,0x0cc,0x0cc,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x000,0x0c6,0x0c6,0x000,0x0c6,0x0c6,0x0c6,0x0c6,0x07e,0x006,0x00c,0x078,0x000, 0x000,0x0c6,0x0c6,0x038,0x06c,0x0c6,0x0c6,0x0c6,0x0c6,0x06c,0x038,0x000,0x000,0x000, 0x000,0x0c6,0x0c6,0x000,0x0c6,0x0c6,0x0c6,0x0c6,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x030,0x030,0x0fc,0x186,0x180,0x180,0x186,0x0fc,0x030,0x030,0x000,0x000,0x000, 0x000,0x038,0x06c,0x064,0x060,0x0f0,0x060,0x060,0x060,0x0e6,0x0fc,0x000,0x000,0x000, 0x000,0x000,0x186,0x0cc,0x078,0x030,0x1fe,0x030,0x1fe,0x030,0x030,0x000,0x000,0x000, 0x000,0x1f8,0x0cc,0x0cc,0x0f8,0x0c4,0x0cc,0x0de,0x0cc,0x0cc,0x1e6,0x000,0x000,0x000, 0x000,0x01c,0x036,0x030,0x030,0x030,0x0fc,0x030,0x030,0x030,0x030,0x1b0,0x0e0,0x000, 0x000,0x018,0x030,0x060,0x000,0x078,0x00c,0x07c,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x018,0x030,0x060,0x000,0x070,0x030,0x030,0x030,0x030,0x078,0x000,0x000,0x000, 0x000,0x018,0x030,0x060,0x000,0x07c,0x0c6,0x0c6,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x018,0x030,0x060,0x000,0x0cc,0x0cc,0x0cc,0x0cc,0x0cc,0x076,0x000,0x000,0x000, 0x000,0x000,0x076,0x0dc,0x000,0x0dc,0x066,0x066,0x066,0x066,0x066,0x000,0x000,0x000, 0x076,0x0dc,0x000,0x0c6,0x0e6,0x0f6,0x0fe,0x0de,0x0ce,0x0c6,0x0c6,0x000,0x000,0x000, 0x000,0x078,0x0d8,0x0d8,0x07c,0x000,0x0fc,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x070,0x0d8,0x0d8,0x070,0x000,0x0f8,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x030,0x030,0x000,0x030,0x030,0x060,0x0c6,0x0c6,0x07c,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x0fe,0x0c0,0x0c0,0x0c0,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x0fe,0x006,0x006,0x006,0x000,0x000,0x000,0x000, 0x000,0x0c0,0x1c0,0x0c6,0x0cc,0x0d8,0x030,0x060,0x0dc,0x186,0x00c,0x018,0x03e,0x000, 0x000,0x0c0,0x1c0,0x0c6,0x0cc,0x0d8,0x030,0x066,0x0ce,0x19e,0x03e,0x006,0x006,0x000, 0x000,0x000,0x030,0x030,0x000,0x030,0x030,0x078,0x078,0x078,0x030,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x036,0x06c,0x0d8,0x06c,0x036,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x0d8,0x06c,0x036,0x06c,0x0d8,0x000,0x000,0x000,0x000,0x000, 0x022,0x088,0x022,0x088,0x022,0x088,0x022,0x088,0x022,0x088,0x022,0x088,0x022,0x088, 0x0aa,0x154,0x0aa,0x154,0x0aa,0x154,0x0aa,0x154,0x0aa,0x154,0x0aa,0x154,0x0aa,0x154, 0x1ba,0x0ee,0x1ba,0x0ee,0x1ba,0x0ee,0x1ba,0x0ee,0x1ba,0x0ee,0x1ba,0x0ee,0x1ba,0x0ee, 0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x030, 0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x1f0,0x030,0x030,0x030,0x030,0x030,0x030, 0x030,0x030,0x030,0x030,0x030,0x1f0,0x030,0x1f0,0x030,0x030,0x030,0x030,0x030,0x030, 0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x1ec,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x1fc,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x000,0x000,0x000,0x000,0x000,0x1f0,0x030,0x1f0,0x030,0x030,0x030,0x030,0x030,0x030, 0x06c,0x06c,0x06c,0x06c,0x06c,0x1ec,0x00c,0x1ec,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x000,0x000,0x000,0x000,0x000,0x1fc,0x00c,0x1ec,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x06c,0x06c,0x06c,0x06c,0x06c,0x1ec,0x00c,0x1fc,0x000,0x000,0x000,0x000,0x000,0x000, 0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x1fc,0x000,0x000,0x000,0x000,0x000,0x000, 0x030,0x030,0x030,0x030,0x030,0x1f0,0x030,0x1f0,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x1f0,0x030,0x030,0x030,0x030,0x030,0x030, 0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x03f,0x000,0x000,0x000,0x000,0x000,0x000, 0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x1ff,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x1ff,0x030,0x030,0x030,0x030,0x030,0x030, 0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x03f,0x030,0x030,0x030,0x030,0x030,0x030, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x1ff,0x000,0x000,0x000,0x000,0x000,0x000, 0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x1ff,0x030,0x030,0x030,0x030,0x030,0x030, 0x030,0x030,0x030,0x030,0x030,0x03f,0x030,0x03f,0x030,0x030,0x030,0x030,0x030,0x030, 0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06f,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x06c,0x06c,0x06c,0x06c,0x06c,0x06f,0x060,0x07f,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x07f,0x060,0x06f,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x06c,0x06c,0x06c,0x06c,0x06c,0x1ef,0x000,0x1ff,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x1ff,0x000,0x1ef,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x06c,0x06c,0x06c,0x06c,0x06c,0x06f,0x060,0x06f,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x000,0x000,0x000,0x000,0x000,0x1ff,0x000,0x1ff,0x000,0x000,0x000,0x000,0x000,0x000, 0x06c,0x06c,0x06c,0x06c,0x06c,0x1ef,0x000,0x1ef,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x030,0x030,0x030,0x030,0x030,0x1ff,0x000,0x1ff,0x000,0x000,0x000,0x000,0x000,0x000, 0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x1ff,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x1ff,0x000,0x1ff,0x030,0x030,0x030,0x030,0x030,0x030, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x1ff,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x07f,0x000,0x000,0x000,0x000,0x000,0x000, 0x030,0x030,0x030,0x030,0x030,0x03f,0x030,0x03f,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x03f,0x030,0x03f,0x030,0x030,0x030,0x030,0x030,0x030, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x07f,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x1ff,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c, 0x030,0x030,0x030,0x030,0x030,0x1ff,0x030,0x1ff,0x030,0x030,0x030,0x030,0x030,0x030, 0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x1f0,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x03f,0x030,0x030,0x030,0x030,0x030,0x030, 0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff, 0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0,0x1e0, 0x01f,0x01f,0x01f,0x01f,0x01f,0x01f,0x01f,0x01f,0x01f,0x01f,0x01f,0x01f,0x01f,0x01f, 0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x1ff,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x076,0x0dc,0x0d8,0x0d8,0x0dc,0x076,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x07c,0x0c6,0x0fc,0x0c6,0x0c6,0x0fc,0x0c0,0x0c0,0x040,0x000, 0x000,0x000,0x0fe,0x0c6,0x0c6,0x0c0,0x0c0,0x0c0,0x0c0,0x0c0,0x0c0,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x0fe,0x06c,0x06c,0x06c,0x06c,0x06c,0x06c,0x000,0x000,0x000, 0x000,0x000,0x0fe,0x0c6,0x060,0x030,0x018,0x030,0x060,0x0c6,0x0fe,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x07e,0x0d8,0x0d8,0x0d8,0x0d8,0x070,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x066,0x066,0x066,0x066,0x07c,0x060,0x060,0x0c0,0x000,0x000, 0x000,0x000,0x000,0x000,0x076,0x0dc,0x018,0x018,0x018,0x018,0x018,0x000,0x000,0x000, 0x000,0x000,0x0fc,0x030,0x078,0x0cc,0x0cc,0x0cc,0x078,0x030,0x0fc,0x000,0x000,0x000, 0x000,0x000,0x038,0x06c,0x0c6,0x0c6,0x0fe,0x0c6,0x0c6,0x06c,0x038,0x000,0x000,0x000, 0x000,0x000,0x038,0x06c,0x0c6,0x0c6,0x0c6,0x06c,0x06c,0x06c,0x0ee,0x000,0x000,0x000, 0x000,0x000,0x03c,0x060,0x030,0x018,0x07c,0x0cc,0x0cc,0x0cc,0x078,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x0fc,0x1b6,0x1b6,0x0fc,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x006,0x00c,0x0fc,0x1b6,0x1b6,0x1e6,0x0fc,0x0c0,0x180,0x000,0x000,0x000, 0x000,0x000,0x038,0x060,0x0c0,0x0c0,0x0f8,0x0c0,0x0c0,0x060,0x038,0x000,0x000,0x000, 0x000,0x000,0x000,0x07c,0x0c6,0x0c6,0x0c6,0x0c6,0x0c6,0x0c6,0x0c6,0x000,0x000,0x000, 0x000,0x000,0x000,0x0fe,0x000,0x000,0x0fe,0x000,0x000,0x0fe,0x000,0x000,0x000,0x000, 0x000,0x000,0x030,0x030,0x030,0x1fe,0x030,0x030,0x030,0x000,0x1fe,0x000,0x000,0x000, 0x000,0x000,0x060,0x030,0x018,0x00c,0x018,0x030,0x060,0x000,0x0fc,0x000,0x000,0x000, 0x000,0x000,0x018,0x030,0x060,0x0c0,0x060,0x030,0x018,0x000,0x0fc,0x000,0x000,0x000, 0x000,0x000,0x01c,0x036,0x036,0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x030, 0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x030,0x1b0,0x1b0,0x0e0,0x000,0x000,0x000, 0x000,0x000,0x030,0x030,0x000,0x000,0x1fe,0x000,0x000,0x030,0x030,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x076,0x0dc,0x000,0x076,0x0dc,0x000,0x000,0x000,0x000,0x000, 0x000,0x070,0x0d8,0x0d8,0x070,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x030,0x030,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x030,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x01e,0x018,0x018,0x018,0x018,0x018,0x1d8,0x0d8,0x078,0x038,0x000,0x000,0x000, 0x000,0x1b0,0x0d8,0x0d8,0x0d8,0x0d8,0x0d8,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x0e0,0x1b0,0x060,0x0c0,0x190,0x1f0,0x000,0x000,0x000,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x07c,0x07c,0x07c,0x07c,0x07c,0x07c,0x000,0x000,0x000,0x000, 0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000 ]); // CGA 16色カラー video_cga_color_8 = new Uint8Array([ 0x00,0x00,0x00,0xff, // Black 0x00,0x00,0xaa,0xff, // Blue 0x00,0xaa,0x00,0xff, // Green 0x00,0xaa,0xaa,0xff, // Cyan 0xaa,0x00,0x00,0xff, // Red 0xaa,0x00,0xaa,0xff, // Magenta 0xaa,0x55,0x00,0xff, // Brown 0xaa,0xaa,0xaa,0xff, // Light Gray 0x55,0x55,0x55,0xff, // Gray 0x55,0x55,0xff,0xff, // Light Blue 0x55,0xff,0x55,0xff, // Light Green 0x55,0xff,0xff,0xff, // Light Cyan 0xff,0x55,0x55,0xff, // Light Red 0xff,0x55,0xff,0xff, // Light Magenta 0xff,0xff,0x55,0xff, // Yellow 0xff,0xff,0xff,0xff // White ]); video_cga_color = new Uint32Array(video_cga_color_8.buffer); video_cga_black = video_cga_color[0]; video_cga_green = video_cga_color[2]; video_cga_cyan = video_cga_color[3]; video_cga_red = video_cga_color[4]; video_cga_magenta = video_cga_color[5]; video_cga_brown = video_cga_color[6]; video_cga_lgray = video_cga_color[7]; video_cga_gray = video_cga_color[8]; video_cga_lgreen = video_cga_color[10]; video_cga_lcyan = video_cga_color[11]; video_cga_lred = video_cga_color[12]; video_cga_lmagenta = video_cga_color[13]; video_cga_yellow = video_cga_color[14]; video_cga_white = video_cga_color[15]; // CGA カラー パレット video_cga_palette = new Uint32Array(4); // MDA 輝度 video_mda_int0 = video_cga_black; video_mda_int1 = video_cga_gray; video_mda_int2 = video_cga_lgray; video_mda_int3 = video_cga_white; // コンポジット モニタ用 色差データ video_comp_cr = new Int16Array([ 10356, 13530, 14645, 13530, 10356, 5605, 0, -5605, -10356, -13530, -14645, -13530, -10356, -5605, 0, 5605]); video_comp_cg = new Int16Array([ -1689, -4952, -7460, -8833, -8860, -7540, -5071, -1830, 1689, 4952, 7460, 8833, 8860, 7540, 5071, 1830]); video_comp_cb = new Int16Array([-18462, -9992, 0, 9992, 18462, 24122, 26110, 24122, 18462, 9992, 0, -9992, -18462, -24122, -26110, -24122]); // コンポジット モニタ用 8色カラー video_comp_phase = new Uint8Array([0x00, 0x1e, 0xc3, 0x87, 0x78, 0x3c, 0xe1, 0xff]); video_comp_phase_green = 0xc3; video_comp_phase_cyan = 0x87; video_comp_phase_red = 0x78; video_comp_phase_magenta = 0x3c; video_comp_phase_yellow = 0xe1; // コンポジット モニタ用 カラー パレット video_comp_palette_phase = new Uint8Array(5); // 位相 video_comp_palette_int = new Uint8Array(5); // 輝度 video_comp_palette_phase[4] = 0x00; video_comp_palette_int[4] = 0x0; video_comp_lpf_int = new Uint16Array(8); video_comp_lpf_chrom = new Uint16Array(4); video_mem_work = new Uint8Array(2); video_mem_work_2 = new Uint16Array(video_mem_work.buffer, 0, 1); // テキスト モード ビデオ メモリ クリア用 video_mem_work[0] = 0x20; video_mem_work[1] = 0x07; video_text_clear = video_mem_work_2[0]; video_composite = false; // ビデオ初期化 function video_init() { mem[MEM_SCR_ROW_MAX] = 24; mem[MEM_SCR_COLS_1] = 0; mem[MEM_VBUF_SIZE_0] = 0x00; mem[MEM_VBUF_START_0] = 0x00; // 初期ビデオ モード セット regs[I_AH] = 0x00; regs[I_AL] = INIT_MODE; bios_video(); video_frame_cnt = 0; video_refresh_req1 = video_refresh_req2 = false; } // 画面更新 function video_refresh() { var char_blink = video_frame_cnt & 0x10; var cursor; switch(video_cursor_mode) { case 0x20: cursor = 0; break; case 0x40: cursor = video_frame_cnt & 0x8; break; case 0x60: cursor = video_frame_cnt & 0x10; break; default: cursor = 1; } var video_buff; var i_buff = 0; var i_buff2; var video_mem = window.video_mem; var video_mem_o; var i_mem; var i_mem_cur; var col; var i_font; var font1; var line; var attr; var fore, back; var bit; var video_comp_color; var pixels; var lpf_int; var lpf_chrom; var e_o; var phase; var phase4; var pal_phase; var l_h; var cr, cg, cb; var prev_chrom; var prev_diff; var chg_phase; var chg_chrom; var chg_cnt; var color; var pre; var npost; var vid_int, vid_chrom; var chrom; var diff; var amp; var int; var int0; switch(video_adapter_mode) { // ビデオ モード(ハードウェア設定) case 0: case 1: i_mem_cur = -1; if(cursor) { if(video_cursor_start < video_scan_lines && video_cursor_end >= video_cursor_start && video_cursor_end < video_scan_lines) i_mem_cur = video_cursor_addr << 1; } video_buff = window.video_buff; i_mem = video_start_addr << 1; while(i_buff < video_i_buff_end) { for(col = 40; col; col--) { i_font = video_mem[i_mem] << 3; // 文字 attr = video_mem[i_mem + 1]; // 属性 fore = video_cga_color[attr & 0xf]; if(video_blink) { back = video_cga_color[(attr & 0x70) >> 4]; if(attr & 0x80) { // ブリンク if(char_blink) fore = back; } } else { back = video_cga_color[attr >> 4]; } i_buff2 = i_buff; for(line = video_scan_lines; line; line--) { font1 = video_font_8x8[i_font++]; for(bit = 0x80; bit; bit >>= 1) video_buff[i_buff2++] = (font1 & bit) ? fore : back; i_buff2 += 312/* 320-8 */; } // カーソル if(i_mem == i_mem_cur) { i_buff2 = i_buff + video_cursor_start * 320; for(line = video_cursor_end - video_cursor_start + 1; line; line--) { for(bit = 8; bit; bit--) video_buff[i_buff2++] = fore; i_buff2 += 312/* 320-8 */; } } i_buff += 8; i_mem = (i_mem + 2) & 0x7fff; } i_buff += video_i_buff_adj; } break; case 2: case 3: i_mem_cur = -1; if(cursor) { if(video_cursor_start < video_scan_lines && video_cursor_end >= video_cursor_start && video_cursor_end < video_scan_lines) i_mem_cur = video_cursor_addr << 1; } video_buff = window.video_buff; i_mem = video_start_addr << 1; while(i_buff < video_i_buff_end) { for(col = 80; col; col--) { i_font = video_mem[i_mem] << 3; // 文字 attr = video_mem[i_mem + 1]; // 属性 fore = video_cga_color[attr & 0xf]; if(video_blink) { back = video_cga_color[(attr & 0x70) >> 4]; if(attr & 0x80) { // ブリンク if(char_blink) fore = back; } } else { back = video_cga_color[attr >> 4]; } i_buff2 = i_buff; for(line = video_scan_lines; line; line--) { font1 = video_font_8x8[i_font++]; for(bit = 0x80; bit; bit >>= 1) video_buff[i_buff2++] = (font1 & bit) ? fore : back; i_buff2 += 632/* 640-8 */; } // カーソル if(i_mem == i_mem_cur) { i_buff2 = i_buff + video_cursor_start * 640; for(line = video_cursor_end - video_cursor_start + 1; line; line--) { for(bit = 8; bit; bit--) video_buff[i_buff2++] = fore; i_buff2 += 632/* 640-8 */; } } i_buff += 8; i_mem = (i_mem + 2) & 0x7fff; } i_buff += video_i_buff_adj; } break; case 4: case 5: if(video_composite) { // 320x200 コンポジット モニタの処理 video_buff = video_data.data; i_mem = (video_start_addr << 1) & 0x1fff; var video_comp_palette_phase = window.video_comp_palette_phase; var video_comp_palette_int = window.video_comp_palette_int; video_comp_color = window.video_comp_color; lpf_int = video_comp_lpf_int; lpf_chrom = video_comp_lpf_chrom; while(i_buff < 256000/* 200*320*4 */) { for(; ; ) { // 偶数ライン/奇数ライン lpf_int.fill(0); int = 0; lpf_chrom.fill(0); chrom = 0; phase = 0; bit = -1; cr = cg = cb = 0; prev_chrom = 0; prev_diff = 0; chg_cnt = -1; color = false; pre = npost = true; for(i_buff2 = i_buff + 1276/* 320*4-4 */; ; ) { var i; if(bit < 0) { if(npost) { pixels = video_mem[i_mem]; i_mem = (i_mem + 1) & 0x1fff; i = pixels >> 6; bit = 4; } else { i = 4; } } else { i = (pixels >> bit) & 0x3; bit -= 2; } pal_phase = video_comp_palette_phase[i]; if(l_h = (pal_phase << phase) & 0x80) { vid_int = 0x9f0; vid_chrom = 683; } else { vid_int = vid_chrom = 0; } if(video_comp_palette_int[i]) { // 高輝度 vid_int += 0x500; vid_chrom += 341; } phase4 = 0; for(; ; ) { int = int - lpf_int[phase] + vid_int; lpf_int[phase] = vid_int; if(!phase4) int0 = int; if(video_comp_color) { // 色信号あり chrom = chrom - lpf_chrom[phase4] + vid_chrom; lpf_chrom[phase4] = vid_chrom; if (chrom > prev_chrom) diff = 1; else if(chrom < prev_chrom) diff = -1; else diff = 0; if(diff != prev_diff) { if(chg_cnt > 1) { if(chg_cnt < 4) chg_phase = (chg_phase - (4 - chg_cnt)) & 0xf; amp = (prev_diff > 0) ? prev_chrom - chg_chrom : chg_chrom - prev_chrom; // RGB の各計算値は 0〜255 の範囲外になるものもあるが,Uint8ClampedArray によりクランプされる cr = (video_comp_cr[chg_phase] * amp) >> 12; cg = (video_comp_cg[chg_phase] * amp) >> 12; cb = (video_comp_cb[chg_phase] * amp) >> 12; chg_cnt = -1; color = true; } if(diff) { chg_phase = phase << 1; if(diff < 0) // 立ち下がり chg_phase ^= 0x8; // 位相を 180°ずらす chg_chrom = prev_chrom; chg_cnt = 0; } prev_diff = diff; } prev_chrom = chrom; if(chg_cnt >= 0) chg_cnt++; } phase = (phase + 1) & 0x7; phase4 = phase & 0x3; if(!phase4) break; if(l_h != ((pal_phase << phase) & 0x80)) { // 色信号反転 if(l_h) { vid_int -= 0x9f0; vid_chrom -= 683; } else { vid_int += 0x9f0; vid_chrom += 683; } l_h = pal_phase = 0x00; } } if(pre) { pre = false; } else { if(int0) { video_buff[i_buff] = (int0 + cr + 0x40) >> 7; video_buff[i_buff + 1] = (int0 + cg + 0x40) >> 7; video_buff[i_buff + 2] = (int0 + cb + 0x40) >> 7; } else { video_buff[i_buff] = video_buff[i_buff + 1] = video_buff[i_buff + 2] = 0; } if((i_buff += 4) >= i_buff2) { if(i_buff > i_buff2) break; npost = false; } } if(color) { cr = cg = cb = 0; color = false; } } if(video_mem == window.video_mem_o) { video_mem = window.video_mem; break; } video_mem = window.video_mem_o; i_mem = (i_mem - 80) & 0x1fff; } } } else { video_buff = window.video_buff; video_mem_o = window.video_mem_o; i_mem = (video_start_addr << 1) & 0x1fff; var video_cga_palette = window.video_cga_palette; while(i_buff < 64000/* 200*320 */) { for(i_buff2 = i_buff + 320; i_buff < i_buff2; ) { pixels = video_mem[i_mem]; video_buff[i_buff] = video_cga_palette[pixels >> 6]; video_buff[i_buff + 1] = video_cga_palette[(pixels & 0x30) >> 4]; video_buff[i_buff + 2] = video_cga_palette[(pixels & 0x0c) >> 2]; video_buff[i_buff + 3] = video_cga_palette[pixels & 0x03]; pixels = video_mem_o[i_mem]; video_buff[i_buff + 320] = video_cga_palette[pixels >> 6]; video_buff[i_buff + 321] = video_cga_palette[(pixels & 0x30) >> 4]; video_buff[i_buff + 322] = video_cga_palette[(pixels & 0x0c) >> 2]; video_buff[i_buff + 323] = video_cga_palette[pixels & 0x03]; i_buff += 4; i_mem = (i_mem + 1) & 0x1fff; } i_buff += 320; } } break; case 6: if(video_composite) { // 640x200 コンポジット モニタの処理 video_buff = video_data.data; i_mem = (video_start_addr << 1) & 0x1fff; video_comp_color = window.video_comp_color; lpf_int = video_comp_lpf_int; lpf_chrom = video_comp_lpf_chrom; pal_phase = window.video_comp_palette_phase[0]; var fore_vid_int_l, fore_vid_int_h; var fore_vid_chrom_l, fore_vid_chrom_h; if(window.video_comp_palette_int[0]) { // 高輝度 fore_vid_int_l = 0x500; fore_vid_int_h = 0xef0; fore_vid_chrom_l = 341; fore_vid_chrom_h = 1024; } else { fore_vid_int_l = fore_vid_chrom_l = 0; fore_vid_int_h = 0x9f0; fore_vid_chrom_h = 683; } while(i_buff < 512000/* 200*640*4 */) { for(; ; ) { // 偶数ライン/奇数ライン lpf_int.fill(0); int = 0; lpf_chrom.fill(0); chrom = 0; phase = 0; bit = 0; var alt; cr = cg = cb = 0; prev_chrom = 0; prev_diff = 0; chg_cnt = -1; color = 0; pre = 2; npost = true; for(i_buff2 = i_buff + 2552/* 640*4-8 */; ; ) { if(!bit) { if(npost) { pixels = video_mem[i_mem]; i_mem = (i_mem + 1) & 0x1fff; bit = 0x80; } } if(pixels & bit) { if(l_h = (pal_phase << phase) & 0x80) { vid_int = fore_vid_int_h; vid_chrom = fore_vid_chrom_h; } else { vid_int = fore_vid_int_l; vid_chrom = fore_vid_chrom_l; } alt = (l_h != ((pal_phase << ((phase + 1) & 0x7)) & 0x80)); } else { vid_int = vid_chrom = 0; alt = false; } bit >>= 1; phase4 = phase & 0x3; for(; ; ) { int = int - lpf_int[phase] + vid_int; lpf_int[phase] = vid_int; if(video_comp_color) { // 色信号あり chrom = chrom - lpf_chrom[phase4] + vid_chrom; lpf_chrom[phase4] = vid_chrom; if (chrom > prev_chrom) diff = 1; else if(chrom < prev_chrom) diff = -1; else diff = 0; if(diff != prev_diff) { if(chg_cnt > 1) { if(chg_cnt < 4) chg_phase = (chg_phase - (4 - chg_cnt)) & 0xf; amp = (prev_diff > 0) ? prev_chrom - chg_chrom : chg_chrom - prev_chrom; // RGB の各計算値は 0〜255 の範囲外になるものもあるが,Uint8ClampedArray によりクランプされる cr = (video_comp_cr[chg_phase] * amp) >> 12; cg = (video_comp_cg[chg_phase] * amp) >> 12; cb = (video_comp_cb[chg_phase] * amp) >> 12; chg_cnt = -1; color = 2; } if(diff) { chg_phase = phase << 1; if(diff < 0) // 立ち下がり chg_phase ^= 0x8; // 位相を 180°ずらす chg_chrom = prev_chrom; chg_cnt = 0; } prev_diff = diff; } prev_chrom = chrom; if(chg_cnt >= 0) chg_cnt++; } phase = (phase + 1) & 0x7; if(!(phase & 0x1)) break; phase4++; int0 = int; if(alt) { // 色信号反転 if(l_h) { vid_int -= 0x9f0; vid_chrom -= 683; } else { vid_int += 0x9f0; vid_chrom += 683; } } } if(pre) { pre--; } else { if(int0) { video_buff[i_buff] = (int0 + cr + 0x40) >> 7; video_buff[i_buff + 1] = (int0 + cg + 0x40) >> 7; video_buff[i_buff + 2] = (int0 + cb + 0x40) >> 7; } else { video_buff[i_buff] = video_buff[i_buff + 1] = video_buff[i_buff + 2] = 0; } if((i_buff += 4) >= i_buff2) { if(i_buff == i_buff2 + 8) break; npost = false; } } if(color) { if(!--color) cr = cg = cb = 0; } } if(video_mem == window.video_mem_o) { video_mem = window.video_mem; break; } video_mem = window.video_mem_o; i_mem = (i_mem - 80) & 0x1fff; } } } else { video_buff = window.video_buff; video_mem_o = window.video_mem_o; i_mem = (video_start_addr << 1) & 0x1fff; fore = window.video_cga_palette[0]; back = video_cga_black; while(i_buff < 128000/* 200*640 */) { for(i_buff2 = i_buff + 640; i_buff < i_buff2; ) { pixels = video_mem[i_mem]; var pixels_o = video_mem_o[i_mem]; for(bit = 0x80; bit; bit >>= 1) { video_buff[i_buff] = (pixels & bit) ? fore : back; video_buff[i_buff + 640] = (pixels_o & bit) ? fore : back; i_buff++; } i_mem = (i_mem + 1) & 0x1fff; } i_buff += 640; } } break; case 7: i_mem_cur = -1; if(cursor) { if(video_cursor_start < 14 && video_cursor_end >= video_cursor_start && video_cursor_end < 14) i_mem_cur = video_cursor_addr << 1; } video_buff = window.video_buff; i_mem = video_start_addr << 1; while(i_buff < 252000/* 350*720 */) { for(col = 80; col; col--) { i_font = video_mem[i_mem] * 14; // 文字 attr = video_mem[i_mem + 1]; // 属性 if((attr & 0x77) == 0x00) { fore = back = video_mda_int0; } else { switch(attr) { case 0x70: fore = video_mda_int0; back = video_mda_int2; break; case 0x78: fore = video_mda_int1; back = video_mda_int2; break; case 0xf0: if(video_blink) { back = video_mda_int2; fore = (char_blink) ? back : video_mda_int0; } else { fore = video_mda_int0; back = video_mda_int3; } break; case 0xf8: if(video_blink) { back = video_mda_int2; fore = (char_blink) ? back : video_mda_int1; } else { fore = video_mda_int1; back = video_mda_int3; } break; default: fore = (attr & 0x08) ? video_mda_int3 : video_mda_int2; back = video_mda_int0; } } i_buff2 = i_buff; for(line = 14; line; line--) { font1 = video_font_9x14[i_font++]; for(bit = 0x100; bit; bit >>= 1) video_buff[i_buff2++] = (font1 & bit) ? fore : back; i_buff2 += 711/* 720-9 */; } if((attr & 0x7) == 0x1) { // 下線 i_buff2 = i_buff + 8640/* 12*720 */; for(bit = 9; bit; bit--) video_buff[i_buff2++] = fore; } // カーソル if(i_mem == i_mem_cur) { i_buff2 = i_buff + video_cursor_start * 720; for(line = video_cursor_end - video_cursor_start + 1; line; line--) { for(bit = 9; bit; bit--) video_buff[i_buff2++] = fore; i_buff2 += 711/* 720-9 */; } } i_buff += 9; i_mem = (i_mem + 2) & 0x7fff; } i_buff += 9360/* 13*720 */; } } } // 画面更新 カラー パレット変更時用 function video_refresh_1l() { var i_line = ((video_line_cnt > 240) ? 502 : 240) - video_line_cnt; if(i_line >= 200) return; var video_buff; var i_buff, i_buff2; var video_mem = (i_line & 0x1) ? video_mem_o : window.video_mem; var i_mem = ((video_start_addr << 1) + (i_line & 0xfe) * 40) & 0x1fff; var bit; var video_comp_color; var pixels; var lpf_int; var lpf_chrom; var phase; var phase4; var pal_phase; var l_h; var cr, cg, cb; var prev_chrom; var prev_diff; var chg_phase; var chg_chrom; var chg_cnt; var color; var pre; var npost; var vid_int, vid_chrom; var chrom; var diff; var amp; var int; var int0; if(video_adapter_mode == 6) { if(video_composite) { video_buff = video_data.data; i_buff = i_line * 2560/* 640*4 */; video_comp_color = window.video_comp_color; lpf_int = video_comp_lpf_int; lpf_chrom = video_comp_lpf_chrom; pal_phase = window.video_comp_palette_phase[0]; var fore_vid_int_l, fore_vid_int_h; var fore_vid_chrom_l, fore_vid_chrom_h; if(window.video_comp_palette_int[0]) { // 高輝度 fore_vid_int_l = 0x500; fore_vid_int_h = 0xef0; fore_vid_chrom_l = 341; fore_vid_chrom_h = 1024; } else { fore_vid_int_l = fore_vid_chrom_l = 0; fore_vid_int_h = 0x9f0; fore_vid_chrom_h = 683; } lpf_int.fill(0); int = 0; lpf_chrom.fill(0); chrom = 0; phase = 0; bit = 0; var alt; cr = cg = cb = 0; prev_chrom = 0; prev_diff = 0; chg_cnt = -1; color = 0; pre = 2; npost = true; for(i_buff2 = i_buff + 2552/* 640*4-8 */; ; ) { if(!bit) { if(npost) { pixels = video_mem[i_mem]; i_mem = (i_mem + 1) & 0x1fff; bit = 0x80; } } if(pixels & bit) { if(l_h = (pal_phase << phase) & 0x80) { vid_int = fore_vid_int_h; vid_chrom = fore_vid_chrom_h; } else { vid_int = fore_vid_int_l; vid_chrom = fore_vid_chrom_l; } alt = (l_h != ((pal_phase << ((phase + 1) & 0x7)) & 0x80)); } else { vid_int = vid_chrom = 0; alt = false; } bit >>= 1; phase4 = phase & 0x3; for(; ; ) { int = int - lpf_int[phase] + vid_int; lpf_int[phase] = vid_int; if(video_comp_color) { // 色信号あり chrom = chrom - lpf_chrom[phase4] + vid_chrom; lpf_chrom[phase4] = vid_chrom; if (chrom > prev_chrom) diff = 1; else if(chrom < prev_chrom) diff = -1; else diff = 0; if(diff != prev_diff) { if(chg_cnt > 1) { if(chg_cnt < 4) chg_phase = (chg_phase - (4 - chg_cnt)) & 0xf; amp = (prev_diff > 0) ? prev_chrom - chg_chrom : chg_chrom - prev_chrom; cr = (video_comp_cr[chg_phase] * amp) >> 12; cg = (video_comp_cg[chg_phase] * amp) >> 12; cb = (video_comp_cb[chg_phase] * amp) >> 12; chg_cnt = -1; color = 2; } if(diff) { chg_phase = phase << 1; if(diff < 0) // 立ち下がり chg_phase ^= 0x8; // 位相を 180°ずらす chg_chrom = prev_chrom; chg_cnt = 0; } prev_diff = diff; } prev_chrom = chrom; if(chg_cnt >= 0) chg_cnt++; } phase = (phase + 1) & 0x7; if(!(phase & 0x1)) break; phase4++; int0 = int; if(alt) { // 色信号反転 if(l_h) { vid_int -= 0x9f0; vid_chrom -= 683; } else { vid_int += 0x9f0; vid_chrom += 683; } } } if(pre) { pre--; } else { if(int0) { video_buff[i_buff] = (int0 + cr + 0x40) >> 7; video_buff[i_buff + 1] = (int0 + cg + 0x40) >> 7; video_buff[i_buff + 2] = (int0 + cb + 0x40) >> 7; } else { video_buff[i_buff] = video_buff[i_buff + 1] = video_buff[i_buff + 2] = 0; } if((i_buff += 4) >= i_buff2) { if(i_buff == i_buff2 + 8) break; npost = false; } } if(color) { if(!--color) cr = cg = cb = 0; } } } else { video_buff = window.video_buff; i_buff = i_line * 640; var fore = window.video_cga_palette[0]; var back = video_cga_black; for(i_buff2 = i_buff + 640; i_buff < i_buff2; ) { pixels = video_mem[i_mem]; for(bit = 0x80; bit; bit >>= 1) video_buff[i_buff++] = (pixels & bit) ? fore : back; i_mem = (i_mem + 1) & 0x1fff; } } } else { if(video_composite) { video_buff = video_data.data; i_buff = i_line * 1280/* 320*4 */; var video_comp_palette_phase = window.video_comp_palette_phase; var video_comp_palette_int = window.video_comp_palette_int; video_comp_color = window.video_comp_color; lpf_int = video_comp_lpf_int; lpf_chrom = video_comp_lpf_chrom; lpf_int.fill(0); int = 0; lpf_chrom.fill(0); chrom = 0; phase = 0; bit = -1; cr = cg = cb = 0; prev_chrom = 0; prev_diff = 0; chg_cnt = -1; color = false; pre = npost = true; for(i_buff2 = i_buff + 1276/* 320*4-4 */; ; ) { var i; if(bit < 0) { if(npost) { pixels = video_mem[i_mem]; i_mem = (i_mem + 1) & 0x1fff; i = pixels >> 6; bit = 4; } else { i = 4; } } else { i = (pixels >> bit) & 0x3; bit -= 2; } pal_phase = video_comp_palette_phase[i]; if(l_h = (pal_phase << phase) & 0x80) { vid_int = 0x9f0; vid_chrom = 683; } else { vid_int = vid_chrom = 0; } if(video_comp_palette_int[i]) { // 高輝度 vid_int += 0x500; vid_chrom += 341; } phase4 = 0; for(; ; ) { int = int - lpf_int[phase] + vid_int; lpf_int[phase] = vid_int; if(!phase4) int0 = int; if(video_comp_color) { // 色信号あり chrom = chrom - lpf_chrom[phase4] + vid_chrom; lpf_chrom[phase4] = vid_chrom; if (chrom > prev_chrom) diff = 1; else if(chrom < prev_chrom) diff = -1; else diff = 0; if(diff != prev_diff) { if(chg_cnt > 1) { if(chg_cnt < 4) chg_phase = (chg_phase - (4 - chg_cnt)) & 0xf; amp = (prev_diff > 0) ? prev_chrom - chg_chrom : chg_chrom - prev_chrom; cr = (video_comp_cr[chg_phase] * amp) >> 12; cg = (video_comp_cg[chg_phase] * amp) >> 12; cb = (video_comp_cb[chg_phase] * amp) >> 12; chg_cnt = -1; color = true; } if(diff) { chg_phase = phase << 1; if(diff < 0) // 立ち下がり chg_phase ^= 0x8; // 位相を 180°ずらす chg_chrom = prev_chrom; chg_cnt = 0; } prev_diff = diff; } prev_chrom = chrom; if(chg_cnt >= 0) chg_cnt++; } phase = (phase + 1) & 0x7; phase4 = phase & 0x3; if(!phase4) break; if(l_h != ((pal_phase << phase) & 0x80)) { // 色信号反転 if(l_h) { vid_int -= 0x9f0; vid_chrom -= 683; } else { vid_int += 0x9f0; vid_chrom += 683; } l_h = pal_phase = 0x00; } } if(pre) { pre = false; } else { if(int0) { video_buff[i_buff] = (int0 + cr + 0x40) >> 7; video_buff[i_buff + 1] = (int0 + cg + 0x40) >> 7; video_buff[i_buff + 2] = (int0 + cb + 0x40) >> 7; } else { video_buff[i_buff] = video_buff[i_buff + 1] = video_buff[i_buff + 2] = 0; } if((i_buff += 4) >= i_buff2) { if(i_buff > i_buff2) break; npost = false; } } if(color) { cr = cg = cb = 0; color = false; } } } else { video_buff = window.video_buff; i_buff = i_line * 320; var video_cga_palette = window.video_cga_palette; for(i_buff2 = i_buff + 320; i_buff < i_buff2; i_buff += 4) { pixels = video_mem[i_mem]; video_buff[i_buff] = video_cga_palette[pixels >> 6]; video_buff[i_buff + 1] = video_cga_palette[(pixels & 0x30) >> 4]; video_buff[i_buff + 2] = video_cga_palette[(pixels & 0x0c) >> 2]; video_buff[i_buff + 3] = video_cga_palette[pixels & 0x03]; i_mem = (i_mem + 1) & 0x1fff; } } } } // ---- キーボード エミュレーション ---- keyboard_keys_norm = { "0":[0x30,0x0b],"1":[0x31,0x02],"2":[0x32,0x03],"3":[0x33,0x04],"4":[0x34,0x05], "5":[0x35,0x06],"6":[0x36,0x07],"7":[0x37,0x08],"8":[0x38,0x09],"9":[0x39,0x0a], "A":[0x61,0x1e],"B":[0x62,0x30],"C":[0x63,0x2e],"D":[0x64,0x20],"E":[0x65,0x12],"F":[0x66,0x21], "G":[0x67,0x22],"H":[0x68,0x23],"I":[0x69,0x17],"J":[0x6a,0x24],"K":[0x6b,0x25],"L":[0x6c,0x26], "M":[0x6d,0x32],"N":[0x6e,0x31],"O":[0x6f,0x18],"P":[0x70,0x19],"Q":[0x71,0x10],"R":[0x72,0x13], "S":[0x73,0x1f],"T":[0x74,0x14],"U":[0x75,0x16],"V":[0x76,0x2f],"W":[0x77,0x11],"X":[0x78,0x2d], "Y":[0x79,0x15],"Z":[0x7a,0x2c], "a":[0x61,0x1e],"b":[0x62,0x30],"c":[0x63,0x2e],"d":[0x64,0x20],"e":[0x65,0x12],"f":[0x66,0x21], "g":[0x67,0x22],"h":[0x68,0x23],"i":[0x69,0x17],"j":[0x6a,0x24],"k":[0x6b,0x25],"l":[0x6c,0x26], "m":[0x6d,0x32],"n":[0x6e,0x31],"o":[0x6f,0x18],"p":[0x70,0x19],"q":[0x71,0x10],"r":[0x72,0x13], "s":[0x73,0x1f],"t":[0x74,0x14],"u":[0x75,0x16],"v":[0x76,0x2f],"w":[0x77,0x11],"x":[0x78,0x2d], "y":[0x79,0x15],"z":[0x7a,0x2c], " ":[0x20,0x39],"!":[0x21,0x02],"\"":[0x22,0x28],"#":[0x23,0x04],"$":[0x24,0x05],"%":[0x25,0x06], "&":[0x26,0x08],"'":[0x27,0x28],"(":[0x28,0x0a],")":[0x29,0x0b],"*":[0x2a,0x09],"+":[0x2b,0x0d], ",":[0x2c,0x33],"-":[0x2d,0x0c],".":[0x2e,0x34],"/":[0x2f,0x35],":":[0x3a,0x27],";":[0x3b,0x27], "<":[0x3c,0x33],"=":[0x3d,0x0d],">":[0x3e,0x34],"?":[0x3f,0x35],"@":[0x40,0x03],"[":[0x5b,0x1a], "\\":[0x5c,0x2b],"]":[0x5d,0x1b],"^":[0x5e,0x07],"_":[0x5f,0x0c],"`":[0x60,0x29],"{":[0x7b,0x1a], "|":[0x7c,0x2b],"}":[0x7d,0x1b],"~":[0x7e,0x29], "Backspace":[0x08,0x0e],"Tab":[0x09,0x0f],"Enter":[0x0d,0x1c],"Esc":[0x1b,0x01], "F1":[0x00,0x3b],"F2":[0x00,0x3c],"F3":[0x00,0x3d],"F4":[0x00,0x3e],"F5":[0x00,0x3f],"F6":[0x00,0x40], "F7":[0x00,0x41],"F8":[0x00,0x42],"F9":[0x00,0x43],"F10":[0x00,0x44],"F11":[0x00,0x85],"F12":[0x00,0x86], "Insert":[0xe0,0x52],"Del":[0xe0,0x53],"Home":[0xe0,0x47],"End":[0xe0,0x4f], "PageUp":[0xe0,0x49],"PageDown":[0xe0,0x51], "Up":[0xe0,0x48],"Down":[0xe0,0x50],"Left":[0xe0,0x4b],"Right":[0xe0,0x4d], "Escape":[0x1b,0x01], "ArrowUp":[0xe0,0x48],"ArrowDown":[0xe0,0x50],"ArrowLeft":[0xe0,0x4b],"ArrowRight":[0xe0,0x4d] }; keyboard_keys_norm_num = { "0":[0x30,0x52],"1":[0x31,0x4f],"2":[0x32,0x50],"3":[0x33,0x51],"4":[0x34,0x4b], "5":[0x35,0x4c],"6":[0x36,0x4d],"7":[0x37,0x47],"8":[0x38,0x48],"9":[0x39,0x49], "*":[0x2a,0x37],"+":[0x2b,0x4e],"-":[0x2d,0x4a],".":[0x2e,0x53],"/":[0x2f,0xe0],"Enter":[0x0d,0xe0], "Insert":[0x00,0x52],"Del":[0x00,0x53],"Home":[0x00,0x47],"End":[0x00,0x4f], "PageUp":[0x00,0x49],"PageDown":[0x00,0x51], "Up":[0x00,0x48],"Down":[0x00,0x50],"Left":[0x00,0x4b],"Right":[0x00,0x4d],"Clear":[0xf0,0x4c], "ArrowUp":[0x00,0x48],"ArrowDown":[0x00,0x50],"ArrowLeft":[0x00,0x4b],"ArrowRight":[0x00,0x4d] }; keyboard_keys_shift = { "0":[0x30,0x0b],"1":[0x31,0x02],"2":[0x32,0x03],"3":[0x33,0x04],"4":[0x34,0x05], "5":[0x35,0x06],"6":[0x36,0x07],"7":[0x37,0x08],"8":[0x38,0x09],"9":[0x39,0x0a], "A":[0x41,0x1e],"B":[0x42,0x30],"C":[0x43,0x2e],"D":[0x44,0x20],"E":[0x45,0x12],"F":[0x46,0x21], "G":[0x47,0x22],"H":[0x48,0x23],"I":[0x49,0x17],"J":[0x4a,0x24],"K":[0x4b,0x25],"L":[0x4c,0x26], "M":[0x4d,0x32],"N":[0x4e,0x31],"O":[0x4f,0x18],"P":[0x50,0x19],"Q":[0x51,0x10],"R":[0x52,0x13], "S":[0x53,0x1f],"T":[0x54,0x14],"U":[0x55,0x16],"V":[0x56,0x2f],"W":[0x57,0x11],"X":[0x58,0x2d], "Y":[0x59,0x15],"Z":[0x5a,0x2c], "a":[0x41,0x1e],"b":[0x42,0x30],"c":[0x43,0x2e],"d":[0x44,0x20],"e":[0x45,0x12],"f":[0x46,0x21], "g":[0x47,0x22],"h":[0x48,0x23],"i":[0x49,0x17],"j":[0x4a,0x24],"k":[0x4b,0x25],"l":[0x4c,0x26], "m":[0x4d,0x32],"n":[0x4e,0x31],"o":[0x4f,0x18],"p":[0x50,0x19],"q":[0x51,0x10],"r":[0x52,0x13], "s":[0x53,0x1f],"t":[0x54,0x14],"u":[0x55,0x16],"v":[0x56,0x2f],"w":[0x57,0x11],"x":[0x58,0x2d], "y":[0x59,0x15],"z":[0x5a,0x2c], " ":[0x20,0x39],"!":[0x21,0x02],"\"":[0x22,0x28],"#":[0x23,0x04],"$":[0x24,0x05],"%":[0x25,0x06], "&":[0x26,0x08],"'":[0x27,0x28],"(":[0x28,0x0a],")":[0x29,0x0b],"*":[0x2a,0x09],"+":[0x2b,0x0d], ",":[0x2c,0x33],"-":[0x2d,0x0c],".":[0x2e,0x34],"/":[0x2f,0x35],":":[0x3a,0x27],";":[0x3b,0x27], "<":[0x3c,0x33],"=":[0x3d,0x0d],">":[0x3e,0x34],"?":[0x3f,0x35],"@":[0x40,0x03],"[":[0x5b,0x1a], "\\":[0x5c,0x2b],"]":[0x5d,0x1b],"^":[0x5e,0x07],"_":[0x5f,0x0c],"`":[0x60,0x29],"{":[0x7b,0x1a], "|":[0x7c,0x2b],"}":[0x7d,0x1b],"~":[0x7e,0x29], "Backspace":[0x08,0x0e],"Tab":[0x00,0x0f],"Enter":[0x0d,0x1c],"Esc":[0x1b,0x01], "F1":[0x00,0x54],"F2":[0x00,0x55],"F3":[0x00,0x56],"F4":[0x00,0x57],"F5":[0x00,0x58],"F6":[0x00,0x59], "F7":[0x00,0x5a],"F8":[0x00,0x5b],"F9":[0x00,0x5c],"F10":[0x00,0x5d],"F11":[0x00,0x87],"F12":[0x00,0x88], "Insert":[0xe0,0x52],"Del":[0xe0,0x53],"Home":[0xe0,0x47],"End":[0xe0,0x4f], "PageUp":[0xe0,0x49],"PageDown":[0xe0,0x51], "Up":[0xe0,0x48],"Down":[0xe0,0x50],"Left":[0xe0,0x4b],"Right":[0xe0,0x4d], "Escape":[0x1b,0x01], "ArrowUp":[0xe0,0x48],"ArrowDown":[0xe0,0x50],"ArrowLeft":[0xe0,0x4b],"ArrowRight":[0xe0,0x4d] }; keyboard_keys_shift_num = { "0":[0x30,0x52],"1":[0x31,0x4f],"2":[0x32,0x50],"3":[0x33,0x51],"4":[0x34,0x4b], "5":[0x35,0x4c],"6":[0x36,0x4d],"7":[0x37,0x47],"8":[0x38,0x48],"9":[0x39,0x49], "*":[0x2a,0x37],"+":[0x2b,0x4e],"-":[0x2d,0x4a],".":[0x2e,0x53],"/":[0x2f,0xe0],"Enter":[0x0d,0xe0], "Insert":[0xe0,0x52],"Del":[0xe0,0x53],"Home":[0xe0,0x47],"End":[0xe0,0x4f], "PageUp":[0xe0,0x49],"PageDown":[0xe0,0x51], "Up":[0xe0,0x48],"Down":[0xe0,0x50],"Left":[0xe0,0x4b],"Right":[0xe0,0x4d], "ArrowUp":[0xe0,0x48],"ArrowDown":[0xe0,0x50],"ArrowLeft":[0xe0,0x4b],"ArrowRight":[0xe0,0x4d] }; keyboard_keys_ctrl = { "2":[0x00,0x03],"a":[0x01,0x1e],"b":[0x02,0x30],"c":[0x03,0x2e],"d":[0x04,0x20],"e":[0x05,0x12], "f":[0x06,0x21],"g":[0x07,0x22],"h":[0x08,0x23],"i":[0x09,0x17],"j":[0x0a,0x24],"k":[0x0b,0x25], "l":[0x0c,0x26],"m":[0x0d,0x32],"n":[0x0e,0x31],"o":[0x0f,0x18],"p":[0x10,0x19],"q":[0x11,0x10], "r":[0x12,0x13],"s":[0x13,0x1f],"t":[0x14,0x14],"u":[0x15,0x16],"v":[0x16,0x2f],"w":[0x17,0x11], "x":[0x18,0x2d],"y":[0x19,0x15],"z":[0x1a,0x2c],"[":[0x1b,0x1a],"\\":[0x1c,0x2b],"]":[0x1d,0x1b], "6":[0x1e,0x07],"-":[0x1f,0x0c], "A":[0x01,0x1e],"B":[0x02,0x30],"C":[0x03,0x2e],"D":[0x04,0x20],"E":[0x05,0x12], "F":[0x06,0x21],"G":[0x07,0x22],"H":[0x08,0x23],"I":[0x09,0x17],"J":[0x0a,0x24],"K":[0x0b,0x25], "L":[0x0c,0x26],"M":[0x0d,0x32],"N":[0x0e,0x31],"O":[0x0f,0x18],"P":[0x10,0x19],"Q":[0x11,0x10], "R":[0x12,0x13],"S":[0x13,0x1f],"T":[0x14,0x14],"U":[0x15,0x16],"V":[0x16,0x2f],"W":[0x17,0x11], "X":[0x18,0x2d],"Y":[0x19,0x15],"Z":[0x1a,0x2c], "Backspace":[0x7f,0x0e],"Tab":[0x00,0x94],"Enter":[0x0a,0x1c],"Esc":[0x1b,0x01], "F1":[0x00,0x5e],"F2":[0x00,0x5f],"F3":[0x00,0x60],"F4":[0x00,0x61],"F5":[0x00,0x62],"F6":[0x00,0x63], "F7":[0x00,0x64],"F8":[0x00,0x65],"F9":[0x00,0x66],"F10":[0x00,0x67],"F11":[0x00,0x89],"F12":[0x00,0x8a], "Insert":[0xe0,0x92],"Del":[0xe0,0x93],"Home":[0xe0,0x77],"End":[0xe0,0x75], "PageUp":[0xe0,0x84],"PageDown":[0xe0,0x76], "Up":[0xe0,0x8d],"Down":[0xe0,0x91],"Left":[0xe0,0x73],"Right":[0xe0,0x74],"Cancel":[0x00,0x00], "Escape":[0x1b,0x01], "ArrowUp":[0xe0,0x8d],"ArrowDown":[0xe0,0x91],"ArrowLeft":[0xe0,0x73],"ArrowRight":[0xe0,0x74] }; keyboard_keys_ctrl_num = { "-":[0x00,0x8e],"*":[0x00,0x96],"+":[0x00,0x90],".":[0x00,0x93],"/":[0x00,0x95],"Enter":[0x0a,0xe0], "Insert":[0x00,0x92],"Del":[0x00,0x93],"Home":[0x00,0x77],"End":[0x00,0x75], "PageUp":[0x00,0x84],"PageDown":[0x00,0x76], "Up":[0x00,0x8d],"Down":[0x00,0x91],"Left":[0x00,0x73],"Right":[0x00,0x74],"Clear":[0x00,0x8f], "ArrowUp":[0x00,0x8d],"ArrowDown":[0x00,0x91],"ArrowLeft":[0x00,0x73],"ArrowRight":[0x00,0x74] }; keyboard_keys_alt = { "0":[0x00,0x81],"1":[0x00,0x78],"2":[0x00,0x79],"3":[0x00,0x7a],"4":[0x00,0x7b], "5":[0x00,0x7c],"6":[0x00,0x7d],"7":[0x00,0x7e],"8":[0x00,0x7f],"9":[0x00,0x80], "A":[0x00,0x1e],"B":[0x00,0x30],"C":[0x00,0x2e],"D":[0x00,0x20],"E":[0x00,0x12],"F":[0x00,0x21], "G":[0x00,0x22],"H":[0x00,0x23],"I":[0x00,0x17],"J":[0x00,0x24],"K":[0x00,0x25],"L":[0x00,0x26], "M":[0x00,0x32],"N":[0x00,0x31],"O":[0x00,0x18],"P":[0x00,0x19],"Q":[0x00,0x10],"R":[0x00,0x13], "S":[0x00,0x1f],"T":[0x00,0x14],"U":[0x00,0x16],"V":[0x00,0x2f],"W":[0x00,0x11],"X":[0x00,0x2d], "Y":[0x00,0x15],"Z":[0x00,0x2c], "a":[0x00,0x1e],"b":[0x00,0x30],"c":[0x00,0x2e],"d":[0x00,0x20],"e":[0x00,0x12],"f":[0x00,0x21], "g":[0x00,0x22],"h":[0x00,0x23],"i":[0x00,0x17],"j":[0x00,0x24],"k":[0x00,0x25],"l":[0x00,0x26], "m":[0x00,0x32],"n":[0x00,0x31],"o":[0x00,0x18],"p":[0x00,0x19],"q":[0x00,0x10],"r":[0x00,0x13], "s":[0x00,0x1f],"t":[0x00,0x14],"u":[0x00,0x16],"v":[0x00,0x2f],"w":[0x00,0x11],"x":[0x00,0x2d], "y":[0x00,0x15],"z":[0x00,0x2c], " ":[0x20,0x39], "Backspace":[0x00,0x0e],"Tab":[0x00,0xa5],"Enter":[0x00,0x1c],"Esc":[0x00,0x01], "F1":[0x00,0x68],"F2":[0x00,0x69],"F3":[0x00,0x6a],"F4":[0x00,0x6b],"F5":[0x00,0x6c],"F6":[0x00,0x6d], "F7":[0x00,0x6e],"F8":[0x00,0x6f],"F9":[0x00,0x70],"F10":[0x00,0x71],"F11":[0x00,0x8b],"F12":[0x00,0x8c], "Insert":[0x00,0xa2],"Del":[0x00,0xa3],"Home":[0x00,0x97],"End":[0x00,0x9f], "PageUp":[0x00,0x99],"PageDown":[0x00,0xa1], "Up":[0x00,0x98],"Down":[0x00,0xa0],"Left":[0x00,0x9b],"Right":[0x00,0x9d], "Escape":[0x00,0x01], "ArrowUp":[0x00,0x98],"ArrowDown":[0x00,0xa0],"ArrowLeft":[0x00,0x9b],"ArrowRight":[0x00,0x9d] }; keyboard_keys_alt_num = { "*":[0x00,0x37],"+":[0x00,0x4e],"-":[0x00,0x4a],"/":[0x00,0xa4], "Enter":[0x00,0xa6], "Insert":[0x00,0xa2],"Del":[0x00,0xa3],"Home":[0x00,0x97],"End":[0x00,0x9f], "PageUp":[0x00,0x99],"PageDown":[0x00,0xa1], "Up":[0x00,0x98],"Down":[0x00,0xa0],"Left":[0x00,0x9b],"Right":[0x00,0x9d], "ArrowUp":[0x00,0x98],"ArrowDown":[0x00,0xa0],"ArrowLeft":[0x00,0x9b],"ArrowRight":[0x00,0x9d] }; keyboard_keys_raw = { "0":0x0b,"1":0x02,"2":0x03,"3":0x04,"4":0x05,"5":0x06,"6":0x07,"7":0x08,"8":0x09,"9":0x0a, "A":0x1e,"B":0x30,"C":0x2e,"D":0x20,"E":0x12,"F":0x21,"G":0x22,"H":0x23,"I":0x17,"J":0x24, "K":0x25,"L":0x26,"M":0x32,"N":0x31,"O":0x18,"P":0x19,"Q":0x10,"R":0x13,"S":0x1f,"T":0x14, "U":0x16,"V":0x2f,"W":0x11,"X":0x2d,"Y":0x15,"Z":0x2c, "a":0x1e,"b":0x30,"c":0x2e,"d":0x20,"e":0x12,"f":0x21,"g":0x22,"h":0x23,"i":0x17,"j":0x24, "k":0x25,"l":0x26,"m":0x32,"n":0x31,"o":0x18,"p":0x19,"q":0x10,"r":0x13,"s":0x1f,"t":0x14, "u":0x16,"v":0x2f,"w":0x11,"x":0x2d,"y":0x15,"z":0x2c, " ":0x39,"Backspace":0x0e,"Tab":0x0f,"Enter":0x1c,"Esc":0x01, "F1":0x3b,"F2":0x3c,"F3":0x3d,"F4":0x3e,"F5":0x3f,"F6":0x40,"F7":0x41,"F8":0x42,"F9":0x43,"F10":0x44, "F11":0x85,"F12":0x86, "Insert":0x52,"Del":0x53,"Home":0x47,"End":0x4f,"PageUp":0x49,"PageDown":0x51, "Up":0x48,"Down":0x50,"Left":0x4b,"Right":0x4d,"Clear":0x4c, "Control":0x1d,"Alt":0x38,"CapsLock":0x3a,"NumLock":0x45,"ScrollLock":0x46,"PrintScreen":0x37, "Escape":0x01, "ArrowUp":0x48,"ArrowDown":0x50,"ArrowLeft":0x4b,"ArrowRight":0x4d }; // オート リピート レート keyboard_rep_rate_tbl = [ 33.36, 37.53, 41.70, 45.87, 50.04, 54.21, 58.38, 62.55, 66.72, 75.06, 83.40, 91.74, 100.08, 108.42, 116.76, 125.10, 133.44, 150.12, 166.80, 183.48, 200.16, 216.84, 233.52, 250.20, 266.88, 300.24, 333.60, 366.96, 400.32, 433.68, 467.04, 500.40 ]; keyboard_buff = new Uint8Array(mem.buffer, MEM_KB_BUFF, 32); // BIOS キー入力バッファ keyboard_input = []; // キー入力 addEventListener("keydown", keyboard_event, false); addEventListener("keyup", keyboard_event, false); addEventListener("mousemove", keyboard_get_lock, false); // キーボード初期化 function keyboard_init() { // BIOS キー入力バッファ keyboard_i_buff_head = keyboard_i_buff_tail = 0; mem[MEM_KB_BUFF_HEAD_0] = mem[MEM_KB_BUFF_TAIL_0] = KB_BUFF_OFF & 0xff; mem[MEM_KB_BUFF_HEAD_1] = mem[MEM_KB_BUFF_TAIL_1] = (KB_BUFF_OFF & 0xff00) >> 8; mem[MEM_KB_STATUS_0] = mem[MEM_KB_STATUS_1] = 0; mem[MEM_KB_FLAGS] = 0x10; keyboard_input.length = 0; keyboard_data = undefined; keyboard_rep_delay = 500; keyboard_rep_rate = keyboard_rep_rate_tbl[11]; keyboard_rep_next = Number.MAX_VALUE; keyboard_status = 0x00; } // キー入力イベント function keyboard_event(e) { if(!run_state) // 停止中 return; e.preventDefault(); if(e.repeat) return; var data = {down:(e.type == "keydown"), key:e.key, loc:e.location, shift:e.shiftKey, ctrl:e.ctrlKey, alt:e.altKey, caps:e.getModifierState("CapsLock"), num:e.getModifierState("NumLock"), scroll:e.getModifierState("ScrollLock")}; if(data.down) { keyboard_rep_next = timer.now() + keyboard_rep_delay; keyboard_rep_data = data; } else { keyboard_rep_next = Number.MAX_VALUE; } if(keyboard_input.length < 4) keyboard_input.push(data); } // キーボード割り込み // INT 09H 割り込みをフックするプログラムのため,83 キーボード(と F11,F12)の // INT 09H 割り込みをエミュレートする. // 動作環境によりキーボードのキー配列が異なることと,Shift キー押下状態と文字の // 対応が一般には 83 キーボードとは合わないことにより,83 キーボードの動作を完全 // にエミュレートすることはできない. function keyboard_int() { pic_m_isr |= 0x02; pic_m_irr &= 0xfd; if(run_hwint_ss < 0) { run_hwint_ss = ss4; run_hwint_sp = regs_2[I_SP_2]; } run_halt = false; keyboard_data = keyboard_input.shift(); // キー入力データ取り出し if((keyboard_scan = keyboard_keys_raw[keyboard_data.key]) == undefined) { if(keyboard_data.key == "Shift") keyboard_scan = (keyboard_data.loc == KeyboardEvent.DOM_KEY_LOCATION_RIGHT) ? 0x36 : 0x2a; else keyboard_scan = 0; } if(keyboard_scan) { if(!keyboard_data.down) keyboard_scan |= 0x80; keyboard_status |= 0x01; int(0x09); return false; } keyboard_int_def(); return true; } // キーボード割り込み デフォルト処理 function keyboard_int_def() { keyboard_scan = 0; keyboard_status &= 0xfe; pic_m_isr &= 0xfd; // EOI var data; if((data = keyboard_data) == undefined) return; keyboard_data = undefined; var down = data.down; var key = data.key; if(down) { if(run_pause) { // Pause 中 run_pause = false; return; } // Pause キーの処理 if(key == "Pause") { run_pause = true; return; } } // modifier key var status_0 = mem[MEM_KB_STATUS_0] & 0xf3; var status_1 = mem[MEM_KB_STATUS_1]; var flags = mem[MEM_KB_FLAGS]; switch(key) { case "Shift": if(data.loc == KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { if(down) status_0 |= 0x01; else status_0 &= 0xfe; } else { // STANDARD,LEFT if(down) status_0 |= 0x02; else status_0 &= 0xfd; } break; case "Control": if(data.loc == KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { if(down) flags |= 0x04; else flags &= 0xfb; } else { // STANDARD,LEFT if(down) status_1 |= 0x01; else status_1 &= 0xfe; } break; case "Alt": if(data.loc == KeyboardEvent.DOM_KEY_LOCATION_RIGHT) { if(down) flags |= 0x08; else flags &= 0xf7; } else { // STANDARD,LEFT if(down) status_1 |= 0x02; else status_1 &= 0xfd; } break; case "CapsLock": if(down) status_1 |= 0x40; else status_1 &= 0xbf; if(data.caps) status_0 |= 0x40; else status_0 &= 0xbf; break; case "NumLock": if(down) status_1 |= 0x20; else status_1 &= 0xdf; if(data.num) status_0 |= 0x20; else status_0 &= 0xdf; break; case "ScrollLock": if(down) status_1 |= 0x10; else status_1 &= 0xef; if(data.scroll) status_0 |= 0x10; else status_0 &= 0xef; break; case "Insert": if(down) status_1 |= 0x80; else status_1 &= 0x7f; status_0 ^= 0x80; break; case "PrintScreen": // SysRq if(down) status_1 |= 0x04; else status_1 &= 0xfb; break; } mem[MEM_KB_STATUS_0] = status_0 | ((status_1 & 0x03) << 2) | flags & 0x0c; mem[MEM_KB_STATUS_1] = status_1; mem[MEM_KB_FLAGS] = flags; if(down) { // スキャン コード/文字コードを BIOS のキー入力バッファに書き込む. // Shift キー押下状態と文字の対応は,一般には 101 キーボードとは合わない. var numpad = (data.loc == KeyboardEvent.DOM_KEY_LOCATION_NUMPAD); var keys; if(data.alt) // Alt keys = (numpad) ? keyboard_keys_alt_num : keyboard_keys_alt; else if(data.ctrl) // Ctrl keys = (numpad) ? keyboard_keys_ctrl_num : keyboard_keys_ctrl; else if(data.shift) // Shift keys = (numpad) ? keyboard_keys_shift_num : keyboard_keys_shift; else keys = (numpad) ? keyboard_keys_norm_num : keyboard_keys_norm; var code = keys[key]; if(code != undefined) { var char = code[0]; // 文字コード if(status_0 & 0x40) { // CapsLock var char2 = char & 0xdf; if(char2 >= 0x41 && char2 <= 0x5a) // 'A' 〜 'Z' char ^= 0x20; } var new_tail = (keyboard_i_buff_tail + 2) & 0x1f; if(new_tail != keyboard_i_buff_head) { // バッファ一杯でない keyboard_buff[keyboard_i_buff_tail] = char; keyboard_buff[keyboard_i_buff_tail + 1] = code[1]; // スキャン コード keyboard_i_buff_tail = new_tail; var off = KB_BUFF_OFF + keyboard_i_buff_tail; mem[MEM_KB_BUFF_TAIL_0] = off & 0xff; mem[MEM_KB_BUFF_TAIL_1] = (off & 0xff00) >> 8; if(run_wait == 2) // 入力待ち run_wait = 0x10002; } } } } function keyboard_get_lock(e) { var status_0 = mem[MEM_KB_STATUS_0] & 0x8f; if(e.getModifierState("CapsLock")) status_0 |= 0x40; if(e.getModifierState("NumLock")) status_0 |= 0x20; if(e.getModifierState("ScrollLock")) status_0 |= 0x10; mem[MEM_KB_STATUS_0] = status_0; removeEventListener("mousemove", keyboard_get_lock, false); } // ---- I/O エミュレーション ---- // 1 バイト入力 function in_1(port) { var al = 0; var count; switch(port) { case I_DMA_STATUS: al = io_dma_status; io_dma_status ^= 0x04; // チャネル 2 TC break; case I_PIC_M_STATUS_0: al = (pic_m_ris) ? pic_m_isr : pic_m_irr; break; case I_PIC_M_STATUS_1: al = pic_m_imr; break; case I_TIMER_0_COUNT: count = (timer_0_latch < 0) ? get_timer_0_count() : timer_0_latch; switch(timer_0_control & 0x30) { case 0x10: // 下位バイト al = count & 0xff; timer_0_latch = -1; break; case 0x30: // 2 バイト if(timer_0_i_lh = !timer_0_i_lh) { al = count & 0xff; break; } // fall thru case 0x20: // 上位バイト al = (count & 0xff00) >> 8; timer_0_latch = -1; break; } break; case I_TIMER_2_COUNT: count = (timer_2_latch < 0) ? get_timer_2_count() : timer_2_latch; switch(timer_2_control & 0x30) { case 0x10: // 下位バイト al = count & 0xff; timer_2_latch = -1; break; case 0x30: // 2 バイト if(timer_2_i_lh = !timer_2_i_lh) { al = count & 0xff; break; } // fall thru case 0x20: // 上位バイト al = (count & 0xff00) >> 8; timer_2_latch = -1; break; } break; case I_PARALLEL_A: if(io_parallel_b & 0x80) { al = conf_sw1; } else { al = keyboard_scan; keyboard_scan = 0; keyboard_status &= 0xfe; } break; case I_PARALLEL_B: al = io_parallel_b & 0x0f; break; case I_KB_STATUS: al = keyboard_status; break; case I_JOYSTICK: al = 0xff; break; case I_MDA_DATA: if(video_mode == 7) { switch(video_crtc_index) { case 14: // Cursor (H) al = video_cursor_addr >> 8; break; case 15: // Cursor (L) al = video_cursor_addr & 0xff; break; } } break; case I_MDA_STATUS: if(video_mode == 7) al = (timer_now >= video_line_next - 0.008857722827089869 && timer_now < video_line_next - 0.0005536076766931168) // 水平同期 ? 0x01 : 0x00; break; case I_CGA_DATA: if(video_mode != 7) { switch(video_crtc_index) { case 14: // Cursor (H) al = video_cursor_addr >> 8; break; case 15: // Cursor (L) al = video_cursor_addr & 0xff; break; case 16: // Light Pen (H) al = video_lp_latch >> 8; break; case 17: // Light Pen (L) al = video_lp_latch & 0xff; break; } } break; case I_CGA_UNKNOWN: if(video_mode != 7) al = 0x08; break; case I_CGA_STATUS: if(video_mode != 7) { if(video_line_cnt <= 16) // 垂直同期 al = 0x09; else if((video_line_cnt <= 40 || video_line_cnt > 240) // 垂直帰線区間 || timer_now >= video_line_next - 0.018996827809120993) // 水平帰線区間 al = 0x01; else al = 0x00; al |= video_lp_trigger; } break; case I_FDC_STATUS: al = 0x80; break; } regs[I_AL] = al; } // 2 バイト入力 function in_2(port) { var ax = 0; switch(port) { // 1 バイトの I/O を行うべきところを 2 バイトの I/O を行っている // プログラムがあるので,2 バイト I/O にも対応しておく. case I_MDA_DATA: case I_MDA_STATUS: case I_CGA_DATA: case I_CGA_STATUS: in_1(port); regs[I_AH] = 0; return; } set_reg2(I_AX, ax); } // 1 バイト出力 function out_1(port) { var al = regs[I_AL]; var new_count; switch(port) { case O_PIC_M_COMMAND_0: // EOI は Non-Specific/Specific EOI のみ対応する switch(al & 0xf8) { case 0x20: // Non-Specific EOI if(pic_m_isr & 0x01) pic_m_isr &= 0xfe; else pic_m_isr &= 0xfd; break; case 0x60: // Specific EOI switch(al & 0x07) { // IR レベル case 0: pic_m_isr &= 0xfe; break; case 1: pic_m_isr &= 0xfd; break; } break; } if((al & 0x9a) == 0x0a) // IRR/ISR 選択 pic_m_ris = al & 0x01; break; case O_PIC_M_COMMAND_1: // PIC の再初期化には対応していない pic_m_imr = al; break; case O_TIMER_0_COUNT: switch(timer_0_control & 0x30) { case 0x10: // 下位バイト new_count = al; break; case 0x20: // 上位バイト new_count = al << 8; break; case 0x30: // 2 バイト if(timer_0_o_lh = !timer_0_o_lh) { timer_0_count_low = al; timer_0_next = Number.MAX_VALUE; return; } new_count = timer_0_count_low | (al << 8); break; default: return; } if(!new_count) new_count = 0x10000; timer_0_latch = -1; switch(timer_0_control & 0x0e) { case 0x00: // Mode 0 break; case 0x04: // Mode 2 case 0x0c: case 0x06: // Mode 3 case 0x0e: if(timer_0_next < Number.MAX_VALUE) { // カウント開始済み timer_0_new_count = new_count; return; } break; default: return; } timer_0_count = new_count; timer_0_org = timer_now; timer_0_osc_cnt = timer_0_count; timer_0_next = timer_0_org + timer_0_count * OSC_INT; timer_0_new_count = 0; break; case O_TIMER_2_COUNT: switch(timer_2_control & 0x30) { case 0x10: // 下位バイト new_count = al; break; case 0x20: // 上位バイト new_count = al << 8; break; case 0x30: // 2 バイト if(timer_2_o_lh = !timer_2_o_lh) { timer_2_count_low = al; timer_2_org = Number.MAX_VALUE; return; } new_count = timer_2_count_low | (al << 8); break; default: return; } if(!new_count) new_count = 0x10000; timer_2_latch = -1; switch(timer_2_control & 0x0e) { case 0x00: // Mode 0 break; case 0x04: // Mode 2 case 0x0c: case 0x06: // Mode 3 case 0x0e: if((timer_2_control & 0x06) == 0x06) { // Mode 3 beep_osc.frequency.value = TIMER_FREQ / new_count; if(sound_state != 1) { // ビープ音発生中でない if((io_parallel_b & 0x03) == 0x03) { // ゲート オン,イネーブル beep_osc_gain.connect(aud_ctx.destination); sound_state = 1; } } } if(timer_2_org < Number.MAX_VALUE) { // カウント開始済み if(!(io_parallel_b & 0x01)) { // ゲート オフ timer_2_org += timer_now - timer_2_gate_off; timer_2_gate_off = timer_now; } if(timer_2_new_count) { var next = timer_2_org + timer_2_span; if(timer_now >= next) { timer_2_org = next; timer_2_count = timer_2_new_count; } } timer_2_new_count = new_count; timer_2_span = Math.ceil((timer_now - timer_2_org) / (timer_2_count * OSC_INT)) * timer_2_count * OSC_INT; return; } break; default: return; } timer_2_count = new_count; timer_2_org = timer_now; timer_2_new_count = 0; if(!(io_parallel_b & 0x01)) // ゲート オフ timer_2_gate_off = timer_now; break; case O_TIMER_CONTROL: if(al & 0x30) { switch(al & 0xc0) { case 0x00: // タイマー 0 timer_0_control = al; timer_0_o_lh = timer_0_i_lh = false; timer_0_latch = -1; switch(al & 0x0e) { case 0x00: // Mode 0 case 0x04: // Mode 2 case 0x0c: case 0x06: // Mode 3 case 0x0e: timer_0_next = Number.MAX_VALUE; break; } break; case 0x80: // タイマー 2 timer_2_control = al; timer_2_o_lh = timer_2_i_lh = false; timer_2_latch = -1; switch(al & 0x0e) { case 0x00: // Mode 0 case 0x04: // Mode 2 case 0x0c: case 0x06: // Mode 3 case 0x0e: timer_2_org = Number.MAX_VALUE; break; } break; } } else { // ラッチ switch(al & 0xc0) { case 0x00: // タイマー 0 if(timer_0_latch < 0) timer_0_latch = get_timer_0_count(); break; case 0x80: // タイマー 2 if(timer_2_latch < 0) timer_2_latch = get_timer_2_count(); break; } } break; case O_PARALLEL_B: if((io_parallel_b ^ al) & 0x03) { // タイマー 2/スピーカーの状態が変わった if(al & 0x01) { // ゲート オン if(!(io_parallel_b & 0x01)) { // ゲート オフだった if(timer_2_org < Number.MAX_VALUE) timer_2_org += timer_now - timer_2_gate_off; } } else { if(io_parallel_b & 0x01) { // ゲート オンだった if(timer_2_org < Number.MAX_VALUE) timer_2_gate_off = timer_now; } } switch(al & 0x03) { case 0x03: // ゲート オン,イネーブル if(sound_state == 2) { // スピーカー直接出力中 spk_out.onaudioprocess = null; spk_out_gain.disconnect(); } if((timer_2_control & 0x06) == 0x06) { // Mode 3 if(timer_2_org < Number.MAX_VALUE) { // ビープ音発生 beep_osc_gain.connect(aud_ctx.destination); sound_state = 1; break; } } sound_state = 0; break; case 0x01: // ゲート オン,ディスエーブル switch(sound_state) { case 1: // ビープ音発生中 beep_osc_gain.disconnect(); break; case 2: // スピーカー直接出力中 spk_out.onaudioprocess = null; spk_out_gain.disconnect(); break; } sound_state = 0; break; default: // ゲート オフ if(sound_state == 2) { // スピーカー直接出力中 spk_out_buff[spk_out_i_put] = timer_now - spk_out_put_prev; spk_out_i_put = (spk_out_i_put + 1) & 0x7ff; if(spk_out_i_put == spk_out_i_get) spk_out_i_get++; spk_out_put_prev = timer_now; } else { if(sound_state == 1) // ビープ音発生中 beep_osc_gain.disconnect(); if(io_parallel_b & 0x01) { // 最初のゲート オフ出力 // ビープを停止しただけの場合もあるので,まだ直接出力は開始しない sound_state = 0; } else { // スピーカー直接出力開始 spk_out_i_put = spk_out_i_get = 0; spk_out_put_prev = spk_out_get_prev = spk_out_time = timer_now; spk_out_val = (al & 0x02) ? 1 : 0; spk_out_gain.connect(aud_ctx.destination); spk_out.onaudioprocess = spk_out_dummy_proc; sound_state = 2; } } } } io_parallel_b = al; break; case O_MDA_INDEX: if(video_mode == 7) video_crtc_index = al; break; case O_MDA_DATA: if(video_mode == 7) { switch(video_crtc_index) { case 10: // Cursor Start video_cursor_mode = al & 0x60; video_cursor_start = al & 0x1f; break; case 11: // Cursor End video_cursor_end = al & 0x1f; break; case 12: // Start Address (H) video_start_addr = video_start_addr & 0xff | ((al & 0x3f) << 8); break; case 13: // Start Address (L) video_start_addr = video_start_addr & 0x3f00 | al; break; case 14: // Cursor (H) video_cursor_addr = video_cursor_addr & 0xff | ((al & 0x3f) << 8); break; case 15: // Cursor (L) video_cursor_addr = video_cursor_addr & 0x3f00 | al; break; } } break; case O_MDA_MODE: if(video_mode == 7) { if(video_disabled = !(al & 0x08)) { // 非表示 ctx_scr.fillRect(0, 0, elem_scr.width, elem_scr.height); video_refresh_req1 = video_refresh_req2 = false; break; } video_blink = ((al & 0x20) != 0); } break; case O_CGA_INDEX: if(video_mode != 7) video_crtc_index = al; break; case O_CGA_DATA: if(video_mode != 7) { switch(video_crtc_index) { case 6: // Vertical Displayed video_vertical_disp = al & 0x7f; if(video_adapter_mode <= 3) text_work((video_adapter_mode < 2) ? 320 : 640); break; case 9: // Max Scan Line Address video_scan_lines = (al & 0x1f) + 1; if(video_adapter_mode <= 3) text_work((video_adapter_mode < 2) ? 320 : 640); break; case 10: // Cursor Start video_cursor_mode = al & 0x60; video_cursor_start = al & 0x1f; break; case 11: // Cursor End video_cursor_end = al & 0x1f; break; case 12: // Start Address (H) video_start_addr = video_start_addr & 0xff | ((al & 0x3f) << 8); break; case 13: // Start Address (L) video_start_addr = video_start_addr & 0x3f00 | al; break; case 14: // Cursor (H) video_cursor_addr = video_cursor_addr & 0xff | ((al & 0x3f) << 8); break; case 15: // Cursor (L) video_cursor_addr = video_cursor_addr & 0x3f00 | al; break; } } break; case O_CGA_MODE: if(video_mode != 7) { if(video_disabled = !(al & 0x08)) { // 非表示 ctx_scr.fillRect(0, 0, elem_scr.width, elem_scr.height); video_refresh_req1 = video_refresh_req2 = false; break; } video_blink = ((al & 0x20) != 0); var old_mode = video_adapter_mode; if(al & 0x02) { // グラフィック モード video_comp_color = !(al & 0x04); if(al & 0x10) video_adapter_mode = 6; else video_adapter_mode = (video_comp_color) ? 4 : 5; } else { // テキスト モード video_adapter_mode = ((al & 0x01) << 1) | ((~al & 0x04) >> 2); video_comp_color = false; } set_palette(); if(video_adapter_mode == old_mode) break; switch(video_adapter_mode) { case 0: case 1: elem_scr.width = width320; elem_scr.height = height200; elem_scr.style.width = String(width320 << 1) + "px"; elem_scr.style.height = String(height200 << 1) + "px"; text_work(320); break; case 2: case 3: elem_scr.width = width640; elem_scr.height = height200; elem_scr.style.width = String(width640) + "px"; elem_scr.style.height = String(height200 << 1) + "px"; text_work(640); break; case 4: case 5: elem_scr.width = width320; elem_scr.height = height200; elem_scr.style.width = String(width320 << 1) + "px"; elem_scr.style.height = String(height200 << 1) + "px"; break; default: // 6 elem_scr.width = width640; elem_scr.height = height200; elem_scr.style.width = String(width640) + "px"; elem_scr.style.height = String(height200 << 1) + "px"; } ctx_scr.fillRect(0, 0, elem_scr.width, elem_scr.height); video_data = ctx_scr.getImageData(0, 0, elem_scr.width, elem_scr.height); video_buff = new Uint32Array(video_data.data.buffer); } break; case O_CGA_COLOR: if(video_mode != 7) { video_cga_palette[0] = video_cga_color[al & 0xf]; video_comp_palette_phase_0 = video_comp_phase[al & 0x7]; video_comp_palette_int[0] = al & 0x8; video_palette = ((al & 0x20) != 0); video_bright = ((al & 0x10) != 0); video_comp_palette_int[1] = video_comp_palette_int[2] = video_comp_palette_int[3] = (video_bright) ? 0x8 : 0x0; set_palette(); switch(video_adapter_mode) { case 4: case 5: case 6: video_color_chg = video_line_cnt; break; } } break; case O_CGA_LP_CLEAR: if(video_mode != 7) video_lp_trigger = 0x0; break; case O_CGA_LP_PRESET: if(video_mode != 7) { if(!video_lp_trigger) { var i_line = ((video_line_cnt > 240) ? 502 : 240) - video_line_cnt; var ma_c; switch(video_adapter_mode) { case 2: case 3: if((ma_c = 114 - Math.floor((video_line_next - timer_now) / 0.0005587302296800291)) > 113) { if(++i_line == 262) i_line = 0; ma_c = 0; } video_lp_latch = video_start_addr + ((i_line >> 3) * 80) + ma_c; break; default: if((ma_c = 57 - Math.floor((video_line_next - timer_now) / 0.0011174604593600583)) > 56) { if(++i_line == 262) i_line = 0; ma_c = 0; } video_lp_latch = video_start_addr + ((i_line >> ((video_adapter_mode < 4) ? 3 : 1)) * 40) + ma_c; } video_lp_trigger = 0x2; } } break; } function text_work(width) { var lines = video_vertical_disp * video_scan_lines; if(lines > 200) lines = 200 - 200 % video_scan_lines; video_i_buff_end = lines * width; video_i_buff_adj = (video_scan_lines - 1) * width; } function set_palette() { if(video_comp_color) video_comp_palette_phase[0] = video_comp_palette_phase_0; else video_comp_palette_phase[0] = (video_comp_palette_phase_0) ? 0xff : 0x00; if(video_adapter_mode == 5) { if(video_bright) { video_cga_palette[1] = video_cga_lcyan; video_cga_palette[2] = video_cga_lred; video_cga_palette[3] = video_cga_white; } else { video_cga_palette[1] = video_cga_cyan; video_cga_palette[2] = video_cga_red; video_cga_palette[3] = video_cga_lgray; } video_comp_palette_phase[1] = video_comp_palette_phase[2] = video_comp_palette_phase[3] = 0xff; } else { if(video_palette) { if(video_bright) { video_cga_palette[1] = video_cga_lcyan; video_cga_palette[2] = video_cga_lmagenta; video_cga_palette[3] = video_cga_white; } else { video_cga_palette[1] = video_cga_cyan; video_cga_palette[2] = video_cga_magenta; video_cga_palette[3] = video_cga_lgray; } if(video_comp_color) { video_comp_palette_phase[1] = video_comp_phase_cyan; video_comp_palette_phase[2] = video_comp_phase_magenta; } else { video_comp_palette_phase[1] = video_comp_palette_phase[2] = 0xff; } video_comp_palette_phase[3] = 0xff; } else { if(video_bright) { video_cga_palette[1] = video_cga_lgreen; video_cga_palette[2] = video_cga_lred; video_cga_palette[3] = video_cga_yellow; } else { video_cga_palette[1] = video_cga_green; video_cga_palette[2] = video_cga_red; video_cga_palette[3] = video_cga_brown; } if(video_comp_color) { video_comp_palette_phase[1] = video_comp_phase_green; video_comp_palette_phase[2] = video_comp_phase_red; video_comp_palette_phase[3] = video_comp_phase_yellow; } else { video_comp_palette_phase[1] = video_comp_palette_phase[2] = video_comp_palette_phase[3] = 0xff; } } } } } // 2 バイト出力 function out_2(port) { //var ax = get_reg2(I_AX); switch(port) { // 1 バイトの I/O を行うべきところを 2 バイトの I/O を行っている // プログラムがあるので,2 バイト I/O にも対応しておく. case O_PIC_M_COMMAND_0: case O_PIC_M_COMMAND_1: case O_MDA_INDEX: case O_MDA_DATA: case O_MDA_MODE: case O_CGA_INDEX: case O_CGA_DATA: case O_CGA_MODE: out_1(port); return; } } // タイマー 0 カウント取得 function get_timer_0_count() { if(timer_0_next == Number.MAX_VALUE) return 0; var count = Math.ceil((timer_0_next - timer_now) / OSC_INT); if(count >= timer_0_count) { if(timer_0_count == 0x10000) return 0; return timer_0_count; } if(count <= 0) return 0; return count; } // タイマー 2 カウント取得 // タイマー 2 を CRT との同期に使うことはまず無いだろうと思うが, // 一応タイマー 0 と同様の計算方法にしておく. function get_timer_2_count() { if(timer_2_org == Number.MAX_VALUE) return 0; if(!(io_parallel_b & 0x01)) { // ゲート オフ timer_2_org += timer_now - timer_2_gate_off; timer_2_gate_off = timer_now; } if(timer_2_new_count) { var next = timer_2_org + timer_2_span; if(timer_now >= next) { timer_2_org = next; timer_2_count = timer_2_new_count; timer_2_new_count = 0; } } var n = Math.floor((timer_now - timer_2_org) / (timer_2_count * OSC_INT)); if(n) { if(!(timer_2_control & 0x04)) { // Mode 2,3 以外 timer_2_org = Number.MAX_VALUE; return 0; } } var count = timer_2_count - Math.floor((timer_now - (timer_2_org + (n * timer_2_count) * OSC_INT)) / OSC_INT); if(count >= timer_2_count) { if(timer_2_count == 0x10000) return 0; return timer_2_count; } if(count <= 0) return 0; return count; } //--> </SCRIPT> </BODY> </HTML> |