• Please review our updated Terms and Rules here

Help me get into this guy's mind: 8086 INC/DEC pairs

Trixter

Veteran Member
Joined
Aug 31, 2006
Messages
7,478
Location
Chicagoland, Illinois, USA
I had a desire to pull a digger with one of my favorite arcade game conversions, Robotron. I wanted to disassemble it, add dual-joystick routines, fix the speed, and release it. (The alternative, which I might still do someday, is write one from scratch.)

The code is a bit of a mess; it was written by someone who definitely wasn't familiar with the 8086 instruction set. DEC/JNZ pairs abound, while LOOP is nowhere to be found; that sort of thing. This is all understandable, but there's something completely confusing all throughout the code that I'd like some guidance on. I can't figure out what the guy was doing when he wrote sequences like this:

Code:
seg000:5DE8                 mov     bl, al
seg000:5DEA                 inc     al
seg000:5DEC                 dec     al
seg000:5DEE                 mov     al, [bx-7D70h]
seg000:5DF2                 inc     al
seg000:5DF4                 dec     al
seg000:5DF6                 mov     byte ptr ds:word_9E26, al
seg000:5DF9                 mov     al, [bx-7D6Fh]
seg000:5DFD                 inc     al
seg000:5DFF                 dec     al
seg000:5E01                 mov     byte ptr ds:word_9E26+1, al
seg000:5E04                 mov     si, cx
seg000:5E06                 mov     bl, [si-57EAh]
seg000:5E0A                 inc     bl
seg000:5E0C                 dec     bl
seg000:5E0E                 mov     si, cx
seg000:5E10                 mov     al, [si-580Ah]
seg000:5E14                 inc     al
seg000:5E16                 dec     al
seg000:5E18                 mov     cl, al
seg000:5E1A                 inc     al
seg000:5E1C                 dec     al
seg000:5E1E                 call    sub_6C5D
seg000:5E21                 mov     cl, ds:byte_9E20
seg000:5E25                 inc     cl
seg000:5E27                 dec     cl
seg000:5E29                 mov     si, cx

What in the heck is up with the INC reg/DEC reg pairs? Was he trying to set flags? If so, how come he didn't do anything with the flags? There's not a single conditional jump in that code block.

Before you think "that's data misinterpreted as code", it's not; the data is in a different part of the segment. Besides, the last four instructions are illustrative of other blocks of similarly odd, but deliberate, code. It is everywhere; here's a subroutine:

Code:
seg000:5E88 sub_5E88        proc near
seg000:5E88                 mov     si, cx
seg000:5E8A                 mov     al, [si-574Ah]
seg000:5E8E                 inc     al
seg000:5E90                 dec     al
seg000:5E92                 rcl     ah, 1
seg000:5E94                 xor     al, 3
seg000:5E96                 rcr     ah, 1
seg000:5E98                 mov     si, cx
seg000:5E9A                 mov     [si-574Ah], al
seg000:5E9E                 retn
seg000:5E9E sub_5E88        endp

Can anyone get inside the mind of a guy from 1983 trying to write assembler code for a CPU he wasn't familiar with, and tell me what he was thinking? What is up with all the useless INC/DEC pairs? Was that a flag-setting procedure on 6502 or 6809 or z80 or something?

reenigne had some helpful suggestions offline:
  1. Some kind of automatic translation from a different CPU's machine code.
  2. Not originally written in assembler - artifacts of a compiler for a weird source language.
  3. Something to do with copy protection.

It's not #3, as the INC/DEC sequences are part of the running program after all disk activity is finished. I'm thinking #2 is most likely.
 
reenigne had some helpful suggestions offline:
  1. Some kind of automatic translation from a different CPU's machine code.
  2. Not originally written in assembler - artifacts of a compiler for a weird source language.
  3. Something to do with copy protection.

It's not #3, as the INC/DEC sequences are part of the running program after all disk activity is finished. I'm thinking #2 is most likely.

Well, my first idea was: does this have anything to do with self-modifying code?
Have you looked if this code is ever modified at any point during the loading/execution of the program?
It could be that some of these instructions are overwritten with others at some point?
I wonder what it is that sub_6C5D does, since that one apparently receives a copy of whatever was in al, in cl as well... unless that part of the code was modified. So I wonder if it looks at both cl and al, and perhaps does some kind of comparison.

So it could be some weird copy protection... but otherwise yes it's probably some code generator that does something funny.
 
It certainly looks like bad compiler output. The only benefit of inc/dec whenever you use a variable is to always have the flags reflect it, in case that is a needed feature. Writing a compiler that consistently depends on such a feature is just silly though.

It may be a case of "If it works, don't break it" in combination with limited resources for the people who wrote the compiler. A sort of "How can we make this compiler in as little time as possible?" case. This might be supported by the very generalized way it's consistently using cx/si as a second datastructure pointer, setting si every single time it's used.
 
Last edited:
Looks to me like the mindless use of a macro designed to set the condition code bits every time a register is loaded. While there are some architectures that do exactly that, I suspect this is a set of macros used to implement an abstract machine. I suspect that INC/DEC pairs were used so as not to affect the carry status bit.

No compiler writer in his right mind would let this stuff go by--although the INC/DEC pair at 5E18 don't make sense, as cl is the affected register. But at 5e25, we see them used on cl correctly.
 
Last edited:
My guess is that the guy wanted the game to run at the same speed as the original so he added code everywhere to slow it down.
 
I vote for Chuck's answer: this was written in assembly but using macros to do practically everything. Too bad we can't see the original source code to find out.
 
