ctr

DeMon48_128k

Intel 8049 (8048, 8748, 8749, 8035, 8039) / MCS-48 Hardware-Debugger & Monitor

Documentation - Chapter 3


3.0.0 MCS-48 in the 21st century

Note: Even if the texts in this chapter refer to the 8049 µC, they also apply without restriction to the types 8048, 8035, 8039, 8748, 8749, 8040, 8050! (8048, 8035, 8748 = 64 bytes internal RAM; 8040, 8050 = 256 bytes internal RAM)

When I got my hands on the 8049 microcontrollers once again in 2021, I was a bit curious as to whether there were any signs of life left in this truly archaic component and what it was like to program it. After all, the MCS-48 family was very popular from the late 1970s to the end of the 1980s.

However, a RAM should be used as program memory in order to avoid the cumbersome handling of (E)EPROMs, programming and erasing devices (where are they actually?) this time: an integrated EPROM simulator.

3.1.0 First power-up

A power supply was connected and the only on-chip hardware was the oscillator with a crystal and capacitor with *RESET=L and EA=H.

It was actually expected that only at the oscillator connections XTAL1 and XTAL2 of the µC activity would be measured, but when EA=H the MCS-48 µCs generate even in the reset-state signals at *PSEN and ALE and continuously output the address 0x000 on BUS and P2.0 to P2.3 (This is what it says in the "Intel 8-bit embedded controllers" manual from 1990: "[...]ALE and PSEN (if EA = 1) are active while in reset." Source: "Intel 8-bit embedded controllers" manual (1990); chapter "Single Component MCS48-System" / "2.12 Reset"; p.1-10, which I had obviously overlooked). Unfortunately, this thwarted the idea of being able to simply load the RAM used as program memory with data when isolated from the 8049 in the reset-state.

PSEN und ALE bei RESET=L,EA=H
Image: *PSEN and ALE with *RESET=L und EA=H

3.2.0 NOP-Loop

The simplest way to do a further test was a "NOP loop": the CPU only finds "NOP" instructions (No OPeration) in its address space. If these NOP-instructions are executed by the CPU, the incrementing of the program counter (PC), which always points to the program memory address of the next instruction to be executed, can be observed on the external address signals.

Among other things, oscillator, program counter (PC), output and demultiplexing of the address information, reading of the opcode, instruction decoder and the internal state machine have to work correctly for this.

In order not to have to program an EPROM or other device for this and since it is only the opcode for "NOP" (0x00), a 74HC541 (Octal 3-State Buffer) was used as a hard-wired instruction memory, which places the opcode on the BUS lines while *PSEN=L.

Instead of the "transparent latch" 74LS373 proposed by Intel for storing the lower 8 address bits of BUS an edge-triggered D-flip-flop with tri-state outputs was provided as well as for the lower nibble (P2.0 to P2.3) of P2, because this allows to achieve a less noisy address bus.

Since the address lines of the RAM have to be isolated from the 8049 during data loading via the interface between the personal computer and the debugger/monitor hardware, the tri-state outputs of these D-FFs were of course very welcome.

And because the bidirectional data bus of the RAM also has to be separated from the 8049 when loading data, a "Bi-Directional 8-Bit Bus Transceiver", 74HC245, was also used for the NOP loop test. *PSEN, *RD, *WR, ALE were provisionally buffered so that even in the event of accidents on these lines, the 8049 was not at risk of damage.

Daten- und Adressbus in NOP-Schleife
Image: Executing the NOP loop (D0...D7, P2.0...P2.3, ALE, *PSEN, *RESET)

NOP-Test Schaltung
Image: The circuit used for the NOP test

3.3.0 Program and data memory in a single RAM

In order to be able to use the same RAM as an external program memory and as a data memory that can be written to or read using the "MOVX @Ri,A" and "MOVX A,@Ri" instructions and the *WR and *RD signals of the µC, the *PSEN and *RD signals of the µC were linked together by an AND-gate, so the *OE line of the RAM always assumes a low level as soon as one of these signals becomes active (low).

