I thought I had come up with a neat solution, and then came along the CARD binary and f*cked it up, yet again.
So, I will release SAD where I've got to, as it's better (I believe) then previous version, but it still doesn't
work right automatically, which was my target. It's a tough problem to solve.
Here's a little lecture as to why. (code examples provided...)
Firstly, disassembly of a binary where the opcodes are of variable lengths is tough, because once you do something wrong,
you get lost, as you don't know where the next opcode truly starts. You also can't go backwards, and have to step through
forwards for each opcode.
Secondly there is always the debate about 'static' versus 'emulate'.
Static goes through and creates a map where the code jumps or calls, and so builds a kind of tree structure.
The problem with this is that 'shortcut' and some clever code can confuse this method.
Sorting out arguments (= data) for subroutines is sometimes difficult if you don't emulate.
This is what SAD does, and it has a kind of 'fingerprint' code match to spot the tricks, which sort of works, ... but....
Emulation means that the setup must emulate every condition and event to find all the code, which creates its own problems.
How do you know the emulation has covered everything ? Also as the code execution may be dependent on values set a long way away,
you HAVE to be VERY CAREFUL about how you handle jumps and 'shortcuts', and make sure the code is emulated in the right order, which
gets tricky when there's a bunch of (possibly nested) jumps to handle
back to what happened -
As you probably now know, or are learning, the standard way to add arguments (data,parameters) to a subroutine for a single bank
binary is like THIS -
Code: Select all
Sub57:
pop R18 R18 = pop(); # get calling address
ldb R1a,[R18++] R1a = [R18++];
ldb R1b,[R18++] R1b = [R18++]; # get one word (2 bytes)
ldb R1c,[R18++] R1c = [R18++];
ldb R1d,[R18++] R1d = [R18++]; # get one word (2 bytes)
push R18 # push modified address back
Code: Select all
2a,2d, scall 41fe Sub57(4f0,9a40);
f0,04,40,9a #args
What the code above does is to add 4 to that return address, so there are four bytes of data.
A9L already has some extra 'nasty' tricks (nasty for a disassembler that is - it's perfectly good code)
here is the param getter for rolling averages
Code: Select all
3695: cc,38 pop R38 R38 = pop(); # This subroutine's return addr
3697: cc,3a pop R3a R3a = pop(); # Caller subroutine's return addr
3699: b2,3b,3c ldb R3c,[R3a++] R3c = [R3a++];
369c: b2,3b,3d ldb R3d,[R3a++] R3d = [R3a++]; # 3C = Word param from caller
369f: c8,3a push R3a push(R3a); # restore caller address (+2)
36a1: c8,38 push R38 push(R38); # restore this return address
Note that the first pop to R38 does not get changed, just PUSHed back. (R38 is THIS subr's return address).
also A9L has a JUMP to a subroutine within a subroutine ....a few A9L listings around still have this wrong, as it so happens the error doesn't
cause an invalid opcode (another little gotcha for disassembly processes)
this trick is here -
Code: Select all
Sub54:
7273: ef,79,08 call 7aef Sub71();
7276: c9,7f,72 push 727f push(727f);
7279: 28,12 scall 728d OCC_chk1(81,6,10,46); # SCCS Open Circuit Check, pars = E81,limit,bit mask(=SCCS),IO
727b: 81,06,10,46 #args
727f: 28,a4 scall 7325 Sub63();
7281: 91,10,46 orb R46,10 Scvnt = 1; # LSO output line 4 ON (Speed Control Vent)
7284: c9,97,72 push 7297 push(7297); # return from 7A76 to this address
7287: 28,06 scall 728f OCC_chk2(82,4,01,46); # DOL Open Circuit Check, E82,limit,bit mask(=DOL),IO
7289: 82,04,01,46 #args
OCC_chk1:
728d: 28,8a scall 7319 Sub62(); # SCCS controls off & stuff?
OCC_chk2:
728f: e7,e4,07 jump 7a76 goto Test_IO; # Open Circuit Check function
actually PUSHED (for returns) and arguments extracted by 7a76, but why the jump ?? Why not just CALL 7a76 ??
Frankly, although it works, it doesn't make a lot of sense to do it this way. I had to add a whole raft of code to handle this.
..continued......