As for the NES (and SNES too mostly), here's a basic overview. I did not write any NES games but did write an NES emulator (Graybox) and did a fair amount of rev-engineering of old carts.
As for programming language: yes, it was all assembly. Programming the NES meant working directly with hardware interrupts, DMA ports, bank switching etc. Luckily, programming the 6502 (or rather, the 2A03) is quite easy[1]:
- there are few registers: A, X and Y mainly, the latter two being usable only for indexing and iterating
- the instruction set is small and mostly straightforward
- not a lot of memory: main RAM is 2KB, with an optional battery backed 8KB extension. Of that 2KB, 256 bytes are reserved for the stack and page 0 (the first 256 bytes) was where you'd want to store your most used pointers and values because of some special addressing modes
These 3 things together make for an environment that is easy enough to memorise while working with it. Yes, you manage all memory yourself but that meant essentially that you create a full map of where everything goes up ahead and that map isn't very big because you only have to worry about 2K, so you could plot that out on a piece of graph paper. You had to plan things out a bit more and statically assign variables and constants to RAM and ROM (on cartridge) locations.
It gets a bit tricker once your cartridge data goes beyond the addressable limits of the CPU. That's 64KB, of which the lower 32KB is set in stone and mapped to all kinds of hardware ports and RAM. This is where bank-switching comes into play, which means mapping a section of the ROM into (part of) the higher 32KB address space.
This can be used however the programmer wants, but an example use might be having a game with 3 levels, with all the level data, meta data and code for each level crammed into separate 8KB memory areas on the cartridge. The level might have callbacks for e.g. initialisation, per frame update, etc. "Loading" the level would mean mapping that 8KB chunk of memory at e.g. 0xC000. You could then specify that the init routine is always at 0xC000, the frame update routine is at 0xC200 and the level data starts at 0xC800. The game's main code housed in another memory chunk then controls level changes simply by swapping in the right chunk and jumping to absolute addresses 0xC000 and 0xC200 at the appropriate times.
W.r.t. graphical data: the NES's tiles data are 2-bit 8x8 pixel maps. For the background they are combined with a 1/4 resolution 2-bit layer. These 4-bit values were then indexed into a 16-entry palette, with I believe 53 effective unique colours available. Sprites also used the 2-bit pixel data and each sprite specified its own 2-bit group index again forming a 4-bit pal index. The BG image on screen is a 32x30 array of tile index numbers.
Essentially, by having a ton of repetition and indexes into indexes you can keep data very small. Level data was often stored as vertical bars of tile indexes and because those vertical bars were re-used as well, those were indexed as well and only stored once on the cartridge. Simple data compression techniques work similarly. This allowed Mario 1 to be 32KB of data (with room to spare) and 8KB of bitmap data.
As for dev environments, I've seen some photos where people worked on some certifiably ancient computers hooked up to EEPROM burners for work. Tool-assisted debugging was not really a possibility until after the SNES age[2]. This is the main reason so many old games have "obvious" bugs in them and why things like Gameshark could do what they do; player health would always be at mem-location X, so you can force it to be 100 at all times.
If you find these things interesting I encourage you to look at e.g. http://wiki.nesdev.com/w/index.php/Nesdev_Wiki
There are quite a few programming courses for NES to be found online as well.
I hope this simplified overview gave some insight into 80s-era game development.
[1] Relatively speaking. Also I'm biased as I wrote Graybox itself in about 85% PowerPC assembly.
[2] See the making of FF6 article: http://www.edge-online.com/features/the-making-of-final-fantasy-vi/