9s12SerialMonitor


CREDITS

These notes were sent to me by Eric Engler, in discussion of BinLoad. For more information, please see his website.

Notes on the 9s12 Serial Monitor (AN2548)

Before I get started here, I want to say that I wrote these notes over a peiod of months while developing my Pluto debugger. None of this material has been reviewed by Freescale, and there may be errors. If anyone notifies me of errors I'll update this document accordingly.

This document is a series of notes, and doesn't always have the best logical organization. But I wanted to get this information out to help others, so I'll work more on the organization over time.

A Bit of History

The Serial Monitor was developed by Motorola/Freescale as a follow-up
to the very popular D-bug12 monitor. D-bug12 itself was a follow-up to
Buffalo on the 68hc11 family. Both Buffalo and D-bug12 are text mode
monitors that watch the serial port when they are in the active state,
and they execute various debugging commands typed into a Terminal
program on the PC. These monitors were extremely popular for many
years because they only require a serial cable connected to an
Evaluation Board (EVB).

I wrote two open source IDEs that interact directly with D-bug12:
AsmIDE for assembler programs, and EmbeddedGNU for gcc programs. Both
of these IDEs can execute D-bug12 commands on behalf of users, and
also support an integrated terminal window for users to interact with
D-bug12 directly.

I should mention that there are two primary ways D-bug12 can be used:
the mode I've already described is often called the EVB mode (where
it runs on a single target board that connects to a PC using a serial
cable), and the "pod" mode where it connects to a PC using a serial
cable on one side, and connects to a different target board using a
BDM cable. In the "pod" mode you have a much stronger debugging
environment, but it requires two separate boards, a BDM cable, and
a serial cable. Since it's generally harder to set up this kind of
environment, many users only use D-bug12 in the EVB mode.

Although D-bug12 is extremely cool in many ways, there are some
disadvantages with the approach taken by D-bug12:

   1) it uses a lot of flash space (due in part to being coded in
      C in a non-optimal coding style, and also to the overhead
      needed to support a friendly text interface)

   2) in EVB mode it offers poor support for breakpoints

   3) in EVB mode the user can't examine registers or memory while
      a program is running. The monitor is completely dormant while
      the user program is running.

   4) in EVB mode, it could start a program, but not stop it from
      running, which meant that the user often had to press the reset
      button on the EVB to stop the program, which would also clobber
      the registers, making it hard to determine where the program
      may have been stuck in a loop.

   5) it always defaults to a slow 9600 baud rate following a reset

So, the engineers at Motorola/Freescale embarked on a new quest to
devise a more powerful serial debugging monitor, that would offer
many of the advantages of a BDM. Further, this does not require two
boards or a BDM cable, and is also much smaller so it uses less
flash on the MCU, thereby allowing a user program to have the vast
majority of flash. Their end result was a very efficient monitor
that only requires 2K of flash, and offers much better debugging
support than D-bug12 in EVB mode.

Although this new Serial Sonitor is very compact and powerful, two
things had to go: there is no support for text style debugging using
a terminal program, and there is no support for real BDM debugging
using a pod mode. Therefore, you must use a custom program on your
PC that understands how to interact with the binary interface to
the Serial Monitor.

The first PC program that was widely used with the Serial Monitor is
uBug12, developed by Alex Harvey of Technological Arts. This program
was designed to offer users a similar text mode interface as that of
D-bug12, so it was easy for D-bug12 users to use this program. But
uBug12 was still not as easy to use as modern visual debuggers, so
I developed Pluto to fill that gap.

This document discusses the Serial Monitor in the context of uBug12
and Pluto. Most of the times when I talk about a specific command,
I'm referring to a low-level Serial Monitor command.

Both uBug12 and Pluto are oriented towards assembly-language
debugging.

Intro

The Serial Monitor resides at $F800 - $FFFF. This upper 2K block is
protected. The only way to destroy it is to use a BDM. This is good
because you can't mess it up easily!

The user's vector table is typically given a start addr of $FF80 in his
assembler source code, and the serial monitor automatically scales those
vector addresses so it can store them in the Pseudo vector table at $F780,
which is just below the 2K serial monitor.

You can look at the Pseudo Vector reset vector to check the starting addr
of the program. The command is "md f7fe" to show a memory dump of the
pseudo vector address for reset (used at power up or reset). If the value
at $F7FE is $4000, the user program will start execution at $4000.

