News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Storing and restoring segment registers

Started by fredrikhr, March 12, 2025, 06:43:40 AM

Previous topic - Next topic

fredrikhr

Following the open-sourcing of the Command&Conquer Game series by Electronic Arts earlier this month, I have started looking at the codebase for Red Alert;
( https://github.com/electronicarts/CnC_Red_Alert ) out of pure curiosity.
The code is dated 1995 and contains some Assembly code, mostly written for the Borland Turbo Assembler TASM. I am looking at "modernizing" the code to make it compilable with the modern version of MASM just as way to learn the differences between old TASM code and MASM.

Anyway, Red Alert had both a 32-bit and a 16-bit launcher application (as well as the actual RA95.exe executable). The 16-bit DOS Launcher lives in the LAUNCH.ASM file

It declares a 16-bit CODE segment in ( https://github.com/electronicarts/CnC_Red_Alert/blob/main/LAUNCH/LAUNCH.ASM#L111 ) and later also a 16-bit DATA segment ( Line 550 ).

TITLE    "Red Alert launcher"
.386
; ...

_code segment para public use16 'code'
; ...
_code ends

_data segment dword public use16 'data'
; ...
_data ends

end

In the application entry point it starts setting up the DS segment register:
mov ax,_data
mov ds,ax
(ref. https://github.com/electronicarts/CnC_Red_Alert/blob/main/LAUNCH/LAUNCH.ASM#L136 )

However, when assembling this MASM returns the following error message:
LAUNCH/LAUNCH.ASM(136): error A2004: symbol type conflict

When converting some of the other TASM Assembly to MASM I ran into similar issues (e.g. in the Keyboard and Timer interrupt handlers) where MASM is decidedly unhappy with assigning the _DATA (or even @DATA or SEG @DATA or SEG _DATA) values to the ax register (in order to subsequently assign that to DS, ES, etc.

Why? And what declaration on the segments need to be done in order for this to work?

Using Microsoft (R) Macro Assembler Version 14.43.34808.0 and invoking it thusly:
> ml.exe /c LAUNCH\LAUNCH.ASM
Microsoft (R) Macro Assembler Version 14.43.34808.0
Copyright (C) Microsoft Corporation.  All rights reserved.

 Assembling: LAUNCH\LAUNCH.ASM
LAUNCH\LAUNCH.ASM(136) : error A2004:symbol type conflict
LAUNCH\LAUNCH.ASM(282) : error A2004:symbol type conflict

zedd151

Maybe use an older, legacy version of ml.exe? Like what is already in the Masm32 SDK...
Of course there may be a different reason for your issues. I never code in 16 bit, or any other that uses segment registers explicitly, so maybe I'm not the best to give advice here.
¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

fredrikhr

Sure, I could use an older assembler, I tried the MASM /Zm flag, but that only gives "Enable MASM 5.10 compatibility" (with the same error message). However, using an old assembler defeats the purpose of "modernizing" the code. I'd much rather understand what the issue is here, but I do not understand the error message.

I get that 32-bit flat layout largely makes segments redundant, but this is 16-bit realmode, right?

zedd151

Quote from: fredrikhr on March 12, 2025, 06:57:13 AMHowever, using an old assembler defeats the purpose of "modernizing" the code. I'd much rather understand what the issue is here, but I do not understand the error message.
ok. My bad. I thought that something like "Enable MASM 5.10 compatibility", or similar compatibility option is what you might need - and possibly not available on more modern versions of ml.

Confirms what I said above "maybe I'm not the best to give advice here". Sorry about that.
¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

sinsi

Later versions of ML need the /omf switch.
C:\asm\CnC_Red_Alert-main\LAUNCH>ml/c LAUNCH.ASM
Microsoft (R) Macro Assembler Version 14.43.34809.0
Copyright (C) Microsoft Corporation.  All rights reserved.

 Assembling: LAUNCH.ASM
LAUNCH.ASM(136) : error A2004:symbol type conflict
LAUNCH.ASM(282) : error A2004:symbol type conflict
LAUNCH.ASM(594) : warning A4023:with /coff switch, leading underscore required for start address : Start

C:\asm\CnC_Red_Alert-main\LAUNCH>ml/c /omf LAUNCH.ASM
Microsoft (R) Macro Assembler Version 14.43.34809.0
Copyright (C) Microsoft Corporation.  All rights reserved.

 Assembling: LAUNCH.ASM

C:\asm\CnC_Red_Alert-main\LAUNCH>

Thanks for the link to the source, I (many moons ago) wrote a no-cd launcher for this game :biggrin:)

NoCforMe

Something else: I took a look back at my old 16-bit code.
You might try simplifying your segment directives.
I found mine to be more like this:
Code SEGMENT WORD
ASSUME CS:Code, DS:Code
which just uses default attributes which worked for me.
Not sure if you need all those qualifiers, some of which may be scrambling MASM's little brains ...

(not sure if you need the ASSUME statement; I just included it for a possible example)
Assembly language programming should be fun. That's why I do it.

sinsi

Quote from: NoCforMe on March 12, 2025, 07:53:11 AMSomething else: I took a look back at my old 16-bit code.
You might try simplifying your segment directives.
I found mine to be more like this:
Code    SEGMENT WORD
    ASSUME    CS:Code, DS:Code
which just uses default attributes which worked for me.
Not sure if you need all those qualifiers, some of which may be scrambling MASM's little brains ...

(not sure if you need the ASSUME statement; I just included it for a possible example)

ASSUME is there in the source.
Also this lol
QuoteSimplified segment models are for wimmin

One reason for defining segments - more control over segment attributes
QuoteWe want 32 bit instructions in a 16 bit segment


NoCforMe

#7
OK. What threw me was
QuoteIt declares a 16-bit CODE segment in ( https://github.com/electronicarts/CnC_Red_Alert/blob/main/LAUNCH/LAUNCH.ASM#L111 )

which I assumed meant 16-bit code in that segment.
I've never actually seen that mixture of 16- and 32-bit code in the same program before.
Didn't even know you could do that. (Well, I guess .386 makes that possible.)

So OP, did that MASM option (/omf) fix your problem?

(Sure it isn't actually /omfg?) haha
Assembly language programming should be fun. That's why I do it.

fredrikhr

#8
Quote from: NoCforMe on March 12, 2025, 08:14:49 AMSo OP, did that MASM option (/omf) fix your problem?
Yes, I can confirm that using the /omf option to ml.exe works.

For reference:
> ml /?
Microsoft (R) Macro Assembler Version 14.43.34808.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/omf generate OMF format object file
About OMF (see https://en.wikipedia.org/wiki/Object_Module_Format_(Intel)#Use )

QuoteThe file format is the most important object file format under DOS, 16-bit Windows, and 16-bit and 32-bit OS/2.

Few toolchains use the 32-bit version of the OMF format. For example, the Watcom C toolchain allows generating code for targets that use 32-bit segmented memory layouts.
On the difference between COFF and OMF (ref. https://stackoverflow.com/a/966689/2226662 )

QuoteBorland, for example, still uses OMF object files and libraries, while Microsoft's 32-bit compilers produce COFF format files. Watcom C/C++ v11.0 seems to prefer COFF when compiling and linking Windows applications, but generates OMF object files for use with their DOS4GW 32-bit protected-mode DOS-extender.
The Red Alert source code here targets the Borland C/C++ Compiler and the Borland Turbo Assembler while it shows in some places to be compatible with the Watcom C/C++ compiler. The source code also shows references to DOS 32-bit protected-mode extension.

So yes, all the above clearly indicate that this code base needs to assemble using OMF format (where necessary). Since modern MASM and the modern Linker support OMF, this simply means that I will append the OMF-flag for the few assembly files where it becomes necessary. I think I'll use COFF everywhere else still.

Thanks for the help in figuring this out :)

sinsi

Quote from: fredrikhr on March 12, 2025, 09:18:03 AMSince modern MASM and the modern Linker support OMF
MASM/ML support outputting OMF but later MS linkers will probably choke, since they attempt to convert OMF to COFF.

Also, later MS linkers won't output a 16-bit EXE, only 32-bit PE EXEs.
The MASM32 SDK includes LINK.EXE (renamed to LINK16.EXE)
Microsoft (R) Segmented Executable Linker  Version 5.60.339 Dec  5 1994

zedd151

I had thought in the beginning that it might have been some sort of compatibility issue with the later version of ml. (14.xxxx for the OP here)

I was partially right (it didn't appear to be an issue with the code itself), but also wrong at the same time, (not what I was thinking).

Glad your all sorted, frederikhr
¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

FORTRANS

Hi,

Quote from: NoCforMe on March 12, 2025, 08:14:49 AMwhich I assumed meant 16-bit code in that segment.
I've never actually seen that mixture of 16- and 32-bit code in the same program before.
Didn't even know you could do that. (Well, I guess .386 makes that possible.)

   You can use 32-bit opcodes/instructions in 16-bit code.  I have done that and it
is fairly easy to do.  However  mixing code that works in a 16-bit CPU mode with code
using a 32-bit code requires you change the CPU mode in the program.  This is not
something I have looked at for any practical usage.  One boots in a real 16-bit mode
and I have seen code that then switches to a different mode for the program proper.
Probably in code to just do that to see how it works out.  Have fun as needed.

   Oh, and the use of 32-bit DOS extenders of course.  But that uses prebuilt code from others.
Cheers,

Steve N.

NoCforMe

Quote from: FORTRANS on March 12, 2025, 10:50:34 PMYou can use 32-bit opcodes/instructions in 16-bit code.  I have done that and it
is fairly easy to do.  However  mixing code that works in a 16-bit CPU mode with code
using a 32-bit code requires you change the CPU mode in the program.
That's interesting: I went back and looked at the OP's posted code; I didn't find any such mode-change instructions before the first use of 32-bit instructions.
; dos get free disc space returns with the following
; ax - sectors per cluster
; bx - number of available clusters
; cx - bytes per sector
; dx - cluster on the drive

mul bx ;clusters avail*sectors per cluster
shl edx,16
mov dx,ax
mov eax,edx
and ecx,65535
mul ecx ;*bytes per sector
cmp eax,INIT_FREE_DISK_SPACE
blo Disc_Error
Unless I missed something there ...

So do you really need to do this (change the CPU mode) or not?
Assembly language programming should be fun. That's why I do it.

FORTRANS

Quote from: NoCforMe on March 13, 2025, 09:25:28 AM
Quote from: FORTRANS on March 12, 2025, 10:50:34 PMYou can use 32-bit opcodes/instructions in 16-bit code.  I have done that and it
is fairly easy to do.  However  mixing code that works in a 16-bit CPU mode with code
using a 32-bit code requires you change the CPU mode in the program.
That's interesting: I went back and looked at the OP's posted code; I didn't find any such mode-change instructions before the first use of 32-bit instructions.

Unless I missed something there ...

So do you really need to do this (change the CPU mode) or not?

   No mode change is necessary to run 32-bit instructions in a 16-bit mode.
No mode change is required to run 16-bit instructions in a 32-bit mode.
But code designed to run in a 32-bit mode will not run (for the most part)
in a 16-bit CPU mode.

   You can write code, and using the USE16 and USE32 directives, you can
see that MASM produces rather different opcodes for your code depending on
it being written for a 32-bit mode or a 16-bit mode.  If you write a program,
you (normally) cannot run mixed 16-bit and 32-bit code without either changing
modes, or expecting weird results (crashes/exceptions mostly).

   Amusingly, you can use the 32-bit addressing modes in a 16-bit mode.  You
do have to limit the addressing range to the 16-bit segment limits.  Except
that you can reprogram the segment limits to create "Unreal Mode" or a memory
manager.  DPMI and the DOS extenders used by Watcom compilers come to mind.

Regards,

Steve N.

NoCforMe

Still confused by your answer:
What's the difference between 16- and 32-bit modes and 16- and 32-bit instructions?

What perplexes me is your statement
QuoteBut code designed to run in a 32-bit mode will not run (for the most part) in a 16-bit CPU mode.
Assembly language programming should be fun. That's why I do it.