ナムウィキ/ソニーラジオ からICF-M780N

SONY ICF-M780N は DSPラジオ。 から分離。

ソニー・ラジオ – 木の上のキーナム ウィキ
https://namu.moe/w/소니/라디오

韓国のサイトで興味深い内容の記述があった。韓国語はキムチとナムルしか知らないのでMS翻訳で引用。このページは韓国版のまとめサイトで、ものすごくよくまとめられている。

・ICF-506 (2017 年-現行)
ソニー初の本格的な DSP ダイヤルラジオ. 2017 年に初発売。受信チップは、S i 4831 人として知られている。I C F-F 10 より少し小さいサイズで、スピーカーの口径が大きい。日本では、ICF-801 のフォローアップとして、被験者に, このモデルは日本国内が機種に限定されておらず、世界で発売されている。上部にプラスチック製のハンドルが、DSP である。AA 電池3つの AC 電源を使用する。西洋では、優れたパフォーマンスでご好評をいただいていますが、日本では特に慣習的なアナログのラジオ愛好家の間では、ソニーならではの特性が消えたの批評がある。

・ICF-M780N (日本国内)、ICF-M780SL: PLL 方式の大型ラジオであり、2014年頃に発売された比較的最近のモデルである。ICF-M780N は日本国内版に短波帯のラジオ日経放送だけ受信可能である。国際版である ICF-M780SL は一般的な短波帯もサポートする。そして実際にこの機種はDSP 受信機である!

アマチュア無線もHF帯はIF段からDSP処理が全盛でアナログ固定機は廃れて久しい。ラジオはアナログに限るとかPLLやマイコンはノイズがとか絶対性能が出ないとか思いこんでるなら、そろそろ切り替えても良いと思った。

MS機械翻訳の「木の上のキー」というフレーズが全く理解できなかったのでがんばってハングルを読めるようにした。
소니 : so-ni ソニー
라디오 : ra-di-o ラジオ

これらは音がそのままハングル表記されてるようだ。

나무위키 : na-mu-wi-ki ナム ウィキ

「나무 위키」だと「木 ウィキ」だが、「나무 위 키」だと「木 上 キー」になる。「위(wi)」が「上」で、「키(ki)」は表音として「キー」なので翻訳機が分かち書きの解釈を失敗したものだと分かった。

プラスチック製のハンドルが、DSP である。
である、などと断定されても、プラスチック製のハンドルがDSPであるはずはないので、原文を見直す。

상단 플라스틱 손잡이가 달려있으며 DSP 방식이다.
上部 プラスチック(の*) 取っ手 付いていて、DSP 方式

달려있다 : tar-ryo-iss-ta 取り付けられている、~による(~次第)
~(으)며 ~ : (u-)myo ~で、~ (並列の接続詞、主に文章体)
방식 : bon-sik 方式
~(이)다. : (i-)da ~だ。~である。(文末表現の最も簡単なもの)
*助詞の「の」に対応する助詞は表記されていない。

今回は全く知らなかったハングル(한글)で書かれた韓国語(한국말)を読んでみた。위とか방식とかの漢字語の音が日本語(일본어)に似ていて親しみやすい気がするが、英語テスト1桁点をマークした身にとって外国語はやっぱりよくわからない。でも翻訳機無しで意味が分かるとちょっとおもしろいし、ハングルキーボード(한글 키보드)の入力方法も分かったので満足した。

ラジオの記事なのに韓国語の追記のほうが時間がかかった。

 

広告

ハングルキー配列をマスターできるか。

韓国語は김치(キムチ)と나믈(ナムル)と한국김(韓国のり)以外はほぼ知らないが、韓国語で書かれたガジェットの解説を読みたいのでハングル文字を半日がんばって覚えた。(『1時間でハングルが読めるようになる本』←1時間は無理。3時間。あと音読はできるが意味は理解できない。) 分からない語はggrksると出てくるし、Web翻訳もできるので韓国語を知らなくてもあまり困らないものの、調べるためにはキー入力をしないといけない。これがまたもどかしい。ハングル文字の構造は比較的簡単なので入力方法はすぐに理解できたが、キー配列を覚えていないし、日本語109キーを使っているから配列を覚えるまでハングルはどこかに掲示しておく必要がある。

旧JISキー配列を除いて、これまで配列を覚える方法として画面にキーボードの絵が常に見えるようにしておいて、それを見てキーボードは絶対に見ない方法が効果的だったのでハングルキーボードも同様の方法で試してみる。C#で1行コードの小物アプリを作った。

hangulkey

フォームの背景に拾ってきた配列の絵を指定し、フォームを半透明にしてTopMostプロパティをTrueにしただけ。これさえあればハングルキーボードをマスターしたも同然。

Panasonic RF-P155を改造して短波放送を聴いてみる。

Panasonicの普通のラジオがDSPベースに移行して久しい。

最安クラスのRF-P155R-P145がSi4836-A10を使っているらしく、素性が判っているなら改造も簡単なはずだと、さっそくケーズデンキで買ってきた。2000円。

RF-P155_1

最新のDSPラジオがどういったものなのか、気にはなってたので5分ほど普通に使ってみる。チューニングはアナログとは違うが、びっくりするほど普通の安物ラジオの音だ。(ICF-SW7600GRは間接自慢)

RF-P155_5

即日分解。中央の表面実装チップがSi4836。

RF-P155_2

改造ポイント付近。R8の下側、R14の下側を500kΩ(Bカーブ)の可変抵抗器の両端につなぎ、バンド切り替えスイッチの中点を可変抵抗器のタップにつなぐ。バンド切り替えスイッチは真ん中にして接点を浮かす。

RF-P155_3

筐体を削ったり穴を開けるのもめんどくさいから、バンド切り替えスイッチのツマミを外してそこから線を引き出す。

RF-P155_6

両面テープでネチョっと固定したら、できあがり。ボリュームツマミを取り付けると尚良し。オリジナルの音量、同調ダイヤルはそのままにアンテナも干渉しない完璧なパッケージング。

見てくれはいまいちだがオールバンドのMW/SW/FMラジオになった。バンド切り替えはボリュームを回して少し静止するとバンドが切り替わる動作をするっぽい。FMアンテナにリード線をつけて延長すると、ラジオNIKKEI、CRI、KBSをはじめ朝鮮の声放送とか知らん国の短波放送がいろいろ聞こえてくる。ほかにもアンテナ回り(データシートではSWアンテナは外付けで1石プリアンプ付きが標準)をいじるとか、ステレオ化とか、トーンコントロール(Si4836に搭載の機能)とか、高音質化(スピーカ交換とかアナログ手法で)とか手軽に改造できそう。

参考にしたデータシート。

SILICON LABS Si4836-A10 – BROADCAST MECHANICAL TUNING AM/FM/SW RADIO RECEIVER
https://www.silabs.com/documents/public/data-sheets/Si4836-A10.pdf

Si4825/36-A ANTENNA, SCHEMATIC, LAYOUT, AND DESIGN GUIDELINES
https://www.silabs.com/documents/public/application-notes/AN738.pdf

TUNE2とGND間の抵抗値でバンドを切り替える仕組みになっており、リファレンスでは固定抵抗になっているが、はんだ付けはできるだけしたくないし計算も面倒だから可変抵抗器で実装した。目盛りをつけないとどこに合っているのかよく分からない。このスプレッド同調っぽいチューニングは案外悪くなく、バンドの状態をざっと知りたいときはICF-SW7600GRより軽快だ。短波は18バンドに分かれており、FM/AMも各国対応のバンドがあったりする。すべてのバンドが使えるかは試していないので不明だが、本来想定されてる固定抵抗で実装するほうが使い勝手は良いかも。

aitendoなんかのDSPチップ使用の電子キットラジオやモジュールを改造する記事は探すと結構出てるのに市販DSPラジオの短波対応改造はあまり見かけない。RF-P155の改造記事としては本邦初かも。本当はAM専用のR-P145で試したかったがケーズデンキの在庫が無かった。1,500円とか2,000円出せば同じようなDSPチップを搭載した中国製短波ラジオは買えるけれど、そのへんの電気屋で売ってる最安値ラジオに100円そこそこの部品をつけるだけで短波ラジオになるのがおもしろい。スーパーラジオの調整よりも簡単。

今のDSPラジオには短波コンバータなんて要らないな。

 

短波コンバータを作ってみた。

短波放送を聴くなら短波ラジオを買えばよいのだが、ン十年前に読んでいた『子供の科学』で科学教材社の広告に載っていた「短波コンバータ」なるものが気になって仕方なかった。小学生には若干お高いので結局買わずじまいだったが、これを使えば手持ちのAMラジオで短波が聞ける、とそういうもの。

短波ラジオを持ってるのに、いまだに「短波コンバータ」への未練があるので作ってみる。いつものように短波コンバータでggrksるとOM皆さん素晴らしい成果を公開してくださってますね。ノスタルジックに真空管でとか、正統派高周波なデュアルゲートFETでミキサとか、VCO+リング変調器でとか。

今は短波ラジオを持ってるし、そうでなくともストリーミングで聴けるし、そんなに凝って作ってもどうせ使わないだろう。それにハンダ付けなんて面倒だし。なので手間をかけずに手っ取り早く作ったことにしたい。



できたできた。

swconv.jpg

おそらくラジオ工作史上最も簡単な短波コンバータ。ディップメーターのコイル部に4mくらいのワイヤアンテナの根本を数回巻き付けて、その端をAMラジオのアンテナに接続しただけ。

親ラジオはソニーのICF-801。ごく普通のアナログAM/FMラジオ。アンテナはFM用だが、ラジオ本体に電波をInjectionできればいいのでこれで十分。AMラジオは上側バンドエッジ付近にチューニングしておき、ディップメーターで3~10MHz付近のコイルを使って最大出力くらいで適当に発振させる。ディップメーターのダイヤルをゆっくりゆっくり廻していくと運が良ければ短波放送が聞こえてくる。微調整はディップメーターの出力調整である程度可能だし、AMラジオ側でも行える。ディップメーターでなくともテストオシレータでもアンテナアナライザでもDDSなFGでもとにかく微弱なHF帯の信号を可変に発振できる機器なら何でもいい。短波ラジオの局発を取り出すとかいう意味不明な手段も良い。

夜に試すとダイヤル読み11.5MHz付近でCRI他5局くらい簡単に聞こえてきた。分離も十分。短波特有のフェージングも体感できる。ディップメーターの発振やコイルの結合も思ったより安定しててしばらく聞いていても周波数がそこまでずれたりもしない。

仕組みとしては原理的で、ワイヤーアンテナで受信した短波の電波Fwをディップメーターが発振する信号Foと混合してヘテロダイン周波数Fs=Fw-FoまたはFs=Fo-Fwを得る。AMラジオはSWよりも低い周波数しか受からないので必然的に差の周波数を受信することになる。ディップメーターが12MHz、AMラジオが1600kHzなら受信している周波数は13.6MHzか10.4MHz付近と推定できる。4アマでも習う内容。親ラジオの特性に影響されるから再現性は良くないが、いじるところが多くて面白い。アンテナを工夫するとか、結合コイルの巻き数はとか、ラジオのどこに接続するかとか、ラジオの受信周波数はとか、こんな単純なものでも試すところは無数にある。

