Sunday, 15 April 2012

Itsy Forth: Implementing the Primitives

Itsy Forth is a tiny subset of the Forth programming language. So far we've looked at the Forth outer interpreter, inner interpreter and dictionary. This time we'll define the words required to complete the interpreter.

Peek and Poke

The Forth words to read and write memory are @ and !:
  • @ - ( addr -- x ) read x from addr
  • ! - ( x addr -- ) store x at addr
  • c@ - ( addr -- char ) read char from addr
( before -- after ) shows the contents of the stack before and after the word executes. Here's how @, c@ and ! are implemented. Remember we're keeping the top element of the data stack in the bx register.

        primitive '@',fetch
        mov bx,word[bx]
        jmp next

        primitive '!',store 
        pop word[bx]
        pop bx
        jmp next

        primitive 'c@',c_fetch
        mov bl,byte[bx]
        mov bh,0
        jmp next

Manipulating the Stack

  • drop - ( x -- ) remove x from the stack
  • dup - ( x -- x x ) add a copy of x to the stack
  • swap - ( x y -- y x ) exchange x and y
  • rot - ( x y z -- y z x ) rotate x, y and z

        primitive 'drop',drop
        pop bx
        jmp next

        primitive 'dup',dupe
        push bx
        jmp next

        primitive 'swap',swap
        pop ax
        push bx
        xchg ax,bx
        jmp next

        primitive 'rot',rote
        pop dx
        pop ax
        push dx
        push bx
        xchg ax,bx
        jmp next

Flow Control

if, else, then, begin and again all compile to branch or 0branch.
  • 0branch - ( x -- ) jump if x is zero
  • branch - ( -- ) unconditional jump
  • execute - ( xt -- ) call the word at xt
  • exit - ( -- ) return from the current word
The destination address for the jump is compiled in the cell straight after the branch or 0branch instruction. execute stores the return address on the return stack and exit removes it.

        primitive '0branch',zero_branch
        lodsw
        test bx,bx
        jne zerob_z
        xchg ax,si
zerob_z pop bx
        jmp next

        primitive 'branch',branch
        mov si,word[si]
        jmp next

        primitive 'execute',execute
        mov di,bx
        pop bx
        jmp word[di]

        primitive 'exit',exit
        mov si,word[bp]
        inc bp
        inc bp
        jmp next

Variables and Constants

  • tib - ( -- addr ) address of the input buffer
  • #tib - ( -- addr ) number of characters in the input buffer
  • >in - ( -- addr ) next character in input buffer
  • state - ( -- addr ) true = compiling, false = interpreting
  • dp - ( -- addr ) first free cell in the dictionary
  • base - ( -- addr ) number base
  • last - ( -- addr ) the last word to be defined

        constant 'tib',t_i_b,32768

        variable '#tib',number_t_i_b,0

        variable '>in',to_in,0

        variable 'state',state,0

        variable 'dp',dp,freemem

        variable 'base',base,10

        variable 'last',last,final

        ; execution token for constants
doconst push bx
        mov bx,word[di+2]
        jmp next

        ; execution token for variables
dovar   push bx
        lea bx,[di+2]
        jmp next

Compilation

  • , - ( x -- ) compile x to the current definition
  • c, - ( char -- ) compile char to the current definition
  • lit - ( -- ) push the value in the cell straight after lit

        primitive ',',comma
        mov ax,word[val_dp]
        xchg ax,bx
        add word[val_dp],2
        mov word[bx],ax
        pop bx
        jmp next

        primitive 'c,',c_comma
        mov ax,word[val_dp]
        xchg ax,bx
        inc word[val_dp]
        mov byte[bx],al
        pop bx
        jmp next

        primitive 'lit',lit
        push bx
        lodsw
        xchg ax,bx
        jmp next

Maths / Logic

  • + - ( x y -- z) calculate z=x+y then return z
  • = - ( x y -- flag ) return true if x=y

        primitive '+',plus
        pop ax
        add bx,ax
        jmp next

        primitive '=',equals
        pop ax
        sub bx,ax
        sub bx,1
        sbb bx,bx
        jmp next

