2017-11-28 5 views
1

アセンブリ言語でプログラムを終了したいのですが、[00h、0FFh]のすべてのASCII文字と16進ASCIIをtableの形式で出力することを願っています。何度も失敗した。 スカラータイプを除いてが表示されていますが、それに対処する方法はありません。 これは、コード、エラーは「MOVディ、COL * 8」に存在している:エラー:スカラー型を予期しています

code segment 
assume cs:code 
main: 
    jmp begin 
row dw 0 
col dw 0 
s db 2 dup(0),0Dh,0Ah,'$' 
begin: 
    mov ax,0B800h 
    mov es,ax 
    mov di,0 
    mov cx,2 
    mov bl,00h 
next_row: 
    mov byte ptr es:[di],bl 
    mov byte ptr es:[di+1],04h 
    push di 
    mov dh,bl 
    mov dl,00h 
    mov di,0 
again: 
    push cx 
    mov cl,2 
    rol dx,cl 
    push dx 
    and dx, 0000000000001111B 
    cmp dx, 10 
    jb is_digit 
is_alpha: 
    sub dl, 10 
    add dl, 'A' 
    jmp finish_4bits 
is_digit: 
    add al, '0' 
finish_4bits: 
    mov s[di], dl 
    pop dx 
    pop cx 
    add di, 1 
    sub cx, 1 
    jnz again 
    pop di 
    mov byte ptr es:[di+2],s[0] 
    mov byte ptr es:[di+3],02h 
    mov byte ptr es:[di+4],s[1] 
    mov byte ptr es:[di+5],02h 
    push ax 
    mov ah,02h 
    mov dl,0Dh 
    int 21h 
    mov ah,02h 
    mov dl,0Ah 
    int 21h 
    pop ax 

    add di,110 
    add bl,01h 
    cmp bl,0FFh 
    ja exit 
    add row,1 
    cmp row,24 
    jne next_row 
    jmp next_col 
next_col: 
    mov row,0 
    add col,1 
    mov di,col*8 
    add bl,01h 
    jmp next_row 
exit: 
    mov ah,0 
    int 16h;End 
code ends 
end main 

そして、別の質問です私はあなたには、いくつかを与えることができ、どのように出力sの要素(16進ASCII)知りません助言?

+1

アセンブリプログラムとCの関係は何ですか? – StoryTeller

+0

実際に '8'をメモリに格納するのではなく、' col * 8'を直接的に使用できるようにするには 'col equ 8 'を使用してください。あなたは何にでも「si」を使用しているようではありません。静的な記憶域ではなくレジスタに変数を保持します。また、1を増やして8を掛ける代わりに、8だけ増分するだけです。つまり、列の値を既にスケーリングしたままにしておきます。 –

答えて

0

構文からは、これはMASM/TASM構文であると推測します。

colをより正確にメモリ「可変」(colであるような場合には

mov di,col*8はかなり偽では、...の値としてその後定義されたメモリの最初のバイトのメモリアドレスを有する「組立記号」であるcolあなたの場合、dw 0ディレクティブ=>で定義されている2つのバイトが0の値で生成されます)。

次にmov di,colmov instructionmov r16,r/m16変異体に組み立て、または16ビットの整数値のcolスタンドがcolメモリアドレスのOFFSET一部であるmov di,[col]に適切なインテル構文にされます。アドレスのセグメント部分はデフォルトでdsから取得され、dsを正しく先に設定した場合、movdwによって予約されたそのメモリに到達します。

mov di, col*8があいまいである、あなたはどちらか(OFFSET col)*8アドレスからメモリを取得するために意味するでしょうmov di,[col*8]として=あなたが望まないものをすることをアセンブルし、またはmov di,[col] * 8算術として、メモリからフェッチされた値を乗算し、およびx86 CPUは持っていないことができますそのような指示。

mov di,[col] 
shl di,3  ; di = di * 8 

mov eax,[ebx + esi*8]は32Bモードで有効であるようにmovの指示で任意の「算術様」の操作は、メモリ・アドレスに関連する算術演算であり、それはメモリを意味します。あなたは次のように、個別の値に算術演算を行う必要があります最初にフェッチされるアドレスが計算されます。値に符号/ゼロ拡張を行うmovsx/movzx命令を除き、x86 CPUファミリのmov命令に組み込まれている値そのものには算術演算はありません。他の例外を呼び出すことはできません。 lodsbのような命令の「ストリング」ファミリは、フェッチ後にポインタ値を調整しますが、それは値自体の調整ではなく、ポインタのみです。

実際のメモリオフセットで計算する代わりに、lea命令を使用してメモリアドレス算術CPUモジュールを利用して、値を使った算術演算を行うことができます。もちろん、あなたの特定のコードで


