Author Topic: interrupt  (Read 18964 times)

Offline ProblemHelp

  • Jr. Member
  • *
  • Posts: 8
interrupt
« on: December 04, 2011, 05:50:44 PM »
Hello! first I apologise for my bad english:)
I was sitting behind the computer the whole day and I didn't find a solution, so I decided to come here. Please, please, please help me. I'm new in nasm, so I know very little about it =/ so, i started with a small example. it's about bios call interrupt. in wikipedia I found this:
mov ah, 0x0e
mov al, '!'
int 0x10
and this should print a character '!'. i work in windows7 (mingw, nasm 2.09.10). so i write this code in my program and this don't work...i really don't know why, I just think that something is missed and I don't know what. if someone could tell me where can I read about this, if there are any other examples, please tell me. I don't want that someone give me the code, i beg for simple explanation. My homework is, that i "printf" and "scanf" with interrupt call int 10h the characters and then convert them in big character.  also if anybody know about some book or link  about nasm (simple examples or explanation) i would be very grateful:)
Thank you!!
Talijana

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: interrupt
« Reply #1 on: December 04, 2011, 08:28:44 PM »
Hi Talijana,

Older versions of Windows supported 16-bit code, newer versions do not. I'm pretty sure (not certain) that "Windows 7" does not (if it's 64-bit, definitely not). BIOS interrupts (and dos interrupts) are 16-bit code, and just won't work in 32- or 64-bit CPU modes.

Your best bet is probably to find and install a program called "dosbox". Running "freedos", either as a "dual boot" system or in a virtual machine (VMWare or others) would also work. I think "dosbox" is probably easier(?).

Depending on your video BIOS, int 10h/0Eh might possibly be sensitive to the value in bx - bh is "video page" and bl (less likely) would be "attribute" (color). Just to be "safe", I like to add "mov bx, 7" to the code you show...

Code: [Select]
; nasm -f bin myfile.asm -o myfile.com

org 100h
    mov bx, 7
    mov ah 0x0E
    mov al, '!'
    int 0x10
; we also need to "exit" cleanly
; just "ret" would also work
    mov ah, 0x4C
    mov al, 0 ; "exit code" or "errorlevel"
    int 0x21

If that doesn't solve the problem, get back to us!

Best,
Frank


Offline ProblemHelp

  • Jr. Member
  • *
  • Posts: 8
Re: interrupt
« Reply #2 on: December 04, 2011, 08:57:30 PM »
Thank you very much for your help!
So, I probably have to copy DOSBox folder somewhere? I saved the file in the path: nasm \ mingw \ bin \ file.asm. because I haven't worked in DOSBox yet I have some questions ... So I can normally write code in Notepad, but instead of 32 bits I use 16 bits? And then the command is to translate just like you wrote in the first line? Nobody in school mentioned that this doesn't work in win7, so I wonder if there are any other commands?
Thank you again, I do not know what I would do without you:)

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: interrupt
« Reply #3 on: December 04, 2011, 10:28:59 PM »
No problem.

Quote
So I can normally write code in Notepad, but instead of 32 bits I use 16 bits?

Right. Notepad will, in some cases, save your "myfile.asm" as "myfile.asm.txt". Also, in some cases, Notepad will save as unicode, rather than as plain ascii text. You may need to poke around in Notepad's configuation to avoid that. I'm afraid I don't know how to do it.

Quote
And then the command is to translate just like you wrote in the first line?

Right. Nasm will produce several output formats, controlled by the "-f" switch. "-f bin" is the default, so you don't really need to use it, but I think it's a good habit to always specify the output format. The "-o" switch controls only the name of the output file. It doesn't "make it a .com file". For most output formats, the default name is okay (myfile.obj or myfile.o), but in "-f bin" output, the name would just be "myfile" by default, so you need to use it (or rename "myfile"...). The "org 100h" (you might prefer "org 0x100"... or "org 256") does not control where the file is loaded, but merely informs Nasm where it will be loaded (dos always loads a .com file at 0x100). Your simple example would work without it (it is "Position Independent Code"), but as soon as you access memory, you'll need it.

Quote
Nobody in school mentioned that this doesn't work in win7, so I wonder if there are any other commands?

Might be. I thought "XT" was the last Windows version that supported dos (16-bit) code, but I'm not sure. (try it: Windows will report "binary format not supported" if it isn't) I haven't run Windows since "98". At that time, you could change "GUI=1" to "GUI=0" in "msdos.sys", and boot to "real dos". That hasn't worked for a long time...

