Retrocomputing: MCS-48 Intro

Created: 20 Jul 2025
Updated: 20 Jan 2026

Background

I've always wanted to learn to program in Assembly. But, having looked at some of it's niches (reversing, performance programming) they didn't look too attractive to me.

That is until some years back when I watched Ben Eater's video series on "8-bit breadboard computers" and the more close to the topic of this post his "Build a 65c02-based computer from scratch". Where he builds a computer the old-school way, starting from a 6502 CPU DIP chip, with no code or Operative System to begin with. Adding software support for periphericals one at the time.

Well, I don't own a 6502. But … I managed to scavange a 8042 from an old motherboard!

mcs48-80c42-photo.jpg
Figure 1: NEC's UPD80C42

"A 8042?" I hear you say. YES!. It's a MCU (Microcontroller) (aka a CPU+ROM+RAM) with the peculiarity that it can also act as a CPU, by reading a program from an external ROM.

Hardware

MCU

The 80C42, from the Intel's MCS-48 family of chips, was made by NEC.

This variant was intended to work as a slave MCU, so many of it's pins are supposed to receive orders from it's master. But can work for our needs.

mcs48-80c42-pinout.png
Figure 2: 8042 pinout

EEPROM

Now we need a place to put our program. Since the MCU internal ROM is programable ONLY by using a special programer, that uses a different supply voltages. We are going to use an external EEPROM.

A SSTEE010-150.

sst29-pinout.jpg

Wiring

Among the few resources online, there is this one, where it describes 2 different ways to setup this chip for external EEPROM access.

  1. Connect directly the CPU and the EEPROM
  2. Connect indirectly the CPU through a 74374, a positive edge flip-flop. This gives you access to IO ports on downward SYNC cycles.

For this blog entry I will use 1)

mcs48-wiring.jpeg
Figure 3: wiring for external ROM access

Programming: Blink LED

Background

Programming, in this chip means: Writting bytecode into the EEPROM. That then the 8042 can read an interpret as instructions.

mcs48-opcodes.png
Figure 4: example opcodes, with their HEX bytecode

These are some examples of opcodes with their correspont bytecodes that i took from "MCS-48 Assembly Language Reference Card".

This means if I put 0xF8 on a EEPROM memory address, the CPU will run MOV A,R0, (aka it will "move" the accumulator value to "Register 0").

ASM

Now, we don't want to write bytecode by hand in hexadecimal, for that we will use the asxxxx assembler.

We start with some boilerplate for the assembler.

.ifdef  .__.CPU. ; if we are using as8048 this is defined
.8041
.area   CODE    (ABS)
.endif           ; .__.CPU.

CPU starts reading at address zero (0x0). We put there a jump to where is our program. Skipping some reserve addresss for other operations.

        .org 0x0
reset:
        jmp entry

Our main program startes here.

        .org 0x10
entry:
        mov  A,    #0x0A ; 00001010
        outl P2,   A
        call delay

        mov  A,    #0x15 ; 00010101
        outl P2,   A
        call delay

        jmp  entry       ; repeat main loop

Finally the definition of delay. Basically 2 nested loops with nop on it's deepest level.

delay:  mov  R0, #255   ; init outer loop counter
delay2: mov  R1, #255   ; init inner loop counter
delay1:
        nop
        nop
        nop
        nop
        djnz R1, delay1 ; dec inner count, continue if not zero
        djnz R0, delay2 ; dec outer count, continue if not zero
        ret             ; return to caller

That's it!

The ROM output

Ok, long story short, I copied this makefile elsewhere to build the .bin image I want.

default: blink.bin

# assemble with as8048
%.hex: %.asm
        as8048 -l -o $<
        aslink -i -o $(<:.asm=.rel)

# convert to bin
%.bin: %.hex
        hex2bin -p 00 -e bin $<

This outputs something like this

00000000  04 10 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000010  23 0a 3a 14 1c 23 15 3a  14 1c 04 10 b8 ff b9 ff  |#.:..#.:........|
00000020  00 00 00 00 e9 20 e8 1e  83 00 00 00 00 00 00 00  |..... ..........|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000003f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 9e  |................|
00000400

Now we could write this .rom to our EEPROM with a programmer.

Testing

aka Confirming the CPU is doing what is supposed to do

The Setup

My testing setup consists on 2 additional parts.

  • I use the SS (Single Step) input pin on the 8042. That allows me to use a momentary button to advance one instruction at the time. Instead of going at the speed of the clock. This button needs to be debounced, I used a 555 for that.
  • I use the trick Ben Eater used for his 6502 series. And that is use an Arduino as some sort of digital analyzer. I use it to monitor what is on the DATA and ADDRESS bus as I Single Step.

Failure and …

So here is the image.

$ xxd blink.rom
00000000: 1535 0410 0000 0000 0000 0000 0000 0000  .5..............
00000010: 235f 3a14 1c23 af3a 141c 0410 b955 b863  #_:..#.:.....U.c
00000020: 0000 e820 e91e 8300 0000 0000 0000 0000  ... ............
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
...

And here is some Serial debug output with the address, data in hexadecimal, and data in hexadecimal and binary.

0000: 1a 0001 1010
0001: 3a 0011 1010
0002: 08 0000 1000
0003: 10 0001 0000

We start at address 0x00, and we advance one byte at the time. That seems good.

0010: 23 0010 0011
0011: 5f 0101 1111
0012: 35 0011 0101

Woah there, we jumped to address 10 (?). Oh right, the jmp entry.

07ff: 00 0000 0000

Ok, some random (?) reading.

0013: 18 0001 1000
0014: 1c 0001 1100
001c: b6 1011 0110
001d: 5a 0101 1010
001e: b4 1011 0100
001f: 63 0110 0011

Wait something is wrong here. The second nibble of data is not always matching the expected data in memory.

For example the first reading, was:

0000: 1a

But it should have been:

0000: 15

Let's check the cables….yup..I got the wiring of this improvised sniffer wrong. Fixing…

But before that let's see a bit more.

0020: 00
0021: 00
0022: e4 1110 0100
0023: 20 0010 0000
0020: 00
0021: 00
0022: e4
0023: 20

Ok, we are looping.

Some wiring fixes and it worked blinked!.

Future

I want do more with this. I was asked: "What is the difference between the 8042 and the Z80?". And honestly all I can talk is about specs.

  • No RAM expansion.
  • Poor instruction set.

But what does this REALLY mean?.

Let's try run something more "real" on it!

References

A special section is dedicated to honour some of the resources I found online. There are a few more (not many). But these resulted the more useful, accurate and "modern". Two of these resources went offline while writting this blog entry … so before I bitrot myself, here are the waybackmachine links: