8086(16bit x86)の機械語に触れる導入として、Haskellによる逆アセンブラ開発の取っ掛かりを説明します。ここから機械語の勉強を始めることを想定していますので、機械語に関する知識は特に前提とはしていません。
Haskellは初歩的な機能のみ使います。以下の内容を理解していれば十分です。
練習の解答例は別記事に掲載します。
この記事のコードをまとめたリポジトリです。
この記事は勉強会のテキストとして使用していました。
この記事には姉妹編があります。
- 8086による機械語入門 2015.11.09
2進数
文字列で指定した2進数を数値に変換する関数です。HUnitのテストも書きます。
※ 使用例をメモする目的で、使用している関数のテストも書いています。
import Test.HUnit
import System.IO
binToInt '0' = 0
binToInt '1' = 1
binStrToInt bin = f (reverse bin)
where
f "" = 0
f (x:xs) = (binToInt x) + 2 * (f xs)
tests = TestList
[ "reverse" ~: reverse "11001" ~?= "10011"
, "binStrToInt 1" ~: binStrToInt "101" ~?= 5
, "binStrToInt 2" ~: binStrToInt "11001" ~?= 25
]
main = do
runTestText (putTextToHandle stderr False) tests
Cases: 3 Tried: 3 Errors: 0 Failures: 0
以降はこのコードに付け足しながら進みます。
⇒ リビジョン 0
練習
【問1】数値を2進数の文字列に変換する関数bin
をテストファーストで作成してください。
⇒ 解答例
16進数
練習
【問2】16進数の文字列を数値に変換する関数hexStrToInt
をテストファーストで作成してください。
ヒント: Data.Char.digitToInt
(リファレンス)
【問3】数値を16進数の文字列に変換する関数hex
をテストファーストで作成してください。
⇒ 解答例
桁揃え
16進数の頭に0を付加して桁を揃えます。指定した桁から溢れる場合は切り揃えます。
テストファーストを意識して進めます。テストコードの追加分と実装を分けて掲載します。
, "replicate" ~: replicate 5 'a' ~?= "aaaaa"
, "hexn 1" ~: hexn 2 1 ~?= "01"
, "hexn 2" ~: hexn 2 255 ~?= "ff"
, "hexn 3" ~: hexn 8 65535 ~?= "0000ffff"
, "hexn 4" ~: hexn 2 256 ~?= "00"
hexn n x
| r < 0 = drop (-r) x'
| otherwise = replicate r '0' ++ x'
where
x' = hex x
r = n - length x'
replicate
はリストを指定した回数だけ繰り返します。
⇒ リビジョン 4
メモリをリストで表現
メモリをシミュレートするためバイト区切りのリストを作成します。
16進数文字列→リスト
, "hexStrToList 1" ~: hexStrToList "123456" ~?= [0x12, 0x34, 0x56]
, "hexStrToList 2" ~: hexStrToList "010203" ~?= [1, 2, 3]
hexStrToList "" = []
hexStrToList (h:l:xs) = hexStrToInt [h, l] : hexStrToList xs
⇒ リビジョン 5
リスト→16進数文字列
, "listToHexStr 1" ~: listToHexStr [0x12, 0x34, 0x56] ~?= "123456"
, "listToHexStr 2" ~: listToHexStr [1, 2, 3] ~?= "010203"
listToHexStr [] = ""
listToHexStr (x:xs) = hexn 2 x ++ listToHexStr xs
⇒ リビジョン 6
バイトオーダー
メモリはバイトごとに区切られています。1バイトに収まりきらない数値は分割して格納します。
バイトごと区切って並べる方法にいくつか種類があります。これをバイトオーダーと呼びます。
分割した際に逆順に並べ替える方式をリトルエンディアンと呼びます。並べ替えない方式はビッグエンディアンです。
- リトルエンディアン:
0x12345678
→[0x78, 0x56, 0x34, 0x12]
- ビッグエンディアン:
0x12345678
→[0x12, 0x34, 0x56, 0x78]
【注意】バイトオーダーを考えるのは分割後です。分割前のバイトオーダーは考えません。
人間の目にはビッグエンディアンの方が直感的ですが、現在主流なのはリトルエンディアンです。今回対象とする8086はリトルエンディアンのみを使います。
数値→リトルエンディアン
バイト数を指定してリトルエンディアンを作成します。
, "toLE 1" ~: toLE 2 1 ~?= [1, 0]
, "toLE 2" ~: toLE 2 0x10000 ~?= [0, 0]
, "toLE 3" ~: toLE 4 0x12345678 ~?= [0x78, 0x56, 0x34, 0x12]
toLE 0 _ = []
toLE n x = x `mod` 0x100 : toLE (n - 1) (x `div` 0x100)
⇒ リビジョン 7
リトルエンディアン→数値
バイト数を指定して数値を読み取ります。
, "fromLE 1" ~: fromLE 2 [0, 1] ~?= 0x100
, "fromLE 2" ~: fromLE 2 [0x78, 0x56, 0x34, 0x12] ~?= 0x5678
, "fromLE 3" ~: fromLE 4 [0x78, 0x56, 0x34, 0x12] ~?= 0x12345678
fromLE 0 _ = 0
fromLE n (x:xs) = x + 0x100 * fromLE (n - 1) xs
以上で逆アセンブラ開発の準備は完了です。
⇒ リビジョン 8
練習
【問4】数値⇔ビッグエンディアンの相互変換を実装してください。
ヒント: べき乗(累乗)の演算子は^
です。例: 2^3
(2の3乗)
⇒ 解答例
ビット演算
2進数を対象にしたビット演算という操作があります。
Haskellでは関数名が長くてやや煩雑なため、導入はPythonで行います。簡単な式しか扱わないため、Pythonの知識は前提としません。
HaskellではPythonとは演算子が異なるため、対応を示します。
※ import Data.Bits
が必要です。
操作 | Python | Haskell |
---|---|---|
左シフト | << |
shiftL |
右シフト | >> |
shiftR |
論理積 | & |
.&. |
論理和 | | |
.|. |
排他的論理和 | ^ |
xor |
反転 | ~ |
complement |
※ Pythonの演算子はC言語やJavaとも共通です。
練習
【問5】今まで実装した関数をビット演算で書き換えてください。
⇒ 解答例
逆アセンブラ
いよいよ逆アセンブラ作りに入ります。
テストが長くなったので、今までのテストは分離します。
testHex = TestList
[ "reverse" ~: reverse "11001" ~?= "10011"
(略)
]
データシートを参照しながら進めます。
mov ax, imm16
簡単な命令をndisasmで逆アセンブルします。
B83412 mov ax,0x1234
B83412
を機械語、mov
をニーモニック、ax
や0x1234
をオペランドと呼びます。
機械語をリストで渡して逆アセンブルします。
disasm (x:xs)
| x == 0xb8 =
"mov ax,0x" ++ hex (fromLE 2 xs)
testDisAsm = TestList
[ "b8 1" ~: disasm [0xb8, 0, 0] ~?= "mov ax,0x0"
, "b8 2" ~: disasm [0xb8, 0x34, 0x12] ~?= "mov ax,0x1234"
]
main = do
runTestText (putTextToHandle stderr False)
(TestList [testHex, testDisAsm])
⇒ リビジョン 10
文字列渡し
バイナリを16進数文字列で渡せるようにします。
, "b8 3" ~: disasm' "b80000" ~?= "mov ax,0x0"
, "b8 4" ~: disasm' "b83412" ~?= "mov ax,0x1234"
disasm' hex = disasm $ hexStrToList hex
⇒ リビジョン 11
mov r16, imm16
レジスタ番号で処理するように拡張します。
, "b8-bf 0" ~: disasm' "b90100" ~?= "mov cx,0x1"
, "b8-bf 1" ~: disasm' "ba1000" ~?= "mov dx,0x10"
, "b8-bf 2" ~: disasm' "bb0001" ~?= "mov bx,0x100"
, "b8-bf 3" ~: disasm' "bc0010" ~?= "mov sp,0x1000"
, "b8-bf 4" ~: disasm' "bdff00" ~?= "mov bp,0xff"
, "b8-bf 5" ~: disasm' "be00ff" ~?= "mov si,0xff00"
, "b8-bf 6" ~: disasm' "bffeca" ~?= "mov di,0xcafe"
reg16 = ["ax", "cx", "dx", "bx", "sp", "bp", "si", "di"]
disasm (x:xs)
| 0xb8 <= x && x <= 0xbf =
"mov " ++ reg16 !! (x - 0xb8) ++ ",0x" ++ hex (fromLE 2 xs)
⇒ リビジョン 12
mov r8, imm8
8bitレジスタにも対応します。
, "b0-b7 1" ~: disasm' "b000" ~?= "mov al,0x0"
, "b0-b7 2" ~: disasm' "b101" ~?= "mov cl,0x1"
, "b0-b7 3" ~: disasm' "b210" ~?= "mov dl,0x10"
, "b0-b7 4" ~: disasm' "b311" ~?= "mov bl,0x11"
, "b0-b7 5" ~: disasm' "b412" ~?= "mov ah,0x12"
, "b0-b7 6" ~: disasm' "b5ff" ~?= "mov ch,0xff"
, "b0-b7 7" ~: disasm' "b6ee" ~?= "mov dh,0xee"
, "b0-b7 8" ~: disasm' "b7ca" ~?= "mov bh,0xca"
reg8 = ["al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"]
disasm (x:xs)
| 0xb0 <= x && x <= 0xb7 =
"mov " ++ reg8 !! (x - 0xb0) ++ ",0x" ++ hex (xs !! 0)
⇒ リビジョン 13
8bitと16bitの統合
wフラグを見て切り替えます。
regs = [reg8, reg16]
disasm (x:xs)
-- DATA TRANSFER
-- MOV = Move:
-- Immediate to Register [1011wreg][data][data if w=1]
| 0xb0 <= x && x <= 0xbf =
"mov " ++ reg ++ "," ++ imm
where
w = (x `shiftR` 3) .&. 1
reg = regs !! w !! (x .&. 7)
imm = "0x" ++ hex (fromLE (w + 1) xs)
⇒ リビジョン 14
ビットパターン
データシートには機械語が2進数で表記されていますが、ここまでの実装では16進数に変換して範囲チェックしていました。2進数のままパターンマッチできるように修正します。
ビット分解
1バイトを8ビットに分解する関数を実装します。
, "getBits" ~: getBits 0xbd ~?= (1,0,1,1,1,1,0,1)
getBits x = (b 7, b 6, b 5, b 4, b 3, b 2, b 1, b 0)
where
b n = (x `shiftR` n) .&. 1
このままではエラーになります。
No instance for (Bits t0) arising from a use of `getBits'
The type variable `t0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Bits Int -- Defined in `Data.Bits'
instance Bits Integer -- Defined in `Data.Bits'
instance Bits ghc-prim:GHC.Types.Word -- Defined in `Data.Bits'
(略)
テストで指定した0xbd
がInt
, Integer
, ghc-prim:GHC.Types.Word
のどの型か曖昧だというエラーメッセージです。このような場合、関数に型注釈を追加します。
名前 :: 引数の型 -> 戻り値の型
getBits :: Int -> (Int,Int,Int,Int,Int,Int,Int,Int)
これでテストが通ります。
※ 今までは型推論に任せて型注釈は省略していましたが、型注釈を付けるのが一般的です。
⇒ リビジョン 15
レジスタを再構成
レジスタは3ビットで表されますが、getBits
によってビットごとにばらばらになってしまうため、再構成する関数を実装します。
, "getReg" ~: getReg 1 0 1 ~?= 5
getReg :: Int -> Int -> Int -> Int
getReg r e g =
(r `shiftL` 2) .|. (e `shiftL` 1) .|. g
これもエラー回避のため型注釈が必要です。
引数が複数ある場合は、このように引数を1つずつ->
で区切って書きます。最後が戻り値の型です。
名前 :: 引数の型 -> 引数の型 -> 引数の型 -> 戻り値の型
※ このような表記になるのはカリー化が関係していますが、詳細は省略します。
⇒ リビジョン 16
ビットでパターンマッチ
disasm
でビット分解してパターンマッチ用にdisasmB
を追加します。
disasm (x:xs) = disasmB (getBits x) xs
-- DATA TRANSFER
-- MOV = Move:
-- Immediate to Register [1011wreg][data][data if w=1]
disasmB (1,0,1,1,w,r,e,g) xs =
"mov " ++ reg ++ "," ++ imm
where
reg = regs !! w !! getReg r e g
imm = "0x" ++ hex (fromLE (w + 1) xs)
データシートに近い表記で実装することを意図しています。
⇒ リビジョン 17
mov(続き)
movを進めていきます。
Register/Memory to/from Register [100010dw][mod reg r/m]
このパターンに合わせてデータを作ります。最初の1バイトを列挙します。
-
10001000 00000000
→88 00
-
10001001 00000000
→89 00
-
10001010 00000000
→8A 00
-
10001011 00000000
→8B 00
バイナリエディタで16進数を打ち込みます。この作業をハンドアセンブルと呼びます。
機械語をアセンブリ言語に変換する処理を逆アセンブルと呼びます。
- アセンブル: アセンブリ言語 → 機械語
- 逆アセンブル: 機械語 → アセンブリ言語
ndisasmで逆アセンブルします。
00000000 8800 mov [bx+si],al
00000002 8900 mov [bx+si],ax
00000004 8A00 mov al,[bx+si]
00000006 8B00 mov ax,[bx+si]
d
がオペランドの順番、w
がレジスタサイズを表します。modとr/mで1つのオペランドを表します。(以後ModR/M)
, "88-8b mod=00,r/m=000 1" ~: disasm' "8800" ~?= "mov [bx+si],al"
, "88-8b mod=00,r/m=000 2" ~: disasm' "8900" ~?= "mov [bx+si],ax"
, "88-8b mod=00,r/m=000 3" ~: disasm' "8A00" ~?= "mov al,[bx+si]"
, "88-8b mod=00,r/m=000 4" ~: disasm' "8B00" ~?= "mov ax,[bx+si]"
ModR/Mを処理する関数を実装してオペランドとregをタプルで返します。まず[bx+si]
のみを実装します。
modrm (x:_) = (f mode rm, reg)
where
mode = x `shiftR` 6
reg = (x `shiftR` 3) .&. 7
rm = x .&. 7
f 0 0 = "[bx+si]"
これを使って逆アセンブラを実装します。
-- Register/Memory to/from Register [100010dw][mod reg r/m]
disasmB (1,0,0,0,1,0,d,w) xs
| d == 0 = "mov " ++ rm ++ "," ++ reg
| otherwise = "mov " ++ reg ++ "," ++ rm
where
(rm, r) = modrm xs
reg = regs !! w !! r
⇒ リビジョン 18
r/m
r/mを増やして変化を確認します。
00000000 8901 mov [bx+di],ax
00000002 8902 mov [bp+si],ax
00000004 8903 mov [bp+di],ax
00000006 8904 mov [si],ax
00000008 8905 mov [di],ax
0000000A 89068907 mov [0x789],ax
8906
と8907
がくっ付いています。
即値によるアドレス指定
8906
は mod = 00, r/m = 110 です。そのパターンを調べます。
00000000 88063412 mov [0x1234],al
00000004 89063412 mov [0x1234],ax
00000008 8A063412 mov al,[0x1234]
0000000C 8B063412 mov ax,[0x1234]
modrm
を拡張します。
, "88-8b mod=00,r/m=110 1" ~: disasm' "88063412" ~?= "mov [0x1234],al"
, "88-8b mod=00,r/m=110 2" ~: disasm' "89063412" ~?= "mov [0x1234],ax"
, "88-8b mod=00,r/m=110 3" ~: disasm' "8A063412" ~?= "mov al,[0x1234]"
, "88-8b mod=00,r/m=110 4" ~: disasm' "8B063412" ~?= "mov ax,[0x1234]"
modrm (x:xs) = (f mode rm, reg)
where
mode = x `shiftR` 6
reg = (x `shiftR` 3) .&. 7
rm = x .&. 7
f 0 0 = "[bx+si]"
f 0 6 = "[0x" ++ hex (fromLE 2 xs) ++ "]"
⇒ リビジョン 19
レジスタによるアドレス指定
すべての組み合わせに対応させるため、仕様書からアドレス指定の組み合わせを調べて、アセンブリ言語でテストを書きます。
mov [bx+si],ax
mov [bx+di],cx
mov [bp+si],dx
mov [bp+di],bx
mov [si],sp
mov [di],bp
mov [bp],si
mov [bx],di
nasmでアセンブルしたものをndisasmで逆アセンブルします。
00000000 8900 mov [bx+si],ax
00000002 8909 mov [bx+di],cx
00000004 8912 mov [bp+si],dx
00000006 891B mov [bp+di],bx
00000008 8924 mov [si],sp
0000000A 892D mov [di],bp
0000000C 897600 mov [bp+0x0],si
0000000F 893F mov [bx],di
[bp]
は機械語のパターンが異なるため除外します。(アドレス指定に割り当てられているため)
アドレスの組み合わせを定義してmodrm
を拡張します。
, "88-8b mod=00 1" ~: disasm' "8900" ~?= "mov [bx+si],ax"
, "88-8b mod=00 2" ~: disasm' "8909" ~?= "mov [bx+di],cx"
, "88-8b mod=00 3" ~: disasm' "8912" ~?= "mov [bp+si],dx"
, "88-8b mod=00 4" ~: disasm' "891b" ~?= "mov [bp+di],bx"
, "88-8b mod=00 5" ~: disasm' "8924" ~?= "mov [si],sp"
, "88-8b mod=00 6" ~: disasm' "892d" ~?= "mov [di],bp"
, "88-8b mod=00 7" ~: disasm' "893f" ~?= "mov [bx],di"
regad = ["bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"]
modrm (x:xs) = (f mode rm, reg)
where
mode = x `shiftR` 6
reg = (x `shiftR` 3) .&. 7
rm = x .&. 7
f 0 6 = "[0x" ++ hex (fromLE 2 xs) ++ "]"
f 0 rm = "[" ++ regad !! rm ++ "]"
⇒ リビジョン 20
ディスプレースメント
アドレスに対する差分をディスプレースメントと呼びます。[bp]
は[bp+0]
で表現します。
00000000 894001 mov [bx+si+0x1],ax
00000003 8949FF mov [bx+di-0x1],cx
00000006 895202 mov [bp+si+0x2],dx
00000009 895BFE mov [bp+di-0x2],bx
0000000C 896464 mov [si+0x64],sp
0000000F 896D9C mov [di-0x64],bp
00000012 897600 mov [bp+0x0],si
00000015 897601 mov [bp+0x1],si
00000018 897F01 mov [bx+0x1],di
ディスプレースメントは符号付きです。符号の考え方は次の記事を参照してください。
符号付きで文字列化する関数を用意します。
, "disp8 1" ~: disp8 0 ~?= "+0x0"
, "disp8 2" ~: disp8 0x7f ~?= "+0x7f"
, "disp8 3" ~: disp8 0x80 ~?= "-0x80"
, "disp8 4" ~: disp8 0xff ~?= "-0x1"
disp8 x
| x < 0x80 = "+0x" ++ hex x
| otherwise = "-0x" ++ hex (0x100 - x)
⇒ リビジョン 21
modrm
を拡張します。
, "88-8b mod=01 1" ~: disasm' "894001" ~?= "mov [bx+si+0x1],ax"
, "88-8b mod=01 2" ~: disasm' "8949FF" ~?= "mov [bx+di-0x1],cx"
, "88-8b mod=01 3" ~: disasm' "895202" ~?= "mov [bp+si+0x2],dx"
, "88-8b mod=01 4" ~: disasm' "895BFE" ~?= "mov [bp+di-0x2],bx"
, "88-8b mod=01 5" ~: disasm' "896464" ~?= "mov [si+0x64],sp"
, "88-8b mod=01 6" ~: disasm' "896D9C" ~?= "mov [di-0x64],bp"
, "88-8b mod=01 7" ~: disasm' "897600" ~?= "mov [bp+0x0],si"
, "88-8b mod=01 8" ~: disasm' "897601" ~?= "mov [bp+0x1],si"
, "88-8b mod=01 9" ~: disasm' "897F01" ~?= "mov [bx+0x1],di"
modrm (x:xs) = (f mode rm, reg)
where
mode = x `shiftR` 6
reg = (x `shiftR` 3) .&. 7
rm = x .&. 7
f 0 6 = "[0x" ++ hex (fromLE 2 xs) ++ "]"
f 0 rm = "[" ++ regad !! rm ++ "]"
f 1 rm = "[" ++ regad !! rm ++ disp ++ "]"
where
disp = disp8 (xs !! 0)
⇒ リビジョン 22
ファイルの分割
1ファイルが長くなって来たので分割します。
Leksahでの設定方法を説明します。Leksahについては以下を参照してください。
まずMain.hsの先頭にモジュール宣言を書いておきます。1ファイルだけのときは不要でしたが、ファイルを分割するときには必要となります。モジュール名はファイル名の拡張子を除いた部分と同じにします。
module Main where
Hex
- File → New Module
- Hex と入力 → [OK]
新規作成されたファイルの中身をすべて消します。
※ 中身を理解しないまま流用することによる問題を回避するための措置です。
ファイルの先頭にモジュール名を書いて、コードを移します。
module Hex where
Mainから参照します。
import Hex
⇒ リビジョン 23
DisAsm
逆アセンブラも同様に分割します。
- File → New Module
- DisAsm と入力 → [OK]
新規作成されたファイルの中身をすべて消します。
ファイルの先頭にモジュール名を書いて、コードを移します。
module DisAsm where
Mainから参照します。
import DisAsm
逆アセンブラのテストはMainに残したままにした方が、テストと実装を行ったり来たりするのに都合が良いでしょう。
⇒ リビジョン 24
練習
【問6】以下の手順でmodrm
の実装を完成させてください。
- mod=10,11の機械語をバイナリエディタで作る。
- ndisasmで逆アセンブルしてアセンブリ言語を確認する。
- テストケースを作る。
- 逆アセンブラを実装する。
⇒ 解答例
【問7】mov命令の2番目Immediate to Register/Memory
を実装してください。
⇒ 解答例
【問8】mov命令の残りを実装してください。
⇒ 解答例
【問9】disasm
が命令の長さも返すように修正してください。想定される仕様をテストの修正として示します。仕様変更がdisasm'
にまで及ぶとテストの修正が大変なため、与えられたバイナリの長さと比較する修正を提示します。
[ "b8 1" ~: disasm [0xb8, 0, 0] ~?= (3, "mov ax,0x0")
, "b8 2" ~: disasm [0xb8, 0x34, 0x12] ~?= (3, "mov ax,0x1234")
disasm' hex
| length bin == len = snd asm
| otherwise = "length? " ++ show len
where
bin = hexStrToList hex
asm = disasm bin
len = fst asm
-
show
は引数を文字列に変換する関数です。
⇒ 解答例
【問10】複数の命令を含んだバイナリを渡すと逆アセンブル結果をリストで返す関数を実装してください。具体的には以下のテストを通してください。
, "disasms" ~: disasms [0xc6, 0x47, 1, 1, 0xb0, 1]
~?= [(4, "mov byte [bx+0x1],0x1"), (2, "mov al,0x1")]
, "disasms'" ~: disasms' "C6470101B001"
~?= ["mov byte [bx+0x1],0x1", "mov al,0x1"]
⇒ 解答例
【問11】逆アセンブル結果にアドレスやダンプを含めてndisasmと同じ出力にしてください。具体的には以下のテストを通してください。
, "ndisasm" ~: ndisasm 0 [0xc6, 0x47, 1, 1]
~?= (4, "00000000 C6470101 mov byte [bx+0x1],0x1")
, "ndisasms" ~: ndisasms 0 [0xc6, 0x47, 1, 1, 0xb0, 1]
~?= [ "00000000 C6470101 mov byte [bx+0x1],0x1"
, "00000004 B001 mov al,0x1"
]
- 第一引数は開始アドレスです。
⇒ 解答例
ファイル読み込み
main
でコマンドライン引数を調べて、何も指定されていなければテスト、ファイルが指定されていれば読み込んで逆アセンブルするコードを示します。
依存パッケージにbytestringを追加してください。
import System.Environment
import qualified Data.ByteString
qualified
を指定すると使用する際に名前空間まで付ける必要があります(例: Data.ByteString.readFile
)。元々あった関数と同名の関数(例: readFile
)が衝突するのを回避しています。
main = do
args <- getArgs
if args == []
then do
runTestText (putTextToHandle stderr False)
(TestList [testHex, testDisAsm])
return ()
else do
bytes <- Data.ByteString.readFile $ args !! 0
putStr $ unlines $ ndisasms 0
[fromIntegral b | b <- Data.ByteString.unpack bytes]
<-
はリスト内包表記の中か外かで意味が変わります。リスト内包表記の外で使われている<-
はアクションから値を取り出す構文です。<-
はdo
を指定したブロックでしか使えません。詳細は次の記事を参照してください。
コマンドラインからファイルを指定して呼び出し、逆アセンブルできることを確認してください。
$ ./disasm test
00000000 C60012 mov byte [bx+si],0x12
00000003 C606123456 mov byte [0x3412],0x56
00000008 C6401234 mov byte [bx+si+0x12],0x34
0000000C C680123456 mov byte [bx+si+0x3412],0x56
00000011 B012 mov al,0x12
00000013 C7001234 mov word [bx+si],0x3412
00000017 C70612345678 mov word [0x3412],0x7856
0000001D C740123456 mov word [bx+si+0x12],0x5634
00000022 C78012345678 mov word [bx+si+0x3412],0x7856
00000028 B81234 mov ax,0x3412
⇒ リビジョン 32
練習
【問12】8086の全命令を実装してください。
※ 解答例はありません。