Difference between revisions of "Christmas Tree"

From SizeCoding
Jump to: navigation, search
(Added content of how_it_works.txt from Serato submission)
(Updated Serato asm source to match entry)
Line 53: Line 53:
 
width = 40
 
width = 40
  
*=$304-39
+
;; BASIC header
 +
*= $0801
 +
!word +, 10
 +
!byte $9e
 +
!text "2092"
 +
!byte 0
 +
+ !word 0
 
table
 
table
!byte width-1   
+
!byte -1   
!byte width-3   
+
!byte -3   
!byte width-5   
+
!byte -5   
!byte width-7   
+
!byte -7   
!byte width-3   
+
!byte -3   
!byte width-7   
+
!byte -7   
!byte width-11  
+
!byte -11  
!byte width-15  
+
!byte -15  
!byte width-5   
+
!byte -5   
!byte width-11  
+
!byte -11  
!byte width-17  
+
!byte -17  
!byte width-23  
+
!byte -23  
!byte width-3   
+
!byte -3   
!byte width-3   
+
!byte -3   
entry
+
code
lax table-1,y
+
-- lsr
bmi *
+
sbc #(128-width/2)
lsr
 
 
sta pntr
 
sta pntr
 
lda #'*'
 
lda #'*'
 
- jsr chrout
 
- jsr chrout
 
inx
 
inx
cpx #width
 
 
bne -
 
bne -
 
jsr crlf
 
jsr crlf
 
iny
 
iny
jmp entry
+
entry
 +
lax table,y
 +
bmi --
 +
rts
 
end
 
end
 
</syntaxhighlight>
 
</syntaxhighlight>

Revision as of 14:16, 25 December 2021

During the Vintage Computing Christmas Challenge 2021 the goal was to create the shape of a given Christmas tree with as few bytes as possible. All platforms and languages were allowed. The tree should be centered and look exactly like shown bellow.

                         *
                        ***
                       *****
                      *******
                        ***
                      *******
                    ***********
                  ***************
                       ***** 
                    *********** 
                 ***************** 
              *********************** 
                        ***
                        ***

There were mainly two kinds of approaches:

  • calculate stars using a formula
  • save amount of stars within data

Do to the trunk of the tree, the second type was more efficient.

Shortest DOS version

The shortest DOS version with 44 bytes came from Hellmood:

org 100h			; define start of code for data access
db 80				; screen width, located at [SI], also "push ax"
mov bx,d			; pointer to data for XLAT
mov cx,1120			; 14 lines with 80 chars each
X: mov ax,cx		; get current sequence position
div byte [si]		; transform to [X,Y] in AL, AH
xlat				; get tree width for current line number
sub ah,40			; center tree in the middle
jnc F				; - 
neg ah				; -
F: sub ah,al		; inside or outside tree?
salc				; set AL depending on carry flag
imul ax,byte -42	; transform into STAR or nothing
int 29h				; write current char to screen
loop X				; repeat unil all chars are plotter
ret					; return to prompt (JMP to former AX=0x0000)
d:db 2,2,12,9,6,3,8,6,4,2,4,3,2,1

Winning entry for C64

The shortest entry overall was for the C64 and was submitted by Serato with only 37 bytes:

		!cpu 6510			; enable unintended opcodes in ACME assembler

		chrout = $FFD2
		crlf = $AAD7
		pntr = $D3
		width = 40

		;; BASIC header
		*= $0801
		!word +, 10
		!byte $9e
		!text "2092"
		!byte 0
+		!word 0
table
		!byte -1  
		!byte -3  
		!byte -5  
		!byte -7  
		!byte -3  
		!byte -7  
		!byte -11 
		!byte -15 
		!byte -5  
		!byte -11 
		!byte -17 
		!byte -23 
		!byte -3  
		!byte -3  
code
--		lsr
		sbc #(128-width/2)
		sta pntr
		lda #'*'
-		jsr chrout
		inx
		bne -
		jsr crlf
		iny
entry
		lax table,y
		bmi --
		rts
end

Serato's entry was provided with the following comments:

This entry to the Christmas Tree challenge saves space in six ways:

 1. Using the c64 kernal routines to output individual characters and carriage return/linefeed. These routines save/restore X/Y allowing the registers to be used as loop counters.

 2. Writing to the PNTR kernal variable to indent each row.

 3. Using the unintended LAX opcode to simultaneously load A and X with the table values.

 4. Storing the row widths as 2's complement negative values, to simplify the indent calculation to divide by two (LSR) and subtract (SBC). The inner loop simply counts back up to zero for rendering the correct width.

 5. BASIC loads the Y register from address $30d. This defaults to 0 on startup, so Y does not need to be initialised.

 6. Placing the table before the code in memory, the MSB of the table entries do not match the MSB of the first opcode (LSR), allowing the outer loop exit condition to be set implicitly in the Negative flag as the table values are read.

These optimisations result in 37 bytes of machine code and data. Two further optimisations are possible each of which save 1 byte, but break the rules of this challenge:

 a - LSR and SBC can be replaced with the unintended opcode ARR, if you are willing to "centre" the tree on a virtual screen width of 32 chars.

 b - The BASIC interpreter leaves the line number in the zero page word at $39, so by setting the line number in the basic header to the address of the table, "LAX ($39),y" can be used for the table access.