• Please review our updated Terms and Rules here

XTIDE tech support thread

Quite! Do you need one? Shipping would be high but I do have several PC and XT compatibles that could use a home -- I also have some bare motherboards kicking around, that would be reasonable to ship and you could just use any regular AT power supply with them.

Thanks for the offer but, as you said, shipping from the US would be high. Way too high. (Yes, I'm cheap. ;) ). Besides, I don't have the space for another machine anyway. As a matter of fact, I already have several machines (ranging from 386 to Pentium II) I'd like to have set up but don't have the space for that.

I personally do not see the point of getting to the bottom of Norton SI's buggy code. If Norton SI is crashing because the disk controller is too fast, what's the solution? Make the BIOS slower? I feel alecv's quest is folly.

Well, it could still be Bochs' fault I suppose but yeah, it certainly looks like NU SI is to blame. When looking at the code in IDA there are lots of odd stuff like this for example;
OddWayToQuit.jpg

BTW, Happy Birthday Trixter! :)
 
It does a far call to a procedure that quits the program when a simple jmp would have been sufficient.

EDIT: And there is of course a NOP before every far call of this type, the whole program is in fact full of NOPs due to this idiocy.
 
Well you can blame that on the compiler used to program this thing. I've seen lots of stupid generated code (cf. the thread on the Hitech Z80 C--the "ld h,h and ld l,l sequences). The far call is probably just the compiler not knowing that the routine doesn't return. The NOPs similarly are an artifact of not being able to decide at code generation what flavor of jump or call to use. As a matter of fact, you can see this gratuitous use of inserted NOPs in early MASM assemblies. Forward-reference jumps can be an annoyance.

In general, given the time, the code is neither better nor worse than the typical code from any other HLL for the x86.
 
In general, given the time, the code is neither better nor worse than the typical code from any other HLL for the x86.

True. I've been wondering many times how much of a difference it would make to recompile old software (games as well as productivity software) from the 80's and 90's with modern compilers. How much of the perceived need for better hardware was actually just a need for a better compiler?
 
Even with MASM 5, the big step forward was optimizing forward jumps. No more gratuitous no-ops. But it is surprising that not even "peephole optimization" was performed by many of the early x86 compilers.
 
Thanks for the offer but, as you said, shipping from the US would be high. Way too high. (Yes, I'm cheap. ;) ). Besides, I don't have the space for another machine anyway. As a matter of fact, I already have several machines (ranging from 386 to Pentium II) I'd like to have set up but don't have the space for that.

Let me know if you decide you need it -- it's yours for the cost of shipping! Thanks for hacking on this stuff :)
 
True. I've been wondering many times how much of a difference it would make to recompile old software (games as well as productivity software) from the 80's and 90's with modern compilers. How much of the perceived need for better hardware was actually just a need for a better compiler?

I've looked into this a lot, actually -- taking code meant for Turbo Pascal 6 and recompiling it with Stony Brook Pascal with all optimizations turned on, or taking masm source and running it through optasm or tasm with multi-pass. In the pascal tests, there was, on average, 5% speedup and 5% code size reduction. For asm, less than 1% (it just optimized the jumps).

The only "recompile this for massive speedup" gains I've ever seen was for BASIC programs. Microsoft Quickbasic compiles to P-code that calls routines provided by a library, but others (Powerbasic? Turbo Basic?) compile to actual x86 code. THOSE are big speedups.
 
I suspect that an aggressively optimizing C compiler might be more effective, but C has changed a lot since the K&R days. I've got some old code written for Lattice C; to get an effectively working version that's optimizable by a modern compiler would mean a lot of code changes. Old C programs got away with murder (cf. the "NUXI" issue when compiling old Unix for different platforms).

Even old assembly isn't that reliable. I recall that MASM 2 or 3 would quietly transform an "LEA reg,constant" into a "MOV reg, constant", supposedly in the name of optimization. That broke the prime rule of assembling; when I write "LEA", I mean "LEA" and not "MOV"...
 
Last edited:
Even with MASM 5, the big step forward was optimizing forward jumps. No more gratuitous no-ops. But it is surprising that not even "peephole optimization" was performed by many of the early x86 compilers.
I see this all the time when disassembling old code. Short forward jumps immediately followed by a NOP.

Let me know if you decide you need it -- it's yours for the cost of shipping! Thanks for hacking on this stuff :)
Thanks! That's very generous of you! :)

I've looked into this a lot, actually -- taking code meant for Turbo Pascal 6 and recompiling it with Stony Brook Pascal with all optimizations turned on, or taking masm source and running it through optasm or tasm with multi-pass. In the pascal tests, there was, on average, 5% speedup and 5% code size reduction. For asm, less than 1% (it just optimized the jumps).
A measly 5% size reduction could make a big difference on a cached system if it means some inner loop fits entirely in the cache. So I imagine 486 (and to some extent 386) systems would benefit the most from having code recompiled.

The only "recompile this for massive speedup" gains I've ever seen was for BASIC programs. Microsoft Quickbasic compiles to P-code that calls routines provided by a library, but others (Powerbasic? Turbo Basic?) compile to actual x86 code. THOSE are big speedups.
QuickBASIC compiles to native x86 code, QBASIC on the other hand translates the source code to P-code IIRC. The same applies to other older interpreted BASIC variants I think.

Even old assembly isn't that reliable. I recall that MASM 2 or 3 would quietly transform an "LEA reg,constant" into a "MOV reg, constant", supposedly in the name of optimization. That broke the prime rule of assembling; when I write "LEA", I mean "LEA" and not "MOV"...

Could you not disable this behaviour somehow? If not, I can see how that would be aggravating if you really wanted it to be a LEA for some reason. I agree that the assembler should do what it has been told to do but on the other hand I can't think of a single reason why you would want it to be an actual LEA instead of MOV. At least not for "normal" use. LEA is after all intended for calculating the effective address from *multiple* addressing components, if you're using it for a single component then you're simply doing it wrong.
 
A measly 5% size reduction could make a big difference on a cached system if it means some inner loop fits entirely in the cache. So I imagine 486 (and to some extent 386) systems would benefit the most from having code recompiled.

It would benefit even more by rewriting it into 386+ protected mode code :)

