I am a bit annoyed that I haven’t been able to explain how my spiral challenge solution works clearly enough.
To rehash the challenge it is this: Generate the image below, it will be tested in VICE C64 with Inject To RAM. Smallest number of bytes wins.
The image is a rectangular spiral winding clockwise inwards starting at the upper left corner using inverted @ which has a screen code of $80.
The code loads in so the Entry Point is located at address $73 which is called every time the kernal prints a character. At this point x = $ff, y = $b1 and the Z flag is clear so a bne at address $73 will be taken.
The submitted code
This is the result that won the challenge, it is not really intended to be read and understood as is but I’ll break it down below.
$47 ldy #$ec
$4a adc #$0e
$4c bpl $50
$4e eor #$ff
$50 adc #8
$52 bmi $52
$54 sta 2
$57 bpl $5b
$59 eor #$ff
$5b cmp 2
$5d bcs $60
$61 lda #$20
$63 bcc $67
$67 sta $03c5
$6a inc $68
$6c bne $70
$6e inc $69
$71 cpy #$14
$73 bne $49
$75 beq $46
The entry point is not the first address, after loading and printing out a character the kernal jumps to $73.
; code begins at EntryPoint ; x is row #, y is column # ; x is $ff at entry, loops back to entry point every line org $46 DrawRow: dex ; decrement the row # here to make row loop work without extra cpx ldy #-20 ; y is horizontal position, leftmost = -20, rightmost = +19 DrawChar: ; first iteration y starts with 1, so there are 19 bytes to ignore txa adc #14 ; compensate for initial x value bpl Bottom_Half eor #$ff ; negate for right side Bottom_Half: adc #8 ; largest x=20, so add 8 to y, largest y = 12 Halt: bmi Halt ; when row = $78 the adc will set the N flag so loop infinitely when done sta 2 ; store off the vertical distance from center tya bpl Right_Side eor #$ff Right_Side: ; a = horizontal distance from center cmp 2 ; horiz dist > vert dist? bcs Horiz ; => on a horizontal slice txa ; on a vertical slize Horiz: lsr ; odd or even distance? lda #$20 ; space on odd distance bcc DrawSpace asl ; invert @ on even distance asl DrawSpace: sta $400-40*1-19 ; adjust 1 row up + y start value inc DrawSpace+1 ; increment destination address bne NotCrossPage inc DrawSpace+2 NotCrossPage iny cpy #20 EntryPoint: bne DrawChar ; <- autostarts here with Z clear (address $73) beq DrawRow ; loop each row
The spiral is drawn by sweeping the screen top to bottom, and each row left to right. At the top left corner y = -20 (horizontal position) and x = $ff + 14 = 13 (vertical position). Each column y increments and each row x decrements.
To break down the idea I’ve split up the screen in slices, where the left and right edges towards the center are the “horizontal” slices (pink, light blue), and the top and bottom slices (cyan, purple) are “vertical slices”. There is a section in the center that is always vertical as seen here:
The blue line is considered the center (horizontal and vertical distance are both 0).
Processing begins at the top/left of the screen minus 51 because of the initial values of x and y but once the top left character column is considered -20 (in the y register).
the horizontal distance to the center is (0-20) = 20 = H, and the vertical is 13 ($ff + 14) = V. for this calculation it doesn’t matter if we’re in the upper or lower / left or right distance to the center.
To make up for the screen not being a square I add 8 to the vertical position so V2 = V+8 (there are 40 columns and 25 rows, 40/2 – 25/2 = 8)
From the two distances I just pick the greater value D = H > V2 ? H : V2; and now I can simply check if D is even or odd which is just an lsr / bcc which lets me pick either inverted @ for even numbers (0 is even!) or space for odd numbers!
This is literally all it does. And there is a check for V2 < 0 to stop processing (bmi Halt)
To get the spiral to wind the correct direction (and also not just be all “concentric” rectangles) I just fiddled a bit with constants and branch conditions, I didn’t actually work out the finer details by hand!