性能は市販の短波ラジオには遠く及ばないが、こういう不便なのも良い。

ワイヤーアンテナを外に出してみたらHF帯のどのコイルでもなんらかの放送が聞こえてくる。午前10時前後に全部で30局以上は確認できる感じ。また親ラジオをFM76MHz付近のバンドエッジで受信し、VHF帯のコイルを使ったら、Eスポが発生していて90MHz台にある中韓あたりの放送も5局くらい入ってきた。親ラジオ単体+同一のワイヤアンテナで90MHz台を直接受信するよりなぜかずっと多く聞こえる。さらにHF帯のアマチュアのCWまで聞こえてきた。デジタル表示で一発選曲の広帯域受信機で聞くよりも未知の電波を捉えた気分が楽しい。

太陽観測用のサンプリズムをばらしてみた。

望遠鏡で太陽を見るための旧い小道具。サンプリズムとサンプリズム用サングラス。

sunprism1

これらを使うと「太陽を観てみた。」の写真のように直接太陽を観望できる。

裏に穴がある。
sunprism2

穴から向こうがスケスケ。
sunprism3

海外では今も作られているのに、ことあるごとに世界最高を自負する日本においては神様であられる「消費者様からのご意見」に怯え負け、PL法のもとに製造を封印し二度と作られることはない。

Cool-Ceramic Safety Herschel prism
http://astrosolar.com/en/products/whitelight/baader-2-cool-ceramic-safety-herschel-prism/

White Light Solar Wedges
https://luntsolarsystems.com/product/white-light-solar-wedges/

Hercules-Red SOLAR SYSTEMS 1.25″
https://www.aliexpress.com/store/product/Hercules-SOLAR-systems-1-25-Herschel-prism/1371081_32532173381.html

バーダー製のは国際光機で取り扱いがあるし、AliExpressで扱ってるものなら国内でも比較的簡単にとり寄せできる気がする。

構造は非常に簡単。くさび型の板ガラスが入っていて、透過光は反対側に素通して表面反射像だけを接眼側に導く、裏面反射像は視野外へ反射させるようになっている。これで94~96%の光を外に逃がして、4~6%だけをアイピース側に送ることになる。これでもまだ減光不足なので濃い緑色のフィルターでさらに減光させる。フィルタが緑色なのは溶接面の遮光ガラスを流用していたのかも。バーダー製など今どきの海外製は白色光で観測できるのもある。

プリズムのガワの蓋を開けたところ。一回り大きな穴が空いた板バネが入っていた。
sunprism4-2

板バネを取り除くとくさびガラスが全部見える。接着剤で固定されている。スケールの反射像がずれた二重になっていて平行ガラスでないことが分かる。プリズムのガワ自体は穴の開いた裏蓋以外24.5mmの天頂プリズムと同一で、直角プリズムを横から固定するイモネジの穴もあいている。
sunprism4

プリズム無しで直接見るためのサングラスも存在するが、使ってみるとかなり熱くなる。2分も連続で観測した後取り外してフィルタ面に触ってみるとアツッていうくらい。一説では10~15分の連続使用で割れるらしい。くさびプリズムを通すと3分程度連続でも暖かいっていうくらいで割れるようなことはめったにないはず。でも9割以上の光・熱エネルギーがプリズムの外に放出されているうえに、プリズムの少し後ろの筒外で焦点を結んでいるので、その光線に触れるとアツッってなる。最初にあげた海外製品はこれの対策はしてある。

減光率がどの程度なのか定量的に測ってみたいが、きちんとした機材は無いしCdsとかフォトダイオードで電圧を測るとかだとフィルタを通すとかなり減光されてまともに測れない。とりあえずデジカメのマニュアルモードで簡易的に見てみる。そのへんにあったパルックボール(電球型蛍光灯)100V13W電球色を50cmほどの距離で撮影。カラーバランスはマニュアルなので白色に見える。

サンプリズム透過 F2.8 E1/2000 ISO125 ND3
sunprism2-1

サンプリズム反射 F2.8 E1/60 ISO125 ND3
sunprism2-2

サングラス透過 F2.8 E1/60 ISO3200 ND0
sunprism2-3

サンプリズム反射+サングラス F2.8 E1/15 ISO12800 ND0
sunprism2-4

映った明るさのばらつきがあるので正確ではないが、感覚的にプリズム反射で6段、サングラス透過で8段、合成で14段くらいの減光ができるみたい。画像処理ソフトできちんと明るさ測ってやらんとダメかも。

プリズムの形状がどうなってるかも調べた。横から見ると台形になっていて斜めの部分の角度は約6度。図の下側が反射面、上側が放出面になる。両面ともメッキやコートは無し。斜めになった裏の反射面の反射光が視野外になればいいだけなので板ガラスを磨けば作れなくも無い。

sunprism3-1

この板きれが穴の開いた天頂プリズムのガワに嵌めこんであるだけだし、溶接面の遮光ガラスをフィルターにすれば自作もできるが、そういうのは自己責任で。

8080のTinyBASICで遊ぶ その4 – 走らせる。

分割して掲載したソースをコンパイルやアセンブルしてPATBSIM.EXE、PATBSIM.HEXができたら実行する。

「PALO ALTO TINY BASIC V3.0/OK/>」 と出たら成功。

patbsim-1

PRINT 3+3*3 [Enter]

と入力すると、四則演算の結果として

12

が表示されればそこはもうハローTiny BASICワールド。

Ctrl-Cでシミュレータのコンソールに切り替え、1:LoadResでコンソール読み込みのファイル名を指定 (prime.bas)、指定ファイルがキー入力として流し込まれる。(いわゆるLOADコマンドの代わり)

あとはRUN[Enter]するだけ。省略表記でR.でもよい。

patbsim-2

Ctrl-Cで出てくるメニューで終了を含む各種制御ができる。9:Shutdownで終了。

Tiny BASICのソースを用意すればスタートレックだって遊べる。STARTREK.BASの動作が若干バギーなのでコードは掲載しない。

patbsim-3.JPG

ちょっとだけ解説というか個人的感想。

Tiny BASICを動かしたければ、単純にWindowsで動くインタプリタを用意すればよいのだが、オリジナルのPALO ALTO TINY BASICのコードをできるだけいじらずに動かしたかったので8080シミュレータ(エミュレータ)を作り、その中でPALO ALTO TINY BASICのコードを動かすことにした。8080アセンブラで書かれたインタプリタをWin32なアセンブラやCに移植するよりは、単純な8080エミュレータを新規に組むほうが確実だろう。目的としてタイニーBASICを動かすこと以外は考えていないが、テストプログラムですべてのマシン語命令が動くことは確認しているのでファームとI/OポートやメモリマップドI/Oを追加すれば他の8080マシンのエミュレーションにも使えるとは思う。

今回作ったC-Simがエミュレータというのかシミュレータというのかは微妙で、クロックサイクルや割り込みタイミングの再現やらオペコードの分解解釈などを実装しているのでエミュレーションに近いと思う。再現しているのは8080CPU本体と64kBytesメモリのほかはIOポート3種類と簡易ICE機能のみ。本当はディスクIFとBIOSを実装してCP/M互換機にしたいが「今後の実装」ってことで放置。

自分が生まれる前の市販マイコン登場前夜に個人が作ってたマイコンってこんな雰囲気だったんだろうか。できればその時代の最先端に触れたかった。うらやましい。

8080のTinyBASICで遊ぶ その3 – サンプルコード

前回、前々回で作ったC-SimとPALO ALTO TINY BASICベースのファームで動く簡単なTiny BASICのサンプルコード。どこかの資料から拝借したそのままだと思う。

PRIME.BAS 指定した数値までの素数を表示

1010 INPUT "MAX",M
1100 IF M >= 2 PRINT 2, " IS PRIME NUMBER."
1200 A = 3
1210 GOSUB 2000
1220 A = A+2
1230 IF A<=M GOTO 1210
1340 STOP
2000 I = 3
2005 E = A/3
2010 IF I > E GOTO 2100
2020 IF A/I*I = A RETURN
2030 I = I+2
2040 GOTO 2010
2100 PRINT A," IS PRIME NUMBER."
2110 RETURN

RNDSORT.BAS 10個のランダムな値をソートして表示。

100 REM SET 10 RANDOM VALUES
110 FOR I=0 TO 9
120 @(I)=RND(1000) 
130 NEXT I
140 REM SORT
150 FOR I=8 TO 0 STEP -1
160 FOR J=0 TO I
170 GOSUB 510
180 NEXT J
190 NEXT I
200 REM PRINT
210 FOR I=0 TO 9
220 PRINT #5,@(I)
230 NEXT I
240 STOP
500 REM SWAP SUBROUTINE
510 IF @(J)>=@(J+1) GOTO 550
520 T=@(J);@(J)=@(J+1);@(J+1)=T
550 RETURN

8080のTinyBASICで遊ぶ その2 – TinyBASIC本体コード

8年くらい前に作った8080シミュレータで動くPalo Alto Tiny BASICのオブジェクトコード。

BASIC Programming Resources and Chipmunk Basic Archive
http://www.nicholson.com/rhn/basic/basic.info.html

ここからもらってきた「Palo Alto Tiny BASIC Interpreter Version 3.0」の最後の部分にある1文字出力、1文字入力、入力チェックルーチンを前回のシミュレータに合わせて変更したもの。基本的にはCP/M-80の低水準入出力と同じ仕様にしてある。アセンブルはCP/M用のDigital Research Macro Assembler (MAC) をDOS上で動くCP/Mシミュレータで動かしてPATBSIM.HEXファイルを作成する。

PALO ALTO TINY BASIC V3.0 末尾の入出力部抜粋。

;
CRLF: 	MVI 	A, 0DH  		;CR in A
;    				;***********************
OUTCH:	JMP 	USEOUT  	;*** JMP USER-OUTPUT ***
;    				;***********************
CHKIO: 	JMP 	USEINP  	;*** JMP USER-INPUT  ***
;    				;***********************
GETLN: 	LXI 	D, BUFFER 	;*** MODIFY THIS *******
;    				;***********************
GL1: 	CALL 	OUTCH  	;prompt or echo
GL2:
 	CALL 	CHKIO  		;get a character
 	JZ 	GL2  		;wait for input
 	CPI 	LF
 	JZ 	GL2
L3: 	STAX 	D  		;save char.
 	CPI 	08H  		;is it Back-Space?
 	JNZ 	GL4  		;no, more tests
 	MOV 	A, E  		;yes, delete?
 	CPI 	LOW BUFFER
 	JZ 	GL2  		;nothing to delete
 	LDAX 	D  		;delete
 	DCX 	D
 	JMP 	GL1