いくつかのレジスタの値を維持する方が最適であり、そしてだけに何かを読んで/保存、それを更新するために、事前に乗じすべての時間を維持するために、そしてないadd xxx,8を行います/メモリから。すべてをレジスタに収めることができないときは、最後の手段としてのみメモリを使用してください。


編集:ソースをもう少し読んだ後...

おそらく、コードを多くの小さなステップで記述し、それぞれのコードの後に​​+デバッグを続けてください。

現時点では、あなたには役に立たない出力ルーチンであるint 21hで直接ビデオRAM書き込みをミックスしていますが、それを選択してください。

すべての拡張ASCII文字を表示したいので、直接書き込みは唯一の選択です(DOSは10,13などの制御コード値を表示してから表示することになり、カーソルの移動(改行、タブ)、ビープ音など)。

256シンボルが必要な場合は、16x16テーブルが理にかなっています。

多分、最初の16個の拡張ASCIIを1行で表示してコードを書き始めてください。同様に:あなたは、最初にテキストモードの設定を追加するように...「クリアスクリーン」を、それを拡張するプラスあなたがあることを確認することができます(16個のシンボルの1行を示しています)

code segment 
assume cs:code, ds:code 
main: 
    jmp begin 
; reserved for data in future 

; top left corner of the table at [0, 4] position 
TABLE_LEFT_TOP_OFFSET equ  (4*80*2) 
; the table will be 16x16, each element being 5x1 
; total size = 80x16 = (will fit 80x25 text mode screen) 
; element 5 chars as: 
; symbol, space, first hex digit, second hex digit, space 

begin: 
    mov  ax,0B800h 
    mov  es,ax  ; es = text mode VRAM segment 

; display 16 symbols (single row) on proper positions 
    mov  di,TABLE_LEFT_TOP_OFFSET ; di = starting VRAM adr 
    xor  dl,dl ; symbol value = 0 
    mov  cx,16 ; loop counter 
row_loop: 
    ; show symbol + space 
    mov  al,dl ; symbol ASCII 
    mov  ah,4h ; colour attribute 
    mov  es:[di],ax  ; write symbol 
    mov  al,' ' 
    mov  es:[di+2],ax ; write space after it 
    ; advance loop for next symbol 
    inc  dl 
    add  di,5*2 ; move 5 chars forward 
    dec  cx 
    jnz  row_loop 

    ; exit back to DOS 
    xor  ah,ah ; ah = 0 - wait for key service 
    int  16h 
    int  20h  ; terminate to DOS 

code ends 
end main 

あなたは期待通り、この作品を確認したら期待されるモードで:

begin: 
    mov  ax,3  ; ah = 0 (set gfx mode), al = 3 
    int  10h   ; set VGA text mode 80x25 chars 

コンパイル+実行+検証動作します。あなたは16進数を追加することができます...再びそれが動作することを確認+実行...

... 
    xor  dl,dl ; symbol value = 0 
rows_loop: 
    mov  cx,16 ; loop counter 
    ... 

... 
    ; both dl and di are ready for next line 
    ; so all is needed is just to loop until dl==0 
    test dl,dl ; until all 256 symbols were displayed 
    jnz  rows_loop 
    ; exit back to DOS 
    ... 

その後、あなたはすべての256個のシンボルを表示するために16行のループを追加することができます表示します。あなたが唯一の0〜255の値を行います知っているように、あなたは完全にループ/プッシュ/ポップを回避することができ、そしてちょうど簡単な2桁の変換を行います。

... 
    mov  es:[di+2],ax ; write space after it 

    ; show hexadecimal value of symbol - first digit 
    mov  al,dl 
    shr  al,4 ; al = upper 4 bits of dl (first hex digit) 
    add  al,'0' 
    cmp  al,'9' 
    jbe  first_hex_digit_ok 
    add  al,('A'-'0'-10) ; adjust it to A-F letter if needed 
first_hex_digit_ok: 
    mov  es:[di+4],ax ; write first hex digit 
    ; show hexadecimal value of symbol - second digit 
    mov  al,dl 
    and  al,0Fh ; al = lower 4 bits of dl (second hex digit) 
    add  al,'0' 
    cmp  al,'9' 
    jbe  second_hex_digit_ok 
    add  al,('A'-'0'-10) ; adjust it to A-F letter if needed 
second_hex_digit_ok: 
    mov  es:[di+6],ax ; write second hex digit 
    ; write space after that 
    mov  al,' ' 
    mov  es:[di+8],ax ; write space after it 

    ; advance loop for next symbol 
    inc  dl 
    ... 

デバッグ+は、それが(私は実際にはしませんでした動作することを確認だから私のコードがうまくいくことを願っています)。

