Mini Micro and the 6502: Adding retro to the neo-retro
Tags: miniscript, minimicro, 6502, assembly
Published: 2024-03-12 12:53:24

I couldn't help but feel something was missing in the [Mini Micro](https://joestrout.itch.io/mini-micro) environment. After all, we are encouraged to poke around and go deeper in this [MiniScript](https://miniscript.org/)-centered ecosystem, but there *are* practical limits to a "neo-retro virtual computer." Naturally the "virtual" part of that description means there is no hardware to discover. There is no processor for which you can write assembly code in order to squeeze out extra performance.

## The Processor

That gave me an idea. Why not give the Mini Micro a virtual CPU for which we could write assembly? It wouldn't provide us with any performance benefit, but it would definitely add some immersion, and it has the potential to be a cool, educational tool.

I had done some dabbling with NES development. While some aspects of the NES are indeed a nightmare, the one thoroughly enjoyable aspect to it is writing assembly code for its 6502-based processor. The 6502 would be my processor of choice to add to the Mini Micro.

## Necessary Ingredients

In order to make this happen, we need some components:

1. A 6502 assembler
2. A MiniScript program that can read the assembled binary file and execute it
3. A way for the assembly program to utilize Mini Micro's features
4. A goal

For #1, yes, I could write my own assembler in MiniScript within the Mini Micro environment, but I decided to choose [ca65](https://cc65.github.io/doc/ca65.html), at least for now. This assembler is robust, very versatile, and offers some very nice features, not the least of which is the ability to painstakingly describe your memory layout. More on this later!

For #2, that is where the bulk of my project comes into play. My intent is to write more posts on this aspect of the journey as development (hopefully) continues.

For #3, the answer is memory-mapped IO. We'll need a scheme. More on this later as well!

For #4, I'd like to be able to make some sort of Pong clone for the Mini Micro, completely in 6502 assembly.

## Memory Mapping

![The 1976 Second Edition 6502 manual showing an example of memory mapping convention](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w489mbwesrqclst5cyuf.png)<figcaption>The 1976 Second Edition MCS6500 MICROCOMPUTER FAMILY
PROGRAMMING MANUAL provides a convention for memory mapping</figcaption>

The 6502 has a 16-bit address space, but it has no special way to communicate with other hardware (ie. sound, video, etc. etc..). As a result, in real systems, certain addresses were actually mapped directly to the hardware devices instead of being mapped to RAM.

Did you want to make the sound chip on your system play a tone? Perhaps you'd need to write a value to address $5000 that represents the pitch of the tone you'd like to play. Did you want to change a pixel on the video output? Perhaps you'd need to write the color to $6000, the X coordinate to $6001, and the Y coordinate to $6002. This was different in every system, and it all depended on the hardware components and how they were mapped.

Given that the Mini Micro has no real hardware, but that it has similar functionality (ie. sound and video) tied to native MiniScript objects, we can implement memory mapping to utilize this same functionality. We just need to pick a wise scheme that maps Mini Micro functionality to the assembly world in a smart and efficient manner.

## Hello World: 6502 style

Let's see how far I've gotten in about two weeks. Here's a 6502 assembly program that is far from complete, but actually assembles and runs in Mini Micro:

```asm
.CODE

msg: .asciiz "Hello, world!"

reset:
ldy $4003   ; Store previous text.delimiter
ldx #$00
stx $4003   ; Disable text.delimiter by setting it to 0

loop:
lda msg, x
beq done
sta $4002   ; print single character of msg[x]
inx
jmp loop
done:
ldx #$0D
sty $4003   ; Enable previous text.delimiter by setting it to char(y)
stx $4002   ; Print the new line
brk

.segment "VECTORS"
.word reset
.word reset
.word reset
```

And here we have its output when executed on the Mini Micro:

![Output of 6502 "Hello World" on the Mini Micro](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oosy17x44xi1zuxcc1qx.png)<figcaption>From 6502 assembly to the Mini Micro display: "Hello, world!"</figcaption>

Whew! That's a lot of effort to get a simple message printed to the screen!

But how does it work? How does reading (ie. lda, ldx, ldy) and writing (ie. sta, stx, sty) to memory affect text output?

I mapped addresses $4001 to $4003 to Mini Micro's text functionality for this test:

- $4001: Writing a byte to this address outputs it as a **number** to the screen (ie. Writing 65 will print "65" to the screen)
- $4002: Writing a byte to this address outputs it as a **character** to the screen (ie. Writing 65 will print "A" to the screen)
- $4003: Reading or writing a byte to this address gets or sets respectively the value of `text.delimiter` -- which is the character in Mini Micro that gets printed after every call to `print`. By default it is the same value as `char(13)` in MiniScript (or '\r' in C).

## The Journey

The important thing so far is that it works -- but is it sufficient for Pong?

As you can see, in this run we've managed to emulate 223 cycles (BRK isn't emulated correctly just yet, so its cycle count isn't accurate) in 0.03506 seconds. That means that the virtual CPU is working at an average speed of ~6.360Khz -- wildly underperforming the real chip that could run between 1Mhz and 3Mhz in the real world. Hopefully we'll explore performance in later updates.

Is it enough? Can we squeeze more performance out of it? Join me on the journey, and let's find out.

Do you have any good 6502 memories? Do you have ideas for this project? Let me know!