3

hi i need help displaying contents of a register.my code is below.i have been able to display values of the data register but i want to display flag states. eg 1 or 0. and it would be helpful if to display also the contents of other registers like esi,ebp.

my code is not printing the states of the flags ..what am i missing

section .text
global _start       ;must be declared for using gcc
_start :                     ;tell linker entry point

mov eax,msg         ; moves message "rubi" to eax register
mov [reg],eax       ; moves message from eax to reg variable


mov edx, 8   ;message length
mov ecx, [reg];message to write
mov ebx, 1      ;file descriptor (stdout)
mov eax, 4      ;system call number (sys_write)
int 0x80        ;call kernel

mov eax, 100
mov ebx, 100
cmp ebx,eax

pushf
pop dword eax

mov [save_flags],eax

mov edx, 8        ;message length
mov ecx,[save_flags]         ;message to write
mov ebx, 1          ;file descriptor (stdout)
mov eax, 4          ;system call number (sys_write)
int 0x80     


mov eax, 1      ;system call number (sys_exit)
int 0x80        ;call kernel

section .data

msg db "rubi",10

section .bss

reg resb 100

save_flags resw 100
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
hardeep
  • 93
  • 2
  • 12
  • *sys_write* doesn't take integers to print. You need to convert *save_flags* to a string and then use *sys_write* pointing it at the string buffer containing the converted value. – Michael Petch Dec 01 '15 at 22:05
  • how do i do that is there a kind of example to help part of code – hardeep Dec 01 '15 at 22:13
  • 1
    Just for the record, for practical use you should just use a debugger like gdb to examine registers. See http://stackoverflow.com/tags/x86/info for some notes on how to use gdb to debug asm (layout asm / layout reg works well) – Peter Cordes Dec 02 '15 at 14:55

2 Answers2

3

I'm not going for anything fancy here since this appears to be a homework assignment (two people have asked the same question today). This code should be made as a function, and it can have its performance enhanced. Since I don't get an honorary degree or an A in the class it doesn't make sense to me to offer the best solution, but one you can work from:

    BITS_TO_DISPLAY equ 32       ; Number of least significant bits to display (1-32)

section .text
global _start                    ; must be declared for using gcc
_start :                         ;    tell linker entry point

    mov edx, msg_len             ; message length
    mov ecx, msg                 ; message to write
    mov ebx, 1                   ; file descriptor (stdout)
    mov eax, 4                   ; system call number (sys_write)
    int 0x80                     ; call kernel

    mov eax, 100
    mov ebx, 100
    cmp ebx,eax

    pushf
    pop dword eax

    ; Convert binary to string by shifting the right most bit off EAX into
    ; the carry flag (CF) and convert the bit into a '0' or '1' and place
    ; in the save_flags buffer in reverse order. Nul terminate the string
    ; in the event you ever wish to use printf to print it

    mov ecx, BITS_TO_DISPLAY     ; Number of bits of EAX register to display
    mov byte [save_flags+ecx], 0 ; Nul terminate binary string in case we use printf
bin2ascii:
    xor bl, bl                   ; BL = 0
    shr eax, 1                   ; Shift right most bit into carry flag
    adc bl, '0'                  ; bl = bl + '0' + Carry Flag
    mov [save_flags-1+ecx], bl   ; Place '0'/'1' into string buffer in reverse order
    dec ecx
    jnz bin2ascii                ; Loop until all bits processed

    mov edx, BITS_TO_DISPLAY     ; message length
    mov ecx, save_flags          ; address of binary string to write
    mov ebx, 1                   ; file descriptor (stdout)
    mov eax, 4                   ; system call number (sys_write)
    int 0x80

    mov eax, 1                   ;system call number (sys_exit)
    int 0x80                     ;call kernel

section .data
msg db "rubi",10
msg_len equ $ - msg

section .bss
save_flags resb BITS_TO_DISPLAY+1 ; Add one byte for nul terminator in case we use printf

