Update to my tutorial in C

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
dougeff
Posts: 2673
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Update to my tutorial in C

Post by dougeff » Tue Sep 04, 2018 2:39 pm

I haven't changed my website yet, but it should be very soon.

I did manage to finish the example code this past weekend, and posted everything on github.

https://github.com/nesdoug?tab=repositories

Feel free to comment, if you have any issues / see any problems. Thanks.


Here's the support libraries I wrote "nesdoug.s" and "nesdoug.h"

https://github.com/nesdoug/01_Hello/tree/master/LIB
nesdoug.com -- blog/tutorial on programming for the NES

User avatar
Banshaku
Posts: 2334
Joined: Tue Jun 24, 2008 8:38 pm
Location: Fukuoka, Japan
Contact:

Re: Update to my tutorial in C

Post by Banshaku » Tue Sep 04, 2018 7:03 pm

Good to see an update!

I'm looking at the lib code to see if there is any method that could be used in C that I don't have yet ;) One thing I would like to know is that neslib has a history for update. Is the history from Shiru or from your own fix? Since I use some of the methods inside, if there was some important fix I will be more than happy to update the affected method.

I will check the example once I have some time.

User avatar
dougeff
Posts: 2673
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Update to my tutorial in C

Post by dougeff » Tue Sep 04, 2018 7:39 pm

Changes I made to neslib.h...

added the word "const" on many of the prototypes, to quiet some warnings.

split() now takes 1 argument (X) instead of 2.

in neslib.s

added ldx #0 to end of rand8, per calima's suggestion.

added "export _flush_vram_update_nmi" so I could use the flush function without needing to pass a pointer.

split adjusted to take 1 argument.

The version history, I don't know who added the last items, maybe "veg" whoever that is.
nesdoug.com -- blog/tutorial on programming for the NES

User avatar
Banshaku
Posts: 2334
Joined: Tue Jun 24, 2008 8:38 pm
Location: Fukuoka, Japan
Contact:

Re: Update to my tutorial in C

Post by Banshaku » Tue Sep 04, 2018 10:16 pm

I see.

Since I'm using rand8 sparsely, could you kindly explain why the need to set x to #0? For now I do not see the impact of not setting it for an 8 bit value returned in A. Where does the value of X affect rand8? Maybe it's something related with cc65 internal on return value that I'm not very familiar yet.

User avatar
rainwarrior
Posts: 7804
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Update to my tutorial in C

Post by rainwarrior » Wed Sep 05, 2018 1:20 am

Any assembly function that is called as a C function must return a 16 bit value in A/X, even if the function is defined as returning unsigned char. This is just a limitation of the compiler, it assumes that any 8-bit return value is automatically promoted to a 16-bit int, and relies on the assembly function to do this. (Otherwise a leftover value in X has the potential to generate incorrect results.)
http://cc65.github.io/doc/cc65-intern.html#ss1.3

User avatar
Banshaku
Posts: 2334
Joined: Tue Jun 24, 2008 8:38 pm
Location: Fukuoka, Japan
Contact:

Re: Update to my tutorial in C

Post by Banshaku » Wed Sep 05, 2018 1:27 am

Oh, I think I read something about that but since I usually do not return value I didn't put much attention and completely forgot about it. That was when I started to learn the innards of cc65 to know how to use methods.

I will review my code just in case I have some issues. I know that the metatitle attribute code return an 8 bit value and I didn't set the X. Good think I asked about that, I have now found another source of possible bugs!

Thanks!

User avatar
battagline
Posts: 152
Joined: Wed Sep 05, 2018 11:13 am
Location: Colorado
Contact:

Re: Update to my tutorial in C

Post by battagline » Wed Sep 05, 2018 11:20 am

Great tutorial Doug,

I'm new to NES Dev. I had been using NESASM3, but had been running into problems organizing my code with it. You have me really excited about using C and ca65/cc65

Thanks for the tutorials,
Rick
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com

User avatar
dougeff
Posts: 2673
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Update to my tutorial in C

Post by dougeff » Wed Sep 05, 2018 12:15 pm

Well, on that note, I deleted the old tutorial, and am rewriting it (I have rough drafts done).

Give me a week or 2 to get it back in shape.
nesdoug.com -- blog/tutorial on programming for the NES

na_th_an
Posts: 554
Joined: Mon May 27, 2013 9:40 am

Re: Update to my tutorial in C

Post by na_th_an » Wed Sep 05, 2018 2:13 pm

Thanks for your work.

About the split function, I have a rough version which somewhat works with the Y parameter. At least it fits my needs (vertical scrolling with top status bar using vertical mirroring). I'm sure it could be done better (it's a bit shaky), but my kung fu is not strong enough.

Code: Select all

;;void __fastcall__ split(unsigned int x,unsigned int y);

_split:

	; Extract SCROLL_Y1, SCROLL_X1, WRITE1 from parameters.

	sta <TEMP

	txa
	bne @1
	lda <TEMP
	cmp #240
	bcs @1
	sta <SCROLL_Y1
	lda #0
	sta <TEMP
	beq @2	;bra

@1:
	sec
	lda <TEMP
	sbc #240
	sta <SCROLL_Y1
	lda #8					;; Bit 3
	sta <TEMP
@2:

	jsr popax
	sta <SCROLL_X1
	txa
	and #$01
	asl a
	asl a 					;; Bit 2
	ora <TEMP               ;; From Y
	sta <WRITE1				;; Store!

	; Calculate WRITE2 = ((Y & $F8) << 2) | (X >> 3)

	lda <SCROLL_Y1
	and #$F8
	asl a
	asl a
	sta <TEMP 				;; TEMP = (Y & $F8) << 2
	lda <SCROLL_X1
	lsr a
	lsr a
	lsr a 					;; A = (X >> 3)
	ora <TEMP 				;; A = (X >> 3) | ((Y & $F8) << 2)
	sta <WRITE2				;; Store!

	; Wait for sprite 0 hit

