DEBUG (DEBUG.COM、DEBUG.EXE) で Quine その2。自分自身を出力するプログラムのx86アセンブラ版。

前回でクワインはできたが、標準出力だとリダイレクトするとDEBUG自身の出力がゴミになる。そのままでもエラーを無視すれば動かなくも無いが気持ち悪いし、標準エラー出力にするとリダイレクトできるのはNT系32ビットのCMDに限られて、DOSでは動かしにくいしであまり面白くない。クワインなら一発で自分自身を吐き出してほしい。

そこで結果は標準出力ではなくファイルに出力するようにした。ついでに「『自分自身を出力するプログラム』を出力する」機能も追加した。通常アセンブラで開発、デバックするときは次の手順になるが、

ソースコード→アセンブル→バイナリ→デバッグ

これだと

無→デバッグ→バイナリ+ソースコード

という意味不明なことになる。

クワインのコード(DEBUG.COMのレスポンス)

f100 630 0      
a100            
mov ah,3c       
mov cx,0        
mov dx,1a6      
int 21          
mov ax,3d01     
int 21          
jnc 113         
int 20          
mov bx,ax       
mov si,1c0      
mov di,40       
mov ah,40       
mov cx,10       
mov dx,si       
int 21          
mov ah,40       
mov dx,1a4      
mov cx,2        
int 21          
add si,10       
dec di          
jnz 11b         
mov si,1c0      
mov di,47       
mov ah,40       
mov dx,1a0      
mov cx,4        
int 21          
mov ah,40       
mov dx,si       
mov cx,10       
int 21          
mov ah,40       
mov dx,1a3      
mov cx,3        
int 21          
add si,10       
dec di          
jnz 13a         
mov si,5c0      
mov di,7        
mov ah,40       
mov cx,10       
mov dx,si       
int 21          
mov ah,40       
mov dx,1a4      
mov cx,2        
int 21          
add si,10       
dec di          
jnz 163         
mov ah,3e       
mov dx,1a6      
int 21          
int 20          
                
a1a0            
db"db ",27,d,a  
db"quout.txt",0 
                
a1c0            
db 'f100 630 0      '
db 'a100            '
db 'mov ah,3c       '
db 'mov cx,0        '
db 'mov dx,1a6      '
db 'int 21          '
db 'mov ax,3d01     '
db 'int 21          '
db 'jnc 113         '
db 'int 20          '
db 'mov bx,ax       '
db 'mov si,1c0      '
db 'mov di,40       '
db 'mov ah,40       '
db 'mov cx,10       '
db 'mov dx,si       '
db 'int 21          '
db 'mov ah,40       '
db 'mov dx,1a4      '
db 'mov cx,2        '
db 'int 21          '
db 'add si,10       '
db 'dec di          '
db 'jnz 11b         '
db 'mov si,1c0      '
db 'mov di,47       '
db 'mov ah,40       '
db 'mov dx,1a0      '
db 'mov cx,4        '
db 'int 21          '
db 'mov ah,40       '
db 'mov dx,si       '
db 'mov cx,10       '
db 'int 21          '
db 'mov ah,40       '
db 'mov dx,1a3      '
db 'mov cx,3        '
db 'int 21          '
db 'add si,10       '
db 'dec di          '
db 'jnz 13a         '
db 'mov si,5c0      '
db 'mov di,7        '
db 'mov ah,40       '
db 'mov cx,10       '
db 'mov dx,si       '
db 'int 21          '
db 'mov ah,40       '
db 'mov dx,1a4      '
db 'mov cx,2        '
db 'int 21          '
db 'add si,10       '
db 'dec di          '
db 'jnz 163         '
db 'mov ah,3e       '
db 'mov dx,1a6      '
db 'int 21          '
db 'int 20          '
db '                '
db 'a1a0            '
db 'db"db ",27,d,a  '
db 'db"quout.txt",0 '
db '                '
db 'a1c0            '
db '                '
db 'n quin.com      '
db 'r cx            '
db '530             '
db 'w               '
db 'g               '
db 'q               '
                
n quin.com      
r cx            
530             
w               
g               
q               

第一世代のジェネレータ(perlのコード)

#!perl

# DEBUG.COM用 クワイン ジェネレータ 2