I've switched to Linux, which (speaking of interrupts) uses int 80h (or 0x80). It's a pretty nice platform on which to learn assembly language (IMO), but is probably more of a change than you want to make. If your homework involves int 0x10 (or other BIOS/dos interrupts), you'll need dos support.

Best,
Frank


Offline ProblemHelp

  • Jr. Member
  • *
  • Posts: 8
Re: interrupt
« Reply #4 on: December 05, 2011, 12:57:00 PM »
Thank you very much for your help, but it doesn't work...:/dosbox "says" ->this program can not be run in dos mode. I have copied the nasm folder into dos emulator-that's ok or not? I decided that I will write a program on my own, on a paper and ask for help also my teacher (what should I do with my computer/program). just one question about my homework...i must also read set of signs, for example "word" with interrupt but in int 10h i found only this: Read Character and Attribute at Cursor -09h. so if i think in the right way, that mean, that i can just read one character, but not the whole word. So if i read 'z', I must write 'Z'. Only if I take the the first character in the "word" -'w'-but the whole word I must than read with scanf (for example
                   
Code: [Select]
mov eax, 0 ;counter
                     mov esi, dword [esp+4]
                     .loop:
                     mov bl, byte[esi]
                     cmp bl, 'a' and so on
than we shoud change it with 'A'. But to do that in this way is very slowly, so i need somehow bl compare between 'a' and 'z' and I know the ascii code for 'a' is 97, for 'z' is 122 ...so I need just instrustion for "between". in c++ is that so easy:)
I'm right in these things i wrote?
Thank you again for your help, answers and patience.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: interrupt
« Reply #5 on: December 05, 2011, 10:05:02 PM »
Hi Talijana,

I think asking your teacher for help is an excellent idea! Presumably, you're paying good money to take this course, so you really shouldn't need to ask for help here - although you're welcome to.

You seem to have several unrelated problems. The first thing is to get your "tools" installed and working. Traditionally, a program that just prints "hello world" is used for this purpose, but your program that just prints "!" will do the job.

When does Dosbox say "this program can not be run in dos mode"? When you try to run your program? When you try to run Nasm? Maybe Notepad? There may be two solutions to this problem. One would be to edit and assemble your program in Windows, and just run the output ("myfile.com" or whatever) in Dosbox. The other way would be to use the dos build of Nasm (and some editor? maybe Dosbox comes with one?) in Dosbox. I don't know which would be easier.

You mention copying folders around. You might be able to get it to work that way, but what you probably want to do is to set the "PATH" environment variable to include wherever you've got Nasm installed - "C:\nasm" perhaps, or maybe it installs under "C:\Program Files\nasm". Directory (folder) names with a space in 'em can cause trouble, even though it's "legal". I would avoid it. In the "good old days", this was set in a file named "autoexec.bat". Dosbox probably (just guessing) does it that way. Windows... I think right-click on the "shortcut", select "properties" and perhaps "advanced"... There's probably an easier way to do it. This is an "OS question", and doesn't have anything to do with assembly language but you still need to know how to do it.

Once you've got your tools installed, and can edit, assemble, and run some program - any program - then you're ready to start learning assembly language. This may be the easy part! :)

"printf" and "scanf" are complicated functions. They can be written in assembly, but it's not a job for a beginner! Maybe something more like "puts" and... well "gets", but as you know (I hope) "gets" is considered dangerous as it allows a buffer overrun, which can be exploited. You want to NOT allow a buffer overrun! Call it "ngets", although there's no such C function. The latest code you posted includes a "counter". That's the right idea (although I don't think eax is a good choice of register).

Start with something even simpler - don't try to "capture" input, but just get and process one character at a time. Int 10h is the video interrupt - int 10h/9 reads character and attribute of a character that's already on the screen - not what you want. To get keyboard input, you want the keyboard interrupt, int 16h. There's a dos interrupt that implements "ngets", int 21h/0Ah, but I get the impression that your homework assignment is to "do it yourself" Use if you can... but that comes later...

There's no "between" instruction. It might be useful to think of it as "not between", but even that will require several instructions. Just to illustrate...

Code: [Select]
; nasm -f bin upper01.asm -o upper01.com

org 100h

begin:
; get a character from the keyboard
    mov ah, 0
    int 16h
; now we've got a character in al
; quit if user hits "ESC"?
    cmp al, 1Bh ; I think ESC is 1Bh?
    je exit ; jump if equal
