Depth: a ball behind the near pillars and in front of the far ones
The demo for what V5 added. Four pillars at four distances, each ONE 8 by 8 tile stretched to its own width and height, and a ball walking past all of them at a distance between two. What it shows is the thing an ordering cannot. The ball is sprite NOUGHT and every pillar is numbered after it, so table order puts the ball in front of all four - and it is still hidden behind two of them, because the depth buffer is asked per column. Caught mid-straddle in the checks: the ball is 48 wide and the pillar 32, so it shows on both sides and nowhere across the middle. Writing it found the conceptual trap in the feature, which is now written down where somebody will hit it. The pillars first carried their own distance in their entries AND wrote that same distance into their columns, so each was asked whether it was in front of itself - and 20 is not nearer than 20, so all four vanished. THE BUFFER IS WHAT HAS BEEN DRAWN AND A SPRITE'S DEPTH IS A QUESTION ASKED OF IT. Scenery writes it; it does not ask. Also found that a scheme only gives a colour to index one. The default palette sets each scheme's paper and ink and nothing between them, so art drawn in index two comes out black until a program writes a palette. And the fixture disk's root directory was full: four blocks, 32 entries, all taken, so adding an app failed the whole disk build. Loudly, which is the right way round - but it is a wall that moves for free, so it is eight blocks and 64 entries now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
cb898450b5
commit
fba1b553d2
@@ -0,0 +1,377 @@
|
||||
; A ball that goes behind some pillars and in front of others.
|
||||
;
|
||||
; Four pillars, each ONE 8 by 8 TILE stretched to its own width and height, at four different
|
||||
; distances. One ball, moving across all of them. Which pillars it passes behind is decided
|
||||
; per screen column by the depth buffer, and that is the thing worth watching: the ball is in
|
||||
; front of the far pillars and behind the near ones IN THE SAME FRAME, without the program
|
||||
; sorting anything or drawing anything twice.
|
||||
;
|
||||
; ---- Why that needs a buffer rather than an ordering ----
|
||||
;
|
||||
; Sprites are drawn in table order, so a program can put one in front of another by numbering
|
||||
; them. That is enough when things are flat. It stops being enough the moment a thing is
|
||||
; nearer than one part of the scenery and further than another - a ball halfway past a pillar
|
||||
; is in front of the floor beside it and behind the pillar itself, and there is no order of
|
||||
; two sprites that means "behind, on those columns only".
|
||||
;
|
||||
; So the scenery says how far away it is, a column at a time, and the ball says how far away
|
||||
; IT is. The device compares them per pixel.
|
||||
;
|
||||
; Written by Anachronaut
|
||||
|
||||
#Include services.asm
|
||||
|
||||
#Program
|
||||
|
||||
#Base 0x5000
|
||||
|
||||
start:
|
||||
; The atlas: the tiles, the sprite table and the depth buffer are all in it.
|
||||
INIA 0d4
|
||||
OUTA 0xE3
|
||||
INIA 0x30
|
||||
OUTA 0xE2
|
||||
INIA 0x03
|
||||
OUTA 0xE8
|
||||
|
||||
SETD.0 Message
|
||||
SWI osPrintString
|
||||
INIA 0x0A
|
||||
OUTA 0x00
|
||||
|
||||
CALL putArt
|
||||
CALL clearDepth
|
||||
CALL putPillars
|
||||
CALL putBall
|
||||
|
||||
INIA 0x01
|
||||
OUTA 0x02 ; Key mode.
|
||||
|
||||
everyFrame:
|
||||
CALL waitFrame
|
||||
CALL stepBall
|
||||
CALL moveBall
|
||||
INA 0x01
|
||||
INIB 0x01 ; READY
|
||||
AND
|
||||
BRQ everyFrame
|
||||
INA 0x00
|
||||
|
||||
RSTA
|
||||
OUTA 0x02 ; Line mode. The sprites and the buffer are the system's to clear.
|
||||
SWI osExit
|
||||
|
||||
; ---- Two tiles ----
|
||||
;
|
||||
; Tile 200 is the ball, which has a shape and so comes out of the Data Segment. Tile 201 is
|
||||
; the pillar, which is a solid block and so is a Fill: 64 bytes of index one, no art needed.
|
||||
putArt:
|
||||
INIA 0x01
|
||||
OUTA 0xE0 ; SourceBank: Data Memory.
|
||||
SETD.1 BallArtAt
|
||||
SETD.0 BallArt
|
||||
STD.0.1
|
||||
LDA.1
|
||||
OUTA 0xE1
|
||||
INCD.1
|
||||
LDA.1
|
||||
OUTA 0xE2
|
||||
INIA 0d4
|
||||
OUTA 0xE3
|
||||
INIA 0x32
|
||||
OUTA 0xE4 ; Tile 200 is at 200 times 64, which is 0x3200.
|
||||
RSTA
|
||||
OUTA 0xE5
|
||||
OUTA 0xE6
|
||||
INIA 0x40
|
||||
OUTA 0xE7
|
||||
INIA 0x01
|
||||
OUTA 0xE8 ; Blit.
|
||||
|
||||
INIA 0x32
|
||||
OUTA 0xE4
|
||||
INIA 0x40
|
||||
OUTA 0xE5 ; Tile 201 is 64 bytes further on, at 0x3240.
|
||||
INIA 0x01
|
||||
OUTA 0xE2 ; Index ONE, which is the only index a scheme gives a colour to:
|
||||
; the default palette sets each scheme's paper and ink and
|
||||
; nothing between them.
|
||||
RSTA
|
||||
OUTA 0xE6
|
||||
INIA 0x40
|
||||
OUTA 0xE7
|
||||
INIA 0x02
|
||||
OUTA 0xE8 ; Fill.
|
||||
RET
|
||||
|
||||
; ---- The buffer, emptied ----
|
||||
;
|
||||
; Nought in a column means nothing is there. It is the program's buffer and nobody clears it
|
||||
; between programs, so a program that means "nothing yet" has to say so.
|
||||
clearDepth:
|
||||
INIA 0d4
|
||||
OUTA 0xE3
|
||||
INIA 0xD0
|
||||
OUTA 0xE4
|
||||
RSTA
|
||||
OUTA 0xE5
|
||||
OUTA 0xE2 ; Fill takes the byte it writes from SourceLow.
|
||||
INIA 0x02
|
||||
OUTA 0xE6
|
||||
INIA 0x80
|
||||
OUTA 0xE7 ; 640 bytes, one a column.
|
||||
INIA 0x02
|
||||
OUTA 0xE8
|
||||
RET
|
||||
|
||||
; ---- The pillars ----
|
||||
;
|
||||
; Four records of seven bytes: x, y, height, depth. Each becomes a sprite entry and a run of
|
||||
; the depth buffer saying how far away that part of the scenery is.
|
||||
putPillars:
|
||||
INIA 0d4
|
||||
SETD.0 PillarLeft
|
||||
STA.0 ; Sprite one is at 0xC010; sprite nought is the ball.
|
||||
INIA 0x10
|
||||
SETD.0 EntryLow
|
||||
STA.0
|
||||
SETD.3 Pillars
|
||||
|
||||
putOnePillar:
|
||||
; ---- The entry, written straight through ----
|
||||
;
|
||||
; Sixteen bytes out of one port with the address named once, because the controller's Data
|
||||
; port steps on after every byte. DP3 walks the record and survives the calls below.
|
||||
INIA 0d4
|
||||
OUTA 0xE3
|
||||
INIA 0xC0
|
||||
OUTA 0xE4
|
||||
SETD.0 EntryLow
|
||||
LDA.0
|
||||
OUTA 0xE5
|
||||
|
||||
INIA 0xC9
|
||||
OUTA 0xE9 ; Tile 201, the pillar block.
|
||||
INIA 0x01
|
||||
OUTA 0xE9 ; Attribute one.
|
||||
; Kept as they go past, because putPillarDepth needs the column and a Data Pointer cannot
|
||||
; be walked backwards to find it again.
|
||||
LDA.3
|
||||
OUTA 0xE9 ; X low.
|
||||
SETD.0 PillarXLow
|
||||
STA.0
|
||||
INCD.3
|
||||
LDA.3
|
||||
OUTA 0xE9 ; X high.
|
||||
SETD.0 PillarXHigh
|
||||
STA.0
|
||||
INCD.3
|
||||
LDA.3
|
||||
OUTA 0xE9 ; Y low.
|
||||
INCD.3
|
||||
LDA.3
|
||||
OUTA 0xE9 ; Y high.
|
||||
INCD.3
|
||||
INIA 0x11
|
||||
OUTA 0xE9 ; One tile by one, which is the art. The size below is the look.
|
||||
RSTA
|
||||
OUTA 0xE9 ; No flags.
|
||||
INIA 0d32
|
||||
OUTA 0xE9
|
||||
RSTA
|
||||
OUTA 0xE9 ; Thirty two pixels wide, whatever the tile is.
|
||||
LDA.3
|
||||
OUTA 0xE9 ; Height low.
|
||||
INCD.3
|
||||
LDA.3
|
||||
OUTA 0xE9 ; Height high.
|
||||
INCD.3
|
||||
|
||||
; ---- And a depth of NOUGHT, which is not the pillar's distance ----
|
||||
;
|
||||
; It is tempting to put the pillar's own distance here, and it is wrong: the depth buffer
|
||||
; is what has already been DRAWN, and a sprite's depth is a QUESTION ASKED OF IT. A pillar
|
||||
; that both wrote 20 into its own columns and carried 20 would be asked whether it was in
|
||||
; front of itself, and 20 is not nearer than 20, so it would vanish. Which it did.
|
||||
;
|
||||
; Scenery writes the buffer. It does not ask.
|
||||
RSTA
|
||||
OUTA 0xE9
|
||||
|
||||
CALL putPillarDepth
|
||||
|
||||
; On to the next record and the next entry.
|
||||
INCD.3
|
||||
SETD.0 EntryLow
|
||||
LDA.0
|
||||
INIB 0d16
|
||||
CCF
|
||||
ADD
|
||||
STQ.0
|
||||
SETD.0 PillarLeft
|
||||
LDA.0
|
||||
DECA
|
||||
STA.0
|
||||
BNA putOnePillar
|
||||
RET
|
||||
|
||||
; ---- How far away those thirty two columns are ----
|
||||
;
|
||||
; A Fill, because the whole run is one number: the pillar is flat on, so every column of it
|
||||
; is the same distance. DP3 is on the depth byte of the record and stays there.
|
||||
;
|
||||
; The buffer begins at 0xD000, whose low byte is nought - so the column's low byte IS the
|
||||
; address's low byte, and its high byte only has to be added to 0xD0. No sixteen bit sum.
|
||||
putPillarDepth:
|
||||
LDA.3
|
||||
OUTA 0xE2 ; The byte to fill with, which is the depth.
|
||||
INIA 0d4
|
||||
OUTA 0xE3
|
||||
SETD.0 PillarXHigh
|
||||
LDA.0
|
||||
INIB 0xD0
|
||||
CCF
|
||||
ADD
|
||||
OUTQ 0xE4
|
||||
SETD.0 PillarXLow
|
||||
LDA.0
|
||||
OUTA 0xE5
|
||||
RSTA
|
||||
OUTA 0xE6
|
||||
INIA 0d32
|
||||
OUTA 0xE7 ; Thirty two columns of it.
|
||||
INIA 0x02
|
||||
OUTA 0xE8
|
||||
RET
|
||||
|
||||
; ---- The ball ----
|
||||
;
|
||||
; Sprite nought, so it is in front of every pillar as far as the TABLE is concerned. What
|
||||
; puts it behind some of them is its depth and nothing else, which is the whole point.
|
||||
putBall:
|
||||
INIA 0d4
|
||||
OUTA 0xE3
|
||||
INIA 0xC0
|
||||
OUTA 0xE4
|
||||
RSTA
|
||||
OUTA 0xE5
|
||||
INIA 0xC8
|
||||
OUTA 0xE9 ; Tile 200.
|
||||
INIA 0x02
|
||||
OUTA 0xE9 ; Attribute two.
|
||||
RSTA
|
||||
OUTA 0xE9
|
||||
OUTA 0xE9 ; X, low then high.
|
||||
INIA 0d230
|
||||
OUTA 0xE9
|
||||
RSTA
|
||||
OUTA 0xE9 ; Y.
|
||||
INIA 0x11
|
||||
OUTA 0xE9 ; One tile by one.
|
||||
RSTA
|
||||
OUTA 0xE9 ; No flags.
|
||||
INIA 0d48
|
||||
OUTA 0xE9
|
||||
RSTA
|
||||
OUTA 0xE9
|
||||
INIA 0d48
|
||||
OUTA 0xE9
|
||||
RSTA
|
||||
OUTA 0xE9 ; Forty eight by forty eight, from eight by eight of art.
|
||||
INIA 0d45
|
||||
OUTA 0xE9 ; And forty five away, which is between the pillars.
|
||||
RET
|
||||
|
||||
waitFrame:
|
||||
INA 0x30
|
||||
INIB 0x01
|
||||
AND
|
||||
BRQ waitFrame
|
||||
RET
|
||||
|
||||
; Two pixels to the right, wrapping past the far side. Sixteen bits in two bytes, so the high
|
||||
; one steps only when the low one came round to nought.
|
||||
stepBall:
|
||||
SETD.0 BallX
|
||||
LDA.0
|
||||
INCA
|
||||
INCA
|
||||
STA.0
|
||||
BNA stepCheck
|
||||
SETD.0 BallXHigh
|
||||
LDA.0
|
||||
INCA
|
||||
STA.0
|
||||
stepCheck:
|
||||
SETD.0 BallXHigh
|
||||
LDA.0
|
||||
INIB 0x02
|
||||
CCF
|
||||
SUB
|
||||
BNQ stepDone ; Not past 0x0200, so nowhere near the end.
|
||||
RSTA
|
||||
SETD.0 BallX
|
||||
STA.0
|
||||
SETD.0 BallXHigh
|
||||
STA.0
|
||||
stepDone:
|
||||
RET
|
||||
|
||||
moveBall:
|
||||
INIA 0d4
|
||||
OUTA 0xE3
|
||||
INIA 0xC0
|
||||
OUTA 0xE4
|
||||
INIA 0x02
|
||||
OUTA 0xE5 ; X is bytes two and three of the entry.
|
||||
SETD.0 BallX
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
SETD.0 BallXHigh
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
RET
|
||||
|
||||
#Data
|
||||
|
||||
#Base 0x3000
|
||||
|
||||
Message:
|
||||
"A ball, behind the near pillars and in front of the far ones. Press a key."
|
||||
|
||||
; x, y, height, depth. Nearer pillars are taller, which is the only thing making this look
|
||||
; like distance rather than like four bars.
|
||||
Pillars:
|
||||
0x50 0x00 0x64 0x00 0xF0 0x00 0d20 ; x 80, y 100, 240 tall, near
|
||||
0xC8 0x00 0xDC 0x00 0x78 0x00 0d60 ; x 200, y 220, 120 tall, far
|
||||
0x68 0x01 0xA0 0x00 0xB4 0x00 0d30 ; x 360, y 160, 180 tall, nearish
|
||||
0xF4 0x01 0x04 0x01 0x50 0x00 0d80 ; x 500, y 260, 80 tall, furthest
|
||||
|
||||
; Where putPillarDepth reads the column from. DP3 is on the record's depth byte by then, and
|
||||
; a Data Pointer cannot be read backwards, so the two halves are kept here as they go past.
|
||||
PillarXLow:
|
||||
0x00
|
||||
PillarXHigh:
|
||||
0x00
|
||||
PillarLeft:
|
||||
0x00
|
||||
EntryLow:
|
||||
0x00
|
||||
|
||||
BallArt:
|
||||
0x00 0x00 0x01 0x01 0x01 0x01 0x00 0x00
|
||||
0x00 0x01 0x01 0x01 0x01 0x01 0x01 0x00
|
||||
0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
|
||||
0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
|
||||
0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
|
||||
0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01
|
||||
0x00 0x01 0x01 0x01 0x01 0x01 0x01 0x00
|
||||
0x00 0x00 0x01 0x01 0x01 0x01 0x00 0x00
|
||||
|
||||
BallArtAt:
|
||||
#Reserve 0d2
|
||||
|
||||
BallX:
|
||||
0x00
|
||||
BallXHigh:
|
||||
0x00
|
||||
Reference in New Issue
Block a user