×
INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Contact US

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Students Click Here

subroutine queery

subroutine queery

subroutine queery

(OP)
hello can anyone tell me the syntax for declaring, calling and returning from a subroutine. Thanx

RE: subroutine queery

aldeburan,

In a primitive ways declare a subroutine is just the same as a location.
MyProc:
   mov ax,bx

Calling it simply use a "call" command:
call MyProc

and for return syntax is "ret". Here is a simple one.
   jmp start
MyProc:
   mov ax,bx
   ret
start:
   call MyProc
   END

A later compiler can use a procedure to make asm program more organize, i.e:
MyProc1 PROC
   mov ax,bx
   ret
ENDP

MyProc2 PROC NEAR
   mov ax,bx
   ret
ENDP

and call it with:
call MyProc1
call MyProc2

You can also add parameter in the procedure, i.e.:
MyProc PROC value:WORD
   mov ax, value
   ret
ENDP

and call it with:
   push reg/mem/immediate
   call MyProc

In a latest MASM you can also call it with:
   invoke MyProc, 1

Other compilers maybe have their ways. But as long as you use push & call, any compiler will accept it (I think)

Sorry if there's a wrong explanation
Regards

-- AirCon --

RE: subroutine queery

(OP)
I suppose it would be easier if i gave an example of my code. Its the same code im trying to write with keys 1-8 playing musical notes. Someone recommended me to use jmp commands to a 'mainroutine' to reduce the size of code, however i cant always get the program to jump back to the original instruction without being in endless loops. So what i want to do is use subroutines, then the prog will return to the next instruction in the main block.
Heres a sample:

        .model large
        .stack 100h
        .data
prompt  DB 'Press 1-8 to play notes! Press x to exit!',13,10,'$'
        .code
        mov ax,@data
        mov ds,ax
        jmp rowrow
mainroutine proc
    pop ax
        out 42h,al
        pop ax    ;popping them back off again
        out 42h,al    ;and loading into port 42h
        in al,61h
        or al,00000011b
        out 61h,al    ;turn on speaker
        RET

rowrow:
        mov al,10110110b     
        out 43h,al
        mov al,97h
        push ax        ;push LSB for notes reverse order
        mov al,0ah
        push ax    ;push MSB for the notes
        CALL mainroutine
        CALL delay3
        CALL turnoffspeaker


             **
             **
            code
             **
END

This doesnt work - you may see it as obvious but i am just a beginner. Do you have to push the address of the instruction you want to return to? if so im pushing other values onto ax that i need, is there another way i can do this?
I put the 'mainroutine' subroutine at the beginning only because i seen that is what you done, is this right?.
I am not getting any errors when i assemble but when i run the program i get booted out of dos and i need to restart my PC.
What does 'mov ax,bx' do?, whats its function here? since i see i have missed it out.
If your wondering how im getting the values for the notes it's the clock frequency, 1193180Hz divided by the frequency of the note required, i.e. for concert A, where all musical scales are taken from, 1193180/440 = 2711d = A97h, this value is loaded into counter 2 of the PIT 8253/8254 byte at a time, then the speaker is switched on by oring 00000011 with bit 0 and 1 of port B (61h), since we dont want to change any of the other bits.

Any advice you could give on the above would be greatly appreciated.

RE: subroutine queery

Seems you have mixed up the routine & procedure. But I'm not sure it is the problem or not. Lets change this first:

        mov ds,ax
        jmp rowrow
mainroutine proc
    pop ax

into:

        mov ds,ax
        jmp rowrow
mainroutine:
    pop ax

Try again, and learn to use debug
About 'mov ax,bx' is nothing but an example

-- AirCon --

RE: subroutine queery

Hm, not easy to explain this.
In assembler you are largely responsible for everything the processor does. If you write a procedure using PROC, all that's really happening is you are telling the assembler to remember its name, so this location can be called by name later.
So a piece of code starting
MyName PROC NEAR
 .....
