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

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

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

Windows 8.1のDOS起動ディスクの中身の使い方。

Windows XP~8.1まであった機能、フロッピーフォーマット時の「MS-DOSの起動ディスクを作成する」オプション。Windows10で無くなったが、これでできるディスクの内容が日本人にとっては意味不明。英語が分からないとかじゃなくて、ファイルの内容が。

win81fd1

Windows MeのDOS部分が普通に起動できるのだが、
win81fd2

これらのファイルの使い道を知りたい。
win81fd3

ggrksると「IO.SYS、MSDOS.SYS、COMMAND.COM以外は要らないので消すべし。」みたいなのが出てきて疑問に答えてくれない。

実際にはこれらのファイルはフォントとキーボードレイアウトを各国仕様に変更するのに使う。フォーマット後のFDにできているCONFIG.SYSとAUTOEXEC.BATを適切に調整すれば自国で使ってる基本的な環境が整う状態になっている。もちろん国際化対応は日本も例外ではないが、2バイト文字フォントは入れられないのでキーボードだけの対応になる。具体的には次のようにCONFIG.SYS、AUTOEXEC.BATを修正する。

CONFIG.SYS

DEVICE=\DISPLAY.SYS CON=(EGA,437,6)

AUTOEXEC.BAT

MODE CON CP PREP=((437 850 852 860 863 865) \EGA.CPI)
MODE CON CP SEL=437
KEYB US,437,\KEYBOARD.SYS

これで表示に関してはmodeコマンドでmode con cp sel=850などコードページを437 850 852 860 863 865の中から切り替えて使える。キーボードはUS配列を指定しているので特に変わらず使えるが、KEYB HU,852,KEYBRD2.SYSでハンガリー用配列になるなど動的に切り替えができる。

win81fd4

外国語のこと知らんし っていう日本人向けおすすめ設定。これで日本語キーボードが使える。

CONFIG.SYS は0バイト(空)のまま。

AUTOEXEC.BAT

KEYB JP,437,KEYBOARD.SYS

AUTOEXEC.BATを作るだけ。フォーマット後にKEYB.COM、KEYBOARD.SYSはすでに入っているからグレーなサイトから持ってきたりする必要無し。KEYBOARD.SYSのJPはコードページ437、932の両方をサポートしているが表示側のJFONT.SYS、JDISP.SYSとフォントファイルが無いから、CP932はどうせ使えないので437を指定する。

CONデバイスのCP437を省略せずきちんと設定する場合はこんな感じ。これ以外のことはできないし、ここまで律儀に書く必要も無い。

CONFIG.SYS

DEVICE=\DISPLAY.SYS CON=(EGA,437,1)

AUTOEXEC.BAT

@ECHO OFF
MODE CON CP PREP=((437) \EGA.CPI)
MODE CON CP SEL=437
KEYB JP,437,\KEYBOARD.SYS

DOS起動ディスクの本来の使用用途は表示・キーボードの設定のためにCONFIG.SYSとAUTOEXEC.BATを作って、後はBIOS FLASHユーティリティをコピーしたらFDでPC起動してFLASHしてね、というのが正しい使い方なのだと思う。

16ビットWindowsプログラムをアセンブラしてみる。

アセンブラでWin16なWindowsプログラムを組んでみる。

リアルモード16ビットDOSのプログラムはいろいろ例が出てるのに、16ビットWindowsのプログラムは探してもほとんど無い。見つけてもINVOKEマクロで手抜きとか。ここではINVOKE無しで通常の呼出規約でAPIコールをする。ぱっと見はWin32に似ているが、結構違う。16ビットなのでレジスタの操作も16ビット単位。

これは前回のVS2017のmlとリンカではできなくてVisual C++ 6.0 SP5用のProcessor Packに入っているMASM 6.15を、リンカはVisual C++ 1.51に付いているものを使った。今のところMSDNで手に入る環境フルセットのMASM 6.11だけでもできると思う。

