Asmusr Posted April 15, 2016 Share Posted April 15, 2016 (edited) This is the development thread for the F18A racing demo. Only a few weeks ago I had no idea how 8-bit racing games like Pole Position were created. I had a vague idea that the screen was drawn from polygons like most 3D games today. Then I found this excellent page that explains a lot. The starting point is an image of the road: The image is 512 pixels wide - twice as wide as a normal TI screen - so if we center the view this gives us 128 pixels on each side for scrolling the view from side to side. The F18A allows us to create such a view by setting the page size to 2X1 in VREG 29 (>1D). Once we have done that we can scroll the entire screen left and right by varying the horizontal scroll offset in VREG 27 (>1B) between 0 and 255 (128 is the center). Note that there are no stripes in the road yet, these will be added later by changing the palette. More to follow... Edited October 15, 2021 by Asmusr 10 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted April 15, 2016 Author Share Posted April 15, 2016 After drawing the basic road in Paint Shop Pro I imported it into Magellan. I decided to use the F18A 8 color mode (ECM 3). I also added a few more details, mountains and clouds copied from the Pole Position arcade game. Later I also imported the sprites for the car, and all is included in the attached Magellan file. I then used the Export/Assembler Data option in Magellan to produce the attached road.a99 assembly file. More to follow... road2.mag road.a99 2 Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted April 15, 2016 Share Posted April 15, 2016 For the future Rasmus... 3 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted April 15, 2016 Author Share Posted April 15, 2016 Next step is to make the road look like we're moving. There are two components in this: the alternating color bands that seem to be moving towards you and the curvature of the road. If we had a full bitmap screen and a fast enough processor one way to obtain this effect would be to recalculate and update the bottom part of the screen every frame. But even though the GPU in the F18A might be fast enough we only have 256 8x8 patterns to draw the screen. Fortunately the F18A allows us to use a trick that was also used back in the days: we can change scroll offset and colors for each scanline while the screen is drawn. Scanline effects are really difficult on a stock TI-99/4A because the CPU and the VDP are not in sync, but on the F18A we have a feature called the HSYNC trigger that allows code to be performed each time a scanline has been drawn. So both color bands and curvature are accomplished by scanline effects: color bands by alternating the color palette and curvature by scrolling each individual scanline horizontally by a calculated amount. At any time the base image stays exactly the same, which you would see if you could stop the F18A GPU from performing its task. More to follow... 6 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted April 15, 2016 Author Share Posted April 15, 2016 In the demo, almost everything is done by the F18A GPU. To start the GPU, first you need to upload your code to VDP RAM. Then you need to set 2 VDP registers to tell the GPU where to start. * Init GPU routine * Set the GPU PC which also triggers it LI R0,GPUPRG/256+>3600 BL @VWTR LI R0,>3700 BL @VWTR It used to be difficult to produce code in CPU RAM bound for VDP RAM, but not any longer with the XORG feature of XAS99. Here is an example of a GPU scanline program, IDLE makes the GPU idle until its activated again by a new HSYNC. * * F18A GPU program * GPUPRG LI R15,>47FE ; Set up stack pointer * Set the HSYNC trigger LI R0,>4000 MOVB R0,@>6032 ; Write to VR 50 RETURN IDLE * HSYNC routine CLR R1 ; For storing scanline MOVB @>7000,R1 ; Get scanline no SWPB R1 ; Swap to LSB INC R1 ; Changes affect next line since HSYNC is triggered at end CI R1,240 ; Check if last scanline (30 row mode) JGT RETURN ; Greater - return JLT GPU1 ; Smaller - continue CLR R1 ; Equals - wrap to 0 * Now R1 holds the actual affected scanline * Set palette LI R0,>5010 ; Palette reg 8 MOV @Z,R1 ANDI R1,>0020 ; 0-31 pal 0, 64-32 pal 1 SRL R1,1 AI R1,PALS ; Add palette base LI R2,8 PLOOP MOV *R1+,*R0+ ; Copy palette word DEC R2 JNE PLOOP JMP RETURN ; Return *// GPUPRG More to follow... 4 Quote Link to comment Share on other sites More sharing options...
Tursi Posted April 17, 2016 Share Posted April 17, 2016 Beautifully smooth! Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted April 21, 2016 Share Posted April 21, 2016 Mesmerizing... Quote Link to comment Share on other sites More sharing options...
Asmusr Posted April 28, 2016 Author Share Posted April 28, 2016 I'm still working through the info on Lou's Pseudo 3d Page and improving the demo at each step. The first step was to calculate a map that records the depth (z-value) of each scanline of the road. The map is used to generate more accurate road stripes (color bands) and later for displaying sprites on the road. The formula is something like: z = yCamera / (yScanline - yHorizon) I used a Java program to generate the assembly code for the map, which ended up like this: ZTBL DATA >FFFF,>F5C2,>EC4E,>E38D,>DB6D,>D3DC,>CCCC,>C631 DATA >BFFF,>BA2E,>B4B4,>AF8A,>AAAA,>A60D,>A1AF,>9D89 DATA >9999,>95DA,>9249,>8EE2,>8BA2,>8888,>8590,>82B9 DATA >8000,>7D63,>7AE1,>7878,>7627,>73EC,>71C7,>6FB5 DATA >6DB6,>6BCA,>69EE,>6822,>6666,>64B8,>6318,>6186 DATA >6000,>5E86,>5D17,>5BB3,>5A5A,>590B,>57C5,>5689 DATA >5555,>542A,>5307,>51EB,>50D7,>4FCA,>4EC5,>4DC5 DATA >4CCD,>4BDA,>4AED,>4A06,>4924,>4848,>4771,>469F DATA >45D1,>4508,>4444,>4384,>42C8,>4210,>415C,>40AC DATA >4000,>3F57,>3EB1,>3E0F,>3D70,>3CD5,>3C3C,>3BA6 DATA >3B13,>3A83,>39F6,>396B,>38E3,>385E,>37DB,>375A DATA >36DB,>365F,>35E5,>356D,>34F7,>3483,>3411,>33A1 DATA >3333,>32C7,>325C,>31F3,>318C,>3127,>30C3,>3061 There are 104 entries in the map - one for each road scanline. As you can see I scaled the z value so that the top scanline of the road has the maximum value >FFFF. Now when you have a scanline you can easily look up the z value. You simply pick one of the most significant bits from the z value to determine the color of the road stripes/color band for the given scanline. Perhaps it's not easy to see from this screenshot, but the result looks much better: More to follow... 3 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted April 28, 2016 Author Share Posted April 28, 2016 Next step was to replace the sine curve demo road with a 'real' track. If you think of the track as a list of road segments of equal length (like a toy race track), it turns out that all the information you need to store is the curvature for each segment. So a long track can be stored in only a few bytes. What is the curvature? Well, if you draw a straight like between two y values, the x value will change with the same amount for each step: x=x+dx If you want to draw a curve instead, the amount you add to x should also change for each step: x=x+dx dx=dx+ddx ddx is the constant curvature that you can save for each track segment. As Lou explains, you will get the most interesting view by showing two segments on the screen at the same time: at the bottom of the screen you show the segment on which the car is currently driving, and at the top you show the next incoming segment (which might have another curvature). For each frame the top segment moves further down the screen (at a steady speed) until it covers the entire screen. At that point the top segment becomes the current segment and a new segment starts appearing at the top of the screen and so forth... By rolling the horizon (hills/clouds) in the opposite direction in the opposite direction of the road curvature (of the bottom segment) you will get an intensified impression of the road turning. I have also added basic steering using joystick 1. I better stop rambling and post the demo as it is now. racing.dsk racing.a99 3 Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted April 28, 2016 Share Posted April 28, 2016 Freakin' A Buddy! I like it so far! I have to leave soon, so I just viewed it with JS99'er, but when I get home tonight it's going straight to R.I. for testing. Quote Link to comment Share on other sites More sharing options...
Asmusr Posted April 28, 2016 Author Share Posted April 28, 2016 Here's a video. Look much smoother on hardware or JS99'er. And here's the entire, surprisingly short GPU screen drawing routine. HILLSY EQU 120 ROADY1 EQU 136 ROADY2 EQU 239 ROADH EQU ROADY2-ROADY1+1 HEIGHT EQU 240 GPUPR1 XORG GPUPRG LI R15,>47FE ; Set up stack pointer * Set the HSYNC trigger LI R0,>4000 MOVB R0,@>6032 ; Write to VR 50 JMP GPU0 RETURN IDLE * HSYNC routine CLR R3 ; For storing scanline MOVB @>7000,R3 ; Get scanline no SWPB R3 ; Swap to LSB INC R3 ; Changes affect next line since HSYNC is at end CI R3,HEIGHT ; Check if last scanline JGT RETURN ; Greater - return JLT GPUP14 ; Smaller - continue GPU0 CLR R3 ; Equals - wrap to 0 * Now R3 holds the actual affected scanline * After last scanline, i.e. before scanline 0, setup next screen * Loop through scanlines, bottom to top, and calculate the X (scroll) offset * Also keep track of min and max values LI R0,(ROADH-1)*2+XOFFS ; Point to X offset for last scanline LI R1,ROADY2 ; Scanline number LI R2,ROADH ; Number of road scanlines MOV @XPOS,R4 ; X (FP 10.6) (currently just zero) S @XCAR,R4 ; Subtract players desired car position CLR R5 ; DX (FP 10.6) LI R6,>7FFF ; Min X, set to max int LI R7,->8000 ; Max X, set to min int MOV @SEGPOS,R10 ; Segment position as FP 14.2 SRL R10,2 ; Convert to integer GPUP1 MOV R4,*R0 ; Save X offset for scanline C R4,R6 ; Compare to minimum JGT GPUP2 ; Greater - continue MOV R4,R6 ; Save as minimum GPUP2 C R4,R7 ; Compare to maximum JLT GPUP3 ; Smaller - continue MOV R4,R7 ; Save as maximum GPUP3 C R1,R10 ; Compare scanline number to segment position JL GPUP4 ; If smaller we are in the top segment MOV @SEGBOT,R8 ; Get bottom segment pointer JMP GPUP5 GPUP4 MOV @SEGTOP,R8 ; Get top segment pointer GPUP5 A *R8,R5 ; DX += DDX A R5,R4 ; X += DX DECT R0 DEC R1 DEC R2 JNE GPUP1 * Check min and max values CLR R9 ; Reset X adjustment AI R6,160*64 ; Min X + 160 (FP 10.6) JGT GPUP6 ; If zero or greater JEQ GPUP6 ; it's within range -160 - 159 MOV R6,R9 ; Otherwise save adjustment JMP GPUP7 GPUP6 AI R7,-159*64 ; Max X - 159 (FP 10.6) JLT GPUP7 ; If zero or smaller JEQ GPUP7 ; it's within range -160 - 159 MOV R7,R9 ; Otherwise save adjustment * Loop again to adjust X values GPUP7 SRA R9,1 ; Divide adjustment by 2 JEQ GPUP9 ; Skip if zero LI R0,XOFFS LI R2,ROADH GPUP8 MOV *R0,R4 ; Get X offset S R9,R4 ; Adjust MOV R4,*R0+ ; Write X offset DEC R2 JNE GPUP8 * Adjust sprite in opposite direction SRA R9,6 ; Convert to integer NEG R9 AI R9,128-32 JGT GPU20 CLR R9 JMP GPU21 GPU20 CI R9,256-64 JLT GPU21 LI R9,256-64 GPU21 SWPB R9 MOVB R9,@SPRATB+1 * Clouds and hills offsets GPUP9 MOV @SEGBOT,R0 MOV *R0,R0 ; Get curvature for bottom segment (FP 10.6) MPY @SPEED,R0 ; Multiply by speed (FP 10.6) SRA R1,2 ; Scale S R1,@CLOUDX SLA R1,1 ; Hills move with double speed of clouds S R1,@HILLSX * Update position MOV @SPEED,R0 SRL R0,4 A R0,@ZPOS * Move segment MOV @SEGPOS,R1 SRL R0,3 A R0,R1 GPUP10 CI R1,HEIGHT*4 ; Bottom scanline in FP 14.2 JLE GPUP13 * Bottom segment out of screen MOV @SEGTOP,R0 MOV R0,@SEGBOT ; Bottom segment = top segment CI R0,SEGLSE ; Check if top segment is last in list JEQ GPUP11 INCT R0 ; Not last - get next JMP GPUP12 GPUP11 LI R0,SEGLST ; Last - get first GPUP12 MOV R0,@SEGTOP ; Set top segment LI R1,ROADY1*4 ; Top road scanline in FP 14.2 * Save segment position GPUP13 MOV R1,@SEGPOS ; Set segment position * Save a copy of curvature for bottom segment MOV @SEGBOT,R0 MOV *R0,@CURVAT * Scrolling: branch on landscape type GPUP14 CI R3,ROADY1 ; Is it road? JHE GPUP16 ; Yes - jump to road CI R3,HILLSY ; Is it hills? JHE GPUP15 ; Yes - jump to hills * Clouds / sky MOV @CLOUDX,R1 JMP GPUP17 * Hills GPUP15 MOV @HILLSX,R1 JMP GPUP17 * Road GPUP16 MOV R3,R1 ; Scanline AI R1,-ROADY1 ; Translate first road scanline to 0 SLA R1,1 ; Convert to word MOV @XOFFS(R1),R1 ; Get offset from table * Scroll scanline GPUP17 LI R2,>2000 ; Center of horizontal scrolling in FP 10.6 S R1,R2 ; Subtract scanline offset MOV R2,R1 ; Copy SLA R1,2 ; Convert to FP 8.8 MOVB R1,@>601B ; Set scroll register ANDI R2,>4000 ; Isolate bit that decides name table SRL R2,6 ; Move bit into place MOVB R2,@>6002 ; Set name table register to 0 or 1 * Road markings CI R3,ROADY1 ; Test scanline JLT GPUP19 ; No road above line ROADH1 * Get scanline depth from map MOV R3,R1 ; Get the scanline AI R1,-ROADY1 ; Translate first road scanline to 0 SLA R1,1 ; Convert to word MOV @ZTBL(R1),R1 ; Get depth from table * Decide which palette to use MOV @ZPOS,R2 ; Get position SLA R2,6 ; Convert to 8.8 FP A R2,R1 ; Add position to make road marking move ANDI R1,>1000 ; 0000-0FFF pal 0, 1000-1FFF pal 1 SRL R1,8 ; Shift bit to fit size of palette AI R1,PALS ; Add palette base * Set palette LI R2,8 ; Number of colors/words in a palette LI R0,>5010 ; Palette reg 8 GPUP18 MOV *R1+,*R0+ ; Copy palette word DEC R2 ; Counter JNE GPUP18 ; Copy loop * Return GPUP19 B @RETURN Next step is to add more sprites... 6 Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted April 28, 2016 Share Posted April 28, 2016 I used a few extra minutes that I should not have, but I noticed two weird things. #1: It did not work in Classic99, dunno why, maybe I'm using an older version. #2: I tried loading it via the HDX and it did not load. This is the first time I've experienced something like that. I'll figure it out later when I have time. I'm guessing but it may have something to do with the stored order. Once I copied the files to my HxC acting as DSK1 it worked flawlessly. "Top Notch" so far Rasmus! Quote Link to comment Share on other sites More sharing options...
Asmusr Posted April 28, 2016 Author Share Posted April 28, 2016 #1: It did not work in Classic99, dunno why, maybe I'm using an older version. Because Classic99 does not emulate all of the F18A, Quote Link to comment Share on other sites More sharing options...
artrag Posted April 28, 2016 Share Posted April 28, 2016 Arcade level, awesome! 2 Quote Link to comment Share on other sites More sharing options...
Tursi Posted April 28, 2016 Share Posted April 28, 2016 I'm in love. That looks absofreakinglutely fantastic. Classic99 only emulates the GPU code and some enhanced sprites, since this is using a different graphics mode it won't work yet. Quote Link to comment Share on other sites More sharing options...
Iwantgames:) Posted April 29, 2016 Share Posted April 29, 2016 Man this is beautiful! Quote Link to comment Share on other sites More sharing options...
+retroclouds Posted April 29, 2016 Share Posted April 29, 2016 This looks great. I think we need a new retro homebrew game console. F18A + TMS9900. Rasmus rocks! Quote Link to comment Share on other sites More sharing options...
Willsy Posted April 29, 2016 Share Posted April 29, 2016 Wow! That's awesome. I notice you have three (it seems) three "stripes" of clouds. It might be interesting to give them a parallax effect. Make the bottom clouds largest, the next row slightly smaller, and the top row smaller still, and move them horizontally at progressively slower speeds for the parallax effect. Amazing demo and a great showcase for the F18 Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted May 26, 2016 Share Posted May 26, 2016 I showed this to a friend of mine yesterday who was a BIG Pole Position fan from his 5200 days. His response was, "I might have to get a T." Yesssssssssssssssssssssssssssss, hooked another one... (with your help Rasmus). Quote Link to comment Share on other sites More sharing options...
ryoder Posted June 1, 2016 Share Posted June 1, 2016 Awesome work. I'm looking into F18A programming as well. I just want to build something cool and run it on my ColecoVision. I'll pm you and maybe you can school me a bit on F18A. Quote Link to comment Share on other sites More sharing options...
cbmeeks Posted June 13, 2016 Share Posted June 13, 2016 Wow! That's awesome. I notice you have three (it seems) three "stripes" of clouds. It might be interesting to give them a parallax effect. Make the bottom clouds largest, the next row slightly smaller, and the top row smaller still, and move them horizontally at progressively slower speeds for the parallax effect. Amazing demo and a great showcase for the F18 Actually, it might look better in reverse. The smaller clouds on the bottom to give the illusion of distance. The larger top clouds would move slower. This is just a hunch. Quote Link to comment Share on other sites More sharing options...
Asmusr Posted June 13, 2016 Author Share Posted June 13, 2016 Actually, it might look better in reverse. The smaller clouds on the bottom to give the illusion of distance. The larger top clouds would move slower. This is just a hunch. I agree that the smaller clouds should be closest to the horizon, but the largest ones should move fastest. Quote Link to comment Share on other sites More sharing options...
cbmeeks Posted June 13, 2016 Share Posted June 13, 2016 I agree that the smaller clouds should be closest to the horizon, but the largest ones should move fastest. That's what I meant. Just typed it wrong on this stupid iPhone. Lol Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted June 14, 2016 Share Posted June 14, 2016 I agree that the smaller clouds should be closest to the horizon, but the largest ones should move fastest. I just know whatever you do will look great. Quote Link to comment Share on other sites More sharing options...
Omega-TI Posted July 17, 2016 Share Posted July 17, 2016 ? Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.