QuickBASIC compiles to native x86 code

Then I would argue it is a bad compiler. There was barely any speedup compiling with QB45, but then again the last time I tested it was 25 years ago.

Could you not disable this behaviour somehow? If not, I can see how that would be aggravating if you really wanted it to be a LEA for some reason. I agree that the assembler should do what it has been told to do but on the other hand I can't think of a single reason why you would want it to be an actual LEA instead of MOV. At least not for "normal" use. LEA is after all intended for calculating the effective address from *multiple* addressing components, if you're using it for a single component then you're simply doing it wrong.

It doesn't matter, the assembler should do the right thing unless told otherwise. Maybe in Chuck's example, LEA was intentionally being used instead of MOV as a component in a cycle-exact timing loop.
 
Exactly. There's a lot about Intel x86 assembly language that I don't like. For example, "MOV" has so many meanings; there is more than one way to encode an operation and you have to rely on luck if it matters. One thing that surprised me is that the built-in assembler in DEBUG sometimes generates different code than MASM (or a handful of other assemblers) does.

Having spent a long time with MASM, you soon discover it's like Philadelphia--you know how to get somewhere if you've lived most of your life there. Otherwise, the place is a mystery--"you can't get there from here" feelings.

Take the matter of unconditional jumps. Open the MASM manual and you'll discover that there's a SHORT qualifier to specify that the jump target is less than 128 bytes away, but there's no LONG qualify to specify that a 3-byte jump should be used.

Most people would give up and just encode the thing using a DB for the opcode and a DW for a (computed) target, or even encapsulate it into a macro. But if you live with MASM you know that the trick is to specify NEAR PTR and then MASM will generate the long form, unless qualified by SHORT:

Code:
 0100                           Plough:
 0100  EB FE                            jmp     Plough
 0102  E9 FFFB                          jmp     near ptr Plough
 0105  EB F9                            jmp     short near ptr Plough

Life was so much easier when a symbolic opcode really meant something...
 
Exactly. There's a lot about Intel x86 assembly language that I don't like. For example, "MOV" has so many meanings;

What meanings does it have other than "copy a value from one register-or-memory-location to another register-or-memory-location"? I suppose if you were previously used to a RISC architecture it might seem strange to have the same mnemonic for loads, stores, and register-to-register copies but I like the regularity of having just one. x86 assembly has become a lot more complicated and ugly in more recent CPUs but the original 16-bit language is quite nice IMO.

there is more than one way to encode an operation and you have to rely on luck if it matters.

Can you give a (non MASM-specific) example? The assemblers I've used (A86 and nasm/yasm) always pick the shortest encoding. If there's more than one shortest encoding (e.g. "MOV rw,rmw" or "MOV rmw,rw" for a register-to-register copy) then it doesn't matter which the assembler picks (unless you're doing something very strange like reusing the code bytes as data, or counting bytes after compression for size-coding purposes).