GL4: 	CPI 	CR  		;was it CR?
 	JZ 	GL5  		;yes, end of line
 	MOV 	A, E  		;else, more free room?
 	CPI 	LOW BUFEND
 	JZ 	GL2  		;no, wait for CR/Rub-Out
 	LDAX 	D  		;yes, bump pointer
 	INX 	D
 	JMP 	GL1
GL5: 	INX 	D  		;end of line
 	INX 	D  		;bump pointer
 	MVI 	A, 0FFH  	;put marker after it
 	STAX 	D
 	DCX 	D
 	JMP 	CRLF
;-------------------------------
USEOUT:
	OUT	1
 	CPI 	CR  		;was it CR?
 	RNZ   			;no, return
 	MVI 	A, LF  		;yes, give LF
 	CALL 	USEOUT
 	MVI 	A, CR
 	RET
;-------------------------------
USEINP:
	IN	2
	ORA	A
 	RZ   			;no input, return zero
;-------------------------------
	IN	3
 	ANI 	7FH
 	CPI 	3  		;is it Control-C?
 	RNZ   			;no, return char
 	JMP 	INIT  		;yes, restart
;-------------------------------
	END

PATBSIM.HEX

:03010000C300F049
:10F00000310002CDA5F72180003EC3BECA26F077AD
:10F010002100A02281003EF032C8002106202200FB
:10F020002026FF220220112FF0CD72F6C35DF05092
:10F03000414C4F20414C544F2054494E592042419D
:10F040005349432056332E300D4F4B0D57484154F2
:10F050003F0D484F573F0D534F5252590D3100024B
:10F060002167F022B70021000022BD0022B9001163
:10F0700049F0CD72F63E3ECDADF7D511CA00CDDCDC
:10F08000F5CD2FF57CB5C1CAD6F01B7C121B7D12C5
:10F09000C5D57993F5CD71F5D5C2ACF0D5CD8AF54E
:10F0A000C12A0020CD0DF66069220020C12A00206F
:10F0B000F1E5FE03CA5DF0855F3E008C572A8100B2
:10F0C000EBCDFAF4D26AF5220020D1CD18F6D1E1C9
:10F0D000CD0DF6C375F0210FF7CD2FF5D51A13FE20
:10F0E0002ECAFAF023BECADDF03E7F1BBEDA01F164
:10F0F00023BED2F0F023D1C3D9F03E7F23BED2FC91
:10F10000F07E236EE6FF67F1E9CD37F5C31BF0CD46
:10F1100037F5C35DF0CD37F5110220210000CD7920
:10F12000F5DA5DF0EB22B700EB1313CDAAF7211F40
:10F13000F7C3D9F0CD68F3D5CD37F5CD71F5C2075A
:10F14000F6F1C324F1D300C9CDDCF5E521FFFFCDF5
:10F15000C8F52C03CDDCF5E3CD37F5CD71F5DA5DDF
:10F16000F0E37CB5CA5DF02BE3CDFFF6CD72F6CDB2
:10F17000AAF7CD79F5C35EF10E08CDC8F53B06CDF3
:10F18000A5F7C32BF1CDC8F50D24CDA5F7C31BF111
:10F19000CDC8F5230ECD68F33EC0A5B4C206F64D2A
:10F1A000C3A9F1CD81F6C3C7F1CDC8F52C13CDC8E5
:10F1B000F52C083E20CDA7F7C3AEF1CD1CF5C390CA
:10F1C000F1CDA5F7C316F5CD68F3C5CDBBF6C1C328
:10F1D000A9F1CD43F6CD68F3D5CD71F5C207F62A76
:10F1E000B700E52AB900E521000022BD003922B9A7
:10F1F00000C324F1CD37F52AB9007CB5CA3DF5F935
:10F20000E122B900E122B700D1CD27F6C316F5CD32
:10F2100043F6CD00F52B22BD002180F7C3D9F0CDF8
:10F2200068F322C1002186F7C3D9F0CD68F3C33457
:10F23000F221010022BF002AB70022C300EB22C541
:10F2400000010A002ABD00EB606839C34FF2097E55
:10F2500023B6CA6FF27E2BBAC24EF27EBBC24EF20A
:10F26000EB21000039444D210A0019CD18F6F92A86
:10F27000C500EBC316F5CD96F5DA3DF522BB00D5FA
:10F28000EB2ABD007CB5CA3EF5CDFAF4CA99F2D19D
:10F29000CD27F62ABB00C37FF25E23562ABF00E5C6
:10F2A0007CAA7A19FAABF2ACFACFF2EB2ABD007362
:10F2B00023722AC100F1B7F2BBF2EBCDF0F4D1DA40
:10F2C000D1F22AC30022B7002AC500EBC316F5E12C
:10F2D000D1CD27F6C316F5210000C3E0F2CD68F3C7
:10F2E0007CB5C22BF1CD8CF5D224F1C35DF02ABBE5
:10F2F00000F9E122B700D1D1D5CD81F6C327F3CDF6
:10F3000096F5DA1BF3CD39F311CA00CD68F3CD378A
:10F31000F5D1EB732372E122B700D1F1CDC8F52C02
:10F3200003C3F8F2C316F5D5CD96F5D231F3C33D3C
:10F33000F543D1CDB0F6C305F3C1D5EB2AB700E54F
:10F3400021F8F222B7002100003922BB00D53E206F
:10F35000C5C3ADF71AFE0DCA65F3CD00F5CDC8F5EE
:10F360002C03C35AF3C316F5CDB0F3E5218EF7C3D2
:10F37000D9F0CD9BF3D86FC9CD9BF3C86FC9CD9B96
:10F38000F3C8D86FC9CD9BF36FC8D86CC9CD9BF3B8
:10F39000C06FC9CD9BF3D06FC9E1C979E1C1E5C5A3
:10F3A0004FCDB0F3EBE3CDF0F4D12100003E01C925
:10F3B000CDC8F52D06210000C3E2F3CDC8F52B0022
:10F3C000CDECF3CDC8F52B15E5CDECF3EBE37CAA42
:10F3D0007A19D1FAC3F3ACF2C3F3C306F6CDC8F57C
:10F3E0002D92E5CDECF3CDDBF4C3CCF3CD50F4CDD1
:10F3F000C8F52A2DE5CD50F40600CDD8F4E3CDD8DC
:10F40000F4EBE37CB7CA0EF47AB2EBC207F67D21C7
:10F410000000B7CA42F419DA07F63DC216F4C34237
:10F42000F4CDC8F52F4EE5CD50F40600CDD8F4E369
:10F43000CDD8F4EBE3EB7AB3CA07F6C5CDBBF460E5
:10F4400069C1D17CB7FA06F678B7FCDBF4C3EFF3F9
:10F45000216BF7C3D9F0CD96F5DA61F47E23666FA0
:10F46000C9CDDCF578B7C0CDC8F52809CD68F3CD96
:10F47000C8F52901C9C33DF5CD67F47CB7FA06F696
:10F48000B5CA06F6D5E52AC70011A5F7CDFAF4DA14
:10F4900095F42100F05E235622C700E1EBC5CDBBF9
:10F4A000F4C1D123C9CD67F41BCDD8F413C92A0008
:10F4B00020D5EB2A8100CDD1F4D1C9E56C2600CD51
:10F4C000C6F4417DE1670EFF0CCDD1F4D2C8F4192A
:10F4D000C97D936F7C9A67C97CB7F07CB5C87CF511
:10F4E0002F677D2F6F23F1ACF206F678EE8047C9C7
:10F4F0007CAAF2F6F4EBCDFAF4C97CBAC07DBBC9A4
:10F50000CD96F5DA3DF5E5CDC8F53D0DCD68F34472
:10F510004DE1712370C9CD1CF5C33DF5CDC8F53B58
:10F5200004F1C32BF1CDC8F50D04F1C31BF1C91AC9
:10F53000FE20C013C32FF5CD2FF5FE0DC8D5114CFD
:10F54000F0CDA5F7CD72F62AB700E57E23B6D1CA75
:10F5500026F07EB7FAEEF2CDFFF6C141CDB0F63E11
:10F560003FCDA7F7CD72F6C326F0D51157F0C341B2
:10F57000F57CB7FA06F6110220131A1B87D81A95E4
:10F5800047131A9CDA8BF51BB0C913131AFE0DC270
:10F590008BF513C379F5CD2FF5D640D8C2B8F51346
:10F5A000CD67F429DA06F6D5EBCDAEF4CDFAF4DA70
:10F5B0006BF5CD6CF619D1C9FE1B3FD81321810024
:10F5C00007856F3E008C67C9E3CD2FF5BE23CAD8EF
:10F5D000F5C54E060009C11B1323E3C921000044F1
:10F5E000CD2FF5FE30D8FE3AD03EF0A4C206F60488
:10F5F000C5444D292909291A13E60F856F3E008C51
:10F6000067C11AF2E3F5D51152F0C341F5CDFAF412
:10F61000C81A021303C30DF67892C220F67993C874
:10F620001B2B1A77C318F6C1E122BD007CB5CA4175
:10F63000F6E122BF00E122C100E122C300E122C5C0
:10F6400000C5C9215201CDDBF4C139D26AF52ABD0A
:10F65000007CB5CA69F62AC500E52AC300E52AC1BF
:10F6600000E52ABF00E52ABD00E5C5C92A00202B18
:10F670002BC997471A13B8C8CDA7F7FE0DC274F669
:10F68000C9CDC8F5220F3E22CD73F6FE0DE1CA1B8F
:10F69000F1232323E9CDC8F527053E27C388F6CDFE
:10F6A000C8F55E0B1AEE40CDA7F71A13C38BF6C947
:10F6B0007BB8C81ACDA7F713C3B0F60600CDD8F4AF
:10F6C000F2C6F6062D0DD5110A00D50DC5CDBBF439
:10F6D00078B1CADDF6E32DE56069C3CDF6C10D79D9
:10F6E000B7FAECF63E20CDA7F7C3DEF678B7C4A78D
:10F6F000F75D7BFE0AD1C8C630CDA7F7C3F2F61A74
:10F700006F131A67130E04CDBBF63E20CDA7F7C9C1
:10F710004C495354F1484E4557F10952554EF11595
:10F720004E455854F2764C4554F35A4946F2DD475B
:10F730004F544FF134474F535542F1D2524554552F
:10F74000524EF1F452454DF2D7464F52F20F494E08
:10F75000505554F2F85052494E54F17853544F508A
:10F76000F10F454E44F145F769C354F3524E44F44A
:10F7700078414253F4A553495A45F4AEF77EC35637
:10F78000F4544FF21FF53D53544550F22BF2313EE5
:10F790003DF37223F3783EF37E3DF38D3C3DF385DC
:10F7A0003CF393F3993E0DC3E8F7C3F5F711CA0094
:10F7B000CDA7F7CDAAF7CAB3F7FE0ACAB3F712FE70
:10F7C00008C2CFF77BFECACAB3F71A1BC3B0F7FE55
:10F7D0000DCADFF77BFE4ECAB3F71A13C3B0F71397
:10F7E000133EFF121BC3A5F7D301FE0DC03E0ACD89
:10F7F000E8F73E0DC9DB02B7C8DB03E67FFE03C0B6
:03F80000C300F052
:0000000000