Handling Strings

  • count - ( addr -- addr2 len ) addr contains a counted string. Return the address of the first character and the string's length
  • >number - ( double addr len -- double2 addr2 len2 ) convert string to number
addr contains a string of len characters which >number attempts to convert to a number using the current number base. >number returns the portion of the string which can't be converted, if any. If you're a Forth purist this is where Itsy starts to get ugly :-)

        primitive 'count',count
        inc bx
        push bx
        mov bl,byte[bx-1]
        mov bh,0
        jmp next

        primitive '>number',to_number
        pop di
        pop cx
        pop ax
to_numl test bx,bx
        je to_numz
        push ax
        mov al,byte[di]
        cmp al,'a'
        jc to_nums
        sub al,32
to_nums cmp al,'9'+1
        jc to_numg
        cmp al,'A'
        jc to_numh
        sub al,7
to_numg sub al,48
        mov ah,0
        cmp al,byte[val_base]
        jnc to_numh
        xchg ax,dx
        pop ax
        push dx
        xchg ax,cx
        mul word[val_base]
        xchg ax,cx
        mul word[val_base]
        add cx,dx
        pop dx
        add ax,dx
        dec bx
        inc di
        jmp to_numl
to_numz push ax
to_numh push cx
        push di
        jmp next

Terminal Input / Output

  • accept - ( addr len -- len2 ) read a string from the terminal
  • emit - ( char -- ) display char on the terminal
  • word - ( char -- addr ) parse the next word in the input buffer
accept reads a string of characters from the terminal. The string is stored at addr and can be up to len characters long. accept returns the actual length of the string.
word reads the next word from the terminal input buffer, delimited by char. The address of a counted string is returned. The string length will be 0 if the input buffer is empty.

        primitive 'accept',accept
        pop di
        xor cx,cx
acceptl call getchar
        cmp al,8
        jne acceptn
        jcxz acceptb
        call outchar
        mov al,' '
        call outchar
        mov al,8
        call outchar
        dec cx
        dec di
        jmp acceptl
acceptn cmp al,13
        je acceptz
        cmp cx,bx
        jne accepts
acceptb mov al,7
        call outchar
        jmp acceptl
accepts stosb
        inc cx
        call outchar
        jmp acceptl
acceptz jcxz acceptb
        mov al,13
        call outchar
        mov al,10
        call outchar
        mov bx,cx
        jmp next

getchar mov ah,7
        int 021h
        mov ah,0
        ret

outchar xchg ax,dx
        mov ah,2
        int 021h
        ret

        primitive 'word',word
        mov di,word[val_dp]
        push di
        mov dx,bx
        mov bx,word[val_t_i_b]
        mov cx,bx
        add bx,word[val_to_in]
        add cx,word[val_number_t_i_b]
wordf   cmp cx,bx
        je wordz
        mov al,byte[bx]
        inc bx
        cmp al,dl
        je wordf
wordc   inc di
        mov byte[di],al
        cmp cx,bx
        je wordz
        mov al,byte[bx]
        inc bx
        cmp al,dl
        jne wordc
wordz   mov byte[di+1],32
        mov ax,word[val_dp]
        xchg ax,di
        sub ax,di
        mov byte[di],al
        sub bx,word[val_t_i_b]
        mov word[val_to_in],bx
        pop bx
        jmp next

        primitive 'emit',emit
        xchg ax,bx
        call outchar
        pop bx
        jmp next

Searching the Dictionary

  • find - ( addr -- addr2 flag ) look up word in the dictionary
find looks in the Forth dictionary for the word in the counted string at addr. One of the following will be returned:
  • flag = 0, addr2 = counted string - if word not found
  • flag = 1, addr2 = call address if word is immediate
  • flag = -1, addr2 = call address if word is not immediate

        primitive 'find',find
        mov di,val_last
findl   push di
        push bx
        mov cl,byte[bx]
        mov ch,0
        inc cx
