It is currently Thu Nov 15, 2018 1:29 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Sep 04, 2018 2:39 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2331
Location: DIGDUG
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


Top
 Profile  
 
PostPosted: Tue Sep 04, 2018 7:03 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2041
Location: Fukuoka, Japan
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.


Top
 Profile  
 
PostPosted: Tue Sep 04, 2018 7:39 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2331
Location: DIGDUG
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


Top
 Profile  
 
PostPosted: Tue Sep 04, 2018 10:16 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2041
Location: Fukuoka, Japan
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.


Top
 Profile  
 
PostPosted: Wed Sep 05, 2018 1:20 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6950
Location: Canada
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


Top
 Profile  
 
PostPosted: Wed Sep 05, 2018 1:27 am 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2041
Location: Fukuoka, Japan
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!


Top
 Profile  
 
PostPosted: Wed Sep 05, 2018 11:20 am 
Offline
User avatar

Joined: Wed Sep 05, 2018 11:13 am
Posts: 143
Location: Colorado
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


Top
 Profile  
 
PostPosted: Wed Sep 05, 2018 12:15 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2331
Location: DIGDUG
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


Top
 Profile  
 
PostPosted: Wed Sep 05, 2018 2:13 pm 
Offline

Joined: Mon May 27, 2013 9:40 am
Posts: 496
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:
;;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 106 times

_________________
http://www.mojontwins.com
Top
 Profile  
 
PostPosted: Wed Sep 05, 2018 2:40 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2331
Location: DIGDUG
look at my function _xy_split in nesdoug.s

It's similar.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Wed Sep 05, 2018 6:57 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2041
Location: Fukuoka, Japan
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:
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.


Top
 Profile  
 
PostPosted: Wed Sep 05, 2018 9:55 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2331
Location: DIGDUG
Quote:
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


Top
 Profile  
 
PostPosted: Wed Sep 05, 2018 11:44 pm 
Offline

Joined: Mon May 27, 2013 9:40 am
Posts: 496
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:
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' :)

_________________
http://www.mojontwins.com


Top
 Profile  
 
PostPosted: Thu Sep 06, 2018 12:35 am 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2041
Location: Fukuoka, Japan
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:
.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:
   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:
; 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


Top
 Profile  
 
PostPosted: Tue Sep 11, 2018 11:30 am 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2331
Location: DIGDUG
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


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group