Code golf on the CoCo

Hello, I came across a website where people ask others to write programs in any programming language they wish and to make the program as short as possible.

I thought this would be a cool 6809 programming exercise since I’ve mainly been working on optimizing my programs for speed which almost always increases the size of the program, see my blog entries about 6809 optimization routines.

I came across what I thought was a cool request to draw the Mona Lisa on screen using the same method as a programmer named Jakub ‘Ilmenit’ Debski wrote. He dedicating the program to his wife Iza, who pasted away February 15th, 2014. This link describes the method the program needs to work.

The original program by Ilmenit was created for an Atari XL computer with a 6502 processor and is only 250 bytes long. It uses two system calls, one to setup the graphics mode and the other to draw pixels on the screen. My version for the CoCo 3 and 6809 processor is 283 bytes, but I had to write my own drawing routine and a few bytes to put the CoCo 3 in graphics mode so I’m ok with it being 33 bytes longer than the original.

On the Internet Archive wayback machine website I was able to find some info from the original post and I thought I’d reproduce them here, just so that others can find them easier:

“There are 64 pseudo random brush strokes of cycling color. Each stroke is shorter than previous to increase details. Intro code generates set of random numbers starting from initial 32bit seed with Galois LFSRs as a PRNG. This set is used to control direction of brush movement and and starting position. Each stroke has initial 16bit value that influences the seed. Therefore data is 4 bytes (initial seed) + 64*2 bytes of stroke PRNG seeds. Picture of Mona Lisa (3072 bytes) was compressed to those random brush strokes by external optimization program (a few days of work for modern CPU/GPU combo). The process can be seen as lossy compression (23x) of picture into random brush movements.

added on the 2014-04-13 18:59:08 by ilmenit

“The seeds were found with iterative brute-force approach. Each brush stroke is a layer which covers the previous one, therefore is mostly independent in calculations. For each layer there is a 16bit seed to be found that generates the best picture possible. Therefore there are 64 layers * 65536 possible values per layer and the program iterates through all of them (4 million of combinations – acceptable). This should be done for each initial 32bit seed (means *4 billion combinations) but can be performed in parallel. I’ve checked only a few thousands of randomly chosen initial seeds to find a nice looking picture. Details mask had to be used to multiply difference error on critical parts of the picture (eyes, nose, mouth). Otherwise too many details were put in the background.
Thank you all for the kind words.

added on the 2014-04-14 11:26:07 by ilmenit

I uploaded a video of the program running on a CoCo 3:

I included the source code below and I’d love to hear if anyone has any ideas to shrink the code even more. Hopefully it might inspire others to program their CoCo’s.

Updated code: I managed to get the code to 283 bytes, it is not relocatable now as I used the TFR PC,D to set the values for the A and B registers near the start and tweaked a few more things… I also managed to get a version that uses the BASIC ROM to draw the pixel but it only saves 2 bytes (281 bytes total for that version) since I had to prep the code before the draw pixel routine.

The version below is using all my code (no ROM calls) and is 283 bytes when assembled into a Machine language program loadable from BASIC.

I uploaded a zip file to the Color Computer Archive that contains the binary, a .DSK file and the source code below along with the original Atari XL 6502 version that inspired the code golf article.

See you in the next post, Glen

* Assemble with LWASM

 pragma noforwardrefmax   * Forces LWASM to do multi-pass refrences so it can use reuslts that are smaller than 16 bits

Testing       EQU   0  * 0 = no high speed poke, 1 = faster high speed poke & program autostarts

  ORG   $FFB0        * Palette_0

  FCB   %00000000    * Colour 0
  FCB   %00100010    * Colour 1
  FCB   %00110101    * Colour 2
  FCB   %00111110    * Colour 3

	ORG     $7B96      * starts here so we can use $7C as direct page for variables
* Mona Lisa Brush movement table
* BRUSH = [
  FDB   $030A,$37BE,$2F9B,$072B,$0E3C,$F59B,$8A91,$1B0B
  FDB   $0EBD,$9378,$B83E,$B05A,$70B5,$0280,$D0B1,$9CD2
  FDB   $2093,$209C,$3D11,$26D6,$DF19,$97F5,$90A3,$A347
  FDB   $8AF7,$0859,$29AD,$A32C,$7DFC,$0D7D,$D57A,$3051
  FDB   $D431,$542B,$B242,$B114,$8A96,$2914,$B0F1,$532C
  FDB   $0413,$0A09,$3EBB,$E916,$1877,$B8E2,$AC72,$80C7
  FDB   $5240,$8D3C,$3EAF,$AD63,$1E14,$B23D,$238F,$C07B
  FDB   $AF9D,$312E,$96CE,$25A7,$9E37,$2C44,$2BB9,$2139
* ];

        ORCC    #$50                    * Disable interrupts
        TFR     PC,D                    * A=$7C, B=$1A
        LDX     <$8A                    * X=0, in BASIC memory location $008A and $008B are always zero
