mirror of
https://github.com/deater/dos33fsprogs.git
synced 2024-10-09 11:54:59 +00:00
entropy: get it down below 128 bytes
This commit is contained in:
parent
5a905926f1
commit
d868ae2912
70
two-liners/README.entropy
Normal file
70
two-liners/README.entropy
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
Apple II Entropy Demo (128B)
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
BASIC version -- Dave McKellar
|
||||||
|
6502 Assembly Version -- Deater (Vince Weaver)
|
||||||
|
|
||||||
|
Webpage
|
||||||
|
=======
|
||||||
|
http://www.deater.net/weave/vmwprod/entropy_demo/
|
||||||
|
|
||||||
|
Background
|
||||||
|
==========
|
||||||
|
|
||||||
|
Back in the day people would challenge themselves to write one or two line
|
||||||
|
BASIC programs that did impressive things.
|
||||||
|
|
||||||
|
One of my favorite is "Entropy" by Dave McKellar from Toronto.
|
||||||
|
|
||||||
|
This two-line BASIC program can be found on the Beagle Brother's
|
||||||
|
Apple Mechanic Disk.
|
||||||
|
|
||||||
|
I thought it would be interesting to see if I could convert it to 6502
|
||||||
|
assembly language.
|
||||||
|
|
||||||
|
|
||||||
|
The original code looks like this:
|
||||||
|
|
||||||
|
1 ROT=0:FOR I=1 TO 15: READ A,B: POKE A,B: NEXT: DATA
|
||||||
|
232,252,233,29,7676,1,7678,4,7679,0,7680,18,7681,63,
|
||||||
|
7682,36,7683,36,7684,45,7685,45,7686,54,7687,54,7688,63,
|
||||||
|
7689,0
|
||||||
|
2 FOR I=1 TO 99: HGR2: FOR E=.08 TO .15 STEP .01:
|
||||||
|
FOR Y=4 to 189 STEP 6: FOR X=4 to 278 STEP 6:
|
||||||
|
SCALE=(RND(1)<E)*RND(1)*E*20+1: XDRAW 1 AT X,Y:
|
||||||
|
NEXT: NEXT: NEXT: NEXT
|
||||||
|
|
||||||
|
Line 1 sets up an Applesoft BASIC shape table. The Apple II lacks any sort
|
||||||
|
of sprite or graphics acceleration, but BASIC provides a software vector
|
||||||
|
drawing engine. The data statements setup vector art for a simple box shape.
|
||||||
|
|
||||||
|
Line 2 loops across the screen, drawing a box scaled to size 1, but randomly
|
||||||
|
scaling it up to size 2 leading to some interesting patterns. It is drawn
|
||||||
|
with XDRAW which draws the XOR (inverse) of what's already there. When it
|
||||||
|
hits the end of the screen, it starts again, this time erasing things.
|
||||||
|
But again randomly boxes of different sizes are drawn leading to interesting
|
||||||
|
patterns. After iteration 3, boxes of size 3 are also drawn. And after
|
||||||
|
iteration 8 it clears the screen and starts again.
|
||||||
|
|
||||||
|
This sound simple, but it leads to some neat patterns.
|
||||||
|
|
||||||
|
|
||||||
|
128B 6502 Assembly Demo
|
||||||
|
=======================
|
||||||
|
|
||||||
|
I thought it would be neat to see how small I could make this in assembly
|
||||||
|
language. Currently I have it down to 122 bytes (the executable is 126
|
||||||
|
bytes because DOS33 includes size/address filesystem metadata in the file
|
||||||
|
itself).
|
||||||
|
|
||||||
|
It started as a direct port of the BASIC version, it probably can be made
|
||||||
|
smaller. The assembly code calls directly into the BASIC firmware in various
|
||||||
|
places.
|
||||||
|
|
||||||
|
There were some challenges. One is that Applesoft BASIC has no integers:
|
||||||
|
all numbers are stored in 5-byte floating point. This made it hard to do
|
||||||
|
compact math, though the main issue was dealing with the random numbers.
|
||||||
|
|
||||||
|
As an aside, the Applesoft random number generator is pretty awful and you
|
||||||
|
can actually find multiple academic articles written at the time complaining
|
||||||
|
about it.
|
||||||
|
|
@ -11,7 +11,8 @@
|
|||||||
; 24002 FOR I=1 TO 99: HGR2: FOR E=.08 TO .15 STEP .01:
|
; 24002 FOR I=1 TO 99: HGR2: FOR E=.08 TO .15 STEP .01:
|
||||||
; FOR Y=4 to 189 STEP 6: FOR X=4 to 278 STEP 6:
|
; FOR Y=4 to 189 STEP 6: FOR X=4 to 278 STEP 6:
|
||||||
; SCALE=(RND(1)<E)*RND(1)*E*20+1: XDRAW 1 AT X,Y:
|
; SCALE=(RND(1)<E)*RND(1)*E*20+1: XDRAW 1 AT X,Y:
|
||||||
; NEXT: NEXT: NEXT
|
; NEXT: NEXT: NEXT: NEXT
|
||||||
|
|
||||||
|
|
||||||
; Optimization
|
; Optimization
|
||||||
; 144 bytes: first working version (including DOS33 4-byte size/addr)
|
; 144 bytes: first working version (including DOS33 4-byte size/addr)
|
||||||
@ -22,6 +23,7 @@
|
|||||||
; 136 bytes: store YPOS on stack
|
; 136 bytes: store YPOS on stack
|
||||||
; 135 bytes: store X to HGR_SCALE rather than TXA+STA
|
; 135 bytes: store X to HGR_SCALE rather than TXA+STA
|
||||||
; 131 bytes: some fancy branch elimination by noticing X=1
|
; 131 bytes: some fancy branch elimination by noticing X=1
|
||||||
|
; 126 bytes: nextx: simplify by using knowledge of possible x/y vals
|
||||||
|
|
||||||
;BLT=BCC, BGE=BCS
|
;BLT=BCC, BGE=BCS
|
||||||
|
|
||||||
@ -61,20 +63,19 @@ XDRAW0 = $F65D
|
|||||||
|
|
||||||
entropy:
|
entropy:
|
||||||
|
|
||||||
jsr HGR2 ; HGR2
|
jsr HGR2 ; Hi-res graphics, no text at bottom
|
||||||
; Hires, no text at bottom
|
|
||||||
|
|
||||||
lda #8
|
lda #8 ; Unlike the BASIC, our loop is *100
|
||||||
sta EPOS ; Unlike BASIC, our loop is *10
|
sta EPOS ; 8 to 15 rather than .08 to .15
|
||||||
; 8 to 15 rather than .08 to .15
|
|
||||||
|
|
||||||
eloop:
|
eloop:
|
||||||
lda #4 ; FOR Y=4 to 189 STEP 6
|
lda #4 ; FOR Y=4 to 189 STEP 6
|
||||||
pha ; YPOS stored on stack
|
pha ; YPOS stored on stack
|
||||||
|
|
||||||
yloop:
|
yloop:
|
||||||
lda #4 ; FOR X=4 to 278 STEP 6
|
lda #4 ; FOR X=4 to 278 STEP 6
|
||||||
sta XPOS
|
sta XPOS
|
||||||
lda #0 ; can't fit 278 in one byte, need oflo
|
lda #0 ; can't fit 278 in one byte, need overflow byte
|
||||||
sta XPOSH
|
sta XPOSH
|
||||||
xloop:
|
xloop:
|
||||||
|
|
||||||
@ -93,8 +94,12 @@ xloop:
|
|||||||
; on the Apple II", Behavior Research Methods, Instruments,
|
; on the Apple II", Behavior Research Methods, Instruments,
|
||||||
; & Computers, 1987, 19 (4), 397-399.
|
; & Computers, 1987, 19 (4), 397-399.
|
||||||
|
|
||||||
|
; Many of these values are in Applesoft 5-byte floating point
|
||||||
|
|
||||||
; get random value in FAC
|
; get random value in FAC
|
||||||
|
; (floating point accumlator)
|
||||||
ldx #1 ; RND(1), Force 1
|
ldx #1 ; RND(1), Force 1
|
||||||
|
; returns "random" value between 0 and 1
|
||||||
jsr RND+6 ; we skip passing the argument
|
jsr RND+6 ; we skip passing the argument
|
||||||
; as a floating point value
|
; as a floating point value
|
||||||
; as that would be a pain
|
; as that would be a pain
|
||||||
@ -103,7 +108,7 @@ xloop:
|
|||||||
|
|
||||||
jsr MUL10 ; EPOS is E*100
|
jsr MUL10 ; EPOS is E*100
|
||||||
jsr MUL10 ; so multiply rand*100 before compare
|
jsr MUL10 ; so multiply rand*100 before compare
|
||||||
jsr CONINT ; convert to int
|
jsr CONINT ; now convert to int, result in X
|
||||||
; X is now RND(1)*100
|
; X is now RND(1)*100
|
||||||
|
|
||||||
cpx EPOS ; compare E*100 to RND*100
|
cpx EPOS ; compare E*100 to RND*100
|
||||||
@ -130,7 +135,7 @@ xloop:
|
|||||||
|
|
||||||
ldy #>RND_EXP ; point (Y,A) to RND value
|
ldy #>RND_EXP ; point (Y,A) to RND value
|
||||||
lda #<RND_EXP
|
lda #<RND_EXP
|
||||||
jsr FMULT ; multiply FAC by (Y,A)
|
jsr FMULT ; multiply FAC by RND in (Y,A)
|
||||||
|
|
||||||
inc FAC_EXP ; multiply by 2
|
inc FAC_EXP ; multiply by 2
|
||||||
|
|
||||||
@ -154,23 +159,28 @@ done:
|
|||||||
|
|
||||||
|
|
||||||
jsr XDRAW0 ; XDRAW 1 AT X,Y
|
jsr XDRAW0 ; XDRAW 1 AT X,Y
|
||||||
|
; A is 0 at end. Does that help us?
|
||||||
|
|
||||||
nextx: ; NEXT X
|
nextx: ; NEXT X
|
||||||
lda XPOS ; 2
|
lda XPOS ; 2
|
||||||
clc ; 1
|
clc ; 1
|
||||||
adc #6 ; x+=6 ; 2
|
adc #6 ; x+=6 ; 2
|
||||||
sta XPOS ; 2
|
sta XPOS ; 2
|
||||||
tax ; save in X for later ; 1
|
|
||||||
|
|
||||||
lda #0 ; inc high bit if we wrap past 256 ; 2
|
; we know that the X=4 to 278 STEP 6 loop passes through exactly 256
|
||||||
adc XPOSH ; 2
|
; so we can check for that by looking for overflow to zero
|
||||||
sta XPOSH ; 2
|
|
||||||
|
|
||||||
beq xloop ; if high byte zero, not at end ; 2
|
bne skip ; 2
|
||||||
cpx #22 ; see if less than 278 ; 2
|
inc XPOSH ; 2
|
||||||
bcc xloop ; if so, loop ; 2
|
skip:
|
||||||
|
; the X=4 to 278 STEP 6 loop actually ends when X is at 280, which
|
||||||
|
; is 256+24. The lower part of the loop does not hit 24, so we
|
||||||
|
; can check for the end by looking for the low byte at 24.
|
||||||
|
|
||||||
|
cmp #24 ; see if less than 278 ; 2
|
||||||
|
bne xloop ; if so, loop ; 2
|
||||||
;============
|
;============
|
||||||
; 20
|
; 15
|
||||||
nexty: ; NEXT Y
|
nexty: ; NEXT Y
|
||||||
pla ; YPOS on stack
|
pla ; YPOS on stack
|
||||||
adc #5 ; y+=6
|
adc #5 ; y+=6
|
||||||
|
Loading…
Reference in New Issue
Block a user