124 lines
3.4 KiB
Plaintext
124 lines
3.4 KiB
Plaintext
bits 16
|
|
cpu 8086
|
|
MRATE: equ 26 ; Mutation rate (MRATE/256)
|
|
COPIES: equ 100 ; Amount of copies to make
|
|
gettim: equ 02Ch ; MS-DOS get time function
|
|
pstr: equ 9 ; MS-DOS print string
|
|
section .text
|
|
org 100h
|
|
;;; Use MS-DOS time to set random seed
|
|
mov ah,gettim
|
|
int 21h
|
|
mov [rnddat],cx
|
|
mov [rnddat+2],dx
|
|
;;; Make first parent (random characters)
|
|
mov di,parent
|
|
mov cx,target.size-1
|
|
getchr: call rndchr ; Get random character
|
|
stosb ; Store in parent
|
|
loop getchr
|
|
mov al,'$' ; Write string terminator
|
|
stosb
|
|
;;; Main loop.
|
|
loop: mov bx,parent ; Print current parent
|
|
mov dx,bx
|
|
call puts
|
|
call fitnes ; Check fitness
|
|
test cl,cl ; If zero, we're done
|
|
jnz nxmut ; If not, do another mutation
|
|
ret ; Quit to DOS
|
|
nxmut: mov dl,0FFh ; DL = best fitness yet
|
|
mov di,kids ; Set DI to start of memory for children
|
|
mov ch,COPIES ; CH = amount of copies to make
|
|
xor bp,bp
|
|
;;; Make copy, mutate, and test fitness
|
|
copy: mov bx,di ; Let BX = where next copy will go
|
|
call mutcpy ; Make the copy (and adjust DI)
|
|
call fitnes ; Check fitness
|
|
cmp cl,dl ; Is it better than the previous best one?
|
|
ja next ; If not, just do the next one,
|
|
mov dl,cl ; Otherwise, this is now the best one
|
|
lea bp,[di-target.size] ; Store a pointer to it in BP
|
|
next: dec ch ; One copy less
|
|
jnz copy ; Make another copy if we need to
|
|
mov si,bp ; We're done, the best child becomes
|
|
mov di,parent ; the parent for the next generation
|
|
mov cx,target.size
|
|
rep movsb
|
|
jmp loop ; Next generation
|
|
;;; Make copy of parent, mutating as we go, and store at [DI]
|
|
mutcpy: mov si,parent
|
|
.loop: lodsb ; Get byte from parent
|
|
stosb ; Store in copy
|
|
cmp al,'$' ; Is it '$'?
|
|
je .out ; Then we're done
|
|
call rand ; Otherwise, should we mutate?
|
|
cmp al,MRATE
|
|
ja .loop ; If not, do next character
|
|
call rndchr ; But if so, get random character,
|
|
mov [di-1],al ; and overwrite the current character.
|
|
jmp .loop ; Then do the next character.
|
|
.out: ret
|
|
;;; Get fitness of character in [BX]
|
|
fitnes: mov si,target
|
|
xor cl,cl ; Fitness
|
|
.loop: lodsb ; Get target character
|
|
cmp al,'$' ; Done?
|
|
je .out ; Then stop
|
|
cmp al,[bx] ; Equal to character under [BX]?
|
|
lahf ; Keep flags
|
|
inc bx ; Increment BX
|
|
sahf ; Restore flags (was al=[bx]?)
|
|
je .loop ; If equal, do next character
|
|
inc cx ; Otherwise, count this as a mismatch
|
|
jmp .loop
|
|
.out: ret
|
|
;;; Generate random character, [A-Z] or space.
|
|
rndchr: call rand ; Get random number
|
|
and al,31 ; Lower five bits
|
|
cmp al,27 ; One of 27 characters?
|
|
jae rndchr ; If not, get new random number
|
|
add al,'A' ; Make uppercase letter
|
|
cmp al,'Z' ; More than 'Z'?
|
|
jbe .out ; If not, it's OK
|
|
mov al,' ' ; Otherwise, give a space
|
|
.out: ret
|
|
;;; Random number generator using XABC algorithm
|
|
;;; Returns random byte in AL
|
|
rand: push cx
|
|
push dx
|
|
mov cx,[rnddat] ; CH=X CL=A
|
|
mov dx,[rnddat+2] ; DH=B DL=C
|
|
inc ch ; X++
|
|
xor cl,ch ; A ^= X
|
|
xor cl,dl ; A ^= C
|
|
add dh,cl ; B += A
|
|
mov al,dh ; C' = B
|
|
shr al,1 ; C' >>= 1
|
|
xor al,cl ; C' ^= A
|
|
add al,dl ; C' += C
|
|
mov dl,al ; C = C'
|
|
mov [rnddat],cx
|
|
mov [rnddat+2],dx
|
|
pop dx
|
|
pop cx
|
|
ret
|
|
;;; Print string in DX, plus a newline, saving registers
|
|
puts: push ax
|
|
push dx
|
|
mov ah,pstr
|
|
int 21h
|
|
mov dx,nl
|
|
int 21h
|
|
pop dx
|
|
pop ax
|
|
ret
|
|
section .data
|
|
nl: db 13,10,'$'
|
|
target: db 'METHINKS IT IS LIKE A WEASEL$'
|
|
.size: equ $-target
|
|
section .bss
|
|
rnddat: resb 4 ; RNG state
|
|
parent: resb target.size ; Place to store current parent
|
|
kids: resb COPIES*target.size ; Place to store children
|