findc   mov al,byte[di+2]
        and al,07Fh
        cmp al,byte[bx]
        je findm
        pop bx
        pop di
        mov di,word[di]
        test di,di
        jne findl
findnf  push bx
        xor bx,bx
        jmp next
findm   inc di
        inc bx
        loop findc
        pop bx
        pop di
        mov bx,1
        inc di
        inc di
        mov al,byte[di]
        test al,080h
        jne findi
        neg bx
findi   and ax,31
        add di,ax
 inc di
        push di
        jmp next

Initialisation

  • abort - ( -- ) initialise Itsy then jump to interpret
abort initialises the stacks and a few variables before running the outer interpreter. When Itsy first runs it jumps to abort to set up the system.

        primitive 'abort',abort
        xor ax,ax
        mov word[val_state],ax
        mov word[val_to_in],ax
        mov word[val_number_t_i_b],ax
        xchg ax,bp
        mov sp,-256
        mov si,xt_interpret+2
        jmp next

Up and Running?

Itsy is now around 900 bytes and it's time to give the interpreter a quick test run:
Itsy Forth
Everything seems to be working fine. Next we'll define the compiler so we can continue building Itsy from the Itsy prompt :-)

Tuesday, 3 April 2012

Itsy-Forth: the Dictionary and Inner Interpreter

Itsy Forth is a minimal Forth compiler implemented in under 1kB. Earlier we examined Itsy's outer interpreter. Now we take a closer look at the dictionary and inner interpreter.

Forth Dictionary

Itsy's dictionary is a linked list holding the name and code for each word (subroutine). Each entry in the list has a header containing a link, counted string and XT (execution token). For example here's the dictionary entry for nip:

        ; header
        dw link_to_previous_word
        db 3, 'nip'
xt_nip  dw docolon
        ; body
        dw xt_swap
        dw xt_drop
        dw xt_exit

The first line of the header links to the previous word in the dictionary. The second line holds the word's name preceded by its length. The final line contains the XT, a pointer to the routine which performs the actual operation of the word. Itsy uses four different XTs:

  • docolon - The word is a list of pointers to XTs. Call each in turn.
  • doconst - The word is a constant. Place its value on the data stack.
  • dovar - The word is a variable. Place its address on the data stack.
  • pointer to body - The word is a primitive (machine code). Execute it.

Macros

I'm not a big fan of macros. They're ugly and lock the code to a particular assembler. On the other hand they can add flexibility and make the code less prone to errors. Compare the definition of + with and without macros:

Without macros:

        dw link_to_previous_word
        db 1, '+'
xt_plus dw mc_plus
mc_plus pop ax
        add bx,ax
        jmp next

With macros:

        primitive '+',plus
        pop ax
        add bx,ax
        jmp next

The NASM macros to set up headers and maintain the linked list are pretty simple:

        %define link 0
        %define immediate 080h

        %macro head 4
        %%link dw link
        %define link %%link
        %strlen %%count %1
        db %3 + %%count,%1
        xt_ %+ %2 dw %4
        %endmacro

        %macro primitive 2-3 0
        head %1,%2,%3,$+2
        %endmacro

        %macro colon 2-3 0
        head %1,%2,%3,docolon
        %endmacro

        %macro constant 3
        head %1,%2,0,doconst
        val_ %+ %2 dw %3
        %endmacro

        %macro variable 3
        head %1,%2,0,dovar
        val_ %+ %2 dw %3
        %endmacro

Macro Examples

constant is used to define a Forth constant. E.g. to define false = 0:

        constant 'false',false,0

variable creates a Forth variable. E.g. to create base and initialise to 10:

        variable 'base',base,10

primitive sets up an assembly language word. E.g. to create drop:

        primitive 'drop',drop
        pop bx
        jmp next

colon defines a compiled Forth word. E.g. to define nip:

        colon 'nip',nip
        dw xt_swap
        dw xt_drop
        dw xt_exit

Register Allocation