8080のTinyBASICで遊ぶ その1 – C-Sim本体

8年位前に作った8080シミュレータ本体。VC6でコンパイルできたはず。VS2018でもコンパイルは通ったが入力がおかしくなったので入力周りの調整が必要だと思われ。

patbsim.c

/*
PALO ALTO TINY BASIC用 i8080シミュレータ
>patbsim infile.bas
ハードとICE制御はCtrl+C
*/

#include 
#include 
#include 
#include 

char* Patbdatfile = "patbsim.hex";

const int SIGN   = 0x80;
const int ZERO   = 0x40;
const int AUXCY  = 0x10;
const int PARITY = 0x04;
const int CARRY  = 0x01;

int loadhex(char* Filename);
int dumphex(char* Filename, int size, unsigned char* buf);
void run(void);

unsigned long incr(unsigned long *reg);
void dispreg(char *Munimonic);
int dispregen = 0;
void trace1(int level, char* Msg);


unsigned int val2(char *buf);
unsigned int val4(char *buf);

unsigned char mem[0x10000];
unsigned char reg[10];
unsigned long pc, sp;
unsigned char enint[3] = {0, 0, 0};

unsigned char inst;

unsigned char setflag(int sign, int zero, int halfcy, int parity, int carry);
int isparity(unsigned long value);
int getcy(void);
int getac(void);

int hard_in(unsigned int port);
int hard_out(unsigned int port, int data);

FILE *fpLoad = NULL;
FILE *fpSave = NULL;
int initload = 0;
void inthandl(int sig);
void hard_control(void);
int intsignal = 0;
int lastchar = 0;

void ice_control(void);
int icemode = 0;
int icetrace = 0;
int icerun = 0;
int icestep = 0;

long acc8(int opr, long x, long y, int c, char* state);

/*
long acc8(int opr, long x, long y, int c, char* state)

8ビットアキュムレータのシミュレート
*/
long acc8(int opr, long x, long y, int c, char* state)
{
	
	int sign, zero, ac, parity, cy;
	int i;
	
	switch(opr) {
	case 0:
		//printf("acc8 add %02x %02x\n", x, y);
		ac = ( (x & 0xf) + (y & 0xf) ) >> 4;
		x = x + y;
		cy  = (x >> 8) & 1;
		break;
		
	case 1:
		//printf("acc8 adc %d %d %d\n", x, y, c);
		ac = ( (x & 0xf) + (y & 0xf) + ( c & 0x1) ) >> 4;
		x = x + y + c;
		cy  = (x >> 8) & 1;
		break;

	case 2:
		//printf("acc8 sub %d %d\n", x, y);
		ac = ( (x & 0xf) - (y & 0xf) ) >> 4 & 1;
		x = x - y;
		cy = (x >> 8) & 1;
		break;
		
	case 3:
		//printf("acc8 sbb %d %d %d\n", x, y, c);
		ac = ((x & 0xf) - (y & 0xf) - c) >> 4 & 1;
		x = x - y - c;
		cy = (x >> 8) & 1;
		break;

	case 4:
		//printf("acc8 ana %d %d", x, y);
		ac = 0;
		x = x & y;
		cy = 0;
		break;

	case 5:
		//printf("acc8 xra %d %d", x, y);
		ac = 0;
		x = x ^ y;
		cy = 0;
		break;

	case 6:
		//printf("acc8 orr %d %d", x, y);
		ac = 0;
		x = x | y;
		cy = 0;
		break;

	case 7:
		//printf("acc8 orr %d %d", x, y);
		ac = (x & 0xf - y & 0xf) >> 4;
		x = x - y;
		cy = (x >> 8) & 1;
		break;
	}

	x = x & 0xff;

	if( (x & 0x80) != 0) {
		*state = *state |  SIGN;
	}
	else {
		*state = *state & ~SIGN;
	}
	
	if( x == 0 ) {
		*state = *state |  ZERO;
	}
	else {
		*state = *state & ~ZERO;
	}
	
	parity = 0;
	for(i=0; i > i) & 1);
	}
	if(parity == 0) {
		*state = *state |  PARITY;
	}
	else {
		*state = *state & ~PARITY;
	}
	
	if(cy != 0) {
		*state = *state |  CARRY;
	}
	else {
		*state = *state & ~CARRY;
	}


	if(ac != 0) {
		*state = *state |  AUXCY;
	}
	else {
		*state = *state & ~AUXCY;
	}
	
	return x;
}

/*
int isparity(unsigned long value)

パリティの算出
*/
int isparity(unsigned long value)
{
	int i;
	int p=0;
	for(i=0; i> i) & 1;
	}
	return( ~p & 1 );
}

/*
int getcy(void)

フラグレジスタのキャリー状態を取得
*/
int getcy(void)
{
	if(reg[6] & 0x01) {
		return 1;
	}
	else {
		return 0;
	}
}

/*
フラグレジスタの補助キャリー状態を取得
*/
int getac(void)
{
	if(reg[6] & AUXCY) {
		return 1;
	}
	else {
		return 0;
	}
}


/*
unsigned char setflag(int sign, int zero, int halfcy, int parity, int carry)

フラグレジスタの状態設定。
値に-1を指定すると変更しない。
*/
unsigned char setflag(int sign, int zero, int halfcy, int parity, int carry)
{
	if(sign == 0) {
		reg[6] = reg[6] & ~0x80;
	}
	else if(sign >= 1) {
		reg[6] = reg[6] |  0x80;
	}
	
	if(zero == 0) {
		reg[6] = reg[6] & ~0x40;
	}
	else if(zero >= 1) {
		reg[6] = reg[6] |  0x40;
	}
	
	if(halfcy == 0) {
		reg[6] = reg[6] & ~0x10;
	}
	else if(halfcy >= 1) {
		reg[6] = reg[6] |  0x10;
	}
	
	if(parity == 0) {
		reg[6] = reg[6] & ~0x04;
	}
	else if(parity >= 1) {
		reg[6] = reg[6] |  0x04;
	}
	
	if(carry == 0) {
		reg[6] = reg[6] & ~0x01;
	}
	else if(carry >= 1) {
		reg[6] = reg[6] |  0x01;
	}

	reg[6] = reg[6] & ~0x2a;
	
	return reg[6];
}


/*
int hard_in(unsigned int port)
port ポート番号

I/O空間からの入力をエミュレーションする。
*/
int hard_in(unsigned int port)
{
	int data = 0;
	
	switch(port) {
	case 2:	/* キー入力状態の読み取り */
		if(lastchar != -1) {
			data = 1;
			break;
		}
		
		if(fpLoad != NULL && feof(fpLoad) == 0) {
			//printf("load.\n");
			data = 1;
			break;
		}
		
		data = _kbhit();
		
		break;
		
	case 3:	/* キー入力データの読み取り */
		if(lastchar != -1) {
			data = lastchar;
			lastchar = -1;
			break;
		}
		
		if(fpLoad != NULL && feof(fpLoad) == 0) {
			data = fgetc(fpLoad);
			if(data == '\n') {
				data = 0x0d;
			}
			if(data == EOF) {
				fclose(fpLoad);
				fpLoad = NULL;
				data = 0x0d;
				initload = 0;
			}
			break;
		}
		
		if(_kbhit() != 0) {
			data = getch();
		}
		else {
			data = 0;
		}
		break;
	}
	
	return data;
}


/*
int hard_out(unsigned int port, int data)
port ポート番号
data 出力する値

I/O空間への出力をエミュレートする。
*/
int hard_out(unsigned int port, int data)
{
	int outdata = 0;
	
	switch(port) {
	case 0:
		//printf("Terminate.\n");
		exit(0);
		
	case 1:	/* コンソールへ1文字出力 */
		if(initload == 1) {
			break;
		}
		putchar(data);
		if(fpSave != NULL && data != 0x0d) {
			fputc(data, fpSave);
		}
		outdata = data;
		break;

	case 0xf0:	/* 一時停止 */
		printf("\nexecute OUT F0H. Wait for breaking.\n");
		getchar();
		break;
		
	case 0xfe:	/* トレースモードの開始 */
		dispregen = 1;
		dispreg("hard_out(0)");
		break;

	case 0xff:	/* トレースモードの終了 */
		dispregen = 0;
		break;

	}
	
	return outdata;
}


/*
int eval_flag(unsigned char acc);
int set_cy(int value);
int get_cy(void);
int set_zero(int value);
int set_minus(int value);


int eval_flag(unsigned char acc)
{
	if(acc == 0x00) {
		set_zero(1);
	}
	else {
		set_zero(0);
	}
	
	set_minus(acc & 0x80);

	return(reg[6]);
}

int set_cy(int value)
{
	if(value == 0) {
		reg[6] = reg[6] & 0x01;
	}
	else {
		reg[6] = reg[6] | 0x01;
	}
	
	return(reg[6]);
}

int get_cy(void)
{
	return(reg[6] & 0x01);
}


int set_zero(int value)
{
	if(value == 0) {
		reg[6] = reg[6] & 0x40;
	}
	else {
		reg[6] = reg[6] | 0x40;
	}
	return(reg[6]);
}

int set_minus(int value)
{
	if(value == 0) {
		reg[6] = reg[6] & 0x80;
	}
	else {
		reg[6] = reg[6] | 0x80;
	}
	return(reg[6]);
}
*/


// 000 B 001 C 010 D 011 E 100 H 101 L 111 A
// 00  B        01  D(DE)    10  H(HL) 11 SP/PSW(AF)
/*
b7:S 符号 
b6:Z ゼロ 
b5:未使用 (0に固定) 
b4:H AUXキャリー(パックBCD演算用) 
b3:未使用 (0に固定) 
b2:P パリティ 
b1:未使用 (0に固定) 
b0:C キャリー 

命令コード アセンブラ表記  clock uPDck 意味
00 000 000  NOP              4     4   no operation

00 pp0 001  LXI   rp, imm16 10    10   load imediate register pair
00 ddd 100  INR   rm        5/10  5/10 increment
00 ddd 101  DCR   rm        5/10  5/10 decrement
00 ddd 110  MVI   rm, imm   7/10  7/10 move immediate

00 100 010  SHLD  addr      16    16   store H & L direct
00 101 010  LHLD  addr      16    16   store H & L direct
00 110 010  STA   addr      13    13   store A direct

00 000 111  RLC              4     4   rotate A left
00 001 111  RRC              4     4   rotate A right
00 010 111  RAL              4     4   rotate A left through carry
00 011 111  RAR              4     4   rotate A right through carry
00 100 111  DAA              4     4   decimal adjust A
00 101 111  CMA              4     4   complement A
00 110 111  STC              4     4   set carry
00 111 111  CMC              4     4   complement carry

00 pp1 001  DAD   rp        10    11   add register pair to H & L
00 pp1 010  LDAX  rx         7     7   load A indirect
00 pp1 011  DCX   rp         5     5   decrement register pair
00 pp0 010  STAX  rx         7     7   store A indirect
00 pp0 011  INX   rp         5     5   increment register pair

01 ddd sss  MOV   rm, r     5/7   4/7  move
01 ddd 110  MOV   r, M       7     7   move memory to register
01 110 110  HLT              7     7   halt

10 000 sss  ADD   rm        4/7   4/7  add
10 001 sss  ADC   rm        4/7   4/7  add with carry
10 010 sss  SUB   rm        4/7   4/7  subtract
10 011 sss  SBB   rm        4/7   4/7  subtract with borrow
10 100 sss  ANA   rm        4/7   4/7  and with A
10 101 sss  XRA   rm        4/7   4/7  exclusive or with A
10 110 sss  ORA   rm        4/7   4/7  or with A
10 111 sss  CMP   rm        4/7   4/7  compare with A

11 vvv 111  RST   vec       11    11   restart
11 pp0 001  POP   rr        10    10   pop register pair off stack
11 pp0 101  PUSH  rr        11    11   push register pair on stack
11 001 101  CALL  addr      17    17   call unconditional

11 000 000  RNZ             5/11  5/11 return on no zero
11 001 000  RZ              5/11  5/11 return on zero
11 010 000  RNC             5/11  5/11 return on no carry
11 011 000  RC              5/11  5/11 return on carry
11 100 000  RPO             5/11  5/11 return on parity odd
11 110 000  RP              5/11  5/11 return on positive
11 101 000  RPE             5/11  5/11 return on parity even
11 111 000  RM              5/11  5/11 return on minus

11 001 001  RET             10    11   return
11 101 001  PCHL             5     5   H & L to program counter
11 111 001  SPHL             5     4   H & L to stack pointer

11 011 010  JC    addr      10    10   jump on carry
11 010 010  JNC   addr      10    10   jump on no carry
11 001 010  JZ    addr      10    10   jump on zero
11 000 010  JNZ   addr      10    10   jump on no zero
11 110 010  JP    addr      10    10   jump on positive
11 111 010  JM    addr      10    10   jump on minus
11 101 010  JPE   addr      10    10   jump on parity even
11 100 010  JPO   addr      10    10   jump on parity odd

11 000 011  JMP   addr      10    10   jump unconditional
11 011 011  IN    port      10    10   input
11 010 011  OUT   port      10    10   output
11 100 011  XTHL            18    17   exchange top of stack, H & L
11 101 011  XCHG             4     4   exchange D & E, H & L registers
11 110 011  DI               4     4   disable interrupt
11 111 011  EI               4     4   enable interrupts

11 000 100  CNZ   addr     11/17 11/17 call on no zero
11 001 100  CZ    addr     11/17 11/17 call on zero
11 010 100  CNC   addr     11/17 11/17 call on no carry
11 011 100  CC    addr     11/17 11/17 call on carry
11 100 100  CPO   addr     11/17 11/17 call on parity odd
11 101 100  CPE   addr     11/17 11/17 call on parity even
11 110 100  CP    addr     11/17 11/17 call on positive
11 111 100  CM    addr     11/17 11/17 call on minus

11 000 110  ADI   imm        7     7   add immediate to A
11 001 110  ACI   imm        7     7   add immediate to A with carry
11 010 110  SUI   imm        7     7   subtract immediate from A
11 011 110  SBI   imm        7     7   subtract immediate from A with borrow
11 100 110  ANI   imm        7     7   and immediate with A
11 101 110  XRI   imm        7     7   exclusive or immediate with A
11 110 110  ORI   imm        7     7   or immedeate with A
11 111 110  CPI   imm        7     7   compare immediate with A

上の一覧には次の命令が欠けている
MVI  M,imm
MOV  M,r
*/

/*
メインルーチン
*/
void main(int argc, char* argv[])
{
	if(loadhex(Patbdatfile) != 0) {
		printf("Cannot initialize memory.\n");
		intsignal = 1;
	}
	//dumphex(sizeof mem, mem);
	lastchar = -1;
	
	if(argc >= 2) {
		fpLoad = fopen(argv[1], "r");
		if(fpLoad != NULL) {
			printf("Loading %s ...\n", argv[1]);
			initload = 1;
		}
		else {
			printf("Cannot open %s\n", argv[1]);
		}
	}
	
	signal(SIGINT, inthandl);

	run();
	
	if(fpLoad != NULL) {
		fclose(fpLoad);
	}
	//dumphex(sizeof mem, mem);
}


void hard_control() {
	int ch;
	char buf[1000];

	if(icerun == 1) {
		trace1(3, "ICE Running.");
	}
	
	printf("\n1:LoadRes, 2:SaveLog, 3:Ctrl-C, 4:LoadHex, 7:ICE, 8:Reset, 9:Shutdown >");
	ch = _getch();

	if(ch == 0x03) {
		printf("^C\n");
		return;
	}
	if(ch >= ' ') {
		printf("%c\n", ch);
	}
	else {
		printf("\n");
	}
	
	switch(ch) {
	case '1':
		/*
		1:LoadRes  応答ファイルを開く
		*/
		if(fpLoad != NULL) {
			//すでに開いていたらそれを閉じる。
			fclose(fpLoad);
			fpLoad = NULL;
		}
		printf("Load file>");
		if(fgets(buf, 1000, stdin) != NULL) {
			buf[strlen(buf)-1] = '\0';
			fpLoad = fopen(buf, "r");
			if(fpLoad == NULL) {
				printf("Cannot open %s for Read.\n", buf);
			}
			else {
				printf("Loading %s ...\n", buf);
			}
		}
		break;
		
	case '2':
		/*
		2:SaveLog  ログファイル操作
		*/
		if(fpSave != NULL) {
			//すでに開いていたら閉じるかどうか確認する。
			printf("Close current log file ?  1:Close, 0:Continue>");
			ch = _getch();
			printf("%c\n", ch >= ' ' ? ch : ' ');
			if(ch == '1') {
				printf("Closed.\n");
				fclose(fpSave);
				fpSave = NULL;
			}
			else {
				printf("Continue Logging.\n");
			}
		}
		else {
			//開いているログがなければ追加モードで開く。
			printf("Log file>");
			if(fgets(buf, sizeof buf - 1, stdin) != NULL) {
				buf[strlen(buf)-1] = '\0';
				fpSave = fopen(buf, "a");
				if(fpSave == NULL) {
					printf("Cannot open %s for Append.\n", buf);
				}
				else {
					printf("Appending %s ...\n", buf);
				}
			}
		}
		break;
	case '3':
		/*
		3:Ctrl-C  仮想キー入力にCtrl-Cを送信。
		*/
		lastchar = 0x03;
		break;
		
	case '4':
		/*
		4:LoadHex  Intel HEX ファイルを読み込んでリセットする。
		読み込み後、割込み禁止にしてから、PC=0にする。
		*/
		if(fpLoad != NULL) {
			fclose(fpLoad);
		}
		printf("Load HEX file>");
		if(fgets(buf, 1000, stdin) != NULL) {
			buf[strlen(buf)-1] = '\0';
			
			if(loadhex(buf) != 0) {
				printf("Cannot open %s for Read.\n", buf);
			}
			else {
				printf("Reset.\n");
				enint[2] = 0;
				enint[1] = 0;
				enint[0] = 0;
				pc = 0;
			}
		}
		break;

	case '7':
		icemode = 1;
		/*
		printf("ICE Mode  1:On, 2:Off");
		ch = _getch();
		printf("%c\n", ch >= ' ' ? ch : ' ');
		if(ch == '1') {
			icemode = 1;
		}
		else {
			icemode = 0;
		}
		*/
		break;
		
	case '8':
		printf("Reset.\n");
		enint[2] = 0;
		enint[1] = 0;
		enint[0] = 0;
		pc = 0;
		break;
	case '9':
		if(fpSave != NULL) {
			fclose(fpSave);
			fpSave = NULL;
		}
		exit(0);
		break;
	}
}


void ice_control(void)
{
	int ch;
	static char buf[1000];
	
	while(icemode == 1) {
		//printf("ICE  1:Reg, 2:Mem, 3:IO, 4:Run, 5:Step, 8:DumpMem, 9:Trace, 0:Off >");
		printf("ICE  4:Run, 5:Step, 8:DumpMem, 9:Trace, 0:Off >");
		ch = _getch();
		_putch(ch);
		_putch('\n');
		
		switch(ch) {
		case '1':
			trace1(3,"Current");
			printf("Reg  No.0:BC, 1:DE, 2:HL, 3:AF, 4:SP, 8:PC, 9:Quit >");
			continue;
			
		case '2':
			printf("Memory Addr >");
			continue;
		
		case '3':
			printf("I/O Control  1:Inport, 2:Outport, 9:Quit >");
			continue;
		
		case '4':
			icerun = 1;
			icemode = 0;
			goto loopout;
		
		case '5':
			icestep = 1;
			icerun = 0;
			icemode = 1;
			goto loopout;
			
		case '8':
			printf("Dump filename>");
			if(fgets(buf, sizeof buf - 1, stdin) != NULL) {
				buf[strlen(buf)-1] = '\0';
				
				if(dumphex(buf, sizeof mem, mem) != 0) {
					printf("Cannot open %s for Write.\n", buf);
				}
				else {
					printf("Memory Dumped.\n");
				}
			}
			break;
			
		case '9':
			if(icetrace != 0) {
				icetrace = 0;
			}
			else {
				icetrace = 1;
			}
			printf("Trace %s\n", icetrace == 0 ? "Off" : "On");
			continue;
			
		case '0':
			icerun = 0;
			icemode = 0;
			icetrace = 0;
			icestep = 0;
			goto loopout;
		}
	}

loopout:
	;
}



void inthandl(int sig) 
{
	signal(SIGINT , SIG_IGN);
	
	//printf("\n");
	//trace1(3, "");
	
	if(intsignal == 0) {
		intsignal = 1;
	}
	
	if(fpSave != NULL) {
		fflush(fpSave);
	}
	
	/*
	else if(intsignal == 1) {
		lastchar = 0x03;
	}
	*/
	
	return;
}

/*
unsigned long incr(unsigned long *reg)
reg 16ビットレジスタへのポインタ

16ビットレジスタをインクリメントする。オーバーフローしたら0に戻る。フラグは操作しない。

*/
unsigned long incr(unsigned long *reg)
{
	*reg = (*reg + 1) & 0xffff;
	return *reg;
}


/*
void dispreg(char *Munimonic)
Munimonic 行末に表示する文字列
レジスタの内容を表示する。
PCは現在の値を表示するのでフェッチサイクル後の表示はPC+1になっている。
*/
void dispreg(char *Munimonic)
{
	if(dispregen == 0) {
		return;
	}
	
	if(Munimonic == NULL) {
		Munimonic = "";
	}
	
	//SZ_H_P_C
	printf("F=%02X %c%c%c%c%c ", 
		reg[6], 
		reg[6] & 0x80 ? 'S' : '-',
		reg[6] & 0x40 ? 'Z' : '-',
		reg[6] & 0x10 ? 'H' : '-',
		reg[6] & 0x04 ? 'P' : '-',
		reg[6] & 0x01 ? 'C' : '-'
	);
	printf("I=%02X A=%02X BC=%02X%02X DE=%02X%02X HL=%02X%02X SP=%04X PC=%04X %s\n", inst, reg[7], reg[0], reg[1], reg[2], reg[3], reg[4], reg[5], sp, pc, Munimonic);
}

void trace1(int level, char* Msg)
{
	if(level == 0) {
		return;
	}
	if(level == 1 && dispregen == 0) {
		return;
	}
	
	//SZ_H_P_C
	printf("%c%c%c%c%c ", 
		reg[6] & 0x80 ? 'S' : '-',
		reg[6] & 0x40 ? 'Z' : '-',
		reg[6] & 0x10 ? 'H' : '-',
		reg[6] & 0x04 ? 'P' : '-',
		reg[6] & 0x01 ? 'C' : '-'
	);
	printf("A=%02X BC=%02X%02X DE=%02X%02X HL=%02X%02X SP=%04X PC=%04X %s\n", reg[7], reg[0], reg[1], reg[2], reg[3], reg[4], reg[5], sp, pc, Msg);
}

void run(void)
{
	unsigned char fetch, opr1, opr2, opr3;
	unsigned char f, tmp;
	unsigned int tmp16, tmp16_2;
	unsigned int cy, ac, zero, minus;
	
	pc = 0x0;
	
	while(1) {
		if(intsignal == 1) {
			hard_control();
			intsignal = 0;
			signal(SIGINT, inthandl);
		}
		
		if(icetrace == 1 || icestep == 1) {
			icestep = 0;
			trace1(3, "");
		}
		
		if(icemode == 1) {
			ice_control();
		}
		
		enint[0] = enint[1];
		enint[1] = enint[2];
		//printf("I:%d ", enint[0]);
		
		fetch = mem[pc];
		incr(&pc);
		
		inst = fetch;
		opr1 = (fetch >> 6) & 0x3;
		opr2 = (fetch >> 3) & 0x7;
		opr3 = (fetch >> 0) & 0x7;
		
		//printf("%02x  %d %d %d\n", fetch, opr1, opr2, opr3);
		
		//01 110 110  HLT              7     7   halt
		if(opr1 == 1 && opr2 == 6 && opr3 == 6) {
			dispreg("HLT");
			printf("Halt instruction.\n");
			break;
		}
		
		// 00 000 000  NOP              4     4   no operation
		if(opr1 == 0 && opr2 == 0 && opr3 == 0) {
			dispreg("NOP");
			continue;
		}
		
		//00 pp0 001  LXI   rp, imm16 10    10   load imediate register pair
		if(opr1 == 0 && (opr2 & 1) == 0 && opr3 == 1) {
			dispreg("LXI");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			
			fetch = mem[pc];
			incr(&pc);
			tmp16 = (fetch <> 8) & 0xff;
			}
			continue;
		}
		
		//00 ddd 100  INR   rm        5/10  5/10 increment
		if(opr1 == 0 && opr3 == 4) {
			dispreg("INR");
			ac = ( ((reg[opr2] & 0xf) + 1 ) >> 4) & 1;
			tmp16 = (reg[opr2] + 1) & 0xff;
			reg[opr2] = tmp16;
			// Z S P AC
			setflag(tmp16 & 0x80, tmp16 == 0, ac, isparity(tmp16), 0);	//Z S P CY AC
			continue;
		}
		
		//00 ddd 101  DCR   rm        5/10  5/10 decrement
		if(opr1 == 0 && opr3 == 5) {
			dispreg("DCR");
			ac = ( ((reg[opr2] & 0xf) - 1 ) >> 4 ) & 1;
			tmp16 = (reg[opr2] - 1) & 0xff;
			reg[opr2] = tmp16;
			setflag(tmp16 & 0x80, tmp16 == 0, ac, isparity(tmp16), 0);	//Z S P CY AC
			continue;
		}
		
		//00 ddd 110  MVI   rm, imm   7/10  7/10 move immediate
		if(opr1 == 0 && opr3 == 6) {
			if(opr2 == 6) {
				dispreg("MVI M");
				fetch = mem[pc];
				incr(&pc);
				mem[reg[4] << 8 | reg[5]] = fetch;
				continue;
			}
			else {
				dispreg("MVI");
				fetch = mem[pc];
				incr(&pc);
				reg[opr2] = fetch;
				continue;
			}
		}
		
		//01 ddd 110  MOV   r, M       7     7   move memory to register
		if(opr1 == 1 && opr3 == 6) {
			dispreg("MOV r,M");
			reg[opr2] = mem[reg[4] << 8 | reg[5]];	// HL
			continue;
		}
		
		//01 ddd sss  MOV   rm, r     5/7   4/7  move
		if(opr1 == 1 && opr3 != 6) {
			if(opr2 == 6) {
				dispreg("MOV M");
				mem[reg[4] << 8 | reg[5]] = reg[opr3];
				continue;
			}
			else {
				dispreg("MOV");
				reg[opr2] = reg[opr3];
				continue;
			}
		}
		
		//00 pp1 001  DAD   rp        10    11   add register pair to H & L
		if(opr1 == 0 && (opr2 & 1) == 1 && opr3 == 1) {
			dispreg("DAD");
			if((opr2 & 0x6) == 6) {
				tmp16 = sp;
			}
			else {
				tmp16 = (reg[opr2 & 0x6] << 8) | (reg[opr2 & 0x6 | 1]);
			}
			tmp16 = (( reg[4] <> 8 ) & 0xff;
			reg[5] = ( tmp16 >> 0 ) & 0xff;
			cy = tmp16 >> 16;
			setflag(-1, -1, -1, -1, cy);
			continue;
		}
		
		//00 111 010  LDA   addr      13    13   load A direct
		//00 101 010  LHLD  addr      16    16   store H & L direct
		//00 pp1 010  LDAX  rx         7     7   load A indirect
		if(opr1 == 0 && (opr2 & 1) == 1 && opr3 == 2) {
			if((opr2 & 0x6) == 6) {
				dispreg("LDA");
				fetch = mem[pc];
				incr(&pc);
				tmp16 = fetch;
				
				fetch = mem[pc];
				incr(&pc);
				tmp16 = fetch << 8 | tmp16;
				
				reg[7] = mem[tmp16];
				continue;
			}
			else if((opr2 & 0x6) == 0x4) {
				dispreg("LHLD");
				fetch = mem[pc];
				incr(&pc);
				tmp16 = fetch;
				fetch = mem[pc];
				incr(&pc);
				tmp16 = fetch << 8 | tmp16;
				reg[4] = mem[tmp16+1];	// H (Upper)
				reg[5] = mem[tmp16+0];	// L (Lower)
				continue;
			}
			else{
				dispreg("LDAX");
				tmp16 = ( reg[opr2 & 0x6] << 8 | reg[opr2 & 0x6 | 1]  );
				reg[7] = mem[tmp16];
				continue;
			}
		}
		
		//00 pp0 011  INX   rp         5     5   increment register pair
		if(opr1 == 0 && (opr2 & 1) == 0 && opr3 == 3) {
			dispreg("DCX");
			if((opr2 & 6) == 6) {
				sp = (sp + 1) & 0xffff;
			}
			else {
				tmp16 = ( reg[opr2 & 0x6] <> 8 ) & 0xff;
				reg[opr2 & 0x6 | 1] = ( tmp16 >> 0 ) & 0xff;
			}
			continue;
		}

		//00 pp1 011  DCX   rp         5     5   decrement register pair
		if(opr1 == 0 && (opr2 & 1) == 1 && opr3 == 3) {
			dispreg("DCX");
			if((opr2 & 6) == 6) {
				sp = (sp - 1) & 0xffff;
			}
			else {
				tmp16 = ( reg[opr2 & 0x6] <> 8 ) & 0xff;
				reg[opr2 & 0x6 | 1] = ( tmp16 >> 0 ) & 0xff;
			}
			continue;
		}

		//00 110 010  STA   addr      13    13   store A direct
		//00 100 010  SHLD  addr      16    16   store H & L direct
		//00 pp0 010  STAX  rx         7     7   store A indirect
		if(opr1 == 0 && (opr2 & 1) == 0 && opr3 == 2) {
			if((opr2 & 6) == 6) {
				dispreg("STA");
				fetch = mem[pc];
				incr(&pc);
				tmp16 = fetch;
				fetch = mem[pc];
				incr(&pc);
				tmp16 = (fetch << 8) | tmp16;
				mem[tmp16] = reg[7];
				continue;
			}
			else if((opr2 & 0x6) == 0x4) {
				dispreg("SHLD");
				fetch = mem[pc];
				incr(&pc);
				tmp16 = fetch;
				fetch = mem[pc];
				incr(&pc);
				tmp16 = (fetch << 8) | tmp16;
				mem[tmp16+1] = reg[4];	// H (Upper)
				mem[tmp16+0] = reg[5];	// L (Lower)
				continue;
			}
			else {
				dispreg("STAX");
				tmp16 = ( (reg[opr2 & 0x6] <> 8) & 0xff;
			pc = opr2 << 3;
			continue;
		}
		
		//11 pp0 001  POP   rr        10    10   pop register pair off stack
		if(opr1 == 3 && (opr2 & 1) == 0 && opr3 == 1) {
			dispreg("POP");
			if(opr2 == 6) {
				reg[6] = mem[sp];
				reg[7] = mem[sp+1];
			}
			else {
				reg[opr2 & 0x6 | 1] = mem[sp];
				reg[opr2 & 0x6]     = mem[sp+1];
			}
			sp = (sp + 2) & 0xffff;
		}
		
		//11 pp0 101  PUSH  rr        11    11   push register pair on stack
		if(opr1 == 3 && (opr2 & 1) == 0 && opr3 == 5) {
			dispreg("PUSH");
			sp = (sp - 2) & 0xffff;
			if(opr2 == 6) {
				mem[sp]   = reg[6];
				mem[sp+1] = reg[7];
			}
			else {
				mem[sp]   = reg[opr2 & 0x6 | 1];
				mem[sp+1] = reg[opr2 & 0x6];
			}
		}

		//11 001 101  CALL  addr      17    17   call unconditional
		if(opr1 == 3 && opr2 == 1 && opr3 == 5) {
			dispreg("CALL");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch <> 8 ) & 0xff;
			pc = tmp16;
			continue;
		}
		
		//11 001 001  RET             10    11   return
		if(opr1 == 3 && opr2 == 1 && opr3 == 1) {
			dispreg("RET");
			tmp16 = (mem[sp+1] << 8) | mem[sp];
			sp = (sp + 2) & 0xffff;
			pc = tmp16;
			continue;
		}
		
		//11 101 001  PCHL             5     5   H & L to program counter
		if(opr1 == 3 && opr2 == 5 && opr3 == 1) {
			dispreg("PCHL");
			tmp16 =  ( reg[4] << 8 ) | reg[5];
			pc = tmp16;
			continue;
		}
		
		//11 111 001  SPHL             5     4   H & L to stack pointer
		if(opr1 == 3 && opr2 == 7 && opr3 == 1) {
			dispreg("PCHL");
			tmp16 =  ( reg[4] << 8 ) | reg[5];
			sp = tmp16;
			continue;
		}

		//10 000 sss  ADD   rm        4/7   4/7  add
		//10 001 sss  ADC   rm        4/7   4/7  add with carry
		//10 010 sss  SUB   rm        4/7   4/7  subtract
		//10 011 sss  SBB   rm        4/7   4/7  subtract with borrow
		//10 100 sss  ANA   rm        4/7   4/7  and with A
		//10 101 sss  XRA   rm        4/7   4/7  exclusive or with A
		//10 110 sss  ORA   rm        4/7   4/7  or with A
		//10 111 sss  CMP   rm        4/7   4/7  compare with A
		//setflag(int sign, int zero, int halfcy, int parity, int carry);
		// F : SZ_H_P_C
		if(opr1 == 2) {
			if(opr3 == 6) {
				tmp16 = mem[(reg[4] <> 7) & 1;
			reg[7] = reg[7] << 1 | cy;
			setflag(-1, -1, -1, -1, cy);
			continue;
		}

		//00 001 111  RRC              4     4   rotate A right
		if(opr1 == 0 && opr2 == 1 && opr3 == 7) {
			dispreg("RRC");
			cy = reg[7] & 1;
			reg[7] = (cy <> 1);
			setflag(-1, -1, -1, -1, cy);
			continue;
		}

		//00 010 111  RAL              4     4   rotate A left through carry
		if(opr1 == 0 && opr2 == 2 && opr3 == 7) {
			dispreg("RAL");
			tmp16 = (reg[7] <> 8;
			reg[7] =  tmp16 & 0xff;
			setflag(-1, -1, -1, -1, cy);
			continue;
		}

		//00 011 111  RAR              4     4   rotate A right through carry
		if(opr1 == 0 && opr2 == 3 && opr3 == 7) {
			dispreg("RAR");
			tmp16 = ((reg[7] & 1) << 8) | (getcy() <> 1);
			cy = tmp16 >> 8;
			reg[7] = tmp16 & 0xff;
			setflag(-1, -1, -1, -1, cy);
			continue;
		}
		
		//00 100 111  DAA              4     4   decimal adjust A
		if(opr1 == 0 && opr2 == 4 && opr3 == 7) {
			dispreg("DAA");
			
			// init default data
			tmp16 = reg[7];
			ac = 0;
			cy = 0;	// c=getcy() ?
			
			// 1st Lower 4bits : A[3:0]>9 or AC==1 then A[7:0] 9 ) || getac()) {
				ac = (reg[7] & 0xf) + 6;
				tmp16 = reg[7] + 6;
			}
			
			// 2nd Upper 4bits : A[7:4]>9 or CY==1 then A[7:0]> 4) > 9 ) || getcy()) {
				tmp16 = ( ( (getcy() <> 4) + 6 ) <> 8;
			}
			
			reg[7] = tmp16 & 0xff;
			
			if(reg[7] == 0) {
				zero = 1;
			}
			else {
				zero = 0;
			}
			
			if(reg[7] & 0x80 == 0x80) {
				minus = 1;
			}
			else {
				minus = 0;
			}
			setflag(minus, zero, ac, isparity(reg[6]), cy);
			continue;
		}
		
		//00 101 111  CMA              4     4   complement A
		if(opr1 == 0 && opr2 == 5 && opr3 == 7) {
			dispreg("CMA");
			reg[7] = ~reg[7] & 0xff;
			continue;
		}

		//00 110 111  STC              4     4   set carry
		if(opr1 == 0 && opr2 == 6 && opr3 == 7) {
			dispreg("STC");
			reg[6] = reg[6] | CARRY;
		}
		
		//00 111 111  CMC              4     4   complement carry
		if(opr1 == 0 && opr2 == 7 && opr3 == 7) {
			dispreg("CMC");
			if(getcy() == 1) {
				reg[6] = reg[6] & ~CARRY;
			}
			else {
				reg[6] = reg[6] | CARRY;
			}
			continue;
		}
		
		//11 000 000  RNZ             5/11  5/11 return on no zero
		if(opr1 == 3 && opr2 == 0 && opr3 == 0) {
			dispreg("RNZ");
			if( (reg[6] & ZERO) != ZERO ) {
				tmp16 = (mem[sp+1] << 8) | mem[sp];
				sp = (sp + 2) & 0xffff;
				pc = tmp16;
			}
			continue;
		}

		//11 001 000  RZ              5/11  5/11 return on zero
		if(opr1 == 3 && opr2 == 1 && opr3 == 0) {
			dispreg("RZ");
			if( (reg[6] & ZERO) == ZERO ) {
				tmp16 = (mem[sp+1] << 8) | mem[sp];
				sp = (sp + 2) & 0xffff;
				pc = tmp16;
			}
			continue;
		}

		//11 010 000  RNC             5/11  5/11 return on no carry
		if(opr1 == 3 && opr2 == 2 && opr3 == 0) {
			dispreg("RNC");
			if( (reg[6] & CARRY) != CARRY ) {
				tmp16 = (mem[sp+1] << 8) | mem[sp];
				sp = (sp + 2) & 0xffff;
				pc = tmp16;
			}
			continue;
		}

		//11 011 000  RC              5/11  5/11 return on carry
		if(opr1 == 3 && opr2 == 3 && opr3 == 0) {
			dispreg("RC");
			if( (reg[6] & CARRY) == CARRY ) {
				tmp16 = (mem[sp+1] << 8) | mem[sp];
				sp = (sp + 2) & 0xffff;
				pc = tmp16;
			}
			continue;
		}

		//11 100 000  RPO             5/11  5/11 return on parity odd
		if(opr1 == 3 && opr2 == 4 && opr3 == 0) {
			dispreg("RPO");
			if( (reg[6] & PARITY) != PARITY ) {
				tmp16 = (mem[sp+1] << 8) | mem[sp];
				sp = (sp + 2) & 0xffff;
				pc = tmp16;
			}
			continue;
		}

		//11 101 000  RPE             5/11  5/11 return on parity even
		if(opr1 == 3 && opr2 == 5 && opr3 == 0) {
			dispreg("PRE");
			if( (reg[6] & PARITY) == PARITY ) {
				tmp16 = (mem[sp+1] << 8) | mem[sp];
				sp = (sp + 2) & 0xffff;
				pc = tmp16;
			}
			continue;
		}

		//11 110 000  RP              5/11  5/11 return on positive
		if(opr1 == 3 && opr2 == 6 && opr3 == 0) {
			dispreg("RP");
			if( (reg[6] & SIGN) != SIGN ) {
				tmp16 = (mem[sp+1] << 8) | mem[sp];
				sp = (sp + 2) & 0xffff;
				pc = tmp16;
			}
			continue;
		}

		//11 111 000  RM              5/11  5/11 return on minus
		if(opr1 == 3 && opr2 == 7 && opr3 == 0) {
			dispreg("RM");
			if( (reg[6] & SIGN) == SIGN ) {
				tmp16 = (mem[sp+1] << 8) | mem[sp];
				sp = (sp + 2) & 0xffff;
				pc = tmp16;
			}
			continue;
		}

		//11 000 010  JNZ   addr      10    10   jump on no zero
		if(opr1 == 3 && opr2 == 0 && opr3 == 2) {
			dispreg("JNZ");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch << 8 | tmp16;
			if( (reg[6] & ZERO) != ZERO ) {
				pc = tmp16;
			}
			continue;
		}
		
		//11 001 010  JZ    addr      10    10   jump on zero
		if(opr1 == 3 && opr2 == 1 && opr3 == 2) {
			dispreg("JZ");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch << 8 | tmp16;
			if( (reg[6] & ZERO) == ZERO ) {
				pc = tmp16;
			}
			continue;
		}

		//11 010 010  JNC   addr      10    10   jump on no carry
		if(opr1 == 3 && opr2 == 2 && opr3 == 2) {
			dispreg("JNC");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch << 8 | tmp16;
			if( (reg[6] & CARRY) != CARRY ) {
				pc = tmp16;
			}
			continue;
		}

		//11 011 010  JC    addr      10    10   jump on carry
		if(opr1 == 3 && opr2 == 3 && opr3 == 2) {
			dispreg("JC");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch << 8 | tmp16;
			if( (reg[6] & CARRY) == CARRY ) {
				pc = tmp16;
			}
			continue;
		}

		//11 100 010  JPO   addr      10    10   jump on parity odd
		if(opr1 == 3 && opr2 == 4 && opr3 == 2) {
			dispreg("JPO");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch << 8 | tmp16;
			if( (reg[6] & PARITY) != PARITY ) {
				pc = tmp16;
			}
			continue;
		}

		//11 101 010  JPE   addr      10    10   jump on parity even
		if(opr1 == 3 && opr2 == 5 && opr3 == 2) {
			dispreg("JPE");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch << 8 | tmp16;
			if( (reg[6] & PARITY) == PARITY ) {
				pc = tmp16;
			}
			continue;
		}

		//11 110 010  JP    addr      10    10   jump on positive
		if(opr1 == 3 && opr2 == 6 && opr3 == 2) {
			dispreg("JP");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch << 8 | tmp16;
			if( (reg[6] & SIGN) != SIGN ) {
				pc = tmp16;
			}
			continue;
		}

		//11 111 010  JM    addr      10    10   jump on minus
		if(opr1 == 3 && opr2 == 7 && opr3 == 2) {
			dispreg("JM");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch << 8 | tmp16;
			if( (reg[6] & SIGN) == SIGN ) {
				pc = tmp16;
			}
			continue;
		}
		
		//11 000 011  JMP   addr      10    10   jump unconditional
		if(opr1 == 3 && opr2 == 0 && opr3 == 3) {
			dispreg("JMP");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch << 8 | tmp16;
			pc = tmp16;
			continue;
		}
		


		//11 000 100  CNZ   addr     11/17 11/17 call on no zero
		if(opr1 == 3 && opr2 == 0 && opr3 == 4) {
			dispreg("CNZ");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch <> 8 ) & 0xff;
				pc = tmp16;
			}
			continue;
		}
		
		//11 001 100  CZ    addr     11/17 11/17 call on zero
		if(opr1 == 3 && opr2 == 1 && opr3 == 4) {
			dispreg("CZ");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch <> 8 ) & 0xff;
				pc = tmp16;
			}
			continue;
		}

		//11 010 100  CNC   addr     11/17 11/17 call on no carry
		if(opr1 == 3 && opr2 == 2 && opr3 == 4) {
			dispreg("CNC");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch <> 8 ) & 0xff;
				pc = tmp16;
			}
			continue;
		}

		//11 011 100  CC    addr     11/17 11/17 call on carry
		if(opr1 == 3 && opr2 == 3 && opr3 == 4) {
			dispreg("CC");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch <> 8 ) & 0xff;
				pc = tmp16;
			}
			continue;
		}

		//11 100 100  CPO   addr     11/17 11/17 call on parity odd
		if(opr1 == 3 && opr2 == 4 && opr3 == 4) {
			dispreg("CPO");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch <> 8 ) & 0xff;
				pc = tmp16;
			}
			continue;
		}

		//11 101 100  CPE   addr     11/17 11/17 call on parity even
		if(opr1 == 3 && opr2 == 5 && opr3 == 4) {
			dispreg("CPE");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch <> 8 ) & 0xff;
				pc = tmp16;
			}
			continue;
		}

		//11 110 100  CP    addr     11/17 11/17 call on positive
		if(opr1 == 3 && opr2 == 6 && opr3 == 4) {
			dispreg("CP");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch <> 8 ) & 0xff;
				pc = tmp16;
			}
			continue;
		}

		//11 111 100  CM    addr     11/17 11/17 call on minus
		if(opr1 == 3 && opr2 == 7 && opr3 == 4) {
			dispreg("CM");
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch;
			fetch = mem[pc];
			incr(&pc);
			tmp16 = fetch <> 8 ) & 0xff;
				pc = tmp16;
			}
			continue;
		}
		
		//11 100 011  XTHL            18    17   exchange top of stack, H & L
		if(opr1 == 3 && opr2 == 4 && opr3 == 3) {
			dispreg("XTHL");
			tmp16 = (mem[sp+1] <> 8) & 0xff;
			reg[5] = tmp16 & 0xff;
		}
		
		//11 101 011  XCHG             4     4   exchange D & E, H & L registers
		if(opr1 == 3 && opr2 == 5 && opr3 == 3) {
			dispreg("XCHG");
			tmp16 = (reg[2] <> 8) & 0xff;
			reg[5] = tmp16 & 0xff;
			continue;
		}
		
		//11 110 011  DI               4     4   disable interrupt
		if(opr1 == 3 && opr2 == 6 && opr3 == 3) {
			dispreg("DI");
			enint[2] = 0;
			enint[1] = 0;
			enint[0] = 0;
			continue;
		}
		
		//11 111 011  EI               4     4   enable interrupts
		if(opr1 == 3 && opr2 == 7 && opr3 == 3) {
			dispreg("EI");
			enint[2] = 1;
			continue;
		}
		
		//11 011 011  IN    port      10    10   input
		if(opr1 == 3 && opr2 == 3 && opr3 == 3) {
			dispreg("IN");
			fetch = mem[pc];
			incr(&pc);
			//printf("INPORT %x\n", fetch);
			reg[7] = hard_in(fetch);
			continue;
		}
		
		//11 010 011  OUT   port      10    10   output
		if(opr1 == 3 && opr2 == 2 && opr3 == 3) {
			dispreg("OUT");
			fetch = mem[pc];
			incr(&pc);
			//printf("OUTPORT %x, %x\n", fetch, reg[7]);
			hard_out(fetch, reg[7]);
			continue;
		}
		
	}
}


