What Does This Assembly Language Program Do?

It’s from Leisure Suit Larry 2. I’m convinced it does something. I have no idea what.

Note- The font shown makes it impossible to know whether I’m looking at a capital O or a zero.

[QUOTE=Leisure Suit Larry]
INT 21
JB 0103

MOV [OBEA], AX
JMP O2B2
JMP 1451

CS:

TEST BYTE PTR [OC59],01
JZ 0150

CS:

TEST BYTE PTR [OC59], 02
JZ 014F
IRET

INT 21
JB 0103

TEST BYTE PTR [OC59], 04
JZ 0169
CMP AH, 01
JA 014F
[/QUOTE]

Does it produce a humorous message? Crash the hard disk of a 286? I must know!

It is little more than flow control, switching flow based upon the value of a byte: 1,2,or 4… There is no way of knowing what it does past this, as we have no knowledge of what the values 1,2,4 represent, or what the choices mean. Without knowing its location in memory it isn’t possible to work out what the jump targets are, but since we have so little information anyway it doesn’t matter.

I’m sorry to say that it looks like a load of nonsense to me. INT 21, for example, needs to be called with the function number in AH, but that snippit never sets AH. It has two consecutive jump instructions, which is redundant. Also, the jump and branch instructions reference hexadecimal addresses, with no indication as to what is actually at those addresses.

Edit: Also, the iret is unlikely to make any sense in the context of a program that is making dos calls through int 21(although I could be completely wrong about this; I’m far from an expert in dos programming)

It looks like it tests three different bits of a pointer in memory and then jumps to different addresses depending on what they are. Without knowing what else is in the program, it’s impossible to say what it actually does, but this does appear to be small part of a larger program.

Nothing! That’s the beauty of it!

So what’s the story here? Why this particular snippet of code?

In Leisure Suit Larry 2- Looking For Love In Several Wrong Places, you end up on a tropical island. You want to marry the chieftan’s daughter. He says you must pass several tests. The first test is writing a program in assembly language. Larry writes the program in my OP. He says it is a multitasking, multi user operating system and he calls it Eunuchs- I know enough to get the pun on Unix.

The two consecutive JMP instructions can make sense if the second JMP is located at address 0103. Since conditional jumps (like the first JB) used relative addressing, it was pretty common in the early days to make them jump to a JMP instruction located nearby, which then took control to some distant location.

The IRET is more mysterious.

And, for the record, all those Os are really zeros.

It’s a snippet of dis-assembled code that doesn’t do anything in particular. Larry wouldn’t even write code that looked like that, he’d use actual label names and wouldn’t know what the addresses or offsets were. This was probably copyed from a debugger or dis-assembler to use as for the game graphic because it looked tech-y.

So what kind of assembler code is this? (I’ve only worked in MIPS, and I know it’s not that)

16-bit x86, like for the 8088/8086 up through the 80286, and the 80386 and later in compatibility modes, and various clones of those specifically Intel chips.

The INT 21 opcode means it’s for MS-DOS (or compatible) in specific. The Unix reference is nonsense in this context, even though there were Unix variants for 16-bit x86 hardware.

Interrupt 21 is the MS-DOS general purpose function dispatcher. The only way to know what it is doing is to know what value was placed in the AH register prior to the start of that code snippet.

Without knowing the value in AH, the program could be calling any one of these functions:

