If you have a wired up control setup then throwing wires around might well be the easier option (you can always add another switch to turn things on or off, think like you might see for turbo controls), and if it is emulated then it too should have options.
Still. Yeah it is possible. MAME covers a wide variety of systems with different approaches to the world but the same principles apply whether you are hacking a modern PC game, a 16 bit console game, a commodore 64 game or anything in between.
There are a few things to watch for and that can make life easier, though seeing them on arcade is something of a rarity, but more on that later.
Anyway most devices will have their control state transmitted to some part of memory. Hardware documentation (for which the MAME source code is what most would look to for arcade stuff, and frankly a lot of consoles too) will note where and what it means.
Some games/devices will use this state directly. This is considered bad form by many as switches are imperfect (see switch bounce) and can make for odd behaviours in game.
Other games/devices will copy this state say once per frame to another part of memory and operate from that. This means buttons only update once per frame (modern PC games dodge this by decoupling things from framerate and technically many older things will have interrupts) but it also means that things might not appear pressed one millisecond and not the next while you press the button and the physical contacts figure out what they are doing.
There are further considerations if you are making an emulator like serial (each button has its own section) vs parallel (once a frame/when requested the controller sends a packet of data containing the control info but that should matter little here and is usually more a concern of people making controller adapters, some aspects of turbo/macro controls and third party controllers.
Back on topic. We will have to cover the basics here first before the fun stuff.
Games will either have checks once per frame as to what has been pressed or will have interrupts ("when this happens do this, possibly regardless of what else is happening") and react accordingly.
You have several approaches.
1) You alter the bit in question for the button you want. Twiddle the data section covering the controller state to say something else; say bit 0 is A and bit 1 is B in the whole probably 16 bit value (up, down, left, right, start, few buttons... you need a few bits, more still if you have analogues) you make a quick thing that runs once a frame or whatever and says if bit 0 = pressed then set it to unpressed and then set bit 1 to pressed. You would probably do this with a mask (copy it, blank all the other bits, check to see if the value = pressed, adjust accordingly) so you don't have to do this for every combination of controls. Might have to add a second one if you are swapping buttons.
This will probably be game wide.
Hard to do if the system controller state is read only and the game does not debounce.
2) You alter the thing looking for a given press to look for another (or possibly an impossibility if you are about disabling things -- assuming you are not using a dance mat think left and right at the same time). Here it will typically have something that once a frame checks to see what is pressed (or maybe back to the interrupts thing) and it will check this state and act accordingly. You tell it instead to check for another state. If it is just an action within the game then great. If you are doing this to work around a broken button on your device you can't be bothered to fix then you might end up with 30 odd things to do to take care of all the menus, in game actions and whatever else, though also works to decouple actions that might be doubled up for the same button (ever wanted not to fire a fireball as fire mario when setting out running? You could do it with this approach).
I would typically first find out of if it is debounced (easy enough as in your debugger you set a break on read for the control state, press no buttons to control the game and if almost instantly after the game starts running again it says hold up this read it and copied it somewhere else then you know and can repeat this for the location it copied it too). Once there I would probably find some measure of action that the button press does -- jump in a platformer might be easy as it will change a location, at the start of the chain that led to that change in OAM or whatever it is your device calls its sprite/model location memory will start with a read of the button.
3) If the game has selectable controls then this can also change some things and make it a lot easier. Even if it is just a selection of two or three hardcoded setups rather than full mapping, or has some customisable but maybe the mapping you want is blocked ("yes I do want to put the important jump button on the N64 L shoulder button" sort of thing) the way most coders will implement this means you might dodge having to fiddle with assembly (entry level perfect first project assembly but assembly never the less) and instead can make a cheat.
If is fully mappable then unless you are seeking to make a cheat to have to dodge having to config every time or for an impossible combination (in arcades the coin slot is commonly an extra button, put some mega ability on that coin slot and you have potentially disabled the mega ability, or made it a microtransaction I guess) you probably don't need this.
Anyway with some form of mappable controls the game will have an intermediate step, possibly a completely arbitrary one even if the menu selecting things does not consider it so (if the menu already restricts things to avoid what the devs dubbed silliness then no sense adding extra checks once a frame), decoding the controls and passing it off to the game. You can attack this.
8 way hacks.
The main problem will be if the hardware does not accept multiple inputs at once. Hardware devs soon learned how silly this is but arcade might be before this point in time for some things, and software might similarly be limited (how many things have we all seen that see you have to stop moving to move another direction).
Assuming that is not the case you then get to look at what up does, what left does, what right, and what down does. You might want to then prioritise the diagonal options and might to add in such things to the controller input checking bit it does once a frame, including disabling the conventional compass points if it will try to do something later. Anyway you would do much like the normal movement does and instead have another 4 options for the diagonal (or more likely as the hardware will not have nice microswitches on the diagonals then combinations of up and left, up and right, right and down, left and down) do the same movement options but translating both left or right and up or down at the same time (whether you do this at root 2 speed or have diagonals technically be faster movement is up to you, also be wary of accidentally making a walk through walls or obstacles cheat). This is more advanced than a basic button swap but still pretty within reason for a learning to assembly hack type thing (you are operating well within the original game's general parameters).
In some cases (say the controller stick riding in a cross shaped frame with no diagonals) the game might even do something for you which you could find by flicking both directions pressed with a cheat but that is a different matter.