その後、あなたは結果のコードに見てとることができ、あなたが最初の試みの後に最適化されたコードのように、di+?変位とadd di,5*2を取り除くために代わりstoswを使用できるように、すべての書き込みは、連続したes:[di] = word valueをやってのようなものに気づくことがあり(プラス私は、HEX値を変更した色を追加しました):

... 
; display 16 symbols (single row) on proper positions 
    mov  di,TABLE_LEFT_TOP_OFFSET ; di = starting VRAM adr 
    xor  dl,dl ; symbol value = 0 
rows_loop: 
    mov  cx,16 ; loop counter 
row_loop: 
    ; show symbol + space 
    mov  al,dl ; symbol ASCII 
    mov  ah,4h ; colour attribute for symbol 
    stosw   ; write symbol 
    mov  al,' ' 
    stosw   ; write space after it 
    ; show hexadecimal value of symbol - first digit 
    mov  ah,2h ; colour attribute for hex value 
    mov  al,dl 
    shr  al,4 ; al = upper 4 bits of dl (first hex digit) 
    add  al,'0' 
    cmp  al,'9' 
    jbe  first_hex_digit_ok 
    add  al,('A'-'0'-10) ; adjust it to A-F letter if needed 
first_hex_digit_ok: 
    stosw   ; write first hex digit 
    ; show hexadecimal value of symbol - second digit 
    mov  al,dl 
    and  al,0Fh ; al = lower 4 bits of dl (second hex digit) 
    add  al,'0' 
    cmp  al,'9' 
    jbe  second_hex_digit_ok 
    add  al,('A'-'0'-10) ; adjust it to A-F letter if needed 
second_hex_digit_ok: 
    stosw   ; write second hex digit 
    ; write space after that 
    mov  al,' ' 
    stosw   ; write space after it 
    ; advance loop for next symbol 
    inc  dl 
    dec  cx 
    jnz  row_loop 
    ; both dl and di are ready for next line 
    ; so all is needed is just to loop until dl==0 
    test dl,dl ; until all 256 symbols were displayed 
    jnz  rows_loop 
    ... 

そして最後に、あなたは、単一の行がいっぱい80をいっぱいにするので、行と列のループは、エンドオブローの状況(オーバー特別何もしないことがあり行の文字なので、自動的に次の行に進む)ので、cxのカウンターを完全に取り除くだけで単一の256シンボルのループ:

... 
    xor  dl,dl ; symbol value = 0 
symbol_loop: 
    ; show symbol + space 
    ... 

    ... 
    stosw   ; write space after it 
    ; advance loop for next symbol 
    inc  dl 
    jnz  symbol_loop ; until all 256 symbols are displayed 
    ; exit back to DOS 
    ... 

これだけです。私は自分のコードを検証しなかったので、いくつかのバグの場合は残念ですが、私はasmコードを書くときに自分の考え方を提示しようとしています...実際には、私はおそらく最初の桁だけを行うだろう、+'0'+'A'-'0'-10が期待どおりに動作することを確認してから、2番目の桁を追加します。また、最終的なバージョン(x86 asmの経験のため)でコードがどのように終了するのかについての明確なビジョンがあったため、es:diなどのレジスタの使用方法の多くは非常に幸運に思えます。後で "無料で"。x86アセンブリの初心者であれば、最初のバージョンのコードの後に​​もっと良いアイデアが見つかるはずです。さらに多くのものを上書きすることを躊躇しないでください。あなたの現在のコードで


あなたは別の問題を打つでしょう:

mov byte ptr es:[di+2],s[0] 

のx86 CPU上movのないmov mem, memバリアントがありません、片側は、登録または即時なければなりません。

あなたは、一時的に値を保持するためにALレジスタを使用してのように、2 mov秒にそれを分割する必要があります:

mov al,s[0] 
    mov es:[di+2],al 

は、あなたのソースがいくつかの既存のバリアントを標的にされていることを確認するためにhttp://x86.renejeschke.de/のような命令のリファレンスガイドを使用してください命令がどのように動作しているか完全に理解していることをご存知でしょうか(特にmul/imuldiv/idiv命令では、スタックオーバーフローに関する多くの質問がありますが、リファレンスガイドを確認するだけで簡単に回避できます)。

+0

ありがとうございます! – xhfx

+0

@ xhfxよろしくお願いいたします。あなたはあなたの質問にうまく答える場合、答えを受け入れることを検討するかもしれません。それはあまりにも(ありがとう)として働きます(stackoverflowサイトで)。さらに、未回答の質問をチェックしているときに他の人にも役立ちます。 – Ped7g

+0

あなたはとても親切です!私はアセンブリ言語を初めて使っていますので、多くの質問に出くわしますが、私はあなたのお勧めのサイトだけでなく、Googleと一緒にそれを作るために最善を尽くします! – xhfx

関連する問題