News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

far procedure confusion

Started by darkie, March 14, 2005, 05:55:44 PM

Previous topic - Next topic

darkie

Hi,
     i am learning assembly and am using tasm assembler. i seem to have confusion with far procedures.      I am not using the simplified directives apporoach but the full segment definition approach.Now the far procedures is defined as the procedures in different segments(intersegment) . So is this code for far procedures i have written is right ?

proceg segment
sample proc far
         ret
sample endp
proceg ends

code segment
assume cs:code,ds:nothing
start:
        call sample
         mov ah,4ch
         int 21h
code ends
end start

               If there is any mistake pls explain. I have not found much of far procedure examples using full segment definitions on the net.
                                         Thanks

mnemonic

Hi darkie :)

First of all: http://www.masmforum.com/simple/index.php?topic=161.0 ;)

I don't know what really hapens at your place so it would be helpful to know what errors you get. Does it assemble right or do you get runtime complications?

Anyway, I'm not familiar with Tasm but I'll make some guesses...
What is obvious in the first step is that you don't use "retf" or "ret far" (dunno which works with Tasm). If you call far you should ret far too.
And you may even add a far to the call but as I said, I don't know it for sure. Just give it a try and play around a bit.

Good luck.
Be kind. Everyone you meet is fighting a hard battle.--Plato
-------
How To Ask Questions The Smart Way

darkie

Thanks for the reply. I am using tasm. i think there is no need for ret far or retf  unless i have not specified the  proc directive . (correct me if i am wrong here pls).I didnt have any problems assembling it. I am having doubts abt how to write a far procedure because i didn't see any examples on it.  i will crosspost this in 16 bit programming .

mnemonic

Quote from: darkie on March 14, 2005, 06:52:58 PM
i think there is no need for ret far or retf  unless i have not specified the  proc directive . (correct me if i am wrong here pls).
Did you try it?

Quote from: darkie on March 14, 2005, 06:52:58 PM
[...] unless i have not specified the  proc directive .[...]
Don't know what you mean by that...

Quote from: darkie on March 14, 2005, 06:52:58 PM
I didnt have any problems assembling it. I am having doubts abt how to write a far procedure because i didn't see any examples on it.
So that means the prog runs fine?

Quote from: darkie on March 14, 2005, 06:52:58 PM
i will crosspost this in 16 bit programming .
No need to do this since the thread will be moved, I think.
Be kind. Everyone you meet is fighting a hard battle.--Plato
-------
How To Ask Questions The Smart Way

pbrennick

darkie,
Please do not cross post.  Just post in the proper subforum to get the best help. :U

Paul

pbrennick

darkie,
By the way, retf is definitely required to return from a far call or else the stack will become more and more unbalanced after each successive call until the app eventually will crash and burn.  If you ask for advice, listen to it.

Paul

MichaelW

#6
I modified your source to include a data segment, display a message from the called procedure, and wait for a key before exiting.

DATA SEGMENT
    ASSUME DS:DATA
   
    msg db "Press any key to exit...$"
   
DATA ENDS

proceg segment
sample proc far
    mov   ah,9
    mov   dx,OFFSET msg
    int   21h
    ret
sample endp
proceg ends

code segment
assume cs:code
start:
    mov   ax,SEG DATA
    mov   ds,ax
    call sample
    mov   ah,0
    int   16h
    mov   ax,4c00h  ; Set return value in AL to zero.   
    int   21h
code ends
end start


Building it with TASM version 3.1 and TLINK version 5.1, I get a "No stack" warning from the linker, but the resulting EXE runs OK.

tasm /m3 /la test.asm
pause
tlink test.obj
pause


In the following listing, CBh is the opcode for a far return, and 9Ah the opcode for a far call. TASM encodes the call and return instruction (distance) based on the procedure declaration (just as MASM would). But note that for procedures that are not decared with the PROC directive, you must manually specify call and return instructions of the correct distance (or as noted above, risk a "crash and burn").

