CON 'Raycasting: Port of nice example https://permadi.com/tutorial/raycast/demo/5/ 'F. Permadi also has a nice tutorial: https://permadi.com/1996/05/ray-casting-tutorial-table-of-contents/ 'This example is Copyright 2018 Raymond Allen with MIT license '4d: Now runs about 12 fps (one cog at 80 MHz) 'This can be made faster by using multiple cogs and increasing clock speed 'The code can probably be improved on too, we've kept it simple so easy to follow. 'Started from Chip's VGA example and modified it to show 320x240 screen 'Using 3 cogs after boot: VGA driver (right below here), Serial driver (for debugging, right after VGA), Raycaster (after serial, starts at "MainEntry") 'One thing to improve on is darkening walls and floors. Best way is to come up with tables that map original colors to darker (or ligher) colors within the same palette. 'Palette should probably be improved to include lighter and darker shades of needed colors. 'Should improve precision of sin/cos tables. Should be able to reduce size of these tables with some extra code to only store 0..90 degrees 'Rev: 5b_Spin1a: Trying to convert to Spin instead of pure assembly '****************************** '* VGA 640 x 480 x 8bpp-lut * '****************************** CON 'RJA: new for real P2 - you can use different xdiv and xmul to set clock frequency: /10*125 -> 250 MHz _XTALFREQ = 20_000_000 ' crystal frequency _XDIV = 2'10 ' crystal divider to give 1MHz _XMUL = 25'125 ' crystal / div * mul _XDIVP = 1 ' crystal / div * mul /divp to give _CLKFREQ (1,2,4..30) _XOSC = %10 'OSC ' %00=OFF, %01=OSC, %10=15pF, %11=30pF _XSEL = %11 'XI+PLL ' %00=rcfast(20+MHz), %01=rcslow(~20KHz), %10=XI(5ms), %11=XI+PLL(10ms) _XPPPP = ((_XDIVP>>1) + 15) & $F ' 1->15, 2->0, 4->1, 6->2...30->14 _CLOCKFREQ = _XTALFREQ / _XDIV * _XMUL / _XDIVP ' internal clock frequency _SETFREQ = 1<<24 + (_XDIV-1)<<18 + (_XMUL-1)<<8 + _XPPPP<<4 + _XOSC<<2 ' %0000_000e_dddddd_mmmmmmmmmm_pppp_cc_00 ' setup oscillator _ENAFREQ = _SETFREQ + _XSEL CON 'VGA driver code starts, continues after cog starter (note: modified for 320x240) intensity = 80 '0..128 fclk = _CLOCKFREQ '80_000_000.0 fpix = 25_000_000.0/2.0 'RJA trying for 320x240 fset = (fpix / fclk * 2.0) * float($4000_0000) vsync = 4 'vsync pin 'RJA: changed for real P2 nco_baud =round(fclk / 115_200.0 * 65536.0) & $fffffc00 | 7 'adapted from garryj's V19 USB code CON 'cog assignments DisplayCog=0 'Pushes display buffer to VGA port. Has to be #0 to output on P123 VGA port MainCog=1 'This one handles GUI and top level I/O GraphicsCog=2 'This one draws graphics and text on display buffer 'UsbHostCog1=6 'One of two cogs needed for USB 'UsbHidCog1=7 'The other USB cog Serial_COG=4 CON 'graphics constants 'BmpAddressBackground=$10000 '640x100 =65,080 bytes = $FE38 Bitmap with background 'BmpAddressBrick=$20000 '5,176 bytes, $1438 'BmpAddressFloor=$22000 '5,176 bytes, $1438 'BmpAddressBrickFar=$24000 '5,176 bytes, $1438 'BmpAddressBrickFarther=$26000 '5,176 bytes, $1438 'TrigTables=$30000-$1000 '$10E24, 69,156 bytes OffscreenBufferAddress=$40000 '320x240 =$12C00 DisplayBufferAddress=$50000+$3000 'size is 320x240=$12C00 (was 640x480=$4B0000) 'End of memory=$80000 CON 'Serial pin definites and mailboxes RX_PIN = 63 TX_PIN = 62 'Mailbox for serial commands (right before BmpAddressBackground) Mailbox1=$3FFF0 Mailbox2=$3FFF4 'Note: Code works if you comment out the following two lines! PUB start(status)|usbcog usbcog := coginit(0,@origin, 0) + 1 DAT org 'Start up cogs Origin ''+-------[ Set Xtal ]----------------------------------------------------------+ '' RJA: New for real P2 hubset #0 ' set 20MHz+ mode hubset ##_SETFREQ ' setup oscillator waitx ##20_000_000/100 ' ~10ms hubset ##_ENAFREQ ' enable oscillator ''+-----------------------------------------------------------------------------+ coginit #MainCog,##@MainEntry 'start main cog 'coginit #GraphicsCog,##@GraphicsEntry 'Start graphics driver 'coginit #UsbHostCog1,##@UsbHostEntry 'Start first USB driver coginit #Serial_COG,##@SerialStart 'This one needs to be last! coginit #DisplayCog,##@DisplayEntry 'Start display driver (Note: Has to be last as it also the cog running this code!) OriginEnd 'above coginit will restart this cog with VGA driver jmp #OriginEnd DAT orgh org 0 'DisplayEntry DisplayEntry ' ' ' Setup ' rdfast #0,##@BmpAddressBackground-$400+$436 'load .bmp palette into lut rep @.end,#$100 rflong y shl y,#8 wrlut y,x add x,#1 .end 'RJA: Now, repeating every line to stretch in y direction 'rdfast ##640*1/64/2,m_buf'##DisplayBufferAddress 'RJA:1 line of bitmapset rdfast to wrap on bitmap //RJA making 320x480 (for now) setxfrq ##round(fset) 'set transfer frequency to 25MHz 'the next 4 lines may be commented out to bypass level scaling setcy ##intensity << 24 'r set colorspace for rgb setci ##intensity << 16 'g setcq ##intensity << 08 'b setcmod #%01_0_000_0 'enable colorspace conversion 'RJA dacmodes changed for real P2 wrpin dacmode_s,#0 'enable dac modes in pins 0..3 wrpin dacmode_c,#1 wrpin dacmode_c,#2 wrpin dacmode_c,#3 setnib dira,#$f,#0 'RJA: New for real P2 ' ' ' Field loop ' field mov x,#33 'top blanks call #blank mov m_buf,##DisplayBufferAddress mov x,#480/2 'set visible lines line call #hsync 'do horizontal sync rdfast ##640*1/64/2,m_buf xcont m_rf,#0 'visible line call #hsync 'do horizontal sync xcont m_rf,#0 'visible line add m_buf,#320 djnz x,#line 'another line? mov x,#10 'bottom blanks call #blank drvnot #vsync 'sync on mov x,#2 'sync blanks call #blank drvnot #vsync 'sync off jmp #field 'loop ' ' ' Subroutines ' blank call #hsync 'blank lines xcont m_vi,#0 _ret_ djnz x,#blank hsync xcont m_bs,#0 'horizontal sync xcont m_sn,#1 _ret_ xcont m_bv,#0 ' ' ' Initialized data ' 'RJA: New dacmodes for real P2 dacmode_s long %0000_0000_000_1011000000000_01_00000_0 'hsync is 123-ohm, 3.3V dacmode_c long %0000_0000_000_1011100000000_01_00000_0 'R/G/B are 75-ohm, 2.0V m_bs long $CF000000+16/2 'before sync m_sn long $CF000000+96/2 'sync m_bv long $CF000000+48/2 'before visible m_vi long $CF000000+640/2 'visible m_rf long $7F000000+640/2 'visible rlong 8bpp lut x long 0 y long 0 m_buf long DisplayBufferAddress ' ' CON ''Start of serial output code - This is serial output code adapted from code posted by mindrobots in the P2 forum dat ''Start of serial output code orgh org 0 SerialStart 'loc ptra,#@Mailbox1 'mov rx_target,ptra 'loc ptra,#@Mailbox2 'mov hex_target,ptra wrpin pm_tx, #TX_PIN 'set asynchronous tx mode in smart pin wxpin ##nco_baud, #TX_PIN 'set tx bit period wrpin pm_rx, #RX_PIN 'set asynchronous rx mode in smart pin wxpin ## nco_baud, #RX_PIN 'set rx bit period dirh #TX_PIN 'enable smartpin tx dirh #RX_PIN 'enable smartpin rx jmp #loopback 'for V27 had to make these two vars into registers pm_tx long %0000_0000_00_0_0000000000000_01_11110_0 'async tx byte mode, dir high pm_rx long %0111_0000_00_0_0000000000000_01_11111_0 'async rx byte mode, dir low, inputs pin 0 ' 'Main loop start loopback 'Look for command 'waitx ##100000 rdbyte tx_cmd, rx_target 'mov tx_cmd,#1 'test cmp tx_cmd,#0 wcz if_e jmp #loopback 'check for write byte command cmp tx_cmd,#1 wcz if_e rdbyte tx_char,hex_target 'mov tx_char,#"H" 'test if_e call #send_char 'check for write hex long cmp tx_cmd,#2 wcz if_e rdlong tx_hex, hex_target if_e call #send_LongHexSub 'check for write hex byte cmp tx_cmd,#3 wcz if_e rdbyte tx_hex, hex_target if_e call #send_ByteHexSub 'check for write string byte cmp tx_cmd,#4 wcz if_e rdlong tx_hex, hex_target if_e call #send_StringSub wrbyte #0,rx_target 'message received and processed jmp #loopback Send_StringSub 'send a string rdbyte tx_char,tx_hex cmp tx_char,#0 wcz if_z ret call #send_char add tx_hex,#1 jmp #Send_StringSub Send_LongHexSub 'Send 8 hex chars getnib tx_temp,tx_hex,#7 call #SendHexSub getnib tx_temp,tx_hex,#6 call #SendHexSub getnib tx_temp,tx_hex,#5 call #SendHexSub getnib tx_temp,tx_hex,#4 call #SendHexSub getnib tx_temp,tx_hex,#3 call #SendHexSub getnib tx_temp,tx_hex,#2 call #SendHexSub getnib tx_temp,tx_hex,#1 call #SendHexSub getnib tx_temp,tx_hex,#0 call #SendHexSub ret Send_ByteHexSub push tx_hex mov tx_temp,tx_hex shr tx_temp,#4 and tx_temp,#$0F call #SendHexSub pop tx_hex mov tx_temp,tx_hex and tx_temp,#$0F call #SendHexSub ret SendHexSub 'Send hex mov tx_char,tx_temp cmp tx_char,#9 wcz if_a add tx_char,#"A"-10 if_be add tx_char,#"0" call #Send_Char ret '******************************************************************************* ' Get one character from the input port. '******************************************************************************* rcv_char testb inb,#RX_PIN wc 'sample rx if_nc jmp #rcv_char akpin #RX_PIN rdpin rx_char,#RX_PIN ret '******************************************************************************* ' Output a single character to the tx_pin. '******************************************************************************* send_char rdpin temp,#tx_pin wc 'wait if busy if_c jmp #send_char wypin tx_char,#tx_pin ret wcz rx_target long Mailbox1 hex_target long Mailbox2 tx_cmd long 0 tx_char long 0 tx_hex long 0 timer long 0 rx_char long 0 tx_temp long 0 temp long 0 fit $1F0 DAT ''MainEntry Main Cog 'orgh org 0 MainEntry mov tx_out,#"A" mov ptra,#"B" mov n3,#"U" call #OutputCharSub waitx ##100000000 testing jmp #MainEntry 'Create tables call #@MakeTrigTables 'need #@ for hubex calls? 'Make diagonal distance table call #DiagonalDistance 'painted bottom of screen black call #FillBottom DAT 'main loop MainLoop 'Fill offscreen buffer call #DrawBackground DAT 'raycast Raycast 'jmp #FloorCast 'jmp #ftest 'need to create castarc value ($1555_5555=30 degrees in first iteration) based on playerarc (320=60 degrees) and castColumn (starts at 0, meaning -30 degrees) mov castColumn,#0 'rdlong playerarc,##fPlayerArc mov castarc,playerarc '320=60 degrees subs castarc,#ANGLE30 '160=30 degrees cmps castarc,#0 wcz if_b adds castarc,##ANGLE360 castColumnLoop HorizontalWallStart 'see if ray is facing up or down cmp castarc,##ANGLE0 wcz if_b jmp #RayFacingUp cmp castarc,##ANGLE180 wcz if_a jmp #RayFacingUp RayFacingDown '// truncuate then add to get the coordinate of the FIRST grid (horizontal '// wall) that is in front of the player (this is in pixel unit) '// ROUNDED DOWN 'horizontalGrid = Math.floor(this.fPlayerY/this.TILE_SIZE)*this.TILE_SIZE + this.TILE_SIZE; mov f1,fPlayerY mov f2,#TILE_SIZE qdiv f1,f2 getqx f1 qmul f1,f2 getqx f1 add f1,f2 'f1=horizontalGrid mov horizontalGrid,f1 mov distToNextHorizontalGrid,f2 'var xtemp = this.fITanTable[castArc]*(horizontalGrid-this.fPlayerY); sub f1,fPlayerY mov ptra,castarc shl ptra,#2 add ptra,##@fITanTable rdlong f2,ptra abs f2 wc qmul f1,f2 getqx f1 '=xtemp if_c neg f1 'xIntersection = xtemp + this.fPlayerX; mov xIntersection,fPlayerX shl xIntersection,#16 'make 16.16 (or, make this INT now?) adds xIntersection,f1 jmp #HorizontalWallFind RayFacingUp 'RJA: much of this math could be combined with the above... 'horizontalGrid = Math.floor(this.fPlayerY/this.TILE_SIZE)*this.TILE_SIZE; mov f1,fPlayerY mov f2,#TILE_SIZE qdiv f1,f2 getqx f1 qmul f1,f2 getqx f1 mov horizontalGrid,f1 'distToNextHorizontalGrid = -this.TILE_SIZE; mov distToNextHorizontalGrid,f2 neg distToNextHorizontalGrid 'var xtemp = this.fITanTable[castArc]*(horizontalGrid - this.fPlayerY); subs f1,fPlayerY mov ptra,castarc shl ptra,#2 add ptra,##@fITanTable rdlong f2,ptra abs f2 wc qmul f1,f2 getqx f1 '=xtemp 16.16 if_c neg f1 'xIntersection = xtemp + this.fPlayerX; mov xIntersection,fPlayerX shl xIntersection,#16 'make 16.16 (or, make this INT now?) adds xIntersection,f1 'horizontalGrid--; sub horizontalGrid,#1 HorizontalWallFind cmp castarc,#ANGLE0 wz if_e jmp #HorizontalOutOfBounds cmp castarc,##ANGLE180 wz if_e jmp #HorizontalOutOfBounds 'distToNextXIntersection = this.fXStepTable[castArc]; mov ptra,castarc shl ptra,#2 add ptra,##@fXStepTable rdlong distToNextXIntersection,ptra HorizontalWallFindLoop 'xGridIndex = Math.floor(xIntersection/this.TILE_SIZE); mov n1,xIntersection 'mov f2,#TILE_SIZE 'qdiv f1,f2 'RJA: this could be a simple shift since tile_size==64 'getqx f1 sar n1,#6+16 'n1=xGridIndex, divide by 64 and shift 16 right to make INT cmps n1,#0 wcz if_b jmp #HorizontalOutOfBounds cmp n1,#MAP_WIDTH wcz if_a jmp #HorizontalOutOfBounds 'yGridIndex = Math.floor(horizontalGrid/this.TILE_SIZE); mov n2,horizontalGrid sar n2,#6 'n2=yGridIndex, just divide by 64 as already INT 'var mapIndex=Math.floor(yGridIndex*this.MAP_WIDTH+xGridIndex); cmps n2,#0 wcz if_b jmp #HorizontalOutOfBounds cmp n2,#MAP_HEIGHT wcz if_a jmp #HorizontalOutOfBounds mov f1,#MAP_WIDTH mul n2,f1 add n2,n1 'n2=mapIndex loc ptra,#fMap add ptra,n2 rdbyte f1,ptra cmp f1,#"O" wcz 'Opening in map? if_ne jmp #HorizontalWallFound 'keep looking 'xIntersection += distToNextXIntersection; add xIntersection,distToNextXIntersection 'horizontalGrid += distToNextHorizontalGrid; add horizontalGrid,distToNextHorizontalGrid jmp #HorizontalWallFindLoop HorizontalWallFound 'distToHorizontalGridBeingHit = (xIntersection-this.fPlayerX)*this.fICosTable[castArc]; mov f1,xIntersection mov f2,fPlayerX shl f2,#16 'make 16.16 subs f1,f2 abs f1 mov ptra,castArc shl ptra,#2 add ptra,##@fICosTable rdlong f2,ptra abs f2 wc qmul f1,f2 getqy distToHorizontalGridBeingHit 'if_c neg distToHorizontalGridBeingHit 'RJA: Fix for angle==90,270 ->javascript way won't work with 16.16 fixed point mov f1,horizontalGrid sub f1,fplayerY abs f1 cmp castArc,##ANGLE90 wcz if_e mov distToHorizontalGridBeingHit ,f1 cmp castArc,##ANGLE270 wcz if_e mov distToHorizontalGridBeingHit ,f1 'if_e mov distToVerticalGridBeingHit,verticalGrid 'if_e sub distToVerticalGridBeingHit,fplayerx jmp #FollowXRay HorizontalOutOfBounds mov distToHorizontalGridBeingHit,##MAX_VALUE FollowXRay 'Now, look for vertical wall hit cmp castArc,#ANGLE90 wcz if_b jmp #RayFacingRight cmp castArc,##ANGLE270 wcz if_a jmp #RayFacingRight RayFacingLeft 'note opposite order of left and right compared to javascript 'verticalGrid = Math.floor(this.fPlayerX/this.TILE_SIZE)*this.TILE_SIZE; mov f1,fPlayerX mov f2,#TILE_SIZE qdiv f1,f2 getqx f1 qmul f1,f2 getqx f1 mov verticalGrid,f1 'distToNextVerticalGrid = -this.TILE_SIZE; mov distToNextVerticalGrid,f2 neg distToNextVerticalGrid 'var ytemp = this.fTanTable[castArc]*(verticalGrid - this.fPlayerX); subs f1,fPlayerX mov ptra,castarc shl ptra,#2 add ptra,##@fTanTable rdlong f2,ptra abs f2 wc qmul f1,f2 getqx f1 '=ytemp if_c neg f1 'yIntersection = ytemp + this.fPlayerY; mov yIntersection,fPlayerY shl yIntersection,#16 'make 16.16 (or, make this INT now?) adds yIntersection,f1 'verticalGrid--; subs verticalGrid,#1 jmp #LookForVerticalWall RayFacingRight 'verticalGrid = this.TILE_SIZE + Math.floor(this.fPlayerX/this.TILE_SIZE)*this.TILE_SIZE; mov f1,fPlayerX mov f2,#TILE_SIZE qdiv f1,f2 getqx f1 qmul f1,f2 getqx f1 add f1,f2 'f1=verticalGrid mov verticalGrid,f1 'distToNextVerticalGrid = this.TILE_SIZE; mov distToNextVerticalGrid,f2 'var ytemp = this.fTanTable[castArc]*(verticalGrid - this.fPlayerX); sub f1,fPlayerX mov ptra,castarc shl ptra,#2 add ptra,##@fTanTable rdlong f2,ptra abs f2 wc qmul f1,f2 getqx f1 '=ytemp if_c neg f1 'yIntersection = ytemap + this.fPlayerY; mov yIntersection,fPlayerY shl yIntersection,#16 'make 16.16 (or, make this INT now?) adds yIntersection,f1 'jmp #VerticalWallFind LookForVerticalWall cmp castarc,#ANGLE90 wz if_e jmp #VerticalOutOfBounds cmp castarc,##ANGLE270 wz if_e jmp #VerticalOutOfBounds 'distToNextYIntersection = this.fYStepTable[castArc]; mov ptra,castarc shl ptra,#2 add ptra,##@fYStepTable rdlong distToNextYIntersection,ptra VerticalWallFindLoop '// compute current map position to inspect ' yGridIndex = Math.floor(yIntersection/this.TILE_SIZE); mov n1,yIntersection sar n1,#6+16 'n1=yGridIndex, divide by 64 and shift 16 right to make INT cmps n1,#0 wcz if_b jmp #VerticalOutOfBounds cmps n1,#MAP_HEIGHT wcz if_a jmp #VerticalOutOfBounds ' xGridIndex = Math.floor(verticalGrid/this.TILE_SIZE); mov n2,verticalGrid sar n2,#6 'n2=xGridIndex, just divide by 64 as already INT cmps n2,#0 wcz if_b jmp #VerticalOutOfBounds cmp n2,#MAP_HEIGHT wcz if_a jmp #VerticalOutOfBounds ' var mapIndex=Math.floor(yGridIndex*this.MAP_WIDTH+xGridIndex); mov f1,#MAP_WIDTH mul n1,f1 add n2,n1 'n2=mapIndex loc ptra,#fMap add ptra,n2 rdbyte f1,ptra cmp f1,#"O" wcz 'Opening in map? if_ne jmp #VerticalWallFound 'yIntersection += distToNextYIntersection; 'verticalGrid += distToNextVerticalGrid; adds yIntersection,distToNextYIntersection adds verticalGrid,distToNextVerticalGrid jmp #VerticalWallFindLoop VerticalWallFound 'distToVerticalGridBeingHit =(yIntersection-this.fPlayerY)*this.fISinTable[castArc]; mov f1,yIntersection mov f2,fPlayerY shl f2,#16 'make 16.16 subs f1,f2 abs f1 wc mov ptra,castArc shl ptra,#2 add ptra,##@fISinTable rdlong f2,ptra abs f2 wc qmul f1,f2 getqy distToVerticalGridBeingHit if_c abs distToVerticalGridBeingHit 'RJA: Fix for angle==0,180 ->javascript way won't work with 16.16 fixed point mov f1,verticalGrid sub f1,fplayerx abs f1 cmp castArc,#0 wcz if_e mov distToVerticalGridBeingHit,f1 cmp castArc,##ANGLE180 wcz if_e mov distToVerticalGridBeingHit,f1 'if_e mov distToVerticalGridBeingHit,verticalGrid 'if_e sub distToVerticalGridBeingHit,fplayerx jmp #DrawWallSlice VerticalOutOfBounds mov distToVerticalGridBeingHit,##MAX_VALUE DrawWallSlice 'mov f1,#3'distToHorizontalGridBeingHit 'call #ftest mov isVerticalHit,#0 'if (distToHorizontalGridBeingHit < distToVerticalGridBeingHit) cmp distToHorizontalGridBeingHit,distToVerticalGridBeingHit wcz if_a jmp #VerticalHit 'dist=distToHorizontalGridBeingHit; mov dist,distToHorizontalGridBeingHit 'xOffset=xIntersection%this.TILE_SIZE; mov xOffset,xIntersection shr xOffset,#16 and xOffset,#%111111 'lower 6 bits is mod 64 (cheating a bit) jmp #FishBowlCorrect VerticalHit mov isVerticalHit,#1 'dist=distToVerticalGridBeingHit; mov dist,distToVerticalGridBeingHit 'xOffset=yIntersection%this.TILE_SIZE; mov xOffset,yIntersection shr xOffset,#16 and xOffset,#%111111 'lower 6 bits is mod 64 (cheating a bit) FishBowlCorrect 'dist /= this.fFishTable[castColumn]; mov ptra,castColumn shl ptra,#2 add ptra,##@fIFishTable rdlong f1,ptra 'jmp #ftest shr f1,#1 'RJA: There is a problem for castColumn=159,160 (center of screen) because fishtable=$0001_0000 and lose result on multiply 'fixing for now by losing one bit from fish table with above shift, accounted for 2 lines down mul dist,f1 'since f1<1.0 in 16.16 and dist is INT, can use regular mul shr dist,#16-1 'INT 'var projectedWallHeight=(this.WALL_HEIGHT*this.fPlayerDistanceToTheProjectionPlane/dist); mov projectedWallHeight,#fPlayerDistanceToTheProjectionPlane mul projectedWallHeight,#WALL_HEIGHT qdiv projectedWallHeight,dist getqx projectedWallHeight 'bottomOfWall = this.fProjectionPlaneYCenter+(projectedWallHeight*0.5); 'RJA: Trying to fix holes... cmps projectedWallHeight,#4 wcz if_b mov projectedWallHeight,#4 shr projectedWallHeight,#1 mov bottomOfWall,#ProjectionPlaneYCenter add bottomOfWall,projectedWallHeight 'topOfWall = this.fProjectionPlaneYCenter-(projectedWallHeight*0.5); mov topOfWall,#ProjectionPlaneYCenter sub topOfWall,projectedWallHeight call #drawWallSliceRectangleTinted 'drawWallSlice1' 'jmp #NextColumn DAT 'floor cast FloorCast cmp bottomOfWall,#200 wcz if_ae jmp #NextColumn call #FloorCastSub DAT 'End of Column Loop NextColumn add castColumn,#1 add castArc,#1 mov n1,##ANGLE360 cmp castArc,n1 wcz if_ae sub castArc,n1 cmp castColumn,#PROJECTIONPLANEWIDTH wcz if_b jmp #castColumnLoop DAT 'Copy from offscreen buffer to display buffer call #UpdateScreen jmp #MainLoop'#done DAT 'drawWallSliceRectangleTinted: function(x, y, width, height, xOffset, brighnessLevel) 'RJA: problem when height>200, topOfWall negative 'if (isVerticalHit) ' this.drawWallSliceRectangleTinted(castColumn, topOfWall, 1, (bottomOfWall-topOfWall)+1, xOffset, this.baseLightValue/(dist)); 'else ' this.drawWallSliceRectangleTinted(castColumn, topOfWall, 1, (bottomOfWall-topOfWall)+1, xOffset, (this.baseLightValue-10)/(dist)); drawWallSliceRectangleTinted mov s1,##OffscreenBufferAddress mov s2,##@BmpAddressBrick+$436 'use darker bricks for further away cmp dist,#200 wcz if_a mov s2,##@BmpAddressBrickFar +$436 cmp dist,#400 wcz if_a mov s2,##@BmpAddressBrickFarther+$436 ' var sourceIndex=(bytesPerPixel*xOffset); mov sourceIndex,xOffset 'var lastSourceIndex=sourceIndex+(this.fWallTextureBuffer.width*this.fWallTextureBuffer.height*bytesPerPixel); mov lastSourceIndex,sourceIndex add lastSourceIndex,##64*64 'var targetIndex=(this.offscreenCanvasPixels.width*bytesPerPixel)*y+(bytesPerPixel*x); abs topOfWall wc mov targetIndex,#320 mul targetIndex,topOfWall if_c neg targetIndex adds targetIndex,castColumn 'var heightToDraw = height; '// clip bottom 'if (y+heightToDraw>this.offscreenCanvasPixels.height) ' heightToDraw=this.offscreenCanvasPixels.height-y;' ' cmp bottomOfWall,#199 wcz 'if_a mov bottomOfWall,#199 'RJA Does this work? Do we really need to test this? mov heightToDraw,bottomOfWall if_c neg topOfWall subs heightToDraw,topOfWall add heightToDraw,#1 mov height,heightToDraw 'mov f1,height 'call #ftest ' var yError=0; mov yError,#0 '// we need to check this, otherwise, program might crash when trying '// to fetch the shade if this condition is true (possible if height is 0) 'if (heightToDraw<0) ' return; cmps heightToDraw,#0 wcz if_be RET 'cmp heightToDraw,#200 wcz 'mov f1,bottomOfWall 'if_ae jmp #ftest drawWallSliceLoop2 'yError += height; add yError,height mov ptra,s2 add ptra,sourceIndex rdbyte f2,ptra drawWallSliceLoop3 'while (yError>=this.fWallTextureBuffer.width) cmp yError,#64 wcz if_b jmp #drawWallSliceLoop3Done 'yError-=this.fWallTextureBuffer.width; sub yError,#64 'this.offscreenCanvasPixels.data[targetIndex]=red; 'this.offscreenCanvasPixels.data[targetIndex+1]=green; 'this.offscreenCanvasPixels.data[targetIndex+2]=blue; 'this.offscreenCanvasPixels.data[targetIndex+3]=alpha; cmps targetIndex,#0 wcz if_b jmp #drawWallSliceLoop3Next mov ptrb,s1 add ptrb,targetIndex 'mov f1,ptrb 'jmp #ftest wrbyte f2,ptrb 'targetIndex+=(bytesPerPixel*this.offscreenCanvasPixels.width); drawWallSliceLoop3Next add targetIndex,#320 cmps targetIndex,##320*200 wcz if_ae RET '// clip bottom (just return if we reach bottom) 'heightToDraw--; sub heightToDraw,#1 cmp heightToDraw,#1 wcz if_b RET 'if (heightToDraw<1) ' return; jmp #drawWallSliceLoop3 drawWallSliceLoop3Done 'sourceIndex+=(bytesPerPixel*this.fWallTextureBuffer.width); add sourceIndex,#64 'if (sourceIndex>lastSourceIndex) ' sourceIndex=lastSourceIndex; cmp sourceIndex,lastSourceIndex wcz if_a mov sourceIndex,lastSourceIndex jmp #drawWallSliceLoop2 DAT 'UpdateScreen UpdateScreen loc ptra,#OffscreenBufferAddress loc ptrb,#DisplayBufferAddress mov my,#240 myloop4 mov mx,#320/4 rdfast #320,ptra mxloop4 rflong ml wrlong ml,ptrb++ djnz mx,#mxloop4 add ptra,##320 djnz my,#myloop4 UpdateScreenDone call #mUpdatePlayerArc getct f1 sub f1,t1 mov tx_out,f1 call #outputLongSub mov tx_out,#13 call #outputCharSub getct t1 done 'jmp #done RET DAT 'DrawBackground: copy from background bmp (640x100) to offscreen buffer (320x200) 'Just want the top 100 pixels to make the background DrawBackground 'rdlong marc,##fPlayerArc 'Start copy from this column mov marc,playerArc mredux 'needs to be in range from 0..639 cmp marc,##640 wcz if_ae sub marc,##640 if_ae jmp #mredux 'Copy from Graphics resource to offscreen buffer loc ptra,#@BmpAddressBackground+$436 'hub location of background image loc ptrb,#OffscreenBufferAddress 'image buffer add ptra,marc 'add offset to starting column 'if marc>320, need to copy to column 639 then start from zero for the rest 'mx0=#bytes to copy 'mx1=how much to add to ptra for next loop==640-mx0 mov mx0,#320 cmp marc,#320 wcz if_a mov mx0,##640 if_a sub mx0,marc mov mx1,##640 sub mx1,mx0 mov mx2,#320 sub mx2,mx0 'fast copy 32 byte (8 long) sections 'RJA made this a hair faster using rdfast and rfbyte 'should convert to rflong to make faster... 'First stage FirstStage mov my,#100'165 'could be just 100 myloop rdfast #320,ptra mov mx,mx0 mxloop 'rdbyte ml,ptra++ 'trying rdfast instead rfbyte ml wrbyte ml,ptrb++ djnz mx,#mxloop add ptra,mx1'#320 'because source image is 640 bytes wide add ptra,mx0 add ptrb,mx2 djnz my,#myloop 'Second stage, if marc>320, need to fill rest of screen from start column of background image cmp marc,#320 wcz if_be jmp #FillBottom loc ptra,#@BmpAddressBackground+$436 'hub location of background image loc ptrb,#OffscreenBufferAddress 'image buffer mov mx0,marc sub mx0,#320 mov mx1,##640 sub mx1,mx0 mov mx2,#320 sub mx2,mx0 add ptrb,mx2 mov my,#100'165 myloop3 rdfast #320,ptra mov mx,mx0 mxloop3 rfbyte ml'rdbyte ml,ptra++ wrbyte ml,ptrb++ djnz mx,#mxloop3 add ptra,mx1'#320 'because source image is 640 bytes wide add ptra,mx0 add ptrb,mx2 djnz my,#myloop3 RET ' Initialized data castarc long $1555_5555 '30 degrees in first loop playerarc long 320'ANGLE90'320'101+160'320 '60 degrees fplayerx long 100 fplayery long 160' mp long 0 my long 0 mx long 0 mx0 long 0 mx1 long 0 mx2 long 0 ml long 0 marc long 0 castColumn long 0 targetIndex long 0 lastBottomOfWall long 140 f1 long 0 f2 long 0 f3 long 0 row long 0 e1 long 0 e2 long 0 s1 long 0 s2 long 0 s3 long 0 n3 long 0 tx_out long 0 'holds character to output tx_cnt long 0 n1 long 0 n2 long 0 t1 long 0 'for testing yend long 0 xend long 0 cellx long 0 celly long 0 tilerow long 0 tilecolumn long 0 tx_target long Mailbox1 'Mailbox for serial output tx_hexTarget long Mailbox2 'Mailbox for Hex output distToNextHorizontalGrid long 0 horizontalGrid long 0 xIntersection long 0 distToHorizontalGridBeingHit long 0 distToNextXIntersection long 0 xGridIndex long 0 yGridIndex long 0 verticalGrid long 0 distToNextVerticalGrid long 0 yIntersection long 0 distToNextYIntersection long 0 distToVerticalGridBeingHit long 0 dist long 0 isVerticalHit long 0 xOffset long 0 yOffset long 0 topOfWall long 0 bottomOfWall long 0 projectedWallHeight long 0 sourceIndex long 0 lastSourceIndex long 0 heightToDraw long 0 height long 0 yError long 0 DAT 'tx mailbox fit $1F0 bignothing long 0[1000] CON 'P123 button definitions (from Ozpropdev) 'Parallax P123-A7/A9 board buttons a9_left_button = 57 'pb3 a9_right_button = 56 'pb2 a9_fire_button = 55 'pb1 'pb0 = reset DAT 'Hubexec routines orgh DAT 'ShowCheckpoint ShowCheckpoint mov tx_out,t1 call #OutputCharSub mov tx_out,#" " call #OutputCharSub RET DAT 'FloorCastSub FloorCastSub mov lastBottomOfWall,bottomOfWall mov targetIndex,lastBottomOfWall 'bottom of wall (where 0=top of screen and 100=middle of screen and 200=bottom of screen) mul targetIndex,#PROJECTIONPLANEWIDTH add targetIndex,castColumn '1 byte per pixel mov PA,##OffscreenBufferAddress add PA,targetIndex 'ratio=(this.fPlayerHeight)/(row-projectionPlaneCenterY); mov row,lastBottomOfWall 'fish table brought out of loop to make faster mov ptra,castColumn shl ptra,#2 add ptra,##@fFishTable rdlong e2,ptra 'RJA: try moving sin, cos reads outside of loop mov ptra,castArc shl ptra,#2 add ptra,##@fSinTable rdlong s1,ptra sub ptra,##@fSinTable add ptra,##@fCosTable rdlong s2,ptra FloorCastLoop mov f1,row 'RJA: Unsing new table speeds this up '{ sub f1,#ProjectionPlaneYCenter loc ptra,#@DDTable shl f1,#2 'long table add ptra,f1 rdlong f1,ptra '} {'this old way without table is slower sub f1,#ProjectionPlaneYCenter shl f1,#16 'make into 16.16 float mov f2,#fPlayerHeight 'shl f2,#16 'if we don't shift, we get answer without the need to shift qfrac f2,f1 '64 bit (f2<<32) divided by 32 bit f1 getqx f1 'f1=ratio=0.8 in first loop. since f2 wasn't shifted, this comes out in 16.16 format without shifting 'diagonalDistance=Math.floor((this.fPlayerDistanceToTheProjectionPlane*ratio)*(this.fFishTable[castColumn])); mov f2,#fPlayerDistanceToTheProjectionPlane '=277 mul f1,f2 'f1=fPlayerDistanceToTheProjectionPlane*ratio in 16.16 } {'moved outside of loop 'fish mov ptra,castColumn shl ptra,#2 add ptra,##fFishTable rdlong f2,ptra qmul f2,f1 } qmul e2,f1 getqy f1 '=(INT) diagonalDistance=255 in first loop 'yEnd = Math.floor(diagonalDistance * this.fSinTable[castArc]); 127 {'using tables is slightly faster than qrotate 'mov f1,ptra 'call #ftest mul ptra,##$2222 'turn into P2 angle (really want $22_222) shl ptra,#8 qrotate f1,ptra getqx f1 'f1=cos(angle) getqx f3 'f3=sin(angle) } '{ {moved outside of loop mov ptra,castArc shl ptra,#2 add ptra,##fSinTable rdlong f2,ptra } mov f2,s1 abs f2 wc shr f2,#1 'make <1 in all cases mul f2,f1 shr f2,#16-1 'INT = yEnd mov f3,f2 if_c neg f3 'var xEnd = Math.floor(diagonalDistance * this.fCosTable[castArc]); {'moved outside of loop sub ptra,##fSinTable add ptra,##fCosTable rdlong f2,ptra } mov f2,s2 abs f2 wc shr f2,#1 mul f1,f2 shr f1,#16-1 if_c neg f1 '} adds f1,fplayerx adds f3,fplayery mov xend,f1 mov yend,f3 mov cellX,xend mov cellY,yend 'qdiv f1,#TILE_SIZE 'getqx cellx 'qdiv f2,#TILE_SIZE 'getqx celly '// Get the tile intersected by ray: ' var cellX = Math.floor(xEnd / this.TILE_SIZE); ' var cellY = Math.floor(yEnd / this.TILE_SIZE); shr cellX,#5 shr cellY,#5 '//Make sure the tile is within our map 'if ((cellX < MAP_WIDTH) && ' (cellY < MAP_HEIGHT) && ' (cellX >= 0) && (cellY >= 0)) '// Find offset of tile and column in texture ' var tileRow = Math.floor(yEnd % this.TILE_SIZE); ' var tileColumn = Math.floor(xEnd % this.TILE_SIZE); mov tilecolumn,xend mov tilerow,yend and tilecolumn,#%111111 and tilerow,#%111111 'mov f1,tilerow ' cmp castColumn,#100 wcz 'if_e cmp row,#166 wcz 'if_e mov f1,tilecolumn 'if_e jmp #ftest mov ptrb,tilerow mul ptrb,#64 'could shift instead add ptrb,tilecolumn add ptrb,##@BmpAddressFloor+$436 rdbyte n1,ptrb wrbyte n1,PA add PA,#320 'next column 'mov f1,#$67'n1 'jmp# #ftest add row,#1 cmp row,#200 wcz if_b jmp #FloorCastLoop RET DAT 'mUpdatePlayerArc (maybe move this back to cog later, after size optimization) mUpdatePlayerArc 'rdlong marc,##fPlayerArc 'Get player angle 'testp #a9_right_button wc testp #30 wc if_c sub PlayerArc,#2'ANGLE15 'testp #a9_left_button wc testp #22 wc if_c add PlayerArc,#2'ANGLE15 cmps PlayerArc,#0 wcz if_b add PlayerArc,##ANGLE360 cmp PlayerArc,##ANGLE360 wcz if_ae sub PlayerArc,##ANGLE360 'wrlong marc,##fPlayerArc 'Save player angle 'tick mov tx_out,#"." call #OutputCharSub 'testp #a9_fire_button wc testp #46 wc if_c RET 'var playerXDir=this.fCosTable[this.fPlayerArc]; 'var playerYDir=this.fSinTable[this.fPlayerArc]; 'var dx=0; 'var dy=0; '// move forward 'if (this.fKeyUp) '{ ' dx=Math.round(playerXDir*this.fPlayerSpeed); ' dy=Math.round(playerYDir*this.fPlayerSpeed); '} 'fixed for when sin or cos are negative... 'RJA: For now, just make sure can't go into wall... mov n1,fplayerx mov n2,fplayery mov ptra,PlayerArc shl ptra,#2 add ptra,##@fCosTable rdlong f1,ptra abs f1 wc shr f1,#1 mul f1,#4'16 'speed shr f1,#16-1 if_c neg f1 adds fplayerx,f1 mov e1,f1 'save dx sub ptra,##@fCosTable add ptra,##@fSinTable rdlong f1,ptra abs f1 wc shr f1,#1 mul f1,#4'16 'speed shr f1,#16-1 if_c neg f1 adds fplayery,f1 mov e2,f1 'save dy ' // make sure the player don't bump into walls mov cellY,fplayerY shr cellY,#6 'n1=yGridIndex, divide by 64 and shift 16 right to make INT mov cellX,fplayerX shr cellX,#6 'n1=yGridIndex, divide by 64 and shift 16 right to make INT mov xoffset,fplayerX and xoffset,#%111111 'lower 6 bits is mod 64 (cheating a bit) mov yoffset,fplayerY and yoffset,#%111111 'lower 6 bits is mod 64 (cheating a bit) MovingRightLeft cmps e1,#0 wcz if_be jmp #MovingLeft MovingRight mov f1,#MAP_WIDTH mul f1,cellY add f1,cellX 'n2=mapIndex add f1,#1 'if ((this.fMap.charAt((playerYCell*this.MAP_WIDTH)+playerXCell+1)!='O')&& loc ptra,#fMap add ptra,f1 rdbyte f1,ptra cmp f1,#"O" wcz if_e jmp #MovingUpDown mov f1,#TILE_SIZE-minDistanceToWall '(playerXCellOffset > (this.TILE_SIZE-minDistanceToWall))) cmp xoffset,f1 wcz if_be jmp #MovingUpDown sub fPlayerX,xoffset 'this.fPlayerX-= (playerXCellOffset-(this.TILE_SIZE-minDistanceToWall)); add fPlayerX,f1 jmp #MovingUpDown MovingLeft mov f1,#MAP_WIDTH mul f1,cellY add f1,cellX 'n2=mapIndex sub f1,#1 'if ((this.fMap.charAt((playerYCell*this.MAP_WIDTH)+playerXCell-1)!='O')&& loc ptra,#fMap add ptra,f1 rdbyte f1,ptra cmp f1,#"O" wcz if_e jmp #MovingUpDown mov f1,#minDistanceToWall '(playerXCellOffset < (minDistanceToWall))) cmp xoffset,f1 wcz if_ae jmp #MovingUpDown sub fPlayerX,xoffset 'this.fPlayerX+= (minDistanceToWall-playerXCellOffset); add fPlayerX,f1 jmp #MovingUpDown MovingUpDown cmps e2,#0 wcz if_ae jmp #MovingDown MovingUp mov f1,#MAP_WIDTH sub cellY,#1 'if ((this.fMap.charAt(((playerYCell-1)*this.MAP_WIDTH)+playerXCell)!='O')&& mul cellY,f1 add cellX,cellY 'n2=mapIndex loc ptra,#fMap add ptra,cellX rdbyte f1,ptra cmp f1,#"O" wcz if_e jmp #mUpdatePlayerArcDone mov f1,#minDistanceToWall '(playerYCellOffset < (minDistanceToWall))) cmp yoffset,f1 wcz if_ae jmp #mUpdatePlayerArcDone sub fPlayerY,yoffset 'this.fPlayerY+= (minDistanceToWall-playerYCellOffset); add fPlayerY,f1 jmp #mUpdatePlayerArcDone MovingDown mov f1,cellY mov f1,#MAP_WIDTH add cellY,#1 'if ((this.fMap.charAt(((playerYCell+1)*this.MAP_WIDTH)+playerXCell)!='O')&& mul cellY,f1 add cellX,cellY 'n2=mapIndex loc ptra,#fMap add ptra,cellX rdbyte f1,ptra cmp f1,#"O" wcz if_e jmp #mUpdatePlayerArcDone mov f1,#TILE_SIZE-minDistanceToWall '(playerYCellOffset > (this.TILE_SIZE-minDistanceToWall))) cmp yoffset,f1 wcz if_be jmp #mUpdatePlayerArcDone sub fPlayerY,yoffset 'this.fPlayerY-= (playerYCellOffset-(this.TILE_SIZE-minDistanceToWall )); add fPlayerY,f1 mUpdatePlayerArcDone drvl #22 'left drvl #30 'right drvl #46 'up drvl #38 'down fltl #22 fltl #30 fltl #46 fltl #38 RET DAT 'Fillbottom FillBottom 'RJA: just the bottom now that floorcasting is working loc ptrb,#OffscreenBufferAddress 'image buffer add ptrb,##200*320 'mov mx,#320/4 mov ml,##$07070707 '0 is white, 7 is black 'fill rest of screen with black mov my,#40'240-100'165 myloop2 mov mx,##320/4 mxloop2 wrlong ml,ptrb++ djnz mx,#mxloop2 djnz my,#myloop2 RET DAT 'OutputCharSub OutputCharSub 'Output character in tx_out 'mov tx_out,#"C" wrbyte tx_out,##Mailbox2'tx_out,##Mailbox2'##tx_hexTarget 'set byte to send wrbyte #1,tx_target 'send command #1 OutputCharWait rdbyte tx_out,tx_target cmp tx_out,#0 wcz if_nz jmp #OutputCharWait ret DAT 'OutputLongSub OutputLongSub 'Output long in tx_out as hex, setting MSB to make sure non-zero wrlong tx_out,tx_hexTarget wrbyte #2,tx_target 'send command #2 jmp #OutputCharWait DAT 'OutputByteSub OutputByteSub 'Output long in tx_out as hex, setting MSB to make sure non-zero wrlong tx_out,tx_hexTarget wrbyte #3,tx_target 'send command #3 jmp #OutputCharWait DAT 'OutputStringSub OutputStringSub 'Output a string at provided HUB location wrlong tx_out,tx_hexTarget wrbyte #4,tx_target 'send command #4 jmp #OutputCharWait DAT 'wait 1 sec Wait1SecSub 'wait one second GETCT tx_cnt 'get initial CNT ADDCT1 tx_cnt,##80_000_000 'make initial CT1 target WAITCT1 'wait for CT1 target ret DAT 'wait 1 millisecond Wait1msSub 'wait one milli second GETCT tx_cnt 'get initial CNT ADDCT1 tx_cnt,##80_000 'make initial CT1 target WAITCT1 'wait for CT1 target ret DAT 'wait 10 milliseconds Wait10msSub 'wait one milli second GETCT tx_cnt 'get initial CNT ADDCT1 tx_cnt,##800_000 'make initial CT1 target WAITCT1 'wait for CT1 target ret DAT 'wait 100 milliseconds Wait100msSub 'wait one milli second GETCT tx_cnt 'get initial CNT ADDCT1 tx_cnt,##800_0000 'make initial CT1 target WAITCT1 'wait for CT1 target ret DAT 'ftest: for debugging ftest call #Wait1SecSub mov tx_out,f1 call #OutputLongSub mov tx_out,#13 call #OutputCharSub jmp #ftest CON ''Game constants '// size of tile (wall height) TILE_SIZE = 64 WALL_HEIGHT = 64 '// Remember that PROJECTIONPLANE = screen. This demo assumes your screen is 320 pixels wide, 200 pixels high PROJECTIONPLANEWIDTH = 320 PROJECTIONPLANEHEIGHT = 200 ProjectionPlaneYCenter = PROJECTIONPLANEHEIGHT / 2 'floating point contstants fPlayerDistanceToTheProjectionPlane = 277 fPlayerHeight = 32 fPlayerSpeed = 16 MAP_WIDTH = 20 MAP_HEIGHT = 20 ANGLE60 = PROJECTIONPLANEWIDTH '320 '// You must make sure these values are integers because we're using loopup tables. ANGLE30 = trunc(ANGLE60 / 2.0) ANGLE15 = trunc(ANGLE30 / 2.0) ANGLE90 = trunc(ANGLE30 * 3.0) ANGLE180 = trunc(ANGLE90 * 2.0) ANGLE270 = trunc(ANGLE90 * 3.0) ANGLE360 = trunc(ANGLE60 * 6.0) '320*6=1920 ANGLE0 = 0 ANGLE5 = trunc(ANGLE30 / 6.0) ANGLE3 = trunc(ANGLE30 / 10.0) ANGLE10 = trunc(ANGLE5 * 2.0) ANGLE45 = trunc(ANGLE15 * 3.0) MAX_VALUE=$7FFF_FFFF 'max positive value in 16.16 minDistanceToWall=30 DAT 'Graphics command interface (not being used) orgh GraphicsInterface long 0[11] 'fPlayerArc long ANGLE60 DAT 'DiagonalDistance: RJA: Make diagonal distance table (to speed up floor casting) DiagonalDistance 'ratio=(this.fPlayerHeight)/(row-projectionPlaneCenterY); mov row,#ProjectionPlaneYCenter mov n1,#ProjectionPlaneYCenter loc ptra,#@DDTable DiagonalDistanceLoop mov f1,row sub f1,#ProjectionPlaneYCenter shl f1,#16 'make into 16.16 float mov f2,#fPlayerHeight 'RJA: Moving multiply up here, before the divide, makes far away floor look better mov f3,#fPlayerDistanceToTheProjectionPlane '=277 mul f2,f3 'f1=fPlayerDistanceToTheProjectionPlane*ratio in 16.16 'shl f2,#16 'if we don't shift, we get answer without the need to shift qfrac f2,f1 '64 bit (f2<<32) divided by 32 bit f1 getqx f1 'f1=ratio=0.8 in first loop. since f2 wasn't shifted, this comes out in 16.16 format without shifting 'diagonalDistance=Math.floor((this.fPlayerDistanceToTheProjectionPlane*ratio)*(this.fFishTable[castColumn])); wrlong f1,ptra++ add row,#1 DJNZ n1,#DiagonalDistanceLoop RET DAT 'Make Trig Tables MakeTrigTables mov n1,#0 'start of table mov n2,##1921 'this many entries mov f1,#$10 'start with angle=$0000_0010 MakeTrigLoop mov f2,##$0001_0000 '1 in 16.16 qrotate f2,f1 getqx e1 'cos getqy e2 'sin mov ptra,n1 shl ptra,#2 add ptra,##@fSinTable wrlong e2,ptra mov ptra,n1 shl ptra,#2 add ptra,##@fCosTable wrlong e1,ptra 'tan 'adjust sign of e2 mov s1,#0 '0=positive mov s2,#0 cmps e2,#0 wcz if_b neg e2 if_b mov s2,#1 'adjust sign of e1 cmps e1,#0 wcz if_b neg e1 if_b mov s1,#1 'figure out sign of tan,itan mov s3,s1 add s3,s2 rcr s3,#1 wc'sign in carry 'now e2 and e1 are positive, low bit in s3 is sign of tan, itan mov f2,e2 shl f2,#15 'can't do 16 in case =1 qdiv f2,e1 getqx f2 shl f2,#1 'make up for that #15 'adjust sign 'rcr s1,#1 wc'sign in carry negc f2 mov ptra,n1 shl ptra,#2 add ptra,##@fTanTable wrlong f2,ptra mov f3,f2 'save for step tables 'itan is actually cotangent mov f2,e1 shl f2,#15 qdiv f2,e2 getqx f2 shl f2,#1 negc f2 mov ptra,n1 shl ptra,#2 add ptra,##@fITanTable wrlong f2,ptra 'inverse sin,cos rcr s1,#1 wc'sign in carry mov f2,#1 qfrac f2,e1 getqx e1 'icos bitl e1,#31 'make sure not so bit looks negative negc e1 rcr s2,#1 wc'sign in carry qfrac f2,e2 getqx e2 'isin bitl e2,#31 'make sure not so bit looks negative negc e2 mov ptra,n1 shl ptra,#2 add ptra,##@fISinTable wrlong e2,ptra ' cmp n1,#0 wcz ' if_e mov f1,e2 ' if_e call #ftest mov ptra,n1 shl ptra,#2 add ptra,##@fICosTable wrlong e1,ptra 'step tables abs f3 wc'absolute value mov f2,#TILE_SIZE qfrac f2,f3 getqx f2 cmp n1,#ANGLE90 wcz if_b jmp #StepTableRight cmp n1,##ANGLE270 wcz if_a jmp #StepTableRight StepTableLeft 'result is negative neg f2 StepTableRight 'result is positive mov ptra,n1 shl ptra,#2 add ptra,##@fXStepTable wrlong f2,ptra 'StepTableUpDown mov f2,#TILE_SIZE qmul f2,f3 getqx f2 cmp n1,##ANGLE180 wcz if_a neg f2 mov ptra,n1 shl ptra,#2 add ptra,##@fYStepTable wrlong f2,ptra 'next add f1,##$22_2222 'angle step add n1,#1 djnz n2,#MakeTrigLoop 'make fish table (actually inverse so can multiply instead of divide) mov n1,#0 'iteration counter mov n2,#ANGLE30 shl n2,#1 add n2,#1 'doing this many iterations FishTableLoop mov f1,n1 subs f1,#ANGLE30 abs f1 mov ptra,f1 shl ptra,#2 add ptra,##@fICosTable rdlong f2,ptra mov ptra,n1 shl ptra,#2 add ptra,##@fFishTable wrlong f2,ptra add n1,#1 djnz n2,#FishTableLoop 'Make inverse fish table mov n1,#0 'iteration counter mov n2,#ANGLE30 shl n2,#1 add n2,#1 'doing this many iterations IFishTableLoop mov f1,n1 subs f1,#ANGLE30 abs f1 mov ptra,f1 shl ptra,#2 add ptra,##@fCosTable rdlong f2,ptra mov ptra,n1 shl ptra,#2 add ptra,##@fIFishTable wrlong f2,ptra add n1,#1 djnz n2,#IFishTableLoop RET DAT 'Map fMap byte "WWWWWWWWWWWWWWWWWWWW" byte "WOOOOOOOOOOOOOOOOOOW" byte "WOOWOWOWOOOWOOWWOWOW" byte "WOOOOOOOWOOWOOOOWWOW" byte "WOOWOWOOWOOWOOWWWWOW" byte "WOOWOWWOWOOWOOWOOWOW" byte "WOOWOOWOWOOWOOWOOWOW" byte "WOOOWOWOWOOWOOOOOWOW" byte "WOOOWOWOWOOWOOWOOWOW" byte "WOOOWWWOWOOWOOWWWWOW" byte "WOOOOOOOOOOOOOOOOOOW" byte "WOOWOWOWOOOWOOWWOWOW" byte "WOOOOOOOWOOWOOOOOWOW" byte "WOOWOWOWOOOWOOWWOWOW" byte "WOOOOOOOWOOWOOOOOWOW" byte "WOOWOWOWOOOWOOWWOWOW" byte "WOOOOOOOWOOOOOOOOWOW" byte "WOOWOWOWOOOWOOWWOWOW" byte "WOOOOOOOOOOWOOOOOOOW" byte "WWWWWWWWWWWWWWWWWWWW" DAT 'Display Driver Refresh notice orgh VSyncFlag long 0 DAT' Graphics Resources bitmapBitmap ' BmpAddressBackground 'orgh BmpAddressBackground - $436 'justify pixels at $1000, pallete at $1000-$400 file "bgr_combopal2.bmp" '640 x 100, 8pbb-lut '65,080 bytes, 64,000 for data, $438 for other BmpAddressBrick 'orgh BmpAddressBrick-$436 '5,176 bytes, $1438 file "brick2_combopal2.bmp" BmpAddressFloor 'orgh BmpAddressFloor-$436 '5,176 bytes, $1438 file "tile7_combopal2.bmp" BmpAddressBrickFar 'orgh BmpAddressBrickFar-$436 '5,176 bytes, $1438 file "brick2_far_combopal2.bmp" BmpAddressBrickFarther 'orgh BmpAddressBrickFarther-$436 '5,176 bytes, $1438 file "brick2_farther_combopal2.bmp" DAT 'Trig Tables TrigTables '$10E24, 69,156 bytes fSinTable long 0[1921] fISinTable long 0[1921] fCosTable long 0[1921] fICosTable long 0[1921] fTanTable long 0[1921] fITanTable long 0[1921] fXStepTable long 0[1921] fYStepTable long 0[1921] fFishTable long 0[321] 'don't actually need 1921 for fish table, so dropping it fIFishTable long 0[321] 'RJA: making inverse so can multiply instead of divide in raycast loop DAT 'Diagonal Distance Table DDTable long 0[102] 'only need 100 really DAT 'mailbox 'mailbox1 long 0 'mailbox2 long 0[100]