mirror of
https://github.com/deater/dos33fsprogs.git
synced 2024-12-25 05:29:34 +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:
|
||||
; 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: NEXT: NEXT: NEXT
|
||||
|
||||
|
||||
; Optimization
|
||||
; 144 bytes: first working version (including DOS33 4-byte size/addr)
|
||||
@ -22,6 +23,7 @@
|
||||
; 136 bytes: store YPOS on stack
|
||||
; 135 bytes: store X to HGR_SCALE rather than TXA+STA
|
||||
; 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
|
||||
|
||||
@ -61,20 +63,19 @@ XDRAW0 = $F65D
|
||||
|
||||
entropy:
|
||||
|
||||
jsr HGR2 ; HGR2
|
||||
; Hires, no text at bottom
|
||||
jsr HGR2 ; Hi-res graphics, no text at bottom
|
||||
|
||||
lda #8
|
||||
sta EPOS ; Unlike BASIC, our loop is *10
|
||||
; 8 to 15 rather than .08 to .15
|
||||
lda #8 ; Unlike the BASIC, our loop is *100
|
||||
sta EPOS ; 8 to 15 rather than .08 to .15
|
||||
|
||||
eloop:
|
||||
lda #4 ; FOR Y=4 to 189 STEP 6
|
||||
pha ; YPOS stored on stack
|
||||
lda #4 ; FOR Y=4 to 189 STEP 6
|
||||
pha ; YPOS stored on stack
|
||||
|
||||
yloop:
|
||||
lda #4 ; FOR X=4 to 278 STEP 6
|
||||
lda #4 ; FOR X=4 to 278 STEP 6
|
||||
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
|
||||
xloop:
|
||||
|
||||
@ -93,17 +94,21 @@ xloop:
|
||||
; on the Apple II", Behavior Research Methods, Instruments,
|
||||
; & Computers, 1987, 19 (4), 397-399.
|
||||
|
||||
; Many of these values are in Applesoft 5-byte floating point
|
||||
|
||||
; get random value in FAC
|
||||
; (floating point accumlator)
|
||||
ldx #1 ; RND(1), Force 1
|
||||
; returns "random" value between 0 and 1
|
||||
jsr RND+6 ; we skip passing the argument
|
||||
; as a floating point value
|
||||
; as that would be a pain
|
||||
; as that would be a pain
|
||||
|
||||
; Compare to E
|
||||
|
||||
jsr MUL10 ; EPOS is E*100
|
||||
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
|
||||
|
||||
cpx EPOS ; compare E*100 to RND*100
|
||||
@ -130,7 +135,7 @@ xloop:
|
||||
|
||||
ldy #>RND_EXP ; point (Y,A) to RND value
|
||||
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
|
||||
|
||||
@ -154,23 +159,28 @@ done:
|
||||
|
||||
|
||||
jsr XDRAW0 ; XDRAW 1 AT X,Y
|
||||
; A is 0 at end. Does that help us?
|
||||
|
||||
nextx: ; NEXT X
|
||||
lda XPOS ; 2
|
||||
clc ; 1
|
||||
adc #6 ; x+=6 ; 2
|
||||
sta XPOS ; 2
|
||||
tax ; save in X for later ; 1
|
||||
|
||||
lda #0 ; inc high bit if we wrap past 256 ; 2
|
||||
adc XPOSH ; 2
|
||||
sta XPOSH ; 2
|
||||
; we know that the X=4 to 278 STEP 6 loop passes through exactly 256
|
||||
; so we can check for that by looking for overflow to zero
|
||||
|
||||
beq xloop ; if high byte zero, not at end ; 2
|
||||
cpx #22 ; see if less than 278 ; 2
|
||||
bcc xloop ; if so, loop ; 2
|
||||
bne skip ; 2
|
||||
inc XPOSH ; 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
|
||||
pla ; YPOS on stack
|
||||
adc #5 ; y+=6
|
||||
|
Loading…
Reference in New Issue
Block a user