NES Programming Blog
Moderator: Moderators
- NESHomebrew
- Formerly WhatULive4
- Posts: 418
- Joined: Fri Oct 30, 2009 4:43 am
- Contact:
Re: NES Programming Blog
I saw your video. Should be useful for beginners, I did find the loud clicking a bit distracting, I'm not sure if the mic was picking that up, or if it is a part of your recording software.
Re: NES Programming Blog
I made a video about using the debugging tools of FCEUX. I probably forgot about a dozen things to mention, but it was getting pretty long.
https://youtu.be/d2XkJQFs0OQ
https://youtu.be/d2XkJQFs0OQ
nesdoug.com -- blog/tutorial on programming for the NES
Re: NES Programming Blog
I've updated every example code (except for the Spacy Shooty game code, which I plan to rewrite from scratch).
Most of the changes are cosmetic (make comments easier to read), or just trying to make the code more stable.
-added a second v-blank wait in the startup code, before writing to PPU. Which I must have accidentally removed and never put back in.
-moved 'things that need to be done every frame' (like sprite DMA) to NMI code
-added a write to a000 (mirroring) to the init code for my MMC3 examples
-removed the copy of nes.lib from every zip, which apparently was completely unnecessary to keep a copy of, since cc65 is able to find its own copy
At some point, I will also update the Spacy Shooty example code.
Most of the changes are cosmetic (make comments easier to read), or just trying to make the code more stable.
-added a second v-blank wait in the startup code, before writing to PPU. Which I must have accidentally removed and never put back in.
-moved 'things that need to be done every frame' (like sprite DMA) to NMI code
-added a write to a000 (mirroring) to the init code for my MMC3 examples
-removed the copy of nes.lib from every zip, which apparently was completely unnecessary to keep a copy of, since cc65 is able to find its own copy
At some point, I will also update the Spacy Shooty example code.
nesdoug.com -- blog/tutorial on programming for the NES
Re: NES Programming Blog
Great!dougeff wrote:I've updated every example code (except for the Spacy Shooty game code, which I plan to rewrite from scratch).
Most of the changes are cosmetic (make comments easier to read), or just trying to make the code more stable.
-added a second v-blank wait in the startup code, before writing to PPU. Which I must have accidentally removed and never put back in.
-moved 'things that need to be done every frame' (like sprite DMA) to NMI code
-added a write to a000 (mirroring) to the init code for my MMC3 examples
-removed the copy of nes.lib from every zip, which apparently was completely unnecessary to keep a copy of, since cc65 is able to find its own copy
At some point, I will also update the Spacy Shooty example code.
Re: NES Programming Blog
I've decided to finally do some testing with structs vs cc65.
If you declare the actual struct in the global space, it puts them in the BSS, and actually takes about as much time to access as any other variable...
compiles to ...
If, however, you put the struct in the local space, it puts them in the C stack.
compiles to...
Conclusions, just like variables are faster in cc65 if decared globally, structs seem to also be faster if declared globally. And, much better than I thought.
If you declare the actual struct in the global space, it puts them in the BSS, and actually takes about as much time to access as any other variable...
Code: Select all
struct foo {
unsigned char X;
int Y;
int Z;
};
struct foo B;
void main (void){
B.X = 4;
B.Y = 5;
}
compiles to ...
Code: Select all
lda #$04
sta _B
ldx #$00
lda #$05
sta _B+1
stx _B+1+1
If, however, you put the struct in the local space, it puts them in the C stack.
Code: Select all
void main (void){
struct foo C;
C.X = 3;
C.Y = 4;
}
Code: Select all
jsr decsp5
lda #$03
ldy #$00
sta (sp),y
iny
lda #$04
sta (sp),y
lda #$00
iny
sta (sp),y
nesdoug.com -- blog/tutorial on programming for the NES
Re: NES Programming Blog
Yeah, structs by itself are not too bad. It's arrays of structs indexed by non-constants that can be problematic, e.g.:
The above code has to generate code to multiply the index by the struct size (5) to index the array.
"Structs of arrays" is better:
However, this code still has the problem that the 16-bit Y and Z need a multiplication by 2 to access them. Splitting them into separate byte-sized YLo, YHi, ZLo, ZHi members could generate more optimal code, but that in turn would complicate the actual use of those members (say, if you want to add or assign a value to "YLo, YHi").
Code: Select all
struct foo {
unsigned char X;
int Y;
int Z;
};
struct foo B[5];
unsigned char i;
void main (void){
for ( i = 0; i < 5; ++i ) {
B[i].X = 4;
B[i].Y = 123;
}
}
"Structs of arrays" is better:
Code: Select all
struct foo {
unsigned char X[5];
int Y[5];
int Z[5];
};
struct foo B;
unsigned char i;
void main (void){
for ( i = 0; i < 5; ++i ) {
B.X[i] = 4;
B.Y[i] = 123;
}
}
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Re: NES Programming Blog
Things I figured out this weekend, and will be corrected on my blog.
I wrote the code for most of the pages very quickly, and occasionally I would get error messages from the cc65 compiler. "converting pointer to int without a cast" "incompatible pointer type" etc...and I didn't know what caused them, but I slapped an (int) on there, and the error message went away. But, it didn't look right to me, and I could never find any example code that required type casting to fix error messages...so it bothered me a bit.
Well, the reason I never found example code to match what I was doing, was because I was doing things wrong. The ASM code was correct, so I assumed I had correctly addressed the issue, but once I saw the correct answer...I see that I hadn't.
example...
I slapped some (int)'s on there because it gave me error messages...but what I really wanted was this...
and the companion piece...
I believe the error was that my prototype said this...
and, what I really wanted was this...
because, what I'm really doing with the code, is an array of constant pointers to an array of constant characters. And, what I'm really passing to the function is a pointer to an array.
This will be fixed soon on the blog example code.
Further, I don't think I've fully tested 'controller 2' input code. All my example code only tests 'controller 1'. I will have to do that as well.
EDIT, I tested it. Works fine.
I wrote the code for most of the pages very quickly, and occasionally I would get error messages from the cc65 compiler. "converting pointer to int without a cast" "incompatible pointer type" etc...and I didn't know what caused them, but I slapped an (int) on there, and the error message went away. But, it didn't look right to me, and I could never find any example code that required type casting to fix error messages...so it bothered me a bit.
Well, the reason I never found example code to match what I was doing, was because I was doing things wrong. The ASM code was correct, so I assumed I had correctly addressed the issue, but once I saw the correct answer...I see that I hadn't.
example...
Code: Select all
const int AllBackgrounds[] = {(int) &n1,(int) &n2,(int) &n3,(int) &n4 };
Code: Select all
const unsigned char * const All_Backgrounds[]={n1,n2,n3,n4};
Code: Select all
UnRLE(BGDaddress);
Code: Select all
void __fastcall__ UnRLE(int data);
Code: Select all
void __fastcall__ UnRLE(const unsigned char *data);
This will be fixed soon on the blog example code.
EDIT, I tested it. Works fine.
Last edited by dougeff on Tue Sep 06, 2016 3:13 pm, edited 1 time in total.
nesdoug.com -- blog/tutorial on programming for the NES
Re: NES Programming Blog
I've updated every example code on the blog. As usual, if anyone spots any outrageous bugs or bad programming practices, let me know. Thanks.
Here's a quick link to the Spacy Shooty source code...
http://dl.dropboxusercontent.com/s/70f8 ... Spacy4.zip
Here's a quick link to the Spacy Shooty source code...
http://dl.dropboxusercontent.com/s/70f8 ... Spacy4.zip
nesdoug.com -- blog/tutorial on programming for the NES
Re: NES Programming Blog
Update (10-17-2016) I updated reset.s in every file, to make sure that initlib and copydata were included. Also changed, added Wait_Vblank(); to several files, just before rendering was turned on, to fix 1 frame of misaligned screens. Finally, changed the .cfg file on the MMC3 examples, to include the missing segments that I had deleted.
See here for further discussion on missing 'copydata'...causing errors.
viewtopic.php?f=10&t=14947
See here for further discussion on missing 'copydata'...causing errors.
viewtopic.php?f=10&t=14947
nesdoug.com -- blog/tutorial on programming for the NES
Re: NES Programming Blog
Update Feb 9, 2017
I changed every .cfg file to include a "ONCE" segment, so it will compile with the latest version of cc65.
I added a makefile for Linux users, and people who prefer Gnu Make to .bat files. Well, Linux users will have to edit the makefile slightly. I originally wrote them on a Linux computer, but then brought them over to my Windows computer, and edited them to work there...
...anyway, Linux users will have to uncomment out the lines rm *.o and comment the lines del *.o. (etc for .nes files under CLEAN:)
UNRELATED SIDENOTE:
I wrote a 6502 disassembler in python. I might post it in a few weeks.
I changed every .cfg file to include a "ONCE" segment, so it will compile with the latest version of cc65.
I added a makefile for Linux users, and people who prefer Gnu Make to .bat files. Well, Linux users will have to edit the makefile slightly. I originally wrote them on a Linux computer, but then brought them over to my Windows computer, and edited them to work there...
...anyway, Linux users will have to uncomment out the lines rm *.o and comment the lines del *.o. (etc for .nes files under CLEAN:)
UNRELATED SIDENOTE:
I wrote a 6502 disassembler in python. I might post it in a few weeks.
nesdoug.com -- blog/tutorial on programming for the NES
Re: NES Programming Blog
If you are using Make from MSYS, you'll probably have GNU Coreutils, which includes rm. For other things that tend to vary, such as presence or absence of .exe in the name of a native executable produced by the linker, you can use the presence or absence of environment variable COMSPEC to set makefile variables.
See Writing portable makefiles.
See Writing portable makefiles.
Re: NES Programming Blog
Isn't multiplication of 2, 4, 8, 16 etc. unproblematic since the compiler can turn it into a simple bit shift? So, an array of ints shouldn't be that much of an issue. At least it's not comparable to the access complexity of an array of a struct.thefox wrote:However, this code still has the problem that the 16-bit Y and Z need a multiplication by 2 to access them.
Yeah, I would highly adivse against that. If you happen to need an integer in an NES game (which should be more the exception than the rule) let the compiler handle it. Don't fiddle around with two byte values if they are supposed to represent a single number.thefox wrote:Splitting them into separate byte-sized YLo, YHi, ZLo, ZHi members could generate more optimal code, but that in turn would complicate the actual use of those members (say, if you want to add or assign a value to "YLo, YHi").
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Re: NES Programming Blog
Yeah it's not a huge problem, but non-optimal nevertheless.DRW wrote:Isn't multiplication of 2, 4, 8, 16 etc. unproblematic since the compiler can turn it into a simple bit shift? So, an array of ints shouldn't be that much of an issue. At least it's not comparable to the access complexity of an array of a struct.thefox wrote:However, this code still has the problem that the 16-bit Y and Z need a multiplication by 2 to access them.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: NES Programming Blog
It's not just a bit shift. If you can do an array access with an 8-bit index, it can just go into X or Y. If the index is wider, it can't do that anymore, and you get a 16-bit shift plus a 16-bit add operation on a temporary pointer, and then on top of that the array access becomes indirect.DRW wrote:Isn't multiplication of 2, 4, 8, 16 etc. unproblematic since the compiler can turn it into a simple bit shift? So, an array of ints shouldn't be that much of an issue. At least it's not comparable to the access complexity of an array of a struct.thefox wrote:However, this code still has the problem that the 16-bit Y and Z need a multiplication by 2 to access them.
Here's an example:
Code: Select all
unsigned char ac[35];
unsigned int ai[35];
void index_test()
{
static unsigned char i;
// 1.
i = index();
ac[i] = 5; // 8-bit index on 8-bit array
// 2.
i = index();
ac[i*2] = 6; // index is promoted to 16-bit int
// 3.
i = index() * 2;
ac[i] = 7; // index was implicitly cast back to 8-bit before use
// 4.
i = index();
ai[i] = 8; // index is promoted to 16-bit int by implicit mulitplication by 2
}
Code: Select all
; 1.
; i = index();
jsr _index
sta L0017
; ac[i] = 5;
ldy L0017
lda #$05
sta _ac,y
; 2.
; i = index();
jsr _index
sta L0017
; ac[i*2] = 6;
ldx #$00
lda L0017
asl a
bcc L3763
inx
clc
L3763: adc #<(_ac)
sta ptr1
txa
adc #>(_ac)
sta ptr1+1
lda #$06
ldy #$00
sta (ptr1),y
; 3.
; i = index() * 2;
jsr _index
asl a
sta L0017
; ac[i] = 7;
ldy L0017
lda #$07
sta _ac,y
; 4.
; i = index();
jsr _index
sta L0017
; ai[i] = 8;
ldx #$00
lda L0017
asl a
bcc L3764
inx
clc
L3764: adc #<(_ai)
sta ptr1
txa
adc #>(_ai)
sta ptr1+1
lda #$08
ldy #$00
sta (ptr1),y
iny
lda #$00
sta (ptr1),y
So... not as bad as a multiplication, but if you're looking to reduce some of your overhead, it's actually not a terrible idea to "manually" pack striped arrays. A syntax vs convenience tradeoff, though you could simplify the syntax with macros.
Re: NES Programming Blog
O.k., yeah, that makes sense. Maybe I could have optimized some stuff with this knowledge in my game because the x position of each character was an integer.
(y was a byte because I had a status bar at the top, so I could simply declare that every sprite that has a position within the status bar is declared as out of screen and I didn't render these sprites at all.)
(y was a byte because I had a status bar at the top, so I could simply declare that every sprite that has a position within the status bar is declared as out of screen and I didn't render these sprites at all.)
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html