DECODING THE COMPRESSED? OPCODE TABLE $B6C3 IN 128 ML MONITOR - Work n Progress

Started by XmikeX, January 04, 2011, 06:23 PM

Previous topic - Next topic

0 Members and 3 Guests are viewing this topic.

XmikeX

The other day, I noticed how VICE has become a liberating vehicle insofar as any taboos with ROMcode were concerned.  Just then it dawned on me that, for years, I've been dying to do some extensions for the 128's ML monitor within the ROM itself.  So, off I went, and located the monitor inside the basichi file in the VICE's C128 dir.... and behold, the legends were true, there really are 1100 bytes free in the ML Monitor ROM section.  (At this point, it is time to play with this thing.)

......but how to play with it?

Well that's an ongoing battle.  Some aspects are easy, other aspects are mysterious.

For example, while mnemonic encoding itself is easy to understand due to proper documentation, other spots seem to be poorly documented.  Just how are opcode bytes linked to that nefarious so-called OPCDTBL at $B6C3 (ala Mapping the 128) ??  Sounds like a challenge, and maybe we can have some fun with it (fun? this is fun?).
--

--Let's make OPCDTBL the focus of our efforts in this forum post.--

Looking at the raw OPCDTBL table data in memory or looping through the table with vicemon yielded little clues.  Where's the documentation?  The documentation is the assembler and calcmn routines in ROM?  That's not documentation, that's torture. =)

Alas, I jumped to the documentation, I mean disassembly, of these two key routines: "Monitor command: A (Assemble) $B406" and "$B659 CALCMN".   This is somewhat confusing and only yields a few clues for now, but this leads me to want to know how these two routines and that table work together on an opcode per opcode basis.

....about  halfway into it, I realized that the mathematics dancing around the OPCDTBL table could be nothing more than a decompression of sorts.  This encouraged me to believe that the REAL proper table (that did not have to fit in the confines of a PET.. thank you legacy code) could be realized out of this.

ASSUMING OUR DECODING IS CORRECT : The preformatted text further down illustrates the actual table and decoded table, and how it was done.
--
46787 $B6C3 OPCDTBL -- Opcode decoding table.  ( C128 ML MONITOR )
The values in this table are used by the mnemonic and mode calculation routine [$B659]
to determine the packed mnemonic table offset for the specified opcode value.

Address reference table in ROM   - 8x8 array +4
B6C3 (BE00): 40 02 45 03 d0 08 40 09
B6CB (BE08): 30 22 45 33 d0 08 40 09 
B6D3 (BE10): 40 02 45 33 d0 08 40 09 
B6DB (BE18): 40 02 45 b3 d0 08 40 09 
B6E3 (BE20): 00 22 44 33 d0 8c 44 00
B6EB (BE28): 11 22 44 33 d0 8c 44 9a
B6F3 (BE30): 10 22 44 33 d0 08 40 09
B6FB (BE38): 10 22 44 33 d0 08 40 09
B703 (BE40): 62 13 78 a9

Calculated table from address reference table - #$00-BF bytes   - 8x24 array

             40 62 40 02 62 02 45 13 45 03 13 03 d0 78 d0 08 78 08 40 a9 40 09 a9 09
             30 62 30 22 62 22 45 13 45 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09
             40 62 40 02 62 02 45 13 45 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09
             40 62 40 02 62 02 45 13 45 b3 13 b3 d0 78 d0 08 78 08 40 a9 40 09 a9 09
             00 62 00 22 62 22 44 44 33 13 33 d0 78 d0 8c 78 8c 44 a9 44 00 a9 00 11
             62 11 22 62 22 44 13 44 33 13 33 d0 78 d0 8c 78 8c 44 a9 44 9a a9 9a 10
             62 10 22 62 22 44 13 44 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09 10
             62 10 22 62 22 44 13 44 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09 10

Calculated table from address reference table + SKIPs (see below) - #$00-FF bytes   - 8x32 array

             40 62 40 xx 02 62 02 xx 45 13 45 xx 03 13 03 xx d0 78 d0 xx 08 78 08 xx 40 a9 40 xx 09 a9 09 xx
             30 62 30 xx 22 62 22 xx 45 13 45 xx 33 13 33 xx d0 78 d0 xx 08 78 08 xx 40 a9 40 xx 09 a9 09 xx
             40 62 40 xx 02 62 02 xx 45 13 45 xx 33 13 33 xx d0 78 d0 xx 08 78 08 xx 40 a9 40 xx 09 a9 09 xx
             40 62 40 xx 02 62 02 xx 45 13 45 xx b3 13 b3 xx d0 78 d0 xx 08 78 08 xx 40 a9 40 xx 09 a9 09 xx
             00 62 00 xx 22 62 22 xx 44 44 33 xx 13 33 d0 xx 78 d0 8c xx 78 8c 44 xx a9 44 00 xx a9 00 11 xx
             62 11 22 xx 62 22 44 xx 13 44 33 xx 13 33 d0 xx 78 d0 8c xx 78 8c 44 xx a9 44 9a xx a9 9a 10 xx
             62 10 22 xx 62 22 44 xx 13 44 33 xx 13 33 d0 xx 78 d0 08 xx 78 08 40 xx a9 40 09 xx a9 09 10 xx
             62 10 22 xx 62 22 44 xx 13 44 33 xx 13 33 d0 xx 78 d0 08 xx 78 08 40 xx a9 40 09 xx a9 09 10 xx

This listing below illustrates modifications/additions to the ML Monitor Rom code used to decode the table.

