I've just been looking through my dumb code, trying to find out what's wrong with it when I realized what the hell am I doing? I could probably remake the code, possibly even improving it, faster than I'll ever be able to find out what's wrong with it, and since I really haven't done much and everything is integrated together, I wonder if I should just start over. Everything is a mess, because I've just randomly attached junk without knowing what I was doing to try and make it better I've only made it more convoluted. I just remember how fun this was originally compared to how I feel about it know, it's like night and day. I just wish I could start over with something like Walker again, except not on WLA (you know why...). With the experience I've gained, (I had no prior programming knowledge) I honestly feel like I could do what has taken me a whole year in one month. Does anyone have a starter program for ca65, or has anyone even restarted before? I know psychopathicteen has. How far are you in development at this point anyway?
Espozo wrote:
Does anyone have a starter program for ca65
I do.
I looked at yours again, and I still don't understand it. (I don't know why, it's just not what I'm used to.) I think I'll try and make my own, but I'll probably won't get to far.
Espozo wrote:
I looked at yours again, and I still don't understand it. (I don't know why, it's just not what I'm used to.)
If you can't understand it, that's a bug, and I want to fix it. What's the first thing you don't understand?
Do you know how to use IRC? Connect to EFnet, join #nesdev, and see if I'm in the channel.
tepples wrote:
If you can't understand it, that's a bug, and I want to fix it. What's the first thing you don't understand?
Never mind, it's the most understandable thing ever!
(It would probably be just as difficult to figure it out as it would be to figure out what's wrong with it, and I'm lazy, so...)tepples wrote:
Do you know how to use IRC? Connect to EFnet, join #nesdev, and see if I'm in the channel.
Can I use this to ask random things? You know, the kind that don't get answered here:
https://forums.dolphin-emu.org/Forum-de ... discussionHonestly though, I just started trying to make stuff over again, and it's way easier than I thought it would be, which is obviously really good. I'll show you when I actually get something onscreen.
Edit: I just tried the IRC channel thing, but I couldn't even figure out how to write a message... (I pressed most of the keys on the keyboard.)
I'd expect revisions to be standard if you haven't programmed a few games in your environment yet. I've redone some parts of my code 10 times by now and I'm still early in the project.
I wouldn't think starting entirely from scratch is necessary unless every single part of your program is wrong.
Even if you need to redo core functionality you can keep the subroutines and stuff that you like.
It's all just bad. I'm going to keep my old code as reference maybe, but I'm not going to copy/paste even a line. I was never that far anyway.
(Damn, I thought I often got off topic, but I see the IRC channel and they're talking about Pokémon...)
Espozo wrote:
(Damn, I thought I often got off topic, but I see the IRC channel and they're talking about Pokémon...)
Yes, the IRC channel goes off-topic to fill time. But if you wait for a break and pose an on-topic question, or you
/msg someone who's at keyboard and who is likely to know the answer, you can usually get your on-topic question in.
The thing is about the IRC chat is that I don't know where or how to type for it.
Which client are you using? Can you attach a screenshot?
Attachment:
Screenshot (412).png [ 169.81 KiB | Viewed 5557 times ]
Click in the white stripe at the very bottom of the window, below "npt has quit", and start typing. Then press Enter to send the message to the channel.
There's a button labeled "tepples" at the top; that's a tab for a private message pane.
tepples wrote:
Espozo wrote:
I looked at yours again, and I still don't understand it. (I don't know why, it's just not what I'm used to.)
If you can't understand it, that's a bug, and I want to fix it. What's the first thing you don't understand?
It relies heavily on scripts, macros, makefiles and a bunch of stuff never explained. If you aim it as a tutorial for beginners it's not very beginner friendly IMHO. For a beginner you would need to explain about everything you are doing in the readme or at least some more helpful comments in the code. Also for a beginner it's important that it starts simple enough, so the beginner can display something on screen fast enough (otherwise it's hard to know if he is doing things right). But of course your template project is more than just a tutorial, it's more like an example how to do everything from display, input to sound, which can't possibly be that simple.
That said, it's a good continuation from Blargg's ca65 template. I learned a lot from it when trying to make my own SNES program, and I'm very grateful you made it, even though I don't want to fiddle around to set up that complicated development environment on my own PC.
Well, I believe one should be in full control of what their code is doing. If the thing looks like a mess but you're still able to navigate through it and easily modify things whenever necessary (making games is not only about writing new code after all, a lot of it is revising old code), then you should be fine. However, if you often can't remember where/how/when something is done, and the whole thing is so flimsy that it "randomly" breaks without you realizing why, then I'd say something is wrong, and you should engineer things better from the beginning.
Pokun wrote:
It relies heavily on scripts, macros, makefiles and a bunch of stuff never explained.
"Scripts": The Python scripts are
pilbmp2nes.py and
wav2brr.py. Both take data in a PC-friendly format and convert it to a Super NES-friendly format. Both include
--help generated using the argument parser library. What else is there to explain about the usage of these tools? Or are you asking me to take beginners from 0 to the ability to write their own data conversion tools first?
"Macros": In
snes.inc, the
setxy8 family are easier-to-remember names for those
rep and
sep instructions that affect 65816 data width. These instructions should be described in any 65816 tutorial. I'll add doc comments for each of these six macros. The other macros, which push constant values to the stack, already had doc comments. The
spc-*.inc files are a different ball of wax entirely, as they implement an assembler for a different ISA and save you from having to install an even more "complicated development environment".
Quote:
If you aim it as a tutorial for beginners it's not very beginner friendly IMHO.
I sort of intended it as a step up from
my project template for the original NES. But at some point, I need to decide on its scope. Will I include a complete 65816 assembly tutorial? A complete SPC700 assembly tutorial?
Quote:
For a beginner you would need to explain about everything you are doing in the readme or at least some more helpful comments in the code.
What is the first place that I need to comment better?
Quote:
Also for a beginner it's important that it starts simple enough, so the beginner can display something on screen fast enough (otherwise it's hard to know if he is doing things right).
If you have all the pieces installed, and a .sfc file comes out, you know you have it installed correctly.
Quote:
even though I don't want to fiddle around to set up that complicated development environment on my own PC.
It's just Python, Pillow, MSYS, and cc65. Without "that complicated development environment", how would you go about doing any of these?
- Convert background and sprite graphics from the format used by your paint program to the format used by the Super NES. (Or would you prefer to draw graphics directly in a CHR editor, which leads to blockier-looking graphics?)
- Convert wave files from the format used by your audio editor to the format used by the Super NES. (Or would you prefer to make projects without audio or with misappropriated audio?)
- Automatically rebuild when the source data changes. (Or would you prefer to rebuild the whole project from scratch every time the smallest piece changes?)
Well, I'm just about back to square one: (I made it to where I'd still read the controllers, because every game ever does that.)
Code:
.setcpu "65816"
.smart
.include "Header.asm"
.include "InitializeSNES.asm"
.include "InitializeControllers.asm"
;======================================================================
.segment "ZEROPAGE"
;======================================================================
Joy1Data: .res 2
Joy2Data: .res 2
Joy1Press: .res 2
Joy2Press: .res 2
;======================================================================
.segment "CODE"
;======================================================================
.proc Main
InitializeSNES
jsr InitializeControllers
;Enable NMI (when interrupts get re-enabled) and joypad reading
lda #$81
sta $4200
cli ; Restore interrupts -- we're all ready to go!
InfiniteLoop:
jmp InfiniteLoop
.endproc
;======================================================================
.proc VBlank
rep #$30 ; A=16, X/Y=16
pha
phx
phy
phd
phb
php
jsr GetControllerInput
lda $4210 ; clear NMI Flag
plp
plb
pld
ply
plx
pla
rti
.endproc
Now, the two things I need to do now before I get going further are that I need to see if the test is even "running properly". (It assembles, but the screen is black, which is expected.) I'd figure I'd just make the screen white, and I'd kind of like to know, how do you write to cgram not using DMA, if possible? Second, I need to then do something to see if the test is not crashing after the first frame, so I'll increase the value of the color or something every frame. I think I also need to turn on the screen or something too.
Easiest would be to just write to $2122 (CGRAM write register) twice like in Blarg's ca65 template. Something like this (but with your colour of choice, I like green):
Code:
;Set background color to $0A0A (green):
lda #$0A
sta $2122
lda #$0A
sta $2122
;Turn off forced blanking:
lda #$0F
sta $2100
forever:
jmp forever
Another way to see if a particular part of your code works would be to enter a STP instruction in the part of the code you want to know if the PC reached it. Some emulators like no$sns will treat it as a break point and let you know.
Tepples I'll answer you later when I have time in your feedback thread to avoid derailing this one.
Well, I got the color to show onscreen, and I decided that I wanted to see if I could cycle through every color combination by adding 1 to the color value, but for some reason, it kept uploading the newer color value in a spot 1 greater than the last one, so color 0 stayed the same color. I know the program works now, but it'd just be kind of nice to know how to fix it.
Code:
InfiniteLoop:
wai
lda ColorCounter
sta $2122
lda ColorCounter
sta $2122
inc ColorCounter
jmp InfiniteLoop
By the way, what does this mean? I copy/pasted it, but I guess it's important.
Code:
cli ; Restore interrupts -- we're all ready to go!
Espozo wrote:
Well, I got the color to show onscreen, and I decided that I wanted to see if I could cycle through every color combination by adding 1 to the color value, but for some reason, it kept uploading the newer color value in a spot 1 greater than the last one, so color 0 stayed the same color. I know the program works now, but it'd just be kind of nice to know how to fix it.
Code:
InfiniteLoop:
wai
lda ColorCounter
sta $2122
lda ColorCounter
sta $2122
inc ColorCounter
jmp InfiniteLoop
CGRAM is manipulated via two registers:
$2122 for writing/reading data at the current address (write/read-twice)
$2121 for setting CGRAM address (write-once)
The CGRAM address is always automatically incremented on write (on read too I believe, although don't quote me on that).
http://wiki.superfamicom.org/snes/show/Registers
According to superfamicom.org $2122 is only for writing data, not reading. Reading is done using $213B.
CLI (clear interrupt flag) enables maskable interrupts on the CPU side. You probably disabled them using SEI (set interrupt flag) first thing in the beginning. Note that this flag doesn't affect NMI, only IRQs (something I learned only recently). So it's really only important to use CLI if you are using IRQs (using SEI is always important though) but I guess you might as well enable it as a general practice.
Pokun wrote:
According to superfamicom.org $2122 is only for writing data, not reading. Reading is done using $213B..
Ha! Indeed. Should've read the documentation myself first. Got tricked by the big N naming of $2122 which is "CGDATA".
I think you could break down the animation engine into achievable goals:
1) Program a basic animation/metasprite engine, that runs after the object's routine.
2) Program a simple fixed-slot DMA mode for the player character.
3) Allow your engine to keep track of DMA-bandwidth, and make it capable of delaying frames when a set DMA limit is up (should be around 4kB).
4) Do all the complicated stuff. I can break this down further if you know for sure what kind of engine you want.
It's not just that, although that is the main problem.
Well, anyway, I got it to work finally. (It's actually the exact same thing I got going on the M92, now thinking about it) If anyone wants it, here it is. It's not for newcomers, but for someone who already has technical knowledge and doesn't want to deal with all the extra crap in Walker that you'll probably just erase anyway, it's perfect. The color thing is just kind of to say it works, but you can just erase the code for it and then you'll have a clean slate to work with.
Attachment:
SNES Restarter Kit.zip [181.48 KiB]
Downloaded 117 times
Glad you like my batch script, but I suggest you use
elseyf's snes checksum fixer or
SuperFamicheck rather than ucon64.
And a little warning, if you do keep ucon64, try taking out
>nul 2>&1 from the batch script to make sure the program is really running.
The one thing I'd like extra is if the batch file would delete the ".o" file when it's done being assembled. I imagine you could write it one line, but I don't know how.
Espozo wrote:
The one thing I'd like extra is if the batch file would delete the ".o" file when it's done being assembled. I imagine you could write it one line, but I don't know how.
Code:
if exist Game.o del Game.o
You know, this is random, but I just want to make everything as organized and consistent as possible so I'm going to ask this: For objects, do you think I should group all of the resources of an object together, or just have separate folders for all of the different object properties? I mean, it would be like having a folder called "Object Animation" and then having folders underneath it that have the object animation cycles and then the picture for each frame vs. having a folder that just says "Objects" and then having different folders underneath it for every object and then having more folders underneath that?
Espozo wrote:
You know, this is random, but I just want to make everything as organized and consistent as possible so I'm going to ask this: For objects, do you think I should group all of the resources of an object together, or just have separate folders for all of the different object properties? I mean, it would be like having a folder called "Object Animation" and then having folders underneath it that have the object animation cycles and then the picture for each frame vs. having a folder that just says "Objects" and then having different folders underneath it for every object and then having more folders underneath that?
What do you mean by objects? I'm confused.
I mean like game objects, like in a Mario game, a koopa or a goomba or a mushroom or whatever. It's not related to what you just did.
Espozo wrote:
I mean like game objects, like in a Mario game, a koopa or a goomba or a mushroom or whatever. It's not related to what you just did.
So like tile data and stuff? Yeah, I think having a folder named "Objects" with a folder named "Animations" in it is more organized than a bunch of folders named "Objects blablabla".
Well, I think I found something out. The game doesn't like this: (It only works with "force range")
Code:
rep #$30 ;A=16, X/Y=16
lda #ObjectTable
Because "ObjectTable" is out of zero page and can't be represented by a 24 bit number? How would you do this then?
Are you trying to build a far (3-byte) pointer on zero page for use with the
[d],y addressing mode? In that case, try this:
Code:
lda #(ObjectTable & $00FFFF)
sta ptr
lda #(ObjectTable >> 8)
sta ptr+1
Well, I have it in "BSS7E", and when I wanted to do what I did earlier, it said there was a "range error" or whatever, so the address couldn't be represented by a 16 bit number?
This was the code, if this helps. I'm just not sure what to do.
Code:
.proc start_object_identifier
rep #$30 ;A=16, X/Y=16
lda #ObjectTable
sta a:ObjectOffset
tcd
object_identifier_loop:
lda $0000
beq next_object
tax
;jsr (ObjectIdentificationJumpTable-2,x)
rep #$30 ;A=16, X/Y=16
next_object:
lda a:ObjectOffset
clc
adc #ObjectTableEntrySize
sta a:ObjectOffset
tcd
cmp #ObjectTable+ObjectTableSize
bne object_identifier_loop
rts
.endproc
;========================================================================
.segment "RODATA"
;========================================================================
ObjectIdentificationJumpTable:
.word object1_code,object2_code
The name "BSS7E" indicates that it's an uninitialized data segment in bank $7E. This means a 16-bit address is sufficient only if the data bank is set to $7E with PLB.
Wait, so how do you fix it?
What to do to fix it depends on why you are trying to load an entire address into a register in the first place, that is, it depends on what you are planning to use the address for.
Well, the main thing I want to do is set direct page to that address. The reason I want it in a register ever is so I can increment the address. If I can set direct page to that, will I still be able to have "a:" work? (I don't know how else to describe it.)
The most productive places for direct page are $000000-$001F00 (the first 8K of WRAM), $002100 (access PPU with short writes, as on the Atari 2600), and $004300 (use unused DMA channels as a direct page). The other 120K of WRAM cannot be used as direct page.
I mean, I don't know if this is good, but I like to set direct page to be like an additional offset from x and y. I want to have it to where for objects, direct page will be set to the beginning of where the object starts. I mean, direct page is only 16 bit, and the address space of the SNES is 24 bit, so I want the object table to be in the first 16 bits of the address space, but if I put it under zero page, it says that the object table is too big and that there's a memory overflow, but if I put it in BSS7E, it says that there's a "range error" when I'm trying to load the address into the 16 bit accumulator.
The fact that direct page can reach only the first 8K of RAM is something you just have to live with unless you want to put extra bankable RAM on the cartridge and map it to $006000-$007FFF.
If you want to set up a pointer to an object structure, you'll have to store the pointer to the object on direct page and use [d],Y address mode. Or better yet, use structure-of-arrays, with all objects' Y location, all objects' X location, etc. as separate arrays, and use al,X mode to index into those individual arrays.
Oh, I found it out now. I thought "zero page" was the 8KB, but it's only the first 256 bytes, which confused me as to why I was getting "memory overflow" when it wasn't even near 8KB. It works now that I put it in "BSS" instead of "BSS7E".
I forgot, how do you ask for the bank byte of something in ca65? Sorry...
For the value of the
bank attribute of a label's memory area, use
.bank(label). This is more useful on NES.
For bits 23-16 of a label's address the value of the
bank attribute of a label's memory area, use
.bankbyte(label) or
^label. This is more useful on Super NES.
Well, all I want to do is send two tiles to the start of VRAM via HDMA, but for whatever reason, it's filling up all of vram (or at least parts of it, it's hard to tell because it could just be sending 0's.)
This is the code:
Code:
rep #$30 ;A=16, X/Y=16
sep #$20 ;A=8
lda #%10000000 ;Increment VRAM address by 1 after write to $2119
sta $2115
ldx #$0000
stx $2116 ;$2116: Word address for accessing VRAM
ldx #Object1Tiles ; Address of src + offset
stx $4302 ;Store Data offset into DMA source offset
lda #^Object1Tiles ;Bank of src
sta $4304 ;Store data Bank into DMA source bank
ldy $0010 ;Length of data to copy
sty $4305 ;Store size of data block
lda #$01
sta $4300 ;Set DMA mode (word, normal increment)
lda #$18 ;Set the destination register (VRAM write register)
sta $4301
lda #$01 ;Initiate DMA transfer (channel 1)
sta $420B
It doesn't seem to matter what number I send to $4305, which I thought controlled the size. I was going off the fact that 128 x 64 / 2 (8 bits instead of 4) = 256, which = #$10. I tried to copy this code from something else, but it clearly isn't working as intended. I obviously don't plan on keeping the above, I'm just using it for a test. It'll be nice when the summer rolls around and I
actually work on this...
ldy $0010 should probably be ldy #$0010.
Oops... Yeah, it works now.
Espozo wrote:
I was going off the fact that 128 x 64 / 2 (8 bits instead of 4) = 256, which = #$10.
That's a fact, is it?
I thought two tiles was 128 bytes at 8bpp, or 64 bytes at 4bpp, and that 256 was = #$100...
93143 wrote:
That's a fact, is it?
Today is not my day...
I just assume the "size" is going by bytes and not bits and it appears to work how I have it know, so I guess it does. I forgot to mention that.
I know this really lazy, (like I said though, I plan on actually making this game engine publically available if I finish it, so it's best to be as easy to work with as possible) but does anyone know if it's possible to have separate fields of variables in ram (the ".res" stuff) that can be used for various tables in ram? I mean, I want to have my object table automatically increase or decrease based on how many variables are under it. I mean, if I only have a 24 bit "XPosition" variable, then each entry in the object table will be 24 bits, but if I had "XPosition" and "YPosition", then it would be 48 bits. (I hope there's also a way to have the assembler calculate the size of a slot. Otherwise, there wouldn't even be any point to this.) I know I can do this manually, but it would be nice if this could work. (I use ca65 by the way.)
Another more important problem I have is that all the "include" junk is in the main file that has to information like the main game loop. and it's kind of ugly now. Should I create a file that has everything included and just include the "main" file?
Espozo wrote:
I mean, I want to have my object table automatically increase or decrease based on how many variables are under it. I mean, if I only have a 24 bit "XPosition" variable, then each entry in the object table will be 24 bits, but if I had "XPosition" and "YPosition", then it would be 48 bits. (I hope there's also a way to have the assembler calculate the size of a slot. Otherwise, there wouldn't even be any point to this.) I know I can do this manually, but it would be nice if this could work.
I think you're looking for
.STRUCT in ca65. You'd create all the fields inside a struct, and use
.SIZEOF to get the size of the struct which is the size of a slot. To access the values in the slots you'd have to use something like BaseAddress + StructName::FieldName.
Quote:
Another more important problem I have is that all the "include" junk is in the main file that has to information like the main game loop. and it's kind of ugly now. Should I create a file that has everything included and just include the "main" file?
I have no idea how other people approach this problem, but my main file is only includes. First it includes files containing definitions for each system/area in the program (system, mapper, video, audio, input, game, etc.), which consist of constants, macros and variables. Then come the program banks, each with a series of includes for code and data. Each table and subroutine is a separate file, so I can freely include them in whatever bank I want.
tokumaru wrote:
I have no idea how other people approach this problem, but my main file is only includes.
Yeah, that's what I was thinking. I'll probably have one called "Main" and one called "GameLoop" or something like that.
Anyway, I thought I had the ".struct" down, (here's what I wrote)
Code:
.struct "ObjectTableSlot"
XPosition: .res 3
YPosition: .res 3
.endstruct
But it doesn't appear to like the ".res". How am I supposed to do this?
The struct definition isn't supposed to reserve any actual memory yet, it just assigns offsets to each of its members. If you look at the
ca65 documentation you'll see what the correct syntax for structs is, you don't have to guess. Hint: there are no quotation marks, no colons and .res is not the keyword that defines the size of each member.
Then, once the struct is defined, you can reserve memory for slots using that structure like this:
Code:
Slots: .res .sizeof(StructName) * 16
This will reserve memory for 16 slots. Is this whay you're trying to do?
EDIT:
As for accessing the slots, I'm not sure how to do it in 65816 assembly, since I only do 6502. In 6502 you could do something like this:
Code:
;create a pointer to the fifth slot
lda #<(Slots + .sizeof(StructName) * 4)
sta Pointer+0
lda #>(Slots + .sizeof(StructName) * 4)
sta Pointer+1
;use the offset as an index
ldy #StructName::MemberName
;get the data
lda (Pointer), y
This isn't the best way to organize data on the 6502 though, where structures of arrays are preferable over arrays of structures.
Well, I think I got it now:
Code:
.struct ObjectTableSlot
XPosition .byte 3
YPosition .byte 3
.endstruct
However, instead of having the slot size and the slot number automatically calculated together like that, I want to have the thing that finds the number (the ".sizeof") of how large each slot is and the number for how many slots there are separate for a couple of reasons, like whenever I'm incrementing the object table to look though it, I want to increment by the size of each slot, not the size of each slot x the number of slots.
It doesn't appear to like this though:
Code:
ObjectTableEntryNumber = 32
ObjectTableEntrySize: = .sizeof(ObjectTableSlot)
ObjectTable: .res ObjectTableEntryNumber * ObjectTableEntrySize
Command Prompt wrote:
(Line with "ObjectTableEntrySize") Unexpected trailing garbage characters
(Line with "ObjectTable") Constant expression expected
I think the problem is the colon after ObjectTableEntrySize. Try removing that and see if it works. Colons are for labels, and the equal sign is for constant symbols, you shouldn't be mixing those like that. ca65 does have ":=" though, which you can use to create labels through assignment, but, as far as I can tell, ObjectTableEntrySize is supposed to be just a symbol, not a label.
You're right, and I knew that, I'm just being an idiot today...
It works now though.
Wait, I don't know if you already told me this and I'm just not paying attention, but I'm trying to load from the location of one of the items in the struct, like this: "lda XPosition", but it's telling me that it doesn't exist even though it's one of the things under the struct. Is it that I'm not addressing it right, or is this just impossible?
Espozo wrote:
Wait, I don't know if you already told me this and I'm just not paying attention, but I'm trying to load from the location of one of the items in the struct, like this: "lda XPosition", but it's telling me that it doesn't exist even though it's one of the things under the struct. Is it that I'm not addressing it right, or is this just impossible?
I think structs are addressed like:
Code:
ObjectTableSlot::XPosition
Command Prompt is telling me that there's "no such scope" as "ObjectTableSlot", whatever that means. I flipped it around just to try it out, and that doesn't work either.
Yeah, I did mention previously how to get the offset:
tokumaru wrote:
Code:
;use the offset as an index
ldy #StructName::MemberName
Here I used the Y register because in 6502 assembly you'd most likely use indirect indexed addressing to access a structure like this, but I don't know if the 65816 has a better addressing mode for this.
Espozo wrote:
Command Prompt is telling me that there's "no such scope" as "ObjectTableSlot", whatever that means. I flipped it around just to try it out, and that doesn't work either.
Is that BEFORE the struct is defined? It's a known fact that ca65 can't access scopes before they're created, so you have to define the struct before it's used for the first time.
Oh... Yeah, that's the problem...
I'll move all my .includes to the end of the file.
Wait, I just realized something... In the struct, is #StructName::MemberName the location of MemberName in ram, or is it the number that MemeberName is in the struct?
It's the offset of MemeberName from the start of the slot (i.e. the first member always has a value of 0).
That's what I thought, but I was having an error with my metasprite routine, but luckily, I fixed it. Thanks for helping me with that.
(I can't think of any other excuses from coding now...
)
I just noticed something... I'm finally back to where I restarted from. Yay?