Jump to content
IGNORED

343 Scanlines Why?


Primordial Ooze

Recommended Posts

That seems like a lot of work. This project is starting to get the best of me and i may end up abandoning it. Isn't there another way besides counting cycles?

 

Its what you have to do in order to make a viable game on the 2600. There are good online resources to help you and the 6502/6507 datasheet is online too.

Link to comment
Share on other sites

That seems like a lot of work. This project is starting to get the best of me and i may end up abandoning it. Isn't there another way besides counting cycles?

 

Its what you have to do in order to make a viable game on the 2600. There are good online resources to help you and the 6502/6507 datasheet is online too.

Well i've tried to move the code around to no avail. I am at a point where the project has come to a complete stop. I was hoping to have something to show for the Christmas season, but it looks very bleak.

 

Sincerely,

 

Primordial Ooze

Link to comment
Share on other sites

Maybe you should try coding the game in batari Basic? Then you can convert parts to assembler as you get more experienced.

I'll never work! First off this is quite an advanced game requiring custom lightgun code. Second on a 4k Batari rom your only left with 2k of rom space. Third Batari is only useful for learning to code simple games(NO OFFENCE!). Finally i'm quite experienced with assemble language as i have created c64 and spectrum 48k games in assembly language.

 

Sincerely,

 

Primordial Ooze

Link to comment
Share on other sites

Maybe you should try coding the game in batari Basic? Then you can convert parts to assembler as you get more experienced.

I'll never work! First off this is quite an advanced game requiring custom lightgun code. Second on a 4k Batari rom your only left with 2k of rom space. Third Batari is only useful for learning to code simple games(NO OFFENCE!). Finally i'm quite experienced with assemble language as i have created c64 and spectrum 48k games in assembly language.

 

Sincerely,

 

Primordial Ooze

 

 

 

If it was easy, then everyone would do it. It's precisely because of the difficulties involved that it's attractive to those who do.

The reward for getting this machine to do amazing things is satisfaction. It's very much like a difficult crossword or puzzle. If you put in the effort to learn, then you enjoy the success.

So I'd encourage you to keep going. You're most of the way there already!

Cheers

A

  • Like 2
Link to comment
Share on other sites

That seems like a lot of work. This project is starting to get the best of me and i may end up abandoning it. Isn't there another way besides counting cycles?

 

Nope, but while it seems like a lot of work you will quickly learn the cycles. This snippet from Stay Frosty 2 is how I count them. The first # is how many cycles, the 2nd is how far along on the current scanline

IceLoop:
sta WSYNC       	; 3  0 - new line, cycle count starts over
lda #<DF0DATAW		; 2  2 - Frosty Graphics	
sta GRP0		; 3  5 - update the sprite graphics
stx COLUP0		; 3  8- update the sprite color
lda #<DF1FLAG		; 2 10  - nose
sta ENAM1		; 3 13 - nose
lda #<DF6FRACDATA	; 2 15 - load playfield color
sta COLUPF		; 3 18 - set playfield color
lda #<DF2FRACDATA	; 2 20 
sta PF2			; 3 23
lda #<DF2DATAW		; 2 25 - load Object Graphic 
sta GRP1		; 3 28 - preload GRP1 for next line- it's on VDEL
ldx DF1DATA		; 4 32 - load Frosty Color data for next line
lda #<DF3FRACDATA	; 2 34
sta PF0			; 3 37 - right PF0
lda #<DF4FRACDATA	; 2 39
sta PF1			; 3 42 - right PF1
lda #<DF3DATAW		; 2 44 - snowball
sta ENAM0		; 3 47
lda #<DF5FRACDATA	; 2 49
sta PF2			; 3 52 - right PF2
lda #<DF0FRACDATA	; 2 54
sta PF0			; 3 57 - left PF0
lda #<AMPLITUDE		; 2 59 - load music for next line
sta AUDV0		; 3 62
lda #<DF1FRACDATA	; 2 64
sta PF1			; 3 67 - left PF1
dey			; 2 69
bne IceLoop		; 3 72

 

You don't have to count cycles everywhere, but you do need to count them in the Kernel.

  • Like 2
Link to comment
Share on other sites

Counting cycles isn't as tough as it seems once you start doing it on a regular basis, and the benefits far outweigh the hassle. Also, you don't necessarily need to count cycles for all of your code, just the parts where the timing is more critical, such as the part where the scan lines are being drawn. However, I like to put cycle counts in all of my code-- even though I can't always relate the cycle counts to actual scan line positions, since the code might start at different points in a scan line depending on when the program jumps or branches to that section of code.

 

I know you're feeling overwhelmed with how complicated your project is becoming, as well as disappointed that you probably won't have a finished (or even mostly-finished) game by Christmas, but don't give up! And I'm not saying that because I have any desire to play your game, but rather because I think this particular hobby is something people should undertake for the pleasure (and yes, the pain) that *they* get out of it. Pretend that no one else in the whole world will ever play your game. If you aren't doing it for *yourself* (and to heck with what everybody else thinks of your game), then you're setting yourself up for eventual disappointment. Sure, having other people play and enjoy your game can give you a wonderful feeling of validation, but in this hobby I think it really should be all about *your* pleasure and sense of accomplishment.

 