The *PSEN signal of the µC became the 17th address line, A16, of the RAM, so that the external program memory area in the RAM extends from address 0x00000 to address 0x0FFFF and the external data memory area extends from address 0x10000 to address 0x1FFFF (without debugger and without memory-mapped IO).

The 8049 can address 4kByte with the at maximum 12 address lines under its control. The additional address bits are provided by three 4-bit D flip-flops in the MMU (Memory Management Unit) of the debugger/monitor system. This makes it possible to address up to 64kByte for each of the program- and data-memory, independently selectable under software control (see "3.6.0 DeMon48_128k Memory Organization / MMU").

3.4.0 Debug-µC

For the control of the DeMon48_128k system and as the interface to the personal computer an old, but here still as remaining stock available PIC16F874 was chosen. A first version of the debug/monitor system was based on serial communication between the Debug-µC and the rest of the hardware, but this was later changed to parallel data exchange in order to make the system much faster.

3.4.1 NOP-Loop with Debug-µC

The first test firmware for the Debug-µC had the following function:

  1. Activate the *RESET line of the 8049 (*RESET=L)
  2. Isolation of the 8049 from the address and data bus of the program memory
  3. Fill the entire 4kByte program memory with 0x00 ("NOP")
  4. Connect the 8049 to address and data bus of the program memory
  5. Release the *RESET line of the 8049 (*RESET=H)

After starting, the same signals as in the test with the 74HC541 as "program memory" could be measured on the address and data lines of the 8049, so the correct opcode for "NOP" was really read from different addresses in the RAM.

3.4.2 256Byte-Page Write with Debug-µC

This test should prove the separation between program and data areas in the RAM. To do this, the following MCS-48 program writes to a 256-byte page in the data area of the external RAM the complement of the respective address, i.e. 0xFF in address 0x00, 0xFE in address 0x01, 0xFD in address 0x02 to 0x00 in address 0xFF:

000 27 CLR A      ;A = 0x00
001 A8 MOV R0,A   ;R0 = A
002 3A OUTL P2,A  ;P2 = A (Select RAM-Page 0x00)
003 F8 MOV A,R0   ;A = R0
004 37 CPL A      ;A = NOT A
005 90 MOVX @R0,A ;External RAM Write to Address in R0
006 18 INC R0     ;R0 = R0 + 1
007 04 JMP 003    ;Jump to 0x003
008 03

If the separation of data and program memory areas does not work, the program overwrites itself, which would be detectable by the absence of low pulses on the *WR line.

256Byte Page-Write Bild1
Image: 256Byte Page Write (D0...D7, P2.0...P2.3, ALE, *PSEN, *RD, *WR)

256Byte Page-Write Bild2
Image: 256Byte Page Write (D0...D7, P2.0...P2.3, ALE, *PSEN, *RD, *WR) (Continued)

3.5.0 Single-Step Hardware

The single-step circuit implemented with the 74HC74 and 74HC00 devices is active when the *SET input of the D-FF connected to the MCS-48 µC's *SS input is at H-level (see set of drawings). If a L-level is present here, the output of the D-FF is constantly set to H-level. The H-level at the *SET input enables the L-level of the inverted ALE signal to reset the output of the D-FF to L-level, which activates the MCS-48 µC's *SS input.

The MCS-48 µC then stops executing the current instruction (in case of instructions that require 2 instruction cycles to execute, the second instruction cycle is completed) and signals the active single-step mode by setting ALE to H. During this state, for which there is no time limit on the part of the MCS-48 µC, the address of the next instruction to be executed is output on BUS and the lower half of P2.

In order to execute the next command, a rising edge at the clock input of the D-FF causes the H-level that is constantly present at the D input of the D-FF to appear at the *SS input of the MCS-48 µC. This releases the single-step logic in the MCS-48 µC, the next command is addressed and the L-level at the ALE output of the MCS-48 µC, which is used to temporarily store the lower 8 bits of the address signals, resets the D-FF, *SS is set to L-level and the system is finally back in the waiting state in which the address of the next command is output at BUS and the lower half of P2.