Itsy's use of the registers is similar to most 8086 Forths. The system stack is used for the data stack while a register is used for the return stack. Note the top element of the data stack is kept in a register to enhance performance:

  • sp - data stack pointer
  • bp - return stack pointer
  • si - Forth instruction pointer
  • di - pointer to current XT
  • bx - TOS (top of data stack)

Itsy's Inner Interpreter

The Forth inner interpreter needs only three simple routines:

  • docolon - the XT to enter a Forth word. Save the Forth IP on the return stack then point it to the word being entered.
  • exit - return from a compiled Forth word. exit recovers the Forth IP from the return stack.
  • next - return from a primitive (machine code) word and call the next XT.
docolon dec bp
        dec bp
        mov word[bp],si
        lea si,[di+2]

next    lodsw
        xchg di,ax
        jmp word[di]

        primitive 'exit',exit
        mov si,word[bp]
        inc bp
        inc bp
        jmp next

Next we'll define approx 30 words and finally get the interpreter up and running. In the meantime I'd love to hear any comments on the code so far :-)

Wednesday, 14 March 2012

Itsy-Forth: the Outer Interpreter of a 1K Tiny Compiler

Itsy Forth is a tiny indirect-threaded compiler for a subset of the Forth programming language. The compiler contains an interactive interpreter which allows the programmer to define new words (subroutines), execute them and even extend the language.

Each word has an entry in the Forth dictionary. New words are defined with : and end with ;. The following example defines a new word inc which calls two words, 1 and +:

: inc 1 + ;

Outer Interpreter

Itsy was developed top-down, first implementing the interpreter. interpret takes the next word from the input buffer and searches for it in the dictionary. If the word isn't found, Itsy attempts to convert it to a number. In compile mode, Itsy will add the word or number to the current definition. Otherwise the word will be executed.

: interpret
begin
    #tib @ >in @ =
    if
        tib 50 accept #tib ! 0 >in !
    then
    32 word find dup
    if
        state @ =
        if
            ,
        else
            execute
        then
    else
        dup rot count >number
        if
            state @
            if
                last @ dup @ last ! dp !
            then
            abort
        then
        drop drop state @
        if
            ['] lit , ,
        then
    then
again
;

The Itsy interpreter uses 23 different words, 5 variables (state, >in, #tib, dp, last) and a constant (tib). Note the variables dp and last are non-standard. dp points to the first free cell in the data area. last points to the last word to be compiled.

Next we'll define the dictionary structure and implement the inner interpreter. In the meantime I'd love to hear any comments on the code so far :-)

Tuesday, 31 January 2012

Microcontroller / EPROM Macro Photos

A couple of the dev boards I've been experimenting with have a fused quartz window leaving the chip visible, offering the perfect opportunity to take a few close up photos.

The 87C752-5F28 is a Harvard architecture microcontroller based on Intel's 80C51 with 2K EPROM and 64 bytes of RAM.


The M27C4001-15F6 EPROM is a non-volatile 512K memory chip. The memory is normally written with an EPROM programmer and erased using ultraviolet light.

Thursday, 29 December 2011

USB Stick Microcontroller Dev Boards

A USB programmable microcontroller is a cheap and easy way to begin experimenting with microcontroller projects. I've recently been playing with 6 USB sticks which can be picked up for under £25 ($40) and discovered three I would recommend, the Micropendous, Minimus and eZ430. Have I missed your favourite USB stick based microcontroller?

Micropendous3

The Micropendous3 (top left) contains an ATMEL AVR AT90USB647 microcontroller with 64K flash, 2K EEPROM and 4K SRAM and allows access to the MCU's 48 I/O lines. The board also supports the AT90USB1287 with 128K flash, 4K EEPROM and 8K SRAM. Support and development software are available online.

Maximus AVR USB 1.2

Despite it's name the Maximus AVR 1.2 (top right) is based on Microchip Technology's PIC18F4550 microcontroller with 32K flash, 2K SRAM and 256 bytes EEPROM. Unfortunately the board layout doesn't allow access to the MCU pins.

Minimus AVR USB 32K

The Minimus 32K (middle left) uses ATMEL's ATMEGA32U2 with 32K flash, 1K EEPROM and 1K SRAM. The board allows access to the MCU's 22 I/O lines. Development software is available online.

eZ430-F2013

Texas Instrument's eZ430-F2013 (middle right) has a detachable target board containing a MSP430F2013 microcontroller with 2K flash and 128 bytes SRAM. The board allows access to all MCU pins. The eZ430 is supplied with development tools for Windows and support is readily available online. The instruction set is easy to learn with just 27 instructions and 4 addressing modes.

Maximus AVR USB 1.0

The Maximus AVR 1.0 (bottom left) is based on the ATMEL AT90USB162 microcontroller with 16K flash, 512 bytes EEPROM and 512 bytes SRAM. As with the later version, the layout doesn't allow access to the MCU pins.

µRlink ST7Ultralite Primer v1.1

The ST7Ultralite (bottom right) is a board based on ST's 8-bit ST7FLITEUS microcontroller with 1K flash and 128 bytes EEPROM. The slightly odd looking board features a light sensor and buzzer and is supplied with a development environment for Windows. Unfortunately the IDE refused to install and there's little information online.

Sunday, 6 November 2011

A Bibliography of Programming Games

The Programming Games Bibliography is an attempt to compile a complete list of articles / papers on the subject. A Programming Game is played by writing programs to compete against each other, typically by attempting to disable opponents in the memory of a virtual computer or by controlling simulated battle robots.

Please help complete the list by leaving a comment if you remember an article not listed, even if you can only recall vague details. :-)