; see if it needs to be uppercased
    cmp al, 'a'
    jb skip ; jump if below
    cmp al, 'z'
    ja skip ; jump if above
; if we get here, it's "between"
; convert to uppercase
    and al, 0DFh ; or add 32 (20h)
skip:
; print the character
    mov ah, 0Eh
    mov bx, 7 ; ? may not be needed
    int 10h
; go do more
    jmp begin

exit:
; we're all done
    ret

That's not tested and may contain errors. Hopefully it's close enough so you can get it to work. But first, you've got to get an editor and Nasm and Dosbox all playing nicely together! While you're talking to your teacher, find out if you're allowed to use dos interrupts, if the "assignment" doesn't make it clear. If not, we can discuss how to write an equivalent using BIOS interrupts, which doesn't allow a buffer overrun.

That last bit of code you posted looks like the right idea, but looks like 32-bit code, so may not be exactly what you want.

Best,
Frank


Offline ProblemHelp

  • Jr. Member
  • *
  • Posts: 8
Re: interrupt
« Reply #6 on: December 06, 2011, 03:39:21 PM »
Thank you a lot for your explaination:)  here the point: we just get this homework, and they didn't say that we must do in 16 bit code. They just give us a link wikipedia:http://en.wikipedia.org/wiki/BIOS_interrupt_call
and the instruction what we should do: "In assembly language, write function "upper" that converts a string to uppercase. Use the dedicated instructions and registers. To read and write call interrupts using instruction int (e.g. int 10h). In the main program show usage of the functions."
 So, we must use the bios call from this list. I had problem already with installing nasm, because i have 64 bits win7, so they help me with the mingw. But in our school is different, we don't have a lot time for examples, so I must do "everything" on my own and helping with the internet and there is a lot of literature and no time for everything (we don't have to pay school, because I'm a "regular student"). So, for me is important that I know how to write the code/program on the paper,(even if the program is not exactly right-it's also good if i know how to begin, if you know what I mean:)),  because next week we have exam, it's bad just i can't see if i write good, because of my problems. I hope that tomorrow we will solve this problem:). From your good explaination i had 2 ideas:first one is simple, like you told me, step by step, one character and so on...in second i tried somehow with string, maybe the idea is good, but i think that i did something wrong in "word" or sub al, 32...
Code: [Select]
toupper:
mov ah, 0
int 16h   ;reading from kb

cmp al, 'a'
jge .big  ;if al is or bigger than 'a'-97
jmp .print  ;if not

.big:
sub al, 32
jmp .print

.print:
mov ah, 0x0e
int 0x10

main:
pushad
call toupper
popad
ret

Code: [Select]
section .data
string db "word"

section .text
upper:
mov esi, dword [esp]
mov ah, 0x08   
mov al, byte [esi] ;one character
int 0x10   ;i'm not sure if that works-to read character which is on screen already ("word")

cmp al, 'a'
jge .big  ;if al is or bigger than 'a'-97
jmp .print  ;if not

.big:
sub al, 32
jmp .print

.print:
mov ah, 0x0e
int 0x10

add esi, 1 ;next character
loop .upper ;again

main:
pushad
push dword string
call upper
add esp, 4
popad
ret

I really give my best, I hope that something that I wrote is good:)
Big thank you, I really appreciate people who know this "programming" because is't not easy at all!
Talijana

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: interrupt
« Reply #7 on: December 06, 2011, 11:57:20 PM »
http://en.wikipedia.org/wiki/BIOS_interrupt_call

Pretty good explanation (pretty girl, too!), but it does not mention that these BIOS interrupts are 16-bit code. They are, and they just won't work in 32-bit code (trust me - I've tried it!). I haven't tried 64-bit code, but they won't work there, either.

This puts you in a bad situation - kinda like having one foot in each of two canoes. :) Your proposed code has got the "right idea", but there are some errors, which you would easily find if you could try 'em.