To exit the single step mode can be forced at any time by applying a L-level to the *SET input of the D-FF.

Single-Step Hardware
Image: Single-Step Hardware

Single-Step Signale
Image: Single-step activation by *S=H at the D-FF (last instruction consists of 2 ALE cycles)

Single-Step Signale
Image: Execution of individual instructions (1&2 cycles) by pulses at the CLK input of the D-FF

3.6.0 DeMon48_128k Memory organization / MMU

In order to fully utilize the available 128kB memory space in the static RAM of the DeMon48_128k system, the 12 bit address information provided by the MCS-48 µC's address counter and MB-FF is obviously not sufficient (2^12 = 4096dec.).

Therefore, three 74HC173 4-bit D flip-flops independently provide 4 bits for program-memory accesses and 8 bits for data-memory accesses as address signals, which use the "PROG" signal otherwise only required for the operation of the rather obsolete 8243 port expanders (16 additional I/O lines in a large (600mil) 24-pin DIL package) to create the desired state of the additional address signals under software control.

The 17th address line, A16, is provided by the MMU (Memory Management Unit) depending on the current memory configuration (see: "5.0.0 Memory configurations").

Since the provision of the additional address bits takes place under full control of a user program running on the MCS-48 µC, programs that use up to 16 4kByte banks (=64kB) of the program- and up to 256 256Byte pages (=64kB) of the data-memory area (the debugger occupies 512 bytes in the RAM) can actually be created.

3.6.1 DBR - Data Bank Register

The active 256-byte data-memory page is selected via the "DBR" (Data Bank Register), which consists of DBL for the 4 address bits A8 to A11 and DBH for the 4 address bits A12 to A15.

Data-Bank-Register Write
Image: Access to DBH via PROG and effect on A12...A15

If a 4(8)kByte memory map is configured (see "5.0.0 Memory configurations"), for example to be able to use the "PROG" signal with 8243 port expanders, DBL (A8 ... A11) can be automatically loaded with the current state of P2.0 ... P2.3 in each ALE cycle by means of the appropriate jumper configuration, so that the active 256Byte data-memory page or the I/O area is selected via P2.0 ... P2.3 ("OUTL P2,A") and the then transparent DBL register.

3.6.2 PBR - Program Bank Register

The active 4kByte program-memory bank is selected via the "PBR" (Program Bank Register). When changing the 4kB program-memory bank, 16 address bits can be manipulated simultaneously due to the MMU hardware, so that, for example, an unconditional jump from 0x08E7 to 0x53F0 is executed:

08E3 SEL MB0   ;MB-FF (PC.11) = 0
08E4 MOV A,#05 ;A = 05
08E6 MOVD P6,A ;PBR = A
08E7 JMP 3F0   ;PC.0 ... PC.10 = 3F0

53F0 Next instruction

The "SEL MBx" command at the beginning of the sequence may be optional in other cases, but the sequence "MOVD/ORLD" followed by a command to manipulate the program counter ("JMP" / "CALL" / "RET") is strictly required!

MMU PBR Bankswitch Bank7 to Bank8
Image: Change from 4kB program memory bank 7 to 4kB bank 8

The MMU supports subroutine calls across 4kB bank boundaries, but the 4kB bank to which "RET" returns has to be stored, for example, in a register:

08E1 MOV R7,#00 ;R7 = 0 = current 4kB program memory bank
08E3 SEL MB0    ;MB-FF (PC.11) = 0
08E4 MOV A,#05  ;A = 05
08E6 MOVD P6,A  ;PBR = A
08E7 CALL 3F0   ;Stack@SP = PC.0 ... PC.11, PC.0 ... PC.10 = 3F0