Bibliography

  • Vyssotsky, Victor A. Darwin: A Game of Survival and (Hopefully) Evolution.
    New Jersey: Bell Telephone Laboratories, 1961.
  • ℵ0 "Computer Recreations: Darwin."
    Software: Practice and Experience 2 (Jan-Mar 1972): 93-96.
  • Nelson, Theodor H. "Survival of the Fittest."
    Computer Lib / Dream Machines. Self-published, 1974. 49.
  • Nelson, Theodor H. "Survival of the Fittest."
    The CoEvolution Quarterly (Summer 1975): 141.
  • Taylor Rollo, Mary "Robot War: Strategy for Learning."
    Softtalk (Jan 1981): 18-20.
  • Voskuil, Jon "Robotwar."
    SoftSide (Sep 1981): 92-93.
  • Lubar, David "Robotwar."
    Creative Computing (Oct 1981): 68-69.
  • White, Harry "Robot War."
    The Space Gamer 45 (Nov 1981): 4.
  • Edmunds, William "Robotwar: A Wargame for All Programmers."
    Computer Gaming World (Nov-Dec 1981): 13-16,33.
  • "Computer Gaming World's Robotwar Tournament."
    Computer Gaming World (Nov-Dec 1981): 17.
  • Feigel, Curtis "Robotwar."
    BYTE (Dec 1981): 24-34.
  • Hunter, David "Olympic Metals: The RobotWar Tournaments."
    Softline (Jul 1982): 28-30.
  • "Robotwar: Tournament Results."
    Computer Gaming World (Mar-Apr 1983): 30-31.
  • Hoffman, Paul S. "Robots Maneuver Humans Into Programming."
    The Rainbow (Apr 1983): 140-142.
  • Calle, Carlos "Robot Battle."
    HOT CoCo (Sep 1983): 53-54.
  • "We Have a Draw! Robotwar Tournament Results."
    Computer Gaming World (Apr 1984): 36.
  • Dewdney, A. K. "Computer Recreations: In a game called core war hostile programs engage in a battle of bits."
    Scientific American (May 1984): 14–22.
  • "4th Annual Robotwar Tournament."
    Computer Gaming World (Jan 1985): 9.
  • Dewdney, A. K. "Computer Recreations: A Core War bestiary of viruses, worms and other threats to computer memories."
    Scientific American (Mar 1985): 14-23.
  • "Nobody Wins 4th Annual CGW Robotwar Tournament."
    Computer Gaming World (Jun-Jul 1985): 9.
  • Cottrell, Jon "Core Wars."
    Your Computer (Sep 1985): 72–74.
  • Martínez Lara, Sergio "Batcode, una auténtica batalla dentro de tu ordenador."
    MICROHOBBY 70 (18-24 Mar 1986): 22-23.
  • Martínez Lara, Sergio "Batcode, una auténtica batalla dentro de tu ordenador (II)."
    MICROHOBBY 71 (25 Mar - 1 Apr 1986): 28-30.
  • Martínez Lara, Sergio "Batcode (III)."
    MICROHOBBY 73 (8-14 Apr 1986): 28-29.
  • Martínez Lara, Sergio "Batcode, una batalla dentro de tu ordenador (y IV)."
    MICROHOBBY 74 (15-21 Apr 1986): 28-29.
  • Eliraz, Ziv "Core Wars."
    Dragon User (Sep 1986): 16-21.
  • Dewdney, A. K. "Computer Recreations: A program called MICE nibbles its way to victory at the first Core War tournament."
    Scientific American (Jan 1987): 14-20.
  • Edgar, Gerald A. "Darwin: A survival game for programmers."
    Computer Language (Apr 1987): 79-86.
  • Giustozzi, Corrado "Core Wars."
    MCmicrocomputer 67 (Oct 1987): 118-120.
  • Giustozzi, Corrado "Redcode e Mars."
    MCmicrocomputer 68 (Nov 1987): 138-142.
  • Giustozzi, Corrado "Strategia e tattica."
    MCmicrocomputer 69 (Dec 1987): 142-146.
  • Fisch, Henrik and Joachim Graf. "Kampfprogramme: Schlacht im Computer."
    Happy Computer (Dez 1987): 26-32,117-125.
  • de Weger, Mark "Battle of the RAM."
    The Micro User (Jan 1988): 42-43.
  • Wörrlein, Hartmut and Henrik Fisch. "Das Kampfprogram für den C64."
    Happy Computer (Jan 1988): 51,66-67.
  • de Weger, Mark "The program strikes back."
    The Micro User (Feb 1988): 52-54.
  • Fisch, Henrik "Die Schlacht im Computer (Teil 2)."
    Happy Computer (Feb 1988): 131-133.
  • White, Ron "Pulse Train: Spawning a Computer “Virus”."
    80 Micro (Mar 1988): 16-18.
  • "25th Anniversary of Computer Games Weekend."
    The Computer Museum Report 22 (Spring 1988): 1-2.
  • Giustozzi, Corrado "Il torneo di Boston."
    MCmicrocomputer 76 (Jul-Aug 1988): 88-91.
  • Giustozzi, Corrado "Il torneo italiano."
    MCmicrocomputer 77 (Sep 1988): 128-132.
  • Spork, Maz "Slaget om Siliciummet."
    DataTid (Sep 1988): 92-94.
  • Giustozzi, Corrado "Fiat Lux."
    MCmicrocomputer 79 (Nov 1988): 142-144.
  • Tausend, Thomas "Kampf in der RAM-Arena."
    ATARI magazin (Jan 1989): 38-39.
  • Dewdney, A. K. "Computer Recreations: Of worms, viruses and Core War."
    Scientific American (Mar 1989): 110-113.
  • Simeoni, Fabio "La Guerra dei Nuclei."
    Commodore Computer Club (May 1989): 20-23.
  • Spira, Jeff and William R. Buckley. "Core Wars."
    Commodore Magazine (May 1989): 60-61.
  • Gualdi, Marco "RedCode."
    MCmicrocomputer 88 (Set 1989): 276-278.
  • Giustozzi, Corrado "CW: il secondo Torneo Italiano."
    MCmicrocomputer 92 (Jan 1990): 128-131.
  • "Final Omega Tournament Results."
    Computer Gaming World 70 (Apr 1990): 38.
  • Giustozzi, Corrado "Crobots."
    MCmicrocomputer 97 (Jun 1990): 159-162.
  • Giustozzi, Corrado "Il Primo Torneo di Crobots di MC-Link."
    MCmicrocomputer 108 (Jun 1991): 148-150.
  • Zientara, Wojciech "Wojny rdzeniowe."
    Moje Atari (Lip-Sie 1990): 6-8.
  • Giustozzi, Corrado "Il Primo Torneo di Crobots di MCmicrocomputer."
    MCmicrocomputer 115 (Feb 1992): 156-160.
  • Giustozzi, Corrado "Il Secondo Torneo di CRobots di MCmicrocomputer."
    MCmicrocomputer 124 (Dec 1992): 248-253.
  • Giustozzi, Corrado "Il Terzo Torneo di CRobots di MCmicrocomputer."
    MCmicrocomputer 135 (Dec 1993): 262-267.
  • Giustozzi, Corrado "Il Quarto Torneo di CRobots di MCmicrocomputer."
    MCmicrocomputer 146 (Dec 1994): 270-274.
  • Giustozzi, Corrado "Il Quinto Torneo di CRobots di MCmicrocomputer."
    MCmicrocomputer 157 (Dec 1995): 264-268.
  • Giustozzi, Corrado "Il Sesto Torneo di CRobots di MCmicrocomputer."
    MCmicrocomputer 168 (Dec 1996): 302-307.
  • Giustozzi, Corrado "Il Settimo Torneo di CRobots di MCmicrocomputer."
    MCmicrocomputer 179 (Dec 1997): 218-222.
  • Giustozzi, Corrado "L'Ottavo Torneo di CRobots di MCmicrocomputer."
    MCmicrocomputer 190 (Dec 1998): 144-147.