In addition to the pseudo vector table in user flash memory, the serial
monitor also maintains a set of user registers in RAM. These hold the
values of all registers as they were last used whenever the serial
monitor takes active control of the MCU. And whenever the serial monitor
returns control to the user program it first restores those register
values. Therefore, it appears to the user that the serial monitor never
really modifies the registers - they always appear to have the user's
values. While in the active serial monitor mode, if you issue the
ReadRegisters command (RD in uBug12) it will return the values that were
saved in RAM. Likewise, if you modify the registers it will modify the
saved values in RAM.

The serial monitor reset command looks at this pseudo table reset vector
and, if it's been programmed by the user, it sets the user's PC addr to
that value. Therfore, a Go command will start with that address.

The Serial Monitor Go command doesn't take an optional address - it only
begins execution at the address that's currently stored in the user's PC
register. As a user convenience, uBug12 takes any address the user might
specify after a Go command, and it puts it in the user's PC before
executing the low-level Go command.

Boot vs Run Modes

Some people claim it's difficult to startup in the monitor mode following
a powerup. The serial monitor owns the real MCU reset vector, so it always
begins execution at powerup, and then it performs several tests to
determine if the user wants it to go into the active monitor mode, or if
the user wants his program to be immediately executed without
interference from the serial monitor.

Most C32 development boards use a switch that has 2 positions: Boot and
Run. Wytec calls the Boot position "Load" because that position is used
to program the flash. Either way, "Boot" and "Load" mean the same thing.

