Hacker News new | past | comments | ask | show | jobs | submit login
Introduction to Nintendo 64 Programming (1999) (icequake.net)
232 points by whack on June 22, 2019 | hide | past | favorite | 61 comments



> The N64 operating system supports some but not all of functionality of the malloc() and free() standard C library functions. All their functionality will be supported in the future.

Did that ever happen?


Related: there is a fantastic, oft-reposted, and quite in-depth article about the difficulties faced by developers porting 'Resident Evil 2' to the N64 - the primary challenge being fitting it within the 64MByte limitation, as compared to the Playstation's 700MB CD.

A fantastic and critical read to anyone interested in this sort of thing: https://www.gamasutra.com/view/feature/131556/postmortem_ang...


That was a bit of a disappointing read. I was expecting some technical details but it was mostly just about how they managed the project.


This was the article I thought the gp was linking to, some detail about the video decoder: https://web.archive.org/web/20080331015735/http://www.gamasu...


Ya, I kept waiting for the details, it was more about process or high level.


> We dealt with the publisher's requests to add new features, especially towards the end of the project, with aplomb. Rather than an internal attack of "feature creep," these came from the outside. Each time a new feature was proposed, we examined what it would take to implement it and presented an honest account of what it would take, in terms of resources, to implement it.

Just tell the client how long it will take! Of course! This makes me a bit suspicious about the relevance of the rest of the author's advice, given that I certainly don't have access to whatever eldritch magics he channeled in 1999 to gain these powers.


'[The 64DD's] total disk capacity is 64MBytes, enabling the creation of huge games not possible with just a ROM cartridge.'

Huge games!


It could almost fit the JavaScript bytecode from Gmail.