The process works as follows : First, you must have the ML Monitor Rom section patched with the code below.
                                                     --  VICE makes this easy : relevant file in VICE is "basichi" in C128 dir.
                                                 Do a hard reset on your 128, or power-up from purely-OFF state.
                                                 Immediately go into ML Monitor after power-up.
                                                 Type A F3000 INC $3000,X
                                                     -- (doesn't have to be F3000 or $3000 specifically, just need to assemble INC $xxxx,X (FE opcide) into ram.)
                                                 Return to monitor command prompt WITHOUT assembling or disassembling anything else.
                                                 Memory (use M 1400 14FF) from $1400 to 14FF will now have the uncompressed table,
                                                      except for the last byte which is wrong (possible bug in code).
                                                      This last byte should be replaced by a "10" (manually checked).
                                                      We don't have a valid FF opcode that the monitor's assembler will (currently) accept,
                                                      so this is as far as we can fly with it.
                                                 If you are running this under VICE, you can copy the data from x128's ram (if you're using the VIC screen),
                                                      to your favorite text editor.

Relevant Routine starts at $B659
46681 $B659 CALCMN -- Calculates mnemonic and addressing mode.

. fb66a  4c 80 be jmp $be80   ; replace lda $b6c3,x with this, in order to jump to the routine below.
                              ; the code below starts at $be80, part of the ROM freespace (1100 bytes free)
