Skip to main content
3 of 3
deleted 118 characters in body
Martin Rosenau
  • 2.1k
  • 10
  • 17

MS-DOS machine code (.COM file), 126 bytes

(A 63-byte variant would be possible if the program was allowed to read its own code.)

The first half (63 bytes) of the program are code and look like this:

FC BE<3F>01 AC 50 88 C2 B4 02 CD 21 E8 1A 00 59
4E AC 81 FE<7E>01 7C 03 BE<3F>01 38 C1 75 F2 FE
CA 75 EE 81 FE<3F>01 75 DB 8A 16 00 80 31 C0 8E
D8 31 C9 AC 00 C2 E2 FB 0E 1F 88 16 00 80 C3

The second half (63 bytes) are data and are a 1:1 copy of the code.

(In the 63-byte variant reading its own code, the bytes marked with "<>" have different values and the second half of the program is not present.)

I'm also not sure about the random generator's probability distribution:

The program uses the fact that the clock counters and other information modified by interrupts are stored in segment 0 to generate random numbers.

Converted to assembly code the program looks like this:

# 0x100:
    cld                # Ensure SI is being incremented
    mov si, programEnd # Move SI to the first byte of the copy of the program
nextOutput:
    lodsb              # Load one byte of the program ...
    push ax            # ... save it to the stack ...
    mov dl, al         # ... and output it!
    mov ah, 2
    int 0x21
    call pseudoRandom  # Create a random number (in DL)
    pop cx             # Take the stored byte from the stack
    dec si             # Go back to the last byte loaded
nextSearch:
    lodsb              # Load the next byte
                       # If we loaded the last byte ...
    cmp si, 2*programEnd-0x100
    jl notEndOfProgram # ... the next byte to be loaded ...
    mov si, programEnd # ... is the first byte of the program.
notEndOfProgram:
    cmp cl, al         # If the byte loaded is not equal to ...
                       # ... the last byte written then ...
    jne nextSearch     # ... continue at nextSearch!
    dec dl             # Decrement the random number and ...
    jnz nextSearch     # ... continue at nextSearch until the ...
                       # ... originally random number becomes zero.
    cmp si, programEnd # If the last byte read was not the last byte ...
    jnz nextOutput     # ... of the program then output the next ...
                       # ... byte!

    # Otherwise fall through to the random number generator
    # whose "RET" instruction will cause the program to stop.        

    # The random number generator:
pseudoRandom:
    mov dl, [0x8000]   # Load the last random number generated
                       # (Note that this is uninitialized when
                       # this function is called the first time)
    xor ax, ax         # We use segment 0 which contains the ...
    mov ax, ds         # ... clock information and other data ...
                       # ... modified by interrupts!
    xor cx, cx         # Prepare for 0x10000 loops so ...
                       # ... all bytes in the segment are processed ...
                       # ... once and the value of SI will be ...
                       # ... unchanged in the end!
randomNext:
    lodsb              # Load one byte
    add dl, al         # Add that byte to the next random number
    loop randomNext    # Iterate over all bytes
    push cs            # Restore the segment
    pop ds
    mov [0x8000], dl   # Remember the random number
    ret                # Exit sub-routine

programEnd:
    ## Place a copy of the code as data here ##
Martin Rosenau
  • 2.1k
  • 10
  • 17