The idea behind this code is that we continually shift the bits (using the SHR instruction) in the EAX register to the right one bit at a time. The bit that gets shifted out of the register gets placed in the carry flag (CF). We can use ADC to add the value of the carry flag (0/1) to ASCII '0' to get an ASCII value of '0` and '1'. We place these bytes into destination buffer in reverse order since we are moving from right to left through the bits.

BITS_TO_DISPLAY can be set between 1 and 32 (since this is 32-bit code). If you are interested in the bottom 8 bits of a register set it to 8. If you want to display all the bits of a 32-bit register, specify 32.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • I like the `adc` with `'0'` idea. Too bad it doesn't save any instructions over `setc` / `add reg, '0'` :/. I also wonder if it would be a win to `push` onto the stack, and then `write(esp)`. Prob. not, since you want to zero-pad the result, rather than stopping once `shr` sets ZF. (You'd save the value of esp from before a variable-iteration push loop, so you could restore the way you do in a function with a stack frame.) – Peter Cordes Dec 02 '15 at 17:13
  • 1
    Putting the buffer on the stack is the way I would have done it ;) (especially if it was converted to a re-entrant function) but I leave that as an exercise to the student who wishes to earn the better grade. It should also be a function (or macro) since they wish to reuse it for other registers. I leave that as an exercise to the student too. There is a point where I draw the line with homework. And using setc would have been my preference. I supplied it this way in the event some student comes along and might think of writing a 16 bit 8086/8088 variant – Michael Petch Dec 02 '15 at 17:14
  • Yeah, I think talking about ideas is if anything helpful, but actually writing code that can be copied without understanding it is not. I'm surprised you went and wrote base2-string printing code for him, since I'm sure this has come up before and a search would prob. find something. Anyway, I said "I wonder if", rather than "you should have" for a reason. :P – Peter Cordes Dec 02 '15 at 17:20
  • 1
    @PeterCordes I went looking for a variant that was NASM/Ascii to binary/32-bit Linux and didn't see it. I thought one existed. Had I found it I would have marked the entire question as a duplicate. If someone does find one on here I'd be the first person to vote to close. – Michael Petch Dec 02 '15 at 17:22
  • 1
    I feel like assembly language is treated way different on SO from other languages. On most questions, you can refer someone to an answer in another language, and they can at least treat it as pseudocode and implement it in the language they want. People *asking* questions often fall into the trap of trying to "think in assembly language" when they don't know it yet, instead of thinking in C (or just about what has to happen to the data) for an algo, and then translating to asm. There are too many "teach me asm, I have 1/3rd of a clue" asm questions, IMO. – Peter Cordes Dec 02 '15 at 17:35
  • I took this approach for the [linked-lists question from earlier](http://stackoverflow.com/a/34035113/224132). Start off explicitly recommending to think it through in a language you know, then some C and asm examples. A little bit of code, mostly English, with a link to some clean linked-list functions (actually written in Java, but in a way that looks like how you'd do it in C). – Peter Cordes Dec 02 '15 at 17:37
  • @PeterCordes : Absolutely, I agree. If I had my way, I'd answer all the homework questions in pseudo code, but the chances of getting an accepted answer IMHO drops, and then you get a bunch of questions never marked as solved because the students are too lazy to think for themselves ;) Tight rope walk between doing enough to get it solved without actually handing them the answer on a silver platter. – Michael Petch Dec 02 '15 at 17:39
  • @PeterCordes : The version I really wanted to use was the one with the `loop` instruction - just for you! But I figured I'd never hear the end of it lol – Michael Petch Dec 02 '15 at 17:45
  • Unanswered but negative-score questions don't pollute the site too badly. The other day I answered the ["find max of an array" vague question](http://stackoverflow.com/questions/34018519/how-to-compare-elements-in-array-in-assembly/34023537#34023537) in the least helpful-to-the-OP way I could, while still answering the question, just because I wanted to show the OP how broad it was. And I got two upvotes for it. I guess people like loops that saturate the execution ports that can run `PMAXSD`. :P My standpoint is that there's no point writing in asm other than performance. – Peter Cordes Dec 02 '15 at 17:46
  • 1
    Can you please explain why not using `pushfd` before a 32-bit pop? – Weather Vane Dec 02 '15 at 17:48
  • @PeterCordes : From a business perspective there is no reason to use pure assembler unless you are in the rare situation where there isn't a compiler for the target (not likely), or you need to generate a boot loader, there is an optimization a compiler won't do etc. Unfortunately, most of the assembler questions on SO are for academia where assembler is a teaching device. In that case it usually isn't for performance reasons. If SO hadn't become an online debugging tool for students we probably wouldn't be having this convo! – Michael Petch Dec 02 '15 at 17:51
  • @PeterCordes I see thank you. I was looking at my old MASM book which says *When used from a 32-bit code segement, PUSHF does not automatically transfer 32-bit values.* – Weather Vane Dec 02 '15 at 17:52
  • @MichaelPetch: Yeah, I get that when *learning* asm, you won't be getting every drop of performance. And it's useful to know some assembler to help with debugging / understanding stuff, even if you don't know how to optimize it. It still bugs me when people are learning these horrible idioms (like `loop`). Why learn to write code that's nothing like what you'll read from compiler output or from hand-written asm? Even worse when people learn on 8086, with `movsx/movzx`. And 16bit in general with its horribly restrictive register selection for addressing modes... – Peter Cordes Dec 02 '15 at 17:54
  • @WeatherVane: I read the Intel ref manual's paragraph again more carefully: `pushfd` is "intended for use when the operand size attribute is 32". But it does warn that some assemblers will assemble `pushfd` to `9C` without an operand-size prefix in 16bit mode. (i.e. they treat it as a synonym for `pushf`). So you actually make a good point. And I goofed, there's no `pushfw` to explicitly request 16bit size (deleted my old misleading comment). Some assemblers will assemble `pushf` to `66 9C` in 32bit mode, causing a 16b push. – Peter Cordes Dec 02 '15 at 17:58
  • 1
    @PeterCordes With a 32-bit target (or use32) _NASM_ will (or should unless there is a bug) encode it as opcode 9C (with no size prefix). I made the assumption that the target of the OPs code was 32-bit, and assumed it was safe to simply use `PUSHF` as the original OP had used. – Michael Petch Dec 02 '15 at 18:09
  • 1
    @MichaelPetch thanks. Apart from being a different assembler, my ancient MASM book (1987!) refers to 80386 mostly as exceptional differences to the main 16-bit thrust of the edition. – Weather Vane Dec 02 '15 at 18:14
  • @WeatherVane: I usually have Intel's PDF of the insn set ref manual open, so I can alt-tab to it and find an insn in seconds. (link in the x86 tag wiki). It has exact detailed descriptions of what happens to every bit, and to the flags, for every instruction. If I had to choose between it and the online intrinsics guide, I'd take the PDF any day. The intrinsics guide has the occasional bug, and isn't as detailed in describing the vector insns. (and of course doesn't describe insns that don't have intrinsics.) – Peter Cordes Dec 02 '15 at 18:19
2

Note that you can pop directly into memory.

And if you want to binary dump register and flag data with write(2), your system call needs to pass a pointer to the buffer, not the data itself. Use a mov-immediate to get the address into the register, rather than doing a load. Or lea to use a RIP-relative addressing mode. Or pass a pointer to where it's sitting on the stack, instead of copying it to a global!

mov edx, 8        ;message length
mov ecx,[save_flags]         ;message to write    ;;;;;;; <<<--- problem
mov ebx, 1          ;file descriptor (stdout)
mov eax, 4          ;system call number (sys_write)
int 0x80     

Passing a bad address to write(2) won't cause your program to receive a SIGSEGV, like it would if you used that address in user-space. Instead, write will return EFAULT. And you're not checking the return status from your system calls, so your code doesn't notice.


mov eax,msg         ; moves message "rubi" to eax register
mov [reg],eax       ; moves message from eax to reg variable

mov ecx, [reg];

This is silly. You should just mov ecx, msg to get the address of msg into ecx, rather than bouncing it through memory.


Are you building for 64bit? I see you're using 8 bytes for a message length. If so, you should be using the 64bit function call ABI (with syscall, not int 0x80). The system-call numbers are different. See the table in one of the links at . The 32bit ABI can only accept 32bit pointers. You will have a problem if you try to pass a pointer that has any of the high32 bits set.


You're probably also going to want to format the number into a string, unless you want to pipe your program's output into hexdump.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • hi its not crashing but its not printing the states of the flags like 0,1.. im not getting the display but its displaying rubi from data register. – hardeep Dec 02 '15 at 06:25
  • @hardeep: I'd suggest single-stepping with a debugger, and/or run it under `strace` to see what data actually got passed to the system call. – Peter Cordes Dec 02 '15 at 07:19
  • @hardeep: oh right, I forgot that passing a bad address to `write(2)` won't cause your program to receive a `SIGSEGV`. Instead, `write` will return `EFAULT`. And you're not checking the return status from your system calls. – Peter Cordes Dec 02 '15 at 14:39
  • @PeterCordes : He's not trying to print the binary values, he wants to print the digits 0 or 1 to the console representing the bits of the flags registers. Sounds like he wants to convert _EFLAGS_ to a binary string – Michael Petch Dec 02 '15 at 14:50
  • 1
    @MichaelPetch: Oh right. He should write some code to do that, now that we've shown him why he wasn't getting any output. :P – Peter Cordes Dec 02 '15 at 14:52
  • @MichaelPetch: mov [save_flags-1+ecx], bl .what does -1 and + ecx do – hardeep Dec 04 '15 at 09:56
  • @hardeep: did you mean to leave this comment on MichaelPetch's answer? Does http://stackoverflow.com/questions/34058101/referencing-the-contents-of-a-memory-location-x86-addressing-modes/34058400#34058400 help? – Peter Cordes Dec 04 '15 at 12:58
  • @hardeep *save_flags* is a pointer to the beginning of the string I store the binary string in. I originally set _ECX_ to be the position(index) of the last character in the string where I place the nul terminator (0). [save_flags-1+ecx] is indirect addressing. It could have been written as [save_flags+ecx-1]. The first time in the loop that location is the very last position before the nul terminator (-1 from the end). Each time through the loop I decrement _ECX_ to move one character back in the array. – Michael Petch Dec 04 '15 at 14:57
  • @hardeep The square brackets say that I am interested what is at the memory location pointed to by *[save_flags+ecx-1]*. Thus the mov bl simply moves the character in bl into the memory address pointed to by *save_flags+ecx-1* – Michael Petch Dec 04 '15 at 15:00
  • @MichaelPetch ok . and whats the use of adc bl , '0' and why are we shifting right most flag into the carry flag? – hardeep Dec 16 '15 at 23:11
  • @hardeep `shr eax, 1` will shift the _EAX_ register to the right one bit. The least significant bit will fall out of _EAX_ but it is placed into the Carry Flag(CF). So if the right most bit was 1, CF=1. If right most bit was 0 then CF=0. _BL_ is originally set to 0, so a normal add would have added Ascii '0' to _BL_ making BL='0' . With ADC (Add Carry) BL = BL(0) + '0' + CF . So if the CF is 0 then the ASCII character in BL will end up being '0', but if CF was 1 then BL will end up being '1' . – Michael Petch Dec 16 '15 at 23:19
  • @hardeep: questions about MichaelPetch's answer should be posted as comments on it, not mine. `adc bl, '0'` does `bl = bl + '0' + CF`, converting to ASCII. – Peter Cordes Dec 16 '15 at 23:19
  • @PeterCordes : Sorry peter didn't notice he asked in your answer lol – Michael Petch Dec 16 '15 at 23:19