This switch is connected to one of the MCU pins, and this is checked by
the serial monitor following a powerup. This is a simple and reliable
method to force the active montor mode following a powerup or reset.
However, some users don't want to dedicate a whole pin to this purpose
(the C32 doesn't have many pins in the first place), so they use other
methods to force the monitor mode to be used following powerup.

NoICE documentation correctly says if you do not program a reset vector
it will be force a start in the monitor mode after a powerup, so they
internally store the addr the user specified for the reset vector (maybe
they put it in the registry or an INI file), and they set the PC to that
addr before they issue the Go command. They can then avoid programming it
into the chip's pseudo-vector table, and if the reset vector is not
programmed (following flash erasure), then the device will always start in
the active monitor mode, since there's no other address it can start with.

Pluto also lets you specify the startup PC location so you don't need to
program a reset vector, but I don't care for this approach in general.

In addition to the reset vector handling mentioned above, the NoICE
debugger will also issue a serial break that lasts 200 ms after a
Reset command. This is said to force the serial monitor to go active,
presumably even if there was a reset vector programmed. In C#, this
can be done like this (you should probably disable any background COM
port activity while the break is active):

   m_SerialMonitor.BreakState = true;
   sleep(200);
   m_SerialMonitor.BreakState = false;

However, there's no mention in AN2548, or the SerMon source, about the
ability of the SerMon to understand a break sent by the PC to the board,
so Pluto doesn't do this.

RAM Mapping and Startup Code

   1. On power up and reset, many 9s12 devices map RAM starting
      at $2000. This includes a reset while debugging with BDM.

   2. However, if the serial monitor is in "boot" mode (and therefore
      active) upon reset, it always maps RAM to end at $3fff, and it
      goes down from there (so the SP always starts at $4000).

It's wise if the first couple instructions of your code pointed-to by
your reset vector first maps RAM to the same address used by the Serial
Monitor in boot mode, and then sets the SP to $4000, and then sets the
PLL to run the MCU at 24 Mhz. If you following this methodology your
program should run fine whether the switch is in the Boot or Run mode.

Serial Monitor Limitations

The serial monitor has a couple oddities that make it difficult to work
with. Among these are the goofy reset procedures, requirement of using a
fixed ultra-fast baud rate without any kind of checksums to protect
against noise, the lack of structured return frames that identify the
command that is being answered (essential for async operations where
there might be several pending commands: like ReadRegisters and Return
from Breakpoint), and the lack of framed packets to pass user input and
output (to allow user I/O over the same serial port, even from monitor
mode).

Regardless of these limitations, it's a great piece of work that's
only 2K bytes in size. It gives you much of the functionality of a
BDM (start/stop, Examine memory while MCU running, program flash,
etc), and it only requires a serial cable connected to your PC.

Return From Trace/Breakpoints

One poorly documented aspect of the serial monitor is how it handles the
return from a breakpoint. It has it's own SWI interrupt handler, and it
always uses this routine for ALL SWI interrupts: these can occur from a
SWI opcode, the return from a Trace, or from a breakpoint. There's no
way to distinguish between these sources of SWI, so you have to track the
state of the machine so you know what kind of SWI occured (but the hardest
to handle is a user-inserted SWI opcode because the GUI didn't initiate it).

The serial monitor source code defines a separate status code for "trace
returned" and "break returned", but the "break returned" status is never
actually used (because the serial monitor itself can't distinguish these).

Program Flash

Before programming flash, you must always clear it first: fbulk.

To program Flash in uBug12 the command is "fload ;b" for .s19 files with
16 bit addresses, or banked S19 files. This is the most common type.
Note that as12 can only generate 16 bit addresses.

For non-banked (linear) S2 or formatted S19 (went thru SrecCVT, so the
addresses are now linear) records the command is just "fload".

As a rule, use "fload ;b" to download an .s19 file, then use "reset",
then "go 4000" to run program. As mentioned earlier, you can just "go"
if your reset vector points to your start address.

PPAGE is Considered a Register

Technically all of the I/O registers in the low memory range are called
"registers", but they're different from the internal MCU registers
because they have to be read and written using memory addresses (memory
mapped). The PPAGE register is located at address $0030, but sometimes
it's wise to think of it as though it were a high-order extension of PC
(it's not a linear extension, but it controls visibility to the page
window at $8000).

Small user programs normally don't use any kind of flash window or
paging functionality, so users can safely ignore PPAGE most of the time.
However, in a larger device than the C32, it's often necessary to use
the flash Window to give you the ability to run code located in flash
blocks that can't be mapped into the logical 64K on a fulltime basis.
Because of this, both uBug12 and Pluto display the PPAGE value along
with the "main" MCU registers. Anytime you see a PC value between
$8000 and $BFFF, you MUST examine PPAGE to determine exactly which
flash block is being addressed.

The "rd" command in ubug12 shows the register contents, and the current
PPAGE value is shown as the first register. Pluto shows PPAGE as a
register in the upper right part of the screen with the other registers.

The fload command leaves PPAGE set to whatever was used to flash the
program. A Pluto reset will always set PPAGE to $3E, and a reset is
automatically performed in Pluto following a download (Pluto's version
of fload).

RAM Breakpoints

Traditional RAM breakpoints are implemented by swapping the opcode at a
certain address with an SWI instruction (Software Interrupt). You have an
interrupt handler that gains control, and it examines the stack to see
where the break came from. It can then put the original opcode back in
place, and the user can continue execution, or examine registers and
memory, etc. D-bug12 and Buffalo use these "SWI swapping" breakpoints,
but the serial monitor was intended to work with hardware breakpoints
only.

Pluto and ubug12 don't have any kind of "load" command to load programs
into RAM for execution (they can only load programs into flash), but you
could write code to download a program into RAM using block write commands.
The best thing about running from RAM: you can write custom code to support
lots of breakpoints! Of course, you only have 2K of RAM on the C32, so this
will only be an option for very small programs - you also have to share the
RAM with the stack and data values.

To set breakpoints in RAM you have to replace user's opcode with SWI opcde.
However, once the standard SWI handler in the serial monitor detects the
SWI at runtime, it won't reset that opcode back to users original opcode
(as d-bug12 does), because it doesn't handle "opcode swapping" breakpoints.
It was only designed to work with hardware breakpoints, so you would have
to handle opcode swapping yourself if you want this functionality.
Following a break in this situation, you would have to get the address from
the stack to know which breakpoint was triggered, and then replace that SWI
with the the user's orig opcode, and adjust the stack pointer (watch out
for the way the serial monitor is using stack frames to store user
registers - you have to be real careful here), and then set the PC to point
at the opcode you just restored, and then do a Go.

However, this has a twist to it: d-bug12 (and Buffalo before it) used a
simple model of "single fire breakpoints". After a breakpoint was hit,
they backed out all breakpoints by restoring the original opcodes, and the
user had to set new breakpoints with his next Go command. This is a
difficult task for users because they are often in a loop and they want
to break again at the same addr the next time through the loop. We need
to execute the orig opcode following a Go after a break, but we also need
to put the SWI opcode back in place so we can catch the break the next
time through the loop. Therefore, after a breakpoint is hit and the user
wants to resume execution, we have to put the orig opcode back, then set
PC (and adjust the stack, of course) and do a Trace (to execute the
original opcode), and then put SWI back (and play with the stack), and
then issue a Go. It's ugly because we have to keep messing with the stack,
which is complicated by the Serial Monitor's frames.

To make a long story short, it's very difficult to handle opcode swapping
breakpoints in RAM using the serial monitor. The obvious choice is to
use hardware breakpoints only (which can be used in both flash and RAM).
This limits the user to only 2 breakpoints in older chips, or 3 in newer
chips, but it's a whole lot easier to implement!

Hardware Assisted Breakpoints

SWI swapping can't be used in readonly flash memory, so the hardware
assisted approach uses hardware comparators inside the MCU that watch the
bus for a particular address, and when found, they either transfer
control to a BDM device, or execute an internally-generated SWI
instruction. The serial monitor handles the SWI if you aren't using a
BDM.

All versions of hc12 support at least 2 hardware breakpoints, and newer
devices (like the C32) support 3.

It's interesting to note that these breakpoints will likely work in
RAM programs also (if PPAGE = 0, or 3D?). This would be an easy way to
leverage the same code to support breakpoints in either RAM or flash.
The benefit of using hardware breakpoints for RAM is that you don't
have to write code to handle SWI swapping. But you're back to a limit
of only 2 or 3 breakpoints.

Segger's Extended Hardware Assisted Breakpoints

This note isn't directly applicable to the 9s12 family, and it's really
focused at Arm devices. But I thought this idea is very cool so I'm
presenting it here to provoke thought.

The Segger company uses a way to support more than a small number of
flash breakpoints by nodifying the code generated by their C compiler.
This is most appropriate with C programs because users don't pay
attention to opcode addresses and offsets. This wouldn't work in
assembly language programs because the user's program has to know how
many bytes are used.

You can write a little code routine that gets tacked onto the users
binary load module. This determines what condition to evaluate before
breaking. After the user sets many breakpoints in the IDE and presses
the Start button, you can insert JSR MYROUTINE calls into the code
in the code-generation process of compilation. Then you re-flash the
pgm with the JSR's in place. At runtime, the little code routine
determines if a break is needed, and then calls the SWI routine
that initiates a break.

The condition and breakpoint enable status can be in RAM (but I think
Segger uses a small flash area for this - I'm not sure why?). Only the
location of the breakpoint is fixed once you flash the program, and
not the enable status or condition.

No code swapping occurs at runtime, so the readonly flash is not an
impediment. The users program will be slowed down a little, however,
even if the breakpoints are not tagged as being "active", or if the
condition is false. The "active" and "condition table" can go into
RAM, and MYROUTINE checks this. This is useful because sometimes
a user decides that he wants to avoid stopping on a breakpoint at
runtime and he wants to ignore it, while still stopping at other
breakpoints.

Reset Command

After the user presses his board's reset button, you also have to issue
a reset command from Pluto to keep them in sync. Pluto's RESET has extra
functionality to ensure this works ok.

A hard reset is initiated by the user when he presses the hardware reset
button. This looks exactly the same as an initial powerup. However, this
is NOT handled the same way as a soft reset, which is where the PC sends
a RESET command to the serial monitor.

Following a powerup/hard reset, the serial monitor sits quietly and waits
for a single CR. It uses this to validate the baud rate. However, since
it's locked into using 115K only, it does not attempt to dynamically
adjust to the PC's baud rate, so the CR seems to server no real purpose.
I strongly wish they would automatically send a specific frame of
information following powerup/reset to let Pluto know that it was reset,
and therefore the user would not have to press Reset in Pluto. This is
a common problem for users because they forget to press reset in Pluto
after hitting the hardware reset button.

Of course, when pluto starts it does a reset command to make the
initial connection to the serial monitor.

uBug12 often has a connection problem after you reset the board. You have
to "DISCON" and "CON 1" again to re-establish communications. This usually
happens when you press reset on the board. This is because uBug12 doesn't
send a CR first when it resets. In Pluto I always send a CR first, then I
pause and clear any response (which may have been "invalid command" if it
wasn't waiting for a CR), then I send a Halt and Reset command. The user
only has to press Reset in Pluto after resetting the board.

However, the user normally does NOT need to press the hardware reset
button unless things go seriously bad in the user's program. If interrupts
are enabled and you're running in Boot mode under control of the serial
monitor, the user can press the Stop button (HALT command) or Reset button
in Pluto, and this is normally fine.

However, due to the poor Async result frames, it's possible for Pluto
to become disconnected from the serial monitor in a way that requires a
hard reset.

Pluto's RESET does these:
  1) Send a CR, wait a bit, clear buffer
  2) Send a Halt, make sure it completes
  3) Send a RESET, make sure it completes
  4) TODO: Send a 200 ms break. Try testing this with the switch in the
     run position - can we break into the monitor this way?

