x86アセンブリ言語に関するメモ
x86系マイクロプロセッサの持つ主なレジスタ
レジスタ名 | 呼び名 | 主な機能 |
---|---|---|
EAX | アキュムレータ | 算術演算の結果を格納 |
EBX | ベースレジスタ | メモリアドレスを格納 |
ECX | カウントレジスタ | ループ回数をカウント |
EDX | データレジスタ | データを格納 |
ESI | ソースインデックス | データ転送元のメモリアドレスを格納 |
EDI | ディスティネーションインデックス | データ転送先のメモリアドレスを格納 |
EBP | ベースポインタ | データの格納領域のメモリアドレスを格納 |
ESP | スタックポインタ | スタック領域のメモリアドレスを格納 |
主な命令
命令 | 使用例 | 意味 | 詳細 |
---|---|---|---|
MOV | MOV EAX,ECX | EAX = ECX | ECXの値をEAXに格納 |
MOVZX | MOVZX_EAX,ECX | EAX = ECX | MOVのサイズが違うレジスタにコピーするとき用いる版 |
ADD | ADD EAX,ECX | EAX += ECX | EAXにECXを加算 |
SUB | SUB EAX,ECX | EAX -= ECX | EAXからECXを減算 |
MUL | MUL EAX,ECX | EAX *= ECX | EAXにECXを乗算 |
DIV | DIV EAX,ECX | EAX =EAX / ECX | EAXをECXで割って,商をEAXに,余りをEDXに格納 |
EDX = EAX % ECX | |||
INC | INC EAX | EAX++ | EAXに1を加算 |
DEC | DEC EAX | EAX-- | EAXに1を減算 |
XOR | XOR EAX,ECX | EAX = EAX ^ ECX | EAXとECXの各ビットごとに排他的論理和を取り,結果をEAXに格納 |
LEA | LEA EAX,[ECX+4] | EAX = ECX + 4 | ECX+4(アドレス値)をEAXに格納 |
CMP | CMP EAX,ECX | if(EAX==ECX)_ZF=1 | EAXとECXの値を比較してフラグに反映 |
else ZF = 0 | EAXとECXが等しければZF=1,EAXとECXが等しくなければ、ZF=0 | ||
TEST | TEST EAX,EAX | if(EAX==0) ZF = 1 | EAXの値を0と比較してフラグに反映 |
else ZF = 0 | EAXが0と等しければZF=1,EAXが0でなければZF=0 | ||
JE(JZ) | JE 041001000 | if(ZF == 1) | ZFが1なら041001000にジャンプ |
GOTO 041001000 | |||
JNE(JNZ) | JNE 041001000 | if(ZF == 0) | ZFが0なら041001000にジャンプ |
GOTO 041001000 | |||
JMP | JMP 041001000 | GOTO 041001000 | 無条件で041001000にジャンプ |
CALL | CALL lstrcmpW | lstrcmpW() | lstrcmpw関数の呼び出し |
PUSH | PUSH 00000001 | スタックへ00000001を格納 | |
POP | POP EAX | スタックからEAXへ値を取得 | |
NOP | NOP | 何もしない | |
SAR | SAR EAX | EAXを右にシフトする,すなわち2で割る | |
RET | RET | 処理を呼び出し元に戻す |
豆知識
XOR EAX,EAX は MOV EAX,0 と同様の働きをする。
dword ptr は、指定されたメモリアドレスから4バイトのデータを読み出すことを示す。
例えば、
MOV DWORD PTR SS:[EBP-8],1
の場合ベースポインタのアドレス-8から4バイトまでのデータに整数値1を格納している。
Pythonで16進文字列をintに変換
16進文字列からint
- 基数を指定してintにパースすればいい。
>>> s = 'abcdef' >>> print int(s, 16) 11259375
- ちなみにインタラクティブシェルなら0xからはじめた16進文字列を入力すれば10進数にしたものを返してくれる。
>>> 0xdeadbeef 3735928559
intから16進文字列
- hex()を使う
>>> a = 111223 >>> print hex(a) 0x1b277
- 先頭に0xがつくのがいやならformatメソッドを使うとよい
>>> a = 111223 >>> print format(a, 'x') 1b277
- こうなふうに先頭2文字を削除するという手もある
>>> a = 111223 >>> print hex(a)[2:] 1b277
- フォーマット指定子(%記法)を使ってもいい
>>> a = 111223 >>> print '%x' % a 1b277
簡単なタイピングゲーム
はじめに
C言語で簡単なタイピングゲームを作ってみた。
Visual C++でビルドすることを前提としてる。
コード
/*typing_cui1.c*/ #include <time.h> #include <conio.h> #include <ctype.h> #include <stdio.h> #include <string.h> #define Qnumber 20 int main(){ int i, stage, miss = 0, alpsum = 0; char *japanese[Qnumber] = { "交わした約束忘れないよ", "目を閉じ確かめる", "押し寄せた闇", "振り払って進むよ", "いつになったらなくした未来を", "私ここでまた見ることできるの?", "溢れ出した不安の影を", "何度でも裂いて", "この世界歩んでこう", "とめどなく刻まれた", "時は今始まり告げ", "変わらない思いをのせ", "閉ざされた扉開けよう", "目覚めた心は走り出した", "未来を描くため", "難しい道で立ち止まっても", "空はきれいな青さで", "いつも待っててくれる", "だから怖くない", "もう何があっても挫けない" }; char *roman[Qnumber] = { "kawasitayakusokuwasurenaiyo", "mewotojitasikameru", "osiyosetayami", "huriharattesusumuyo", "ituninattaranakusitamiraiwo", "watasikokodematamirukotodekiruno", "ahuredasitahuannnokagewo", "nandodemosaite", "konosekaiayundekou", "tomedonakukizamareta", "tokihaimahajimarituge", "kawaranaiomoiwonose", "tozasaretatobiraakeyou", "mezametakokorohahasiridasita", "miraiwoegakutame", "muzukasiimitidetatidomattemo", "sorahakireinaaosade", "itumomattetekureru", "dakarakowakunai", "mounanigaattemokujikenai" }; double time; clock_t start, end; for (i = 0; i < Qnumber; i++) alpsum += strlen(roman[i]); printf("スペースキーで開始です。\n"); while (_getch() != ' ') ; start = clock(); for (stage = 0; stage < Qnumber; stage++){ printf("%s\n", japanese[stage]); fflush(stdout); int len = strlen(roman[stage]); for (i = 0; i < len; i++){ int ch; do{ ch = _getch(); if (isprint(ch)){ _putch(ch); if (ch != roman[stage][i]){ miss++; _putch('\b'); } } } while (ch != roman[stage][i]); } printf("\n"); } end = clock(); time = (end - start) / CLOCKS_PER_SEC; printf("\n%.2ftypes/sec\n%.1ftypes/min\nmiss types:%d\n", alpsum / time, alpsum / time * 60,miss); return 0; }
入力されるアルファベットも指定しているのでかなり手抜き。
_getch()や_putch()など標準Cでは定義されてない関数を使っているので例えばgccでコンパイルしようとするとエラーになる。
参考
- 作者: 柴田望洋
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2008/10/22
- メディア: 単行本
- 購入: 1人 クリック: 10回
- この商品を含むブログ (3件) を見る
C言語でchar型を返す関数。
はじめに
C言語で文字列が与えられたときそれを逆順したものを返す関数をなぜか作りたくなった。
ポインタを使う
文字列を返す関数を作るときはintやdoubleなどと同じように何も考えず
- 間違え
char hoge(char fuga){ //何らかの処理 return fuga; }
のようにしてはダメ。
C言語の文字列はchar型の配列であるため、その配列の先頭の要素のアドレスを指すポインタを返すようにする。
- 正解
char *hoge(char *fuga) { //何らかの処理 return fuga; }
サンプルコード
ポインタについての理解を助けるためのサンプルコードを添付した。
単に配列変数を指定するとその配列の先頭のアドレスを示すことが確認できる。
scanf("%s", str); などのように int や double と違い変数の先頭に &
をつけなくて良いのはそのため。
冗長になるだけであるが scanf("%s", &str[0]);
のように書くこともできる。
実装例1
配列のi番目と(文字列の長さ-1-i)番目の要素を(文字列の長さ÷2)回入れ替えてる。
char *reverse(char *s) { int i; char tmp; for (i = 0; i< strlen(s)/2; i++) { tmp = s[i]; s[i] = s[strlen(s)-1-i]; s[strlen(s)-1-i] = tmp; } return s; }
実装例2
文字列の先頭を指すポインタと末尾を指すポインタを使って文字を入れ替えている。
使用例
例えばこの問題の仕様に合わせるのであれば、
#include <stdio.h> #include <string.h> char *reverse(char *s) { int i; char tmp; for (i = 0; i < strlen(s)/2; i++) { tmp = s[i]; s[i] = s[strlen(s)-1-i]; s[strlen(s)-1-i] = tmp; } return s; } int main() { char str[21]; fgets(str, sizeof(str), stdin); printf("%s\n", reverse(str)); return 0; }
こんな風に使える。
終わりに
ポインタを全然理解していなかったため、こんな簡単な処理をするだけの関数を書くのにかなり時間がかかってしまった。