There are even a few 64 MByte / 512 Mbit N64 cartridges (IIRC: Resident Evil 2, Pokemon Stadium 2, Conker's Bad Fur Day).


Mario 64 is a bigger game than most I've played in recent memory...


How are you defining "big"?

Mario 64 was impressive for its time, but the worlds are pretty darn small by modern standards.

The game also uses multiple mission objectives (and a requirement that you return to the start of the map after each mission) to pad out the length.


I just saw a girlfriend review of Zelda Ocarina of time (another N64 game).

She was amazed by how many things are packed into this game and how each room or character feels way more unique than the last assassin creed game.

I would not say that all modern games suffer from this but I have come to deeply appreciate games that did the best with the limitation imposed by them by their tech.

In addition to N64 games, I have always been impressed by how Grim Fandango took a low poly graphical engine and turned it into an amazing art style.

Especially when I compare it to so many AAA games with huge budgets and a brown "photorealist" approach as interesting as a wet carrot and that look extremely dated 3 years later.


That’s interesting because I thought I was alone. I’ve been playing Assassins Creed Origins for a while and my general feeling is there’s a TON of content but none of it is important. There will be a city in the desert and maybe one mission, and a bunch of very samey houses? It’s certainly realistic, but the real world is so boring.

In older Assassins Creed games you could tell loving thought went into how you would traverse things, climbing things took a lot more skill and there were secrets to find.

The newer ones the other hand have just tons of bland, and climbing is basically automatic, just push forward. It’s kind of sad to see one of my favorite series become so dull.


To be fair I have played to the new one for less than an hour.

I could play it for free during the stadia demo. If I had played for more than one hour, I would have been able to keep the game. The tech itself is amazing, but after skipping 2 or 3 asscreeds games, I was very surprised to see health bars.

The series always had a bad tendency to pad its length by relying to the endowed progress effect with lots of copy paste content like "collect the 100 feathers".

The health bars and levels already made the first fights boring, not to mention that I don't think they have anything to do in a stealth action game. They just sound like another way to pad the game length.

If the platforming also got worse ? damn, I don't regret letting this franchise go.


It starts off pretty strong but it gets to the point where there will be a giant city and 1 or 2 missions in the entire city. It makes the scenery feel like set dressing rather than a place designed to be played with.


> I just saw a girlfriend review of Zelda Ocarina of time (another N64) game.

Sorry, is this a typo or is there a specific niche of "people who are girlfriends" (?) reviewing games?


[flagged]


Not sure about him, but I'm not in the habit of looking up apparent typos.

>she's a popular youtuber

Thanks for the clarification.


why on earth did nubb comment get flagged ??

As I remember it, it was courteous and informative.


Because of the “You could have just Googled it.”


Just wanted to make sure you didn’t mean “my” in the place of “a”, which I guess you didn’t.


How is that a credential?


I guess OP means "amount of content", which is definitely true. That one little trick, combined with genuine fresh gameplay, gave it some incredible longevity.

I've been playing through the first two Thief games, released 20 years ago, and I put 60 hours into beating the first and am already 30 hours into the second.

Gotta say I'm extremely impressed given most games I've played in the last few years are on the order of a few hours' playtime.


In terms of quantity though, I don't think there was that much content. It all came together well, especially for its time, but that's different.


Define "content".


> The N64 operating system supports some but not all of functionality of the malloc() and free() standard C library functions.

What does this even mean? Each of those does pretty much “has one job”; does malloc give you memory and free only reclaim half of it or something?

> Even though a typical C program can rely on the system to handle initializations

Uh, not if this is an automatic variable…


Maybe it's one of those non-standard free() functions that crashes if you give it a null pointer. A developer I once worked with said that he had gotten into the habit of "if(p) free(p);" because he'd work with such a system before.

(In case anyone is wondering: I can't think of a single commonly-used standard C library that doesn't accept null pointers to free(), i.e. the nullcheck is in the free() itself. Embedded systems, however, may vary widely in how closely they follow the standard.)


I downloaded some thing that purports to be the "Nintendo 64 SDK" and it says that its implementation of malloc cannot be accessed safely by multiple threads (there's an alternative function that does guarantee this). I think this might be it? (FYI: the free function does check for NULL.)


I've never worked with the official OS routines, but I think standard malloc() and free() just weren't provided, in favor of a custom arena-based system. Ref: http://n64devkit.square7.ch/pro-man/pro10/10-05.htm


My only thought about malloc() is that it was susceptible to fragmentation and applications needed to manage their own pools of similar sized blocks. But definitely an odd comment without further allocation.


As I recall their allocator was a fairly basic implementation - I don't think it had small allocation support, or any debug/stacktrace/tagging functionality.

You could drop in your own allocate/free for everything in-game and ignore their implementation (you had pretty much complete control over what went where in the memory so long as things to be copied to the RSP/RDP were aligned correctly and the display buffers were on some specific boundary).

Allocating from pools per system was very common (eg particle effects got a 16kb memory pool for vertices and the code had to gracefully handle hitting that limit). Another common practice was to store a high-water marker at the start of a level load and just roll back to that marker when you changed level.


The owner of this channel https://www.youtube.com/user/KazeBG0 makes great fan games for N64, regularly adding in additional behavior not found in the parent games - e.g. adding new super powers to Mario 64. Pretty impressive stuff.


Most of the videos are blocked by copyright :(


Nintendo has unfortunately not been kind, but I can confirm that Kaze is a legend at what he does.


Here I am. Aligning DIV's. And then people do this. Awesome.


Hey man, I started my career 7 years ago by teaching myself PHP, jQuery, and CSS. First real job was as a "full stack web developer".

I always was in awe of the low level stuff though, so I took every opportunity to move closer and closer to the hardware over the years. I'm now a firmware developer!

Point being, where there's a will there's a way. If you can align some DIVs, you can malloc() some memory and pack bytes efficiently in a struct :) It all comes down to where your interest is.


What has the low level stuff allowed you to do?


Sorry for the delayed response.

It has allowed me to work on some very cool robotics, and IoT projects. I've been able to learn a ton in the process about how computers work. Things like having to learn about how our FreeRTOS scheduler works, have been a fun process and provide some insight (albeit not 1:1) on how larger scale OS's work.

I feel like it's also made me a better programmer on the higher level stuff too. Hard to explain why though. It's not the kind of thing that makes obvious sense. It's not like knowing how to efficiently pack bytes into a struct, somehow helps you be a better Scala or iOS developer. That's just not the case. Rather, there's something about knowing the low-level stuff that just eases my mind when it comes to writing higher level code. I've always been the type of person that gets distracted by needing to know the deeper layer of how something works.

Even in undergraduate classes like biology, I needed to know HOW that mitochondria ACTUALLY handles metabolism. That's what I mean when I say that the lower level stuff "eases my mind". I can call a higher level function, or use some higher level framework, with a clear understanding of how it could (and probably) is implemented. That makes me a better developer, because I can stop asking questions and start making things.


Do you enjoy C over other options?


There aren't many better options when it comes to firmware development. The state of the art, as I understand it currently, is C / C++.

But having worked in many languages (Java, Swift, Scala, Dart, etc.) I have to say, I love C.


I think about this all the time. Thank god somebody has an interest in building programming languages, and other lower level nitty gritty work. Lord knows i wouldn't be able to do it


Absolutely awesome. I have a serious thing for programming on old consoles, specifically the Sega Saturn, and this is a very fascinating manual.


Is this everything, or just a primer that comes with reams of additional documentation?

I think I remember reading that game programmers developing for new (or not yet released) consoles of bygone eras had to contend with sparse documentation, shifting specs, and inadequate access to real hardware... It would be wonderful if someone with experience or knowledge from this era could elaborate on what it was like getting a black box in the mail and trying to stand up some working code.

I'm imagining lots code written by trial and error under intense time pressure, that nobody on the team really understands and that nobody wants to touch. I'm a sucker for this kind of folklore of ancient programming heroism. :)


Is there a public N64 compiler? Might be a fun project to make an N64 game


I made a n64 Rust env that's open source that's albeit a bit raw but was enough to be able to build ROMs of PowerPoint-esque slides of talk of a post mortem getting to that point.

https://github.com/monocasa/sides-bsw19

If you want to use some of the same stuff hit me up on GitHub.


Is there a video recording of your talk available online?


Nah, I don't think there was. Sorry.

Edit: the general thesis was that while we as embedded fairly treat new languages in the embedded space with skepticism, Rust is a legitimate entry. It's not GCed (in the traditional sense) has similar perf and (more importantly) determinism. And by the very nature that the slides are on an N64, it's remarkably possible to support off the beaten path boards.


There are a few projects that build toolchains around gcc, http://n64dev.org/ is a good resource.


So they have multiple 'threads', but those threads work sequentially? Why do they even design like this in the first place? Doesn't have any benefits right?


The description of the scheduler[1] explains why:

“The RSP executes both graphics and audio processes. A graphics process sometimes extends over one frame, but an audio process must be provided in each frame to prevent inappropriate pausing. Therefore, in each frame, if the graphics task is executing, the Scheduler suspends it and saves the task state. Then it starts the audio task executing and prepares the RSP for the restart of the graphics task execution when the audio task finishes.”

[1] http://n64.icequake.net/doc/n64intro/kantan/step2/1-7.html


Thanks.

I think the thread here, really means it is a conceptually independent 'processing unit' on its own, which reads the global state of some sort, then make the modification to it. They have multiple such 'threads', each corresponding to some particular task, but with no explicit locks.

Now it makes more sense to me. Threads here are more logical isolation of functionalities, not really parallel execution workers.

I was automatically associating threading with parallelism, but this makes sense for 'concurrency' even if there is only one of them are working at the time.


Seems like the same rationale as cooperative multitasking modes for RTOSs on microcontrollers without memory protection - each "thread" might handle different I/O requirements (polling game controller button input, setting up the next frame on the GPU, filling the sound buffer, etc.), and as long as none of them crash, they can all get enough CPU time to be soft-realtime.


This is in fact how all threading worked before processors started coming with multiple cores. Windows 95 used "sequential threads". Eventually we got "hyper-threading" which was indeed threads running simultaneously, and then ubiquitous multicore computing.


I feel like you are mixing up threads and cores.

In win95 there is preemption. The kernel can swap out threads on a timer interrupt to create the illusion despite no actual multiprocessing. You don't need real multicore to have that. But the programming model is the same as if you had it.

Elsewhere on this thread (uhh the other kind) it sounds like the threads here are not preemptible. So cooperative multitasking.


Did you mean to respond to me? In a sibling to my original comment it looks like theclaw cites a page that implies the scheduler handles the "yield process" and does preempt running threads. Windows 3.1 used cooperative multitasking which is why a single program could "lock up" the whole system.


The programming model is not quite the same. You can get away with some things if you know only one thread is running at a time. You have to mutex a lot less for one.


I disagree. You don't know where in your code the scheduler will interrupt you, it could be any time, so if you do unsafe things to shared structures you still need the mutex, it's just less likely that you will get rescheduled at precisely the right moment to hit it.

In other words data races will still potentially cause another thread to see a data structure in an intermediate state. It will just be a rare bug instead of a potentially more common one.


The diagram showing running processes within threads reminds me of MIPS pipeline, which I know the n64 CPU wasm I've seen this article before and I didn't notice the copyright Nintendo. Was this apart of the official Nintendo api provided to developers?


I'm not sure about officially, but if you're interested in this type of thing, you should certainly check out this 'N64 Bare Metal Mips Assembly Programming' project on GitHub:

https://github.com/PeterLemon/N64


Yeah, this is the official docs, covering their official RTOSbsnd rnv. Probably still technically not legal to look at, but old enough that the relevant parties don't care anymore.


The graphics component looks so similar to OpenGL!


It should given the N64's relationship to Silicon Graphics




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: