CARD.......an explanation of how the best designs/plans get wrecked....
this is an example of a HORRIBLE code trick from a disassembly point of view. And maybe a primer on how arguments are extracted.
See if you can work out how these actually do their job...
Up until jsa sent me the CARD bin, I had a 'static' (fingerprint') matcher for args.
After all, most bins use variations of the same code to get arguments. e.g. A9L
this worked well, and this code appears often across bins.
Fixed type A9L - get 2 bytes from caller subroutine ...
Code: Select all
Get_par:
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, 3d from caller
369f: c8,3a push R3a push(R3a); # restore caller address (+2)
36a1: c8,38 push R38 push(R38); # restore this return address
Variable args A9L - get specified number of args from caller subroutine.........
Code: Select all
77c2: cc,3c pop R3c R3c = pop(); # this subroutines return address
77c4: b2,3d,3a ldb R3a,[R3c++] R3a = [R3c++]; # Get count of bytes required
77c7: cc,42 pop R42 R42 = pop(); # Get Caller's return address
77c9: b2,43,3b ldb R3b,[R42++] R3b = [R42++];
77cc: c6,17,3b stb R3b,[R16++] [R16++] = R3b; # get byte from caller
77cf: e0,3a,f7 djnz R3a,77c9 R3a--;
if (R3a != 0) goto 77c9; # Get no of bytes into dest. addr
77d2: c8,42 push R42 push(R42);
77d4: c8,3c push R3c push(R3c); # and push modded returns back.
And then their multibank equivalents - (xdt2 bin)
NB. this looks a lot more complicated but it's mostly for fiddling with banks.
This gets 4 bytes from caller subroutine.
Code: Select all
82f93: a3,20,02,36 ldw R36,[R20+2] R36 = [StackPtr+2];
82f97: a3,20,04,3a ldw R3a,[R20+4] R3a = [StackPtr+4];
82f9b: f2 pushp push(PSW);
82f9c: fa di interrupts OFF;
82f9d: 18,02,37 shrb R37,2 R37 >>= 2;
82fa0: b0,37,11 ldb R11,R37 BANK_Select = R37;
82fa3: b2,3b,36 ldb R36,[R3a++] R36 = [R3a++];
82fa6: b2,3b,37 ldb R37,[R3a++] R37 = [R3a++];
82fa9: b2,3b,38 ldb R38,[R3a++] R38 = [R3a++];
82fac: b2,3b,39 ldb R39,[R3a++] R39 = [R3a++];
82faf: b1,11,11 ldb R11,11 Data_Bank = 1;
Stack_Bank = 1;
82fb2: f3 popp PSW = pop();
82fb3: c3,20,04,3a stw R3a,[R20+4] [StackPtr+4] = R3a;
and its xdt2 variable equivalent -
NB. again a lot of fiddling to get the bank(s) right, but it does same job as 77be above
Code: Select all
8a526: a2,20,40 ldw R40,[R20] R40 = [StackPtr];
8a529: 18,02,41 shrb R41,2 R41 >>= 2;
8a52c: c4,11,41 stb R41,R11 BANK_Select = R41;
8a52f: a3,20,02,40 ldw R40,[R20+2] R40 = [StackPtr+2];
8a533: b2,41,3e ldb R3e,[R40++] R3e = [R40++];
8a536: c3,20,02,40 stw R40,[R20+2] [StackPtr+2] = R40;
8a53a: a3,20,04,40 ldw R40,[R20+4] R40 = [StackPtr+4];
8a53e: 18,02,41 shrb R41,2 R41 >>= 2;
8a541: b0,41,11 ldb R11,R41 BANK_Select = R41;
8a544: a3,20,06,46 ldw R46,[R20+6] R46 = [StackPtr+6];
8a548: b2,47,3f ldb R3f,[R46++] R3f = [R46++];
8a54b: c6,27,3f stb R3f,[R26++] [R26++] = R3f;
8a54e: e0,3e,f7 djnz R3e,8a548 R3e--;
if (R3e != 0) goto 8a548;
8a551: b1,11,11 ldb R11,11 Data_Bank = 1;
Stack_Bank = 1;
8a554: c3,20,06,46 stw R46,[R20+6] [StackPtr+6] = R46;
and then jsa sent me THIS - and the air went blue (Do you say this in USA, or
is it more like "swears like a trooper?")
446a - - - - WHAT THE F**K is THAT ??!!!! AAARRGGGHHHH !!!!!!
Code: Select all
Sub_4456:
4456: f8 clc CY = 0;
4457: 20,01 sjmp 445a goto 445a;
Sub_4459:
4459: f9 stc CY = 1;
445a: cc,18 pop R18 R18 = pop();
445c: b2,19,1a ldb R1a,[R18++] R1a = [R18++];
445f: b2,19,1b ldb R1b,[R18++] R1b = [R18++];
4462: b2,19,1c ldb R1c,[R18++] R1c = [R18++];
4465: b2,19,1d ldb R1d,[R18++] R1d = [R18++];
4468: d3,05 jnc 446f if (CY = 1) {
446a: c9,fa,40 push 40fa push(Sub_40fa);
446d: 20,02 sjmp 4471 goto 4471; }
446f: c8,18 push R18 push(R18);
4471: 91,02,2c orb R2c,2 R2c |= 2;
Sub_40f8:
40f8: cc,18 pop R18 R18 = pop();
Sub_40fa:
40fa: ae,19,14 ldzbw R14,[R18++] wR14 = y[R18++];
40fd: b2,19,17 ldb R17,[R18++] R17 = [R18++];
4100: c8,18 push R18 push(R18);
And I knew then I had to throw away all my work on the fingerprint type arg getters.
real shame as they had worked just fine up to now... I had to 'emulate' the code.
Emulate is to set up enough conditions to actually run the code as if it is for real,
but you can 'fake' it quite a lot. But I needed to have a 'fake stack' and have it
built correctly for each 'call' of subroutine, (and some more as it turned out)
Later I also found a bin (which I can't find right now, but it's saved somewhere) which
gets 2 extra bytes if top bit of first argument byte is set...)
So I had to rework a whole bunch of SAD code to do this.
(true answer is 4456 gets 4 args, 4459 gets 6 args)