/*
int loadhex(char* Filename)
Filename ファイル名
戻り 成功したら0

Intel HEXファイルを読み込んでmem[]にストアする。
*/
int loadhex(char* Filename)
{
	char buf[100];
	char *ep;
	int num;
	int ret;
	FILE *fp;
	int n;
	int tmp;

	ret = 0;
	fp = fopen(Filename, "r");
	if(fp == NULL) {
		return 10;
	}
	
	while(ret == 0) {
		ep = fgets(buf, sizeof buf, fp);
		if(ep == NULL) {
			ret = 2;
			break;
		}
		
		if(strlen(buf) >= sizeof buf - 1 ) {
			printf("format error.\n");
			ret = 3;
			break;
		}
		
		if( *(buf + strlen(buf) -1 ) == '\n' ) {
			*(buf + strlen(buf) -1 ) = '\0';
		}
		//printf("read -> %s\n", buf);
		
		if(buf[0] == ':') {
			unsigned int len  = val2(&buf[1]); 
			unsigned int addr = val4(&buf[3]);
			unsigned int type = val2(&buf[7]);
			unsigned int chk  = val2(&buf[9+len*2]);
			//printf("addr=%04x,len=%02x,type=%d,sum=%02x\n", addr, len, type, chk);
			if(type == 0) {
				for(n=0; n<len; n++) {
					unsigned int data = val2(&buf[9+n*2]);
					mem[addr+n] = data;
					//printf("%02x ", data);
				}
				//printf("\n");
			}
			
			tmp = val2(&buf[1]) + val2(&buf[3]) + val2(&buf[5]);
			for(n=0; n<=len+1; n++) {
				tmp = ( tmp + val2(&buf[9+n*2]) ) & 0xff;
			}
			if( tmp != 0 && chk != 0 ) {
				printf("Checksum Error at %04x %x\n", addr, chk);
			}
		}
	}
	fclose(fp);
	ret = 0;
	
	return ret;
}


