Jump to content



1

What's going on with my code?


17 replies to this topic

#1 jbs30000 ONLINE  

jbs30000

    Moonsweeper

  • 459 posts

Posted Wed Dec 14, 2011 3:13 AM

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.
Normal.png - Shifted.png
Attached File  BlankB.asm   6.69K   20 downloads

#2 GroovyBee OFFLINE  

GroovyBee

    7800 Developer

  • 5,782 posts
  • Busy bee!
  • Location:North, England

Posted Wed Dec 14, 2011 3:53 AM

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.

#3 jbs30000 ONLINE  

jbs30000

    Moonsweeper

  • 459 posts

Posted Wed Dec 14, 2011 5:59 AM

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.

#4 GroovyBee OFFLINE  

GroovyBee

    7800 Developer

  • 5,782 posts
  • Busy bee!
  • Location:North, England

Posted Wed Dec 14, 2011 6:07 AM

Have you gone over this tutorial :-

http://www.atariage....tes-vertically/

#5 jbs30000 ONLINE  

jbs30000

    Moonsweeper

  • 459 posts

Posted Wed Dec 14, 2011 6:19 AM

Yes, but I'll look over it again later. Thanks.

#6 RevEng OFFLINE  

RevEng

    River Patroller

  • 2,011 posts
  • bit shoveler
  • Location:Canada

Posted Wed Dec 14, 2011 6:19 AM

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.

#7 jbs30000 ONLINE  

jbs30000

    Moonsweeper

  • 459 posts

Posted Wed Dec 14, 2011 4:00 PM

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.

#8 RevEng OFFLINE  

RevEng

    River Patroller

  • 2,011 posts
  • bit shoveler
  • Location:Canada

Posted Thu Dec 15, 2011 8:09 AM

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.

#9 jbs30000 ONLINE  

jbs30000

    Moonsweeper

  • 459 posts

Posted Thu Dec 15, 2011 5:45 PM

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 by jbs30000, Thu Dec 15, 2011 5:46 PM.


#10 RevEng OFFLINE  

RevEng

    River Patroller

  • 2,011 posts
  • bit shoveler
  • Location:Canada

Posted Thu Dec 15, 2011 7:02 PM

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. :)

#11 SeaGtGruff ONLINE  

SeaGtGruff

    River Patroller

  • 4,546 posts
  • Location:Georgia, USA

Posted Thu Dec 15, 2011 8:08 PM

View Postjbs30000, on Thu Dec 15, 2011 5:45 PM, said:

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


#12 jbs30000 ONLINE  

jbs30000

    Moonsweeper

  • 459 posts

Posted Thu Dec 15, 2011 10:20 PM

SeaGTGruff, I always have Dasm create a list file. Thank you for the advice of deleting commented out code. I definitely will do that.

View PostRevEng, on Thu Dec 15, 2011 7:02 PM, said:

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.

#13 jbs30000 ONLINE  

jbs30000

    Moonsweeper

  • 459 posts

Posted Fri Dec 16, 2011 2:16 AM

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.
Attached File  BlankC.asm.bin   4K   15 downloadsAttached File  BlankC.asm   7.31K   12 downloads

#14 RevEng OFFLINE  

RevEng

    River Patroller

  • 2,011 posts
  • bit shoveler
  • Location:Canada

Posted Fri Dec 16, 2011 6:05 AM

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.

#15 jbs30000 ONLINE  

jbs30000

    Moonsweeper

  • 459 posts

Posted Sun Dec 18, 2011 7:43 PM

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.

#16 RevEng OFFLINE  

RevEng

    River Patroller

  • 2,011 posts
  • bit shoveler
  • Location:Canada

Posted Sun Dec 18, 2011 9:25 PM

View Postjbs30000, on Sun Dec 18, 2011 7:43 PM, said:

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?

#17 jbs30000 ONLINE  

jbs30000

    Moonsweeper

  • 459 posts

Posted Sun Dec 18, 2011 11:20 PM

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.

#18 RevEng OFFLINE  

RevEng

    River Patroller

  • 2,011 posts
  • bit shoveler
  • Location:Canada

Posted Mon Dec 19, 2011 1:39 AM

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)

#19 jbs30000 ONLINE  

jbs30000

    Moonsweeper

  • 459 posts

Posted Mon Dec 19, 2011 4:00 AM

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.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users