[...]
; to the best of my knowledge, a proper human-readable table-decode for $B6C3 is not available in the literature.
; .. in other words, the proper uncompressed? (aka real) table data, is not available in any text,
; so instead of asking endless questions about it (we must spare our dear C= friends' patience),
; we're just going to have to try to generate it ourselves with the following code:

. fbe80  bd 00 be lda $b6c3,x ; duplicate the instruction which used to sit at $b66a
. fbe83  8d 00 13 sta $1300   ; we need to save all pre-existing variables first, so stash A to $1300 mem
. fbe86  8e 01 13 stx $1301   ; stash X to $1301 mem
. fbe89  8c 02 13 sty $1302   ; stash Y to $1302 mem
. fbe8c  ba       tsx         ; transfer stack pointer to X
. fbe8d  8e 03 13 stx $1303   ; stash X (our stack pointer) to $1303 mem
. fbe90  08       php         ; push processor status to top of the stack
. fbe91  68       pla         ; pull the processor status from stack and into A
. fbe92  8d 04 13 sta $1304   ; stash A (our processor status) into $1304 mem
. fbe95  ae 10 13 ldx $1310   ; load X from $1310 (this will set the pointer X.
                              ; we're assuming our patch routine here is running only
                              ; after hard reset where $1310 is "00", so that our
                              ; pointer will work as intended.)
. fbe98  ad 00 13 lda $1300   ; load A (our first A which we saved to $1300.
                              ; it is generated from lda $b6c3,x which is indexing
                              ; off the original "compressed" address reference table)
. fbe9b  9d 00 14 sta $1400,x ; stash this A to $1400+index byte denoted by X
. fbe9e  ee 10 13 inc $1310   ; increment $1310 (we're using $1310 as our pointer for X)
. fbea1  ad 04 13 lda $1304   ; ok..time to return all variables as they once were and
                              ; pass control back to the monitor, load A (our processor status) from $1304
. fbea4  48       pha         ; push A (processor status) to top of stack
. fbea5  28       plp         ; pull processor status from top of stack back to its normal spot
. fbea6  ae 03 13 ldx $1303   ; load X (our stack pointer) from $1303
. fbea9  9a       txs         ; transfer X (our stack pointer) back to its normal spot
. fbeaa  ac 02 13 ldy $1302   ; load original Y from $1302
. fbead  ae 01 13 ldx $1301   ; load original X from $1301
. fbeb0  ad 00 13 lda $1300   ; load original A from $1300
. fbeb3  4c 6d b6 jmp $b66d   ; jump back to instruction immediately after .fb66a lda $b6c3,x

Very good, we have our calculated table.  It's #$BF bytes long and is easily visualized as a 8x24 array (192 values) vs. the original table at #$44 bytes in length and visualized as an 8x8(+4) array (68 values).  We also know from the documentation, I mean disassembly, that the two opcode-generating routines ("Monitor command: A (Assemble) $B406" and "$B659 CALCMN") loop with this table (and sometimes SKIP the table).  On each loop they pull the presumed opcode byte from storage (then apparently check it against the mnemonic table?), increment it with the Accumulator, and put it back in storage until they've reached the proper opcode byte necessary to be assembled.  For example, to assemble PHP (opcode #$08) the loop goes round and round always adding +1 to the opcode byte in storage until #$08 is achieved.

A bit of a warning at this point:  We just noted that the monitor routines sometimes SKIP the table at $B6C3 while looping through to the desired opcode byte.  Our calculated table may therefore be incomplete.  The SKIPs seem to occur as follows:

08                     PHP           (40 62 40 <SKIP> 02 62 02 <SKIP> 45)
10                     BPL $xxxx (40 62 40 <SKIP> 02 62 02 <SKIP> 45 13 45 <SKIP> 03 13 03 <SKIP> d0)
18                     CLC           (40 62 40 <SKIP> 02 62 02 <SKIP> 45 13 45 <SKIP> 03 13 03 <SKIP> d0 78 d0 <SKIP> 08 78 08 <SKIP> 40)

Ramblings regarding this SKIP stuff: If we add the number of table reads with SKIPs we get a total of opcode byte + #$01.  It seems as if the main assembly routines loop (opcode-value+1) times when generating the actual opcode value.  Adding the SKIPs to the 8x24 array yields a 8x32 array (with SKIPs denoted by xx, as illustrated previously).  It may be that these SKIPs represent fill bytes for a full 00-FF table.  Fortunately, this is a work in progress and we can probably apply more monitor patches, or drudge through vicemon, etc., to find out. =)

Hopefully, we're progressing on the "want to know how these two routines and that table work together on an opcode per opcode basis" stuff.

Unfortunately, we're not there yet but our new calc'ed table sheds a glimmer.  The new table is generated by our patch routine by inputting INC $xxxx,X + "10".  However, you can use the patch routine to test out any valid instruction and generate partial table data.  In other words, you can see how far the table-data extends for the instruction/opcode you input, until the main monitor routine loops back to console input.  Please note : Our patch routine duplicates the last byte generated.  This may or may not be a bug, so the final byte may or may not be disregarded.  =)

A few examples are illustrated below (without SKIPs):

Opcode value - Mnemonic  (Table extension)
00                     BRK           (40)
01                     ORA           (40 62)
                         ($zp,X)
08                     PHP           (40 62 40 02 62 02 45)
10                     BPL $xxxx (40 62 40 02 62 02 45 13 45 03 13 03 d0)
18                     CLC           (40 62 40 02 62 02 45 13 45 03 13 03 d0 78 d0 08 78 08 40)
20                     JSR $xxxx  (40 62 40 02 62 02 45 13 45 03 13 03 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                                           30)
24                     BIT $zp      (40 62 40 02 62 02 45 13 45 03 13 03 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                                            30 62 30 22)
28                     PLP            (40 62 40 02 62 02 45 13 45 03 13 03 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                                            30 62 30 22 62 22 45)
[...]

FD                     SBC           (40 62 40 02 62 02 45 13 45 03 13 03 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                         $xxxx,x       30 62 30 22 62 22 45 13 45 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                                            40 62 40 02 62 02 45 13 45 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                                            40 62 40 02 62 02 45 13 45 b3 13 b3 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                                            00 62 00 22 62 22 44 44 33 13 33 d0 78 d0 8c 78 8c 44 a9 44 00 a9 00 11
                                            62 11 22 62 22 44 13 44 33 13 33 d0 78 d0 8c 78 8c 44 a9 44 9a a9 9a 10
                                            62 10 22 62 22 44 13 44 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09 10
                                            62 10 22 62 22 44 13 44 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9)

FE                     INC            (40 62 40 02 62 02 45 13 45 03 13 03 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                         $xxxx,x       30 62 30 22 62 22 45 13 45 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                                            40 62 40 02 62 02 45 13 45 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                                            40 62 40 02 62 02 45 13 45 b3 13 b3 d0 78 d0 08 78 08 40 a9 40 09 a9 09
                                            00 62 00 22 62 22 44 44 33 13 33 d0 78 d0 8c 78 8c 44 a9 44 00 a9 00 11
                                            62 11 22 62 22 44 13 44 33 13 33 d0 78 d0 8c 78 8c 44 a9 44 9a a9 9a 10
                                            62 10 22 62 22 44 13 44 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09 10
                                            62 10 22 62 22 44 13 44 33 13 33 d0 78 d0 08 78 08 40 a9 40 09 a9 09)
---

XmX

XmikeX

Quote from: XmikeX on January 04, 2011, 06:23 PM

...mnemonic encoding itself is easy to understand due to proper documentation


To elaborate on this, Mapping the 128 does indeed describe this encoding well, as follows:

:::::::::
46086 $B406 ASSMBLE -- Handles A (assemble) command or its equivalent (.).
Checks for values following the A or period (.) and signals an error if none is found.
The period is accepted as a synonym for A to simplify the assembly process by allowing you to edit the lines displayed by the D (disassemble) command. Since the D command automatically provides the period before each line, you may not have realized that it is treated as a separate command, but you can substitute it freely for A.

If only an address is found following the command, the routine simply returns to the main loop [$B08B].
Next, the routine searches for the first group of three nonspace characters.
Any values on the input line with fewer than three characters are ignored; this explains why the two-digit hexadecimal byte values displayed in front of the three-character mnemonic by the disassemble routine are ignored when the instruction is edited. It also explains why changes to the two-digit byte values are ignored by this routine.

The three-character pattern is then packed into a two-byte value. This packing scheme is a holdover from the RAM-resident monitors of earlier Commodore computers. It's really unnecessary in the 128, which has room to spare in this block of memory, but Commodore's programmers probably found it easier to reuse the existing code.

All 8502 ML mnemonics consist of combinations of the alphabetic characters A-Z. Since there are only 26 different valid characters, any single character can be represented by a five-bit value (which can hold 0-31), and three five-bit values can fit nicely into two eight-bit bytes.

As an example of how this packing works, suppose the pattern found is LDA-corresponding to hex bytes $4C $44 $41.
First, the value 63/$3F is subtracted from each byte, yielding $OD $05 $02. The binary equivalents are %00001101 %00000101 %00000010. The rightmost five bits of each value are shifted rightward into two bytes. The resulting packed mnemonic in these locations is %01101001 %01000100, or $69 $44.
:::::::::


This is my visually-oriented take on the above description:
::::
LDA --> #$4c #$44 #$41 --> 01001100  01000100  01000001 
       -#$3F #$3F #$3F   - 00111111  00111111  00111111 
       =#$0d #$05 #$02   = 00001101  00000101  00000010 
                              ^^^^^     ^^^^^     ^^^^^ --> 01101  00101  00010
                                                        --> 011010010100010
                                                        --> 01101001.0100010 +0
                                                        ==  01101001 01000100
                                                            (#$69)   (#$44)
#$69 = first byte  -> goes into table at $B721
#$44 = second byte -> goes into table at $B761

::::

ANC is a popular mnemonic label for an undocumented instruction with typical opcode 0B in immediate mode.

ANC --> #$41 #$4e #$43 --> 01000001  01001110  01000011
       -#$3F #$3F #$3F   - 00111111  00111111  00111111
       =#$02 #$0F #$04   = 00000010  00001111  00000100
                              ^^^^^     ^^^^^     ^^^^^ --> 00010  01111  00100
                                                        --> 000100111100100
                                                        --> 00010011.1100100 +0
                                                        ==  00010011 11001000
                                                            (#$13)   (#$C8)
#$13 = first byte  -> goes into table at $B721
#$C8 = second byte -> goes into table at $B761

You can test the encoding scheme by substituting the ANC mnemonic for some other mnemonic in the mnemonic table.
Substituting ANC for BRK is the simplest choice since BRK is at the start of the table.
Writing #$13 to $B721 and #$C8 in $B761 in ROM, or "basichi" file in VICE's C128 dir, changes BRK mnemonic label to ANC.
This won't introduce an actual ANC opcode (0B is one of them) into the monitor, but all references to opcode 00 BRK
will now show up as 00 ANC.
::::

XmX

Hydrophilic

I was looking at this last night.  I don't have an answer yet about the opcode table at $b6c3.  I did generate a list of mnemonics from the tables at $b721 and $b761.  You've probably already done this, but for anyone else interested, here it is (mnemonic, index, 4 per line) :
brk php bpl clc ; $00~03
jsr plp bmi sec ; $04~07
rti pha bvc cli ; $08~0b
rts pla bvs sei ; $0c~0f
??? dey bcc tya ; $10~13
ldy tay bcs clv ; $14~17
cpy iny bne cld ; $18~1b
cpx inx beq sed ; $1c~1f
??? bit jmp jmp ; $20~23
sty ldy cpy cpx ; $24~27
txa txs tax tsx ; $28~2b
dex ??? nop ??? ; $2c~2f
asl rol lsr ror ; $30~33
stx ldx dec inc ; $34~37
ora and eor adc ; $38~3b
sta lda cmp sbc ; $3c~3f

People familiar with 6502 instruction codes should notice the mnemonics go in ascending code order.  To my surprise, there are really ??? encoded strings.  Must be for the disassembler because you can't assemble them (I tried).

More important, perhaps, is there are only 4 unused entries.  So unless you want to add only 2 or 3 opcodes, it looks like it would require a major patch.  At least there's over 1kiB to work with...
I'm kupo for kupo nuts!

XmikeX

Quote from: Hydrophilic on January 05, 2011, 06:20 PM
I was looking at this last night.  I don't have an answer yet about the opcode table at $b6c3.
People familiar with 6502 instruction codes should notice the mnemonics go in ascending code order.  To my surprise, there are really ??? encoded strings.  Must be for the disassembler because you can't assemble them (I tried).

More important, perhaps, is there are only 4 unused entries.  So unless you want to add only 2 or 3 opcodes, it looks like it would require a major patch.  At least there's over 1kiB to work with...

The expansion of the $B6C3 table (with SKIPS) was easily characterized as an 8x32 array (256).  This may suggest that the entire #$00-FF range of opcodes can be accounted for with some tweaks to currrent monitor code and table expansion elsewhere.  In fact, I'd almost bet money that adding support for undoc ops, for example, would be a very mild task to whomever knows how the monitor really works.  Unfortunately, I haven't found any documentation to support these assertions.  Since this monitor appears to be descended from a line of earlier Commodore monitors, perhaps there exists some noteworthy documentation from some PET or other CBM enthusiast of long ago. (?)

.....and yes, first thing I did was to look at the mnemonic label tables.

The preformatted text below illustrates how I laid out the mnemonic label table(s) out, for my own perusal.

As you can see from the opcode matrix i've mated to the mnemonic label table, those four zero'ed out entries are zero'ed out for good reason perhaps. =)  I believe (but not 100% sure) the entries would otherwise be assembled as "NOPs" with "different" "addressing modes".  In other words, if you add mnemonic encode bytes for <some bytes other than 00> into these blanked entries, you'll probably achieve successful assembly of these other flavors of NOP with their associated opcodes assembled into ram.  I've not tried this myself (yet) so I can't say for sure (yet). =)

The tables sit at $B721 and $B761, of course.  The B721 (BC01) and B761 (BD01) stuff is merely (original location) (relocated location in my test rom file).

XmX
--
                                                       6510 (opcodes) Instructions by Addressing Modes
                                                        Offsets   Positive                  Negative
             BRK  PHP  BPL  CLC  JSR  PLP  BMI  SEC         00      20      40      60      80      a0      c0      e0
B721 (BC01): 1c   8a   1c   23   5d   8b   1b   a1     +00  BRK     JSR                                           Implied and/or Immediate
B761 (BD01): d8   62   5a   48   26   62   94   88     +08  PHP     PLP                                           for these groups of instructions:
                                                       +10  BPL     BMI                                            B721 (BC01) - B740 (BC20)
                                                       +18  CLC     SEC                                            B761 (BD01) - B780 (BD20)
             RTI  PHA  BVC  CLI  RTS  PLA  BVS  SEI
B729 (BC09): 9d   8a   1d   23   9d   8b   1d   a1     +00                  RTI     RTS
B769 (BD09): 54   44   c8   54   68   44   e8   94     +08                  PHA     PLA
                                                       +10                  BVC     BVS
                                                       +18                  CLI     SEI
             ***  DEY  BCC  TYA  LDY  TAY  BCS  CLV
B731 (BC11): 00   29   19   ae   69   a8   19   23     +00                                  ??*     LDY
B771 (BD11): 00   b4   08   84   74   b4   28   6e     +08                                  DEY     TAY
                                                       +10                                  BCC     BCS
                                                       +18                                  TYA     CLV
             CPY  INY  BNE  CLD  CPX  INX  BEQ  SED
B739 (BC19): 24   53   1b   23   24   53   19   a1     +00                                                  CPY     CPX
B779 (BD19): 74   f4   cc   4a   72   f2   a4   8a     +08                                                  INY     INX
                                                       +10                                                  BNE     BEQ
                                                       +18                                                  CLD     SED

                                                        Offsets   Positive                  Negative
                                                            00      20      40      60      80      a0      c0      e0
             ***  BIT  JMP  JMP  STY  LDY  CPY  CPX    +04  ??*     BIT     ??*     ??*     STY     LDY     CPY     CPX     Zeropage
B741 (BC21): 00   1a   5b   5b   a5   69   24   24   ; +0c  ??*     BIT     JMP     JMP()   STY     LDY     CPY     CPX     Absolute
B781 (BD21): 00   aa   a2   a2   74   74   74   72     +14  ??*     ??*     ??*     ??*     STY     LDY     ??*     ??*     Zeropage,x
                                                       +1c  ??*     ??*     ??*     ??*     SHY*    LDY     ??*     ??*     Absolute,x

                                                        Offsets   Positive                  Negative
             TXA  TXS  TAX  TSX  DEX  ***  NOP  ***                                         80      a0      c0      e0   
B749 (BC29): ae   ae   a8   ad   29   00   7c   00   ; +0a                                  TXA     TAX     DEX     NOP     Accu/implied 
B789 (BD29): 44   68   b2   32   b2   00   22   00     +1a                                  TXS     TSX     ??*     ??*     Implied

                                                        Offsets   Positive                  Negative
             ASL  ROL  LSR  ROR  STX  LDX  DEC  INC         00      20      40      60      80      a0      c0      e0
B751 (BC31): 15   9c   6d   9c   a5   69   29   53   ; +06  ASL     ROL     LSR     ROR     STX     LDX     DEC     INC     Zeropage
B791 (BD31): 1a   1a   26   26   72   72   88   c8     +0e  ASL     ROL     LSR     ROR     STX     LDX     DEC     INC     Absolute
                                                       +0a  ASL     ROL     LSR     ROR     TXA     TAX     DEX     NOP     Accu/impl
                                                       +0e  ASL     ROL     LSR     ROR     STX     LDX     DEC     INC     Absolute
                                                       +16  ASL     ROL     LSR     ROR     STX^^   LDX^^   DEC     INC     Zeropage,x
                                                       +1e  ASL     ROL     LSR     ROR     SHX^^   LDX^^   DEC     INC     Absolute,x
                                                                                               ^^indexed using Y instead of X



Hydrophilic

I guess I wasn't being clear.  I'm not saying you couldn't have 256 opcodes, but there are only 4 unused mnemonics.  So you could add 4 mnemonics, for example, ANC, DCP, ISB, LAX (or only 3 if the disassembler needs a ? ? ? entry).  ISB and DCP have multiple addressing modes, and so does NOP.  So you could add multiple opcodes with only 3 or 4 new mnemonics, but you couldn't add all the undoc'd opcodes... not even all the useful ones.. without expanding the mnemonic table.

There's plenty of room in the ROM to put a larger table, and the code that actually decodes the mnemonics doesn't seem to depend on a table limited to 64 entries.  But until somebody knows what exactly is going with the opcode table (or rather the code using it), this is something to think about.

It seems to me there must be a good reason the mnemonic table is 64 entries long.  After all, why would you have multiple instances of JMP, CPX, LDY, and ? ? ? unless some other code (the opcode generator no doubt) required a nice binary size.
I'm kupo for kupo nuts!

XmikeX

Hydrophilic wrote : "I guess I wasn't being clear.
  I'm not saying you couldn't have 256 opcodes, but there are only 4 unused mnemonics.
  So you could add 4 mnemonics, for example, ANC, DCP, ISB, LAX (or only 3 if the disassembler needs a ? ? ? entry).
  ISB and DCP have multiple addressing modes, and so does NOP.
  So you could add multiple opcodes with only 3 or 4 new mnemonics, but you couldn't add all the undoc'd opcodes...
  not even all the useful ones.. without expanding the mnemonic table."


Expanding the mnemonic table = Yes Yes, no arguments from me.

... but let's branch off for a moment...

Assuming the position of the mnemonic label is systematically linked to the generated opcode byte,
a bit of a curiosity follows... which may or may not be significant/useful.

B721   BRK 00  opcode byte
B722   PHP 08
B723   BPL 10
B724   CLC 18
B725   JSR 20  JSR $xxxx
B726   PLP 28
B727   BMI 30
B728   SEC 38
B729   RTI 40
B72A   PHA 48
B72B   BVC 50
B72C   CLI 58
B72D   RTS 60
B72E   PLA 68
B72F   BVS 70
B730   SEI 78
B731   ??x 80  <- is expected .. but inserting "ANC" (#$13) here and (#$C8) at $B731
                            or "LXA" (#$6E) / (#$44) .. or anything else, is parsed to an assembled opcode ..value 02  (!),
                            and.... during disassembly, "ANC" (or whatever you enter) displays opcode bytes 02 -and- FF  (!!!)

Hydrophilic wrote : "...the code that actually decodes the mnemonics doesn't seem to depend on a table limited to 64 entries."

That is a possible implication with respect to the presumably-decoded $B6C3, as I hinted at earlier.
""The expansion of the $B6C3 table (with SKIPS) was easily characterized as an 8x32 array (256).
This may suggest that the entire #$00-FF range of opcodes can be accounted for with some tweaks
to currrent monitor code and table expansion elsewhere.""
--

Hydrophilic wrote : " [...] It seems to me there must be a good reason the mnemonic table is 64 entries long."

Heh Heh.  At least we know there are less than 64 valid mnemonic labels (i should hope so!), and that the mnemonic
table can be arranged to mostly-track with that opcode matrix submitted earlier. =)
--

Hydrophilic earlier wrote : " So unless you want to add only 2 or 3 opcodes, it looks like it would require a major patch. "

Assuming we're just expanding tables and adding/changing a few lines of code, perhaps it won't be regarded as anything monumental. =)   I am hoping the process is relatively straightforward, once (if) understood.

XmX

EDIT: Picture added.
EDIT2: Formatting fixed.

XmikeX

Quote from: XmikeX on January 06, 2011, 02:51 PM

I am hoping the process is relatively straightforward, once (if) understood.


If?  Did I write "if" ?

Hrmmm... scratch all that.  I believe I've come to the realization (i'm in denial) that what we're really looking at is Supermon.. sort of...  (hold on, i'm still in denial).

Ok Ok, I've been in denial since Day 1.  =)

~~~~20 seconds of google-ing here~~~~

Well Well ... here's $B6C3 again ...

                          ; supermon mode table... nybble organized
                          ; 0= err  4= implied  8= zer,x   c= zer,y
                          ; 1= imm  5= acc      9= abs,x   d= rel
                          ; 2= zer  6= (ind,x)  a= abs,y
                          ; 3= abs  7= (ind),y  b= (ind)

mode     dfb $40,$02,$45,$03
              dfb $d0,$08,$40,$09
              dfb $30,$22,$45,$33
              dfb $d0,$08,$40,$09
              dfb $40,$02,$45,$33
              dfb $d0,$08,$40,$09
              dfb $40,$02,$45,$b3
              dfb $d0,$08,$40,$09
              dfb $00,$22,$44,$33
              dfb $d0,$8c,$44,$00
              dfb $11,$22,$44,$33
              dfb $d0,$8c,$44,$9a
              dfb $10,$22,$44,$33
             dfb $d0,$08,$40,$09
             dfb $10,$22,$44,$33
             dfb $d0,$08,$40,$09
             dfb $62,$13,$78,$a9

Gotta love these C= oldtimers + CBM-influenced productions, eh?  =)
I hope 3-4 C= oldtimers here (i.e., anyone under 50 is a kid) have been laughing all along (or will claim to have been laughing, after they read this post).

