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
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.
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 .
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.
darkie,
Please do not cross post. Just post in the proper subforum to get the best help. :U
Paul
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
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.
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
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.
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
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 ?
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
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
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.