I suppose the place to start is with "entrypoint". You seem to assume that "main" is the entrypoint. This is true in some cases, but not all. In particular, in a .com executable format, the entrypoint is always the beginning of the file - at "section .text". (Nasm will move "section .data" and "section .bss", if you have one, to the end of the file) Your first example doesn't say "section .text", but Nasm will default to that if you don't say. So the very first thing that is executed will be your "toupper" subroutine. Well... since it doesn't end in "ret" it isn't really a "subroutine". This will work (I think - I'm not testing it either!) for the first character. After printing the first character, you'll "fall through" to "main". pushad and popad push and pop 32-bit registers. This ("probably") won't do any harm, but won't do any good either - I'd use just pusha/popa in 16-bit code (which you're stuck with because of the BIOS interrupts). Then you'll "call toupper", which will get and print the second character... and this will go on "forever", without ever hitting the popa(d) or the "ret" in "main". It may appear to work, but it isn't really doing what you intend! You can fix this by adding "jmp main" (first line of the file in your first example, right after "section .text" in your second example) - and put "ret" at the end of your subroutine! As written, it'll only do one character, but it's a start.

Your second example is a mixture of 16- and 32-bit code, and has some other problems...

Code: [Select]
section .data
string db "word"

As I mentioned, Nasm will move this to the end, so it won't get executed. However, in order to find "string", Nasm will need to be told "org 0x100" (for a .com file)! Also, you might want to terminate "word" with a zero, or otherwise indicate the length, so "upper" knows when it's done.

Code: [Select]
section .text
jmp main ; <--- so we start at "main"!
upper:
mov esi, dword [esp]

This isn't right! You pushed "string", and it was at "[esp]" at that point, but "call" pushed the return address on the stack! It may not be "obvious" that call and ret use the stack, but they do! "[esp + 2]" might work - if you're lucky...

Code: [Select]
mov ah, 0x08   
mov al, byte [esi] ;one character
int 0x10   ;i'm not sure if that works-to read character which is on screen already ("word")

No, this won't work.

Code: [Select]
cmp al, 'a'
jge .big  ;if al is or bigger than 'a'-97
jmp .print  ;if not

There are a couple of subtle errors here. For one thing, you've avoided trying to uppercase anything less than 'a', which is good, but there are a few characters beyond 'z' ('{', '|', '}', and '~') which you also don't want to convert to uppercase. You probably won't encounter any of these characters in your testing, so the bug will never show up, but it's still a bug!

The other thing is even more subtle. "jg" and "jl" test for a signed comparison. 0xFF is treated as -1, and is "less" than zero (or 'a'). "ja" and "jb" are for unsigned comparisons. You will probably never encounter anything that would be "negative" (if treated as signed) in this code, so it won't do any harm, but it still isn't really "right". "jae" would be better. These issues occur in your first example, too. Not too big a deal...

Code: [Select]
.big:
sub al, 32
jmp .print

.print:

Without the "jmp", you'd "fall through" into ".print" anyway. Does no harm, but it's not needed.

Code: [Select]
mov ah, 0x0e
int 0x10

add esi, 1 ;next character
loop .upper ;again

The "loop" instruction works on cx. Since we don't know what's in cx, this will loop an indeterminate number of times. If the length of "word" was in cx, it would work, or if "word" were zero-terminated, we could check for that (instead of using "loop").

Again, you've failed to end your subroutine with "ret"!

Code: [Select]
main:
pushad
push dword string
call upper
add esp, 4
popad
ret

This is okay, except for being a mixture of 32- and 16-bit code. It'll ("probably") work okay anyway.

There's a "gotcha" in converting this to straight 16-bit code. While "mov esi, [esp]" is a legal instruction (although it wants to be "[esp + 2]"), "mov si, [sp]" is not! Addressing modes are quite limited in 16-bit code. :( You can use 32-bit instructions in 16-bit code (and get the 32-bit addressing modes - nice!), but there's a potential "gotcha" there, too. If the "offset" exceeds 64k, it will cause a "segment overrun exception" which dos doesn't handle (probably causing a "hang") - I don't know what Dosbox would do with it. I don't think this exception will occur in your code, but it's a risk. A "proper" 16-bit subroutine would look like this:

Code: [Select]
upper:
; create a "stack frame"
push bp
mov bp, sp
; get your parameter
mov si, [bp + 4] ; 2 for the return address, 2 for "push bp"
mov al, [si]
; etc.
.done:
mov sp, bp
pop bp
ret ;!

That's not tested either, but I think I've got it right (famous last words!). It would really help you if you could try out your code. If you can get Nasm to work at all, it'll produce a .com file - even in a 32- or 64-bit system. You can copy that to somewhere in your Dosbox directory (folder), or perhaps you can run it under Dosbox from where it sits.

