RosettaCodeData/Task/Evolutionary-algorithm/8086-Assembly/evolutionary-algorithm.8086

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