Hello everyone,
I'm a 16 bit novice and I'm searching for an explanation to the following problem. I'm using full segment definitions and using c functions linked with Borland 2.01 library (c0l.obj, cl.lib) and have run into a phenomenon I don't understand, but no doubt you folks will.
If I set up the ds segment using DGROUP (with no ASSUME ), the program runs fine. Printf runs the string in dseg shown below. In addition int 21h will print the "howdy" string.
However, if I replace DGROUP with mov ax, SEG dseg even with an ASSUME ds:dseg, the printf function does not run, although the string printed by int 21h runs without a problem
What is happening here?
My related question is: what code could I use IN PLACE OF mov ax, DGROUP that would achieve the same effect. The command lines I use are: ml /c /omf /Fl /Zi dgroup.asm and ..\bin\link16 /map /codeview dgroup.obj c:\tc\lib\c0l.obj,,,c:\tc\lib\cl.lib
Here's the code:
.model large, c
.386
printf PROTO C, :VARARG
sseg segment para stack '_stack'
db 256 dup ('stack ')
sseg ends
dseg segment para public '_data'
strng db "howdy$", 13, 10, 0
fmt1 db "%s", 0
dseg ends
cseg segment para public '_code'
main PROC
;ASSUME ds:dseg
mov ax, DGROUP ;SEG dseg
mov ds, ax
invoke printf, aDDR fmt1, ADDR strng
mov dx, OFFSET ds:strng
mov ah, 09h
int 21h
exit:
mov ah, 4Ch
int 21h
main ENDP
cseg ends
end main
Many thanks.
Mark Allyn
:bg
first, i wouldn't try to mix C and 16-bit asm - lol
that being said....
there may be an issue with segment ordering
when the linker gets ahold of the object files and library modules, it uses the first ones listed to determine segment order
as modules are added, segments are "inserted" according to combine type and class
i have really forgotten all the segment orders - lol
but, for a small model, it would be code, then data, then stack
if you like, you can open and close all the segments in an include file, in the proper order
then, in the source, you can open and close them in any order because the include file has already established the order
recently, there was another thread that was closely related to this - something to do with mixing 16-bit C and asm
in that thread, you will find links to open watcom
you might look in their documentation to see what the order is
even better, try to find the borland documentation that lays out all the segments
it is also a good idea to match the segment parameters, step by step
for example, i see you are using paragraph alignment
i am pretty sure that borland uses word alignment
the combine and class parameters are also important for large model
these need to be the same for segments in DGROUP so that the linker may combine them together
one other tip
i would start with a small model and work my way up :P
you are only complicating things by using a large model
and - make sure the lib you are linking with is set up for the same memory model
now, for the best free advice you'll get in this thread....
it is much easier in 32-bit world :U
MASM is well equipped to deal with mixed-language programming. I don't know the answer to the question "When is DGROUP/@data required to set up ds", but I know a likely way around the problem, specify an appropriate model and language type and use the simplified segment directives including the .STARTUP directive. If you need to know how to set up DS (and move the stack into DGROUP) and use the full segment directives, use the above method, and when you assemble have MASM create a listing file. Here is the listing file from one of my TC 2.01 tests:
Microsoft (R) Macro Assembler Version 6.14.8444 05/02/11 19:08:01
clibtest.asm Page 1 - 1
;====================================================================
.model large, c
.386
;====================================================================
printf PROTO c :VARARG
getch PROTO c
atof PROTO c :DWORD
;====================================================================
.stack
0000 .data
0000 pi REAL8 3.14159265
400921FB53C8D4F1
0008 r8 REAL8 0.0
0000000000000000
0010 25 64 25 63 00 fmtd db "%d%c",0
0015 25 78 68 25 63 00 fmth db "%xh%c",0
001B 25 66 25 63 00 fmtf db "%f%c",0
0020 32 2E 37 31 38 32 estr db "2.718282",0
38 32 00
0000 .code
;====================================================================
;-------------------------------------------------------------
; This dummy procedures prevent the linker from returning an
; "unresolved external" error. Something in the library code
; is apparently referencing this name, and while the dummy
; procedure will satisfy the linker, if the library were to
; actually call the procedure it would probably fail.
;-------------------------------------------------------------
0000 main proc
0000 CB ret
0001 main endp
.startup
0001 *@Startup:
0001 B8 ---- R * mov ax, DGROUP
0004 8E D8 * mov ds, ax
0006 8C D3 * mov bx, ss
0008 2B D8 * sub bx, ax
000A C1 E3 04 * shl bx, 004h
000D 8E D0 * mov ss, ax
000F 03 E3 * add sp, bx
;====================================================================
;----------------------------------------------------------
; For the C calling convention the arguments are pushed in
; right to left order, and the caller is responsible for
; removing them from the stack after the call returns.
;
; If the arguments are being correctly removed from the
; stack the starting and ending values of SP should match.
;----------------------------------------------------------
invoke printf, ADDR fmtd, 123, 10
0011 6A 0A * push +0000Ah
0013 6A 7B * push +0007Bh
0015 1E * push ds
0016 68 0010 R * push OFFSET DGROUP:fmtd
0019 9A ---- 0000 E * call printf
001E 83 C4 08 * add sp, 00008h
invoke printf, ADDR fmth, 1234h, 10
0021 6A 0A * push +0000Ah
0023 68 1234 * push +01234h
0026 1E * push ds
0027 68 0015 R * push OFFSET DGROUP:fmth
002A 9A ---- 0000 E * call printf
002F 83 C4 08 * add sp, 00008h
invoke printf, ADDR fmtf, pi, 10
0032 6A 0A * push +0000Ah
0034 66| FF 36 0004 R * push dword ptr pi+000000004h
0039 66| FF 36 0000 R * push dword ptr pi
003E 1E * push ds
003F 68 001B R * push OFFSET DGROUP:fmtf
0042 9A ---- 0000 E * call printf
0047 83 C4 0E * add sp, 0000Eh
invoke atof, ADDR estr
004A 1E * push ds
004B 68 0020 R * push OFFSET DGROUP:estr
004E 9A ---- 0000 E * call atof
0053 83 C4 04 * add sp, 00004h
0056 DD 1E 0008 R fstp r8
invoke printf, ADDR fmtf, r8, 10
005A 6A 0A * push +0000Ah
005C 66| FF 36 000C R * push dword ptr r8+000000004h
0061 66| FF 36 0008 R * push dword ptr r8
0066 1E * push ds
0067 68 001B R * push OFFSET DGROUP:fmtf
006A 9A ---- 0000 E * call printf
006F 83 C4 0E * add sp, 0000Eh
invoke getch
0072 9A ---- 0000 E * call getch
.exit
0077 B4 4C * mov ah, 04Ch
0079 CD 21 * int 021h
;====================================================================
end
Microsoft (R) Macro Assembler Version 6.14.8444 05/02/11 19:08:01
clibtest.asm Symbols 2 - 1
Segments and Groups:
N a m e Size Length Align Combine Class
CLIBTEST_TEXT . . . . . . . . . 16 Bit 007B Word Public 'CODE'
DGROUP . . . . . . . . . . . . . GROUP
_DATA . . . . . . . . . . . . . 16 Bit 0029 Word Public 'DATA'
STACK . . . . . . . . . . . . . 16 Bit 0400 Para Stack 'STACK'
Procedures, parameters and locals:
N a m e Type Value Attr
atof . . . . . . . . . . . . . . P Far 0000 Length= 0000 External C
getch . . . . . . . . . . . . . P Far 0000 Length= 0000 External C
main . . . . . . . . . . . . . . P Far 0000 CLIBTEST_TEXT Length= 0001 Public C
printf . . . . . . . . . . . . . P Far 0000 Length= 0000 External C
Symbols:
N a m e Type Value Attr
@CodeSize . . . . . . . . . . . Number 0001h
@DataSize . . . . . . . . . . . Number 0001h
@Interface . . . . . . . . . . . Number 0001h
@Model . . . . . . . . . . . . . Number 0005h
@Startup . . . . . . . . . . . . L Near 0001 CLIBTEST_TEXT
@code . . . . . . . . . . . . . Text CLIBTEST_TEXT
@data . . . . . . . . . . . . . Text DGROUP
@fardata? . . . . . . . . . . . Text FAR_BSS
@fardata . . . . . . . . . . . . Text FAR_DATA
@stack . . . . . . . . . . . . . Text DGROUP
estr . . . . . . . . . . . . . . Byte 0020 _DATA
fmtd . . . . . . . . . . . . . . Byte 0010 _DATA
fmtf . . . . . . . . . . . . . . Byte 001B _DATA
fmth . . . . . . . . . . . . . . Byte 0015 _DATA
pi . . . . . . . . . . . . . . . QWord 0000 _DATA
r8 . . . . . . . . . . . . . . . QWord 0008 _DATA
0 Warnings
0 Errors
And here is the command line I used:
ml /Fl /Sa /c clibtest.asm
I seem to recall the Microsoft libraries requiring that SS=DS. I don't know if TC 2.01 requires it, or just tolerates it.
Hi Dedndave and MichaelW
I won't be able to respond to all of your comments in this note, but wanted to at least respond partially.
First, to Dedndave. Yes, 32 bits would be the way to go cause flat is simpler. But, as I have mentioned in another thread, I am trying to understand how segmented memories work. My other comment is that I would have chosen a small model, but in fact the Borland C library requires large model. Won't work otherwise. It was this that led me in fact to discover the "problem"... or anomaly... or whatever. Once discovered, these things nag and won't let one go.
Second, to MichaelW. If I understand your comment...and I want to be fair and admit that I often stupidly misread comments...I know that the whole problem would go away if I simply used the "simple segment" segment scheme i.e. .model .stack .data .code .startup .exit. I would never have found the (my word) anomaly if I had used the simplified segments. This is (if I read it correctly) exactly what the .lst file you sent shows. It shows that DGROUP is mov(ed) to ax. Right. But, my question is: why is this required when a Borland C funcition is called even though it isn't required when the library is simply linked (as it is in both cases I have presented). The .map file for both of my cases is identical. It clearly shows a mapping of Borland C library functions. The difference is that in the case where DGROUP is used, the printf function gets its ADDR's right and prints, but when DGROUP isn't used, it doesn't.
Regards and thanks,
Mark
If I compile one of the sample C applications (hello.c) from within the BORLAND IDE, with the linker option Map file set to Detailed, I get the following MAP file:
Start Stop Length Name Class
00000H 00FF5H 00FF6H _TEXT CODE
01000H 0141CH 0041DH _DATA DATA
0141EH 01421H 00004H _EMUSEG DATA
01422H 01423H 00002H _CRTSEG DATA
01424H 01425H 00002H _CVTSEG DATA
01426H 0142BH 00006H _SCNSEG DATA
0142CH 01471H 00046H _BSS BSS
01472H 01472H 00000H _BSSEND STACK
01480H 014FFH 00080H _STACK STACK
Detailed map of segments
0000:0000 01FA C=CODE S=_TEXT G=(none) M=C0S ACBP=28
0000:01FA 000D C=CODE S=_TEXT G=(none) M=HELLO.C ACBP=28
0000:0207 003B C=CODE S=_TEXT G=(none) M=IOERROR ACBP=28
0000:0242 0030 C=CODE S=_TEXT G=(none) M=EXIT ACBP=28
0000:0272 0000 C=CODE S=_TEXT G=(none) M=HEAPLEN ACBP=28
0000:0272 00F1 C=CODE S=_TEXT G=(none) M=SETARGV ACBP=28
0000:0363 004A C=CODE S=_TEXT G=(none) M=SETENVP ACBP=28
0000:03AD 0000 C=CODE S=_TEXT G=(none) M=STKLEN ACBP=28
0000:03AD 0026 C=CODE S=_TEXT G=(none) M=ATEXIT ACBP=28
0000:03D3 0152 C=CODE S=_TEXT G=(none) M=MALLOC ACBP=28
0000:0525 0078 C=CODE S=_TEXT G=(none) M=BRK ACBP=28
0000:059D 0000 C=CODE S=_TEXT G=(none) M=FILES ACBP=28
0000:059D 0000 C=CODE S=_TEXT G=(none) M=FILES2 ACBP=28
0000:059D 0113 C=CODE S=_TEXT G=(none) M=WRITE ACBP=28
0000:06B0 0046 C=CODE S=_TEXT G=(none) M=WRITEA ACBP=28
0000:06F6 002B C=CODE S=_TEXT G=(none) M=LSEEK ACBP=28
0000:0721 00EB C=CODE S=_TEXT G=(none) M=LTOA ACBP=28
0000:080C 0021 C=CODE S=_TEXT G=(none) M=CVTFAK ACBP=28
0000:082D 007E C=CODE S=_TEXT G=(none) M=FFLUSH ACBP=28
0000:08AB 0019 C=CODE S=_TEXT G=(none) M=PRINTF ACBP=28
0000:08C4 01E0 C=CODE S=_TEXT G=(none) M=PUTC ACBP=28
0000:0AA4 0004 C=CODE S=_TEXT G=(none) M=REALCVT ACBP=28
0000:0AA8 054E C=CODE S=_TEXT G=(none) M=VPRINTER ACBP=28
0100:0000 0194 C=DATA S=_DATA G=DGROUP M=C0S ACBP=68
0100:0194 000E C=DATA S=_DATA G=DGROUP M=HELLO.C ACBP=48
0100:01A2 005B C=DATA S=_DATA G=DGROUP M=IOERROR ACBP=48
0100:01FE 0006 C=DATA S=_DATA G=DGROUP M=EXIT ACBP=48
0100:0204 0002 C=DATA S=_DATA G=DGROUP M=HEAPLEN ACBP=48
0100:0206 0000 C=DATA S=_DATA G=DGROUP M=SETARGV ACBP=48
0100:0206 0000 C=DATA S=_DATA G=DGROUP M=SETENVP ACBP=48
0100:0206 0002 C=DATA S=_DATA G=DGROUP M=STKLEN ACBP=48
0100:0208 0002 C=DATA S=_DATA G=DGROUP M=ATEXIT ACBP=48
0100:020A 0000 C=DATA S=_DATA G=DGROUP M=MALLOC ACBP=48
0100:020A 0000 C=DATA S=_DATA G=DGROUP M=BRK ACBP=48
0100:020A 0140 C=DATA S=_DATA G=DGROUP M=FILES ACBP=48
0100:034A 0028 C=DATA S=_DATA G=DGROUP M=FILES2 ACBP=48
0100:0372 0000 C=DATA S=_DATA G=DGROUP M=WRITE ACBP=48
0100:0372 0000 C=DATA S=_DATA G=DGROUP M=WRITEA ACBP=48
0100:0372 0000 C=DATA S=_DATA G=DGROUP M=LSEEK ACBP=48
0100:0372 0000 C=DATA S=_DATA G=DGROUP M=LTOA ACBP=48
0100:0372 0031 C=DATA S=_DATA G=DGROUP M=CVTFAK ACBP=48
0100:03A4 0000 C=DATA S=_DATA G=DGROUP M=FFLUSH ACBP=48
0100:03A4 0000 C=DATA S=_DATA G=DGROUP M=PRINTF ACBP=48
0100:03A4 0001 C=DATA S=_DATA G=DGROUP M=PUTC ACBP=48
0100:03A6 0077 C=DATA S=_DATA G=DGROUP M=VPRINTER ACBP=48
0141:000E 0004 C=DATA S=_EMUSEG G=DGROUP M=C0S ACBP=58
0142:0002 0002 C=DATA S=_CRTSEG G=DGROUP M=C0S ACBP=58
0142:0004 0000 C=DATA S=_CVTSEG G=DGROUP M=C0S ACBP=48
0142:0004 0002 C=DATA S=_CVTSEG G=(none) M=CVTFAK ACBP=48
0142:0006 0000 C=DATA S=_CVTSEG G=DGROUP M=REALCVT ACBP=48
0142:0006 0000 C=DATA S=_SCNSEG G=DGROUP M=C0S ACBP=48
0142:0006 0006 C=DATA S=_SCNSEG G=(none) M=CVTFAK ACBP=48
0142:000C 0000 C=BSS S=_BSS G=DGROUP M=C0S ACBP=48
0142:000C 0000 C=BSS S=_BSS G=DGROUP M=HELLO.C ACBP=48
0142:000C 0000 C=BSS S=_BSS G=DGROUP M=IOERROR ACBP=48
0142:000C 0000 C=BSS S=_BSS G=DGROUP M=EXIT ACBP=48
0142:000C 0000 C=BSS S=_BSS G=DGROUP M=HEAPLEN ACBP=48
0142:000C 0000 C=BSS S=_BSS G=DGROUP M=STKLEN ACBP=48
0142:000C 0040 C=BSS S=_BSS G=DGROUP M=ATEXIT ACBP=48
0142:004C 0006 C=BSS S=_BSS G=DGROUP M=MALLOC ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=BRK ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=FILES ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=FILES2 ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=WRITE ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=WRITEA ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=LSEEK ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=LTOA ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=FFLUSH ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=PRINTF ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=PUTC ACBP=48
0142:0052 0000 C=BSS S=_BSS G=DGROUP M=VPRINTER ACBP=48
0147:0002 0000 C=STACK S=_BSSEND G=DGROUP M=C0S ACBP=28
0148:0000 0080 C=STACK S=_STACK G=(none) M=C0S ACBP=74
<snip>
The data segments above starting with _DATA are in the MAP file for your app, followed your stack and data segments:
02600H 02C3AH 0063BH _DATA DATA
02C3CH 02C3FH 00004H _EMUSEG DATA
02C40H 02C41H 00002H _CRTSEG DATA
02C42H 02C49H 00008H _CVTSEG DATA
02C4AH 02C61H 00018H _SCNSEG DATA
02C70H 0346FH 00800H SSEG _STACK
03470H 0347BH 0000CH DSEG _DATA
So without referencing DGROUP or @data, I can effectively do the same thing by specifying the first segment in the group:
mov ax, SEG _DATA
mov ds, ax
And printf works correctly. So I think an imprecise answer to your first question would be something along the lines of:
DGROUP or @data may be required to set up DS if the linker will be combining two or more data or stack segments.
Quotewhy is this required when a Borland C funcition is called even though it isn't required when the library is simply linked
I think the problem is not the function call, but the called function trying to access data through a bad segment address.
:bg
wow, Michael, i haven't seen a map file for a long time - lol
it brings back memories (pun) :P
notice the class names
i think they have to be the same for the linker to combine them
and - they all have to have a combine type of PUBLIC
01000H 0141CH 0041DH _DATA DATA - normal data
0141EH 01421H 00004H _EMUSEG DATA - FPU emulator
01422H 01423H 00002H _CRTSEG DATA - C runtime
01424H 01425H 00002H _CVTSEG DATA - C sumpin - may be where borland puts an ID marker
01426H 0142BH 00006H _SCNSEG DATA - screen segment ?
those segments form DGROUP and should be in the same order in the first obj linked
surprised i don't see a CONST segment
01472H 01472H 00000H _BSSEND STACK
01480H 014FFH 00080H _STACK STACK
as i recall, _BSSEND is a dummy segment used to test for stack overlfow
BSS is, of course, uninitialized data
it looks like it may have been combined with DGROUP, somehow
i guess the class names do not have to match
and - odd that _BSS is in DGROUP, but _SCNSEG is not
doesn't the map file give you the size of DGROUP ?
DGROUP must be a public symbol
now i remember - it goes something like this
the linker will combine segments of the same class name before combining other segments
near as i can figure, it should look something like this....
_TEXT SEGMENT WORD PUBLIC 'CODE'
_TEXT ENDS
DGROUP GROUP _DATA,_EMUSEG,_CRTSEG,_CVTSEG,_SCNSEG,_BSS,_BSSEND
_DATA SEGMENT PARA PUBLIC 'DATA'
_DATA ENDS
_EMUSEG SEGMENT WORD PUBLIC 'DATA'
_EMUSEG ENDS
_CRTSEG SEGMENT WORD PUBLIC 'DATA'
_CRTSEG ENDS
_CVTSEG SEGMENT WORD PUBLIC 'DATA'
_CVTSEG ENDS
_SCNSEG SEGMENT WORD PUBLIC 'DATA'
_SCNSEG ENDS
_BSS SEGMENT WORD PUBLIC 'BSS'
_BSS ENDS
_BSSEND SEGMENT WORD PUBLIC 'STACK'
_BSSEND ENDS
_STACK SEGMENT PARA STACK 'STACK'
_STACK ENDS
if you put that in an INC file and include it at the beginning, your segments will be in order
and - when you open your code segment in the source file...
_TEXT SEGMENT WORD PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:DGROUP,SS:_STACK
of course, you have to put DGROUP into DS, and possibly ES
you may need to add a CONST segment just before _BSS and add it to DGROUP
it could be that this program had nothing to put in that segment
or, that borland does not follow MS convention
CONST SEGMENT WORD PUBLIC 'CONST'
CONST ENDS
one more note...
some of those segments are created by the compiler and may not be necessary to link with the library
you may be able to make a map of some (several) modules from the library and see which are used
some compilers put a marker in there, usually in the form of a copyright notice
the libraries won't work without it - lol
that is some compilers
Hi,
From a textbook:
"Segments defined with the .STACK, .DATA, .DATA?, and .CONST
directives are placed in a group named DGROUP. The value of DGROUP
rather than that of the segment name should be loaded into DS
register at the start of the code section."
And later on:
"Simplified Segment Defaults
...
With full segment definitions, the data segment address in the DS
register is the address from which OFFSET addresses are calculated.
With simplified definitions, the base address for OFFSET addresses
is that of DGROUP for .DATA, .DATA?, or .STACK segments. .CODE,
.FARDATA, and .FARDATA? segments have their own base addresses and
are not part of DGROUP."
Looks like he forgot .CONST there.
Anyway, it looks as if the high level language is using the
equivalent of simplified directives for naming segments. So
use DGROUP or SEG {first data segment name} as MichaelW
suggests. Note that in assembly the order of the segments
may not follow the "standard" ordering, so check with a MAP
listing.
HTH,
Steve N.
Hi Michaelw, Dedndave, and SteveN,
I haven't been able to manage handling Dedndave's suggestions yet, but I will. For the moment, I'll try to respond to Michael W and Steve who seem to be in agreement about the remedy.
So, I tried using _data as MichaelW suggested and what happened was: I got the printf function to work and print "howdy?" and also I got at the very end of the run the int 21h "howdy". BUT, in between there was a bunch of garbage from another data segment.
So, I reasoned thusly: why not write
Continuation from reply to Dedndave, Steve, and Michael W...
Sorry I chopped myself off midsentence. I tried an experiment, attempting to add 371h to _data to get to dseg, but this experiment didn't work.
Anyway, so where MichaelW writes:
"So without referencing DGROUP or @data, I can effectively do the same thing by specifying the first segment in the group:
Code:
mov ax, SEG _DATA
mov ds, ax
And printf works correctly. So I think an imprecise answer to your first question would be something along the lines of:"
I was unable to get the code to run properly.
I'll try to implement any other suggestions. But, the bottom line seems to be that if you want the thing to work you have to somehow load the first segment of the data class into the ds. Or else use DGROUP right from the start. I'm OK with this, but as I say, when I tried to lad the first segment of _data, I kind of got a mess plus the right stuff.
Regards,
Mark
Hi MichaelW and SteveN and 'Dave,
Sorry. I made a mistake in my coding. Went back and fixed the problem so that I REALLY implemented MichaelW's and SteveN's proposal to load the FIRST segment in the data class. It worked! So, I think we're "there" in the sense of answering my original query as to whether I had to use DGROUP exclusively...
But, here is an additional point of interest. If I use the .DOSSEG directive, the code I originally sent you will NOT assemble. The assembler reports an error pertaining to DSEG (no. L2002).
Thanks for working on this. I really appreciate it.
Mark Allyn
This is the code I was using:
.model large, c
.386
printf PROTO C, :VARARG
sseg segment para stack '_stack'
db 256 dup ('stack ')
sseg ends
dseg segment para public '_data'
strng db "howdy$", 13, 10, 0
fmt1 db "%s", 0
dseg ends
cseg segment para public '_code'
main PROC
;ASSUME ds:dseg
;mov ax, DGROUP ;SEG dseg
mov ax, SEG _DATA
mov ds, ax
invoke printf, aDDR fmt1, ADDR strng
mov dx, OFFSET ds:strng
mov ah, 09h
int 21h
exit:
mov ah, 4Ch
int 21h
main ENDP
cseg ends
end main
And the batch file:
ML /Fl /Sa /c mark.asm
pause
LINK16 /MAP mark.obj forcefp.obj c0l.obj,,,fp87.lib emu.lib mathl.lib cl.lib;
pause
mark
pause
And the result:
D:\MASMDOS\tc201_test>ML /Fl /Sa /c mark.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
Assembling: mark.asm
D:\MASMDOS\tc201_test>pause
Press any key to continue . . .
D:\MASMDOS\tc201_test>LINK16 /MAP mark.obj forcefp.obj c0l.obj,,,fp87.lib emu.li
b mathl.lib cl.lib;
Microsoft (R) Segmented Executable Linker Version 5.60.339 Dec 5 1994
Copyright (C) Microsoft Corp 1984-1993. All rights reserved.
D:\MASMDOS\tc201_test>pause
Press any key to continue . . .
D:\MASMDOS\tc201_test>mark
howdy$
howdy
D:\MASMDOS\TC201_~1>pause
Press any key to continue . . .
Are these the results you expected?
Hi MichaelW & SteveN and 'Dave
Thanks MichaelW. Yes, that is the original code I sent and eventually, as I said in my last post, this is the code I used...except, of course, I swapped out the seg dseg snippet for seg _data. And it ran.
One point I don't understand has to do with the _DATA segment (as named in the .map file). In my code the _DATA segment is 371h bytes. What's inside this segment?
Question for SteveN--you gave several quotations from a textbook regarding this problem. Could you give me a reference for the textbook?
Thanks all,
Mark
Hi Mark,
Sure thing. Got it from Edward R. Hamilton Bookseller Company,
which sells remaindered (?) books, EdwardRHamilton.com.
"Structured Assembler Language for IBM Microcomputers" by
Alton R. Kindred, Oxford University Press, 1991, ISBN: 0-19-517296-5.
Regards,
Steve N.
Hi Steve,
Thanks for the reference. I'll see if I can get a copy. My asm library is pretty meager--mostly Randy Hyde + MASM 6.1 Programmer's Guide.
I did a little further investigation into my segment questions. I added a couple of lilnes to the code that MichaelW used. Namely, I loaded ES with the address of my DSEG problem child. _DATA loads DS at 0C58 and DSEG loads at ES=0C85, 37 paragraphs away. 16 bytes per para X 37 paragraphs = 880 bytes away. The "howdy" string actually begins at offset 1 from 0C85, or in other words, 881 bytes away from offset 0 of _DATA. We know from the .map that Michael produced that the length of the _DATA seg is in fact 371h or 881d. So it makes sense arithmetically.
What I don't "get" is why I can't go directly to DSEG when I load ds. All the offsets got to be recalculated by the llinker when it plops this stuff inside of DGROUP --which seems like a waste of effort. Somehow it seems to me that there should be a way to override the .model directive so it doesn't just take it upon itself to force my DSEG into DGROUP.
I read some language in Hyde's chapter on segments, right at the end where he attempts to explain the algo that MASM goes thru to address segments that are plopped into DGROUP. Very confusing.. Somehow it seems like there should be a syntax of the form DGROUP:DSEG that would permit one to go directly to DSEG rather than referencing DGROUP AKA _DATA. On the other hand, that is sort of silly since it just means more typing...but, I submit that it is conceptually much clearer and might have other benefits.
Thanks again
Mark
Quote from: allynm on May 03, 2011, 07:05:28 PM
One point I don't understand has to do with the _DATA segment (as named in the .map file). In my code the _DATA segment is 371h bytes. What's inside this segment?
Most of the segment contents are related to the Borland library. The attachment contains a modified version of your source that uses my support module to display the contents of the segment. The map file shows a length of 74Bh, and it clearly includes the data for the support module.
Good morning, MichaelW.
Thanks for your further investigation and assistance. Later this morning I will run the code.
Regards,
Mark
Quote from: allynm on May 03, 2011, 08:10:28 PM
What I don't "get" is why I can't go directly to DSEG when I load ds. All the offsets got to be recalculated by the llinker when it plops this stuff inside of DGROUP --which seems like a waste of effort. Somehow it seems to me that there should be a way to override the .model directive so it doesn't just take it upon itself to force my DSEG into DGROUP.
Hi,
That seems to be for the HLL compatibility. Checking an _old_
ASM reference, showed an example of using the GROUP directive to
create DGROUP to interface with High Level Languages (HLL). You
could declare another segment and use it directly, but passing
references to a C routine would presumably then be fubared. You
could try reordering the segments in DGROUP using GROUP so that
DSEG was first, but that seems that it might also have side effects.
HLL compatibility was only used a few times by me, and I just did
some simple things that ASM did easily, and FORTRAN, well... I didn't
quite do the fight to the death with the documentation to see how/if
they could be done.
Regards,
Steve N.
Hi Steve N.
Your conjecture about why DGROUP is important for HLL interfacing seems borne out by what MichaelW. found when he inspected the inside of _DATA and found a lot of Borland C stuff inside it. It would seem therefore likely that creating a new GROUP with DSEG as first member would likely have the kind of side effects you mention.
BTW, I ordered the book from Amazon used book dealer for 18 bucks. The book must have been very popular in its field because it was available from a lot of used book vendors.
Thanks,
Mark
Quote from: allynm on May 04, 2011, 04:42:15 PM
BTW, I ordered the book from Amazon used book dealer for 18 bucks. The book must have been very popular in its field because it was available from a lot of used book vendors.
Hi Mark,
Can't comment on its popularity, and I hope you like it. It's
a bit odd compared to other assembly books I have, maybe
because it is a textbook. I tend to use it as a friendly MASM
reference. He apparently wanted to be understood, whereas
the M$ documentation wants to be terse and accurate (maybe).
Cheers,
Steve N.
Hello MichaelW,
Thanks so much for including the support.asm file. It has many very useful macros and looks to me as if it is something you've built over many years. It was very generous of you to share it.
I see what you did with my little pgm and it was very helpful explanation.
Regards,
Mark Allyn
Hello MichaelW,
One last question, if I may. I redid my little program to incorporate your support.asm code. The result was successful. But, I have just one uncertainty about how to interpret the seg:offset pairs that are printed out when the program runs. These pairs are not written the "same" as their counterpart pairs that show up in codeview when I dump memory during a codeview session on the same program.
Is it correct to assume that they are different because the codeview seg:offset pairs are NOT physical addresses, but the pairs showing up during the execution of the program ARE physical addresses? Or is something else happening?
Thanks,
Mark
keep in mind that, when codeview or a debugger are running, they are loaded first
so, the addresses reported are likely to be higher by the amount of space the debugger consumes
Hi Mark,
If I load the program into CV, execute the first two instructions so DS is set to the data segment instead of to the PSP, then start with a "d ds:0" and proceed through the memory, the segment and offset addresses and the data at those addresses match what I see when I run the program within CV. When I run the program on its own, everything except the segment addresses match the dump in CV. The difference in the segment addresses is due to a difference in the load addresses. The segment/offset pairs that you see represent physical addresses. Under RM DOS the represented addresses would match the physical addresses, but under Windows, who knows what's actually where. It's been so long since I used CV that I don't recall what a typical load address would be.
Hi 'Dave and MichaelW,
Yes, I observed exactly what MichaelW observes.
I noted the segment addresses for the CV session and the segment addresses as reported when the program uses MichaelW's memdump macro.
When the CV session runs on my little proggy, my program DS segment address is 0CBA. When the program dumps memory for the DS register, the segment address is 0791. My version of CV reports that the .exe consumes 490,768 bytes of storage. My segment arithmetic isn't at all trustworthy, but I think that the difference between 0CBA0 and 07910 is not as great as 490,768. This was my crude attempt to test 'Dave's suggestion. Maybe I did it wrong? After all, as we used to say, the difference is "in the right direction"...
Regards,
Mark
Hello 'Dave and MichaelW and others following this thread,
I made a final modifcation to the DGROUP program to print the value of one of the variables. This led to an interesting discovery. Namely, the ds: segment override operator is required in order for the program to assemble. Without it, the assembler generates an error. What is also interesting is that the printf C function does NOT have any problem getting the string "strng" or the format string.
I don't understand why the segment override is required. If the dseg segment loads second behind _DATA and the loader can find the strng in the dseg segment in order to execute printf why can't it find eks?
Here's the code:
.model large, c
.386
include c:\masm32\mymasm\michaelw\support.asm
printf PROTO C, :VARARG
sseg segment byte stack 'stack'
db 256 dup ('stack ')
sseg ends
dseg segment byte public 'data'
strng db "howdy$", 13, 10, 0
fmt1 db "%s", 0
fmt2 db "the value of w in memory is %d ", 13, 10, 0
eks word 64
dseg ends
cseg segment byte public 'code'
main PROC
mov ax, _data ;DGROUP
mov ds, ax
invoke printf, aDDR fmt1, ADDR strng ;NOTE - ds override is NOT required
mov ax, ds:eks ;NOTE- ds segment override is required
invoke printf, ADDR fmt2, ax
mov dx, OFFSET ds:strng ;NOTE- ds segment override is required
mov ah, 09h
int 21h
exit:
mov ah, 4Ch
int 21h
main ENDP
cseg ends
end main
Regards,
Mark
that's because you have no ASSUME DS:dseg
i am curious to see if this will assemble and run
the following are listed in the make.bat file - you must provide them
c0l.obj
fp87.lib
emu.lib
mathl.lib
cl.lib
not sure what c0l.obj is :P
you may be able to remove it
if you send me all the files listed above, i can experiment with them :U
Hi Dedndave-
Yes, that's what I thought too. I had previously tried your idea. I added ASSUME ds:dseg before I posted this and in fact the program will assemble and will produce a .exe file but it terminates abnormally when it hits the printf statement. The only way I could get it to run successfully was with the segment override.
I haven't tried to do what you propose in the second response. I looked at C0L.obj briefly using notepad. It looks like cl.lib inasmuch as the same C functions are identified in it as show up in cl.lib. I suppose you know that the l in c0l and cl refers to large model.
Thanks for taking yet another look at this problem.
Mark
I think I selected the Borland libraries and object modules by trial and error.
'Dave,
The 7-zipped file with the libraries is too large to send via the forum (>256 K). I need an email address.
Mark
Quote from: dedndave on May 05, 2011, 11:00:01 PM
not sure what c0l.obj is :P
Hi,
While I am not a C programmer, I seem to recall that it is the
"stub" code that initializes the C libraries and provides an entry
point to the C program.
Regards,
Steve N.