How to convert my old amiga 68000 game to portable c [closed]
Asked Answered
N

0

6

A long time ago, I've made some games for the Commodore Amiga. All done in 68000 assembly (So I still have the sources)

I want to port these games to modern platforms, and instead of hosting an emulator, I thought of converting the Assembly to C, and adding an SDL layer.

Any thoughts on how to tackle the 68000 -> C conversion part? (Not manually but automatically. As in a transpiler)

I was thinking of just creating a bunch of variables with similar names as the registers and then converting things like this:

 MOVE.l #23, d7

into

 MOVEL(23, d7);

The only thing I'm not sure how it would work is branches and subroutines. I think I could get it working with a very big switch statement, and having any label I could potentially jump to being a case statement.

Any thoughts? Any prior art I could draw inspiration from?

Nob answered 17/7, 2018 at 5:36 Comment(26)
The code is way too big to do it manually. No just have it automatically converted would be fine. I don't mind if it becomes crappy C. As long as it becomes portableNob
Why don't you just write an emulator?Aintab
Regarding subroutines; can't you create C functions for anything that's referenced by a BSR/JSR and translate the jumps into C function calls? For other types of branches it seems to me like goto would be easier to work with than switch-cases.Melvinmelvina
Then I need to find a binary somewhere. Find some 68000 emulator. Inherit all that code and have a huge dependency. And of course... It's less of a challenge ;^)Nob
@michael tricky part is of course that goto's only jump in the current scope. The branches might actually jump over a function, or who knows, right into one (I hope I didn't do that back then). With my idea of having a huge switch statement I could even 'emulate' the stackpointer(a7), and implement RTS by popping the last switch value and going through the switch statement again.Nob
I'd go with a 68000 emulator (google that).Pursuivant
there are open source 68k core emulators, but they may be only under GPL-like license (so it depends what's your plan with the final product)... if your code was not some black magic trickery, it may be easier to emulate the core, and put SDL layer on top of that, displaying memory state from emulator... i.e. you would create amiga emulator in the end.. hmmm... Anyway, generally disassembly asm into C usually turns to crap, especially if the assembly was optimized, but you may google if there is some 68k "decompiler" and see what the output would look, maybe would need only "weeks" of work..Rawinsonde
If you search for 68000 decompiler, you might find something that will help.Pounds
I'm sure most of your jumps other than BSR/JSR are within functions so goto + C labels should be totally fine. A few manual fixes total for the whole program might be required for the occasional jump into another function, but that's fine, you're already planning to spend some time porting the graphics output. The decompiler you cobble together does not need to be 100% automatic, as long as problems turn into won't-compile errors in C, rather than subtle gotchas. Labels within functions and if() goto will make your C look very much like asm, unlike a giant switch.Velites
Obviously the hard part of making actual separate C functions for each asm function is detecting which registers or memory locations hold args, and which are just stale. (And call-preserved vs. clobbered). If you used custom calling conventions on a per-function basis when it was convenient, this will be harder to decompile.Velites
@PeterCordes I'd just make 20 or so global variables called a0-a7 and d0-d7, sr, etc and just do method calls without any arguments. The subroutine would then just continue with the global 'register' variables. It's true-er to the source.Nob
@Nob That's not really a reason. If you choose to do so, it's because you decide not to write the emulator from scratch.Aintab
After all, compilers (without optimizations) are basically interpreters specialized for the particular input.Aintab
Your requirement is not far from writing a compiler: you have an input language (68k assembly), and you want programs written using that language to be executed on various plaforms. Assembly languages have normally a not too complex syntax, you could try to use the good old lex+yacc to parse it. After all, yacc name means Yet Another Compiler Compiler... But IMHO, it will be harder than directly using an emulator...Studdingsail
I think it boils down to the using/writing emulator, because what your original proposal is, is exactly that, emulating 68k core. But then you need to emulate lot more, as that code was originally running inside Amiga computer, so you must emulate also memory and the peripherals chips like blitter/copper/disk-system/etc.. (depending what your games did use), and for any top quality Amiga game you would very soon end with complete Amiga emulator, probably even being picky about timing of the emulation... so maybe you should first check if there's something already done which can be reused?Rawinsonde
Yeah global vars for registers should work, nice idea! You can manually clean up a few important function (important for performance or readability), or ones used in the code you're modifying anyway. Link-time / whole-program optimization by modern compilers might be able to do a decent job optimizing away useless spill/reload of the globals around every function call, and at the end of every function when compiling nasty source that assigns them. For regs you never use for arg passing / return, you could have your decompiler use local vars.Velites
Hopefully you can map stack-allocated locals in your asm source to local vars with automatic storage in the C output. You'd really like sp to not be part of the C program's state, with all stack ops turned into local variable allocation, or alloca or C99 VLAs for variable-size subtractions. But C can't directly express the pattern of pushing items on the stack one at a time, then popping them off in reverse order. (i.e. using the call-stack as a stack data structure, which is easy in asm.)Velites
At worst you can model a stack frame (within each function separately) as a char array that you memcpy to/from if you have unaligned or overlapping accesses. This assumes your asm is not totally evil and doesn't do stuff like modifying the parent stack frame directly, relative to SP without having the caller passed a pointer.Velites
Here is a crazy idea. Make every opcode a function that modifies global variables. and uses continuation-passing-style. That makes it possible to implement BSR/JSR and RET in C.Kanpur
Seriously this comment thread shows why this stack needs to start enforcing the policy about answers in comments. No, your half-answer is not a comment. Either write it when it's done, or find a discussion forum.Dingess
How many lines of code are we talking about?Abate
@Dingess Generally, such cases indicates that something is wrong with the question. Indeed it is. To fix that is also quite easy -- post a (CW if you want) answer.Aintab
@Aintab We already know something is wrong with the question - it's even put on hold. Still there's a lot of "helpful" people who just can't stop themselves from filling up the comment section with their opinions. Some of them with an surprisingly high reputation, showing they should have been here long enough to know the policy.Dingess
I did something similar for a decompression code: jotd.pagesperso-orange.fr/misc/fimp_c.zip. Source code included (except for the awk conversion script)Midstream
I did something similar (SDL) but not the same processor (6502) to make old games that I didn't even write run: jotd.pagesperso-orange.fr/oric_remakes.html. Full source code of everything here.Midstream
Great! This is exactly how I envisioned it. Great that the original basic lines are in the comments so you can actually follow quite nicely even though the C is at times cryptic.Nob

© 2022 - 2024 — McMap. All rights reserved.