Sunday, 17 November 2019

6502 CPU Computer

This blog describes what I have accomplished in developing an 8-bit computer based on what would be described as a mix of old legacy and new technology.

Contents:

  • Introduction
  • Overview
  • Project Approach
  • Boards
  • Display options
  • Assemblers/Disassemblers
  • Future experiments/developments
  • Thanks


Introduction:
----------------
The project (which is on-going), came about as a result of being distracted while experimenting with current technology, including the Raspberry Pi, Arduino, ESP8266 and other TTL & CMOS digital circuitry. As a consequence of making this 8-bit computer I have learned so much:

 - Digital circuit design using legacy TTL/CMOS circuits.
 - 6502 addressing, timing, parallel and serial interfacing and logic control.
 - EEPROM usage, programmer design, and programming with Arduino.
 - 6502 assembler.

Initially, the first distraction came from watching and being engrossed in Ben Eater's YouTube videos, where he works through building a computer using discrete logic integrated circuits, supported in part by some modern technology - more of that later.
I then stumbled across Joe's Computer Museum and got hooked into the construction of an 8-bit computer based on the 6502 CPU, and got completely hooked after visiting a number of other 6502 related web-sites; including but not limited to - 6502.org, WilsonMinesCo.com and grappendorf.net

I had some limited experience of the 6502 CPU as many years ago I constructed from a kit, a very early Acorn Atom computer. I regret parting with this machine. Later computers included the Dragon 32 based on the 6809 CPU, where I dabbled in 6809 assembler. Turning full circle I recently acquired an Acorn Electron (based on the 6502 CPU) and I'm now occupied with assembly language coding and interfacing experiments. [more distraction...]

Prior to starting this project I had amassed a range of TTL/CMOS Integrated Circuits, some of which were to prove very useful in my 6502 project.

I started searching for and buying some components needed for the project.

These included the following:

6502 - CPU
AS6C62256 - 32kb Static RAM
AT28C64B - 8kb EEPROM
6551 - Asynchronous Communications Interface Adapter (ACIA)
and a few CMOS ICs for interfacing and logic address mapping.

Overview of 6502 project capabilities
----------------------------------------------
Based on what I had seen and read on various web-sites, I decided on the following:

The main CPU (6502) running at 1mhz clock speed.
Main volatile memory (SRAM) 32kBytes.
Non volatile memory (EEPROM) 8kBytes.
Serial Input/Output communications using the 6551.
Parallel Input/Output using the 6821 Peripheral Interface Adapter
(I had one of these from my earlier Dragon 32 experiments).
A capability to program the EEPROM.

The Computer memory mapping:

E000 - FFFF [EEPROM/ROM]
D200 - DFFF [Unused]
D000 - D1FF [Input/Output ACIA & PIA]
8000 - CFFF [Unused]
0000 - 7FFF [SRAM]

You will note some unused address space, but I wasn't too concerned at the start as I had plenty of learning and development to do if I was to succeed in putting this first attempt together and get it to work!


Project Approach
---------------------
After I watched Ben Eater's videos, I decided the best approach for me was to construct each element of the project in turn on breadboards.
My reason for this was so I could wire up individual elements, test and experiment with them to better understand and learn how the circuits worked before integrating the single elements together as a whole. This worked well for each single element but as I progressed, the amount of point to point breadboard wiring became almost unmanageable, and in some cases unreliable. On the positive side, I gained a much better understanding of how each Integrated Circuit worked, and the interfacing needs of each. I also became appreciative of the host of documentation on the Internet, particularly the availability of the ICs datasheets.

Thanks to a number of Ben Eater videos I was able to put together an EEPROM based address and data bus monitor, and an EEPROM programmer based
on a NANO Arduino. Additionally I'm in a very fortunate position of owning an oscilloscope which has proved invaluable to check the correct operation of the circuits and identifying problems.

I did succeed in getting a working project, comprising all the main elements (CPU, EEPROM, SRAM, ACIA & PIA), although became frustrated with some unreliability.

