MASM (and probably TASM) emits a relocatable .EXE file after linking. Use EXE2BIN (or the /T on later versions of LINK) to get to the absolute file.
Thank you! /LINK /T works fine. Back when I originally wrestled with this I tried using '.model tiny' with 'segment at' but MASM complained. I also tried using MASM with the /AT switch but again, MASM refused so I eventually gave up, feeling very frustrated by not being able to do something so simple despite hours of trial and error and searching for info on google. I then tried it with NASM and it just worked, no questions asked. In my opinion there are too many buttons to push, knobs to twist and levers to pull to get MASM going. And to think that throughout this discussion we've been using the "simplified" directives...
Hell, MASM has all the info it needs to create a .COM program. Why do I even have to use LINK? Or worse, LINK+EXE2BIN?
Did you even look at the hex dump that I posted? 12 bytes.
Yes I did.
There's a good reason for demanding that--you've specified an absolute, not symbolic address, but the assembler has no idea of what segment to tie that address to.
I'm not buying this. Why would the assembler need to tie a segment to the address? Especially when I can do this;
Code:
mov bx,472h
mov word ptr [bx],1234h
without using any segment overrides, in fact without
ever mentioning DS at all in the source file.
But if I do this;
Code:
mov word ptr [472h],1234h
then MASM gives error A2001: immediate operand not allowed. The explanation for A2001 is "A constant or memory offset was given to an instruction that cannot take an immediate operand."
Not only is the error message wrong, it goes away by simply adding a (nonfunctional) segment override. If I as the programmer can't be trusted to know what I'm doing in the latter example, why should the former work? Both examples implies use of DS as the segment register.
Here's a fun little piece of inefficient code;
Code:
7C5D 68C007 * PUSH 07C0 ; New segment ref. to be used for
7C60 1F POP DS ; both Data (DS = Data Segment)
7C61 1E PUSH DS ; . . .
7C62 686600 * PUSH 0066 ; and Code segements . . .
7C65 CB RETF ; after this RETF instruction.
taken from
a page showing a disassembly of the Windows 7 Volume Boot Record. This clearly shows that someone at Microsoft felt the need to use a workaround for the "clean way of thinking" behind the design of MASM. I know it's not exacly proof but I'm willing to bet at least one of two precious body parts that this is the case.
Since NASM doesn't have the ASSUME directive, you/re on your own as far as what segment was intended--and heaven save you if you decide at some point that you'd rather make all of your BIOS segment references relative to ES rather than DS. In MASM/TASM's case, you can do this easily by changing an ASSUME--everything follows. In NASM's case, you have to find every single one and change it.
Suppose you decided that instead of assuming that the BIOS data segment is at 00000, you decide to use 00400h? In the above case, you'd change whatever code loads DS (or whatever segment register you decide to use) and the value of the segment declaration "AT". That's it--everything would adjust.
I ASSUME
that you've read section 2.2 'Quick Start for MASM Users' in the NASM manual? They didn't do a very good job of explaining why ASSUME is not supported (I guess poor documentation is one thing both NASM and MASM have in common) but if you read that and then read
Art of Assembly: Chapter Eight from 8.8.10 'Controlling Segments with the ASSUME Directive' all the way down to the end of 8.8.11 'Combining Segments: The GROUP Directive' it should be very obvious why ASSUME is so bad.
A good assembler tries to make it difficult to construct semantic errors or opaque code. Suppose IBM had published the 5150 BIOS listing where all low-memory references were coded as literal locations, rather than symbolic. Think of the joy trying to read that!
I thought we had covered this already? I agreed with you, remember? That said, there are situations where 'symbols' just gets in the way and adds a pointless layer of abstraction.
Now, as to why I dislike NASM--it's syntax is sui generis. TASM and MASM closely follow Intel's syntax. NASM's macro facility, such as it is, is very weak.
Believe it or not but I actually agree with you on this as well. NASM is not nearly as powerful as MASM in that respect. Accessing elements of a structure on the stack is also a bit more involved with NASM.
Please forgive my evangelism, but I've seen too much code that was agony to read.
I think the coding style has more to do with this than the choice of assembler. And coding styles evolve with experience.
I think LOOP got even slower on modern processors as a result of a Win95 bug.
No, actually I think it was a bug in OS/2.