If you aren't getting enough pleasure from this particular project, then giving up on it is your choice, and no one should begrudge or belittle you for making that choice. But what's really pleasurable (and I'm speaking for myself here) is when you finally manage to get a kernel working the way you want, despite all the difficulties you had getting things to work out just right. That is, the more difficult something is, the greater your sense of accomplishment will be when/if you succeed.

 

And if you *don't* succeed-- no sweat, don't feel bad, because you can still learn from it and apply it to your next attempt.

 

Also, a large part of 2600 game design is figuring out what you need to chop out and do without so you'll be able to get your kernel working-- it's frequently a process of making concessions and doing without one thing so you can have another thing that's more essential to your game.

 

And if all else fails, you might consider splitting your screen display into two different frames and then combining them with flicker. Flicker isn't as pretty as no flicker, but it doesn't look too bad if it's handled well-- things like the colors and intensities (luminances) used can help to decrease the perceived degree of flicker.

  • Like 1
Link to comment
Share on other sites

 

Posted Fri Dec 16, 2011 8:26 PM

Counting cycles isn't as tough as it seems once you start doing it on a regular basis, and the benefits far outweigh the hassle. Also, you don't necessarily need to count cycles for all of your code, just the parts where the timing is more critical, such as the part where the scan lines are being drawn. However, I like to put cycle counts in all of my code-- even though I can't always relate the cycle counts to actual scan line positions, since the code might start at different points in a scan line depending on when the program jumps or branches to that section of code.

 

I know you're feeling overwhelmed with how complicated your project is becoming, as well as disappointed that you probably won't have a finished (or even mostly-finished) game by Christmas, but don't give up! And I'm not saying that because I have any desire to play your game, but rather because I think this particular hobby is something people should undertake for the pl...

Thanks for the encouraging words. When all looked bleak you picked me up and helped me on my way. I'm not gonna give up, not yet!

 

Stella's debugger can help you with your cycle times. Look at the disassembled code in the lower right quadrant, after each instruction is a semicolon followed by a number. That number is the cycles the instruction takes

 

post-3056-0-08844700-1324234377_thumb.png

Thanks i have done a paramilitary cycle count and would like to know what the next step would be to fix the graphical errors. I have attached the latest version of the source code for reference.

Link to comment
Share on other sites

The cycle counts for some of the instructions aren't correct. For example "STA ZP" is 3 cycles and not 2 and "BIT NNNN" is 4 cycles and not 2.

What do you mean sta zp? I don't remember seeing

   lda #1
   sta zp

Anywhere in my code.

 

"BIT NNNN" is 4 cycles and not 2

Ok fixing.

Sincerely,

 

Primordial Ooze

Link to comment
Share on other sites

All my code fits in a single page so basically your telling me to change all the sta's cycles to 4's?

 

No! I'm telling you that ZP and ABS are two different addressing modes and you must put the correct number of cycles down for each.

So which one is ZP and which one is ABS? Sorry I'm still a little new to this.

 

Sincerely,

 

Primordial Ooze

Link to comment
Share on other sites

So which one is ZP and which one is ABS? Sorry I'm still a little new to this.

 

:? Doesn't seem to add up when you stated this previously :-

 

Finally i'm quite experienced with assemble language as i have created c64 and spectrum 48k games in assembly language.

 

I assumed that you would know the difference between accessing zero page (ZP) memory (which is the first 256 bytes) and ABS which is absolute memory e.g. the rest of it.

Link to comment
Share on other sites

 

Finally i'm quite experienced with assemble language as i have created c64 and spectrum 48k games in assembly language.

 

I assumed that you would know the difference between accessing zero page (ZP) memory (which is the first 256 bytes) and ABS which is absolute memory e.g. the rest of it.

Sorry about that. Believe it or not I've never had any problems with running out of cycles so I've never really need to know the difference in cycles and thus never learned the difference between ZP and ABS addressing.

 

http://www.atarimax....i.org/aopc.html shows 6502 addressing modes.

Thanks for that link. It's still a bit confusing but here gos:

Zero page is when you address the first 256 bytes of memory($00-$FF) in other words Atari 2600 ram.

 

Absolute is when you address a memory address directly that is greater then 256. For example

(280)Swcha (281)Swacnt (282 Swchb (283)Swbcnt (284)Intim etc.

Cite: Link

I have attached the source code with the corrected timing cycles. Please let me know if i am correct.

 

Sincerely,

 

Primordial Ooze

Edited by Primordial Ooze
Link to comment
Share on other sites

Please let me know if i am correct.

 

Nope! I suggest that you look at each of your instructions in the kernel and then look up the CPU cycle duration accounting for the addressing mode used, page boundaries crossed (if any) and branches taken/not taken.

 

If you want to write 2600 games in assembler then the ability to work out the cycle time for every instruction used in your display kernel is a mandatory skill (and a good one to have). Due to the fact that every single display kernel CPU cycle is precious, 2600 game programmers spend quite a bit of time thinking about how best to get the work done in the cycles available.

Link to comment
Share on other sites

I only took a quick glance at the kernel, but here are some comments I would have:

  • You count lda pf0data,y as 4 cycles, but all other absolute x/y loads with 5. If you add a bunch of align 256 to your data section and make sure all your tables do not cross a page boundary, all absolute loads should only take 4 cycles.
  • From the cycle count you can already see that the store to COLUP0 is almost at the end of the line. This means that if the duck is in the left part of the screen, then GRP0 is displayed while COLUP0 has not been updated and the color from the line before is still active.
  • The most simple (not necessarily the most efficient) solution would be to write to a color buffer and update COLUP0 directly after GRP0.
  • Since you are currently directly writing to COLUP0, but update GRP0 two single lines later (because of the buffer), color and graphics are not in sync.
  • Since the crosshair does not change its color and needs less cycles than the duck, it might be better to swap the duck and crosshair code, i.e. handle the crosshair in the first line where the playfield is updated, and the duck then in the second line.

Link to comment
Share on other sites

Sorry about that. Believe it or not I've never had any problems with running out of cycles so I've never really need to know the difference in cycles and thus never learned the difference between ZP and ABS addressing.

The difference is due to the size of the address, or the argument of the operand. Every 16-bit address requires 2 bytes to express, which are written in lo-byte/hi-byte order in 6502 assembly. An instruction that uses "absolute addressing" requires 4 machine cycles to execute, with 2 of those cycles relegated to loading first the lo byte, then the hi byte, of the address. But a zero-page address always has a hi byte of $00, making it possible to abbreviate the address to just 1 byte-- the lo byte-- since the hi byte is always $00 by definition. And since there's 1 less byte to have to load for the address, the instruction can execute in 1 less machine cycle-- 3 instead of 4. This makes page zero addresses rather "precious," since instructions that use "zero-page addressing" are a tad quicker than normal (where "normal" is understood to mean "absolute addressing"). True, it's a savings of only 1 cycle, but it quickly adds up to a substantial savings when you're able to use a lot of zero-page instructions. That's why the TIA registers and the RIOT RAM are mapped to page zero addresses, since the TIA registers and RAM addresses are the ones that get used the most.

 

I'll take a look at your code, correct the cycle counts as needed, add the equivalent scan line positions and screen positions, then post the results later.

Link to comment
Share on other sites

In my opinion the best way to check the timer is to check whether or not the timer interrupt flag has been set:

 

wait_loop
	lda TIMINT ; the timer interrupt flag is bit 7 of this address
	bpl wait_loop ; if bit 7 is still 0, the timer hasn't finished counting down

Crap, I don't know why I said "LDA" here, as I always use "BIT" to test TIMINT, since "BIT" doesn't affect the accumulator:

 

wait_loop
  bit TIMINT
  bpl wait_loop

Link to comment
Share on other sites

In my opinion the best way to check the timer is to check whether or not the timer interrupt flag has been set:

 

There is another use of the timer. And that is to see *in advance* if you have enough processing time to do something.

So in that case, it's very useful to actually compare the remaining time, rather than look at the "interrupt" flag.

Case in point, my recent game does this sort of thing...

 

		    lda INTIM					   ; 4
		    cmp #REQUIRED_TIME 		   ; 2
		    bcc notEnoughTime 		  ; 2/3= 8
		   ; do something here that takes as much time as you like, as long as it's < REQUIRED_TIME "ticks"
notEnoughTime

 

Given that there are several places in a frame where you can put this sort of check in, and process code, it's a useful technique for "timeslicing" complex tasks into small execution units.

I guess this is pretty advanced and esoteric stuff, but just thought I'd mention it.

 

Cheers

A

Link to comment
Share on other sites

I only took a quick glance at the kernel, but here are some comments I would have:

  • You count lda pf0data,y as 4 cycles, but all other absolute x/y loads with 5. If you add a bunch of align 256 to your data section and make sure all your tables do not cross a page boundary, all absolute loads should only take 4 cycles.
  • From the cycle count you can already see that the store to COLUP0 is almost at the end of the line. This means that if the duck is in the left part of the screen, then GRP0 is displayed while COLUP0 has not been updated and the color from the line before is still active.
  • The most simple (not necessarily the most efficient) solution would be to write to a color buffer and update COLUP0 directly after GRP0.
  • Since you are currently directly writing to COLUP0, but update GRP0 two single lines later (because of the buffer), color and graphics are not in sync.
  • Since the crosshair does not change its color and needs less cycles than the duck, it might be better to swap the duck and crosshair code, i.e. handle the crosshair in the first line where the playfield is updated, and the duck then in the second line.

Thank you very much!!! Finally after a whole week trying to get it to work i finally managed to do it. I followed your suggestions now as well as cycle counting everything and now it works. With that in mind I would like to release the updated binary and source code as a Christmas present.

 

Link

 

MERRY CHRISTMAS!

 

Primordial Ooze

Edited by Primordial Ooze
Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...