Turbo Assembler Version 3.1     03/14/05 20:19:04     Page 1
test.asm



      1 0000      DATA SEGMENT
      2 ASSUME DS:DATA
      3
      4 0000  50 72 65 73 73 20 61+ msg db "Press any key to exit...$"
      5       6E 79 20 6B 65 79 20+
      6       74 6F 20 65 78 69 74+
      7       2E 2E 2E 24
      8
      9 0019      DATA ENDS
     10
     11 0000      proceg segment
     12 0000      sample proc far
     13 0000  B4 09 mov   ah,9
     14 0002  BA 0000r mov   dx,OFFSET msg
     15 0005  CD 21 int   21h
     16 0007  CB ret
     17 0008      sample endp
     18 0008      proceg ends
     19
     20 0000      code segment
     21      assume cs:code
     22 0000      start:
     23 0000  B8 0000s mov   ax,SEG DATA
     24 0003  8E D8 mov   ds,ax
     25 0005  9A 00000000sr call sample
     26 000A  B4 00 mov   ah,0
     27 000C  CD 16 int   16h
     28 000E  B8 4C00 mov   ax,4c00h ; Set return value in AL to zero.
     29 0011  CD 21 int   21h
     30 0013      code ends
     31      end start
Turbo Assembler Version 3.1     03/14/05 20:19:04     Page 2
Symbol Table




Symbol Name   Type Value

??DATE   Text "03/14/05"
??FILENAME   Text "test   "
??TIME   Text "20:19:04"
??VERSION   Number 030A
@CPU   Text 0101H
@CURSEG   Text CODE
@FILENAME   Text TEST
@WORDSIZE   Text 2
MSG   Byte DATA:0000
SAMPLE   Far PROCEG:0000
START   Near CODE:0000

Groups & Segments   Bit Size Align  Combine Class

CODE   16  0013 Para   none
DATA   16  0019 Para   none
PROCEG   16  0008 Para   none


For procedures declared witht the PROC directive, you should be able to call the procedures from anywhere in the program, without having to worry about whether the procedure is near or far. If a procedure is called before it is defined (known as a "forward reference"), TASM will need to make more than one pass to resolve the reference. So AFAIK you will need to use the /m# option, where # is replaced with the number of passes to allow.

The linker and program loader together will set up a workable stack, but the program loader will leave DS and ES set the segment address of the PSP. If your program uses data, you will need to set DS to the segment address of the data segment. AFAIK you will need to provide a valid assume for any segment register the program uses, but I think the assume for SS is necessary only if you explicitly refer to SS, as in a segment override.
eschew obfuscation

pbrennick

#7
That looks great, the only fault I can find is this:

mov   ah,4ch

It really should be:

mov   ax,4c00h

Or you run the risk of returning false errorlevels to the calling program or DOS.  In your example, al is carrying a value.  Your method will work, but it is not clean programming as it does not close doors that shouldn't be left opened and leads to poor programming habits.

Paul

MichaelW

Hi Paul,

Yes, the return value should be set to zero instead of just leaving it floating. I noticed that when I first examined the code, but it got lost in the shuffle.
eschew obfuscation

pbrennick

Michael,
Very nice work, though.  I had forgotten about the assembler detecting and correcting for far procedure calls.  In the old days I never used macros or procedures.  Things are very different, now; you almost have to.
Paul

darkie

mnemonic. yes the program runs without any problems. sorry i was not clear there. not specifying the proc directive in the sense --> sample proc far or sample proc near. as given in my text there is no need for retf if i have specified it in the proc statement. the same applies to callf i think.How will i set the interrupt procedure in the ds:dx ? can i do it like this ?

mov ax,seg proceg
mov ds,ax
mov dx,offset sample

i tried the ctrl-c interrupt handler and it seems to call the procedure twice .
the program is as follows

data segment
          msg db 'Hello $'
data ends

intrpt segment
interrupt proc far
assume ds:data
          mov dx,offset msg
          mov ax,seg data
          mov ds,ax
          mov ah,09h
          int 21h
assume ds:nothing
iret
interrupt endp

interrupt1 proc far
         iret
inrerrupt1 endp

intrpt ends

code segment
assume cs:code
start:
         mov ax,seg intrpt
         mov ds,ax
         mov dx,offset interrupt
         mov ax,2523h  ;23 ctrl-c handler vector
         int 21h

         mov ah,01h
         int 21h
         
         mov ax,4c00h
         int 21h
code ends
end start

MichaelW thank you for the code.How do i detect that in the procedure the length of  it doesnt cross above the 64kb limit? Is it neccessary to set the ds back if i am accessing the some other segment in the main program?
i came across this statement in michael abrash's book and he had declared something like this --->  OutputStr label byte
                                                      db 0dh,0ah, "Timed Count :'",5 dup(?)
what is this 'label byte' do ?