Friday, 8 July 2011

Darwin: Celebrating 50 Years of Programming Games

screenshot from Darwin-80
Darwin was the earliest programming game, invented by Victor Vyssotsky in August 1961. The idea of the game was to write a program to out-replicate and disable all opponents.

Each program had a number of protected locations and interacted via three functions:

  • PROBE - return the start, end and owner of a memory block
  • CLAIM - reserve memory
  • KILL - disable a program

If a protected location was PROBEd the current program lost control. A CLAIM or KILL could only be used on a location which had previously been PROBEd.

Douglas McIlroy coded Darwin on Bell Labs' IBM 7090 and the game slowly progressed until an unbeatable program emerged, designed by Robert Morris.

Robert's program used an adaptive search. If the program PROBEd an opponent and lost control it would avoid reusing the same offset. A successful offset would be stored then used for future PROBEs and to initialise new copies of itself. Robert's program adapted to become a specialist killer.

In 1972 ℵ0 (Aleph-Null) published the rules of Darwin in Software Practice and Experience, inspiring a number of new implementations. In 1987 Gerald Edgar published his 8080 version to accompany an article in Computer Language.

Unfortunately Darwin faced a major problem. Programs were written in machine language and could only compete against others written for the same computer. When A. K. Dewdney introduced Core War in 1984 he addressed the limitation by defining the MARS virtual computer and Redcode, a simplified assembly language.

Although Darwin has been consigned to the history book, I'll never grow tired of reading about it! Do you remember playing Darwin? If so, I'd love to hear the details.

References


  • ℵ0 "Computer Recreations: Darwin"
    Software: Practice and Experience 2 (Jan-Mar 1972): 93-96.
  • Dewdney, A. K. "In a game called core war hostile programs engage in a battle of bits" Scientific American 250 (May 1984): 14–22.
  • Edgar, Gerald A. "Darwin: A survival game for programmers."
    Computer Language (Apr 1987): 79-86.
  • Levy, Steven Artificial Life: The Quest for a New Creation.
    New York: Pantheon Books, 1992. 317-319.
  • Vyssotsky, Victor A. Darwin: A Game of Survival and (Hopefully) Evolution.
    New Jersey: Bell Telephone Laboratories, 1961.

See Also