53F0 Next instruction
[...]
542A MOV A,R7   ;A = R7
542B MOVD P6,A  ;PBR = A
542C RET        ;PC.0 ... PC.11 = Stack@SP

08E9 Next instruction

3.6.3 Using "8243" port expanders

If a hardware that uses one or more 8243 port expanders is to be tested with the debugger, the storage of the additional address bits A12 to A15 using the "PROG" signal can be switched off via a jumper (see "5.0.0 Memory configurations"). The system then behaves like an 8049 with 4kB of external program-memory and 3.75kB to 4kB (depending on whether an I/O area is enabled) of external data-memory, and the "PROG" signal is free to control the 8243 port expanders.

3.6.4 DA16 - Data Address 16

The MMU can be used to force all memory accesses of the MCS-48 µC to occur with A16=Low, regardless of whether they are program- or data-memory accesses. To do this, the signal DA16 (Data Address 16) is set to "Low" using "ORLD Pp,A" instructions. This achieves something normally impossible: the MCS-48 µC can write to its own program-memory area under software control. This enables, among other things, programs to be loaded by software that runs on the MCS-48 µC itself.

Of course, DA16 can also be set to "High" again in software, so that, for example, a program is first loaded with DA16=Low and then, during its execution with DA16=High, 64kB as program-memory and additional up to 64kB as data-memory are available again.

In addition, this switching option has transformed the Harvard-architecture of the MCS-48 µC into a true Harvard-/Von-Neumann-architecture-hybrid. It has a Von-Neumann-architecture with a shared memory for program and data when DA16=Low and a Harvard architecture with two logically separate memories for program and data when DA16=High.

3.6.5 Expansion Register

In addition to the PBR and DBR, a 4-bit "Expansion Register" / "EXP" is connected to the BCR (Bank Control Register) interface of the MMU.

The "DIS_IO" bit is used to enable (DIS_IO=1) and disable (DIS_IO=0) a memory-mapped I/O area in the address space under software control (see "3.6.8 Memory-Mapped I/O").

BitFunction
3DIS_IO
2None
1None
0None

3.6.6 Accessing the MMU

DA16 = H, A16 = normal :

MOVD P4,ALoad Data Bank Register Low Nibble (DBL : A8 ... A11)
MOVD P5,ALoad Data Bank Register High Nibble (DBH : A12 ... A15)
MOVD P6,ALoad Program Bank Register (PBR : A12 ... A15)
MOVD P7,ALoad Expansion Register

DA16 = L, A16 = forced low :

ORLD P4,ALoad Data Bank Register Low Nibble (DBL : A8 ... A11)
ORLD P5,ALoad Data Bank Register High Nibble (DBH : A12 ... A15)
ORLD P6,ALoad Program Bank Register (PBR : A12 ... A15)
ORLD P7,ALoad Expansion Register

3.6.7 Composition of the address in different situations

Access to program memory:

A16A15A14A13A12A11A10A9A8A7A6A5A4A3A2A1A0
MM16PBR.3PBR.2PBR.1PBR.0PC.11PC.10PC.9PC.8PC.7PC.6PC.5PC.4PC.3PC.2PC.1PC.0

Access to data memory and I/O:

A16A15A14A13A12A11A10A9A8A7A6A5A4A3A2A1A0
MM16DBH.3DBH.2DBH.1DBH.0DBL.3DBL.2DBL.1DBL.0Ri.7Ri.6Ri.5Ri.4Ri.3Ri.2Ri.1Ri.0

Access to the debugger's program and data memory:

A16A15A14A13A12A11A10A9A8A7A6A5A4A3A2A1A0
"1""1""1""1""1""1""1""1"MM8PC.7PC.6PC.5PC.4PC.3PC.2PC.1PC.0

Execution of the instruction in address 0x1FE09 in the debug program memory:

A16A15A14A13A12A11A10A9A8A7A6A5A4A3A2A1A0
"1""1""1""1""1""1""1""1"MM8"0""0""0""0""1""0""0""1"

