Assembler tasm

Javeiros,
Estou iniciando o estudo de assembly com o montador Tasm 5.0, e estou tendo uma dificuldade semelhante aos que iniciam com a linguagem C, que é a seguinte:

Quando clico no executável recém criado, a janela do DOS aparece de forma muito rápida, sumindo logo em seguida como no exemplo abaixo:

#include<stdio.h> int main() { printf("Exibição muito rápida para se ler"); return 0; }
cuja solução é colocar um getch(), conforme abaixo:

[code]#include<stdio.h>
int main()
{
printf(“A mensagem está pausada. Agora voçe pode ler a mensagem”);
getch();
return 0;

Alguém sabe como faço para corrigir o problema no Tasm?
}[/code]

parece não ser um problema.

é q o programa termina e fecha.

vc ja tentou executar ele pelo console?

O jeito mais simples é você rodar seu programa pela linha de comando, não pelo DOS.

A propósito, você está estudando com o TASM e gerando programas de 16 bits que rodam no DOS (e usando INT 21H) , é isso?

Se for isso, é melhor você olhar a lista de comandos do INT 21H.

http://spike.scu.edu.au/~barry/interrupts.html#ah01

Mais uma coisinha chata.

Se for estudar os comandos do DOS, que aparentemente é o que você está fazendo, esteja ciente que programas de 16 bits não rodam em Windows 64 bits nem a pau. Como seus programas aparentemente estão funcionando, estou supondo que você esteja usando uma máquina formatada com Windows 32 bits.

Se precisar rodar esse programa em uma máquina Windows 64 bits, você vai ver uma mensagem de erro esquisita (que é devida a uma limitação do Windows em 64 bits - ele só pode rodar programas em 32 e 64 bits, não em 16.) Nesse caso, você vai ter de instalar uma máquina virtual que rode DOS (como o FreeDOS) ou Windows de 32 bits.

[quote=entanglement]Mais uma coisinha chata.

Se for estudar os comandos do DOS, que aparentemente é o que você está fazendo, esteja ciente que programas de 16 bits não rodam em Windows 64 bits nem a pau. Como seus programas aparentemente estão funcionando, estou supondo que você esteja usando uma máquina formatada com Windows 32 bits.

Se precisar rodar esse programa em uma máquina Windows 64 bits, você vai ver uma mensagem de erro esquisita (que é devida a uma limitação do Windows em 64 bits - ele só pode rodar programas em 32 e 64 bits, não em 16.) Nesse caso, você vai ter de instalar uma máquina virtual que rode DOS (como o FreeDOS) ou Windows de 32 bits.
[/quote]

Quando estou programando em assembly em Windows 64 bits eu costumo usar o dosbox para emular o DOS e conseguir executar meus programas. Funciona perfeitamente.

Que curioso - achei um site onde se chama as APIs do Windows em Assembly e ainda em 32 bits (ou seja, essa coisa de ficar dependendo do DOS de 16 bits já era).

http://win32assembly.online.fr/tutorials.html

Em particular, como se usam muitas macros do macro assembler MASM (Microsoft Macro Assembler), a programação é surpreendentemente muito fácil de ler (parece mais com Pascal que com Assembly).

Vejam só um exemplo:

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

.data
MsgBoxCaption  db "Iczelion Tutorial No.2",0
MsgBoxText       db "Win32 Assembly is Great!",0

.code
start:
invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start 

Note que a macro “invoke” que está em um desses arquivos incluídos já empilha corretamente todos os parâmetros e faz a chamada correta à API do Windows.

Acho que é possível passar um parâmetro para o MASM indicar o que realmente a macro gerou.

Um exemplo de uma “window procedure”.

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    LOCAL hdc:HDC
    LOCAL ps:PAINTSTRUCT

    .IF uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSEIF uMsg==WM_CHAR
        push wParam
        pop  char
        invoke InvalidateRect, hWnd,NULL,TRUE
    .ELSEIF uMsg==WM_PAINT
        invoke BeginPaint,hWnd, ADDR ps
        mov    hdc,eax
        invoke TextOut,hdc,0,0,ADDR char,1
        invoke EndPaint,hWnd, ADDR ps
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor    eax,eax
    ret
WndProc endp 