It certainly looks like bad compiler output. The only benefit of inc/dec whenever you use a variable is to always have the flags reflect it, in case that is a needed feature. Writing a compiler that consistently depends on such a feature is just silly though.

Even so, there are better ways to set the flags (or reg, reg/test reg, reg)... and note that inc/dec don't set all the flags anyway. Carry flag is not reset...
So, hmmmm.
 
That definitely looks like somebody's attempt to set the flags after every load. Why they didn't just do this when they actually needed the flags, and why they used INC/DEC instead of TEST r,r (half the time and half the size) is absolutely beyond me.
 
Well, my first idea was: does this have anything to do with self-modifying code?

Nice idea, but the only self-modifying code I've seen disassembling it so far was to fix up a single return address. So, no.

So it could be some weird copy protection...

Nope, this is code after the game has been loaded and ran a few times.

Looks to me like the mindless use of a macro designed to set the condition code bits every time a register is loaded.

That is what I'm leaning towards as well.

My guess is that the guy wanted the game to run at the same speed as the original so he added code everywhere to slow it down.

An intriguing idea, but this kind of code sequence is all of the place, even in non-speed-sensitive places.

Here's another example, this time putting a message onscreen in text mode, which definitely shows someone who was NOT familiar with how the 8086 worked:

Code:
seg000:0044                 xor     di, di
seg000:0046                 mov     cx, 40
seg000:0049                 nop
seg000:004A
seg000:004A loc_4A:                                 ; CODE XREF: seg000:004Cj
seg000:004A                 movsb
seg000:004B                 inc     di
seg000:004C                 loop    loc_4A

REP MOVSW would have done the trick nicely, obviously. It gets worse; directly after that is this seemingly nonsensical construct:

Code:
seg000:004E                 mov     ds:byte_A9CA, 0
seg000:0053 loc_53:
seg000:0053                 cmp     ds:byte_A9CA, 0
seg000:0058                 jz      short loc_53
seg000:005A                 cmp     ds:byte_A9CA, 0D9h ; '+'
seg000:005F                 jnz     short loc_67

Huh? Oh, wait, just before he printed the "DO YOU WANT COLOR?" message, he replaced the entire keyboard interrupt with one of his own. That was a ton of effort when a single BIOS keyboard read would have worked fine.

I've seen some crazy early PC game code before, but this nearly takes the cake (Dunzhin, so far, is the "worst" -- it's Z80 code that runs in a Z80 emulator!). There's so much INC/DEC fluff and other crud that I think it would be faster and more enjoyable for me to just write my own Robotron from scratch.

I wish there was any indication at all of who ported the game, but there aren't any identifying strings on the disk, and the original packaging/label/manual/warranty/registration card don't have any developer information other than "manufactured by ATARI". This was 32 years ago; I have no idea how to even begin trying to track down who programmed this.
 
REP MOVSW would have done the trick nicely, obviously.

Not really, notice the nasty inc di after the movsb.
So it writes the bytes 'interleaved' to memory, skipping every other byte.
I have no idea what that's trying to achieve, or why... but it is not equivalent to rep movsw at least.

I have no idea how to even begin trying to track down who programmed this.

Even if you do find them, I doubt they'll ever admit to it :)
 
I wish there was any indication at all of who ported the game, but there aren't any identifying strings on the disk, and the original packaging/label/manual/warranty/registration card don't have any developer information other than "manufactured by ATARI". This was 32 years ago; I have no idea how to even begin trying to track down who programmed this.

Hmm... Atari ports of arcade games circa 1983... You could try asking this guy. He worked on 2600 ports rather than PC ports so it's kind of a long shot but he might have some insight or a contact for the next step on the trail.

Edit: Oh wow, there are even some anecdotes about Robotron on that page, and a comment from the author of Apple II Robotron.
 
Last edited:
Not really, notice the nasty inc di after the movsb.

I would have stored the string with its attribute bytes, personally.

Even if you do find them, I doubt they'll ever admit to it :)

Actually, that attitude is changing. 30+ years onward, people are happy to be interviewed (there are some great interviews done by the Antic podcast with a lot of behind-the-scenes gems) and others are happy to share source code.
 
Hmm... Atari ports of arcade games circa 1983... You could try asking this guy. He worked on 2600 ports rather than PC ports so it's kind of a long shot but he might have some insight or a contact for the next step on the trail.

Edit: Oh wow, there are even some anecdotes about Robotron on that page, and a comment from the author of Apple II Robotron.

Holy crap, and this: "I went on to do Ms. Pacman, also for the Apple – and my code for both games was trans-compiled for the PC." I think we have a winner! I'll try to contact him.
 
Steve Hays, who wrote the Apple II version, got in contact with me and told me the person who did the trans-compilation system was Dana Howe. Hopefully I can get in contact with them and can report back.
 
And 6502 does indeed set Zero and Sign flags on register load.

Yes, that might explain it... It seems to emit the inc/dec sequence after every mov reg, mem operation.
Apparently that code was 'good enough'... even though the translation is probably slower than on a real 6502.
Otherwise I guess they would have made the translator just a bit more clever, and only emit the sequence when a following instruction actually looks at the flags, rather than just overwrite them.
 
So the INC/DEC on AL when CL is loaded at 5E18 is an error? And the author didn't notice it?

Could be... The 6502 only has one accumulator, so perhaps they didn't think of that when writing the transcompiler. What is interesting though is that it would actually use cl in that case... So they did think of that :)
Weird.
 
Back
Top