(This is part three of the chronicle of my Retrochallenge 2015/01 submission, which is to port the modern-day Apple II game, Structris, to the Atari 8-bit home computer using an obscure language called PL65. The mediocrity starts here.)
Nice weather persuaded me to go for a mountain bike ride rather than code so I didn’t make much progress this weekend. But a few nights ago, I was able to get a scrolling routine working. It is currently written in straight PL65 and isn’t animating as quickly as I would’ve hoped. I plan to ignore this problem for now. I’d rather have something that resembles the original Apple II version of Structris at the end of the month even if that means it’s running poorly.
!--------------------------------------- ! SCROLL !--------------------------------------- PROC SCROLL() INT A1, A2 POINTER P1, P2 BYTE V1 BASED P1 BYTE V2 BASED P2 BYTE H, FILL BEGIN FILL = 0 H = HEIGHT A1 = BL ! Address of bottom left of the Tetris well A2 = A1 - 32 P1 = A1 P2 = A2 ! loop until we hit the gray wall on the right WHILE V1 <> $88 DO ! loop until we hit the top of the screen WHILE H > 0 DO IF FILL = 0 AND V1 = $00 THEN FILL = 1 ENDIF IF FILL = 1 THEN V1 = V2 ! Copy pixel from above ENDIF P1 = A2 ! Point to prev pixel A2 = A2 - 32 ! Address of next pixel (the one above) P2 = A2 ! Point to the next pixel DEC H ! Decrement ENDWHILE A1 = A1 + 2 A2 = A1 - 32 P1 = A1 P2 = A2 FILL = 0 H = HEIGHT ENDWHILE END
Slightly De-Mystifying the Ill-Behaved STR$()
In my last post, I expressed disbelief that calling the STR$() function to convert integers to strings could cause my custom graphics mode to freak out. After a little bit of RTFM, I see now that PL65’s pointer data type relies on zero page memory locations.
So it does make more sense that the function has the possibility of interfering with system-related zero-page shadow registers. I’ll need to look at that compiler option to see if anything is suspicious. And if I’m willing to really get to the bottom of it, I can force the function to compile to a specific memory location and attempt tracing through the generated code. For now I have a work-around in place.
Here is the definition of Noahsoft’s STR$() function.
POINTER strptr BYTE strval BASED strptr BYTE BASE DATA 10; FUNC STR$(INT NUM) STRING SBUFF$ BYTE A,B BEGIN SBUFF$="0";A=16 REPEAT DEC A B=NUM MOD BASE+48 IF B>=58 THEN B=B+7 ENDIF NUM=NUM/BASE strptr=.SBUFF+A strval=B UNTIL NUM=0 END SBUFF$[A]
Apple LoRes Graphics on the Atari
The original version of Structris on the Apple II uses the LoRes graphics mode. This is a very blocky but also very colorful 40×80 mode that uses very little RAM (1 nybble per pixel). This means that each Tetris shape can have its own color and the player and walls can have distinct colors, too. Because not much RAM is required, this means that moving shapes around doesn’t take as many cycles as a higher resolution graphics mode.
|Mode||Pixel Width||Pixel Hgt||Bytes per Line||Screen Resolution||Total RAM|
|Apple LoRes||4 color clocks||4 scan lines||40 bytes||40×80||800|
On the Atari computer, only with the GTIA chip, which replaced the CTIA chip found on the earliest model 400s and 800s, does the Atari begin to approach the level of freedom for number of colors found in Apple’s LoRes mode. Atari’s graphics modes 9, 10, and 11 allow going beyond the 4 or 5 color modes possible with the CTIA, but each with its own tradeoffs.
I decided to go with mode 10 (ANTIC mode F), which allows me to pick any 8 color/luminance combinations along with the background which will be black. Unfortunately this still isn’t quite enough to faithfully reproduce Structris. I have enough colors for the Tetris pieces and walls but not enough for a blinking white pixel for the player.
The bad news is, out of the box, Atari mode 10 requires 8K of RAM. This would make moving pixels around very expensive. My solution was to take advantage of Atari’s ability to create custom graphics modes using the display list.
To save processor and RAM resources, my display list is set up to point to the same memory location for each set of 4 scan lines. This brings down the RAM required from 8K to 2K. But I noticed that the original Apple II Structris never exceeds using 32 of the 40 available pixels on any line. This allowed me to set the presumably rarely used Narrow Playfield bit in the DMACTL register. So instead of 80 bytes per line, it will be 64 bytes per line. This means the ANTIC chip requires less time to scan memory and gives that time back to the 6502.
More cycles means I can be a lazier programmer.
|Mode||Pixel Width||Pixel Hgt||Bytes per Line||Screen Resolution||Total Bytes|
|Standard Mode 10||2 Color Clocks||1 scan line||40 bytes||80×192||7680|
|My Custom Mode 10||2 Color Clocks||4 scan line||32 bytes||64×40||1280|
Here is my display list at the moment. Note that each set of 4 scan lines point to the same memory location and each row requires 32 bytes ($20). (Output generated from the sweet “atari800” emulator’s debug console).
> dlist 8DA0: 2x 8 BLANK 8DA2: DLI 8 BLANK 8DA3: 4x LMS A000 MODE F 8DAF: 4x LMS A020 MODE F 8DBB: 4x LMS A040 MODE F 8DC7: 4x LMS A060 MODE F 8DD3: 4x LMS A080 MODE F 8DDF: 4x LMS A0A0 MODE F 8DEB: 4x LMS A0C0 MODE F 8DF7: 4x LMS A0E0 MODE F 8E03: 4x LMS A100 MODE F 8E0F: 4x LMS A120 MODE F 8E1B: 4x LMS A140 MODE F 8E27: 4x LMS A160 MODE F 8E33: 4x LMS A180 MODE F 8E3F: 4x LMS A1A0 MODE F 8E4B: 4x LMS A1C0 MODE F 8E57: 4x LMS A1E0 MODE F 8E63: 4x LMS A200 MODE F 8E6F: 4x LMS A220 MODE F 8E7B: 4x LMS A240 MODE F 8E87: 4x LMS A260 MODE F 8E93: 4x LMS A280 MODE F 8E9F: 4x LMS A2A0 MODE F 8EAB: 4x LMS A2C0 MODE F 8EB7: 4x LMS A2E0 MODE F 8EC3: 4x LMS A300 MODE F 8ECF: 4x LMS A320 MODE F 8EDB: 4x LMS A340 MODE F 8EE7: 4x LMS A360 MODE F 8EF3: 4x LMS A380 MODE F 8EFF: 4x LMS A3A0 MODE F 8F0B: 4x LMS A3C0 MODE F 8F17: 4x LMS A3E0 MODE F 8F23: 4x LMS A400 MODE F 8F2F: 4x LMS A420 MODE F 8F3B: 4x LMS A440 MODE F 8F47: 4x LMS A460 MODE F 8F53: 4x LMS A480 MODE F 8F5F: 4x LMS A4A0 MODE F 8F6B: 4x LMS A4C0 MODE F 8F77: 3x LMS A4E0 MODE F 8F80: DLI LMS A4E0 MODE F 8F83: LMS 9F86 MODE 2 8F86: 3x MODE 2 8F89: JVB 8DA0
Finally, a custom LPLOT function maps screen into a 32×40 matrix so that two adjacent pixels are assigned the same color. This is effectively the 4 color color x 4 scan line pixel found on the Apple II LoRes mode.
!--------------------------------------- ! LPLOT !--------------------------------------- PROC LPLOT (BYTE X, Y, C) POINTER P1 BYTE V1 BASED P1 BEGIN P1 = .FRAME_GR + $20 * Y + X V1 = C END ... ! DRAW WALLS FOR I = 0 TO Y2 + 1 DO LPLOT(X1, I, $88) LPLOT(X2, I, $88) NEXT
In the code snippet here, the instruction LPLOT(X1, I, $88) sets 4 color clocks to whatever color/luminance is defined in color register 8.
Dunno. Hopefully some progress.