After much thought and further research, I discovered a modular computer called the RC2014. Although based on the Z80 CPU, it was the approach to
using modular boards that clarified how I could move my breadboard based project to a more semi-permanent and reliable solution.

So it was that I began to design and migrate the components from a breadboard to a more modular form, using veroboard, loosely based on a backplane with separate cards for each element of the 6502 project.

Modular Boards
-------------------
The following image is the current build, working well and reliable, comprising a single backplane with multiple single boards.

From left to right the boards are:

6821 PIA / 6551 ACIA / USB Keyboard-to-serial-ASCII / Address Logic / EEPROM / SRAM / CPU - with power socket (5v) bottom right.


Current build - 6502 computer 

The following image is a reverse view of the first image.


Current build - 6502 computer


The following image is the unpopulated backplane. This has a few unused sockets as I modified the board when I moved from a ZIF socket solution for the EEPROM to a single modular board.


Unpopulated 6502 backplane


The next image is the main CPU board. The main clock runs at 1mhz. I've provided a small jumper connection that can be removed to allow a separate clock source to be connected directly to the 6502 CPU. This is to allow me to use slower clocks for trouble shooting. With this particular CPU, I've managed to clock at 1Hz. With my EEPROM based address and data bus monitor attached to the back-plane bus it's possible to monitor address and data values as any code runs on the 6502 CPU.

6502 main CPU board

The following image is the address and chip select logic board. It provides control signals for the EEPROM/SRAM/ACIA & PIA boards.



Address/chip select logic board



The next image is the EEPROM board, utilising the AT28C64B, 8kByte EEPROM. I've designed this board so it can be removed from the backplane and inserted into an EEPROM programmer board. The jumper is used to place the EEPROM in to either READ only mode, or enable for READ and WRITE actions.


EEPROM board

The following image is the SRAM board (32kByte), utilising the AS6C62256. I've employed an additional logic gate to ensure the SRAM is in WRITE mode
only when the CPU addressing is stable and the PH2 clock is in the correct phase.

SRAM board



The next image is the ACIA (serial comms) board. I used a 65C51 (I couldn't find an original 6551), employing a crystal controlled oscillator for ACIA bit-rate generation.


ACIA - Serial Comms board


The following image is the PIA (parallel interface) board, using a 6821 as explained above. I've made most of the interfacing pins available for getting data in and out of the CPU.


PIA - Parallel Interface Board

The next image is a USB Host board (obtained from Hobbytronics).
This allows me to attach a USB Keyboard to the computer. The USB host board takes USB keyboard input and converts it to ASCII data, which is connected to the ACIA board serial input. This removes the need to use a PC running a communications terminal program to send ASCII data to the CPU.

USB Host Board - Keyboard Interface

The following image is the breadboard based EEPROM programmer, using the Arduino NANO and a couple of TTL shift registers. This is based on Ben Eater's EEPROM programmer slightly modified to accommodate a larger memory capacity EEPROM.


EEPROM Programmer - Arduino NANO based


The next image is the earlier EEPROM modular board, based on a ZIF socket that plugged directly into the back-plane.
Initially this worked reasonably well, but I had suspicions it was contributing to occasional errors. Also I wasn't happy with physically removing the EEPROM from this board and plugging it into the breadboard programmer every time a code change happened. A redesign followed...

ZIF socket based EEPROM board
The following image is the more permanent and reliable EEPROM programmer board. Based on the circuit of my earlier breadboard version, this allows me to plug in the complete EEPROM modular board when I need to make programming changes.
EEPROM programmer - Arduino NANO based

The next image shows an EEPROM modular board plugged into the EEPROM programmer board. I use a PC plugged into the Arduino NANO micro-usb socket, running an Arduino IDE to write 6502 assembled (HEX) code to the EEPROM.

EEPROM programmer - populated with EEPROM board

The final two images show the address and data bus monitor breadboards. These are based again on Ben Eater's solution and monitors 4-bit 'nibbles' and display the address and data HEX values on 7-segment displays. With these monitors plugged into the 16 address and 8 data bus lines on the main 6502 backplane, it's possible to trace through the 6502 instructions in realtime (only practical at very slow clock rates).

16-bit address bus monitor

8-bit data bus monitor

Display options
-------------------
Initially I used a Windows based PC running RealTerm via a USB/FT232 board connected to the 6502 CPU ACIA board to send and receive ASCII data.
The image below shows a screen shot of the RealTerm terminal with the output of the 6502 project, where I'm running a 6502 monitor based on the code
provide by Joe (Joe's Computer Museum). This only displays 5 bytes per line, formatted to display text on an LCD display (a later project element).
Using RealTerm / USB/FT232 / ACIA connectivity I've managed speeds from 115200bps to 300bps.

RealTerm - ACIA output


Additionally I've experimented with the Serial Monitor function in the Arduino IDE; a simple display (below) but lacking in features.



Arduino Serial Monitor - ACIA output


Doing further experiments I've connected the ACIA (with USB/FT232 interface) to a Raspberry Pi computer, and used some different ways to send and receive ASCII data interacting with the 6502 monitor. The two images below show:

1. CuteCom on the /dev/ttyUSB0 port.

2. The Screen command using: screen /dev/ttyUSB0 9600.


CuteCom - running on Raspberry Pi


Screen - running on Raspberry Pi


Taking this one stage further, I've even managed to use my Windows PC to remotely SSH to the Raspberry Pi, then run the screen function to 'connect' to the ACIA and interact with the 6502 based monitor. This is true remote operation of a 6502 CPU based computer!

In each display output I've shown above, the monitor is displaying the contents of the EEPROM upper addresses:

$FFFA/FB - NMI
$FFFC/FD - RESET
$FFFE/FF - IRQ
programmed with the 6502 monitor code, where the monitor start address is $E000:

The final two images below show an LCD display (20 characters x 4 lines). This LCD module connects to an Adafruit serial/LCD backpack interface.
Serial output from the 6502 ACIA module connects to the Adafruit serial interface running at 9600bds, with the ASCII characters appearing on the LCD display. I've needed to modify the 6502 CPU monitor code to ensure the output is formatted appropriately to fit the LCD display.


LCD display 20 x 4  - Boot-up display 




LCD display 20 x 4 - EEPROM address range $FFFB - $FFFF

Assemblers/Dis-assemblers
-----------------------------------
I generally use Notepad++ to create the assembler file and use the RetroAssembler for Windows to assemble/generate the HEX code.
This HEX code is copied to the Arduino IDE where I use the Arduino NANO based EEPROM programmer to write HEX data to the EEPROM.
For small pieces of code where I need to check the status of the CPU (A, X, Y and Status Register) I use Easy 6502 (skilldrick).
To dis-assemble 6502 HEX code I regularly use the Masswerk-Virtual6502 - I've found this especially useful to step through assembled code to better understand how the 6502 code is running.

Future / Experiments
---------------------------
More permanent mounted LCD display.
TV / RGB / VGA display output.
6502 assembly coding
External interfacing using the 6821 (PIA) and 6551 (ACIA) to other digital circuits/sensors/control.
Interfacing with the Acorn Electron.

... and there will be others .. depending on my levels of distraction!

Hopefully you'll have found this article interesting. I haven't provided much technical detail such as circuits and veroboard layouts as this is likely to take me a significant amount of time. If you have any questions I'll do my best to answer them.

Thanks
---------
As I stated earlier, I have many people to thank and to recognise the significance of the effort many people have put in to providing designs, advice, documentation and videos:

Ben Eater
Joe's Computer Museum
6502.org
WilsonMinesCo.com
grappendorf.net
RetroAssembler
skilldrick Easy 6502
masswerk-virtual6502
KIM-1 documentation
BYTE magazine
stardot.org


and others too.... Many thanks.

Saturday, 9 June 2018

Humax FVP-4000T remote control app


Introduction

I've owned a Humax FVP-4000T Freeview unit for a while and overall it performs well and has plenty of useful features (https://uk.humaxdigital.com/product/fvp-4000t/).
It is connected to my home network and therefore the Internet. One small disappointment is the quality of the Infra-Red remote control handset, with some of the buttons occasionally being unresponsive or inconsistent in operation. I also own an old HUDL tablet and I installed the Humax remote control application for Android. This app has most of the controls necessary to operate the FVP-4000T, but the screen display isn't optimised for the HUDL, so this can be slightly frustrating to use. As the control from the HUDL to the FVP-4000T is via my wireless and wired network, the control is easier and more reliable than the infra red unit, so it's frequently in use.

I began to wonder whether an app might be available for Microsoft Windows so I could control the FVP-4000T from my laptop (running Win 8.1), but my initial searches proved fruitless.

One resource I found proved useful in that some control codes had been identified that the Humax Android app uses to control Humax boxes. This can be found at:


This got me thinking about whether I could produce my own app using the Python programming language. I've only been using Python for a couple of years on and off, so do not consider myself at all proficient. There may be some poor aspects of the programming in my script that I developed, or there may be a better way of doing some of the elements, but I got an application working how I expected it to work. I've learned a lot on the way, and I extend my thanks to anyone who has posted Python code snippets or asked questions or given answers on-line.


There were a number of things I needed to investigate as part of this project.

1. Find out which network address, protocols and port the Humax FVP-4000T 'listens' for control codes.
2. What the Humax app that runs on the HUDL sends and the format of the information used.
3. Create a 'window' based Python application - to run as a standalone executable on Windows.

For this write up I have assumed the reader understands many basic concepts relating to network protocols and the Python programming language. There are many good resources on the Internet which should help with understanding these topics.


Network address, protocols and ports
Initially I set a fixed IP address on the Humax FVP-4000T box so that any network investigations would be easier; the last thing I wanted was to 'chase' around finding the FVP-4000T network address every time I worked on this project.


I used Wireshark (https://www.wireshark.org/) installed on my laptop to capture the network traffic sent from my Humax app on my Hudl tablet to determine the protocols, ports and control codes. I used a network splitter cable so I could monitor the traffic from my tablet to the FVP-4000T via my home router.

When the Android Humax app starts it sends out an SSDP (Simple Service Discover Protocol) message.[https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol] discovery 

The Wireshark capture:










In response, the Humax sends:





Here the Humax IP address is identified and also the MAC address. This was to prove useful during the development of my app as I wanted to be able to discover the Humax IP address automatically and confirm that the IP address is assigned to a Humax unit.


Humax Control Codes

Running the Android app I experimented using the 'Mute' control - mainly because when using the app control I could hear when the Humax audio was present or not and didn't need to monitor the TV screen. Also, just using one function should help to identify the code(s) in the network data.
The information I confirmed was that the Android app uses UDP (User Datagram Protocol) [https://en.wikipedia.org/wiki/User_Datagram_Protocol] to send the control message and the Humax destination port is 22558.

The Wireshark capture:


The next information comprises:












In remote control mode, this data comprises the control data which is always:
686d78726100000072637500000000003030

Note that the byte codes 72, 63, 75 equate to 'rcu' - a 'pointer' to Remote Control Unit

The code for 'Mute' in this example above is ascii 72,  byte codes 37, 32.
The complete control message becomes:
686d787261000000726375000000000030303732

I checked some more of the codes from (https://hummy.tv/forum/threads/ip-remote-commands.7783/) to confirm that the control and data format was consistent.

Further investigation revealed a different control data format when performing searches on the Humax.

When using the search function, the control data becomes:
686d7872610000006b65790000000000332c302c

Note that the control data used in 'remote control mode' to select the search mode on the Humax  replaces the 3030 with 332c and adds 302c. Also, the byte codes 6b, 65, 79 equate to 'key' -  a pointer to 'Key text search'.

Actual search text appears after this control data. For example, the text letter 'a' appears as:
686d7872610000006b65790000000000332c302c61 (61 byte code = 'a')

All the information to control the Humax FVP-4000T was now known. I had to investigate how I could use Python3 to develop a set of control functions within a windows interface.


Python script
The key elements needed:
1. Send an SSDP message to find the Humax box details (IP & MAC addresses)
2. Confirm a valid Humax MAC address
3. Set up the host PC to send UDP data
4. Construct remote control and search control messages
5. Create a Graphical User Interface (windows) with selectable functions
6. Define functions - when user selects, the script sends the command
7. Enable user to enter search text and send to the Humax


SSDP Message
I found a Python script which I modified that enabled me to send SSDP messages. When experimenting with my Humax unit, the message returned included :

uuid:1E96FF1A-6771-2F44-A0C5-2c088c9931f9


After some investigation I determined that the elements '2c088c' is an identifier for the Humax MAC vendor ID.  On line I found a range of vendor ID Humax MAC addresses that I included in my application code as part of confirming the IP address associated with its MAC address. It is intended this app 'should' work with different Humax boxes if they have one of the vendor ID MAC addresses I've included in the Python script. See 'Using the application' at the end of this blog if this application doesn't work with your Humax box.

The script extract below creates the SSDP message, sets the SSDP IP, configures the IP socket, sends the message and captures the response(s). It checks for a valid MAC address, extracts the IP address and sets the variable UDP_IP as the 'target' IP address and the port number to 22558 for future messages to be sent to the Humax box.
  
import socket
msg = \
    'M-SEARCH * HTTP/1.1\r\n' \
    'HOST:239.255.255.250:1900\r\n' \
    'ST:upnp:rootdevice\r\n' \
    'MX:2\r\n' \
    'MAN:"ssdp:discover"\r\n' \
    '\r\n'

msg1 = msg.encode()

ssdp_udp = '239.255.255.250'

# Set up UDP socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.settimeout(2)
s.sendto(msg1,(ssdp_udp, 1900))

#humax mac ids (valid mac addresses as of May 2018)
macs = (
    'e820e2', 'dcd321', 'cc4eec', 'a0722c', '942cb3', '940937', '90f305',
    '6cb56b', '4cd08a', '403dec', '3438b7', '2c088c', '2832c5', '08eb74',
    '044f17', '000378'
    )

# receive ssdp responses
try:
    while True:
        data, addr = s.recvfrom(65507) #addr format ('xxx.xxx.xxx.xxx', 1900)
        inputtext = str(data)
        for var in range(len(macs)): # check for valid humax mac address
            query = (inputtext.find(macs[var]))
            if (query != -1):
                ipads = str(addr)
                comma = (ipads.find(","))
                extract = (ipads[2:comma-1]) # get the IP address
               
except socket.timeout:
    pass

#IP is 'extracted' from the ssdp search response
UDP_IP = extract
UDP_PORT = 22558

Remote Control & Search Control Messages
Previously I stated that the Remote Control Message format had a static element. This is used here below, one called 'Control' for the Remote Control,  the other 'Controlkey' for accessing the Search function on the Humax box.

#Static HUMAX message element
Control = bytes.fromhex('686d78726100000072637500000000003030')

Controlkey = bytes.fromhex('686d7872610000006b65790000000000332c302c')

To construct a complete Remote Control Message, the function code is added to the Control Message element. For example the Mute function code is '3732'. The defined Python function looks like:

def Mute():
    SendCmd(bytes.fromhex('3732'))

When the Mute function is selected, it then calls the SendCmd function and passes the hex code to be added to the Control message element. The complete message is then sent to the Humax box.

def SendCmd(code): # Construct full HUMAX control message & send
    Message = Control + code
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(Message, (UDP_IP, UDP_PORT))

I created a series of functions to cover the range of remote control functions available on the Humax. These functions, together with the remote control codes are viewable in the Python script.


Programme Search
To construct a Remote Control Message to do a programme search on the Humax unit, first a Control Message is constructed and sent to the Humax box.  The function to do this is below which opens up the search box on the Humax.  (I include the Windows GUI code, which creates a search text entry box for the user to enter text, a graphical button to submit the entered search text to a Python function to extract the entered text, construct the text string and send to the Humax unit).

# search function creates text entry box & submit button
def Search():
    SendCmd(bytes.fromhex('3437'))
    global textstr #global to be available to Dialog function
    textstr=StringVar()
    textentry=Entry(myGUI, width=12, textvariable=textstr).grid(row=1,column=6)
    button1=Button(myGUI, text='Submit', width=6, command=Dialog).grid(row=2,column=6)

# get entered text & format to send to Humax box
def Dialog(): # send ascii letters to Humax search box
    text = str(textstr.get()) # get text search entry box
    c = len(text) # variable for string length
    pointer = 0 # starting point in string
    cs = "" # hex string construction variable
    while  pointer < c:
        # construct hex characters string without leading 0x, from decimal values of text
        cs=cs+(hex(ord(text[pointer])).replace("0x",""))
        pointer = pointer+1 # move along the string
        SendKey(bytes.fromhex(cs)) # use constructed string as the 'code' to add to the 'control' msg
   
def SendKey(code): # Construct full HUMAX search control + text & send
    Message = Controlkey + code
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    sock.sendto(Message, (UDP_IP, UDP_PORT))


Windows Graphical User Interface (GUI)
The interface I created is fairly simple and clean, and laid out to suit how I use the controls. The Python code can be modified to create a different layout.




When the Search 'button' is selected, the text entry box and submit button appears.




To create the windows interface:

from tkinter import *
# create Window GUI
myGUI = Tk()
myGUI.geometry('550x215')
myGUI.title('HUMAX FVP-4000T Remote Control IP: ' + extract) # include IP address in header
myGUI.resizable(False, False)

All the main Python code follows this code including all the functions and GUI graphical elements.
The final line should be:

myGUI.mainloop()

Within the Python script I include all the codes and functions that worked and those which didn't.



Using the application
If you have Python3 with the Integrated Development Environment (IDE) installed on your machine you can download my script, open it in the IDE and run from there (use F5).

Alternatively download the GUI.exe application below with all the associated files in the 'GUI' folder. When run, a command window will open as well as the GUI window. Research indicates it's possible to run the GUI while suppressing the command window, but I haven't got this to function correctly.

This executable and associated files was made using the application called PyInstaller. PyInstaller converts a .py script to a Windows executable, standalone .exe file.

This can be found at: https://www.pyinstaller.org/

If my PyInstaller files do not work on your machine I suggest downloading PyInstaller on your own machine, and use it to convert my GUI.py script on your own machine.

When running the application (ensure your Humax box is powered up) there will be a short delay of about 3 to 5 seconds as your Humax unit is identified, then the GUI appears. The GUI will not appear if your Humax unit is not identified if your Humax unit MAC address is not listed in my Python script.

One work around to overcome this:
Modify my Python script to remove all the code associated with the SSDP function. In the code element (UDP_IP=extract), replace 'extract' with the IP address of your Humax unit. It's recommended that the Humax unit is assigned a fixed IP address.

Alternatively, modify the Python script to include the MAC address element of your Humax unit. This address should be on the unit.

The GUI controls should be straightforward to use.

When using the Search function, the Humax search screen appears. After entering the text in the text entry box on the application and selecting Submit, the text should appear in the Humax search box. Selecting Search again on the application then 'applies' this as the search text on the Humax box and the search results should appear.

The Power button only works to turn the Humax unit power off.

Navigation around the Humax functions is by the Left, Right, Up and Down buttons. CH- and CH+ work to change programmes, but also scrolls screens when viewing the TV guide or Recordings listings.

I hope you find this application helpful. If you don't like any elements of the programme you are free to modify it to your own taste.

If you find it useful please leave some comments on my blog.

Downloads:

GUI.py (Python3 script) (12KB)

Windows Executable (.exe) and related files in folder 'GUI' (20MB)