;        LDA     #%01111100              * A = $7C
        STA     $FF90                   * CoCo 3 Mode, MMU Enabled, GIME IRQ Enabled, GIME FIRQ Enabled, Vector RAM at FEXX enabled, Standard SCS Normal, ROM Map 16k Int, 16k Ext
        TFR     A,DP                    * DP = $7C
        SETDP   $7C                     * Let the assembler know the DP value

  IF Testing
        STB     $FFD9                  	* High Speed mode enabled

* Clear the screen
        CLR     ,X+
        CMPX    #Brush
        BNE     <                       * All clear
* X now points to the Brush table

* CoCo Prep
* Set Hires Screen Resolution and the number of Colours
;        LDA     #%10000000              * = $80, Graphics mode, Colour output, 60 hz, max vertical res
;        LDB     #%00011010            	 * = $1A, 256 x 192 x 16 Colours requires 24,576 bytes = $6000 RAM
;        LDD     #$801A                  * Graphics mode, Colour output, 60 hz, max vertical res & 256 x 192 x 16 Colours requires 24,576 bytes = $6000 RAM
        LDA     #$80
        STD     $FF98                   * Graphics mode, Colour output, 60 hz, max vertical res
      LEAU    cursor_y,PCR  * U = #cursor_y

      LDB   part		     ; part 0-63, from the end
      STB   length       ;

; (word) crc_seed = word_seed;
      LDD   ,X++         ; get the next value in A and B
      STD   crc_seed+2   ; STA crc_seed LSB & STB crc_seed LLSB
      STD   ,U           ;	STA cursor_y & STB cursor_x

      STA   pixel

* inlined_rnd           * crc_seed
      ASL   cursor_y-1  * LLSB
      ROL   cursor_y-2  * LSB
      ROL   cursor_y-3  * MSB
      ROL   cursor_y-4  * MMSB
      BCC   nofeedback

      LDB   #4          * Set to 4 above
      LEAY  -4,U        * U=crc_seed   ;  LDU   #crc_seed   * U=crc_seed
      LDA   ,Y          * Get the crc_seed
      EORA  -4,Y        * XOR the value
      STA   ,Y+         * save the new value + move pointer
      DECB              * decrement the counter
      BNE   xor_seed_loop ;   bpl	xor_seed_loop

      STA   direction	   * a=direction=crc_seed[0]  crc_seed LLSB

      LDB   direction  ; B=direction
      ANDB  #$02       ; and #2		; random value 0 or 2 for cursor_y/cursor_x
      LSRB             ; make it 0 or 1
      LDA   B,U        ; B=0 then LDA  cursor_y else B=1 then LDA cursor_x
      TST   direction
      BMI   >
      ANDA  #$7F       ; and #$7F	; limit value to 0-127
      STA   B,U        ; sta cursor_y(or cursor_x)

* drawPixel(bx, by, COLOR[part AND 3])
* bx is a range from (0 to 127)
* by is a range from (0 to 95)
* Coco 3 Screen resolution is 256 x 192 x 16 colours, starting at $0000
* each row is 128 bytes

      LDD   cursor_y          * D = address, A = Y - co-ordinate, B= x - co-ordinate
      CMPA  #96
      BHS   DoneDrawPoint
      ANDB  #$7F
      RORB              * 1/2 and move bit zero to the carry
      TFR   D,Y
      LDD   #$F003      * Right Nibble and B has the right nibble colour
      ANDB  part        * Make colour a range of 0 to 3
      BCS   >           * If carry is set then it is an odd number (right nibble used)
      LDA   #$10
      MUL               * Set high nibble with the colour (left nibble)
      LDA   #$0F
      ANDA  ,Y          * A = the original pixel value for the old nibble
      STB   SelfModA_OR_B+1  * Self mod to do an OR of A & B
      ORA   #$FF        * A = A logical OR B
      STA   ,Y          * WRite the new value to the screen

;;;;;;;; loop

      DEC   pixel     ;      DEC pixel
      BPL   next_pixel
      DEC   length     ;      DEC length
      BPL   next_length
      DEC   part     ;     DEC part
      BPL   next_part

      BRA  *      * Loop forever

part  FCB   63
; 	FCB   $B7,$1D,$C1,$04  * Little Endian
  FCB   $04,$C1,$1D,$B7  * Big Endian
;  FCB   $E5,$54,$C8,$7E  * Little Endian
  FCB   $7E,$C8  * Big Endian
; next two bytes will be set when the program starts the crc_seed is 4 bytes so we have to make sure we make space for it
cursor_y  EQU   crc_seed+4
cursor_x  EQU   cursor_y+1
direction EQU   cursor_x+1		; this gets zeroed by setting graphics mode
pixel   EQU   direction+1
length  EQU   pixel+1

  IF Testing
      ORG   $A42E       * Make it autostart
      FDB   CoCo_START

This entry was posted in Uncategorized. Bookmark the permalink.

1 Response to Code golf on the CoCo

  1. Daniel says:

    Woah… that’s crazy! All those lines and stuff.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s