2017-11-28 1 views
1

어셈블리 언어로 프로그램을 끝내고 싶습니다. 나는 [00h, 0FFh]의 모든 ASCII 문자와 16 진수 ASCII를 table 형식으로 출력 할 수 있기를 바랍니다.하지만 오류가 발생하고 시도합니다. 여러 번 실패했지만 실패했습니다. 그것은 스칼라 타입을 제외하고는 을 보여줍니다. 그러나 어떻게 다루지는 않습니다.오류 : 스칼라 유형을 예상합니다

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 

을 그리고 또 다른 질문은 내가, 당신은 몇 가지를 출력하는 방법들에서 요소 (진수 ASCII를)에 제공 할 수 있습니다 몰라 : 이 코드는 오류가 "MOV 디, COL * 8"존재 조언?

+1

어셈블리 프로그램과 C의 관계는 무엇입니까? – StoryTeller

+0

실제로 메모리에 '8'을 저장하는 대신 'col * 8'을 즉각적으로 사용할 수있게하려면'col equ 8'을 사용하십시오. 당신은 무엇이든 'si'를 사용하지 않는 것 같습니다. 변수를 정적 저장소 대신 레지스터에 보관하십시오. 또한 1 씩 증가시키고 8을 곱하는 대신 8 씩 증가시킵니다. 즉, 열 값을 이미 크기 조정합니다. –

답변

0

이 구문은 MASM/TASM 구문이라고 추측합니다. 이러한 경우 mov di,col*8에서

col

메모리 "변수"는 ... 상당히 가짜이다 (보다 정확하게 col 값으로 그 col 후 정의 된 메모리의 첫 번째 바이트의 메모리 어드레스를 갖는 "어셈블리 심볼은" 레이블, 귀하의 경우에는 dw 0 지시어 =>에 의해 정의 된 2 바이트가 0 값으로 생성됩니다). 16 비트 정수 값 col 스탠드 col 메모리 어드레스의 일부인 OFFSET 여기서

그럼 mov di,colmov di,[col]mov instructionmov r16,r/m16의 변형으로 또는 적절한 인텔 문법에 조립된다. 주소의 세그먼트 부분은 기본적으로 ds에서 가져오고, ds을 올바르게 앞에 설정 한 경우 movdw에 의해 예약 된 해당 메모리에 도달합니다.

mov di, col*8가 모호, 당신도 (OFFSET col)*8 주소에서 메모리를 가져 의미 mov di,[col*8]으로 = 당신이 원하지 않는 어떤 것을 조립, 또는 mov di,[col] * 8 산술로, 메모리에서 가져온 값을 곱, 및 x86 CPU는 없습니다 수 그러한 지시. (32B) 모드에서 유효 mov eax,[ebx + esi*8] 같은 메모리 주소와 관련된 연산은 mov 지침에

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

모든 "산술 같은"작업, 그리고 메모리를 의미 : 당신처럼 별도로 값의 산술 연산을 할 필요가 먼저 가져올 주소가 계산됩니다. 값으로 부호/제로 확장을 수행하는 movsx/movzx 명령어를 제외하고는 x86 CPU 제품군의 mov 명령어에 내장 된 값 자체에 대한 산술 연산은 없습니다. 다른 예외는 생각해 낼 수 없습니다. lodsb과 같은 "문자열"명령어 군은 가져온 후에 포인터 값을 조정하지만, 값 자체는 조정되지 않고 포인터 만 조정됩니다.

실제 메모리 오프셋으로 계산하는 대신 lea 명령어를 사용하여 메모리 주소 산술 CPU 모듈을 사용하여 값으로 일부 산술 연산을 계산할 수 있습니다. 물론 특정 코드에서


그것을 업데이트 할에 아무것도 읽기/저장이 모든 시간을 미리 곱, 그리고 유지하기 위해 어떤 레지스터의 값을 유지하고, 단지 add xxx,8을 더 최적 /기억으로부터. 마지막 수단으로 메모리를 사용하십시오. 모든 것을 레지스터에 맞출 수없는 경우에만 사용하십시오.


편집 : 소스를 조금 더 읽은 후 ...

코드를 작성하고 각 단계마다 디버깅을 계속 실행하는 것이 좋습니다.

int 21h 출력 루틴으로 직접 비디오 램 쓰기를 믹싱하는 순간, 아무 것도 쓸 수 없습니다.

모든 확장 ASCII 문자를 표시하려면 직접 쓰기가 유일한 선택입니다 (DOS는 10, 13 및 기타 제어 코드 값을 표시하지 않으므로 커서 이동과 같은 특별한 작업을 수행합니다. (새 줄, 탭), 경고음 등).

그리고 256 심볼을 원한다면 16x16 테이블이 완벽한 의미를 갖습니다.

그래서 처음 16 개의 확장 ASCII를 한 줄로 표시하여 코드를 작성해야합니다. 같은, 당신은 처음에 텍스트 모드의 설정을 추가하는 등 ...에 "명확한 화면"을 확장 플러스 당신이 확인할 수 있습니다 (16 개 문자 한 줄을 보여줍니다)

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의 값을 알고, 당신이 완전히 루프/푸시/팝을 방지, 그냥 수행 간단한 두 자리 번역 할 수 있습니다

... 
    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 
    ... 

디버그 + 작동 확인 (실제로하지 않았다 그래서 나는 단지 내 코드가 작동되기를 바랍니다).

그런 다음 당신은 결과 코드에 좀 걸릴 수 있습니다, 당신은 모든 쓰기가 연속 es:[di] = word value 일처럼 알 수 있습니다, 그래서 당신은 첫 번째 후 최적화 된 코드처럼, 대신 di+? 변위 및 add di,5*2 제거하는 stosw을 사용하여 시도 할 수 있습니다

... 
; 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)를 채우는 때문에 (최종의 행 상황을 통해 특별한 아무것도하지 않는 행과 행 루프를 알 수 있습니다 : (플러스 I은 16 진수 값의 색상을 변경 첨가) 라인의 문자들, 그래서 "자동적으로"다음 라인으로 넘어 간다.) 그래서 당신은 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이 예상대로 작동하는지 확인한 다음 두 번째 숫자를 추가합니다. 또한 코드가 최종 버전 (예 : x86 ASM 환경으로 인해)에서 어떻게 끝날지에 대한 명확한 비전이 있었기 때문에 es:di과 같은 내 레지스터 사용 선택 사항 중 많은 부분이 매우 간단한 행운으로 보일 수 있습니다. 나중에 "무료".x86 어셈블리를 처음 사용하는 경우 초기 버전 코드 이후에 더 좋은 아이디어를 찾고 더 많은 내용을 덮어 쓰지 않으려면 개발 단계를 디버그 + 변경 사항을 한 번에 확인할 수있을 정도로 작게 유지하십시오.

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

한쪽 등록하거나 즉시해야하며, 86 CPU에 mov의 더 mov mem, mem 변형 없습니다 : 다른 문제를 칠 것입니다 현재 코드


.

일시적 값을 보유 할 AL 레지스터를 사용하는 것과 같이,이 mov들에 그 분할해야 할 것 :

소스를 확인하기 위해 http://x86.renejeschke.de/ 같은 명령 참조 가이드를 사용하여
mov al,s[0] 
    mov es:[di+2],al 

보관할는 일부 기존의 변종을 표적으로한다 명령이 어떻게 작동하는지 완벽하게 이해하고 있어야합니다 (특히 mul/imuldiv/idiv 지침은 스택 오버플로에 대해 많은 질문을 제기하기 때문에 상식적으로 작동하지 않으므로 참조 안내서를 먼저 확인하면 쉽게 피할 수 있음).

+0

대단히 감사합니다! – xhfx

+0

@ xhfx 오신 것을 환영합니다. 귀하의 질문에 잘 대답 할 수 있도록 답변을 수락하는 것을 고려해보십시오. 그 질문은 "감사합니다"(stackoverflow 사이트에서)로 작동합니다. 또한 응답하지 않은 질문을 확인할 때 다른 사람들을 도울 수 있습니다. – Ped7g

+0

당신은 너무 친절합니다! 나는 어셈블리 언어에 익숙하지 않으므로 많은 질문을하러 왔지만, Google뿐만 아니라 귀하의 추천 사이트에서도 열심히 노력할 것입니다! – xhfx

관련 문제