Here's a sparsely commented source : http://www.ffd2.com/fridge/programs/supermon.s

There's probably tons more documentation on Supermon, of course.

XmX

BigDumbDinosaur

In my POC single board computer (W65C816S powered), I used a different approach:

;    encoded mnemonics MSB...
;
;              â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"
;      LSN â€"â€">   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
;              â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"
mnetabhb .byte $1c,$84,$24,$84,$ad,$84,$15,$84,$8a,$84,$15,$8a,$ad,$84,$15,$84 ; 0
         .byte $1c,$84,$84,$84,$ac,$84,$15,$84,$23,$84,$53,$a9,$ac,$84,$15,$84 ; 1
         .byte $5d,$13,$5d,$13,$1a,$13,$9c,$13,$8b,$13,$9c,$8b,$1a,$13,$9c,$13 ; 2
         .byte $1b,$13,$13,$13,$1a,$13,$9c,$13,$a1,$13,$29,$ad,$1a,$13,$9c,$13 ; 3
         .byte $9d,$34,$c1,$34,$75,$34,$6d,$34,$8a,$34,$6d,$8a,$5b,$34,$6d,$34 ; 4
         .byte $1d,$34,$34,$34,$75,$34,$6d,$34,$23,$34,$8a,$a9,$5b,$34,$6d,$34 ; 5
         .byte $9d,$11,$89,$11,$a5,$11,$9c,$11,$8b,$11,$9c,$9d,$5b,$11,$9c,$11 ; 6
         .byte $1d,$11,$11,$11,$a5,$11,$9c,$11,$a1,$11,$8b,$a9,$5b,$11,$9c,$11 ; 7
         .byte $1c,$a5,$1c,$a5,$a5,$a5,$a5,$a5,$29,$1a,$ae,$8a,$a5,$a5,$a5,$a5 ; 8
         .byte $19,$a5,$a5,$a5,$a5,$a5,$a5,$a5,$ae,$a5,$ae,$ae,$a5,$a5,$a5,$a5 ; 9
         .byte $69,$69,$69,$69,$69,$69,$69,$69,$a8,$69,$a8,$8b,$69,$69,$69,$69 ; A
         .byte $19,$69,$69,$69,$69,$69,$69,$69,$23,$69,$ad,$ae,$69,$69,$69,$69 ; B
         .byte $24,$23,$99,$23,$24,$23,$29,$23,$53,$23,$29,$c0,$24,$23,$29,$23 ; C
         .byte $1b,$23,$23,$23,$89,$23,$29,$23,$23,$23,$8a,$a5,$5b,$23,$29,$23 ; D
         .byte $24,$a0,$a1,$a0,$24,$a0,$53,$a0,$53,$a0,$7c,$c8,$24,$a0,$53,$a0 ; E
         .byte $19,$a0,$a0,$a0,$89,$a0,$53,$a0,$a1,$a0,$8b,$c9,$5d,$a0,$53,$a0 ; F
;              â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"
;      LSN â€"â€">   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
;              â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"
;
;
;    encoded mnemonics LSB...
;
;              â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"
;      LSN â€"â€">   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
;              â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"
mnetablb .byte $d8,$c4,$22,$c4,$06,$c4,$1a,$c4,$62,$c4,$1a,$4a,$06,$c4,$1a,$c4 ; 0
         .byte $5a,$c4,$c4,$c4,$c6,$c4,$1a,$c4,$48,$c4,$c8,$28,$c6,$c4,$1a,$c4 ; 1
         .byte $26,$ca,$1a,$ca,$aa,$ca,$1a,$ca,$62,$ca,$1a,$4a,$aa,$ca,$1a,$ca ; 2
         .byte $94,$ca,$ca,$ca,$aa,$ca,$1a,$ca,$88,$ca,$88,$08,$aa,$ca,$1a,$ca ; 3
         .byte $54,$26,$5c,$26,$e2,$26,$26,$26,$44,$26,$26,$58,$a2,$26,$26,$26 ; 4
         .byte $c8,$26,$26,$26,$de,$26,$26,$26,$54,$26,$74,$0a,$a2,$26,$26,$26 ; 5
         .byte $68,$48,$a6,$48,$76,$48,$26,$48,$44,$48,$26,$5a,$a2,$48,$26,$48 ; 6
         .byte $e8,$48,$48,$48,$76,$48,$26,$48,$94,$48,$74,$48,$a2,$48,$26,$48 ; 7
         .byte $c4,$44,$da,$44,$74,$44,$72,$44,$b4,$aa,$44,$46,$74,$44,$72,$44 ; 8
         .byte $08,$44,$44,$44,$74,$44,$72,$44,$84,$44,$68,$74,$76,$44,$76,$44 ; 9
         .byte $74,$44,$72,$44,$74,$44,$72,$44,$b4,$44,$b2,$46,$74,$44,$72,$44 ; A
         .byte $28,$44,$44,$44,$74,$44,$72,$44,$6e,$44,$32,$b2,$74,$44,$72,$44 ; B
         .byte $74,$a2,$a2,$a2,$74,$a2,$88,$a2,$f4,$a2,$b2,$94,$74,$a2,$88,$a2 ; C
         .byte $cc,$a2,$a2,$a2,$94,$a2,$88,$a2,$4a,$a2,$72,$62,$a2,$a2,$88,$a2 ; D
         .byte $72,$c8,$a2,$c8,$72,$c8,$c8,$c8,$f2,$c8,$22,$c4,$72,$c8,$c8,$c8 ; E
         .byte $a4,$c8,$c8,$c8,$84,$c8,$c8,$c8,$8a,$c8,$72,$0c,$26,$c8,$c8,$c8 ; F