MichaelW

Your code, as posted, will not assemble.

I don't know the details of how TASM handles the ASSUME directive, but an ASSUME DS:nothing is not something you would normally do in your code. Normally, a small program would have one data segment, and at startup DS would be set to the segment address of that segment. An ASSUME directive would be used to tell the assembler that data labels will be in the data segment (so it knows what segment base to use when it calculates the offset address of these labels). If in your code you need to change the value in DS, you should push it before you change it, and pop it after you have finished using the new value.

From the MS-DOS Encyclopedia, Ray Duncan General Editor, Microsoft Press, 1988:

Quote
The machine interrupt vector for Interrupt 23H (memory locations 0000:008CH through 0000:008FH) contains the address of the routine that receives control when a Control-C (also Control-Break on IBM PC compatibles) is detected during any character I/O function and, if the Break flag is on, during most other MS-DOS function calls.
...
When a Control-C is detected and the program's Interrupt 23H handler receives control, MS-DOS sets all registers to the original values they had when the function call that is being interrupted was made. The program's interrupt handler can then do any of the following:

Set a local flag for later inspection by the application (or take any other appropriate action) and then perform a return from interrupt (IRET) to return control to MS-DOS. (All registers must be preserved.) The MS-DOS function in progress is then restarted and proceeds to completion, and control finally returns to the application in the normal manner.

Take appropriate action and then perform a far return (RET FAR) to give control back to MS-DOS. MS-DOS uses the state of the carry flag to determine what action to take: If the carry flag is set, the application is terminated; if the carry flag is clear, the application continues in the normal manner.

Retain control by transferring to an error-handling routine within the application and then resume execution or take other appropriate action, never performing a RET FAR or IRET to end the interrupt-handling sequence. This option causes not harm to the system.

The assembler will return an error if you exceed the 64KB segment limit.

Again, I don't know the TASM specifics, but for MASM a LABEL directive "Creates a new label by assigning the current location-counter value and the given type to name".

The memory models that use far procedures do so to allow the total code size to exceed 64KB. Small programs normally use a small memory model that supports a single code segment, and for this model the procedures default to near. But you can override the default procedure distance, and you can place multiple far procedures in a single segment.

The message was being displayed twice because the handler was not preserving the registers it changed and this was causing something else to display the message (I determined this by commenting out the interrupt call in the handler).

data segment
    msg db 'Hello $'
data ends

code segment
assume cs:code,ds:data
start:
    mov   ax,seg data
    mov   ds,ax

    push  ds
    push  cs
    pop   ds
    mov   dx,offset CtrlCHandler
    mov   ax,2523h  ;23 ctrl-c handler vector
    int   21h
    pop   ds

    mov   ah,01h
    int   21h

    mov ax,4c00h
    int 21h

CtrlCHandler proc far
    push  ax
    push  dx
    mov   dx,offset msg
    mov   ah,09h
    int   21h
    pop   dx
    pop   ax
    iret
CtrlCHandler endp

code ends
end start

eschew obfuscation

pbrennick

Michael,
Don't you need to enable interrupts from within the handler if you are going to call DOS services?  It's been a while but that sticks in my mind from the tsr days.  If I am wrong, I apologize for breaking in again, just trying to help.

Paul

MichaelW

Hi Paul,

The code seems to work OK. The Ctrl+C handler is not like a handler for an asynchronous interrupt. As stated in the quoted reference, you can return with an IRET, or a RETF, or not return at all. From the Microsoft MS-DOS Programmer's Reference, Version 5, Microsoft Press, 1991:
Quote
Before issuing Interrupt 23h MS-DOS does the following:

Sets all registers to the values they had when the interrupted system function was initially called.

Sets the program's stack to be the current stack. When the handler receives control, the stack has the following contents (from the top of the stack):

  The return address (CS:IP) and the flags need for the iret instruction back to the system.

  The return address (CS:IP) and the flags need for the iret instruction back to the program.

Sets to zero any internal system variables, such as the ErrorMode and InDOS variables, so that the handler can call system functions or even return directly to the program without disrupting system operations.

A CTRL+C handler can call any system function.

Note how the handler can access the program data without having to initialize DS.

I make an effort to provide correct answers, but recently I tend to have an increasing number of what Mark Larson aptly terms "brain farts". Having a knowledgeable and experienced programmer scrutinizing my work can only be a good thing.
eschew obfuscation