NOTE: There's no mention in AN2548, or the SerMon source, about the
      ability of the SerMon to understand a break sent by the PC to
      the board. I don't know if there's any benefit in doing this.

While a Program is Running

You can poll the serial monitor to ask for register or memory contents
while a program is executing (just as though it were a BDM device)!
In uBug12, just use RD or MD to see what's happening as your program is
running. However, most write commands won't work while a program is
running (a memory write will work, but not a register write).

Since a memory write will work while a program is running, you can even
set a new breakpoint in uBug12 while the program is running, but it
might break at the wrong location.

Interrupts

Make sure interrupts are enabled on any line that has a breakpoint!
Technically this isn't needed for breakpoints, since the SWI is still
received and processed by the serial monitor, and it does send the
"return from trace/breakpoint" packet to the PC, but with interrupts
disabled it can't accept any more commands from the PC after that,
so your PC debugger will hang.

This also applies to Single Stepping - make sure interrupts are enabled
when you signle-step. For example, don't set a breakpoint in the routine
that sets the PLL. Pluto won't know if interrupts are enabled when a
given opcode executes, so it lets you visually set the breakpoint on any
line that looks like it holds an opcode, whether interrupts are enabled
or not. It's possible your program will just hang when that breakpoint
is encountered at runtime.  This is one of those times that a physical
hard reset is needed.