It is possible to create a 16-bit .exe format executable - doesn't have to be a .com file. That would require slightly different code, and would require a linker (or including a macro file). Since you're forced to use 16-bit code by using the BIOS interrupts, I'd stick with the .com format if you can.

You're doing a really good job for just "writing it on paper", but there are a few problems which you would easily discover if you could "try it". See if you can find a way to do so, if you can!

Best,
Frank


Offline ProblemHelp

  • Jr. Member
  • *
  • Posts: 8
Re: interrupt
« Reply #8 on: December 09, 2011, 09:51:12 PM »
Sorry that I didn't write before, because i had problems with my internet :S Now, I'm home and it's ok. Your answer I saw on my phone, but I couldn't respond you (I don't have so good phone :)). As I said that I will talk to my teacher..I was 3 hours in school because of this, but we didn't find a solution :S The teacher said that he will look at home, what is the problem.
Code: [Select]
bits 32
global _main

section .data
Vnos db "beseda HGZ ",10, 0
 
section .text


toupper:
mov esi, dword [esp+4]
mov dl, byte [esi]
mov ah,6
int 21h
ret


_main:
pushad
mov dl, 'b';
mov ah,6
int 21h
;push dword Vnos
;call toupper
;add esp, 4
popad
ret
Without function toupper, just printing one character with interrupt is ok, but when we try with the whole word doesnt'work...I think because of this:
Code: [Select]
mov esi, dword [esp+4]
mov dl, byte [esi]
Realy thank you again for everything! Wish me good luck on tuesday :)
Be good,
Talijana

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: interrupt
« Reply #9 on: December 10, 2011, 01:13:03 AM »
Quote
As I said that I will talk to my teacher..I was 3 hours in school because of this, but we didn't find a solution :S The teacher said that he will look at home, what is the problem.

I wish I knew exactly what you tried, and exactly how it failed to work! I hope he can find a solution "at home"!

I asked my roommate, who actually runs Windows, and he says that Windows 7 is "the newest one". He says that dos is supported in "XT mode" (32-bit Windows 7, I suspect), or with something called "exdos" (like Dosbox, I suspect).

http://www.exdos.com/ - apparently not free. I suspect you're better off with Dosbox(?).

I've said (repeatedly) that "bits 32" and dos/BIOS interrupts aren't going to work together. There's an exception to this: DJGPP. This is a port of the GNU toolset to dos (like MinGW is a port of GNU tools to Windows). The executables are huge, since there's a lot of code linked in which provides 32-bit versions of the dos interrupts, but it does allow dos and BIOS interrupts in 32-bit code. I don't think it's a good solution for you, but it might be something to try, in desperation. (tell Nasm "-f coff" to get the proper output format for DJGPP)

You say that (dos) int 21h/6 works, right? That's a good start (and a surprise, in conjunction with the "bits 32"!). Maybe getting the parameter in your subroutine is the only problem. Try "[esp + 2]" instead of "[esp + 4]"... although 4 should be right, if it's really 32-bit code. I really don't think mixing 16-bit and 32-bit code is the best idea... unless you're running DJGPP. If your teacher is running DJGPP and you're not, that would explain a lot! I hope he can help you with a solution to this!

Good luck on Tuesday! (although studying hard may work better than "luck" :) )

Best,
Frank


Offline ProblemHelp

  • Jr. Member
  • *
  • Posts: 8
Re: interrupt
« Reply #10 on: December 10, 2011, 07:33:00 PM »
Thank you and hello again. I'm exercising for tuesday and I found one task for which I don't have the solution and what I did also doesn't work. I did this task in that way that I understand every step. I think that I "made dirty" registers. So the task is about array. 1,2,3,4->3,1,-1,-3. We must mirror during the middle and also substract. For example : 1(first element), 4(last element)--->4-1(first element), 1-4(last element) ; 2(second), 3(second from behind)-->3-2, 2-3. I hope you understand my poor explanation :/ So first I tried to solve this task just for that array (lenght 4). I did this:
Code: [Select]
bits 32
extern _printf
global _main

section .data
array dd 1,2,3,4
size db 4
print db "%d", 10, 0

section .text
push ebp
mov ebp, esp
mov ecx, [ebp+4] ;size
mov esi, [ebp+8] ;pointer on first element
mov edi, [ebp+8];pointer on first element again
mov eax, 12;to help me-we get the last element [edi+eax]