One thing that surprised me is that the built-in assembler in DEBUG sometimes generates different code than MASM (or a handful of other assemblers) does.

Eric Isaacson's A86 assembler is an interesting case here - it actually uses the encoding ambiguities to steganographically watermark your assembled code so that he could prove it was assembled with A86 and determine whether the registered or unregistered version was used!
 
What meanings does it have other than "copy a value from one register-or-memory-location to another register-or-memory-location"? I suppose if you were previously used to a RISC architecture it might seem strange to have the same mnemonic for loads, stores, and register-to-register copies but I like the regularity of having just one. x86 assembly has become a lot more complicated and ugly in more recent CPUs but the original 16-bit language is quite nice IMO.

That's missing the point. The idea of an assembler is that it's the next closest thing to coding in direct machine language--there should be a direct isomorphism between an opcode and its symbolic expression--or at least a way to express it symbolically. That you cannot, but must resort to machine-language encoding is the biggest failure of x86 assembly. I've used assembly for machines that were much more complex than the x86 and was able to precisely specify the operation used.

The basic function of an assembler is to allow for symbolic addresses. Look at very early assemblers and that's all they did--some even allowed for numeric values for the opcode.

The initial 8086 assembler running under ISIS II did pretty much that--the first thing it did was to load a file containing OPDEFs for all of the instructions. It was slow on an 8080, but it was dependable.

I know--the argument is that "assembly is dead, nobody uses it anymore" so why would it matter?

Forgive me for airing a gripe.
 
Last edited:
That's missing the point. The idea of an assembler is that it's the next closest thing to coding in direct machine language--there should be a direct isomorphism between an opcode and its symbolic expression--or at least a way to express it symbolically.

A bijection in other words? So that disassembling some code and then reassembling it would be a lossless operation?

That you cannot, but must resort to machine-language encoding

I'm still not convinced of a compelling use-case for the assembly language and machine language having a one-to-one correspondence. If I'm writing assembly code, by definition there are aspects of the encoding that I don't care about (because I'm letting the machine take care of that for me). If I were forced to disambiguate between the "MOV rw,rmw" and "MOV rmw,rw" forms when writing "MOV AX,BX" then I'd be adding "noise" to the code that I don't really care about. I suppose one could be the default and writing "MOV. AX,BX" or something could give you the other, but then when would you ever use the "MOV." form?
 
A bijection in other words? So that disassembling some code and then reassembling it would be a lossless operation?

Exactly. Why should assembly language be a lossy one? Is there a precedent for that?

If I wanted a "don't bother me with the details of implementation" language, I'd code in an HLL. That I'm making the effort to use assembly should afford a certain degree of precision. The only other alternative is raw machine language.
 
Eric Isaacson's A86 assembler is an interesting case here - it actually uses the encoding ambiguities to steganographically watermark your assembled code so that he could prove it was assembled with A86 and determine whether the registered or unregistered version was used!

I was just about to mention that, but you beat me to it. I hope there are no differences in performance between various encodings...

This is starting to go way off-topic for an "XTIDE support thread". Maybe an admin can create a new "assembler differences" thread and move some of these posts to it?
 
If I wanted a "don't bother me with the details of implementation" language, I'd code in an HLL. That I'm making the effort to use assembly should afford a certain degree of precision.

When I'm writing in assembly, I care about which instructions are used but not which opcodes. So I care about some of the details of implementation, but not all of them. And if for some reason I do care about the encoding at some point, I can write (for example): "db 0x89, 0xd8 ; mov ax,bx" which not only documents the exact encoding but also says "hey, I'm doing something strange here - be sure you know all the implementations before changing this line". I think that's the ideal way to write a line of code like that.

I hope there are no differences in performance between various encodings...

As far as I've been able to tell, there aren't (at least on 8088 and for encodings which are the same length).
 
This is starting to go way off-topic for an "XTIDE support thread". Maybe an admin can create a new "assembler differences" thread and move some of these posts to it?

No need--I've said my piece. What got me started was a quick web search that appears to turn up that nobody seems to know how to specify a 3-byte unconditional jump in MASM. (At least MASM doesn't assemble the wrong stuff--that was a problem in 1.0.) I hardly code assembly any more--it's increasingly impractical on newer architectures, such as ARM.
 
Back
Top