; ; file: asm_io.asm ; Assembly I/O routines ; To assemble for DJGPP ; nasm -f coff -d COFF_TYPE asm_io.asm ; To assemble for Borland C++ 5.x ; nasm -f obj -d OBJ_TYPE asm_io.asm ; To assemble for Microsoft Visual Studio ; nasm -f win32 -d COFF_TYPE asm_io.asm ; To assemble for Linux ; nasm -f elf -d ELF_TYPE asm_io.asm ; To assemble for Watcom ; nasm -f obj -d OBJ_TYPE -d WATCOM asm_io.asm ; IMPORTANT NOTES FOR WATCOM ; The Watcom compiler's C library does not use the ; standard C calling convention. For example, the ; putchar() function gets its argument from the ; the value of EAX, not the stack. %define NL 10 %define CF_MASK 00000001h %define PF_MASK 00000004h %define AF_MASK 00000010h %define ZF_MASK 00000040h %define SF_MASK 00000080h %define DF_MASK 00000400h %define OF_MASK 00000800h ; ; Linux C doesn't put underscores on labels ; %ifdef ELF_TYPE %define _scanf scanf %define _printf printf %define _getchar getchar %define _putchar putchar %endif ; ; Watcom puts underscores at end of label ; %ifdef WATCOM %define _scanf scanf_ %define _printf printf_ %define _getchar getchar_ %define _putchar putchar_ %endif %ifdef OBJ_TYPE segment .data public align=4 class=data use32 %else segment .data %endif int_format db "%i", 0 string_format db "%s", 0 reg_format db "Register Dump # %d", NL db "EAX = %.8X EBX = %.8X ECX = %.8X EDX = %.8X", NL db "ESI = %.8X EDI = %.8X EBP = %.8X ESP = %.8X", NL db "EIP = %.8X FLAGS = %.4X %s %s %s %s %s %s %s", NL db 0 carry_flag db "CF", 0 zero_flag db "ZF", 0 sign_flag db "SF", 0 parity_flag db "PF", 0 overflow_flag db "OF", 0 dir_flag db "DF", 0 aux_carry_flag db "AF", 0 unset_flag db " ", 0 mem_format1 db "Memory Dump # %d Address = %.8X", NL, 0 mem_format2 db "%.8X ", 0 mem_format3 db "%.2X ", 0 stack_format db "Stack Dump # %d", NL db "EBP = %.8X ESP = %.8X", NL, 0 stack_line_format db "%+4d %.8X %.8X", NL, 0 math_format1 db "Math Coprocessor Dump # %d Control Word = %.4X" db " Status Word = %.4X", NL, 0 valid_st_format db "ST%d: %.10g", NL, 0 invalid_st_format db "ST%d: Invalid ST", NL, 0 empty_st_format db "ST%d: Empty", NL, 0 ; ; code is put in the _TEXT segment ; %ifdef OBJ_TYPE segment text public align=1 class=code use32 %else segment .text %endif global read_int, print_int, print_string, read_char global print_char, print_nl, sub_dump_regs, sub_dump_mem global sub_dump_math, sub_dump_stack extern _scanf, _printf, _getchar, _putchar read_int: enter 4,0 pusha pushf lea eax, [ebp-4] push eax push dword int_format call _scanf pop ecx pop ecx popf popa mov eax, [ebp-4] leave ret print_int: enter 0,0 pusha pushf push eax push dword int_format call _printf pop ecx pop ecx popf popa leave ret print_string: enter 0,0 pusha pushf push eax push dword string_format call _printf pop ecx pop ecx popf popa leave ret read_char: enter 4,0 pusha pushf call _getchar mov [ebp-4], eax popf popa mov eax, [ebp-4] leave ret print_char: enter 0,0 pusha pushf %ifndef WATCOM push eax %endif call _putchar %ifndef WATCOM pop ecx %endif popf popa leave ret print_nl: enter 0,0 pusha pushf %ifdef WATCOM mov eax, 10 ; WATCOM doesn't use the stack here %else push dword 10 ; 10 == ASCII code for \n %endif call _putchar %ifndef WATCOM pop ecx %endif popf popa leave ret sub_dump_regs: enter 4,0 pusha pushf mov eax, [esp] ; read FLAGS back off stack mov [ebp-4], eax ; save flags ; ; show which FLAGS are set ; test eax, CF_MASK jz cf_off mov eax, carry_flag jmp short push_cf cf_off: mov eax, unset_flag push_cf: push eax test dword [ebp-4], PF_MASK jz pf_off mov eax, parity_flag jmp short push_pf pf_off: mov eax, unset_flag push_pf: push eax test dword [ebp-4], AF_MASK jz af_off mov eax, aux_carry_flag jmp short push_af af_off: mov eax, unset_flag push_af: push eax test dword [ebp-4], ZF_MASK jz zf_off mov eax, zero_flag jmp short push_zf zf_off: mov eax, unset_flag push_zf: push eax test dword [ebp-4], SF_MASK jz sf_off mov eax, sign_flag jmp short push_sf sf_off: mov eax, unset_flag push_sf: push eax test dword [ebp-4], DF_MASK jz df_off mov eax, dir_flag jmp short push_df df_off: mov eax, unset_flag push_df: push eax test dword [ebp-4], OF_MASK jz of_off mov eax, overflow_flag jmp short push_of of_off: mov eax, unset_flag push_of: push eax push dword [ebp-4] ; FLAGS mov eax, [ebp+4] sub eax, 10 ; EIP on stack is 10 bytes ahead of orig push eax ; EIP lea eax, [ebp+12] push eax ; original ESP push dword [ebp] ; original EBP push edi push esi push edx push ecx push ebx push dword [ebp-8] ; original EAX push dword [ebp+8] ; # of dump push dword reg_format call _printf add esp, 76 popf popa leave ret 4 sub_dump_stack: enter 0,0 pusha pushf lea eax, [ebp+20] push eax ; original ESP push dword [ebp] ; original EBP push dword [ebp+8] ; # of dump push dword stack_format call _printf add esp, 16 mov ebx, [ebp] ; ebx = original ebp mov eax, [ebp+16] ; eax = # dwords above ebp shl eax, 2 ; eax *= 4 add ebx, eax ; ebx = & highest dword in stack to display mov edx, [ebp+16] mov ecx, edx add ecx, [ebp+12] inc ecx ; ecx = # of dwords to display stack_line_loop: push edx push ecx ; save ecx & edx push dword [ebx] ; value on stack push ebx ; address of value on stack mov eax, edx sal eax, 2 ; eax = 4*edx push eax ; offset from ebp push dword stack_line_format call _printf add esp, 16 pop ecx pop edx sub ebx, 4 dec edx loop stack_line_loop popf popa leave ret 12 sub_dump_mem: enter 0,0 pusha pushf push dword [ebp+12] push dword [ebp+16] push dword mem_format1 call _printf add esp, 12 mov esi, [ebp+12] ; address and esi, 0FFFFFFF0h ; move to start of paragraph mov ecx, [ebp+8] inc ecx mem_outer_loop: push ecx push esi push dword mem_format2 call _printf add esp, 8 xor ebx, ebx mem_hex_loop: xor eax, eax mov al, [esi + ebx] push eax push dword mem_format3 call _printf add esp, 8 inc ebx cmp ebx, 16 jl mem_hex_loop mov eax, '"' call print_char xor ebx, ebx mem_char_loop: xor eax, eax mov al, [esi+ebx] cmp al, 32 jl non_printable cmp al, 126 jg non_printable jmp short mem_char_loop_continue non_printable: mov eax, '?' mem_char_loop_continue: call print_char inc ebx cmp ebx, 16 jl mem_char_loop mov eax, '"' call print_char call print_nl add esi, 16 pop ecx loop mem_outer_loop popf popa leave ret 12 ; function sub_dump_math ; prints out state of math coprocessor without modifying the coprocessor ; or regular processor state ; Parameters: ; dump number - dword at [ebp+8] ; Local variables: ; ebp-108 start of fsave buffer ; ebp-116 temp double ; Notes: This procedure uses the Pascal convention. ; fsave buffer structure: ; ebp-108 control word ; ebp-104 status word ; ebp-100 tag word ; ebp-80 ST0 ; ebp-70 ST1 ; ebp-60 ST2 ... ; ebp-10 ST7 ; sub_dump_math: enter 116,0 pusha pushf fsave [ebp-108] ; save coprocessor state to memory mov eax, [ebp-104] ; status word and eax, 0FFFFh push eax mov eax, [ebp-108] ; control word and eax, 0FFFFh push eax push dword [ebp+8] push dword math_format1 call _printf add esp, 16 ; ; rotate tag word so that tags in same order as numbers are ; in the stack ; mov cx, [ebp-104] ; ax = status word shr cx, 11 and cx, 7 ; cl = physical state of number on stack top mov bx, [ebp-100] ; bx = tag word shl cl,1 ; cl *= 2 ror bx, cl ; move top of stack tag to lowest bits mov edi, 0 ; edi = stack number of number lea esi, [ebp-80] ; esi = address of ST0 mov ecx, 8 ; ecx = loop counter tag_loop: push ecx mov ax, 3 and ax, bx ; ax = current tag or ax, ax ; 00 -> valid number je valid_st cmp ax, 1 ; 01 -> zero je zero_st cmp ax, 2 ; 10 -> invalid number je invalid_st push edi ; 11 -> empty push dword empty_st_format call _printf add esp, 8 jmp short cont_tag_loop zero_st: fldz jmp short print_real valid_st: fld tword [esi] print_real: fstp qword [ebp-116] push dword [ebp-112] push dword [ebp-116] push edi push dword valid_st_format call _printf add esp, 16 jmp short cont_tag_loop invalid_st: push edi push dword invalid_st_format call _printf add esp, 8 cont_tag_loop: ror bx, 2 ; mov next tag into lowest bits inc edi add esi, 10 ; mov to next number on stack pop ecx loop tag_loop frstor [ebp-108] ; restore coprocessor state popf popa leave ret 4