.once:
mov edx, dword [esi]; element on the right side
sub edx, dword [edi+eax]; right side-left side
mov ebx, dword [esi+eax];left side
sub ebx, dword [esi]; left side-right side
mov dword [esi], ebx ;we move it in first place
mov dword [edi+eax], edx ;move it in last place
push dword [esi] ;put on the stack element
push dword print
call _printf
add esp, 8
add esi, 4 ;going on next element
sub eax, 4 ;moving back
sub dword [size], 1
cmp dword size], 2 ;want to print just first two elements for cheking (4/2=2)-I think that i must do one loop just for printing array
jg .once
ret

_main:
pushad
push dword [size]
push dword polje
call function
add esp, 8
popad
ret

I hope that I'm not to much annoying :/ This program print just the first element, but right! It prints 3, so I guess I'm in the right way :) If you have time, please tell me what I did wrong. But also I don't want to do the big change in the program ( I want(if this can be) to keep the moving that i did in the beginning, because for me it's more understandably:)). I hope that my program isn't in all wrong :S
Thank you for help!!
Oh! If you want to see what we did with the teacher last time for interrupt:
Code: [Select]
bits 32
global _main

section .data
Vnos db "beseda HGZ ",10, 0
 
section .text


toupper:
mov esi, dword [esp+4]
mov dl, byte [esi]
mov ah,6
int 21h
ret


_main:
pushad
mov dl, 'b';
mov ah,6
int 21h
;push dword Vnos
;call toupper
;add esp, 4
popad
ret
So this is working, but when we do with the function toupper (with the set-doesn't work). I asked and they said that we wont' do any interrupt more, so, for me it's now important to know how we do it, not just if it's work:)  -the comments are in our language.
Greeting,
 Talijana

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: interrupt
« Reply #11 on: December 11, 2011, 02:03:57 AM »
Well, something I can actually test! It looks like what you posted is a translation to English, and has a couple of typos in it - "function" doesn't exist, "array" is only partially translated, missing '[', etc. I "fixed"(?) those, and there remain some serious(?) errors!

Code: [Select]
bits 32
extern _printf
global _main

section .data
array dd 1,2,3,4
size db 4

"db" is plenty big enough to hold the size, but you go on to use it as a dword. This will cause it to include the next three bytes - the beginning of the format string, in this case. There are ways to use a byte of data, "extending" it to a dword, but it's easier to change this to "dd", which is what I did.

Code: [Select]
print db "%d", 10, 0

section .text

function: ; <- missing
push ebp
mov ebp, esp
mov ecx, [ebp+4] ;size

Wrong! Your parameter is pushed on the stack. At this point, it's at [esp]. The "call" instruction "pushes" the return address on the stack, at this point, the "first" (pushed last) parameter is at [esp + 4]. Then, when we enter the function, we push ebp, so the "first" parameter is at [esp + 8]. We make ebp equal to esp at this point, so it's [ebp + 8], too. If you had eliminated the "stack frame", not pushed ebp, then [esp + 4] would be correct. But you pushed "size" first, so it's the "last" parameter, and is actually at [ebp + 12]. Since you don't use ecx, this doesn't do any harm, but it's wrong. By coincidence(?), this makes the next two lines correct...

Code: [Select]
mov esi, [ebp+8] ;pointer on first element
mov edi, [ebp+8];pointer on first element again
mov eax, 12;to help me-we get the last element [edi+eax]

.once:
mov edx, dword [esi]; element on the right side
sub edx, dword [edi+eax]; right side-left side
mov ebx, dword [esi+eax];left side
sub ebx, dword [esi]; left side-right side
mov dword [esi], ebx ;we move it in first place
mov dword [edi+eax], edx ;move it in last place
push dword [esi] ;put on the stack element
push dword print
call _printf
add esp, 8
add esi, 4 ;going on next element
sub eax, 4 ;moving back

At this point, printf has altered eax! Now, in a "real program", you'd probably want to have separate functions to "modify array" and "print array", so this might not be an issue. For now, it's convenient to have printf in the middle of this function for "debugging" purposes, so we'll need to save and restore registers that we're using, and that printf can alter - edx and (maybe) ecx, as well as eax. pushad/popad would be easy, although it does more than we really need...

Code: [Select]
sub dword [size], 1
cmp dword size], 2 ;want to print just first two elements for cheking (4/2=2)-I think that i must do one loop just for printing array
jg .once

; since we created a stack frame, we must destroy it!
mov esp, ebp ; added!
pop ebp ; added!

ret

_main:
pushad
push dword [size]
push dword polje
call function
add esp, 8
popad
ret

