## 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 ##