SignalFunction
MM8MMU A8 output
MM16MMU A16 output
PBRProgram Bank Register
DBLData Bank Register Low Nibble (A8 ... A11)
DBHData Bank Register High Nibble (A12 ... A15)
PCProgram Counter
RiContents of the index register (R0, R1) used in "MOVX" instructions
"1"Is held at logic "1" (+5V = H) by the Debug-µC
"0"Is kept at logical "0" (0V = L) by the Debug-µC

Address space of the basic configuration (see "5.0.0 Memory configurations"):

Start (h)Ende (h)Bezeichnung
000000FFFFMCS-48 program-memory
100001FCFFMCS-48 data-memory (external)
1FD001FDFFMemory Mapped I/O
1FE001FEFFDebug program-memory
1FF001FFFFebug data-memory

3.6.8 Memory-Mapped I/O

With the aid of an "8-bit equality comparator" 74HC688, the IO/*M signal is created from the address signals A8 to A16, which allows 256 I/O addresses to be mapped into the address space for the external data memory. External I/O units can thus be addressed like an external RAM using the "MOVX" instructions of the MCS-48 instruction set, so that 256 * 8 bits (=2048 bits) can be addressed for output and 256 * 8 bits (=2048 bits) for input. The IO/*M signal has H-level in the I/O address area and L-level when the program- or data-memory is accessed.

If the "DIS_IO" bit provided by the MMU's expansion register is set or Jmp2 is not installed, the I/O area is disabled and the whole program- and data-memory is accessible.

IO/*M Erzeugung
Image: IO/*M signal for memory-mapped I/O with DeMon48

For information on the jumpers shown, see "5.0.0 Memory Configurations".

3.7.0 Hardware-Breakpoint

The DeMon48 system currently provides one hardware breakpoint (a breakpoint RAM may be added later), which allows code to be executed without any loss of speed while the breakpoint is active.

Due to the multiplexing of DBR and PBR with the *PSEN signal, it is necessary to delay and shorten the *X_PSEN signal using two RC networks so that the point in time at which the address bus is read is shifted approximately to the middle of the original *PSEN signal.

Important: In the second cycle of the 1-byte "RET" instruction, the address following the instruction byte (0x83) is output by the µC and *PSEN is activated (see chapter 2.5.0).
Therefore, the address following the "RET" instruction must not be specified as the hardware breakpoint, because the breakpoint address will then also be reached when the "RET" instruction is executed!
If a hardware breakpoint must be put at the address following "RET", one can, for example, insert a "NOP" command after "RET".

Hardware Breakpoint Schematic
Image: Hardware-Breakpoint circuit

Hardware Breakpoint Gate Signal
Image: Delayed and shortened gate signal for the "74HC688" 8-bit comparators

3.8.0 Clock generation and input for external clock

The 11MHz clock for the MCS-48 µC is generated via a separately constructed crystal oscillator connected to a switching circuit. The circuit enables switching between two signal sources (internal oscillator and external clock, square wave, 50% duty cycle, 5Vpp) without incomplete oscillator cycles ("glitches") occurring at the clock input of the MCS-48 µC.

The debug-µC automatically connects the 11MHz oscillator to the clock input of the MCS-48 µC while the monitor routines are being executed and to the external clock input while the user program is being executed, if this is selected by the user by setting the "Select" signal to "1".

Alternatively, the "Select" signal can be used to constantly connect the 11MHz oscillator to the clock input of the MCS-48 µC by setting it to "0".

The timing of the Debug-µC when executing the MCS-48 user program in single-step mode is adjusted to the frequency entered in the GUI of the then selected clock source.

If the option of using an external clock is not required, the separate crystal oscillator and the switching circuit can be omitted and an 11 MHz crystal with capacitor be connected directly to the oscillator terminals of the MCS-48 µC (see "2.3.0 Minimal system with external program memory").

Oscillator And Glitchless Clock Switch
Image: Clock generation and glitchless switching