;              â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"
;      LSN â€"â€">   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
;              â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"â€"


To find the mnemonic for any given opcode, use the opcode as a zero-based index into the tables.  Once the two encoded bytes have been retrieved, the same principle used with the C-128's monitor to convert the mnemonic to human-readable format can be used.

Why did I use this method?  All 256 opcodes are valid instructions with the W65C816S, making it impossible to use an opcode bit pattern based method of decoding/encoding, as was done in the C-128 monitor (which does date back in many ways to the old PET monitor).  Incidentally, a third table encodes the addressing mode and total instruction size for each opcode.  As the '816 can manipulate 8, 16, and 24 bit addresses, as well as 8 or 16 bit operands for immediate mode instructions, the assembler itself does some computation based upon the parameters provided by the user.  Complicating things a bit, the BRL (branch long) instruction accepts a 16 bit target address, unlike the BCS, BCS... group of branch instructions.  It's not as simple as with the NMOS processor.
x86?  We ain't got no x86.  We don't need no stinking x86!

XmikeX

Quote from: BigDumbDinosaur on January 07, 2011, 01:51 AM
To find the mnemonic for any given opcode, use the opcode as a zero-based index into the tables.  Once the two encoded bytes have been retrieved, the same principle used with the C-128's monitor to convert the mnemonic to human-readable format can be used.