# ソース前半部	(コメント中の”’はSBCS)
$part1 =<SI (SIは表示文字列のポインタ)
mov di,40		# 64行が対象。残り7行は後半部になる。(DIは減算カウンタ)
mov ah,40		# DOSコール番号 ファイル出力
mov cx,10		# 1行は固定長16文字
mov dx,si		# 表示文字列アドレスをDXにセット
int 21			# 表示する
mov ah,40		# DOSコール番号 ファイル出力
mov dx,1a4		# CRLF文字列アドレス
mov cx,2		# 長さ2文字
int 21			# 表示する
add si,10		# 表示文字列ポインタSIを1行分(16文字)進める
dec di			# 行数カウンタデクリメント
jnz 11b			# 指定行数表示したらループ終了
mov si,1c0		# -- フェーズ2:埋め込みソース表示。ソースはDB ’...’ で囲む。SIは表示文字列のポインタ。
mov di,47		# 71行が対象。(全行)
mov ah,40		# DOSコール番号 ファイル出力
mov dx,1a0		# 文字列 ”DB ’” のアドレス
mov cx,4		# 長さ4文字
int 21			# 表示する
mov ah,40		# DOSコール番号 ファイル出力
mov dx,si		# 表示文字列アドレスをDXにセット
mov cx,10		# 1行は固定長16文字
int 21			# 表示する
mov ah,40		# DOSコール番号 ファイル出力
mov dx,1a3		# 文字列 ’CRLF のアドレス
mov cx,3		# 長さ3文字
int 21			# 表示する
add si,10		# 表示文字列ポインタSIを1行分進める
dec di			# 行数カウンタデクリメント
jnz 13a			# 指定行数表示したらループ終了
mov si,5c0		# -- フェーズ3:後半部ソース表示。
mov di,7		# 7行が対象
mov ah,40		# DOSコール番号 ファイル出力
mov cx,10		# 1行16文字
mov dx,si		# 表示文字列アドレスをDXにセット
int 21			# 表示する
mov ah,40		# DOSコール番号 ファイル出力
mov dx,1a4		# CRLF文字列アドレス
mov cx,2		# 長さ2文字
int 21			# 表示する
add si,10		# 表示文字列ポインタSIを1行分進める
dec di			# 行数カウンタデクリメント
jnz 163			# 指定行数表示したらループ終了
mov ah,3e		# DOSコール番号 ファイルクローズ
mov dx,1a6		# ファイル名のアドレス
int 21			# DOSコール
int 20			# 終了のDOSコール
			# a100コマンド終了の合図
a1a0			# 「エスケープ文字列」”db ”, ”,CR,LF の格納場所
db"db ",27,d,a	# CRLFだけの出力にも使う
db"quout.txt",0	# 出力ファイル名
			# a200コマンド終了の合図
a1c0			# 埋め込みソースの格納場所。次の行からがdb ’...’のプレースホルダになるが、同じなので中身は無し。
EOT

# ソース後半部
$part2 =<<EOT;
			# a300コマンド終了の合図
n quin.com		# 実行ファイル生成時(wコマンド)のファイル名指定
r cx			# ファイルサイズは 600h バイト
530
w			# ファイルに書き込み
g			# 実行コマンド
q			# 終了コマンド
EOT

# #
$part1 =~ s/#.+?\n/\n/g;
$part2 =~ s/#.+?\n/\n/g;

$part1 =~ s/[  \t]+?\n/\n/g;
$part2 =~ s/[  \t]+?\n/\n/g;

print $part1;
@escp = split(/\n/, $part1.$part2);
foreach $itr (@escp) {
	$line1 = "db '".$itr;
	$line2 = " "x (20-length($line1))."'\n";
	print $line1.$line2;
}
print $part2;

実行結果

DOSKEYでマルチコマンド実行。Q1.TXTはジェネレータが生成した第一世代のコード。セマンティクスは同じだが余分な空白が無いので第一世代だけは第二世代以降とは若干異なる。
quin_dbg2-1

Q2.TXTは第二世代のコード、QUOUT.TXTは第三世代のコード。
quin_dbg2-2

コード(DEBUGのレスポンス)をDEBUGにかけるとファイルを2個生成する。自分自身はQUOUT.TXTに、自分自身生成プログラムはQUIN.COMに出力する。そしてQUIN.COMを実行するとQUOUT.TXTを生成する。これも前世代と同じ内容になる。(何を書いているかよくわからなくなった。)
quin_dbg2-3

 

DEBUG.COM の wコマンドがポイントで、ヘルプとか解説サイトにはあまり書かれていない使い方で、あらかじめBX:CXにサイズをセットしておくとnコマンドで指定したファイルにCS:100hか指定アドレスからBX、CXにセットされた長さだけバイナリで保存する。これでメモリイメージを保存できる。

書式
w [セグメントアドレス:オフセットアドレス]

機能
セグメント:オフセットアドレスから (BX << 16 | CX)バイト書き込む。アドレス未指定なら CS:0100h が先頭。
広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中