It's a good idea to keep interrupts enabled as often as you can because
that lets the serial monitor process your commands - it gets receive
interrupts from the serial port, and it processes your commands during
the handling of those interrupts. This is how they make it act like a
BDM.

Breakpoints in ubug12

uBug12 lets you define one breakpoint with the "BR" command. This does
not correspond directly to a Serial Monitor command, since the serial
monitor assumes you'll deal directly with the 9s12 hardware registers
that control breakpoints. Therefore, uBug12 sets the hardware breakpoint
registers by using Serial Monitor WriteByte commands after you tell it
you want a breakpoint via the uBug12 "BR" command.

As an example, my pgm is loaded at 0x4000 and I know 4117 is a valid
opcode address that should support a breakpoint. Also, 3E is the PPAGE
of the flash block that lives at a fixed location of $4000.

Note that when we specify PPAGE in this manner it has nothing to do
with the page window at $8000.

CON 1
RESET
br 4117 3e   (set breakpoint)
go 4000

When the program breaks you can use RD to examine the registers to see
where you are.

What bytes did uBug12 write when you gave it the "br" command?

Note: the 6812 is big-endian, so MSByte is always at the lowest addr.

1) It set address in DBGCA - register addresses $2B and $2c
   (mirror these in DBGCB - register addresses $2e and $2f)

2) It set ppage in DBGCAX - register address $2a
   (mirror in DBGCBX - register addresses $2d)

3) It set DBGC2 - register address $28 - to $80 to enable breakpoint 0
   NOTE: This uses forced breakpoints, which commonly break late.
         ubug12 should be modified to use $90 for a tagged breakpoint.

After setting a breakpoint, use "md 20" to examine the breakpoint
registers. Here's the output of the "md 20" command (after I used the
above "br 4117 3e" command):

        +0 +1 +2 +3   +4 +5 +6 +7   +8 +9 +A +B   +C +D +E +F
0020  - 00 00 00 00 - 00 00 00 00 - 80 00 3e 41 - 17 3e 41 17


Note: uBug12 currently uses forced breakpoints instead of tagged
breakpoints, so it often breaks later than expected.

Hardware Breakpoint Details

One thing seems inconsistant about flash memory addresses: you always
have to identify the PPAGE of the flash block (even if it's a fixed flash
block that is always visible in the 64K logical range), but when you
specify a specific address you always give it a logical 64K address, and
not an offset within the flash block. This is often a convenient way to
do things, but it's inconsistant because we're mixing logical and physical
addresses together to specify one breakpoint address. The more consistant
approach would be to specify a PPAGE and offset within that page, but I
agree this would make our job more nasty because the CPU executes logical
16 bit addresses only. So we have to put up with a little inconsistancy
because it really makes our job easier in the long run.

