- There is no hardware abstraction layer. There is one hardware target, you know exactly how it works, and you can use/abuse it any way you like. And all the hardware is designed together for the sole purpose of making games.
- There is no I/O. All your code and data is just mapped into memory from a ROM pack. You have to swap memory banks in and out, but you can layout the banks explicitly. You typically do one level per bank and just swap banks to change levels.
- There is no memory management. RAM is all yours. You just layout your dynamic data explicitly, or let the assembler do it, or some combination of the two.
- There are no data types or undefined language behavior. Just n-bit registers and instructions. Need to use 12.4 fixed point math? Or pack a flag in the high bit of a pointer? No problem, just write some assembler macros.
Of course, in exchange for control, you have to give up some power. Maximum 16k of code, can only write to video memory between rescans, no divide instruction.. that kind of thing. But better the devil you know. It's a lot more fun solving "how can I do this with what I have?" than "what the hell is going on?"
-You think you know how it works. But lots of little hardware quirks will still bite you in the ass.
-This means that all your data has to fit in your code space, so you either trim code or you trim data.
-Generally you wouldn't do dynamic data at all, ever, so instead you were stuck constantly balancing and tweaking constant arrays of data and balancing them against everything else in the world. Do I allow 3 entries for explosion effects? Or 5? Am I willing to have one less enemy on screen for those extra effects?
-There's also often very few registers and an extremely limited stack.
I mean, I'm not saying you are wrong, and from certain angles sure it's easier to write in 8 bit assembler vs modern day high level languages. But there's also things to consider that happen above the language. What happens when there's a bug in some code and you have to hand it to a programmer who isn't familiar with that code? What happens when two subroutines are too far away from each other to jump? What happens when you get graphic corruption bugs and don't have the modern conveniences of data breakpoints and other tools?