and ending
MyName ENDP
is simply a chunk of code. The assembler won't even put the return at the end of it (well, my ancient copy of tasm doesn't, anyway). This isn't a weakness of the assembler. It's what assembler is all about: you only get what you write, and you can write whatever you want.

To make something function as a subroutine, it needs to end by returning to the code that called it. To do this, you put ret at the end of your procedure. This will be interpreted as ret near or ret far according to whether you defined a PROC NEAR or PROC FAR. But the assembler won't object if you actually override it and specify retf at the end of a near procedure!

Ret returns to the address put on the stack, which is an offset in all cases, and also a segment if the call was far. So retf takes both an offset and a segment from the stack, while retn takes just an offset. The point of a call instruction is to put the appropriate offset and segment on the stack before it does a jump.

You can still call a procedure using a jump rather than a call, but a jump, unlike call, doesn't save the return address on the stack. Therefore at the end of the procedure there will be a horrible crash unless you explicitly put some reasonable address on the stack. There are odd occasions you might do this, for instance to make a procedure "return" to some other place in your code, not the instruction following the jump.

You are right to think about structure in your code. In my view it is even more important in assembler, which is basically hard to read, than in high level languages, which shouldn't be! But I'd try to keep things really simple in your procedure structure.

By the way, I'm not really very sure about your prompt-printing bit. Are you sure you're addressing the line of text correctly?

On procedures, there are all sorts of more complicated structures that tend to appear at the begining and end of procedures, especially when created by higher-level languages. These are things like
push bp
mov bp, sp
etc., which are involved in setting up bp so that it can be used to access variables pushed onto the stack.

RE: subroutine queery

(OP)
Thanx lionelhill, i managed to get it working, heres what i done:

        .model large
        .stack 100h
        .data
prompt  DB 'Press 1-9 to play notes in the scale of D!',13,10,'$'
exit    DB 'Press x to exit!',13,10,'$'
        .code
        mov ax,@data
        mov ds,ax
        jmp firstnote

        public mainroutine
mainroutine  proc near            
        pop bx                
    pop ax                
        out 42h,al                     
        pop ax             
        out 42h,al            
        in al,61h            
        or al,00000011b                
        out 61h,al            
        push bx                
        RET                
mainroutine endp

firstnote:                             
        mov al,10110110b             
        out 43h,al            
        mov al,011h            
        push ax                
        mov al,0cah            
        push ax                
        CALL mainroutine        
        CALL delay1            
        CALL offspeak            
        CALL pause        
            "
            "
         rest of code
            "
            "
END

obviously i have more procedures in the code than i have highlighted here.
My problem was that i was passing data between main and the procedure. I was storing them onto the stack for this. But when you call the procedure the return address gets stored on the top of the stack which would then muck up my data that i was passing. So to combat this, when i first enter the procedure i popped the return address into bx, since bx is not being used, then just before the RET command i pushed it back onto the top of the stack and this restored the return address, woo hoo!

Regarding your statement about my prompt, i never included the command in my example code that displays the code, which goes like;

        mov dx, OFFSET prompt
        mov ah,9
        int 21h
        mov dx, OFFSET exit
        mov ah,9
        int 21h

And they display OK, thanx for your concern and advice.

RE: subroutine queery

My problem was that i was passing data between main and the procedure. I was storing them onto the stack for this. But when you call the procedure the return address gets stored on the top of the stack which would then muck up my data that i was passing ....

At first I already figured that something wrong with the stack, but I didn't know how to explain cause I didn't know the exact result. That's why I asked you to learn debug. I guess you already tried it, aren't you ? Debugger will you best friend in asembly world
Now since you have found out, lets try this one (I'm trying to avoid push & pop by using register only)

mainroutine proc near
  mov ax, dx
  out 42h,al
  shr ax,8
  out 42h,al
  in al,61h
  or al,00000011b
  out 61h,al
  RET
mainroutine endp

firstnote:
  mov al,10110110b
  out 43h,al

;  mov al,011h
;  push ax
;  mov al,0cah
;  push ax

  mov dx, 011CAh
  CALL mainroutine

  mov dx, ????h
  CALL mainroutine

Let me know the result

-- AirCon --

RE: subroutine queery

Aircon is right, you can avoid pushing and popping by using the registers.
Alternatively if you cannot get all the data you need in registers, you can use the stack as you suggest, but there is a much better way to access things you've put on the stack, and that is to use bp.

The way you do this, is at the begining of your subroutine you set bp to be equal to the stack pointer. Conventionally you push bp first in case you ever find yourself working in nested subroutines, where the subroutine above where you are at the moment might itself be using bp in the same way. Hence a subroutine includes at its start
push bp
mov bp, sp
You'll find that the stack now contains:
+08:  a word you pushed
+06:  segment part of return address
+04:  offset part of return address
+02:  old bp
I might have slipped 2 here. Do check. I always get confused whether the stack pointer is decremented before or after pushing.... sorry I'm a bit thick.
Or something similar depending on whether you were making near or far calls.
Hence you can now get at your passed (pushed!) parameters with statements like
mov ax, ss:[bp+08]
bp defaults to using stack segment for exactly this purpose.
If you need more local storage space internally to your subroutine, you can even extend this by subtracting a lump from sp in order to reserve a little local stack space.
push bp
mov bp, sp
sub sp, 06
This space can now be addressed as ss:[bp-6] etc....
Either way, at the end of your subroutine you must restore the old bp and get the sp back to where the return address is before doing the return. You can do this simply by doing
mov sp, bp
pop bp
ret however....

The final ret instruction can free up space on the stack taken by your pushed variables.

I'm not advocating imitating a compiler (otherwise there wouldn't be a lot of point in writing assembler!) but this sort of local stack frame structure is very powerful, and worth knowing about. If you're bursting into assembler to make a recursive routine more efficient, though, try to avoid it.

Just a note on "enter" and "leave". These are Intel's realisation just how important local stack frames are, and do exactly what I've described above. However, they're slower than the individual instructions, so not recommended.

RE: subroutine queery

yes, the whole purpose of the bp register is to access parameters passed to a called routine.

however, recent complexity of routines are requiring alot more paramters to be passed. have you looked at the enter and leave opcodes? these opcodes are specifically used to pass a block of paramters to called routines.

anyhow, i beleive the favoured method as with the modern day BIOS is to use a paramter table and only pass the pointer of this table to the routine such as DS:SI - of corse there are many methods of passing parameters.

you just have to decide which you prefer :)
straiph

"There are 10 types of people in this world, those who know binary and those who don't!"

RE: subroutine queery

I think enter and leave simply do the same as push bp, mov sp, bp and all that stuff, but with less code, and a little more execution time. But I can't swear to how fast they run on good up-to-date processors. I'm still kicking around in early pentium world on that one.

Yes, passing a parameter by reference, rather than by value, is a good time-saver if you've a lot to pass. Straiph is quite right about that one. If you have several complex blocks of values you want to pass, you can of course push their addresses and retrieve them with les si, ss:[bp+something], or whatever.

And in assembler there's still a good case for the odd global variable! You can use that from anywhere without passing anything, so it may not make for easy-read code, but there's nothing quicker.

Red Flag This Post

Please let us know here why this post is inappropriate. Reasons such as off-topic, duplicates, flames, illegal, vulgar, or students posting their homework.

Red Flag Submitted

Thank you for helping keep Tek-Tips Forums free from inappropriate posts.
The Tek-Tips staff will check this out and take appropriate action.

Reply To This Thread

Posting in the Tek-Tips forums is a member-only feature.

Click Here to join Tek-Tips and talk with other members! Already a Member? Login


Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close