The first breakpoint module to be used in 68hc12 family devices was the
BKP module. This supports 2 hardware breakpoints. This is used in many
devices, including the popular 9s12DP256.

The new DBG module is an enhancement over the old BKP module, and it adds
support for one more breakpoint and some other advanced features. Pluto
only uses basic tagged breakpoints in the full address mode, so it doesn't
use any advanced features of the DBG aside from the additional breakpoint.
When using older devices, Pluto can only support 2 breakpoints, since
that's all the hardware can support.

Hardware registers used for breakpoints

$20 - $27 are for the new Debug modes. These registers are not used
with the old BKP module, but you must set DBGC1 to zeros to operate the
new DBG module in the legacy BKP mode (I assume zeros is the power-up
state but we should explicitly set it to zeros).

DBGC1   EQU  $20  ; N/A for BKP mode
DBGSC   EQU  $21  ; N/A for BKP mode
DBGTBH  EQU  $22  ; N/A for BKP mode
DBGTBL  EQU  $23  ; N/A for BKP mode
DBGCNT  EQU  $24  ; N/A for BKP mode

Comparator C - when the DBG module is setup to emulate the older BKP
module (which is what Pluto does), the new comparator C can be used
to provide a third breakpoint. An older device that only has the BKP
module (like the 9s12DP256) will not have Comparator C, and thus can
only support 2 breakpoints.

DBGCCX  EQU  $25  ; ppage for Comparator C, but bit 6 must = 1. Take
                  ; your brkpt target PPAGE, then "and" it with $40,
                  ; and store it here.
DBGCCH  EQU  $26  ; hi byte of addr
DBGCCL  EQU  $27  ; low byte of addr

$28 - $2F are for the legacy BKP Breakpoint mode (which Pluto is using)

BKPCT0/DBGC2   EQU  $28  ; control register
                         ; all zeros, except bits 7, 4, 3, and 2:
                         ;   bit 7 = 1, engage the BKP module to look for
                         ;              breakpoints
                         ;   bit 4 = 1, specify the tagged mode, which
                         ;              means you want to break before
                         ;              executing the opcode
                         ;   bit 3 (newer devices only)
                         ;         set to 1 to enable third breakpoint
                         ;         using comparator C
                         ;   bit 2 = 1 to specify tagged mode for comp C
                         ;
                         ; To set breakpoint C (and 0 and/or 1):
                         ;     store $9c in loc $28
                         ; To clear breakpoint C (but set 0 and/or 1):
                         ;     store $90 in loc $28


BKPCT1/DBGC3   EQU  $29  ; control register for breakpoints 0 and 1.
                         ; Uses mask bits to specify which are enabled:
                         ;     hi 2 bits are mask bits for first bkpt
                         ;     next 2 bits are mask bits for second bkpt
                         ; To set breakpoint 0 only: store $80 in loc $29
                         ; To set breakpoint 1 only: store $20 in loc $29
                         ; To set both breakpoints: store $A0 in loc $29
                         ; To clear both breakpoints: store $00 in loc $29

; Breakpoint 0 (also called A)
BKP0X/DBGCAX   EQU  $2A  ; ppage (typ 3e on C32) - called expansion addr reg
BKP0H/DBGCAH   EQU  $2B  ; hi byte of addr
BKP0L/DBGCAL   EQU  $2C  ; low byte of addr

; Breakpoint 1 (also called B)
BKP1X/DBGCBX   EQU  $2D  ; ppage (typ 3e on C32) - called expansion addr reg
BKP1H/DBGCBH   EQU  $2E  ; hi byte of addr
BKP1L/DBGCBL   EQU  $2F  ; low byte of addr

An Undocumented Breakpoint "Feature"

If you only want one breakpoint you have to code the same data in the special registers for both breakpoints 0 and 1. I haven't seen this documented in official Freescale literature, but it is necessary.

9s12C32 memory map

I wrote a separate paper on this topic, but here's a simple chart that
helps remind you of the values.

9S12C32 PPAGE values:
  3e = $4000 block of flash
  3f = $c000 block of flash

Flash window = $8000.

