rawbits Posted June 5, 2012 Share Posted June 5, 2012 (edited) Hi! I have written some cool things on the VCS in the past 3 months but now I have started a big - and quick - project. I want to do a demo and I tought I can made it load the different parts from a table of addresses. Basically I want to load the picture drawing code sequentially using an address table. But how can I know the addresses of the start of the parts? I know labels are for that, but can I store them somehow in a table? And how? And how can I read the addresses back from table to JSR them? Can I use bankswitching with this? Please help me with this! The VBLANK and - mostly - the Overscan part will be the same - and used up pretty much all avaible cycles - so I don't want to waste space by copying them in all parts... I have 1 month to finish the whole thing. Effects are more or less done. I have ordered the Harmony cart - but didn't get confirmation on the posting so I'm afraid it will not arrives in time or at all. Edited June 5, 2012 by rawbits Quote Link to comment Share on other sites More sharing options...
tokumaru Posted June 5, 2012 Share Posted June 5, 2012 You can easily make a table of addresses like you can with anything else: PointersToParts: .dw DemoPart0 .dw DemoPart1 .dw DemoPart3 And then you can make a subroutine that loads an address from the table and jumps to it, based on an index you give it: CallPart: asl ;multiply the index by 2 because each address is 2 bytes tax ;put it into an index register lda PointersToParts+0, x ;copy the address to RAM sta Pointer+0 lda PointersToParts+1, x sta Pointer+1 jmp (Pointer) ;jump to the code Then you can use the subroutine like this: lda #$01 ;load the index of the part jsr CallPart ;call it Then the RTS at the end of each part will return to the instruction after this call. Bankswitching makes things a bit more complicated. You'd have to somehow list the banks where each part resides, either in a second table or using the highest bits of the addresses in the existing table, and switch to the correct bank before jumping to the address. If the different parts can be called from different banks, you'll also have to remember the index of the bank that made the call so that you can return to it later. If the calls are made from a single bank, just switch back to that bank before returning. 1 Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted June 5, 2012 Share Posted June 5, 2012 (edited) Hi Rawbits, There a couple of common used address modes for loading data that might be useful for you. I'm going to skip one of them "load Absolute,index" and give you an example of "(Indirect),Y" instead: ;usage example LDA (addressPF1Gfx),Y STA PF1 Okay, so you have lots of different graphics you want to use. Each has a label at the start of them like this: PF1Graphics_A: .byte $FF ; |XXXXXXXX| .byte $FF ; |XXXXXXXX| .byte $CF ; |XX XXXX| .byte $BF ; |X XXXXXX| .byte $AF ; |X X XXXX| .byte $AF ; |X X XXXX| .byte $A8 ; |X X X | .byte $AF ; |X X XXXX| .byte $AF ; |X X XXXX| You need to simply build a table of address, like so: MyPF1GfxTab: .word PF1Graphics_A .word PF1Graphics_B .word PF1Graphics_C .word PF1Graphics_D .word PF1Graphics_E ;... and all the rest More often you will see people using ".byte <PF1Graphics_A", that defines just the low part of the address, and likewise ".byte >PF1Graphics_A" could be used to define the high part. But in this example we are using word, and loading the whole thing. So, here comes the loading part: lda graphicsIndex ; used to index the address table... asl ; times by 2, because each address is 2 bytes long tay ; you can use either Y or X to index the address table lda PF1GfxTab,Y sta addressPF1Gfx ; low address lda MyPF1GfxTab+1,Y ; "+1" to get next byte sta addressPF1Gfx+1 ; high address So that's it. You need to define some zero page ram, like in the example above "graphicsIndex" takes 1 byte or ram, and each indirect pointer requires 2. You said you've programmed before, so no need to explain this. These routines are hardly anywhere near as efficient as they can be. You have to custom tailor your own code. For example If you had to chose between display two 48 displays you might do something like this for speed: LOGO_HEIGHT = 8 lda #<TitleScreen bit flags bvc .loadGfxPtrs ; using bit 6, if it's "1" do ending screen logo, otherwise do title screen logo lda #<EndingScreen .loadGfxPtrs: sta gfxPtrs clc adc #LOGO_HEIGHT sta gfxPtrs+2 adc #LOGO_HEIGHT sta gfxPtrs+4 adc #LOGO_HEIGHT sta gfxPtrs+6 adc #LOGO_HEIGHT sta gfxPtrs+8 adc #LOGO_HEIGHT sta gfxPtrs+10 This assumes the data is on the same page, so you don't need to update the high pointers. For byte savings you would take a different approach and put it all in a loop. Edited June 5, 2012 by Omegamatrix Quote Link to comment Share on other sites More sharing options...
tokumaru Posted June 5, 2012 Share Posted June 5, 2012 Wait, Omegamatrix's reply confused me a bit... Rawbits, do you want to use tables for accessing code or data? My reply explains how you can call different parts of code dynamically (i.e. you decide what runs when at run time, as opposed to compile time), while Omegamatrix's deals with using the same code to manipulate different sets of data. Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted June 5, 2012 Share Posted June 5, 2012 I assumed here that he was drawing a picture using the PFx or GRPx registers, and updating the pointers mid-screen. That's how I read it anyhow. Quote Link to comment Share on other sites More sharing options...
rawbits Posted June 5, 2012 Author Share Posted June 5, 2012 Omegamatrix: Thanks but I'm quite familiar with graphics on the VCS. I was interested in dinamically switch code segments to use for drawing. Chopper Commander: Thank you! If you can store addresses using labels in a table like that then this is what I'm looking for. Bank switching is just an option for now because I don't know how big the effects are and I haven't done the gfx and music for it... Quote Link to comment Share on other sites More sharing options...
+SvOlli Posted June 5, 2012 Share Posted June 5, 2012 Here's my humble suggestion using ca65 and make, hacked up in less than half an hour by copy'n'pasting other stuff floating around here. Trying to be simple, yet flexible. The code is for a PAL system, for NTSC, the timer values need to be changed. Just 4K now, but I've got bankswitching templates for the ca65 as well. Greetings, SvOlli demoscheduler.zip Quote Link to comment Share on other sites More sharing options...
Joe Musashi Posted June 5, 2012 Share Posted June 5, 2012 (edited) You can easily make a table of addresses like you can with anything else: PointersToParts: .dw DemoPart0 .dw DemoPart1 .dw DemoPart3 And then you can make a subroutine that loads an address from the table and jumps to it, based on an index you give it: CallPart: asl ;multiply the index by 2 because each address is 2 bytes tax ;put it into an index register lda PointersToParts+0, x ;copy the address to RAM sta Pointer+0 lda PointersToParts+1, x sta Pointer+1 jmp (Pointer) ;jump to the code Then you can use the subroutine like this: lda #$01 ;load the index of the part jsr CallPart ;call it Then the RTS at the end of each part will return to the instruction after this call. Another "classic" method to implement a jump table is to push the target address onto the stack and use RTS instead of an indirect jump. This saves a couple of bytes, but you can only use it if you have room for two bytes on the stack. Note that RTS pulls an address from the stack and jumps to that address+1, so the table needs to be adjusted by subtracting 1 from the actual target. ; jump index in X CallPart: lda PointersToPartsHi, x pha lda PointersToPartsLo, x pha rts ;jump to the code PointersToPartsLo: ; address-1 lo-bytes .db <(DemoPart0-1) .db <(DemoPart1-1) .db <(DemoPart2-1) PointersToPartsHi: ; address-1 hi-bytes .db >(DemoPart0-1) .db >(DemoPart1-1) .db >(DemoPart2-1) Edited June 5, 2012 by Joe Musashi Quote Link to comment Share on other sites More sharing options...
tokumaru Posted June 6, 2012 Share Posted June 6, 2012 Chopper Commander: Thank you! Heh, you're welcome, but "Chopper Commander" is my rank on the forums or something, not my username! =) Another "classic" method to implement a jump table is to push the target address onto the stack and use RTS instead of an indirect jump. Yeah, this is a very common trick on the 6502. I didn't want to confuse rawbits since he wasn't familiar with these things, but your method (address table split into low and high bytes and RTS instead of JMP) is indeed more optimized and what I'd use in an actual project. Quote Link to comment Share on other sites More sharing options...
SeaGtGruff Posted June 6, 2012 Share Posted June 6, 2012 I prefer the method Omegamatrix mentioned where the lo bytes and hi bytes are stored in separate tables, because this gives you 256 possible entries. I don't know if that's the most common method, but I believe it's the one I've seen used the most. And if you're using bankswitching, you can have a third table for the bank number. That's how I loaded the text data for all the pages in the infamous "E.T. Book Cart"-- one table for the lo byte, a second table for the hi byte, and a third table for the bank number. Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted June 6, 2012 Share Posted June 6, 2012 (edited) I seen both Joe's and tokumaru's methods used before, but Joe's is a little better because you can use stack ram that you normally use for JSR anyway. You just have to know where the stack pointer is. Thinking about it now, if time was a factor you might be able to go a little quicker by jumping into a jump table. Takes more bytes, but saves some cycles. Might be economical for smaller tables. lda eventNumber ;3 @3 can hold values 0 to 85 asl ;2 @5 carry gets cleared also! adc eventNumber ;3 @8 muliply by 3 sta indirectAddr ;3 @11 jmp.ind (indirectAddr);5 @16 + 3 = 19 cycles JumpTable: .byte $4C ; JMP absolute opcode .word PartOne .byte $4C .word PartTwo .byte $4C .word PartThree .byte $4C .word PartFour .byte $4C .word PartFive .byte $4C .word PartSix Of course you go alot faster still when your index is always in multiples of three. lda eventNumber ;3 @3 always in multiples of 3 sta indirectAddr ;3 @6 jmp.ind (indirectAddr);5 @11 + 3 = 14 cycles Edit: It is also implied here that the high address in "indirectAddr" has been set well before hand: lda #>JumpTable sta indirectAddr+1 You don't need to ever update it, so that is why it is left out of the routine. Edited June 6, 2012 by Omegamatrix Quote Link to comment Share on other sites More sharing options...
+SvOlli Posted June 6, 2012 Share Posted June 6, 2012 Omegamatrix, one question. Why: JumpTable: .byte $4C ; JMP absolute opcode .word PartOne .byte $4C .word PartTwo .byte $4C .word PartThree .byte $4C .word PartFour .byte $4C .word PartFive .byte $4C .word PartSix And not: JumpTable: JMP PartOne JMP PartTwo JMP PartThree JMP PartFour JMP PartFive JMP PartSix Imo, it makes the code more readable. Quote Link to comment Share on other sites More sharing options...
rawbits Posted June 6, 2012 Author Share Posted June 6, 2012 (edited) Thank you all for the help! Sorry of the missnaming I have played VCS games when I was a child but never could memorize the titles - sometimes I still can't do it... Also I couldn't find the Edit button on my profile and then I realized it's orange. XD Maybe I need more sleep... Edited June 6, 2012 by rawbits Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted June 6, 2012 Share Posted June 6, 2012 You're right of course, SvOlli. I really had pointers stuck in my mind when I coded it last night. The train of thought blinded me, ha ha. I should also mention that the jump table needs to start at a beginning of a page. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.