作成方法

ml msgbx16.asm /link libw.lib

msgbx16.asm

.model	small, pascal, nearstack
.386

MessageBox PROTO FAR PASCAL :WORD, :FAR PTR SBYTE, :FAR PTR SBYTE, :WORD


.code
sStart:
	sub	sp, 4
	
	push	word ptr 0
	push	ds
	push	offset strMsg
	push	ds
	push	offset strCapt
	push	word ptr 40h
	call	far ptr MessageBox

	mov	ax, 4c00h
	int	21h


.data
strCapt	db 'msgbx16', 0
strMsg	db 'Hello World !', 0


.stack
	db	100 dup (0)


	end	Start

動作はVMware上のWindwos2000Pro。

masmwin

VS2017のml.exe、ml64.exeを使ってみる。

アセンブラでWindowsのプログラムってどうやったらできるのかな? って思った。ずっと前にMASM32とか調べたら高機能マクロで簡単にできるよとか書いてあって、そんならCでやるわってなってから放置してた。

VS2017に付いてるml.exe(14.13.26132.0)の32ビット版でMessageBoxAを叩いて終わるだけのもの。これでも一応APIの呼び方は分かる。スタックに押し込んでAPIをコール。

msgbx.asm

.model	flat, stdcall

extern	MessageBoxA@16 : proc
extern	ExitProcess@4 : proc

public Start

.code
Start:
	push 40h
	push offset strCapt
	push offset strMsg
	push 0
	call MessageBoxA@16

	push eax
	call ExitProcess@4

.data
strCapt	db 'msgbx', 0
strMsg	db 'Hello World !', 0

end

アセンブルとリンクはml一行で完結。

ml msgbx.asm /link /subsystem:windows /Entry:Start kernel32.lib user32.lib

devenv.exe msgbx.exeでデバッグ。debugやsymdebと同じtやpやrコマンドが使える。

msgbx-devenv

64ビット版も作った。FASTCALL規約でAPIの引数がスタックではなくてレジスタを使う。

msgbx64.asm

extern	MessageBoxA : proc
extern	ExitProcess : proc

public	Start

.code
Start:
	sub rsp, 8h
	
	mov rcx, 0
	lea rdx, strMsg
	lea r8, strCapt
	mov r9, 40h
	call MessageBoxA

	mov ecx, 0
	call ExitProcess

.data
strCapt	db 'msgbx64', 0
strMsg	db 'Hello World !', 0

end
ml64 msgbx64.asm /link /subsystem:windows /entry:Start kernel32.lib user32.lib

Windows 10 EnterpriseをApril 2018 Update(1803)してみる。

ノートPCで使っているVL版(ボリュームライセンス版)のWindows 10 Enterprise、1703なので先日出た1803に更新してみる。

前回のFall Creators Update(1703)それ以前のCreators Updateと比べて、今回は超簡単だった。スタート→設定→更新プログラムのチェック。あとは完了まで若干の操作を除いて放置するだけ。約1.5時間。

Win10EntUpd1803

で終わったところ。前回見た「お使いのPCは監視され、…」の表示は無くなった。

Win10Ent1803-1

Win10Ent1803-2

今回もちょっと不具合。

・スキャナのユーティリティEPSON SCANが消えた。前回も、前々回も発生したのと同じ症状。EPSON SCANを再インストールして復活。読み取り設定などは消えずに残っていた。

・Synapticsのトラックパッドが使えなくなった。今回初めて。Win+X→デバイスマネージャ→マウスと他の…からSynaptic PS/2…を関連ファイルごと削除→再起動で自動的に再認識。設定→デバイス→マウス→その他のマウスオプションを開くと新しいツールのセットアップが始まった。

不具合はこれくらいだった。細かいところがいろいろ変わってる。表示切替時の効果が早くなった(アニメーションが完了するまでの時間が短くなった)気がするが気のせい?