If you store $3e in the PPAGE register (Address $0030), then the $4000
block of flash will also be visible at $8000. This is the normal case
after a powerup in the Run mode, or a Pluto reset.

Tagged vs Forced Breakpoints

In most cases we want tagged breakpoints instead of forced breakpoints.
A taggedbreakpoint will follow an opcode thru the instruction prefetch
queue and will always break just before that instruction executes.
However, a forced breakpoint will break on the first instruction boundary
after a match occurs, which will typically be later than you wanted.

It's possible that you may have undesired effects if you try to change
a breakpoint that's already in the prefetch queue, but this is highly
unlikely with the serial monitor (might be possible with a BDM?).

Bit 4 at $28 is set to 1 for tag mode, so for one breakpoint only, store
$90 in address $28.

In addition to the tagged mode, we also want these modes (we can have
them both - they're not mutually exclusive):

  "Dual Address" mode supports 2 breakpoints - but we must give up data
  breakpoints (often called Watchpoints). If you need a data breakpoint,
  you can only use one opcode breakpoint (which means you're not in dual
  address mode).

  "Full Breakpoint" mode looks at PPAGE and the whole 16 bit address,
  not just part of it. It doesn't seem very useful to break on a large
  range of memory addresses, so Full mode normally desired.


Summary of the Pluto Breakpoint plan:

- I always reprogram all breakpoints before Start/Go

- For StepOver, I set a breakpoint on the next source code line that
  has an opcode.

  StepOver requires me to set my own breakpoint, but only one is needed,
  regardless of what the user has set. The user's breakpoints aren't
  used during StepOver (which means we won't break on any breakpoint
  he may have set inside the subroutine he is now stepping over).

  As discussed below for "Start", I have to do one SerMon Trace, and
  then set my breakpoint and do a SerMon Go.

- when user presses Start, I ensure that a max of 3 breakpoints are
  "active", and then I do one StepInto to move past the current
  address (in case we were on a breakpoint already), and stop if the
  next instruction is also at a breakpoint location. If that's not a
  breakpoint, then set all 3 breakpoints and do a SerMon Go.

  To determine the PPAGE from the 64K address (a valid procedure
  on the C32 with only 32K of flash):

    if addr < 0x4000, then PPAGE = 0 (RAM)
    else if user addr < 0xc000, then PPAGE = 3e
    else PPAGE = 3f

  For devices with > 32K of flash, I'll use the same flash banks
  (3e and 3f). As12 can only support 64K, so I can use the page
  window to make one additional 16K flash block into $8000. So I
  can support 3 blocks of 32K each. This will require some
  modification to the flash burning code, and I haven't done this
  yet.

  if no breakpoint is enabled:
    $28 = $00
    $29 = $00 (this byte probably not used if $28 = 0)

  if only 1 breakpoint is enabled:
    $28 = $90 (trigger mode)
    $29 = $80

    $2a = PPAGE (breakpoint 0)
    $2b = MSB
    $2c = LSB

  I must also copy the same above values to breakpoint 1
  (undocumented feature):

    $2d = PPAGE (breakpoint 1 - copy of 0)
    $2e = MSB
    $2f = LSB

  if only 2 breakpoints are enabled:
    $28 = $90 (trigger mode)
    $29 = $A0

    $2a = PPAGE (breakpoint 0)
    $2b = MSB
    $2c = LSB

    $2d = PPAGE (breakpoint 1)
    $2e = MSB
    $2f = LSB

  if all 3 breakpoints are enabled:
    $28 = $9C (trigger mode, and enable breakpoint C, tag mode)
    $29 = $A0

    $2a = PPAGE (breakpoint 0)
    $2b = MSB
    $2c = LSB

    $2d = PPAGE (breakpoint 1)
    $2e = MSB
    $2f = LSB

    $25 = PPAGE (breakpoint C) "and" with $40  (ppage & 0x40)
    $26 = MSB
    $27 = LSB

-------

S12BKPV1.PDF - explains the breakpoint modes. Somewhat easy to read.
               I think this applies to all 9s12 devices if used in BKP
               compatibility mode. Doesn't cover comparator C in BKP
               mode, since this is a new feature.

S12DBGV1.pdf - explains the new DBG modes, and Comparator C. Harder to
               read than the above document. This applies to the newest
               devices only, including the E128 and C32 that often come
               with a serial monitor already burned in flash.