@3:
	bit PPU_STATUS
	bvs @3
@4:
	bit PPU_STATUS
	bvc @4

	; Set scroll value
	lda PPU_STATUS
	lda <WRITE1
	sta PPU_ADDR
	lda <SCROLL_Y1
	sta PPU_SCROLL
	lda <SCROLL_X1
	ldx <WRITE2
	sta PPU_SCROLL
	stx PPU_ADDR
	
	rts
Attachments
poc.zip
(63.66 KiB) Downloaded 389 times

User avatar
dougeff
Posts: 2673
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Update to my tutorial in C

Post by dougeff » Wed Sep 05, 2018 2:40 pm

look at my function _xy_split in nesdoug.s

It's similar.
nesdoug.com -- blog/tutorial on programming for the NES

User avatar
Banshaku
Posts: 2334
Joined: Tue Jun 24, 2008 8:38 pm
Location: Fukuoka, Japan
Contact:

Re: Update to my tutorial in C

Post by Banshaku » Wed Sep 05, 2018 6:57 pm

I had issues with return value with X not set to 0 for pad too so this thread helped for that. thanks!

I don't want to sound rude but if it does I apologize in advance. I just want to know the motive behind such approach for labels in the above example. Maybe it should be a subject for another thread but this always scratch an itch everytime I see it.

It always puzzle me when peoples uses either anonymous label or numbered one inside logic. From my point of view, even though the code is simple, it just obfuscate it for the next user that will read it. It may be obvious the day you wrote it but it won't in the years later.

For example, instead of using : and just jmp with :-, usually I will write something based on the context. So if I'm processing metatile, I may write

Code: Select all

whileMetatileLeft:
   xxxx   ; code that process metatile here   
...
   inx
   bne whileMetatileLeft
I could have just wrote @1 or : but you have no idea how much it helped me re-read my code I did a long time ago. I think named label are useful, however verbose they are.

Again, I apologize if it sounded rude.

User avatar
dougeff
Posts: 2673
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Update to my tutorial in C

Post by dougeff » Wed Sep 05, 2018 9:55 pm

I had issues with return value with X not set to 0 for pad too
Hmm, I forgot to fix pad_poll() and others. Will maybe have to add some more ldx #0
nesdoug.com -- blog/tutorial on programming for the NES

na_th_an
Posts: 554
Joined: Mon May 27, 2013 9:40 am

Re: Update to my tutorial in C

Post by na_th_an » Wed Sep 05, 2018 11:44 pm

Banshaku wrote:I had issues with return value with X not set to 0 for pad too so this thread helped for that. thanks!

I don't want to sound rude but if it does I apologize in advance. I just want to know the motive behind such approach for labels in the above example. Maybe it should be a subject for another thread but this always scratch an itch everytime I see it.

It always puzzle me when peoples uses either anonymous label or numbered one inside logic. From my point of view, even though the code is simple, it just obfuscate it for the next user that will read it. It may be obvious the day you wrote it but it won't in the years later.

For example, instead of using : and just jmp with :-, usually I will write something based on the context. So if I'm processing metatile, I may write

Code: Select all

whileMetatileLeft:
   xxxx   ; code that process metatile here   
...
   inx
   bne whileMetatileLeft
I could have just wrote @1 or : but you have no idea how much it helped me re-read my code I did a long time ago. I think named label are useful, however verbose they are.

Again, I apologize if it sounded rude.
No, what you are saying makes complete sense.

I was just completing an already existing function so I guess I adhered to the existing code style. The annonymous labels - I think they are used so you can cut and paste a routine to another project without having to care about identifier collisions. Other than that, I don't find any advantages. I'm not an assembly coder, tho' :)

User avatar
Banshaku
Posts: 2334
Joined: Tue Jun 24, 2008 8:38 pm
Location: Fukuoka, Japan
Contact:

Re: Update to my tutorial in C

Post by Banshaku » Thu Sep 06, 2018 12:35 am

One way to avoid collision in cc65 is to create scopes like .proc for procedures or even define local scopes to wraps things. For example, let say you have a function with some label like this:

Code: Select all

.proc subMyFunction
   ; init code here
processData:
    ; xxx
    bne processData
    rts
.endproc
processData is scoped to subMyFunction and will not clash if you define it again in another procedure. You can even call that part is you require just to "process the data" from another procedure without the beginning code with:

Code: Select all

   jmp subMyFunction::processData
and now you just jumped there. Another case, those zpTemp variable that means nothing when seen but you need to use them anyway? Scope them!

Code: Select all

; void __fastcall __ function2(unsigned char counter)
;
; process data. receive value in A
;
.proc subFunction2
; ------- local scope begin -------
.scope local
      counter    = zpTemp   ; counter points on zpTemp
.endscope
; ------- local scope end -------
       ; save parameter for later use
       sta local::counter
       ....
loop:
      ; do stuff on it
      lda #$23
      cmp local::counter
      bne loop
  
      rts
.endproc
and now you zpTemp has more meaning as the "local counter". Easier to read the code. This is just vary basic stuff. There is more advanced scope that makes you code quite "interesting" but now I try to avoid that :lol:

Sorry to have hijacked the thread m(_ _)m

User avatar
dougeff
Posts: 2673
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Update to my tutorial in C

Post by dougeff » Tue Sep 11, 2018 11:30 am

Ok, the new tutorial is up an 99% finished.

And all the code is on github.

https://github.com/nesdoug
nesdoug.com -- blog/tutorial on programming for the NES

Post Reply