00 00 Terminate process
01 01 Character input with echo
02 02 Character output
03 03 Auxiliary input
04 04 Auxiliary output
05 05 Printer output
06 06 Direct console input/output
07 07 Unaltered character input no echo
08 08 Character input without echo
09 09 Display string
0A 10 Buffered keyboard input
0B 11 Check input status
0C 12 Flush input buffer then input
0D 13 Drive reset
0E 14 Select drive
0F 15 Open file
10 16 Close file
11 17 Find first file
12 18 Find next file
13 19 Delete file
14 20 Sequential read
15 21 Sequential write
16 22 Create file
17 23 Rename file
18 24 MSDOS Reserved function ±
19 25 Get current drive
1A 26 Set disc transfer area (DTA) address
1B 27 Get default drive data
1C 28 Get drive data
1D 29 MSDOS Reserved function ±
1E 30 MSDOS Reserved function ±
1F 31 MSDOS Reserved function ±
20 32 MSDOS Reserved function ±
21 33 Random read
22 34 Random write
23 35 Get file size
24 36 Set relative record number
25 37 Set interrupt vector
26 38 Create new PSP
27 39 Random block read
28 40 Random block write
29 41 Parse filename
2A 42 Get date
2B 43 Set date
2C 44 Get time
2D 45 Set time
2E 46 Set verify flag (non-functional in DOS Plus)
2F 47 Get DTA address
30 48 Get DOS version number
31 49 Terminate and stay resident
32 50 Get disc information (undocumented call) §±
33 51 Get or set break flag (non-functional in DOS Plus)
34 52 Find active byte (undocumented call) §±
35 53 Get interrupt vector
36 54 Get drive allocation data
37 55 Set or get DOS switch character
38 56 Get or set country information
39 57 Create directory
3A 58 Delete directory
3B 59 Set current directory
3C 60 Create file
3D 61 Open file
3E 62 Close file
3F 63 Read file or device
40 64 Write file or device
41 65 Delete file
42 66 Set file pointer
43 67 Get or set file attributes
44 68 I/O control
45 69 Duplicate handle
46 70 Redirect handle
47 71 Get current directory
48 72 Allocate memory block
49 73 Release memory block
4A 74 Resize memory block
45 75 Execute program
46 76 Terminate process with return code
4D 77 Get return code
4E 78 Find first file
4F 79 Find next file
50 80 Set address of PSP (undocumented call) §±
51 81 MSDOS Reserved function ±
52 82 MSDOS Reserved function ±
53 83 MSDOS Reserved function ±
54 84 Get verify flag (non-functional in DOS Plus) ±
55 85 MSDOS Reserved function ±
56 86 Rename file (not implemented) ±
57 87 Get or set file date and time stamps (not implemented) ±
58 88 Get or Set allocation strategy (DOS Version 3.0+) ±
59 89 Get extended error information (DOS Version 3.0+) ±
5A 90 Create temporary file (DOS Version 3.0+) ±
5B 91 Create new file (DOS Version 3-0+) ±
5C 92 Lock or unlock file region (DOS Version 3.0+) ±
5D 93 MSDOS Reserved function ±
5E 94 Get machine name/Get or set printer setup (DOS Version 3.1+) ±
5F 95 Device redirection (DOS Version 3.1+) ±
60 96 MSDOS Reserved function ±
61 97 MSDOS Reserved function ±
62 98 Get PSP address (DOS Version 3.0+) ±
63 99 Get lead byte table (DOS Version 2.25 only) ±
64 100 MSDOS Reserved function ±
65 101 Get extended country information (DOS Version 3.3+) ±
66 102 Get or set code page (DOS Version 3.3+) ±
67 103 Set handle count (DOS Version 3.3+) ±
68 104 Commit file (DOS Version 3.3+) ±
E0 224 Call BDOS (See below)

I’ve added a few more comments to make it a bit more readable:

; call MS-DOS general function dispatcher (function code in AH)
; jump if “below” arithmetic flag is set upon return from MS-DOS function
INT 21
JB 0103

; store value in AX register in memory location referenced by pointer
MOV [OBEA], AX
JMP O2B2
JMP 1451 ; this jump is unreachable if this were human-written assembly code.
; it is probably the destination of the jump below instruction after the DOS call above.
; In other words, after the MS-DOS call, either store the return value in AX in a
; particular variable and jump to one location or just jump to another location
; (the second option probably means an error return from the function call)

CS:

; check bit 0 in the memory location referenced by the pointer, and jump if it is not set
TEST BYTE PTR [OC59],01
JZ 0150