/*
int dumphex(char *Filename, int size, unsigned char* data)
size ダンプする長さ
data ダンプするメモリの先頭

メモリの内容をIntel HEXファイルで表示する。(チェックサムは現時点で未対応)
長さは16バイト単位で切り上げ
16バイトすべてが00Hの場合は表示しない。
*/
int dumphex(char *Filename, int size, unsigned char* data)
{
	int n, m;
	const chartbl[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
	unsigned char *pbuf;
	char buf[1000];
	FILE* fpOut;
	
	pbuf = data;
	
	if((fpOut = fopen(Filename, "w")) == NULL) {
		printf("Cannot open %s for Write. (dumphex)\n", Filename);
		return 10;
	}
	
	for(n=0; n> 8) & 0xf];	// size is divide by 16
		buf[4] = chartbl[ (n >>  4) & 0xf];
		buf[5] = chartbl[ (n >>  0) & 0xf];
		buf[6] = '0';	//chartbl[ (n >>  0) & 0xf];
		buf[7] = '0';
		buf[8] = '0';
		for(m=0; m>4 & 0xf];
			buf[9+m*2+1] = chartbl[ *(pbuf+m)>>0 & 0xf];
		}
		buf[9+m*2] = '\0';
		
		if(strcmp(&buf[9], "00000000000000000000000000000000") != 0) {
			fprintf(fpOut, "%s\n", buf);
		}
		
		pbuf = pbuf + 16;
	}
	
	fclose(fpOut);
	
	return 0;
}


/*
unsigned int val2(char *buf)
buf 文字列
戻り 変換した値

bufが指す2字の文字列を16進数の1バイトに変換する。
*/
unsigned int val2(char *buf)
{
	char charval[3] = "00";
	int intval = 0;
	
	charval[0] = *(buf+0);
	charval[1] = *(buf+1);
	sscanf(charval, "%x", &intval);

	return intval;
}


/*
unsigned int val4(char *buf)
buf 文字列
戻り 変換した値

bufが指す4字の文字列を16進数の1ワードに変換する。
*/
unsigned int val4(char *buf)
{
	char charval[5] = "0000";
	int intval = 0;
	
	charval[0] = *(buf+0);
	charval[1] = *(buf+1);
	charval[2] = *(buf+2);
	charval[3] = *(buf+3);
	sscanf(charval, "%x", &intval);

	return intval;
}

オペコード解析の分岐をテーブル化してないのは意図的。テーブル化すればコンパイラの最適化よりも確実に早くなると思う。

とりあえず、非プログラマだって8080の仕様書見て原理的エミュレータくらい組めるよって言いたかった。

Spectre、Meltdown対策を完全にしてみる。

3日前にようやくSandy Bridge世代以降のSpectre、Meltdown対策のマイクロコード更新が始まったのにあわせて、(放置していた)PCのファームウェア更新をしてみた。

自宅ではこれに該当するのはIvy Bridge世代のDQ77MK、DQ77KBのインテルマザーベースのデスクトップPCとBroadwell世代のHP製i5ノートPC。自分が使っていないECSのSandy BridgeマザーベースのデスクトップPCはBIOS更新が2013年で止まって放置プレイで対象外。

DQ77MKマザーは2018年3月29日にインテルがリリースしたBIOSアップデート [MKQ7710H 86A] 0073を適用する。インテルマザーの常なのかBIOSアップデートがいつもながらうまくいかない。起動時F7キー押下ではIntel MEファームの更新で失敗する。BIOSリカバリーモードで更新するのは分解してジャンパーをいじる必要があって面倒なのでやりたくなかったが、なぜか3回同じF7操作を試したら更新がかかった。更新後デバイスマネージャをよく見るとプライマリNICを認識しておらず、BIOS設定のファクトリーリセットを行ったら回復してきた。

DQ77KBマザーも2018年3月29日にリリースされているBIOSップデート [KBQ7710H 86A] 0060を適用するのだが、これがドはまり。

・起動時F7キー押下でIntel ME Firmware更新がエラーで進まず。
・分解してリカバリーBIOSモードでは更新できたが、セカンダリNIC、WLANアダプタ(Wi-Fi 7260AC)を認識せず使えなくなった。温度、ファン回転数も取得できなくなった。起動時にシャーシイントリュージョンエラーが必ず出る。
・BIOS設定のファクトリーリセットを行ったが変わらず認識しない。
・もう一度ファームバージョン0060でFlash再書き込みしても変わらず。
・更新前のファームバージョン0059に戻したのにNIC、センサーとも回復しない。シャーシイントリュージョンも変わらず。
・バックアップ電池抜き、MBEXリセットとかやっても変わらない。
・Intel MEのファームを上書きで初期化したが特に変わらず。
半日ハマったorz…

結局次の手順でようやく各種デバイスの回復とファーム更新が完了。
ファームバージョン0053(KB0053P.BIO)をBIOSリカバリーモードで書き込んでNIC、センサー、WLANアダプタの認識とChassis Intrusionエラーが出ないことを確認。
・ファームバージョン0060(KB0060.BIO、最新のもの)にBIOSリカバリーモードで更新。

HPノートはメーカサイトから一番簡単そうなWindows EXE版のツールをダウンロードして更新。更新は特に問題なし。起動直後とかSSDのアクセスが多い時の動作が明らかに遅くなったorz…

更新前後で効果があるか確認した。DQ77MKの画面だが、他も同じ。

CPUの脆弱性「Meltdown」「Spectre」に関する状態を確認する – マイナビニュース
https://news.mynavi.jp/article/win10tips-247/

更新前
melt1

更新後
melt2

これであと5年戦える。MSはWin10サポート外のはずのSandyに手を打ってるし、Intelもサポート期限切れ製品なのに対応してくれたっていうのはえらいと思う。でもノートPCは明らかに遅くなったわ。

スペクターとメルトダウンから Windows デバイスを保護する
https://support.microsoft.com/ja-jp/help/4073757/protect-your-windows-devices-against-spectre-meltdown

KB4100347: Intel microcode updates
https://support.microsoft.com/en-us/help/4100347/intel-microcode-updates-for-windows-10-version-1803-and-windows-server