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