With these changes, I'm getting 3 and -5. Not quite right. I think the problem is that we've bumped esi (but not edi?) by 4, and decreased eax by 4 - [esi + eax] is not what we intend! I haven't straightened out this issue, but I'll post what I've got so far (in case I missed telling you about changes I made).

Code: [Select]

; these two lines just to make it work in Linux
;%define _printf printf
;%define _main main

bits 32
extern _printf
global _main

section .data
    polje dd 1,2,3,4  ; was "array"
    size dd 4 ; increased from "db"
    print db "%d", 10, 0

section .text
function:

    push ebp
    mov ebp, esp

    mov ecx, [ebp + 12] ;size
    mov esi, [ebp + 8] ;pointer on first element
    mov edi, [ebp + 8];pointer on first element again
    mov eax, 12;to help me-we get the last element [edi+eax]

.once:
    mov edx, dword [esi]; element on the right side
    sub edx, dword [edi+eax]; right side-left side
    mov ebx, dword [esi+eax];left side
    sub ebx, dword [esi]; left side-right side
    mov dword [esi], ebx ;we move it in first place
    mov dword [edi+eax], edx ;move it in last place


    pushad

    push dword [esi] ;put on the stack element
    push dword print
    call _printf
    add esp, 8

    popad

    add esi, 4 ;going on next element
    sub eax, 4 ;moving back
    sub dword [size], 1
    cmp dword [size], 2 ;want to print just first two elements for cheking (4/2=2)-I think that i must do one loop just for printing array
    jg .once
   
    mov esp, ebp
    pop ebp
   
    ret

_main:
    pushad

    push dword [size]
    push dword polje
    call function
    add esp, 8

    popad
    ret

Maybe that'll give you something closer to work with. I'm not really sure I understand the "specification" (assignment). An array A, B, C, D is supposed to become (D-A), (C-B), (B-C), (A-D)? If I've got that right, there may be a "problem" - we've altered A and B before we calculate the last two elements! Maybe we can solve this by doing the calculations out-of-order, or maybe we'll need to save "old A" and "old B" in temporary variables. You know how to do a temporary variable? Something like...

Code: [Select]
function:
    push ebp
    mov ebp, esp
    sub esp, 8 ; enough for two temporary varaiables
; I like to give 'em names
%define oldA ebp - 4
%define oldB ebp - 8
; then, once we've got "old A"...
    mov [oldA], edx
; ... and so on...

See if you can get anywhere with that...

I'm going to ignore the "interrupt" code for now. In addition to the code, I'd need to know what command line (output format) you're giving Nasm, what (if any) linker, and what OS (or "fake OS") you're trying to run it on. We can get back to that, if you need to.

Best,
Frank


Offline ProblemHelp

  • Jr. Member
  • *
  • Posts: 8
Re: interrupt
« Reply #12 on: December 11, 2011, 02:11:55 PM »
THANK YOU A LOT!!!! MY PROGRAM WORKS!! I apologise for my errors because of my language (polje-array), yes you are right, I translate it from my language to english, so I forgot to translate the whole:S really sorry! Because of my error it prints 3 and -5, so I correct it! The error was here:
Code: [Select]
.once:
mov edx, dword [esi]
sub edx, dword [edi+eax]
mov ebx, dword [edi+eax] ;HERE !! this is correct
sub ebx, dword [esi]
mov dword [esi], ebx
mov dword [edi+eax], edx
Now, I just must to print it at all :)
one more question. We never do "pushad" and "popad" in function, just in main. Why must do it in these? ( I know just that pushad save "old amount" of registers). Here:
Code: [Select]
pushad  ;?
push dword [esi]
push dword izpis
call _printf
add esp, 8
popad; ?

So if i good understand: when we do at main push dword [size], then we put it in esp+4 or esp? I think that it's on esp-we push it first. Maybe you explain me it in your post, but I dont understand good :S My problem is that I don't understand, why is the size on ebp+12, because in the main I put it before the array. So In my head is now this:
Code: [Select]
_main:
pushad
push dword [dolzina] ;esp
push dword polje esp+4
call funkcija -->push ebp=esp+8; mov ebp, esp->ebp=esp+8
So why is mov ecx [ebp+12]? :S
 When I do some task in assembly I always draw I picture, what is going on with the stack:)
Thank you again, I'm happy now, that my "idea" is working:)

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: interrupt
« Reply #13 on: December 12, 2011, 12:35:30 AM »
Good! And good questions!

