Hello Brian, Steve,

     There is a very limited space in RAM on the TPDD1 to load the dumping program into and I needed to make it as small and usable as possible, and for me it was easier to dump to a formatted HEX output and capture that in a terminal program.

I'll poke around and try updating the up-loadable assembly program to dump the data in binary and put in some spots where a start word and length byte can be modified this weekend. And you're right, I made up a simple RS-232 cable with only RX, TX, and GND when I was working on dumping the ROM. I should have used the hardware flow control lines too, but thinking back I wasn't sure if the hardware flow control bits for the UART in the 6301 would have been set. But looking at the source now, they were. Also, at the end of my code I jump into an infinite loop, I've not played around with returning to the running firmware, but this might be possible too. That could allow for running multiple 'external' programs without power cycling the TPPD1.

I'm glad you put the TPPD2 firmware on the web too. I've been poking around the firmware that Steve supplied and there are some things that weren't making sense and I thought there might have been some dumping errors still, but your copy is exactly the same.

But now looking the the stuff that didn't make sense again, it's actually a pretty cool implementation of some code compaction tricks.

*Weird thing #1 - the array of CPX statements.*

[FD77]    [26 1D      ]    [&   ]     BNE A_FD96
[FD79]    [DE 99      ]    [    ]    A_FD79:    LDX A_99
[FD7B]    [A6 02      ]    [    ]        LDA A 0x02, X
[FD7D]    [97 89      ]    [    ]    A_FD7D:    STA A A_89
[FD7F]    [C6 B2      ]    [    ]    A_FD7F:    LDA B #0xB2
[FD81]    [F7 40 00   ]    [ @  ]        STA B (CPLD)4000
[FD84]    [71 BF 02   ]    [q   ]        AIM #0xBF, (register)PORT1Data
[FD87]    [39         ]    [9   ]    A_FD87:    RTS
[FD88]    [C6 40      ]    [ @  ]    A_FD88:    LDA B #0x40
*[FD8A]    [8C C6 4B   ]    [  K ]        CPX #0xC64B*
[FD8D]    [9E 81      ]    [    ]        LDS A_81
*[FD8F]    [8C C6 42   ]    [  B ]        CPX #0xC642
[FD92]    [8C C6 41   ]    [  A ]        CPX #0xC641
[FD95]    [8C C6 49   ]    [  I ]        CPX #0xC649
[FD98]    [8C C6 4A   ]    [  J ]        CPX #0xC64A*
[FD9B]    [D7 8E      ]    [    ]        STA B A_8E
[FD9D]    [86 FF      ]    [    ]    A_FD9D:    LDA A #0xFF
[FD9F]    [20 DC      ]    [    ]        BRA A_FD7D

These just didn't make sense at first, and also the branches and jumps pointed to the middle of the opcoode (the C6 in the middle). But this is just code compaction. What makes this cool is C6 is a load accumulator B command that takes 2 bytes. So what is really happening? Since in this part of the code we're not concerned with the index register, those commands are just superfluous.

What the code is doing is loading acc B with 0x40, 0x4B, 0x42, 0x49, or 0x4A without a lot of extra branches or jumps between opcodes. Looking at line FD77 there is a BNE (branch if not equal) to address FD96, the middle of the CPX #0xC649 opcode, which turns out to be C6 49, or LDA B #0x49. The next opcode is CPX #0xC64A, which does nothing in this code, but hides C6 AA, or LDA B #0x49 (there is other code that jumps to that). Seems a little complicated, but the author saved 5 bytes by not having 2 byte branch opcodes between each LDA in this section alone. And when you only have 4K of space, every byte counts. Especially if you want to put your name in the code, you need to do what you can!


*Weird thing #2 - Software interrupts and invalid opcodes.*

[FD73]    [3F         ]    [?   ] SWI
[FD74]    [13         ]    [    ]        INVALID

When de-compiling I often look for invalid opcodes to locate issues or data tables. What I noted was just before many of the invalid opcodes was a SWI, or software interrupt. This is cool because the software interrupt jumps to a block of code that gets the SWI address out of of the stack and gets the next byte after that address, which is the invalid or nonsensical opcodes after the SWI. I still need to dig deeper into what these are all doing, but it's a neat way to pass a parameter to a function without using a register.

*Weird thing #3 - Other standalone invalid opcodes.
*

The 6301 traps invalid opcodes, and I see that there are many standalone invalid opcodes that make no sense. But when using the TRAP, the invalid opcodes become a 1 byte call to a subroutine instead of a 3 byte call. Another form of code compaction.


So thank you Steve and Brian for helping out with this! There are some really cool things to discover in these ancient devices.


This firmware is completely different form the TPPD1, and a lot more complicated. It'll take a while to decipher and figure out the little tricks that were used to save space, and also decode the functionality. As i figure out more I'll update my GitHub and this list.


Darren Clark


On 1/24/24 19:36, Brian K. White wrote:
I'm currently parsing the returned ascii hex dump to generate the binary, but could you possibly make a version that just sends the raw binary bytes? Even better one that can take parameters or even just have a few bytes edited on the fly to dump an arbitrary address & length?

Reply via email to