CS:

; check bit 1 in the memory location referenced by the pointer, and jump if it is not set
TEST BYTE PTR [OC59], 02
JZ 014F
IRET ; return from interrupt (will be executed if we didn’t jump, i.e. bit 1 is set)

; another MS-DOS function call
INT 21
JB 0103

; check bit 2 in the memory location referenced by the pointer, and jump if it is not set
TEST BYTE PTR [OC59], 04
JZ 0169
CMP AH, 01 ; compare value in AH register against the value 01
JA 014F ; if value in AH register is greater than 01, jump

So as others have pointed out, the code fragment doesn’t really do much

And even that doesn’t really make sense. The JB 0103 is a relative jump meaning jump forward 103 (hex, presumably) bytes from the location of this JB instruction itself. The JMP 1451 instruction is certainly nowhere nearly 103 bytes forward from that JB instruction.

Geez, it’s been YEARS since I’ve coded anything in assembly language! (ETA: Does anybody write 80*86 assembly code any more?)

For a disassembly dump such as this one, the relative operand of the JB (probably something like +06) is shown as an absolute address (0103) , otherwise it would drive people even madder.

I work for a company that does image processing, and we have some routines written in assembly to take advantage of MMX and SSE2, etc. Those were written before the C++ compilers started generating the SSE instructions by themselves.

And yet another remark, to the effect that this code doesn’t make much sense:

There are two INT 21 instructions, both of them followed by a JB 0103 instruction. First of all, it’s unlikely that both would be followed by relative jumps of the same distance forward. More to the point, IIRC, didn’t INT 21 always return with the OVERFLOW flag (or was it the CARRY flag) set to indicate success/failure? Thus, INT 21 was almost universally followed by a conditional jump on that flag.

ETA: Heracles’s remark above ninja’d this post. Still, I don’t remember disassembly dumps displaying relative jumps the way he said, but I could be mis-remembering that. Working with disassembly dumps is going to drive anyone mad anyway, no matter what.

If the assembly was just a little more random gibberish, I might wonder if the bytes of the assembled code, translated into ASCII, might spell something (hey, it’s what I would do to hide a secret message in something like this).
But there’s just enough reasonable structure there to make me think it’s part of a real program (or several fragments combined).
But if someone is really bored and has too much time on their hands, they could check.

If you look at the page of Leisure Suit Larry creator Al Lowe, it seems he was a computer geek. He boasts of programming on an Apple ][ E and using a 300 baud modem. So I’m sure it’s not random gibberish. I will accept the opinion of several of the experts here that it is part of a larger program. So, the hunt continues.

I wrote a few lines today. Worse, I write code to generate raw machine code. It isn’t exactly something you want to do everyday, but when you need it, there isn’t a lot of choice. Some of it involves opcodes that compilers just don’t generate.

The point is that it is a bit like finding a bolt on the road. You feel sure it comes from something special, so you take it to a auto repair place, and they identify it as a GM bolt. One that was made for 20 years and used in hundreds of models of car for many purposes. Sure, it isn’t random bit of metallic junk, but it is so generic that there of zero chance you will ever make further progress. It is x86 code, and fits the paradigm for DOS code. So it really is like knowing it was for a GM vehicle, and one made in the last 20 years. About it.

Actually, the coolest situation would be this:

The text you’re seeing on-screen is generated dynamically by disassembling a random section of the Leisure Suit Larry 2 program itself, in memory or on disk.

(If this were true, the Amiga and Atari ST versions would display something completely different from the MS-DOS version.)

But if it’s always the same text, it’s probably not that.

I emailed Al Lowe. His response follows.

[Quote=Al Lowe]
Tell them that I just grabbed COMMAND.COM from C:\ and grabbed a few lines
of code. Cheap, quick, simple.

It also makes the following joke that much funnier:

CHIEF: What did you write, Larry?
LARRY: A multi-tasking, multi-user OS that runs on 6502s. I call it Eunuchs!
[/Quote]