brett@pigpen (Brett S Bourbin) (08/31/88)
Here is the demo that was talking about before. Sorry for the delay, but I had to retype the whole thing into my UNIX system, since I do not have my 850 interface anymore. Please tell me what you think. Again, it is a RAW demo, it has no sounds or bells or whistles. Maybe when I release the demo where the stars start to rotate according to the joystick and.. whoops, I guess I shouldn't talk about that yet! 8^) ; ; Three-D Scrolling Stars Demo ; ; Version 1.0-Atari800 ; Written by Brett Bourbin, Selgus Limited. ; ; This program is released into the public domain. It may be copied, used, ; hacked to death, as long as you don't try to claim ownership of the code. ; ; This version was written for the MAC/65 assembler, but I did not use any ; of the special features, so it should work fine under the Atari Assem/Ed. ; sdlstl = $0230 ; display list shadow register colpf0 = $02c4 ; playfield shadow color register 0 colbk = $02c8 ; background shadow color register ; dlist = $3800 ; space for the custom display list screen = $4000 ; space for the screen memory code = $5100 ; guess what this is the starting address for? 8^) ; random = $d20a ; random number register vcount = $d40b ; vertical line counter register ; stnum = 12 ; the number of stars on the screen as a time ; ; *= $80 ; page zero usage starx *= *+stnum ; star X co-ordinates stary *= *+stnum ; star Y co-ordinates starz *= *+stnum ; star Z co-ordinates oldx *= *+stnum ; old screen locations for erase routine oldy *= *+stnum plotx *= *+stnum ; new star locations ploty *= *+stnum color *= *+1 ; star color yofset *= *+1 ; Y offset used to plot the star pixel *= *+1 ; more plotting storage limit *= *+1 ; random number upper limit lo *= *+1 ; page zero pointer hi *= *+1 temp *= *+1 mplier *= *+1 ; multiplication registers mcand *= *+1 divsor *= *+1 ; division register starnm *= *+1 ; current star number working on ; ; *= code ; start of the code section (like you didn't know) ; bits .byte $40,$10,$04,$01 ; star pixel offsets inside a screen byte ; lotabl *= *+$60 ; table to hold screen memory line locations hitabl *= *+$60 ; ; ; Get a random number from zero to the value passed in the ACCUM. ; This routine could be better, but is not needed for a demo. 8^) rand sta limit ; save the upper limit rnd lda random ; get the hardware random number cmp limit ; check against the limit bcs rnd ; loop back until it is within the limit rts ; ; Clear out the screen memory. clear lda # <screen ; get the address of the screen memory ldy # >screen sta lo ; save in page zero pointer sty hi ldx #$0f ; initialize the number of 256 byte pages nextpg ldy #0 ; initialize index tya ; copy zero into the ACCUM clrpg sta (lo),y ; clear out the memory dey bne clrpg ; go through the total page inc hi ; increase the pointer to the next page dex ; decrease the page counter bne nextpg ; clear all the pages needed rts ; ; Initialize the screen memory line address table and create the custom ; display list. A table is used to keep the addresses of each line of the ; display so that no math is needed to find the correct Y index into the ; screen memory at plot time. (ie. no need to multiply Y co-ordinate by 40) initad lda # <screen ; get the address of the screen memory ldy # >screen sta lo ; save in page zero pointer sty hi ldy #0 ; initialize the index register initlp lda lo ; get the LSB of the pointer sta lotabl,y ; save in the table lda hi ; do the same for the MSB of the pointer sta hitabl,y lda #40 ; get the number of bytes per line clc adc lo ; add to the lo pointer sta lo lda #0 ; clear the ACCUM adc hi ; catch any carry overflow sta hi iny ; increase the line index cpy #$5f ; check if we are at the end bcc initlp ; no, do the next line ; ldx #$70 ; number of lines to initialized lda #$0d ; mode 13 OPCODE for ANTIC makedl sta dlist,x ; save in the display list memory dex bne makedl ; initialize the whole block lda #$70 ; 8 blank line OPCODE for ANTIC sta dlist ; make top 24 lines blank in display list sta dlist+1 sta dlist+2 lda #$4d ; mode 13 OPCODE plus a reload memory latch sta dlist+3 lda # <screen ; save the address of the screen memory sta dlist+4 ; into the display list lda # >screen sta dlist+5 lda #$41 ; jump at end of display list OPCODE sta dlist+99 lda # <dlist ; save the start address of the list sta dlist+100 lda # >dlist sta dlist+101 rts ; ; Mulitply the absoulte value of the ACCUM by the value passed in the Y ; register. The result is returned in ACCUM (LSB) and Y (MSB) registers. mult ldx #0 ; initialize the sign value cmp #0 ; check the value in the ACCUM bpl posgn ; it was positive eor #$ff ; take the one's complement clc adc #1 ; make into the two's complement dex ; change sign value to -1 posgn sta mcand ; save the arguments passed for the multiply function sty mplier lda #0 sta temp ; initialize the temporary storage ldy #8 ; initialize the bit counter mult1 asl temp ; shift out a bit from the past result rol mplier ; rotate it in bcc mult2 ; wasn't set so no need to update lda temp clc adc mcand ; add in the multiplicand sta temp ; save the new value back lda mplier ; get the multiplier adc #0 ; catch any carry overflow sta mplier mult2 dey ; try the next bit bne mult1 lda temp ; get the LSB of the result ldy mplier ; and the MSB rts ; ; Divide the value in DIVSOR (two bytes) by the value passed in the ACCUM. ; The result is an 8 bit value returned in the ACCUM. divide sta temp ; save the value passed lda divsor+1 ; get the MSB of the divisor ldy #8 ; initialize the bit counter div1 asl divsor ; shift the divsor LSB rol a ; rotate with the MSB of the divisor cmp temp ; test the LSB bcc div2 ; no need to increase the result sbc temp inc divsor ; increase the result register div2 dey ; do the next bit bne div1 lda divsor ; get the result rts ; ; Create a new random star location with the ranges: ; 40 <= X <= 50 | Y = RND(X+1) - X/2 ! Z = RND(X+1) - X/2 ; The X register holds the current star number as an index. newpt lda #11 ; set upper limit of the X co-ordinate jsr rand ; get the random number clc adc #40 ; add 40 to it sta starx,x ; save the co-ordinate sta temp ; save a copy for more calculations clc adc #1 ; add one to the random number jsr rand ; get a random number from 0 to X+1 lsr temp ; divide the saved value by two sec sbc temp ; subtract from the new random number sta stary,x ; save the co-ordinate jsr rnd ; use the same upper limit as before sec sbc temp ; subtract from the new random number sta starz,x ; save the co-ordinate rts ; ; Main code of the stars demo. This part of the program does the basic ; setup and initialization, before going into the actual star movements. stars lda #0 ; get a zero for clearing purposes ldx #$7f ; initialize index clrpg0 sta $80,x ; clear out bottom half of page zero dex bpl clrpg0 ; loop through all locations sta colbk ; change background color to black jsr initad ; initialize the screen memory line addresses jsr clear ; clear out screen memory lda # <dlist ; save the start address of the display list ldy # >dlist sta sdlstl ; save in the shadow registers sty sdlstl+1 lda #$0e ; make the stars white sta colpf0 ; save in playfield color register 0 ; ldx #stnum-1 ; get the number of stars to initialize initst jsr newpt ; create new co-ordinates for the star dex ; decrease the star counter bpl initst ; showst ldx #stnum-1 ; get the number of stars to move nextst stx starnm ; save the value for later ; ; First part is to check if the star is still on the screen. The way I do ; this is by checking if |Y| <= X AND |Z| <= X. If not, then a new point ; is created. lda stary,x ; get the Y co-ordinate bpl chkof1 ; it is already positive, no need for ABS function eor #$ff ; take the one's complement clc adc #1 ; make into the two's complement chkof1 cmp starx,x ; compare it to the X co-ordinate beq notgn1 ; still on the screen bcs getnew ; star off the screen, must create a new one notgn1 lda starz,x ; get the Z co-ordinate bpl chkof2 ; it is already positive, no need for ABS function eor #$ff ; take the one's ... well, you know! 8^) clc adc #1 chkof2 cmp starx,x ; compare it to the X co-ordinate beq notgn2 ; still on the screen bcc notgn2 ; once again, still on the screen getnew jsr newpt ; off the screen, create a new star ; ; Now, once I know it is still on the screen, I use a perspective formula to ; transfer the 3D image to a 2D screen co-ordinate. The formula that I am ; using is X' = 79 * (D * Y / X) + 79 and Y' = 47 * (D * Z / X) + 47 where ; D equals 1 (D is the focal point). notgn2 lda stary,x ; get the Y co-ordinate ldy #79 ; get the middle screen X co-ordinate jsr mult ; multiply them together sta divsor ; use the result as the divisor in the division sty divsor+1 ldy starnm ; get the star number as an index lda starx,y ; get the X co-ordinate jsr divide ; divide by the X co-ordinate cpx #0 ; check the sign of the result bpl ncomp1 ; no need for the absolute value eor #$ff ; you must know the procedure by now! 8^) clc adc #1 ncomp1 clc adc #79 ; add the offset to 0,0 ldx starnm ; get the star index once again sta plotx,x ; save the new star screen location lda starz,x ; get the Z co-ordinate ldy #47 ; get the middle screen Y co-ordinate jsr mult ; multiply them together sta divsor ; do all the same stuff as above (OK, I am lazy! 8^) ) sty divsor+1 ldy starnm lda starx,y jsr divide cpx #0 bpl ncomp2 eor #$ff clc adc #1 ncomp2 sta temp ; save for a second lda #47 ; get the offset to 0,0 sec sbc temp ; subtract our number from it ldx starnm ; get our star index sta ploty,x ; save the new star screen location ; dec starx,x ; now move the star by changing the X co-ordinate bne skip1 jsr newpt ; if it hits zero, then it is off the screen skip1 dex ; decrease the star counter bpl nextst ; calculate all the positions ; ; Next, we wait for the TOF (top-of-frame) to occur so we have a nice display. waitlp lda vcount ; get the vertical line counter cmp #$7a ; at the bottom of the screen yet? bne waitlp ; no, keep in a tight loop sameln cmp vcount ; check against the current vertical line beq sameln ; wait for the next line to be drawn ; ; After that, we must erase all the old stars on the screen before anything ; new can be plotted. ldx #stnum-1 ; get the number of stars to erase erase ldy oldy,x ; get the old Y co-ordinate lda lotabl,y ; get the address of that screen line sta lo ; save in a page zero pointer lda hitabl,y sta hi ldy oldx,x ; get the old X co-ordinate lda #0 ; clear out the whole byte, no need to mask bits sta (lo),y ; save back in screen memory dex ; erase the next star bpl erase ; ; Finally, here is where we plot the new stars on the screen. I am using a ; 160 by 96 line screen which has four pixels per byte in screen memory. I ; mask in the new pixel for each star and then I am done, since each star is ; just one pixel long and wide. ldx #stnum-1 ; get the number of stars to plot plotit lda ploty,x ; get the Y location sta oldy,x ; save for the erase routine tay ; make a copy for an index lda lotabl,y ; get the address of that screen line sta lo ; save in a page zero pointer lda hitabl,y sta hi lda plotx,x ; get the X co-ordinate and #3 ; keep only 2 least significant bits tay ; use as in index lda bits,y ; get the correct bit pattern sta pixel ; save in a temporary register lda plotx,x ; get the X again [could use PHA/PLA but its a demo!] lsr a ; divide by four lsr a sta oldx,x ; save as the new X location tay ; use as the index into the current line lda (lo),y ; get the screen byte ora pixel ; mask in the pixel bitmap sta (lo),y ; save back in screen memory dex ; plot the next star bpl plotit ; jmp showst ; redo all the calculations from the start ; *= $02e0 ; set the run address .word stars ; the first routine that is executed .end - Brett __ __ _ __ _ | || | / || || \ Brett S Bourbin | || || || || | INTERNET: brett@PIGPEN.UMD.EDU | || || || || | \_||_/ |__||__||__| Instructional Computing Programs College Park