It all goes back to the "C calling convention" (called "CDECL", I think - but I don't know how to pronounce that). Among other things (parameters on the stack, return value in eax,...) it requires that certain registers retain their value across function calls - ebx, esi, edi, and ebp. (esp has to be right, too, when we get to "ret"! - but that's a CPU thing, not a C thing) Other registers can have their values altered within a function - ecx and edx, and eax is the return value. So we were using eax and edx (not sure about ecx) in your subroutine, and calling printf was about to trash 'em. So...

Code: [Select]
pushad  ;?
push dword [esi]
push dword izpis
call _printf
add esp, 8
popad; ?

Normally I push/pop just the registers I need, but in this case it seemed easier to "save 'em all" with pusha/popa (you usually dont need to say pushad/popad or pushaw/popaw - Nasm will do the right thing with pusha/popa, depending on if it's 16- or 32-bit code). Your subroutine doesn't actually return to C, so we don't need to follow that convention, but "main" returns, so we need to preserve any of those registers that we alter. Again, pushad/popad is "easy" (and short). Strictly speaking, "main" ought to return a "meaningful" value in eax - zero for "no error" usually - but "whatever it was" is close enough for now...

I'm not artistic, so I tried to write code that would "draw a picture" for me. My first attempt printed it with low values on top, working up. I thought it was more understandable with high values on top, working down (?), so I "flipped" it...

Code: [Select]
; for 32-bit Linux
; nasm -f elf32 showstack.asm
; gcc -o showstack showstack.o

; for 32-bit Windows? (or 64-bit) ???
; adds underscore to "main" and "printf"
; "-m32" tells 64-bit gcc that we want 32-bit code?
; nasm -f win32 --PREFIX _ showstack.asm
; gcc -m32 -o showstack.exe showstack.obj

global main
extern printf

section .data
    fmt db `stack: 0x%8X contents: 0x%8X\n`, 0

section .text
main:

; standard C prologue
    push ebp
    mov ebp, esp

; save caller's regs that need to be preserved
; we don't actually alter edi, but save it so we can use it freely
    push ebx
    push esi
    push edi

; identifyable values in regs...
    mov ebx, 0xBBBBBBBB
    mov esi, 0x55555555

; push a couple of identifyable "parameters"
    push 0xDEADBEEF
    push 0x0BADF00D
    call showstack
    add esp, 4 * 2

; restore caller's regs
    pop edi
    pop esi
    pop ebx

; destroy the stack frame   
    mov esp, ebp
    pop ebp
    ret

showstack:
    push ebp
    mov ebp, esp
    push ebx
    push esi

; loop counter
    mov esi, 5
; start our fake stack pointer "up the stack" a bit
    lea ebx,[esp + esi * 4]

.top:
    mov ecx, [ebx]

    push ecx ; contents
    push ebx ; fake stack pointer
    push fmt ; format string (two hex integers)
    call printf
    add esp, 4 * 3

    sub ebx, 4
    sub esi, 1
    jns .top

    pop esi
    pop ebx
    mov esp, ebp
    pop ebp
    ret

I hope that'll work for Windows, too... Here's what the output looks like on my machine, with some comments added.

Code: [Select]
stack: 0xBFFFF588 contents: 0xDEADBEEF ; "last" (in a C listing) parameter, +12
stack: 0xBFFFF584 contents: 0x BADF00D ; "first" (in a C listing) parameter, + 8
stack: 0xBFFFF580 contents: 0x 804837F ; return address, ebp + 4
stack: 0xBFFFF57C contents: 0xBFFFF598 ; caller's saved ebp, ebp=esp here
stack: 0xBFFFF578 contents: 0xBBBBBBBB ; caller's saved regs
stack: 0xBFFFF574 contents: 0x55555555

Some of the numbers will be different in Windows (if it works), but ought to follow the same pattern. See if that helps. Don't be afraid to ask more questions - it's an important thing to understand!

Best,
Frank


Offline ProblemHelp

  • Jr. Member
  • *
  • Posts: 8
Re: interrupt
« Reply #14 on: December 22, 2011, 10:45:01 PM »
I'm late, but I didn't want to write you before I become results of my exam and I can tell you with a smile that I became 80% !!! Thank you for everything, you really helped me! Thank you a lot! So in January we start with mic and mac, so maybe I will write you again if I will have some problems:)
 I wish you a happy new year!
Talijana