Why did I use this method?  All 256 opcodes are valid instructions with the W65C816S, making it impossible to use an opcode bit pattern based method of decoding/encoding, as was done in the C-128 monitor (which does date back in many ways to the old PET monitor).  Incidentally, a third table encodes the addressing mode and total instruction size for each opcode. ]

Outstanding stuff, BDD.
....and tables..tables everywhere.... inescapable reality.

XmX

XmikeX

If anyone is interested in exploring the monitor-code in a more-interactive fashion within an emulated (or real) 128 environment, I've relocated Supermo^W the 128's ML Monitor into RAM.  I have it ranging from $2000- $2ffff with a few relocated tables in $2cxx-2exx.  A few bytes of BRK vector code, just before monitor is executed, is provided at $1FF0 if needed.  I've included an attachment, with monitor code and modded ROM files for VICE x128.  As described in the .README, these ROM files simply reassign some necessary vectors and the MONITOR command to the relocated locations.

XmX

Hydrophilic

Warning: Long post!

At first, I was pretty excited about the source code you found, XmikeX, but looking at it was rather disappointing... except for the tables at the end, there are virtually no comments.  Simply disassembling the ROM would tell about as much.  So that's what I decided to do.

It took a few days, but I finally finished my analysis of the ML Monitor ROM.  Now my victory over all C128 ROMs is complete (said with evil voice).  Besides finishing that project I started years ago, it was also a nice break from working on Media Player 128.

Anyway, the table at $B6C3 is just an addressing mode table and has nothing to do with the mnemonics.  The first 64 bytes hold addressing mode info for all the even-numbered opcodes, and the last 4 bytes hold addressing mode info for all the odd-numbered opcodes.  Each byte holds addressing mode info for 2 opcodes.

It should be obvious that 64 bytes * 2 codes/byte = 128 codes = all the even-numbered opcodes.

What is not obvious is the last 4 bytes work for all odd-numbered opcodes.  That is because half of the odd opcodes are illegal and get rejected.  And the other half all have the same 8 addressing modes.  4 bytes * 2 codes/byte = all 8 addressing modes.  By the way, the valid odd opcodes are ORA, AND, EOR, ADC, STA, LDA, CMP, SBC.  There is one exception, opcode $89, which would logically be STA #nn.  That would be interesting if it worked.

Also there is no such things as SKIPs.  The assemble command methodically steps through all opcodes (starting from 0) until it finds a match or the opcode-test wraps around to zero again (then your entry is invalid).

It is easy to see why somebody might think so... if you set a break point with a debugger and try examing the current opcode at certain points, you might notice the opcode number suddenly "skips" several codes.  But it is only bad debugging by you or your software.

When testing the opcodes, there are several items that need to be checked, and usually this is done by JSR $B57F.  A normal program would return the results of the test to the main assembly code.  But in the ML Monitor ROM, if the test fails, it doesn't return at all!  Instead it discards the return address on the stack, sets the next opcode in sequence, and JMPs back to the start of the loop.  These clever stack tricks lead to the illusion of SKIPs.

