jbs30000 Posted December 14, 2011 Share Posted December 14, 2011 OK, so I'm playing around with sprite positioning. Everything is OK, but at a certain point at the left side of the screen my sprite (player0) will shift up one pixel. So it's like a small vertical strip on the left-hand side is one pixel lower (for sprites) than the rest of the screen. Just curious. Thank you. - BlankB.asm Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 14, 2011 Share Posted December 14, 2011 I can see a couple of places in the code where you are doing "lda 0" which is actually reading TIA register CXM0P instead of loading the accumulator with the immediate value 0. You also need to make sure that the carry flag is set before you do "sbc Player0Y" too. The "adc #SPRITE_HEIGHT" immediately after that instruction will actually be adding SPRITE_HEIGHT+1 when Player0Y is less than your line counter in X. Quote Link to comment Share on other sites More sharing options...
jbs30000 Posted December 14, 2011 Author Share Posted December 14, 2011 I made the changes but all it did was make that one area act slightly different. I played around with some other areas of code that I thought might effect it but that didn't work either. Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 14, 2011 Share Posted December 14, 2011 Have you gone over this tutorial :- http://www.atariage.com/forums/topic/38020-session-23-moving-sprites-vertically/ Quote Link to comment Share on other sites More sharing options...
jbs30000 Posted December 14, 2011 Author Share Posted December 14, 2011 Yes, but I'll look over it again later. Thanks. Quote Link to comment Share on other sites More sharing options...
RevEng Posted December 14, 2011 Share Posted December 14, 2011 The shifting happens because you're updating GRP0 mid-scanline. If the player is to the left of your GRP0 update, the update happens on the scanline in question. If the player is to the right of your GRP0 update, it gets drawn on the next pass of the scanline. (making it appear lower) It's generally referred to as tearing or shearing, and it can happen to COLUP0/1, missiles, and ball updates too. You'll want to update your players near the beginning of the scanline, when the beam is off-screen. If you need to update both GRP0 and GRP1 and time at the beginning of the line is tight, its common to use the VDELP registers to delay the drawing of one of the players until the other is drawn. Quote Link to comment Share on other sites More sharing options...
jbs30000 Posted December 14, 2011 Author Share Posted December 14, 2011 Well, I played around with the timing and found I could use two sprite display routines and two simple routines to display missiles 0 and 1, but not the ball. But if I have to time when to turn on the displays...this will be interesting. Quote Link to comment Share on other sites More sharing options...
RevEng Posted December 15, 2011 Share Posted December 15, 2011 It makes kernel programming a fun part of the job - it winds up being like a jigsaw puzzle, as you try to fit functionality into the parts of the scanline they need to be in... stealing time from one part of the kernel to be more efficient elsewhere. If your missiles only need to be one line tall, you should check out how Combat (or the bB multisprite kernel) handles them. Very compact code. Quote Link to comment Share on other sites More sharing options...
jbs30000 Posted December 15, 2011 Author Share Posted December 15, 2011 (edited) Well, so far, (without looking at Combat or bB kernel*) I know how to put in code to display two sprites (single color) and both missiles, with no code left over to display the ball. I even figured that I have enough cycles left over to write to the VDELP1 register as suggested in post 6. And still being a beginner at 2600 asm programming** reading disassembled programs or even Batari kernels isn't easy. I've decided that I'll have to make notes, and probably even charts, on all of the code before I can decipher what's going on. * I tried looking at the bB standard kernel. It was hard to make out. ** I'm not new to asm, I've studied Intel asm. I'm just new to the 2600 registers and timing issues and so on. Edited December 15, 2011 by jbs30000 Quote Link to comment Share on other sites More sharing options...
RevEng Posted December 16, 2011 Share Posted December 16, 2011 There's a pretty good write-up of the combat missile trick in the ATARI 2600 ADVANCED PROGRAMMING GUIDE at minidig. Also, you can just set VDELP1 before your kernel runs. No need to set it during the actual kernel itself. Quote Link to comment Share on other sites More sharing options...
SeaGtGruff Posted December 16, 2011 Share Posted December 16, 2011 Well, so far, (without looking at Combat or bB kernel*) I know how to put in code to display two sprites (single color) and both missiles, with no code left over to display the ball. I even figured that I have enough cycles left over to write to the VDELP1 register as suggested in post 6. And still being a beginner at 2600 asm programming** reading disassembled programs or even Batari kernels isn't easy. I've decided that I'll have to make notes, and probably even charts, on all of the code before I can decipher what's going on. * I tried looking at the bB standard kernel. It was hard to make out. ** I'm not new to asm, I've studied Intel asm. I'm just new to the 2600 registers and timing issues and so on. With regard to reading batari Basic kernels, one thing I'd suggest is saving a copy of the assembly listing to another file so you can edit it, as you'll probably want to cut out any lines that are commented out. The standard kernel uses a lot of compiler directives to decide whether to use one section of code or another section of code, depending on which kernel options are being used, and the sections of code that are *not* used will be commented out. If you remove those unused sections, the code that's left-- the code that's actually being used-- will be easier to follow. Note, I'm referring to an assembly listing for a game, not the raw assembly from the include file, because the include file by itself won't have any of the sections commented out. For example, make a very simple batari Basic pogram that doesn't use any options, such as the one below, then compile it and look at the assembly listing from it. The default compiler batch doesn't create an assembly listing, but you can modify the dasm command line so a complete assembly listing will be saved to a separate file. rem * a simple batari Basic program loop drawscreen goto loop Quote Link to comment Share on other sites More sharing options...
jbs30000 Posted December 16, 2011 Author Share Posted December 16, 2011 SeaGTGruff, I always have Dasm create a list file. Thank you for the advice of deleting commented out code. I definitely will do that. There's a pretty good write-up of the combat missile trick in the ATARI 2600 ADVANCED PROGRAMMING GUIDE at minidig. Also, you can just set VDELP1 before your kernel runs. No need to set it during the actual kernel itself. For some reason I was thinking I'd have to write to it every scan-line. I thought that maybe it was like a strobe register. And I'll check out that link. Thank you. Quote Link to comment Share on other sites More sharing options...
jbs30000 Posted December 16, 2011 Author Share Posted December 16, 2011 OK, one last question. I try to find answers myself for any problems I run into, but for the last one, and this one, I'm not sure what to search for. The code I have puts both players on the screen. So far so good. Player 0 is the movable face and player 1 is a stick figure. Everything is fine until I move player 0 up or down on the same scanlines as player 1. This messes up both players. BlankC.asm.binBlankC.asm Quote Link to comment Share on other sites More sharing options...
RevEng Posted December 16, 2011 Share Posted December 16, 2011 It's your SBC and ADC instructions in the kernel. To get the correct results for SBC the carry needs to be set first (you can do so explicitly with SEC) and to get correct results with ADC you need to clear the carry first. (you can do so explicitly with CLC) If the carry isn't set for SBC first, or if the carry is set for ADC first, the result is off by one. (assuming it's not the second half of a 16-bit operation, which it isn't here.) Since you haven't set the carry state before each operation, the arithmetic that happens for one player changes the carry state for the next one, depending on it's Y position. Setting the carry explicitly will add more cycles to your kernel. You should look into the skipdraw or switchdraw positioning routines, which are efficient and don't have this issue. Quote Link to comment Share on other sites More sharing options...
jbs30000 Posted December 19, 2011 Author Share Posted December 19, 2011 Actually, what I'm doing should be the skipdraw routine. Actually, it's confusing because in the Andrew Davie tutorials it's listed as skipdraw, but searching these forums and Googling there's also some version which has variables like YPosFromBot, but the only examples I can find use Subpixel positioning, but I'm not interested in that. The pixel drawing routine is fixed at 18 cycles, which is cool, but I don't think it works with lda (sprite ptr),y. Or if it does I don't know how to change the code. Adding sec to both draw routines does fix the problem, but of course adds cycles. In a post about skipdraw there's using an illegal opcode to save two cycles, but it doesn't show how to really use it. ; Thomas Jentzsch Skipdraw ;======================================================================== ;The best way, I knew until now, was (if y contains linecounter): tya ; 2 ; sec ; 2 <- this can sometimes be avoided sbc SpriteEnd ; 3 adc #SPRITEHEIGHT ; 2 bcx .skipDraw ; 2 = 9-11 cycles ; ... ; --------- or ------------ ;If you like using illegal opcodes, you can use dcp (dec,cmp) here: lda #SPRITEHEIGHT ; 2 dcp SpriteEnd ; 5 initial value has to be adjusted bcx .skipDraw ; 2 = 9 ; ... ;Advantages: ;- state of carry flag doesn't matter anymore (may save 2 cycles) ;- a remains constant, could be useful for a 2nd sprite ;- you could use the content of SpriteEnd instead of y for accessing sprite data ;- ??? ;======================================================================== ;An Example: ; ; skipDraw routine for right player TXA ; 2 A-> Current scannline SEC ; 2 Set Carry SBC slowP1YCoordFromBottom+1 ; 3 ADC #SPRITEHEIGHT+1 ; 2 calc if sprite is drawn BCC skipDrawRight ; 2/3 To skip or not to skip? TAY ; 2 lda P1Graphic,y ; 4 continueRight: STA GRP0 ;----- this part outside of kernel skipDrawRight ; 3 from BCC LDA #0 ; 2 BEQ continueRight ; 3 Return... I'm pretty much already doing the example code, +1 cycle because I'm using indirect addressing with Y instead of absolute. But I've tried changing the routine to the shorter illegal opcode version, but have no clue how to make it work. Quote Link to comment Share on other sites More sharing options...
RevEng Posted December 19, 2011 Share Posted December 19, 2011 Actually, what I'm doing should be the skipdraw routine. Oh yeah, you're right. I forgot the plain version used carry based instructions because I always use the illegal DCP version. What's the problem you're running into? Quote Link to comment Share on other sites More sharing options...
jbs30000 Posted December 19, 2011 Author Share Posted December 19, 2011 OK, let's take this: TXA ; 2 A-> Current scannline SEC ; 2 Set Carry SBC slowP1YCoordFromBottom+1 ; 3 ADC #SPRITEHEIGHT+1 ; 2 calc if sprite is drawn BCC skipDrawRight ; 2/3 To skip or not to skip? TAY ; 2 lda P1Graphic,y ; 4 continueRight: STA GRP0 ;----- this part outside of kernel skipDrawRight ; 3 from BCC LDA #0 ; 2 BEQ continueRight ; 3 Return... What do I replace with the lda #SPRITEHEIGHT ; 2 dcp SpriteEnd ; 5 initial value has to be adjusted bcx .skipDraw ; 2 = 9 It may be obvious, but I'm totally missing it. Quote Link to comment Share on other sites More sharing options...
RevEng Posted December 19, 2011 Share Posted December 19, 2011 In 19 cycles vs the 20, but + a temp memory location. ; ------- this part before the kernel lda #0 sta temp1 ; a scratch memory location lda player1y sta SpriteEnd ; SpriteEnd gets obliterated each time the kernel runs ; lda #SPRITEHEIGHT ; 2 dcp SpriteEnd ; 5 initial value has to be adjusted bcc .skipDraw ; 2 = 9 ldy SpriteEnd ; 3 lda P1Graphic,y ; 4 continueRight: STA GRP0 ; 3 ;----- this part outside of kernel skipDrawRight ; 3 from BCC LDA temp1 ; 3 - zero-page instead of direct addressing to burn a cycle BEQ continueRight ; 3 Return... I usually use indirect addressing for the player data (+1 cycle) so I just "nop" during the skipDraw to balance out the +2 cycles in the non-skipDraw branch. (instead of using the zero-page access to balance out the +1 cycle that I used here) Quote Link to comment Share on other sites More sharing options...
jbs30000 Posted December 19, 2011 Author Share Posted December 19, 2011 OK, after studying the code I finally figured out how it works. Thank you. I also add the sprite height to the variable I'm using for "SpriteEnd" so that I can place the sprite at position 1 and still display the whole sprite. 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.