Interessante isso. Parece código Pascal mesmo :smiley:
Esse assembly é para qual(is) processador(es)?

O MASM (Microsoft Macro Assembler), que é também conhecido por “ML”, tem uma coleção de macros, nesse caso é para x86 mesmo (Windows 32 bits).
Deve haver macros equivalentes para x64.

Essas macros são convertidas para o código equivalente em Assembly mas você não precisa ficar criando manualmente uma porção de labels (imagine só o que você teria de fazer no caso da window procedure abaixo: - não tive paciência de converter o código completo, e não sei se a conversão está correta - em particular, no caso de chamar uma API do Windows, acredito que não seja simplesmente usar CALL PostQuitMessage e sim usar algo bizarro como MOV EDX, PostQuitMessage ; CALL [EDX] - não tenho muita experiência com Windows 32 bits e Assembly. )

WndProc proc 
    MOV EAX, uMsg
    CMP EAX, WM_DESTROY
    JNE L00001
    SUB EAX, EAX
    PUSH EAX
    CALL PostQuitMessage
    JMP L00010
L00001:
    CMP EAX, WM_CHAR
    JNE L00002
... etc ...
L0010:
    XOR EAX, EAX
    RET

O legal das macros é que dá pra deixar o assembly um pouco mais legível, como o código que você mostrou acima. Eu vi até um livro na internet uns dias atrás que mostrava como simular algo parecido com a programação orientada a objeto em assembly, usando macros.

Se bobear fica até mais legível que alguns programas em C que já vi por aí :slight_smile:

O único problema é que macro assemblers não fazem milagres.

Por exemplo, você poderia ter uma condição em que um compilador C bem bobinho iria juntar 2 jumps consecutivos em um só, mas o Macro Assembler não conseguir fazer isso.

Mas como é um “macro assembler” e você mesmo pode definir macros, você pode construir programas bem complexos e usando todos aqueles truques sujos que os compiladores normalmente usam. Um truque sujo que já vi em um compilador é o seguinte: quando você converte um número de binário para decimal, você normalmente dividiria por 10 e obteria o resto da divisão por 10 (usando a instrução DIV ou IDIV). Só que o compilador da Microsoft (que é bem ingênuo, por sinal - experimente ler o código gerado pelo compilador da Intel) usa um truque sujo, porque ele sabe que é possível usar uma multiplicação por 0x6666666666666666 e um shift não sei por que valor, para obter o mesmo resultado, só que em bem menos ciclos.

[quote=entanglement]Se bobear fica até mais legível que alguns programas em C que já vi por aí :slight_smile:

O único problema é que macro assemblers não fazem milagres.

Por exemplo, você poderia ter uma condição em que um compilador C bem bobinho iria juntar 2 jumps consecutivos em um só, mas o Macro Assembler não conseguir fazer isso.

Mas como é um “macro assembler” e você mesmo pode definir macros, você pode construir programas bem complexos e usando todos aqueles truques sujos que os compiladores normalmente usam. Um truque sujo que já vi em um compilador é o seguinte: quando você converte um número de binário para decimal, você normalmente dividiria por 10 e obteria o resto da divisão por 10 (usando a instrução DIV ou IDIV). Só que o compilador da Microsoft (que é bem ingênuo, por sinal - experimente ler o código gerado pelo compilador da Intel) usa um truque sujo, porque ele sabe que é possível usar uma multiplicação por 0x6666666666666666 e um shift não sei por que valor, para obter o mesmo resultado, só que em bem menos ciclos.
[/quote]

Legal esses truques que dá pra fazer com macros. Mais legal ainda deve ser a cara do programador que tem que manter esses códigos deve fazer :smiley:

Você deve usar uma chamada de interrupção de teclado, que tem em todo SO para PC e pode ser utilizado em qualquer montador pois não depende do mesmo, no windows é a interrupção 21h, no linux pode passar o parametro 4 para interrupção 80h.

http://www.acm.uiuc.edu/sigwin/old/workshops/winasmtut.pdf

Atenção: int 21h é só válida para o DOS (16 bits), o Windows (32 ou 64 bits) requer chamar as APIs adequadas. Veja um exemplo no PDF que passei acima.