So anyway, here is what each byte in the $B6C3 table means.  The high nibble holds an address mode code for opcode & 2 == 0, and the low nibble for opcode & 2 == 1.  Each nibble is an index into the table at $b707 "INDCTBL".

The INDCTBL has 14 entries.  Each ones has 6 "char" bits at the top, while the 2 lowest bits tell how many operand bytes are needed for the instruction.  The "char bits" refer to characters in the tables at $B715 and $B71B, but that isn't very important right now.  What is important is the meaning of the 14 entries of INDCTBL:
 
00, no chars, 0 bytes, error (special case)
01, "#$"      1 byte,  immediate
02, "$"       1 byte,  zero page
03, "$"       2 bytes, absolute address
04, no chars, 0 bytes, implied
05, no chars, 0 bytes, accumulator
06, "($,X)"   1 byte,  indexed indirect
07, "($),Y"   1 byte,  indirect indexed
08, "$,X"     1 byte,  zero page index x
09, "$,X"     2 bytes, absolute index x
10, "$,Y"     2 bytes, absolute index y
11, "($)"     2 bytes, indirect
12, "$,Y"     1 byte,  zero page index y
13, "$,X),Y"  1 byte,  relative address (special case)


So with that in mind, let's go back to the $B6C3 table and take a look at a few examples.  Let's check out the first 8 bytes corresponding to the first 16 even-numbered opcodes.

>B6C3 40 ;BRK [imp] and ? ? ?
>B6C4 02 ;? ? ? and ASL zp
>B6C5 45 ;PHP [imp] and ASL A
>B6C6 03 ;? ? ? and ASL abs
>B6C7 D0 ;BPL rel and ? ? ?
>B6C8 08 ;? ? ? and ASL z,x
>B6C9 40 ;CLC [imp] and ? ? ?
>B6CA 09 ;? ? ? and ASL abs,x

With a little study, you should see how those bytes give you the addressing mode of each instruction.  Go ahead, take your time.

But no matter how hard you try, you can not see how the mnemonics are generated.  You probably noticed in the example that 50% are ? ? ? codes, about 25% are ASL codes, and the other 25% are some special instruction.  So how are the mnemonics determined?  Unfortunately, that info is not stored in a table but generated by evil code.

I am sure "evil code" isn't the best description.  And although I can tell you how it works (in about 1000 words, don't worry, I'll spare you), I have no idea how somebody could come up with it originally.  So I'll just post the code.

;calculate index into Compressed Mnemonic Tables
;input .Y = valid opcode
;return .A = index ($00~$3F) to tables $B721 and $B761
. FB687  98       TYA         ;recall opcode
. FB688  29 8F    AND #$8F    ;isolate some high codes
. FB68A  AA       TAX         ;and save
. FB68B  98       TYA         ;recall opcode
. FB68C  A0 03    LDY #$03    ;bits to discard (minimum)
. FB68E  E0 8A    CPX #$8A    ;special high codes ?
. FB690  F0 0B    BEQ $B69D   ;yes, next bit check (result $30~37 -> $28~2F)

;loop to find 1 bit
. FB692  4A       LSR         ;discard bit and test
. FB693  90 08    BCC $B69D   ;not 1, next bit check
. FB695  4A       LSR         ;discard 2 more bits (here and next line)

;loop to finish
. FB696  4A       LSR         ;discard bit
. FB697  09 20    ORA #$20    ;set bit for high mnemonics (table index $20~$3F)
. FB699  88       DEY         ;count bits to discard
. FB69A  D0 FA    BNE $B696   ;not all, loop to finish
. FB69C  C8       INY         ;1 to break loop below and exit (RTS would be better)

;next bit check
. FB69D  88       DEY         ;count bits to discard
. FB69E  D0 F2    BNE $B692   ;not done, loop to find 1 bit
. FB6A0  60       RTS         ;exit


So in summary, to add undocumented opcodes, you would first need to expand the mnemonic tables for new names.  Then you would need update the addressing mode nibbles in the table at $B6C3 for the even-numbered opcodes.  You would (probably) need a new (but similar) table for odd-numbered opcodes.  Most importantly, you would need to re-write the mnemonic index code above (more evil code?), or use another table.

Can I make a request?

If anybody decides to hack the ML Monitor ROM for new opcodes (or really any reason), would you please, please, PLEASE, fix that stupid 16 char limit on filenames?  This stupidity prohibits valid names like @0:LONGFILENAME.BIN

The working buffer is 32 chars, so just change 1 byte
FB36C  C0 11    CPY #$11
into
FB36C  C0 20    CPY #$20

Thanks!
I'm kupo for kupo nuts!

XmikeX

That's wonderful stuff Hydro, although you might be a little late with some of this.  Some stuff looks very interesting though.  I'll have to check it out when more coherent.

Meanwhile, a few happenings and goings-on were already self-evident shortly after that sparse Supermon disassembly came along.**  -- The "mode table" nybble revelation (as documented previously) from that sparse "source", plus one/two insights from BDD, come to mind.

Forum-wise though, it did look like as if things might have gotten a bit uninteresting (pardon ze pun).  However, since at least Hydro seems active, might as well dump whatever I've got:

http://www.commodore128.org/index.php?topic=3709.0

There. Now Hydro or one of you other superhumans can take it to the next level.  Wink. Wink. =)
Undoc op support and single-step walkthrough please.

By the way:  I wonder how many of the forum-oldtimers (the ones that eat/breathe C-128 daily) knew about the relevant disassembly inside C-128 Internals by Abacus, but decided to keep mum about it?

Real Sneaky, Fellas.  Kudos on that one. =)

XmX

**The monitor relocation exercise, which I bet no one has downloaded yet, made evident some of those so-called "clever" stack tricks.  Didn't take long to realize out why things were jumping around to the WRONG locations after encountering RTS's with no clear JSR's behind them.