- ベストアンサー
8086アセンブラで、メモリ間のデータをストリング命令でブロック転送したい
ソースが長めなので、簡潔に書きます。ご無礼の段、ご容赦ください。 【目的】PC-9801本体のCバス(汎用拡張スロット)に挿したサウンドボード上の ROM BIOSを読み出し、バイナリファイルに落としたい。既にエミュレータ用に実機からの 吸出しツールは存在するが、ソースが無いので、自作することにした。 その前段階としてアセンブラの修行も兼ねて、ROM BIOSの先頭3ワード(6バイト)を メモリ上のバッファにコピーし、比較して値の合致を確認したい。 将来的にはSCSI ROM BIOSの解析等を試みたい。 【方針】8086のストリング命令でダイレクトにメモリtoメモリでブロック転送を する。具体的にはrep movsbを用い、6バイトを転送する。 【備考】PC-9801-26K互換音源のROM BIOS(少なくとも先頭8バイト)は一意であり、 その並びは、0001h, 0000h, 00d2h である。例外はありません。 86音源でも同様で、下位互換性があることは、拙作ツール(OPNCHK.COM)にて確認済み。 なお、上記バイト列は、セグメントCC000h:オフセット2E00hから読み出し 可能である。 なお実行にあたり、所謂メモリマネージャの類(MELEMM.386等)は一切 組み込まない状態で行なう(EMSメモリマネージャ等との同居対応は将来の課題とします)。 【開発環境】PC-9801DA2(Cyrix Cx486DLC-25MHzに載せ換え; 13.6MB RAM; HA-55BS4 SCSIボード + 240MB SCSI HDD + SONY CPD-17SF9 CRT + NASM 2.06rc10 on NEC DOS 5.0A-H + Turbo Debugger v3.2 と、 秋葉で買ったジャンクFDに入ってたMASM ver 3.00; 予備機 VX41/RS21/EPSON 286VF/EPSON 486HX2/Xv13R16[K6-2 400MHz]/ AT互換機上のNekoIIエミュ/Cygwin上のnasmw.exe) 【参考書】PC-98、8086アセンブラ、テクニカルデータ、古雑誌等 定番本100冊ほど 【拙作コードの失敗点をご指南いただきたい。NASMコードですが、MASM/TASMでも構いません】 ; PC-9801-26K compatible Sound ROM BIOS Copy Program (i/o address 0188h) ; Programmed by OrzHacker666 ; Date 2009-07-13 for NASM 2.06rc10 [Bits 16] org 100h ; COM program section .text start: push es ; これを保存しないと、 push ds ; 画面がめちゃくちゃになる mov ax, 0cc00h ; Sound ROM セグメントアドレス mov es, ax mov ds, ax ; DS:SI -> ES:DI 無意味か? mov bx, 2e00h ; Sound ROM オフセットアドレス lea si, [es:bx] ; ES:BX がSound ROMの開始点 lea di, [ds:sbuff] ; sbuffは仮に確保したバッファ。 ; どこにあるかは、当たり前ですが、不明。そこら辺はCの変数宣言と同じですが。 mov cx, 8 ; とりあえず、アタマ8バイトをコピー cld rep movsb CompareWithOriginal: cmp word [es:bx+4], 00d2h ; これは通る。当たり前。 jne FailedCpyRom cmp word [ds:sbuff+4], 00d2h ; ここで失敗判定。なぜ? jne FailedCpyRom ; sbuffにes:bx~が正しく ; 転送されていないのか? SuccessCpyRom: ; これを拝めれば…。 pop ds pop es mov ah, 9 lea dx, [SUCCESSMsg] int 21h mov ax, 4c00h int 21h FailedCpyRom: ; 見飽きましたOrz pop ds pop es mov ah, 9 lea dx, [FAILEDMsg] int 21h mov ax, 4c00h int 21h section .data SUCCESSMsg: db 'Succeeded !!', 0dh, 0ah, '$' FAILEDMsg: db 'Failed(--;)', 0dh, 0ah, '$' section .bss sbuff: resb 8 ; Cで書くと、差し詰め unsigned char sbuff[8]; であろうか…。 識者の方、よろしくお願いいたします。気になって夜も眠れません。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
>回答中の、"cmp word es:[di-4], 00D2h"は MOVSD×2でDIはsbuff+8を指しています。DI-4はsbuff+4のことです。 これくらいはアセンブラをやる以上、直ぐに分かってください。 >sbuff自体については、セグメント値は不定で、調べる意義は無い ちょっと乱暴な話ですね。sbuffはbss(初期値なし静的領域)のよう なので、実行時のDSです。 確実なのはtextセグメント(コードと同じ)に次のようにデータを 定義して使います。 LES DI,CS:adr 'ES:DIにsbuffのセグメント:オフセットが入る == 中略 == adr DD sbuff 'sbuffのセグメント:オフセットが記録される >DS=ESではダメです だって、そうなっていますよ。AXをDSにもESにもMOVしてます。 前にも書いたんですが、セグメントの使い方が分かっていない? 前のサンプルは上記仮定(sbuffは実行時DSにある)で作っています。 PUSH DS、POP ES ES←DSにしています。(受け取り側が実行時DS) この後で、DSに0CC00hを代入しています。 >[es:]のオーバーライドは必須です CMP WORD ES:[DI-4],00D2h ちゃんと、ESでオーバライドしてるはずですけどね。 >ジレンマに陥ります どこがでしょう?アセンブラで、セグメントレジスタを使う以上は 自分の自由に使えば良い訳で、適当に値を設定したり、戻したりすれば 済む話なのでは? 例: REP MOVSB PUSH DS PUSH ES POP DS POP ES 後の4命令で、DSは実行時DSに戻り、ESはROMセグメントを指します。 サンプルではこれをやっていないので、実行時DSはESにある状態です。 故にセグメントオーバーライドしています。
その他の回答 (2)
- trapezium
- ベストアンサー率62% (276/442)
>lea di, [ds:sbuff] ; sbuffは仮に確保したバッファ。 >; どこにあるかは、当たり前ですが、不明。そこら辺はCの変数宣言と同じですが。 いやcom programならcsと同じsegment上のオフセット値。なので、 >mov ax, 0cc00h ; Sound ROM セグメントアドレス >mov es, ax >mov ds, ax ; DS:SI -> ES:DI 無意味か? これだとやっぱり(3)ということになります。
補足
ご回答ありがとうございます。 あれから数時間格闘したのですが、自作のデバッグコードを 仕込んだりしてみた結果、以下のような中間結果が出ました。 まず、 1)sbuff自体については、セグメント値は不定で、調べる意義は無い。 強いて言えば、お二方ご指摘の通り、ROM領域にぶち当たらないように することでしょうか。ただし、ソース末尾に未定義領域として sbuff: resb 8 として確保しており、また、割り込み禁止状態でmov word [sbuff+4], 00d2hと強制書込した上で、 cmp word [sbuff+4], 00d2hが一致することを確認したので、 sbuffは確実にRAM領域に確保されていると考えられる(なにより、resb擬似命令で ROM領域にバイト列をリザーブするのは、NASMの仕様上あり得ないことなので。MASMでも同様でしょう)。 上記は、mov ds, axでES=DS=CC00Hとするのを止め、DS=初期値と した結果、書込成功したということです。 ご指南の通りにmov ds, axを廃止したところ、mov word [ds:sbuff], 00d2h でも mov word [sbuff], 00d2h でも、(バイト数の無駄の違いこそあれ)sbuffへの00d2hの値の書込みは正常に行なわれていることが確認されました。 2)コピー失敗時のセグメントレジスタおよびsbuffの値を追ったところ、 以下の通りであった:(DSは初期状態のままとした場合) DS = 1322H ES = CC22H sbuff = 0222H Q: DS/ES/sbuffともに、下1バイトが22Hとなっているが、これは どこからきた値なのか?ESの上1バイトがCCHとなっているのは、 最初にAXから代入した際(mov ax, 0cc00h/mov es, ax)から理解できる。 sbuffの上1バイトについては起動毎に変わる(不定域ですから)。 DSの13xxHについては、org 100hでCOM宣言した際のオリジナル値であり、 SSも同じ値であると思われる。これも、不定だと思います。 いま、参考書をひっくり返して思案しています。すみません。
- nda23
- ベストアンサー率54% (777/1415)
セグメントオーバライドが間違っていますが、DS=ESなので、結果は 同じになります。 lea si, [es:bx] ; ES:BX がSound ROMの開始点 → MOV SI,BX セグメントオーバライドは無意味、1バイトと数クロックの損 cmp word [es:bx+4], 00d2h ; これは通る。当たり前。→ES:は不要 セグメントオーバライドは無意味、1バイトと数クロックの損 DS:送り出し側、ES:受け取り側です。ここまではDS=ESなら問題は ありません。送り出し側の+4と受け取り側の+4で内容が異なる となると、次の点が可能性として考えられます。 (1)割り込みにより、内容が書き換えられる。 (2)送り出し側と受け取り側のアドレスが一部で重複する。 (3)受け取り側がROMで、内容が変化しない。 個人的な見解としては(3)ではないかと思います。 転送先(sbuff)のセグメントがROMと同じなのがどうもアヤシイ PUSH ES ;保存 PUSH DS ;保存 PUSH DS ;ES←DSのための第1ステップ POP ES ;ES←DSのための第2ステップ MOV AX,0CC00h MOV DS,AX MOV BX,2E00h MOV SI,BX LEA DI,sbuff CLD CLI ;念のため割り込み禁止にする MOVSD ;ダブルワード(4バイト)転送 MOVSD ;ダブルワード(4バイト)転送 CMP WORD ES:[DI-4],00D2h STI ;割り込み許可 JNE FailedCpyRom 上記は規定データセグメント上にsbuffがあるものと仮定しています。 もう一度、セグメントレジスタの使い方を見直してください。
お礼
お礼の欄で釈明することをお許しください。 #1)DS=ESなので、結果は同じになります。 A. DS=ESではダメです。ESはSound ROM BIOSのセグメントアドレスを 示すために、0cc00hを代入する必要があります。 MOV SI,BX としてしまうと、これは単にsiにbx(=2e00h)を代入するだけになってしまいます。ここで lea si, [es:bx]としているのは、siレジスタに、CC00H:2E00Hのアドレスを代入したいということなのです。 [es:]のオーバーライドを取り払ってしまえば、Sound ROMと無関係なBX(アドレス)がSIに入ってしまいます。 #2)cmp word [es:bx+4], 00d2h ; これは通る。当たり前。→ES:は不要 A. 同上の理由で、[es:]のオーバーライドは必須です。無関係のBXと00d2hは 一致しません。 Q. 結局、DSは初期値のままでいてほしい、かつESはSound ROMのセグメントアドレスを 示して、常にBXと共に用いる必要がある、というジレンマに陥ります。
補足
ご回答ありがとうございます。cli/sti対策をしてみました。 # 回答中の、"cmp word es:[di-4], 00D2h"は、sbuffのオフセットアドレスから 4バイト目の値と00d2hの比較の書き間違いでしょうか? # 正しくは、"cmp word es:[sbuff+4], 00d2h"もしくは、push di/pop diを movsbの前後に挟み込むしかないでしょうね。 # word [es:di+4]との比較ですと、さらに4バイト先まですっ飛んでしまいますので。 あれから数時間格闘したのですが、自作のデバッグコードを 仕込んだりしてみた結果、以下のような中間結果が出ました。 まず、 1)sbuff自体については、セグメント値は不定で、調べる意義は無い。 強いて言えば、お二方ご指摘の通り、ROM領域にぶち当たらないように することでしょうか。ただし、ソース末尾に未定義領域として sbuff: resb 8 として確保しており、また、割り込み禁止状態でmov word [sbuff+4], 00d2hと強制書込した上で、 cmp word [sbuff+4], 00d2hが一致することを確認したので、 sbuffは確実にRAM領域に確保されていると考えられる(なにより、resb擬似命令で ROM領域にバイト列をリザーブするのは、NASMの仕様上あり得ないことなので。MASMでも同様でしょう)。 上記は、mov ds, axでES=DS=CC00Hとするのを止め、DS=初期値と した結果、書込成功したということです。 ご指南の通りにmov ds, axを廃止したところ、mov word [ds:sbuff], 00d2h でも mov word [sbuff], 00d2h でも、(バイト数の無駄の違いこそあれ)sbuffへの00d2hの値の書込みは正常に行なわれていることが確認されました。 2)コピー失敗時のセグメントレジスタおよびsbuffの値を追ったところ、 以下の通りであった:(DSは初期状態のままとした場合) DS = 1322H ES = CC22H sbuff = 0222H Q: DS/ES/sbuffともに、下1バイトが22Hとなっているが、これは どこからきた値なのか?ESの上1バイトがCCHとなっているのは、 最初にAXから代入した際(mov ax, 0cc00h/mov es, ax)から理解できる。 sbuffの上1バイトについては起動毎に変わる(不定域ですから)。 DSの13xxHについては、org 100hでCOM宣言した際のオリジナル値であり、 SSも同じ値であると思われる。これも、不定だと思います。 いま、参考書をひっくり返して思案しています。すみません。
お礼
試行錯誤の後、完成しましたので、ソースを掲載いたしまして終了いたします。 なお、この後にSound BIOS ROM 16KB全部を sbuff resw 8192 とした領域へ完全コピーすることに成功しました(別ソースですが)。 # あとは、DOSファンクションコールで、RAMからファイルへの # 読み書きを付け加えれば当初の目的は達成可能となります。 # ご指南ありがとうございました。 ; PC-9801 Sound BIOS Rom Copy Program(first 3 words(6 bytes) only) ; Programmed by OrzHacker666 ; ver 1.04 ; Date 2009-07-14 ; for NASM 2.06rc10 [Bits 16] org 100h ; COM program section .text start: push ds mov ax, 0cc00h ; Sound BIOS Segment Address mov ds, ax ; DS=CC00H mov bx, 2e00h ; Sound BIOS Offset Address lea si, [ds:bx] ; DS:SI points the Sound BIOS Address lea di, [sbuff] ; and store them on ES:DI (sbuff) mov cx, 3 ; copy first 6 bytes (3 words) cld rep movsw CompareWithOriginal: pop ds cmp word [sbuff], 0001h jne FailedCpyRom cmp word [sbuff+2], 0000h jne FailedCpyRom cmp word [sbuff+4], 00d2h ; Check if sbuff contains jne FailedCpyRom ; the original Sound BIOS Data SuccessCpyRom: mov dx, SUCCESSMsg mov ah, 9 int 21h mov ax, 4c00h int 21h FailedCpyRom: mov dx, FAILEDMsg mov ah, 9 int 21h mov ax, 4c00h int 21h section .data SUCCESSMsg: db 'Succeeded !!', 0dh, 0ah, '$' FAILEDMsg: db 'Failed(--;)', 0dh, 0ah, '$' section .bss sbuff resw 3 以上