;----------------------------------------------------------- ;----vwBASIC Framework Template for the ASDK ---- ;----The Abstract Assembly SDK for the Atari 2600 VCS ---- ;----a FREE Abstract Assembly/BASIC Programming Framework--- ;----For the Atari 2600 VCS ---- ;----GOLD EDITION VERSION 2.1 ---- ;----------------------------------------------------------- ;----BROUGHT TO YOU BY RELATIONALFRAMEWORK.COM ---- ;----------------------------------------------------------- ;----WRITE ONCE, RUN ON ANY ATARI VCS PLATFORM: ---- ;----BANK SWITCHED 6K ---- ;----GAME BINARIES RUN ON CART, TAPE OR IN THE EMULATOR ---- ;----------------------------------------------------------- ;----Anyone may use this Development Kit however they wish - ;----100% FREE --------------------------------------------- ;----Build fun 4-way scrolling games fast using Abstract Assembly, like bAtari BASIC ;----(you can even use the bB compiler to generate your abstract asm if you prefer!) ;----------------------------------------------------------- ;----------------------------------------------------------- ;----Source is fully commented and the doc's also explain--- ;----how to make function calls to the phantom hardware for: ;----------------------------------------------------------- ;----Displaying large scrolling bitmapped virtual worlds ---- ;----stored as WYSIWYG images inline (instead of being flipped back and forth). ---- ;----------------------------------------------------------- ;----Seting, flipping or checking virtual world pixels (x,y) ---- ;----panning the playfield camera (x,y) anywhere in the virtual world ---- ;----------------------------------------------------------- ;----Displaying sprites (x,y) and loading them into RAM from the inline WYSIWYG sprite library ;----A vertical sprite flipping function (can be combined with horizontal flip). ---- ;----------------------------------------------------------- ;----Mapping sprites to either screen or virtual world coordinates (panning the CAM object can move the latter). ;----Sprite collision detection anywhere in the virtual world. ;----------------------------------------------------------- ;---------------------------------------------------------- ;--- SuperCharger Static RAM Memory Map: ;---------------------------------------------------------- ;--- The ASDK has been condensed to fit in the SuperChargers spare 2k bank ;---------------------------------------------------------- ;--- over 4K of memory is available for game code, graphics and sound: ;--- There are bytes used for the inline sprite library; ;--- this can be expanded or reduced in size. ;--- bytes can be freed from the music scores and code can be ;--- "played" by the music engine instead (just relocate the score lables) ;--- And, you have 384 bytes of RAM (effectively 641 Bytes; the RAM Doubler turns 256 into 512 and the paddle memory is used too - Sega Gamepad not supported!) ;---------------------------------------------------------- ;--- Need more RAM? :) ;---------------------------------------------------------- ;--- If you want more than 50 variables uncomment the superchargervar routines and allocate more RAM. ;---------------------------------------------------------- ;--- Note About features of the SuperCharger RAM model: ;---------------------------------------------------------- ;;All SuperCharger RAM is Static RAM - initialisable without ;;code! This includes the virtual world and the 20 4-bit and optional variables ;; ;;--------------------------------------------------------- ;;--------------------------------------------------------- ;;ASDK Framework Objects: ;;--------------------------------------------------------- ;; The Virtual World! 10x the size of the viewable screen; x,y addressable ;;--------------------------- ;; The CAM object (the visible screen/playfield) x,y positioning: ;; BITIndex is the x index, BYTErowoffset is the y index in 12 step increments ;;--------------------------- ;; The Sprite objects (player0,player1, missile0,missile1) ;; x,y addressable, collapse when 0,0 (also can be mapped to the virtual world) ;;--------------------------- ;; Music Engine (plays multiple scores, shapes notes, interrupts one voice for sfx, automatically returns to main theme (loops it) when secondary themes interrupt) ;;--------------------------------------------------------- ;;--------------------------------------------------------- ;; Static RAM variables (20 4-bit static RAM variables) ;; It's static RAM! Initialise them inline without code! ;;--------------------------------------------------------- ;; Optimal 256 aditional static RAM variables (8-bit) ;;--------------------------------------------------------- ;; Dynamic RAM variables 27 permanent and 4 temp variables (8-bit) ;;--------------------------------------------------------- ;; ;; ;;--------------------------------------------------------- ;;--------------------------------------------------------- ;;ASDK Framework Functions: ;;--------------------------------------------------------- ;;--------------------------------------------------------- ;; Set pixel function: set, flip or poll a virtual world pixel ;;---------------------------------------------------------------------------------------- ;; getbitstatus (argument: Accumulator (0 - flip pixel, 1 - set pixel, 2 - poll)) ;; (return : Accumulator (0 - off, not zero - on). ;; Note that setting or flipping a pixel returns it's previous state (usefull!) ;; (uses all temp vars a,b,c,d) ;;---------------------------------------------------------------------------------------- ;;---------------------------------------------------------------------------------------- ;; Mapping sprites to the virtual world and detecting collisions in the virtual world: ;;---------------------------------------------------------------------------------------- ;; findspritexyfromvirtualworldpixel - returns the corresponding x,y sprite coordinates in temp vars c & d ;; for any target pixel in the virtual world (if it's outside the viewable area 0's are returned to collapse the sprite). ;; (does not touch temp vars a & b) ;;---------------------------------------------------------------------------------------- ;;---------------------------------------------------------------------------------------- ;; Scrolling or panning the camera to another area: ;;---------------------------------------------------------------------------------------- ;; scrollvirtualworldtoggle should be set to 1 to engage both kernels any time you change the CAM coordinates (set it back to 0 after scrolling to eliminate flicker). ;;---------------------------------------------------------------------------------------- ;;----------------------------------------------------------------------------------------;; ;; Loading sprite definitions: ;;---------------------------------------------------------------------------------------- ;; loadplayer (arguments: accumulator (0/1; player0/player1),y (0,8,16,24... sprite image index in inline sprite library) ;; loadplayerupsidedown (same arguments, vertical flip) ;; (does not touch temp vars a & b) ;;----------------------------------------------------------------------------------------;; ;; set4bitvar: Typecasting function to cast and store 8-bit variables (with values 0-15) as 4-bit variables ;;----------------------------------------------------------------------------------------;; ;; Where to put your Abstract Assembly game: ;; Put it in Gameloop and Gameloop2 (top and bottom blanks) - your game code goes there! ;;----------------------------------------------------------------------------------------;; ;;-- Update log: ;;----------------------------------------------------------------------------------------;; ;;20140109 Memory optimisation for more space: Bankswitching switchboard moved to bank3 which is always in view since we ;; switch only the top 2K, not the entire 4k of address space, thus only one switchboard is needed ;;20140125 Compatibility fix for Real SuperCharger Hardware; Accumulator contents are lost switching from bank 3/2 to 3/1. ;;-------- ;;20140128 Freed temp vars a & b from all functions except getbitstatus and scrolling the virtual world ;;20140128 Fixed bug in function loadspriteupsidedown (sprites were juxtaposed) ----------------------------------------------------------------------------------------------------- ;;--------------------------------------------------------- PROCESSOR 6502 INCLUDE vcs.h ; INCLUDE macro.h ;uncomment if you need macros SEG.U VARS ORG $80 ;---------------------------------------------------------------- ;---------------------------------------------------------------- ;---------------------------------------------------------------- ;SuperCharger constants ;------------------------ ;---------------------------------------------------------------- WRITEPROTECT_DEST_ADRS = $FFF8 RAM = $F000 ;---------------------------------------------------------------- ;---------------------------------------------------------- ;-- Constants: ;---------------------------------------------------------- TIME_OVERSCAN1 = 35;-1 ; $D38A TIME_OVERSCAN2 = 35-1 ; $DA0D TIME_VBLANK1 = 43 ; $D2B1 ;20140204 need a bit more time, removing trailing wsync... TIME_VBLANK2 = 17 ; $D9D5 TIME_BIG = 255-1 ; $D9DF ;------------------------------------------------------------------------------ ;--- RAM VARIABLE MAP ;------------------------------------------------------------------------------ ; SuperChip Reminder - if you want to write: ; use $F000-$F07F, but if you want to read, use $F080-$F0FF. ; now using the CBS RAM Double SuperChip for 256 extra bytes of RAM. ; to render the large 240 byte virtual worlds dynamic. ;------------------------------------------------------------------------------ ;DYNAMICSYNC = $8c o=$8c ;--- two bytes for music engine, pointer & sustain (score is ROM data block) MUSICINDEX = $80 ; storage location (1st byte in RAM) SUSTAINFORFRAMES = $81 ; 129 a = $82 ; 10 bytes for variables, a-j b = $83 ; note: vars a,b,c,d,e are temp vars so use the stack if you need to preserve them! c = $84 d = $85 e = $86 ; music engine is using this (it shouldn't) so also a temp var, push to preserve f = $87 g = $88 h = $89 i = $8a j = $8b ;REVBT = $8d ; REVBT var and ReverseB (Reversebyte) routine are not used at all. ;YIndex = $8d ; reusing this location then, will be used to calculate BYTErowoffset as a large virtual world Y value for concept (not necessary) ; Yindex depreciated; BITIndex is direct x position for the virtual world Camera and BYTErowoffset is used to calculate y pos ;(+12 for each y position increment; this is the width of the virtual world, 12 byte row x 8 bits= 96 bits per row [92 addressable, first four are unused]) ;PLAYFIELDINDEX = $8e ; just feed variable $8e for reassignment... var1 = $8e ;PLAYFIELDINDEXstep = $8f ; 18 bytes for variables so far... ;temp1 = $8f; this temp var mirrors PLAYFIELDINDEXstep for use in your gameloop or anywhere outside of the secondary kernel. var2 = $8f ; PLAYFIELDINDEXstep var freed - replaced with temp var d in kernel ;reserving another 15 bytes, $90-9F ; --- virtual world variables BITIndex = $90 BYTErowoffset = $9d ; incs 1 y position in virtual world for each rowsize (12 bytes);goes with BITIndex (x position) ;20130912 optimisation, freed 3 more vars (now x,y and z) ;p0 = $91 ;p1 = $92 ;p2 = $93 x = $91 y = $92 z = $93 ;-- Scrolling Demo Vars (depreciated) scrollspeed = $94 ; scrollspeed! scrollspeedinit = $95; scrollspeed fade... changed to scroll direction (not so intuitive) ;-- End Scrolling Demo vars ;--------------------------- SCROLLOUT Game Vars px = $96 ; used for game status ( no player x!) py = $97; player y (there is no player X in Scrollout, it's always on the left) bx = $98; ball x by = $99; ball y bitx = $9a bity = $9b ;bitstatus = $9c ; depreciated, don't use ; $9d in use for BYTErowoffset player0x = $9e player0y = $9f ;--------------------------- End Scrollout Game Vars ;mirroring some vars: ;(YIndex, scrollspeed,scrollspeedinit,bitstatus) = (t,u,v,w) t = $8d ; YIndex u = $94 ; scrollspeed v = $95 ; scrollspeedinit w = $9c ; bitstatus RAMplayfield = $A0 ; - BF$ ; using $A0-DF$ (60 bytes) to hold playfield image (20x10 in 40x10 grid) ; Note: half of this screen buffer, 30 bytes, is loaded WYSIWYG from the virtual world buffer (240 bytes), ; then it is expanded 2:1 to 60 bytes and flipped to and fro for the display. player1x = $e0 player1y = $e1 ; 95 bytes of RAM reserved so far; 35 + 60 for the screen buffer missile0x = $e2 missile0y = $e3 missile1x = $e4 missile1y = $e5 ;ball0x = $e6 ;ball0y = $e7 p = $e6 q = $e7 k = $e8 l = $e9 m = $ea n = $eb ; now 105 bytes of system RAM reserved; 23 remaining o = $ec ; 22 remaining... must leave most of that for the stack ;) ;Flicker Free mode: ;these next three variables are used to disengage the primary kernel ;whenever the playfield is not scrolling across the virtual world ;: These two have been depreciated, only the toggle is needed ;;checkvirtualx = $ed ;;checkvirtualy = $ef scrollvirtualworldtoggle = $f0 ; flag variable, could be a bit r = $ed s = $ef score = $f1 ;really close to the stack now... 15 bytes remain ;------------------------------------------------------------------------------ ; now using CBS RAM for 2x the SC .. 256 bytes! ;SuperChipWRITE = $F000 ;;ExtendedPlayfieldSCW = $F000; $1000 ; $F000 ;SuperChipREAD = $F080 ;;ExtendedPlayfieldSCR = $F100 ; 256 bytes now CBS RAM! $F080; $1080 ; $F080 ; makes no diff offset by 1 for cbs ram overwrite bug ; find write bug... there was no write bug ; ---- end ram vars ; TOP 16 bytes of high RAM (top of CBS RAM) to load sprite data ;Sprite0SCW = ($F000 + $F0) ; + 240 ;Sprite0SCR = ($F100 + $F0) ; + 240 ;Sprite1SCW = ($F000 + 248) ; + $F8 ;--redefine for supercharger ;;;Sprite1SCW = ($F000 + $f8) ; + $F8 ;;Sprite1SCR = ($F100 + $f8) ; ---------Code Locations: ;---------------------------------redefine for SuperCharger ; Supercharger Bank Config SCSAVE DS 1 ; Display Remaining RAM ;echo "----",($100 - *) , "bytes left (RAM)" echo "----ASDK Compact SuperCharger based RUN ANYWHERE Edition - 6K of fun!" ; Bank 1 ; initially the 2nd 2k: SEG BANK1 ORG $1000,0 rorg $1800 ;added nop ;---------------------------------- next 254 bytes are page-break free! ;---------------------------------- great spot for data tables as arrays (these break on pagebreaks) DYNAMICSTATICRAMARRAYS ; ---- at the top, 254 bytes until pagebreak (the nop) must put a spacer in after that ;mazecolours ; artifact colourset (all high intensity colours from the extreme left of the chart) ; .byte $b0,$50,$60,$a0,$02,$40,$22,$c0 ; .byte $92,$f4,$52,$a2,$d0,$f2,$70,$e2 ; --------------,0 GameLoop ;----------------------------- 2K game loop! ;---imported block replacegameloopcode ;---end imported block ; jmp GameLoopReturn ; rts ;--------------------------- ; ;---- end gameloop ;--------------------------- ;bityindex .byte 0,12,24,36,48,60,72,84,96,108,120,132,144,156,168,180,192,204,216,228 ;use fastyindex, same table ;----------------------------------------------------------------- ;----------Gameloop Subroutines: (these can also live in bank 3!) ;----------------------------------------------------------------- ;----------------------------------------------------------------- ;----------------------------------------------------------------- ;------------------------------ ;---------------Init Section2:- ;------------------------------ init2 ; init section relocated here to prevent page break in kernel: ;---------------- lda #1 sta scrollvirtualworldtoggle ; -----init: turns off after Camera view table is refreshed from the virtual world ;Init: ;--- start imported block replaceinitcode ;--- end imported block rts ;-------------------------- ;--------------------------end init section 2 ;resposition cam: ;; lda #120 ;; sta BYTErowoffset ;; lda #1 ;; sta scrollvirtualworldtoggle ;; lda #0 ;; sta BITIndex ;; sta f ; !important, reset frame or monsters may ghost! ;------------------------------------------------------------------------ ;20140202 optimisation for heavy getbitstatus calls: ;moved to bank2 ;----------------------------------------------- ;------------------------------------ end bank 1 ;----------------------------------------------- ; Bank 2 ; potential 2nd 2k: can be swapped with bank1 SEG BANK2 ORG $1800,0 rorg $1800 ; added nop superchargervars ;- note this label could be in bank1 right in the same spot ; - under the switchboard but this table would still be used: ;uncomment these "RAM variables" if you wish to use them (you can also init them here). ; uncomment more of them as you need them (Note: if you need more than 250 variables you will need to page align this block). ; HEX FFEFAB ; .byte %01010101,%01011110 ; .byte 0,0,0,0,0,0,0,0,0,0,0,0 ; .byte 0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;------------------------------------------------------------------------ ;playfieldlookup table relocated to bank3 to support kernel access as well ;-------------------------------------------------------- ;-- double buffer update: 20130410 ;-- this function piggybacks GetBitStatus whenever the playfield CAM ;-- overlaps the virtual world and bits are set there ;-- (otherwise set pixels would not show until you scroll and engage the primary kernel) ;-------------------------------------------------------- doublebufferupdate ; d,c temp vars hold target x,y for localised playfield pixel: ; They are set whenever a pixel is set or flipped and can be ; used to map sprite movement to virtual world movement (clear them first) ;get argument: ldx a ; 1 = set pixel, 0 = flip pixel ldx c ldy playfieldlookup,x ;;;20140203 opt ;; lda #0 ;; ldy c ;; beq donegetlocalisedrowoffset ;; jmp getlocalisedrowoffset ;;getlocalisedrowoffsetOpt ;; cpy #3;#2 ;; bcc donegetlocalisedrowoffsetOpt ;; clc ;; adc #18; #12 ;; dey ; ;; dey ;; dey ;; cpy #3;#2 ; why double compare? ;; bcs getlocalisedrowoffsetOpt ;; beq donegetlocalisedrowoffset ;;donegetlocalisedrowoffsetOpt ;tay ;cpy #0 ; implied everytime we reload the accumulator in the loop ;beq donegetlocalisedrowoffset ;;getlocalisedrowoffset ;; clc ;; adc #6 ;; dey ;; bne getlocalisedrowoffset ;;donegetlocalisedrowoffset ;----------------*end opt) ; tay ;--------------------------------------------------------------- ; y reg now has offset for playfieldbuffer target row (y value) ; var b holds the x value (may push offset up 1-5 bytes in row to find target bit) lda d bne notzerobit lda RAMplayfield,y ldx a beq flippixel0 ora #%00110000; backwards phat pixel 0 sta RAMplayfield,y rts flippixel0 eor #%00110000; flip phat pixel 0 sta RAMplayfield,y rts notzerobit cmp #16 bcc continueopta jmp not15bit continueopta ;optimisation pass1, unweight algorithm: cmp #8 bcc optjump jmp not7bit optjump cmp #1 bne not1bit lda RAMplayfield,y ldx a beq flippixel1 ora #%11000000; backwards phat pixel 1 sta RAMplayfield,y rts flippixel1 eor #%11000000; flip phat pixel 1 sta RAMplayfield,y rts not1bit cmp #2 bne not2bit iny lda RAMplayfield,y ldx a beq flippixel3 ora #%11000000; forwards phat pixel 3 sta RAMplayfield,y rts flippixel3 eor #%11000000 ; flip phat pixel 3 sta RAMplayfield,y rts not2bit cmp #3 bne not3bit iny lda RAMplayfield,y ldx a beq flippixel4 ora #%00110000; forwards phat pixel 4 sta RAMplayfield,y rts flippixel4 eor #%00110000 ; flip phat pixel 4 sta RAMplayfield,y rts not3bit cmp #4 bne not4bit iny lda RAMplayfield,y ldx a beq flippixel5 ora #%00001100; forwards phat pixel 5 sta RAMplayfield,y rts flippixel5 eor #%00001100 ; flip phat pixel 5 sta RAMplayfield,y rts not4bit cmp #5 bne not5bit iny lda RAMplayfield,y ldx a beq flippixel6 ora #%00000011; forwards phat pixel 6 sta RAMplayfield,y rts flippixel6 eor #%00000011 ; flip phat pixel 6 sta RAMplayfield,y rts not5bit cmp #6 bne not6bit iny iny ; move over another byte lda RAMplayfield,y ldx a beq flippixel7 ora #%00000011; backwards phat pixel 7 sta RAMplayfield,y rts flippixel7 eor #%00000011 ; flip phat pixel 7 sta RAMplayfield,y rts not6bit cmp #7 bne not7bit iny iny ; move over another byte lda RAMplayfield,y ldx a beq flippixel8 ora #%00001100; backwards phat pixel 8 sta RAMplayfield,y rts flippixel8 eor #%00001100 ; flip phat pixel 8 sta RAMplayfield,y rts not7bit ; cmp #16 ; bcc bopt2 ; jmp not15bit ;bopt2 cmp #12 bcs not11bit cmp #8 bne not8bit iny iny ; move over another byte lda RAMplayfield,y ldx a beq flippixel9 ora #%00110000; backwards phat pixel 9 sta RAMplayfield,y rts flippixel9 eor #%00110000 ; flip phat pixel 9 sta RAMplayfield,y rts not8bit cmp #9 bne not9bit iny iny ; move over another byte lda RAMplayfield,y ldx a beq flippixel10 ora #%11000000; backwards phat pixel 10 sta RAMplayfield,y rts flippixel10 eor #%11000000 ; flip phat pixel 10 sta RAMplayfield,y rts not9bit cmp #10 bne not10bit iny iny ; move over another byte iny ; move over another byte lda RAMplayfield,y ldx a beq flippixel11 ora #%00110000; backwards phat pixel 11 sta RAMplayfield,y rts flippixel11 eor #%00110000 ; flip phat pixel 11 sta RAMplayfield,y rts not10bit cmp #11 bne not11bit iny iny ; move over another byte iny ; move over another byte lda RAMplayfield,y ldx a beq flippixel12 ora #%11000000; backwards phat pixel 12 sta RAMplayfield,y rts flippixel12 eor #%11000000 ; flip phat pixel 12 sta RAMplayfield,y rts not11bit cmp #12 bne not12bit iny iny ; move over another byte iny ; move over another byte iny ; move over another byte lda RAMplayfield,y ldx a beq flippixel13 ora #%11000000; forewards phat pixel 13 sta RAMplayfield,y rts flippixel13 eor #%11000000 ; flip phat pixel 13 sta RAMplayfield,y rts not12bit cmp #13 bne not13bit iny iny ; move over another byte iny ; move over another byte iny ; move over another byte lda RAMplayfield,y ldx a beq flippixel14 ora #%00110000; forewards phat pixel 14 sta RAMplayfield,y rts flippixel14 eor #%00110000 ; flip phat pixel 14 sta RAMplayfield,y rts not13bit cmp #14 bne not14bit iny iny ; move over another byte iny ; move over another byte iny ; move over another byte lda RAMplayfield,y ldx a beq flippixel15 ora #%00001100; forewards phat pixel 15 sta RAMplayfield,y rts flippixel15 eor #%00001100 ; flip phat pixel 15 sta RAMplayfield,y rts not14bit cmp #15 bne not15bit iny iny ; move over another byte iny ; move over another byte iny ; move over another byte lda RAMplayfield,y ldx a beq flippixel16 ora #%00000011; forewards phat pixel 16 sta RAMplayfield,y rts flippixel16 eor #%00000011 ; flip phat pixel 16 sta RAMplayfield,y rts not15bit cmp #16 bne not16bit iny iny ; move over another byte iny ; move over another byte iny ; move over another byte iny lda RAMplayfield,y ldx a beq flippixel17 ora #%00000011; backwards phat pixel 17 sta RAMplayfield,y rts flippixel17 eor #%00000011 ; flip phat pixel 17 sta RAMplayfield,y rts not16bit cmp #17 bne not17bit iny iny ; move over another byte iny ; move over another byte iny ; move over another byte iny lda RAMplayfield,y ldx a beq flippixel18 ora #%00001100; backwards phat pixel 18 sta RAMplayfield,y rts flippixel18 eor #%00001100; flip phat pixel 18 sta RAMplayfield,y rts not17bit cmp #18 bne not18bit iny iny ; move over another byte iny ; move over another byte iny ; move over another byte iny lda RAMplayfield,y ldx a beq flippixel19 ora #%00110000; backwards phat pixel 19 sta RAMplayfield,y rts flippixel19 eor #%00110000; flip phat pixel 19 sta RAMplayfield,y rts not18bit iny iny ; move over another byte iny ; move over another byte iny ; move over another byte iny lda RAMplayfield,y ldx a beq flippixel20 ora #%11000000; backwards phat pixel 20 sta RAMplayfield,y rts flippixel20 eor #%11000000; flip phat pixel 20 sta RAMplayfield,y rts ;------------------------------------------------------ ;-----end double buffer update ;-- 20131024 routine for mapping sprite movement to coordinates on the virtual world; ;-- return values are only set if the coordinates overlap the playfield (can't display a sprite offscreen! But you can map it's movement for when you scroll over to it again) ;-------------------------------------------------------- findspritexyfromvirtualworldpixelBANK2 ;-------------------------------------------------------- ;-- inputs bitx, bity ;-- outputs temp var d,c ;-------------------------------------------------------- ; lda bitx ; sec ; sbc BITIndex ; cmp #20 ; bcs VirtualWorldPositionNotInCameraView ; sta d ; d now holds x position value in visible playfield ; is bity also within the playfield buffer? ; ldy #0 ; lda BYTErowoffset ;calcVWyopt1 cmp #36 ;bcc donecalcVWyopt1 ;sec ;sbc #36 ;iny ;iny ;iny ;bne calcVWyopt1 ;donecalcVWyopt1 ;calcVWy1 cmp #0 ; beq donecalcVWy1 ; sec ; sbc #12 ; iny ; ; bvc calcVWy1 ; branch usually ;jmp calcVWy1 ;donecalcVWy1 ; sty c ; lda bity ; sec ;-- and here... the above 30 bytes of code are used twice (should sub, save 25 bytes... l8r) ; sbc c jsr ispixelincamera; reusing shared routine instead of duplicating it above, saves some more space. cmp #10 beq VirtualWorldPositionNotInCameraView sty c ; c now holds y position value in visible playfield ;now calculate x sprite coordinate for x pixel position: inc d lda d ;multiply by 8 - screen is 20 fat pixels wide for 160 fine pixels asl;x2 asl;x2 asl;x2 ...= x8 sta d inc d ;now calculate y sprite coordinate for y pixel positon: ; tougher, sprite y counts down, pixel y counts up lda #9 sec sbc c ; now playfield y is inverted; screen is 10 phat pixels tall (0-9) ;replace x10 with 10 byte lookup ; can factor in the adjustments too: tay lda tenbytelookup,y ;; sta c ;; ; now calculate sprite y from it screen is 96 pixes so must multiply by 10 ;; asl ;; asl ;; asl ; x8 ;; clc ;; adc c ;...x9 ;; adc c ;.. x10 ;----------adjust (also handled in table) ;; sec ;; adc #5 ;; cmp #60 ;; bcs doneadjustpixels ;; adc #2 ;; cmp #10 ;; bcs doneadjustpixels ;; adc #2 ;;doneadjustpixels sta c ; c now holds sprite y coordinate (inverted) that maps to the visible screen pixel ;rts jmp findspritexyfromvirtualworldpixelRETURN tenbytelookup ; .byte 9,10,20,30,40,50,60,70,80,90 ;adjustd: .byte 11,17,27,37,47,56,66,76,86,96 VirtualWorldPositionNotInCameraView lda #0 ; clear sprite coordinates, nothing to display sta c sta d jmp findspritexyfromvirtualworldpixelRETURN ; rts ;-------------------------------------------------------- ;--end findsprite coordinates from virtualworld position ;-------------------------------------------------------- ;------------------------------------------------------------------------------------- ;-- space saving optimisation: shared routine from getbitstatus,findspritexyfromvirtualworkpixel ispixelincamera ;checks if target virtual world pixel is in the camera view (visible playfield) ; load acculuator with 11 and rts to kickout ; clv ;yes ; no ; is bitx also within the playfield buffer? lda bitx cmp BITIndex bcc kickout ; needed extra kickout sec sbc BITIndex cmp #20 ; bcs skipdoublebufferupdate bcc continuecheckpixelincam kickout lda #10 ; kickout rts continuecheckpixelincam sta d ; d now holds x position value in visible playfield; value for playfield buffer overlap ; is bity also within the playfield buffer? ;lda BYTErowoffset ;20140202 load balancing opt for lower screen roll ldy bity ;; lda BYTErowoffset ;; bne continuecheckpix ;; rts ; no offset adjustment necessary for bity ;;continuecheckpix lda BYTErowoffset bne notzerooffset cpy #10 bcs kickout ; out of range rts ; y already has bitindex and it needs no offset notzerooffset lda fastyindex,y cmp BYTErowoffset ; new needed kickout for y! bcc kickout ; avoid negative bne cnchk ldy #0 rts cnchk sec sbc BYTErowoffset cmp #120;#108 bcs kickout ; not in view cmp #12 bne noty48;12 ldy #1 rts noty48 cmp #60 bne noty12 ldy #5 rts noty12 bcs noty60 ; load balance cmp #24 bne noty24 ldy #2 rts noty24 cmp #36 bne noty36 ldy #3 rts noty36 ; opt: has to be 4 ;; - cmp #48 ;; - bne noty60;noty48 ldy #4 rts noty60 cmp #72 bne noty72 ldy #6 rts noty72 cmp #84 bne not84 ldy #7 rts not84 cmp #96 bne not96 ldy #8 rts not96 ; has to be 108: ldy #9; #108 rts ; opt 20140220; simpler load balancing... ;calcVWyopt cmp #24;#48;#36 20140131 better optimisation ; bcc donecalcVWyopt ;+` sec ; sbc #24; #48;#36 ;; iny; ;; iny ; iny ; iny ; bne calcVWyopt ; ;donecalcVWyopt ; ;calcVWy cmp #0 ; beq donecalcVWy ; sec ; sbc #12 ; iny ; ; bvc calcVWy ; branch usually ; ;jmp calcVWy ;donecalcVWy ; ;tya ;;; sty c ;;; lda bity ;;; sec ;;; sbc c ; ;-- the above 30 bytes of code are used twice, here and... ; rts ;---------------------------- ;------------------------------------------------------------------------------------- ;----GetBitStatus (subroutine/function) gets or sets bit status; accumulator passes the argument ; vars a and b used as temp vars; initially used the stack (preferred) but ; revised on debugging and never pushed it back ;) ; (so vars a and b will be overwritten if you use them in your code) ;------------------------------------------------------------------------------------- getbitstatusBANK2 ;------------------------------------------------------------------------------------- ; ... this should be a dual get/set routine. ; arguments passed via the accumulator (lda #arg [0,1,2]) ; always do this... lda #0 to flip the target bit via inversion, will clear or set it. ; lda #1 for setting it, also returns it's prior state in the accumulator, 0 if it was previously off ; lda #2 for polling - just returns the bits status ;20131127 yes you can...; sty a;20131031 now in bank2; can't pass accumulator when bankswitching so using y! sta a ; keep get/set flag that was passed in the accumulator cmp #2 beq skipdoublebufferupdate ; 20140123 bugfix, polled pixels ghosting in playfield buffer (shouldn't be set anywhere) ;-- 20130410 Now that the primary kernel can be disengaged, ;-- pixels set on the large virtual world must also be set on the ;-- second display buffer (playfield buffer in zero page) whenever they overlap the CAM coordinates ;-- (otherwise they won't appear until you scroll) jsr ispixelincamera cmp #10 beq skipdoublebufferupdate sty c ; c now holds y value for playfield buffer update ;doublebuffer update: jsr doublebufferupdate skipdoublebufferupdate ;---20131127 getbitstatus rewrite for size and perf (need it for SuperCharger framework fit) ; lda #0 ldx bity lda fastyindex,x ; 20140203 best opt; using a 20 byte lookup table! ;ad01 beq nobyterowoffset ; loadbalancing opt2 20140131 ; cpx #5 ; bcc ad0a ; clc ; adc #60 ; dex ; dex ; dex ; dex ; dex ; jmp ad01 ;ad0 beq nobyterowoffset ; loadbalancing opt ;ad0a cpx #2 ; bcc ad1 ; clc ; adc #24 ; dex ; dex ; jmp ad0 ;ad1 ; clc ; adc #12 ; dex ; bne ad1 nobyterowoffset tax lda bitx clc adc #4; opt: bit 0-bit92 = bit4-96 ; find bitx byte offset, remaninder is the target bit: ; can optimise this further too! What is it doing? ; looks like / 8; 40 goes in 5 goes out... ; replace with lsr lsr lsr, need the remainder too ; 42 goes in 5 r2 comes out, 5 is added to x, r2 in a ; 00101010 no +1, carry is clear ; 00010101 +2, carry set ; 00001010 no +4 carry clear ;---------- ; 00000101 5 r2 ; 44 goes in r4 cones out, 5 is added to x, r4 in a ; 00101100 no +1 carry clear ; 00010110 no +2 carry clear ; 00001011 +4 carry set ;---------- ; 00000101 5 r4 ; 47 goes in 5 r7 comes out... ldy #0 lsr bcc skipr1 iny skipr1 lsr bcc skipr2 iny iny skipr2 lsr bcc skipr4 iny iny iny iny skipr4 sta b txa clc adc b tax ; /8 value added to x tya ; remainder in a ;;ad3 cmp #32;#24 ; load balancing opt ; 20140131 pass2, 32 works better ;; bcc ad2 ;; inx; ;; inx ;; inx ;; inx ;; sec ;; sbc #32;#24 ;; jmp ad3 ;;ad2 cmp #8 ;; bcc havebit ;; inx ;; sec ;; sbc #8 ;; jmp ad2 havebit ldy ExtendedPlayfieldSCR,x ; read area of superchip/CBS RAM/ SuperCharger ROM/RAM ; reg x now has target byte to overwrite, accumulator has bitpointer ;-- only 8 handlers cmp #0 bne notb0 tya and #%10000000; was it set? sta b ; push previous bit status into temp var b tya ldy a beq clear0; get/set status is in var a, 0 flips bit, 1 always sets it ora #%10000000 ; set it cpy #0 bne done0; branch always clear0 eor #%10000000 ; clear it done0 ;sta ExtendedPlayfieldSCW,x ; store in write area of SUPERCHIP jmp donegetset notb0;----------------- cmp #1 bne notb1 tya and #%01000000; was it set? sta b ; push previous bit status into temp var b tya ldy a beq clear1; get/set status is in var a, 0 flips bit, 1 always sets it ora #%01000000 ; set it cpy #0 bne done1; branch always clear1 eor #%01000000 ; clear it done1 ;sta ExtendedPlayfieldSCW,x ; store in write area of SUPERCHIP jmp donegetset notb1;----------------- cmp #2 bne notb2 tya and #%00100000; was it set? sta b ; push previous bit status into temp var b tya ldy a beq clear2; get/set status is in var a, 0 flips bit, 1 always sets it ora #%00100000 ; set it cpy #0 bne done2; branch always clear2 eor #%00100000 ; clear it done2 ;sta ExtendedPlayfieldSCW,x ; store in write area of SUPERCHIP jmp donegetset notb2;----------------- cmp #3 bne notb3 tya and #%00010000; was it set? sta b ; push previous bit status into temp var b tya ldy a beq clear3; get/set status is in var a, 0 flips bit, 1 always sets it ora #%00010000 ; set it cpy #0 bne done3; branch always clear3 eor #%00010000 ; clear it done3 ;sta ExtendedPlayfieldSCW,x ; store in write area of SUPERCHIP jmp donegetset notb3;----------------- cmp #4 bne notb4 tya and #%00001000; was it set? sta b ; push previous bit status into temp var b tya ldy a beq clear4; get/set status is in var a, 0 flips bit, 1 always sets it ora #%00001000 ; set it cpy #0 bne done4; branch always clear4 eor #%00001000 ; clear it done4 ;sta ExtendedPlayfieldSCW,x ; store in write area of SUPERCHIP jmp donegetset notb4;----------------- cmp #5 bne notb5 tya and #%00000100; was it set? sta b ; push previous bit status into temp var b tya ldy a beq clear5; get/set status is in var a, 0 flips bit, 1 always sets it ora #%00000100 ; set it cpy #0 bne done5; branch always clear5 eor #%00000100 ; clear it done5 ;sta ExtendedPlayfieldSCW,x ; store in write area of SUPERCHIP jmp donegetset notb5;----------------- cmp #6 bne notb6 tya and #%00000010; was it set? sta b ; push previous bit status into temp var b tya ldy a beq clear6; get/set status is in var a, 0 flips bit, 1 always sets it ora #%00000010 ; set it cpy #0 bne done6; branch always clear6 eor #%00000010 ; clear it done6 ;sta ExtendedPlayfieldSCW,x ; store in write area of SUPERCHIP jmp donegetset notb6;----------------- ; cmp #7 ; bne notb7 tya and #%00000001; was it set? sta b ; push previous bit status into temp var b tya ldy a beq clear7; get/set status is in var a, 0 flips bit, 1 always sets it ora #%00000001 ; set it cpy #0 bne done7; branch always clear7 eor #%00000001 ; clear it done7 ;sta ExtendedPlayfieldSCW,x ; store in write area of SUPERCHIP ; jmp donegetset ;notb7;----------------- ;fall through ;----------------end rewrite donegetset ldy a cpy #2 ;20131130 adding getstatus option 2 (0 flip, 1 set, 2 getstatus only) bne continuesetorflip ; (saves doing double 0 calls to poll status) ; 20140131 lda b not yet (sc doesn't allow a cmp#0 following lda to fall through) lda b rts continuesetorflip ;-- turn off write protect ldy #%00011011;write protect off, still in bank2 cmp RAM,y nop cmp WRITEPROTECT_DEST_ADRS ;-- write value to RAM "ROM" tay cmp RAM,y ; prepare the value for the write nop cmp ExtendedPlayfieldSCR,x ; update Virtual World Pixel ;sta ExtendedPlayfieldSCW,x ldy #%00011001 ; still in bank2, write protect on cmp RAM,y nop cmp WRITEPROTECT_DEST_ADRS ;20131031 doing the LDA b in the switchboard after bankswitching (can't pass accumulator) ;20131127 yes you can... ; 20140131 no, can't lda b yet (sc doesn't allow a cmp#0 following lda to fall through) lda b; pla ; return previous bit status rts ; getbitstatus ------------------------------- ;----SetBitStatus (SCROLLOUT) ;setbitstatus ; nop ; not used, above routine handles both ; nop ; rts ;---------------------------- ;---------------------------- ;---------------------------- ;;AbstractPlayfieldBuilderBANK2 ; ---opt: no longer used; now part of transpose ;---------------------------- ;start abstract playfield builder - pushes 20x10 bit grid blocks into RAM playfield ; double size playfield pixels so 60 bytes of RAM from 30 bytes of ROM ;--------------------------------------------------------------------- ; using $A0-BF$ (60 bytes) to hold playfield image (20x10 in 40x10 grid) ;----------------------------- ;end abstract playfieldbuilder ; lda #0 ; 30 bytes of ROM to read ; sta b ; using variable b to hold rom offset ; ;lda #0; $A0 offset to point to start of playfield RAM grid ; sta c ; using variable c ; lda #%10101010 ; 20131128 combining in pushextendedplayfield... ;; rts ;abstract playfield builder ------------------------- ;--------------------------------------------------------- ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;------------------------------------------------------------- ; Background Music Engine ------------------------- ;------------------------------------------------------------- PlayMusic ; pha ; push a ; tya ; pha ; push y ;jmp musicfinish ;fix timing? yes, reduce it! lda SUSTAINFORFRAMES ;cmp #0 bne waitnextnote ;get next notes: ; - freeing e ;; lda e ;; cmp #1 ;; bne continuenote ;; ldy MUSICINDEX ;; iny ;; ; iny ; this throws it off but sounds good ;; ; lda #10 ;; ; gets reset next, sta SUSTAINFORFRAMES ;; jmp skipnote continuenote ldy MUSICINDEX ; jsr selectsong; lda MusicData,y sta AUDC0 ; audio wave type Oscillator 0 iny ; jsr selectsong; lda MusicData,y sta AUDF0 ;audio frequency Oscillator 0 ;------------- skipnote ;-- substitute sound fx forvoice 0 (overwrite)? iny ; jsr selectsong; lda MusicData,y sta AUDC1 ; audio wave type Oscillator 0 iny ; jsr selectsong; lda MusicData,y sta AUDF1 ;audio frequency Oscillator 0 iny ; jsr selectsong; lda MusicData,y sta SUSTAINFORFRAMES ; cmp #0; 0 duration signals reset to start of tune bne jump1 sta MUSICINDEX ;initialize and start over ;; lda #0; reset to main theme #5; reset song index to cool theme ;; sta var1 ; reset song index to main theme (in case a 2ndary theme was playing) rts ;!!! double work: jmp PlayMusic jump1 iny ; point to next data line (for musical score) in the table sty MUSICINDEX waitnextnote ;---------------------------Wax and Wane the two voices accross each other lda SUSTAINFORFRAMES sta AUDV0 ;sec ;; lda #16 ;; sec ;; sbc SUSTAINFORFRAMES ;(like the other effect) sta AUDV1 dec SUSTAINFORFRAMES ;decrement framedelay counter ;; bne continuewaitnextnote ;; ldy #0 ;; sty e ; clear sound effect if set continuewaitnextnote ; ldy e ; beq musicfinish ; ldy #1 ; cpy SUSTAINFORFRAMES ; bcs musicfinish ; sty SUSTAINFORFRAMES musicfinish rts ;selectsong ; lda var1 ;; beq maintheme ; cmp #1 ; beq secondtheme ; ;--- add additional themes ; cmp #2 ; beq thirdtheme ;maintheme ; lda MusicData,y ; rts ;secondtheme ; lda MusicDataPacAteDot,y ; rts ; ;--- add additional theme scores ;thirdtheme ; lda MusicData2,y ; rts ;-- add additional theme scores ;-- deactivated... ;------------------------------------- ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;---- 20131128 REVISE THIS FOR 8 HANDLERS INSTEAD OF 12: ;-------- And revised to combine PushExtendedPlayfield inline with less code/cycles ------------------------------------- pushabstractextendedplayfieldBANK2 ;------ ;------------------------------------- ; BITIndex - determines how far to shift in to pull the 3 byte row: ; p0, p1, p2 - variables to hold the 3 byte row ; 20130912 optimisation for memory, freeing 3 vars: ; (replacing p0,p1,p2 with temp vars a,b,d) ;---- 20131128 REVISE THIS FOR 8 HANDLERS INSTEAD OF 12: ;revision.... ldx BITIndex ; inx ; inx ; inx ; inx ; push it up 4, that's where it really is now we need 8 handlers instead of 12 ;add to row offset lda #0 sta b; byte offset to start at txa opt32 cmp #24 bcc doneopt32 inc b inc b inc b sec sbc #24 jmp opt32 doneopt32 getindexoffset cmp #8 bcc doneindexoffset inc b sec sbc #8 jmp getindexoffset doneindexoffset sta a ; bit offset position in ;now add byte offset down lda BYTErowoffset clc adc b ; add bytes down (already 12 per row) and amount of bytes in sta b ; byte offset to start at lda #0; #9 ;... inc to 9 instead sta c ; dec 9 to 0, 10 steps to build 10 rows shiftandtransposephatpixels lda b ; preserve pha lda a pha ldy b; #! ; lda a ; bne posnot0 ;pos0 ;---------------------------------- ;-- no shifting required, just transpose ;-- (shift and) call transposer (reuse transposer!) ldx c lda ExtendedPlayfieldSCR,y+3 ; 4th byte holds a bit we need sta c ; store the 4th byte with that extra bit in c lda ExtendedPlayfieldSCR,y sta a;p0 lda ExtendedPlayfieldSCR,y+1 sta b;p1 lda ExtendedPlayfieldSCR,y+2 sta d;p2 pla bne notbp0 jmp transposenext ; bitpos0 notbp0 rol c rol d;p2 rol b;p1 rol a;p0 cmp #1 bne notbp1 jmp transposenext;bitpos1 notbp1 rol c rol d;p2 rol b;p1 rol a;p0 cmp #2 bne notbp2 jmp transposenext notbp2 rol c rol d;p2 rol b;p1 rol a;p0 cmp #3 bne notbp3 jmp transposenext notbp3 rol c rol d;p2 rol b;p1 rol a;p0 cmp #4 bne notbp4 jmp transposenext notbp4 rol c rol d;p2 rol b;p1 rol a;p0 cmp #5 bne notbp5 jmp transposenext notbp5 rol c rol d;p2 rol b;p1 rol a;p0 cmp #6 bne notbp6 jmp transposenext notbp6 rol c rol d;p2 rol b;p1 rol a;p0 ;pla ; stx c ; jsr transposer ; jmp transposenext ;fall through to transpose next if bitpos7 ;---------------------- transposenext pha stx c ; restore c jsr transposer pla ; restore a & b temp vars used to loop and point (Bit and Byte offsets) sta a pla sta b lda b clc adc #12 sta b ; advance down one row relative to position inc c ; lda #10 ;debug first row only ; sta c ; debug lda c cmp #10 beq donegoback jmp shiftandtransposephatpixels; if we have another row to grab donegoback rts ;------------------------------------------------------------------------ ;------------------------------------------------------------------------ transposer ;----called in line rowbyrow from pushabstractextendedplayfield-replaces abstract playfield builder! lda c pha ; need cx3 lda #0 ldy c cx3 cpy #0 beq donecx3 clc adc #6 dey jmp cx3 donecx3 sta c ldy #0 ldx a ; 2 bits of first byte build 4 bits of pf0 target byte in memory ; next two bits of it build the first 4 bits of pf1, ;-- txa and #%00001000 ; keep 1st bit (1/2 byte) ; is the 1st bit set? beq skip01 tya ora #%00110000 ;set two playfield bits for 1 in pfo btye tay ; put it in y skip01 txa and #%00000100 ; keep 2nd bit (half byte) ; is the 2nd bit set? beq skip02 tya ora #%11000000 ; set next two playfield bits for 1 in pf0 byte tay skip02 ;-- write phat pf0: txa ; preserve X in A ldx c sty $A0,x ; c write offset will be 60 when b read offset is 30 tax ; restore X ;-- remember to subtract 5 from c before rts! inc c ; on to next playfield byte target, pf1 ; next two bits of read b go into the next c ldy #0 ; clear target txa ;x still has 1st byte from abstract ROM playfield and #%0000001 ; keep 3rd bit beq skip03 tya ora #%00110000 ; set two bits for 1 in pf1 target byte tay skip03 txa ; get our byte back and #%00000010 ; keep 4th bit beq skip04 tya ora #%11000000 ; set two bits for 1 in pf1 target byte tay skip04 ; ------- TRANSFORM 2nd of 3 source BYTES in the row ; inc b ; get 2nd byte ; tya ; preserve y ; ldy b ;ldx MyAbstractPlayfield,y; get rom playfield byte ldx b; $a0+#30,y ; ... from the RAM page it was dropped off at! ; tay ; restore y txa ; x now has 2nd byte (of 3 in row) from abstract ROM playfield and #%10000000 ; first bit set? beq skip05 tya ora #%00001100 ; set two bits for 1 in pf1 target byte tay skip05 txa and #%01000000 ; check 2nd bit set beq skip06 tya ora #%00000011 ; set two bits for 1 in pf1 target byte - DONE with PF1 tay ;* 20120801 this WAS the location of the col5 bug 00001111! skip06 txa ; preserve X in A ldx c sty $A0,x ; c write offset will be 60 when b read offset is 30 tax inc c ; on to next playfield byte target, pf2 ldy #0 ; clear target for 3rd byte write txa and #%00100000 ; check 3rd bit set beq skip07 tya ora #%00000011; set two bits for 1 in pf2 target byte tay skip07 txa and #%00010000 ; check 4th bit set beq skip08 tya ora #%00001100; set two bits for 1 in pf2 target byte tay skip08 txa and #%00001000; check 5th bit set beq skip09 tya ora #%00110000; set two bits for 1 in pf2 target byte tay skip09 txa and #%00000100; 6th bit set? beq skip010 tya ora #%11000000; set lasttwo bits for 1 in pf2 target byte tay skip010 ;-- on to next target byte pf0(2): txa ; preserve X in A ldx c sty $A0,x ; 3rd byte in row; c write offset will be 60 when b read offset is 30 tax ;-- inc c ; on to next playfield byte target, pf0(2) ldy #0 ; clear target txa ; superfluous here ;) and #%00000010; 7th bit set? (2nd bit, had to reverse...) beq skip011 tya ora #%00110000 ; set two bits for 1 in pf0(2) tay skip011 txa and #%00000001; 8th bit set? (1st bit, had to reverse order) beq skip012 tya ora #%11000000 ; set two bits for 1 in pf0(2) tay skip012 ;-- on to next target byte pf1(2): txa ; preserve X in A ldx c sty $A0,x ; 4th byte in row; c write offset will be 60 when b read offset is 30 tax ; restore X from A inc c ; on to next playfield byte target, pf1(2) ldy #0 ; clear target ; inc b ; get 3nd byte ; tya ; preserve y ;ldx MyAbstractPlayfield,y; get rom playfield byte ldx d; $a0+#30,y ; from the RAM page (2nd half of where we write too) it was dropped off at ;) ; tay ; restore y txa ; x now has 2nd byte (of 3 in row) from abstract ROM playfield and #%00010000 ; first bit set? beq skip013 tya ora #%00000011 ; set two bits for 1 in pf1(2) tay skip013 txa and #%00100000 ; 2nd bit set? beq skip014 tya ora #%00001100; set two bits for 1 in pf1(2) tay skip014 txa and #%01000000 ; 3rd bit set? beq skip015 tya ora #%00110000; set two bits for 1 in pf1(2) tay skip015 txa and #%10000000; 4th bit set? beq skip016 tya ora #%11000000; set two bits for 1 in pf1(2); done with it tay skip016 txa ; preserve X in A ldx c sty $A0,x ; 5th write; c write offset will be 60 when b read offset is 30 tax ; restore x inc c ; on to next playfield byte target, pf2(2) ldy #0 ; clear target txa and #%00000001; 5th bit set? beq skip017 tya ora #%11000000; set two bits for 1 in pf2(2) tay skip017 txa and #%00000010; 6th bit set? beq skip018 tya ora #%00110000; set two bits for 1 in pf2(2) tay skip018 txa and #%00000100; 7th bit set? beq skip019 tya ora #%00001100; set two bits for 1 in pf2(2) tay skip019 txa and #%00001000; 8th bit set? beq skip020 tya ora #%00000011; set twobits for 1 in pf2(2); done with it tay skip020 ; txa ; preserve X in A ldx c sty $A0,x ;6th write c write offset will be 60 when b read offset is 30 ;lda c ;sec ;sbc #5 pla sta c ; restore c from cx3 rts ; end transposer AND ; end --- pushabstractextendedplayfield ;--------------------------------------- ;------------------------------------- ; Music Data ------------------------- ;------------------------------------------------------------- ; volume0,wave,freq,volume1,wave,freq,framesduration (0 duration loops it) ;------------------------------------------------------------- ; -- volume has been depreciated for vol based sfx ; now : wav,freq,wav,freq,framesduration (5 vars) ;:note several musical score labels are defined below but ;: only one demo score of 30 bytes is populated. ; The musical scores can go here or in bank2 or some in each. MusicData5 ; .byte 8,1,3,21,18 ; .byte 8,1,3,21,14 ; .byte 11,1,3,1,0 ; ,0 - and loop ;--------------------------- ;moved playfieldlookup to bank3 ;Bank2Routine ; lda #$1e ; rts ;----------------------------------------------- ;-------------------------------------end bank 2 ;----------------------------------------------- ; Bank 3 ; the first 2k SEG BANK3 ORG $2000,0 rorg $1000 ; added ; Simple Playfield Test Card Start CLEAN_START ; ----SCROLLOUT ; LARGE PLAY AREA IS MALLEABLE IN CBS RAM ; ---- see routine in bank 1 (Note: with CBS RAM only 3.5k in bank0; don't want to waste 1/4 k on each large ROM WYSIWYG bitmap here or we will quickly run out of space for game code!) ;---- SCROLLOUT CORE is in FrameRelay (entire frame for plenty of cycles) along with the Twin Engines for abstract rendering ;;Reset ; nop ;? jmp jmparoundgraphics ;--------------- RAM ROM for Virtual World and Sprites: ;--------------- MusicData1 ; play the virtual world as a song score! ;---------------Virtual World Bitmap: virtualworld REPLACEExtendedPlayfieldSCR ;GameScreenBitmap ; 20x10 grid (3 bytes) is read from a larger play area 5x as wide (12 bytes) and twice as tall ; 1 0 4 2 12 3 20 4 28 5 36 6 44 7 52 8 60 9 68 72* 10 76 11 84 12 ;; .byte %00000000 ,%00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %00000000, %00000000, %00000000, %00000000, %00000110, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %00000000, %00000000, %00111000, %00000000, %00001111, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %01000000, %00000000, %00101000, %00000000, %00000110, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %00111000, %00000000, %00111000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %00101000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %00111000, %00000000, %00001110, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %00000000, %00000000, %00001010, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %00000000, %00000000, %00001110, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00111000, %00000000, %00000000 ;; .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00101000, %00000000, %00000000 ;-- next 10 rows is another screen down and 5 across: ;; .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00111000, %00000000, %00000000 ;; .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %00000000, %00000000, %00000000, %00000000, %00011000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %00000000, %00000000, %00000000, %00000000, %00111100, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;; .byte %00000000, %00000000, %01111111, %00000000, %00011000, %00000000, %00000000, %01111111, %00000000, %00000000, %00000000, %00000000 ;; .byte %00000000, %00000000, %01010101, %00000000, %00000000, %00000000, %00000000, %01100111, %00000000, %00000000, %00000000, %00000000 ;; .byte %00000000, %00000000, %01010101, %00000000, %00000000, %00000000, %00000000, %01100111, %00000000, %00000000, %00000000, %00010000 ;; .byte %00000000, %00000000, %01111111, %00000000, %00000000, %00000000, %00000000, %01111110, %00000000, %00000000, %00000000, %00101000 ;; .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00010000 ;; .byte %00000000, %00000000, %00000000, %00000000, %00000000, %11111111, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 ;------------------------------------------------------------- ;add padding if pagebreak breaks sprite1 ; (sprite RAM (16 bytes) now in bank II) ;-- next 16 bytes used as Sprite RAM (supports vertical flip) Sprite0SCR .byte 0,0,0,0,0,0,0,0 ;-- page break hitting sprite 1 RAM, pushing it down to the next page: ;--------------------------------- jmparoundgraphics ;--------------------------------- ;clear ramplayfield check: ldx #$F0 ; #60;#30 ; clear 1/2 the ram page lda #0 clearram sta RAMplayfield,x dex bne clearram ;------------------------ ;---------------------------------------- ; ... ok init registers: ldx #128 regs sta 0,x dex bne regs ; just the stack: ldx #255 txs ;init stack to FF - memory on the 2600 is $80-$FF! lda #0 ; test sta BITIndex ; sta BYTErowoffset ; each row size (12 bytes) inc's Y value 1 from (x,0) sta SUSTAINFORFRAMES; intialize music engine to read score sta MUSICINDEX ; initialize music engine ROM song offset at beginning of score ; lda #%00100101 jmp jumparoundgraphics2 ;--! Relocate this if it falls over a page break and SuperCharger can't access it Sprite1SCR .byte 0,0,0,0,0,0,0,0 jumparoundgraphics2 ;------------------------------------ ; init section: -- ;------------------------------------ ;---------- jsr init2 ; init section relocated to balance page break in kernel!~ ;----------- ; larger missiles ; lda #%00010000 ; sta NUSIZ0 ; sta NUSIZ1 ;nop ;nop ;lda #30 ;sta score ;------------------ ;----------------end Init Section ;----------------- ; jsr AbstractPlayfieldBuilder ; ok in init... ;jsr framerelay ;------------------------------------------------------------ StartOfFrame ;----------------------------------------------- ;------------------------------------------------------------ ; PRIMARY KERNEL is now only called when the playfield scrolls across the virtual world: lda scrollvirtualworldtoggle beq DisengagePrimaryKernel jsr framerelay ; PrimaryKernel - can do lots of calcs during a blank frame :) ; lda #0 DisengagePrimaryKernel lda #0 sta VBLANK lda #2 sta VSYNC ; vertical sync signal; initiate electron guns to upper left corner! sta WSYNC ; 3 scanlines worth of vertical sync (so TV get's a lock on it) sta WSYNC sta WSYNC lda #0 sta VSYNC ; vertical sync finished ;--------------------------------------------- ; 37 Scanlines of Vertical Blank ;--------------------------------------------- ; ldx #0 ;DYNAMIC VERTICAL BLANK: ; 37 x 76 = 2812 ... /64 = 44 ; IT'S 43.94 - SHOULD i LDX 43 AND WSYNC AFTERWARDS? ; YES OTHERWISE THAT'S 2816 .. 4 CYCLES OVER, 43 & TRAILING WSYNC SHOULD BE 2812 ldx #TIME_VBLANK1 ; #41;#43x64=2752,2812 available looks ok stx TIM64T ; 2740 cycles free for calls here ;) ;------------Music Engine Call: Relocated to the bottom blank ;-------------------------------- ;jsr callbank1ForMusicEngine ; music engine and musical scores are in bank1 along with the graphic images ;-----------GameLoop call: ;-------------------------------- ;preserve stack jsr GameLoop ; the gameloop is called every single frame in contrast to the init section, which is only called once ; jmp GameLoop;extra stack space GameLoopReturn ;---------------------------------------- ;; jsr MusicEngine ;-- Note: you can also call a second gameloop from the lower blank timeslice after the screen is drawn ;-------------------------------- ; ;-------------------------------- ;-------------------------------- ; use remaining timeslice for electron beam lda #0 sta a ; init temp var to 0 for sprite 0 sta b ; init temp var to 0 for sprite 1 ; sta player1x ;! bug, this is overwriting screen buffer ram... check ram buffer DIRECTION ; lda #180 ; sta player1y ;debug: these two lines turn off player0 (0,0 coordinates collapse sprite object) ; sta player0x ; sta player0y ;---------- ;---------- ;jmp MyVerticalBlank ;debug lda player0x ; desired position argument AND zero flag kick out beq skipplayer0horizontalsetup ldx #0 ; 0 for player1 ;sta HMCLR jsr TheMagicRoutine ; :-) skipplayer0horizontalsetup ;; jsr callbank1ForMusicEngine ;= jsr PlayMusic, music engine is in bank1 ; call horizontal positioning for 2nd sprite? lda player1x ; desired position argument AND zero flag kick out beq skipplayer0horizontalsetup2 ldx #1 ; 1 for player2 ;sta HMCLR ; this clears HMPx fine tuners ; jsr PosObject ; sta WSYNC ;sta HMOVE jsr TheMagicRoutine ; :-) ;STY HMP0,X ; Fine positioning value skipplayer0horizontalsetup2 ;-- ; call horizontal positioning for 3rd sprite? lda missile0x ; desired position argument AND zero flag kick out beq skipplayer0horizontalsetup3 ldx #2 ; 2 for missile0 jsr TheMagicRoutine ; :-) ;STY HMP0,X ; Fine positioning value skipplayer0horizontalsetup3 ;--4 ;-- ; call horizontal positioning for 4th sprite ; sta missile1x lda missile1x ; desired position argument AND zero flag kick out beq skipplayer0horizontalsetup4 ldx #3 ; 3 for missile1 jsr TheMagicRoutine ; :-) ;STY HMP0,X ; Fine positioning value skipplayer0horizontalsetup4 STA WSYNC STA HMOVE MyVerticalBlank; using timer instead of sta WSYNC lda INTIM bne MyVerticalBlank STA WSYNC ; TRAILING WSYNC (USED 43 INSTEAD OF 44 FOR INTIM) ;--------------------------------------------- ; 192 scanline playfield core ---------------- ;--------------------------------------------- ;;stx c; using temp var c to keep x free for indexing ; push bitmap 40x10 playfield screen within core: ; ldy PLAYFIELDINDEX ; increment by six every 10 pixels so y is 10 pix high ; same as ldy #0 since it's initialised ; RAMplayfield offset ; sta WSYNC ; lda #0 ;FIX sta WSYNC lda #9;#10 sta d ; prep temp var d to replace PLAYFIELDIndexStep in kernel ldx #96; #0; #96; #0 ; framerelay is an entire frame;96x2=192 ldy #0 ;sta PF0 ;sta PF1 ;sta PF2 sty c sta WSYNC MyPlayfieldCore ; hmmm... 192/2 = 96 addressable pixels (perfect) ; with 152 (76*2) cycles ; ; count out manually before the trailer ; ---- ; pull x back (at the top now) ; pla ;4 ; tay ;2 ldy c ; lda playfieldlookup,x lda RAMplayfield,y ; y now points to playfield bitmap array in high RAM (CBS) sta PF0 ;; iny ; lda RAMplayfieldByte2 ; lda RAMplayfield,y+1 sta PF1 ;; iny lda RAMplayfield,y+2 sta PF2 ;; iny ; wait until scanline is half way through... ; enough cycles already, 5+3+6+3+6+3=26 ; inx lda RAMplayfield,y+3 ; y now points to MyPlayfield bitmap array sta PF0 ;; iny lda RAMplayfield,y+4 sta PF1 ;; iny lda RAMplayfield,y+5 sta PF2 ;; iny ; 53 cycles so far ;------- end time between lines (76 cycles or less!) ; tya ;2 ; dec d; PLAYFIELDINDEXstep ; 6 lda d ; dec d beq obtainnewposition ; 3 bn dec d ; better here.. saves three cycles on heavier branch!N/A need 10! ;tya ; pha ; should be able to comment this next line; where is c reused? ; sty c ; 3 - less cycles than tya pha... pla tay... ;can't save two cycles here, need 10 for msl init; lda #9;#10 lda #0 cpx missile0y bne Missile0onbranch0; missile0done lda #10 Missile0onbranch0 STA ENAM0 jmp stepovermtn ; obtainnewposition ; cpx missile0y bne Missile0onbranch1; missile0done lda #10 Missile0onbranch1 STA ENAM0 ;lookup table opt? ; inc c ;5 ;pha ;3 ;GOTO NEXT PIXEL ROW: tya ;2 clc ;2 adc #6 ;2 sta c ;3 ; inc c lda #9 ; 2 sta d; PLAYFIELDINDEXstep ; 3 ; lda #10 ; get this out of here stepovermtn ;fix this, just broke it ;------------cycle border ;----- save these three cycles, already four cycles away: sta WSYNC ;;; inx ;3+2+3 (on) , 3+2+3+2 (off) ; lda #10 ; !! Saving cycles ; can't need 10 not 9, ld + ld still < dec saved 1 cycle ; cpx missile0y ; beq Missile0on; missile0done ; lda #0 ;Missile0on ; STA ENAM0 ; ;? ;cpx missile0y ; 3 ;beq Missile0on ;2 ;lda #0 ;3 ;beq missile0done ;2 ;Missile0on ; lda #10 ;3 ; sta ENAMO ; LDA #0 ;missile0done ; sta ENAM0 ;3 lda #0 ;2 ; save cycles again, better branch balancing sta PF0 ;3 sta PF1 ;3 sta PF2 ;3 ; accumulator already zero : cpx missile1y ;3 bne missile1set ;2 ;txa ;2 lda #11 missile1set sta ENAM1 ; 3 ;------------------------------------- ; 11 cycles and counting! ; just freed 2 :) ;jmp jumparoundspriteinitb ;debug ldy a ;3 bne savecycles ; 3 sty GRP0 ; clears sprite register when it's 0 ;) ; 0 cycles beq jumparoundspritedetail ;0 cycles ; lda #%01100101 ;TESTDATA, 1 savecycles dey ; 2 ;----------------19 cycles ... so far lda Sprite0SCR,y ; sprite0 ; 4 (absolute,y) ;lda TESTDATA-1,x ; sta GRP0 ; 3 sty a ; 3 ;bvc ;bne! bvc jumparoundspriteinit ;3 ;jmp jumparoundspriteinit ;3 jumparoundspritedetail ;-----------------------32 cycles ... so far cpx player0y ;3!0 ;lda c ;cmp player0y bne jumparoundspriteinit ;2!0 lda #8 ; 8x8 sprite matrice ; 2!0 sta a ; 3!0 jumparoundspriteinit ;---------------------39 cycles ;debug: ;; jmp jumparoundspriteinitb ; save many cycles ;--sprite for player2 ldy b ;3 ; beq fixsprite2 ;fix; bne savecycles2 ; 3 sty GRP1 ; 0 beq jumparoundspritedetailb ;0 savecycles2 dey ;2 lda Sprite1SCR,y; ; ;4 ;lda TESTDATA-1,x sta GRP1 ;3 sty b ;3 ;jmp jumparoundspriteinitb ;bne! bvc jumparoundspriteinitb jumparoundspritedetailb ;-------------------------------------57 cycles so far cpx player1y ; 3 bne jumparoundspriteinitb ; 2 lda #8 ; 8x8 sprite matrice ;2 sta b ; 3 jumparoundspriteinitb ;--- 67 cycles ;--end sprite for player2 ; nop ; nop ; nop ; nop ; nop ;nop ; nop ; nop ; nop ;-- 76 cycles ...... need to push some to the previous line ;nop ;nop sta WSYNC ; cpy #96; #192; #96 ; either offseting it at the top or the bottom of the loop changes the visible rainbow colours ;) ;cmp #96 dex ;2 ; STA WSYNC ; bne MyPlayfieldCore ;3 beq doneplayfieldcore jmp MyPlayfieldCore doneplayfieldcore ;--------------------------------------------------- ;-----Vertical Blank ------------------------------- ;--------------------------------------------------- lda #%01000010 ;sta VBLANK sta VBLANK ; vertical blank time after screen is drawn ;!!! ldx #0 ; DYNAMIC OVERSCAN: ; seeded with ? to match 30 calls to WSYNC? ; (30 x 76)/64 = 35.6 ;30x76 2280 - 64*35 = 2240, use that ldx #TIME_OVERSCAN1 stx TIM64T ; ; These events all take time and should make use of timer regs ; dynamic timer should be seeded with one for scanline by scanline work ; ... done, there are 2000 cycles available: ; notenough time! relocate to the blank space above the screen: jsr PlayMusic ; jsr DanceToMusic ; jsr pushabstractextendedplayfield ;-------------------------------------------------------- ;----Call Second Game Loop if needed (note: a second gameloop can live here in the footer if you need more gameloop time in a single frame) ;-------------------------------------------------------- ;-------------------------------------------------------- ;---- call Game Loop II (use this timeslice too) jsr GameLoop2 ; jmp GameLoop2 ;GameLoop2RETURN ;-------------------------------------------------------- ;;;;;;;;;; ;-------------------------------------------------------- ;----Call Music Engine ;-------------------------------------------------------- jsr MusicEngine ;20131124 jsr callbank1ForMusicEngine ; music engine and musical scores are in bank1 ;debugtiming jsr MusicEngine ; --- put it in the top blank; 7x76 more cycles there ;-------------------------------------------------------- ;-------------------------------------------------------- Overscan ;DYNAMIC: lda INTIM bne Overscan sta WSYNC ; trailer jmp StartOfFrame ;--------------------------------- ;--- ;Optimised algorythm for positioning the 5 sprite objects horizontally: ; ;---- The Battlezone Horizontal Positioning Routine TheMagicRoutine: LD7E0: CMP #$11 ;2 BCS LD7EA ;2 SBC #$04 ;2 BCS LD7EA ;2 ADC #$A5 ;2 LD7EA: STA WSYNC ;3 ;;?here ;; STA HMOVE ;3 LD7EE: SBC #$0F ;2 BCS LD7EE ;2 ->5 cycles per iteration! eor #$07 ;2 ASL ;2 ASL ;2 ASL ;2 ASL ;2 TAY ;2 STA RESP0,X STa HMP0,X ; Fine positioning value ; STA WSYNC ;3 ; STA HMOVE ;3 RTS ;6 ;--------------------------------- ;--- ;--------------------------------------------------------- ; Reverse Byte Routine 1100000 in REVBT becomes 00000011 ;--------------------------------------------------------- ; Note: This routine is not used, comment it out if you need the space: ;;ReverseB ; REVBT holds byte to be reversed ;; lda #0 ;; sta b ;; lda REVBT ;; and #%10000000 ; keep just the 8th bit ;;;is the 8th bit set? ;; beq skip1 ;; ; reflect it to set bit 1 ;; lda b ;; ora #%00000001 ;; sta b ; put it back in b ;;skip1 ;; ;; lda REVBT ;; and #%0100000 ; keep just the 7th bit ;;; is the 7th bit set? ;; beq skip2 ;; ;reflect it to set bit 2 ;; lda b ;; ora #%00000010 ;; sta b ;;skip2 ;; ;; lda REVBT ;; and #%00100000 ;keep just the 6th bit ;; beq skip3 ; is the 6th bit set? ;; lda b ;; ora #%00000100; reflect to set bit 3 ;; sta b ;;skip3 ;; ;; lda REVBT ;; and #%00010000 ; keep just the 5th bit ;; beq skip4; is the 5th bit set? ;; lda b ;; ora #%00001000; reflect to set bit 4 ;; sta b ;;skip4 ;; ;; lda REVBT ;; and #%00001000 ; keep just the 4th bit ;; beq skip5; is the 4th bit set? ;; lda b ;; ora #%00010000; reflect to set bit 5 ;; sta b ;;skip5 ;; ;; lda REVBT ;; and #%00000100 ; keep just the 3rd bit ;; beq skip6; is the 3rd bit set? ;; lda b ;; ora #%00100000; reflect to set bit 6 ;; sta b ;;skip6 ;; ;; lda REVBT ;; and #%00000010 ; keep just the 2nd bit ;; beq skip7; is the 2nd bit set? ;; lda b ;; ora #%0100000; reflect to set bit 7 ;; sta b ;;skip7 ;; ;; lda REVBT ;; and #%00000001 ; keep just the 1st bit ;; beq skip8 ;; lda b ;; ora #%10000000; reflect to set bit 8 ;; sta b ;;skip8 ;; ;; lda b ;; sta REVBT ; put the reversed byte in REVBT and return ;; ;; rts ;; ;------------------- ; ldy #0 ; jmp pushabstractextendedplayfield ;;pushabs ;; ldy #0 ;; ldx #0 ;;pushabs2 lda ExtendedPlayfield,x ;; sta $a0+30,y ;; iny ;; inx ;; lda ExtendedPlayfield,x ;; sta $a0+30,y ;; iny ;; inx ;; lda ExtendedPlayfield,x ;; sta $a0+30,y ;; iny ;; txa ;; clc ;; adc #10 ;; tax ;; cpy #30 ;; bne pushabs2 ;; rts ;;pushcondensedfield ; commenting this traditional routine out; It's all in the wrists - Flynn ; ldy #0 ;pushcondensedfield2 ldx MyAbstractPlayfield,y ; stx $a0+#30,y ; put it 1/2 way down on target ram page ; iny ; cpy #30 ; bne pushcondensedfield2 ; rts ;; ldy #0 ; what is this instruction doing here !? ; ----FRAMERELAY----- (Primary Kernel) ; ----BLANKS A FRAME- ; ----DOES STUFF ;)-- ;Replace this with 30 wysnc's + 30% of the screen's 192 scanline's worth of time... ; -- taking a portion of the screen should suffice instead.. see how it looks ; polling intim down from 114 perhaps ... (30 scanlines + 70 from the screen) framerelay ; not like this - lda $fff9; use Bank 1 (CBS RAM), point back to bank 0 before JSR lda #0 sta VBLANK lda #2 sta VSYNC ; vertical sync signal; initiate electron guns to upper left corner! sta WSYNC ; 3 scanlines worth of vertical sync (so TV get's a lock on it) sta WSYNC sta WSYNC lda #0 sta VSYNC ; vertical sync finished sta PF1 sta PF0 sta PF2 ; clear playfield registers for blank frame sta GRP0 ; clear sprite 0 for blank frame sta GRP1 ; sta ENAM1 ; sta ENAM0 ; 37 scanlines ofvertical blank ... 37 x 76 = 2812 ... /64 = 44 ;+192 scanlines of screen = (229 * 76)/64 = 271.9 (271 + trailing WSYNC) ; ------- break that into 255 and 16 ;) ldx #TIME_VBLANK2 stx TIM64T ; ;--- note: 76x17 cycles available right here: ;--- you could put a small block of code here if you need more time: ;--- end small block of code w16 lda INTIM bne w16 ldx #TIME_BIG ; stx TIM64T ; big block of time now! Can put the kitchen sink here if you want :) ;-----------------------------------------"Kitchen Sink" loop: ;call recieved, turn off event: sta scrollvirtualworldtoggle ; 0 already in accumulator ; ---- time intensive calls go here! replacekitchensinkcode ; playfield setup and builder calls: ;----------DEMO ROUTINE--- change scenery ;;;;; jsr changescenerydemoroutine ;jsr pushcondensedfield ; ; ! Gameloop relocated for ptfwd (dual kernel) ; Plenty of time for code here that only runs when scrolling (primary kernel activated) ;--------------------------------------------------------- ;--"Drawscreen:" ;-- Now Call the Twin Engines that emulate hardware level ;-- Horizontal Scrolling and Scaling ;----------------------------------------slide view window along bitmapped panorama currently loaded into CBS RAM: ; call primary rending engine: jsr pushabstractextendedplayfield ;-- any code that needs to access the double buffer in low RAM can go here ;-- before calling the secondary rendering engine: ;-- ... not anymore (opt combined) ;--------- call 2ndary rendering engine: ;--------- expand and flip 30 bytes of system RAM buffer into 60 Bytes for display: ;;;;;!!! Gone! ASDK Supercharger remix: jsr AbstractPlayfieldBuilder ;------------------------------------------- ;----------------------- ;---------------------------- ; ------------------------------------------ ; ---- Resume Framework ;------------------------------------------- w17 lda INTIM bne w17 ; done with large block of time! ;AGAIN, 2 WSYNC PATCH ... NO IDEA WHERE THESE WERE LOST TO; ROUNDING PERHAPS LDA #0 STA WSYNC STA WSYNC STA WSYNC ;+30 scanlines of vertical blank ; (37 x 76)/64 = 35.6 ;-----Vertical Blank ------------------------------- ;--------------------------------------------------- lda #%01000010 sta VBLANK ; vertical blank time after screen is drawn ldx #0 ; DYNAMIC OVERSCAN: ; seeded with ? to match 30 calls to WSYNC? ; (37 x 76)/64 = 35.6 ldx #TIME_OVERSCAN2 stx TIM64T ; -------- time for more calls here: Overscan2 ;DYNAMIC: lda INTIM bne Overscan2 sta WSYNC ; trailer rts ;--------------done with blank frame (framerelay) ;------------------------------------------------------------ ;------------END FRAMERELAY---------------------------------- ;------------------------------------------------------------ ;------------------------------------------------------------ ;------------------------------------------------------------- ;------------------------------------------------------------- ;------------------------------------------------------------- ;------------------------------------------------------------- ;------------------------------------------------------------- ;------------------------------------------------------------- ;-----------GameLoop2 ; 1200 bytes free for GameLoop2 or subroutines here in Bank 3 ; (300 bytes in use for graphics RAM and demo song) ; (get/free another 80 bytes: comment writesuperchargervar and readsuperchargervar if not using, the 20 4bit vars from set4bitvar are enough extra vars) ; (plus the full 2K GameLoop in Bank 1) ;-- 3 small ASDK Framework routines live here (did not fit in the extra bank) ;-- Only 1 if you are not using writevirtualworldvar and readvirtualworldvar you can comment them out ;-- (the extra 20 4bit variables should be enough extra variables!) ;------------------------------------------------------------------ ;--------------------------------------------- ;;set4bitvar; --- Typecast: Pass 8 bit value to 4-bit VirtualWorld var (0-15) ;;--vwBASIC has it's own inline write ;;------------------------------------- to run a lookup for the reset spot in Gameloop2 ;; args: registers y,a ;; ldy with offset for each of the 20 VirtualWorld vars: 0,12,24,36,48,60... ;; lda with the 8 bit value to cast and set as 4-bit value (0-15 only!) ;; no read function is provided; just BITe the target variable ;; at [ExtendedPlayfieldSCR,y] to read it (y=0,12,24...228) ;; and branch off of the 2 most significant bits since it's left aligned, ;; or LDA it and LSR LSR LSR LSR to use it as a 4-bit value (0-15). ;;--------------------------------------------------------------- ; ; in accumulator already lda a ; push Nyble to the right (that's where the free 4-bit Virtual World var is) ; asl ; asl ; asl ; asl ; sta b ; lda ExtendedPlayfieldSCR,y ; load and clear the target 4-bit var ; asl ; asl ; asl ; asl ; lsr ; lsr ; lsr ; lsr ; ora b ;set and store it ; ldx #%00001011;write protect off, in bank 3,1 contiguous ; cmp RAM,x ; nop ; cmp WRITEPROTECT_DEST_ADRS ; tax ; cmp RAM,x ; nop ; cmp ExtendedPlayfieldSCR,y ; ;-- turn write protect back on ; ldy #%00001001 ; in bank3 and 1, write protect on ; cmp RAM,y ; nop ; cmp WRITEPROTECT_DEST_ADRS ; rts ;done ;;-------------------------------------------------- ;;-------------------------------------------------------- ;;-------------------------------------------------------- ;writesuperchargervars ;;---- args: registers a,x (value to write, target variable number (0-99)) ; ;;---- use 100 bytes of ROM for 100 variables in bank II ;;---- ; ;-- turn off write protect switch to bank2; yet we remain in bank 3! ; ldy #%00011011;write protect off, bank2 ; cmp RAM,y ; nop ; cmp WRITEPROTECT_DEST_ADRS ; ; ;-- write value to RAM "ROM" ; tay ; cmp RAM,y ; prepare the value for the write ; nop ; cmp superchargervars,x ; update Virtual World Pixel ; ; ;-- turn write protect on switch back to bank1 (we're still in bank 3) ; ; ;-- ; ldy #%00001001 ; cmp RAM,y ; nop ; cmp WRITEPROTECT_DEST_ADRS ; rts ;readsuperchargervars ;---- args: accumulator (target variable number (0-255)) ;---- returns variable in accumulator ;---- use up to 256 bytes of ROM as RAM for additional variables in bank II ;---- ; ;-- keep write protect on, switch to bank2; yet we remain in bank 3! ; ldy #%00011001;write protect off, bank2 ; cmp RAM,y ; nop ; cmp WRITEPROTECT_DEST_ADRS ; ; ;-- write value to RAM "ROM" ; tay ; lda superchargervars,y ; update Virtual World Pixel ; ; ;-- turn write protect on switch back to bank1 (we're still in bank 3) ; ;-- ; ldy #%00001001 ; cmp RAM,y ; nop ; cmp WRITEPROTECT_DEST_ADRS ; rts ;------------------------------------------------------------------ ;----imported from bank2 for speed opt and space ;---- BankIII ASDK Framework Routines: loadplayer;BANK2 ;------------------------------------ ; arguments: registers a,y ; a (0,1) player0, player1 ; y (set argument to 0/8/16/24/etc to load image 1/2/3/4) ; (target image is loaded upside down into high RAM) ;------------------------------------ ldx #7 sta c sty d PushSprite0toCBSRAM lda SPRITEDATA,y ; get 1st byte of selected sprite from ROM image library ;-- turn off write protect ldy #%00001011;write protect off, in bank 3,1 contiguous ; ldy #%00011011; write protect off, bank 3,2! (routine now in bank2) cmp RAM,y nop cmp WRITEPROTECT_DEST_ADRS tay lda c bne updatesprite1 ;20140121 had it backwards cmp RAM,y nop cmp Sprite0SCR,x ; put it in high RAM upside down jmp doneupdatesprite updatesprite1 cmp RAM,y nop cmp Sprite1SCR,x doneupdatesprite ;-- turn write protect back on ldy #%00001001 ; in bank3 and 1, write protect on ; ldy #%00011001 ; bank 3,2 write protect on cmp RAM,y nop cmp WRITEPROTECT_DEST_ADRS inc d ldy d dex bpl PushSprite0toCBSRAM rts ;--- loadplayerupsidedown;BANK2 ;------------------------------------ ; arguments: registers a,y ; a (0,1) player0, player1 ; y (set argument to 0/8/16/24/etc to load image 1/2/3/4) ; (target image is loaded right side up in high RAM) ;------------------------------------ ldx #0 sta c sty d PushSprite0toCBSRAM1 lda SPRITEDATA,y ; get 1st byte of selected sprite from ROM image library ;-- turn off write protect ldy #%00001011; write protect off, bank 3,1 contiguous ; ldy #%00011011; write protect off, bank 3,2 contigous cmp RAM,y nop cmp WRITEPROTECT_DEST_ADRS tay lda c bne updatesprite1a cmp RAM,y nop cmp Sprite0SCR,x ; put it in high RAM rightsideup jmp doneupdatesprite1 updatesprite1a cmp RAM,y nop cmp Sprite1SCR,x doneupdatesprite1 ;-- turn write protect back on ldy #%00001001 ; in bank 3 and 1 contiguous, write protect on ;ldy #%00011001 ; in bank 3,2 contiguous write protect on cmp RAM,y nop cmp WRITEPROTECT_DEST_ADRS inc d ldy d inx cpx #8 bcc PushSprite0toCBSRAM1 rts ;--- ;---- ;------------------------------------------------------------------ ;-- Bank Switching Switchboard: ;------------------------------------------------------------------ ;---- switchboard opt; one copy only now, here in Bank3 ;------------------------------------------------------------------ getbitstatus ldx #%00011001 ; keep write protect on, go to 3/2 ;started at %00001001 ; bank 3 and 1 ;tax cmp RAM,x ; prepare the write nop cmp WRITEPROTECT_DEST_ADRS ; write to the control byte (bank3+2!) jsr getbitstatusBANK2 ;lda b; debug - this should still break on the SuperCharger ;-- keep write protect on switch back to bank1 (we're still in bank 3) ;-- ldy #%00001001 cmp RAM,y nop cmp WRITEPROTECT_DEST_ADRS lda b ; restore lost accumulator (real supercharger fix!) rts pushabstractextendedplayfield ldx #%00011001 ; keep write protect on, go to 3/2 ;started at %00001001 ; bank 3 and 1 ;tax cmp RAM,x ; prepare the write nop cmp WRITEPROTECT_DEST_ADRS ; write to the control byte (bank3+2!) jsr pushabstractextendedplayfieldBANK2 ;-- keep write protect on switch back to bank1 (we're still in bank 3) ;-- ldy #%00001001 cmp RAM,y nop cmp WRITEPROTECT_DEST_ADRS rts MusicEngine ldx #%00011001 ; keep write protect on, go to 3/2 ;started at %00001001 ; bank 3 and 1 ;tax cmp RAM,x ; prepare the write nop cmp WRITEPROTECT_DEST_ADRS ; write to the control byte (bank3+2!) jsr PlayMusic ;-- keep write protect on switch back to bank1 (we're still in bank 3) ;-- ldy #%00001001 cmp RAM,y nop cmp WRITEPROTECT_DEST_ADRS rts findspritexyfromvirtualworldpixel ldx #%00011001 ; keep write protect on, go to 3/2 ;started at %00001001 ; bank 3 and 1 ;tax cmp RAM,x ; prepare the write nop cmp WRITEPROTECT_DEST_ADRS ; write to the control byte (bank3+2!) jmp findspritexyfromvirtualworldpixelBANK2 findspritexyfromvirtualworldpixelRETURN ;-- keep write protect on switch back to bank1 (we're still in bank 3) ;-- ldy #%00001001 cmp RAM,y nop cmp WRITEPROTECT_DEST_ADRS rts ;------------------------------------------------------------------ ;--end switchboard ;------------------------------------------------------------------ ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;----------------------------------------------------------- ;----------------------------------------------------------- GameLoop2 ;----------------------------------------------------------- ;--- game loop for the bottom blank ;----------------------------------------------------------- replacegameloop2code doneGameLoop2 ; jmp GameLoop2RETURN;+stack rts ; --------------------- End GameLoop2 fastyindex .byte #0 .byte #12 .byte #24 .byte #36 .byte #48 .byte #60 .byte #72 .byte #84 .byte #96 .byte #108 .byte #120 .byte #132 .byte #144 .byte #156 .byte #168 .byte #180 .byte #192 .byte #204 .byte #216 .byte #228 ; = bityindex .byte 0,12,24,36,48,60,72,84,96,108,120,132,144,156,168,180,192,204,216,228 ;--------------------------------------------------------------------- playfieldlookup ; used for fast * to replace fast calc: asl asl + + (for multiply x6) with faster lookup :) .byte 0,6,12,18,24,30,36,42,48,54 MusicData ;MusicData1 ;MusicData2 REPLACEMUSICDATA ;------------------------------------------------------------------- fastmultiply;15 byte routine courtesy of David Holz, codebase64 wiki lda #$00 beq enterLoop doAdd: clc adc a;num1 loop: asl a;num1 enterLoop: ;For an accumulating multiply (.A = .A + num1*num2), set up num1 and num2, then enter here lsr b;num2 bcs doAdd bne loop rts ;result is in accumulator ;------------------------------------------------------------------- ; 8bit/8bit division ; David Holz) ; ; Input: num, denom in zeropage ; Output: num = quotient, .A = remainder fastdivide lda #$00 ldx #$07 clc divroll;: rol a;num rol cmp b;denom bcc divdex;:+ sbc b;denom divdex;: dex bpl divroll;:-- rol a;num - quotient in a, % in accumulator rts ; 19 bytes ; ; Best case = 154 cycles ; Worst case = 170 cycles ; ; With immediate denom: ; Best case = 146 cycles ; Worst case = 162 cycles ; ; Unrolled with variable denom: ; Best case = 106 cycles ; Worst case = 127 cycles ; ; Unrolled with immediate denom: ; Best case = 98 cycles ; Worst case = 111 cycles ;--------------------------------------end fast divide ;-------------------------------------------------------- ;--WYSIWYG inline Sprite Library (vertical flip supported!) ;---16 images (128 bytes) can be expanded up to 32 ;---(can be relocated to bank 3 with no changes to free up space here) ;-------------------------------------------------------- ; jsr loadplayer0, loadplayer1, y reg holds the index argument ; (set y to 0/8/16/24/etc to load image 1/2/3/4/etc) ; (target image is loaded upside down into high RAM) ; vertical flip functions added: jsr loadplayer0upsidedown, loadplayer1upsidedown, y reg holds the index argument ; (hardware already provides support for horizontal flip) ;------------------------------------- sprites REPLACESPRITEDATA ;-- End inline sprite library table ;------------------------------------ ;----------------------------------------------- ;-----------------------------------End Bank III ;----------------------------------------------- ; Supercharger Header (8448 Bin Format) SEG HEADER ORG $3000,0 ; rorg $1800 ; Start Address DC.W Start ; Bank Select Configuration ; Bits 7-5 Write Pulse Delay ; Bits 4-2 Bank Config ; 000 3 ROM ; 001 1 ROM ; 010 3 1 ; 011 1 3 ; 100 3 ROM ; 101 2 ROM ; 110 3 2 ; 111 2 3 ; Bit 1 Write Enable (1=enable) ; Bit 0 ROM power (1=on) DC.B %00001001 ; %00001010 ; Bank 3+1 ; Page Count, Checksum, and Multi-Load Index DC.B $18, $00, 0 ; Progress Indicator (6K) DC.W $0224 ; Bank Offsets ORG ($3010),0 DC.B $00,$04,$08,$0C,$10,$14,$18,$1C ; Bank 1 DC.B $01,$05,$09,$0D,$11,$15,$19,$1D ; Bank 2 DC.B $02,$06,$0A,$0E,$12,$16,$1A,$1E ; Bank 3 ; Bank Checksums ORG ($3030),0 DC.B $00,$00,$00,$00,$00,$00,$00,$00 ; Bank 1 DC.B $00,$00,$00,$00,$00,$00,$00,$00 ; Bank 2 DC.B $00,$00,$00,$00,$00,$00,$00,$00 ; Bank 3 ; Padding To 8448 Bytes DS.B 184,0