Yeah for some older assembly instruction lists it is more reasonable to memorise them all. For the most part though trying to memorise everything is like trying to learn a human language by memorising its dictionary, would be an impressive feat but the one that learns spelling rules, grammar rules, everyday language and maybe their own specialist bit will probably do just fine (theoretically might even do better) if moving there tomorrow.
I do tend to break instructions into three major categories, in my case from assembly as a general concept shared between systems (this is the "basic maths, program flow, hardware fiddling" from my earlier reply) though probably defaulting to X86 for much terminology as it is more likely to be known by random other coders or have reference material.
Still whether you are using something that might scarcely be Turing complete or a modern cluster of supercomputer on a chip you still have the same three things to consider.
1) The maths. I won't say everything else is just getting to this but... it kind of is. Some things have more or less functionality than others but maths is maths. You will be adding, hopefully subtracting, multiplying, dividing, shifting, rotating, ceiling, floor, rounding, trigonometry, logarithms, doing masks, boolean logic, and maybe doing it on specialist data types or data sizes or masses of data in the case. Basically most systems by their processor, bios or otherwise will effect most of your average spreadsheet https://help.libreoffice.org/Calc/Mathematical_Functions
but maybe limited to whole numbers, whole numbers of a given bit size, floats of various precisions or have to recreate that in software.
You might look to look up the basic "everything is adding" concept that is often taught to new electrical engineers or computer science types.
Adding is adding, subtraction is just another type of adding, multiplication is just a short form of adding, division is subtraction if you take it to logarithms, https://www.chilimath.com/lessons/advanced-algebra/logarithm-rules/
, and again that is a form of adding), shifting is short form multiplication/division, rotating is but a form of shifting...
2) Program flow. Maths is easy enough to do, it is the ability of computers to make choices depending upon previous conditions that make things interesting.
Branching, goto, jumps...
Some will compare it to higher level languages and loops in those where IF, ELSE, WHILE and so forth are the order of the day https://www.learnpython.org/en/Loops
. Hence the if equal, if not equal, if greater than, if less than and maybe stacking a few up to get a if between this range, blindly jump back to here... type instructions.
Add in interrupts (basically checking to see if something has happened every go around/few instructions in a program is tedious, machine makers soon realised that the ability to have something external reach in and say "do this now" is quite useful) and you have this.
3) Hardware fiddling.
CPUs have registers which are small sections of memory that operate very fast, benefit from being able to reach out and touch memory elsewhere in the system (though there are also tools that spare the CPU having to do this, this is DMA aka direct memory access) and occasionally need to reset things back to a baseline. Space is not infinite at this level by any means so new space constantly needs to be made, old things put to the side for a moment or otherwise fiddled with.
To that end I usually learn whatever a given CPU's equivalent of MOV is (the instruction that copies data from one register to another, possibly copies to memory, and possibly is used to insert a simple number, aka an immediate, into a register so you can go on from there, sometimes these are separate concepts), whatever you need to do to fiddle with memory, whatever the push and pop is, whatever the NOP (no-operation, not necessary a dedicated instruction but there will usually be a convention used in a given game/assembler/system/style of an instruction that does nothing but waste a bit of time) and whatever is needed to reset flags (if the compare used in program flow does not immediately cause a branch to somewhere else it will tend to first set a flag in the CPU to say compare not equal that the instruction that actually does branch will check first and go from there, more for negative result, errors and whatever else).
Hamming code, linked lists, https://docs.google.com/document/d/1iNSQIyNpVGHeak6isbP6AHdHD50gs8MNXF1GCf08efg/pub?embedded=true
, what checksums are and why you care about them, https://ece.uwaterloo.ca/~ece611/LempelZiv.pdf
(warning, rather advanced and far from necessary but might provide some insights) and much more* might serve to make you a more rounded coder able to recognise memory structures, programming ideas and more but building something from the ground up because you knew no better still leaves you understanding something at the end, possibly even more so than someone trying to fit it to preconceived notions of how things normally work.
*I often use the example of stats in games. Typically it will be one character with one stat after the other, or it will be one stat type after the other. Think how the naive stats thing would be done in a spreadsheet with character and stats in columns going, one of those is taking a column at a time and the other is a row at a time. Now what happens when you have generated stats (HP = 4xdef+2x constitution + 5x level number...) or maybe some kind of weird matrix maths. Items also have two main formats, that being each place on this table refers to how many you have, or this item identifier has a value for how many you have, though that breaks down a bit when you get a nice modern RPG with a whole bunch of specialist bits that might as well be a character all of its own and caked in XML.
As far "writing opcodes to the ROM"
Sometimes yeah you do. Various higher level disassemblers (think something like IDA) will have it all baked in such that you can find where you want to go, tap in a valid instruction and it will overwrite just like your hex editor would a hex value or in the text window.
Other times people will make instructions, assemble them with an assembler and manually overwrite the according instructions.
Though that might be getting ahead of things.
For the most part there are two approaches for the would be hacker
1) In place.
2) You find some free space somewhere and smack the game hard enough that it goes there, does your extras and goes back to what it would have been doing "normally".
In place is nicer and more desirable if you can do it. However space is always at a premium and you might want to actually do something more fancy. Overwriting code next to what you did tends to break a game (though not always) so do that at your peril, you might optimise the code to do what it needs to do in less instructions (yours and the original) as not all devs have to be the best or foresee everything (or maybe they tried to and don't need to**).
To that end you will soon be off finding space (observations of things that never run in the game, simple blank space in a ROM, space clawed back from something else, something the dev left in that might otherwise only have been seen on https://tcrf.net/The_Cutting_Room_Floor
), overwriting an instruction (or maybe adding something to a vblank routine -- if you are hardcoding a cheat that only needs to write this area of memory once a frame...) to tell it to jump to your new code's location, have the code do all it needs to do and then jump back, return if you will, to where it needs to go.
**most programmers will soon run into bounds checking, sometimes only after something bad though https://hackaday.com/2015/10/26/killed-by-a-machine-the-therac-25/
. If the original devs included a bunch that other parts of the game ensure will never happen then arguably it is redundant. This does not happen so much on older systems like the SNES -- every instruction and every byte tends to be treated as valuable so no wasting it double checking answers when you could be having more functionality the player cares about.
I normally recommend learning to hardcode a cheat in as a first project. You can try altering opcodes (there might be more than one compared to a single memory address***), inserting something in a vblank or doing something else entirely.
***death in something like mario might be from time, mushroom, pits, enemies, hazards, crushing... and all do their own thing.
Also possibly useful: the SNESdev forum, some 15 years and counting of discussion about development of original games and such for the SNES. romhacking.net is more about hacking existing games.
snesdev/nesdev is certainly a valuable resource, and oversight of mine to not include it earlier. As far as here being about existing games then sure, however those that style themselves as the ultimate form of the craft being that of someone able to rock up to any game and make a dent in understanding how it works or make it conform to their actions. They can probably still manage the basic form of a program and introduction to the practical realities of a system.