O Windows (32 bits) roda programas DOS (16 bits), mas não o Windows (64 bits).

No Linux, você pode usar Int 80h ou então a instrução SYSENTER :

Obrigado pela ajuda entanglement, faz tempo que não trabalho com assembly, vou aproveitar para ler o conteúdo dos seus links e praticar um pouco mais.

[quote=entanglement]http://www.acm.uiuc.edu/sigwin/old/workshops/winasmtut.pdf

Atenção: int 21h é só válida para o DOS (16 bits), o Windows (32 ou 64 bits) requer chamar as APIs adequadas. Veja um exemplo no PDF que passei acima.

O Windows (32 bits) roda programas DOS (16 bits), mas não o Windows (64 bits).

No Linux, você pode usar Int 80h ou então a instrução SYSENTER :


[/quote]

Em qualquer sistema para cima do win2000 nt existe um artifício de proteção desses registradores. É como uma máquina virtual. Existe uma dll que possui uma api para mapear esses registradores sem precisar acessa-los diretamente. Eu usava inline com o delphi quando precisava mapear discos e teclados.

Qualquer tentativa de acessar interrupção nos sitemas novos resulta na tela azul com um “deny” bem grande.

Para isso existe o ddk xp

http://www.delphigroups.info/2/5/1064692.html

Amigos,
Inicialmente quero agradecer pelas respostas dadas. :thumbup:
Testei algumas interrupções mas não obtive resultados.Paciência.

Aproveitando a oportunidade gostaria de fazer outra pergunta tendo como base o programinha abaixo:

[code].model small
.stack

.code
inicio:

  ;leitura
  mov ah,1h    ;Lê o caracter digitado  
  int 21h      ;Lança a interrupção para o caracter digitado

  ;escrita
  mov dl,al    ; O valor lido em al, vai para dl
  mov ah,2h    ; A função 2h(segundo a tabela) deveria imprimir o caractere em dl
  int 21

 end inicio

[/code]
Este é um programinha simples que deveria ler um caracter do teclado retornando-o em seguida (ECO), mas que não funciona.
Algo semelhante ao

scanf(); printf():
do c.

A versão que funciona é:

[code] .model small
.stack

.code
inicio:

   ;leitura
   mov ah, 2h   ;Não entendo o porque da função de impressão está logo no começo
   mov ah, 1h   ;Lê o caracter digitado  
   int 21h      ;Lança a interrupção para o caracter digitado
   mov dl,al     
  
   ;escrita
   mov ah,4ch   ; a função 4ch deveria finalizar o dos, mas aqui ela estranhamente faz o que o 2h deveria fazer: a impressão.
   int 21h  

 end inicio[/code]

Onde está o erro na primeira versão? O código está bem estruturado, como em um algoritmo simples.
Na segunda versão, o código começa com mov ah,2h que deveria ser usado para impresão, para mim, isto torna o código pouco lógico.Mas o pior é que funciona!

E por fim a linha mov ah, 4ch deveria apenas fechar o programa, mas neste caso a linha esta funcionando como um “printf()”

Alguém gostaria de comentar?.

Hum… realmente você está escrevendo programas de 16 bits que endereçam apenas 64 KB (dá para deduzir isso pela declaração .model small).

Além disso, no Windows 32 bits (exemplo: Windows XP, Windows Vista Starter Edition etc.) eles são executados em um ambiente que simula o antigo MS-DOS de 16 bits.

Infelizmente não tenho aqui nenhum programa que consegue gerar arquivos .com ou .exe de 16 bits - mesmo o ML.EXE (que é o Macro Assembler da Microsoft que vem com o MS Platform SDK) só gera programas de 32 e 64 bits. (Ele até aceitou o .model small - ou seja, conseguiu
entender o seu arquivo .asm - mas não conseguiu gerar um executável de 16 bits. Que pena)

Recentemente joguei fora um livrinho chamado “Advanced MS-DOS” do Ray Duncan, que estava ocupando espaço em casa.

Uma cópia escaneada desse livro pode ser encontrada em: http://fornax.elf.stuba.sk/SUPERMAN/SYSTEMS/DOS/advdos.txt

Você pode tentar ler esse livro para ver como é que se faz para imprimir na tela ou ler alguma coisa do teclado.