Skip to content

Instantly share code, notes, and snippets.

@Sakrac

Sakrac/spiral.s

Last active Jan 31, 2021
Embed
What would you like to do?
The explanation how this works is below, this change moved code to zero page to save 1 byte.
Last minute update:
Disassembly:
$46 dex
$47 ldy #$ec
$49 txa
$4a adc #$0e
$4c bpl $50
$4e eor #$ff
$50 adc #8
$52 bmi $52
$54 sta 2
$56 tya
$57 bpl $5b
$59 eor #$ff
$5b cmp 2
$5d bcs $60
$5f txa
$60 lsr
$61 lda #$20
$63 bcc $67
$65 asl
$66 asl
$67 sta $03c5
$6a inc $68
$6c bne $70
$6e inc $69
$70 iny
$71 cpy #$14
$73 bne $49
$75 beq $46
Source:
org $46
{ ; x is $ff at entry, loops back to entry point every line
{
dex ; decrement the row # here to make row loop work without extra cpx
ldy #-20 ; y is horizontal position, leftmost = -20, rightmost = +19
{ ; first iteration y starts with 1, so there are 19 bytes to ignore
txa
adc #14 ; compensate for initial x value
{
bpl %
eor #$ff
}
adc #8 ; largest x=20, so add 8 to y, largest y = 12
bmi * ; when row = $78 the adc will set the N flag so loop infinitely when done
sta 2 ; store off the positive distance from center
tya
{
bpl %
eor #$ff
}
{
cmp 2
bcs %
txa
}
{
lsr
lda #$20
bcc %
asl
asl
}
{
sta $400-40*1-19 ; adjust 1 row up + y start value
inc !+1
bne %
inc !+2
}
iny
cpy #20
bne ! ; <- autostarts here with Z clear (address $73)
}
beq ! ; loop each row
}
}
Theory: kernal will jsr $73 sometime at startup!
Previous version:
org $2e4
{ ; x is $ed at entry, loops back to entry point every line
{
inx
ldy #-20
{
txa
{
bpl %
eor #$ff
}
adc #8
bmi *
sta 2
tya
{
bpl %
eor #$ff
}
{
cmp 2
bcs %
txa
}
{
lsr
lda #$20
bcc %
asl
asl
}
{
sta $400-40*5
inc !+1
bne %
inc !+2
}
iny
cpy #20
bne !
}
dc.b $4c
}
dc.w !
}
EXPLANATION
First idea:
Spiral is almost drawn as a distance from closest border as alternating @ and space, with the @ closest to the border
0 <= y <= 24 {
0 <= x <= 40 {
x2 = x < 20 ? x : 39-x
y2 = y <= 12 : y : 24-y
*screen++ = ((x2 < y2 ? x2 : y2) & 1) ? '@' : ' '
}
}
To correct the image as a spiral, shift the top left quadrant two steps left
0 <= y <= 24 {
0 <= x <= 39 {
x2 = x < 20 ? x : 39-x
y2 = y <= 12 : y : 24-y
if( x2 < 20 && y <= 12) x2 += 2
*screen++ = ((x2 < y2 ? x2 : y2) & 1) ? '@' : ' '
}
}
at this point: 76 bytes!
optimization #1: I used a basic loader, I can use a 2 byte auto start vector instead so I chose $314 interrupt!
I decided to put the code before $314.
at this point: 66 bytes!
Realized I was copying the row to zero page for comparing, then if y2 < x2 loading it back from zero page instead of using
tya, the lowest bit is the same in both cases
65 bytes!
optimization #1: it is easier to count down to 0 than to count up
24 >= y >= 0 {
39 >= x >= 0 {
x2 = x < 20 ? x : 39-x
y2 = y <= 12 : y : 24-y
if( x2 > 20 && y >= 12) x2 += 2
*screen++ = ((x2 < y2 ? x2 : y) & 1) ? '@' : ' '
}
}
at this point: about the same size, but I realized that I can count the distance in reverse instead.
Back to trying to draw the distance from the border, but this time the max value (19) is the column at the border, and 12 the row at the top.
Unfortunately i had a bug at this point so I took out the quadrant fix and tried to get just the rectangles drawing again.
By chance I discovered that by slightly shifting X and Y I got the spiral shape without the quadrant fix! several bytes saved!
To make the center flip vertically/horizontally cheaper I also switched to counting from -half to +half and doing eor #$ff to "negate" at the center.
-13 <= y < 12 {
-20 <= x < 20 {
y2 = (y < 0 ? y^$ff : y) + 8 ; add 8 to make rows compare with columns in the corners (20 = 12+8)
x2 = x < 0 ? x^$ff : x
*screen++ = ((x2 > y2 ? x2 : y) & 1) ? ' ' : '@'
}
}
at this point: 57 bytes!
This is about as far as I could go without trickery so next was making the line loop cheaper.
I can just keep drawing for a bit after the screen so y can keep going but it needs to stop at some point.
New test: when y2 + 8 check is done, check if it sets the N flag.
y = -13
{
-20 <= x < 20 {
y2 = (y < 0 ? y^$ff : y) + 8 ; add 8 to make rows compare with columns in the corners (20 = 12+8)
bmi *
x2 = x < 0 ? x^$ff : x
*screen++ = ((x2 > y2 ? x2 : y2) & 1) ? ' ' : '@'
}
beq back to loop
}
this saved a cpy #12 and moved the branch, so we're at 55 bytes
Now that branch at the end is two bytes, but if I change to a jmp the start address is already right after at $314.
Problem is I can't initialize the y value to -13 then.
Solution: when entering this code x is kind of close, $ed, which can be compensated by starting to
draw the screen 5 lines earlier and move the increment to the top, but also requires switching x and y
registers.
-20 <= y < 20 {
inx
y2 = (x < 0 ? x^$ff : x) + 8 ; add 8 to make rows compare with columns in the corners (20 = 12+8)
bmi *
x2 = y < 0 ? y^$ff : y
*screen++ = ((x2 > y2 ? x2 : y2) & 1) ? ' ' : '@'
}
dc.b $4c ; jmp opcode
Final size: 52 bytes including load address + autostart vector, effective code size: 48 bytes (well, 50 if you count the jump address :)
@Sakrac

This comment has been minimized.

Copy link
Owner Author

@Sakrac Sakrac commented Jan 19, 2021

76 bytes

@Sakrac

This comment has been minimized.

Copy link
Owner Author

@Sakrac Sakrac commented Jan 25, 2021

no no, 57 bytes!

@Sakrac

This comment has been minimized.

Copy link
Owner Author

@Sakrac Sakrac commented Jan 27, 2021

fiddy-too!

@Sakrac

This comment has been minimized.

Copy link
Owner Author

@Sakrac Sakrac commented Jan 27, 2021

only works in c64 vice auto start, requires x to be $ed on entry

@Sakrac

This comment has been minimized.

Copy link
Owner Author

@Sakrac Sakrac commented Jan 30, 2021

51

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment