; A thing that moves without the screen moving. ; ; Everything drawn on this machine before sprites was in a CELL. Something between two cells ; meant rewriting both of them, and something moving a pixel at a time meant rewriting them ; sixty times a second - which is affordable for one thing and not for twenty. ; ; A sprite is put at a PIXEL, and the device draws it over whatever is already there. Moving ; it costs two bytes: the low and high halves of where it now is. That is the whole of the ; loop below, and it is why this program can run over the shell's own text without disturbing ; a single character of it - nothing underneath is written to at all. ; ; ---- What it leaves behind ---- ; ; The sprite, still in the table. On purpose, and for the same reason Flip leaves the screen ; it flipped to: a program that FAULTED would have left it too, and a system that only tidied ; up after programs which remembered would be one that left a ball sitting over the prompt ; the first time somebody's game crashed. The table is the system's to clear. ; ; Written by Anachronaut #Include services.asm #Program #Base 0x5000 start: ; The atlas, which is where both the tiles and the sprite table live. Four is what CosmOS ; uses for it; see the table in the CosmOS README for who owns which number. INIA 0d4 OUTA 0xE3 INIA 0x30 OUTA 0xE2 INIA 0x03 OUTA 0xE8 SETD.0 Message SWI osPrintString INIA 0x0A OUTA 0x00 CALL putBall CALL screenWidth CALL putSprite ; Key mode, so a key arrives when it is pressed rather than when Return is. INIA 0x01 OUTA 0x02 everyFrame: CALL waitFrame CALL stepBall CALL moveSprite ; Anything typed ends it. Asked for and never waited on, so a key pressed between frames ; is still there when this looks. INA 0x01 INIB 0x01 ; READY AND BRQ everyFrame INA 0x00 ; Taken, so the shell is not handed a key meant for this. RSTA OUTA 0x02 ; Line mode again. The sprite is left where it is. SWI osExit ; ---- The art, into a tile above the font ---- ; ; Two hundred, which is well clear of the 135 glyphs the character generator copies back, so ; nothing here costs the shell a letter. Blitted rather than poked: it is already sixty four ; bytes of Data Segment and the controller moves it in one command. putBall: INIA 0x01 OUTA 0xE0 ; SourceBank: Data Memory. ; The pointer written down before it is read out of memory a byte at a time, because a Data ; Pointer's two halves cannot be got at any other way. 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 begins at 200 times 64, which is 0x3200. RSTA OUTA 0xE5 OUTA 0xE6 INIA 0x40 OUTA 0xE7 ; Sixty four bytes. INIA 0x01 OUTA 0xE8 ; Blit. RET ; ---- How wide the screen is, in pixels ---- ; ; Columns times eight, and this machine cannot multiply. A and B are one sixteen bit shift ; register though: with the column count in A and nothing in B, A:B holds columns times 256, ; and five shifts right divide that by thirty two - which is columns times eight, high byte ; left in A and low byte in B. ; ; Asked rather than assumed, because the shell runs eighty columns and a game may well have ; asked for forty before starting this. screenWidth: INA 0x32 RSTB SHR SHR SHR SHR SHR SETD.0 WidthHigh STA.0 ; B cannot be stored, and there is no move from it. Adding nothing to it puts it in Q, ; which can be copied to A, which can. RSTA CCF ADD MVQA SETD.0 WidthLow STA.0 RET ; ---- The entry, written straight through ---- ; ; The controller's Data port steps its address on after every byte, so all eight go out of ; one port with the address named once. putSprite: INIA 0d4 OUTA 0xE3 INIA 0xC0 OUTA 0xE4 RSTA OUTA 0xE5 ; Sprite nought is at 0xC000. INIA 0xC8 OUTA 0xE9 ; Tile 200. INIA 0x01 OUTA 0xE9 ; Attribute one, so the ball comes out in scheme one's ink. RSTA OUTA 0xE9 OUTA 0xE9 ; X, low then high. INIA 0d96 OUTA 0xE9 RSTA OUTA 0xE9 ; Y, ninety six pixels down. INIA 0x11 OUTA 0xE9 ; One tile across by one down. RSTA OUTA 0xE9 ; Not mirrored, not turned over, not behind. RET ; A frame, which is the only regular beat this machine has. waitFrame: INA 0x30 INIB 0x01 AND BRQ waitFrame RET ; ---- One pixel to the right ---- ; ; Sixteen bits in two bytes, so the high one is stepped only when the low one came back round ; to nought - which is what a carry is, done by hand. stepBall: SETD.0 BallX LDA.0 INCA STA.0 BNA stepCheck SETD.0 BallXHigh LDA.0 INCA STA.0 ; Round to the left edge at the far side. Both halves have to match, and the high one is ; tested first because it is the one that is usually wrong. stepCheck: SETD.0 BallXHigh LDA.0 SETD.1 WidthHigh LDB.1 CCF SUB BNQ stepDone SETD.0 BallX LDA.0 SETD.1 WidthLow LDB.1 CCF SUB BNQ stepDone RSTA STA.0 ; DP0 is still BallX, from the comparison just above. SETD.0 BallXHigh STA.0 stepDone: RET ; Two bytes out of one port, which is the whole cost of moving a sprite. moveSprite: 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, over the shell's own words. Nothing underneath is written to. Press a key." ; Index nought is not a colour, it is a hole - so the corners of the tile are what the ball ; is not, and whatever is behind shows through them. 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 WidthLow: 0x00 WidthHigh: 0x00