commit 823b04b8d5102252002d6fb25c544c8dd7deb785
Author: Gerd Beuster <gerd@frombelow.net>
Date: Mon, 31 Aug 2020 08:09:06 +0200
Start working on project; monitor works on PC
Diffstat:
5 files changed, 405 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,3 @@
+datasheets/
+6502_monitor/__pycache__/
+*~
diff --git a/6502_monitor/machine.py b/6502_monitor/machine.py
@@ -0,0 +1,22 @@
+# Dummy class to run ESP32 programs on normal PC
+
+import random
+
+class Pin():
+
+ OUT = 0
+ IN = 1
+ PULL_UP = 2
+
+ def __init__(self, _direction, _pullup):
+ pass
+
+ def on(self):
+ pass
+
+ def off(self):
+ pass
+
+ def value(self):
+ return random.choice([0, 1])
+
diff --git a/6502_monitor/monitor.py b/6502_monitor/monitor.py
@@ -0,0 +1,228 @@
+#!/usr/bin/env python3
+"""Monitor and control 6502 busses
+
+This program controls the busses (and other required signals like PHI2)
+of a 6502 from an ESP32."""
+
+import sys
+import machine
+import time
+import pdb
+
+# Connect 6502 address bus (mostly) to left side of ESP32
+# Can I really use GPIO10?
+# A14 & A15 not connect due to lack of GPIO pins
+A_Pins = [36, 39, 34, 35, 32, 33, 25, 26, 27, 14, 12, 13, 10, 15]
+D_Pins = [2, 0, 4, 16, 17, 5, 18, 19]
+RW_Pin = 21
+PHI2_Pin = 22
+RST_Pin = 23
+
+# Max memory size is 2**14, because due to a lack of GPIO pins, only
+# A0..A13 are connected
+MEM_SIZE = 2**14
+mem = bytearray(MEM_SIZE)
+# By default, we emulate memory. This allows to run a 6502 with an ESP32
+# as the only "peripheral" device. If emulate_memory is set to False,
+# this monitor can be used to monitor the bus lines between a 6502
+# and an actual memory chip.
+emulate_memory = True
+
+# Thin wrapper taking care of switching I/O mode of Pins.
+class Pin:
+ def __init__(self, pin):
+ self.pin = pin
+
+ def low(self):
+ p = machine.Pin(self.pin, machine.Pin.OUT)
+ p.off()
+
+ def high(self):
+ p = machine.Pin(self.pin, machine.Pin.OUT)
+ p.on()
+
+ def set(self, value):
+ p = machine.Pin(self.pin, machine.Pin.OUT)
+ if value == 0:
+ p.off()
+ else:
+ p.on()
+
+ def read(self):
+ p = machine.Pin(machine.Pin.IN, machine.Pin.PULL_UP)
+ return p.value()
+
+def read_address_bus():
+ rw = RW.read()
+ data = 0
+ for p in reversed(A):
+ data = ((data << 1) + p.read())
+ A.reverse()
+ return (rw, data)
+
+def read_data_bus():
+ data = 0
+ for p in reversed(D):
+ data = ((data << 1) + p.read())
+ return data
+
+def write_data_bus(data):
+ data = 0
+ for p in D:
+ p.set(data & 1)
+ data = data >> 1
+
+def pp_mem(start, end):
+ """Pretty print memory content"""
+ print(' 0 1 2 3 4 5 6 7 8 9 A B C D E F')
+ for chunk in range(start//0x100*0x100, end+1, 16):
+ l = "{:04X}: ".format(chunk)
+ l += " ".join(["{:02X}".format(x)
+ for x in mem[chunk:min(chunk+16, end+1)]])
+ print(l)
+
+def update_buses_and_print_state(end='\n'):
+ (rw, address) = read_address_bus()
+ # Get/set data bus and print command prompt
+ if rw:
+ format_string = "{address:04X} > {data:02X}"
+ else:
+ format_string = "{address:04X} < {data:02X}"
+ if rw and emulate_memory:
+ # CPU wants to read memory, and we
+ # emulate the memory. Therefore we
+ # answer this request
+ data = mem[address]
+ write_data_bus(data)
+ else:
+ # This is either a memory write operation or
+ # a memory read operation answered by the
+ # actual memory, not our emulation.
+ # Therefore we read the data from the bus.
+ data = read_data_bus()
+ mem[address] = data
+ print(format_string.format(address=address, data=data), end=end, flush=True)
+
+def tick():
+ # Execute on CPU cycle with a clock frequency of 500 Hz or
+ # less.
+ PHI2.low()
+ time.sleep(.001)
+ PHI2.high()
+ time.sleep(.001)
+
+def run(cycles):
+ # Run for a number of cycles. This function is calle from rep.
+ # Since the buses' states are printed at the begin of each
+ # loop, it is not printed here for the final cycle.
+ assert(cycles > 0)
+ for _ in range(cycles-1):
+ tick()
+ update_buses_and_print_state()
+ tick()
+
+def warn_if_memory_not_emulated():
+ if not emulate_memory:
+ print('WARNING: Memory not emulated; monitoring buses only.')
+
+
+def print_help_msg():
+ print("""\
+Commands:
+r: Reset
+<n>: Run for n clock cycles (empty = 1 cycle)
+m <addr> [<data>]: Write to emulated RAM
+d <from> <to>: Dump emulated RAM
+w <filename>: Write emulated RAM to file
+l <filename>: Load emulated RAM from file
+b <filename>: Execute batch file
+n: RAM emulation off: No bus write operations, listen only
+e: RAM emulation on: Write from emulated memory to data bus
+h: This information
+x: Exit
+""")
+
+def repl(script=[]):
+ # Get and parse next command
+ global mem
+ global emulate_memory
+ update_buses_and_print_state(end='')
+ if len(script) == 0:
+ print(": ", end='', flush=True)
+ l = sys.stdin.readline()[:-1]
+ else:
+ print(": {}".format(script[0]), flush=True)
+ l = script[0]
+ script = script[1:]
+ # Filter out comments
+ comment_start = l.find('#')
+ if comment_start != -1:
+ l = l[:comment_start]
+ # Tokenize input
+ l = l.split(" ")
+ cmd = l[0]
+ args = l[1:]
+ # Empty command = run for one clock cycle
+ if(len(cmd) == 0):
+ cmd = '1'
+ if cmd.isdigit():
+ # Run for a number of cycles
+ run(int(cmd, 16))
+ elif cmd == 'r':
+ # Reset
+ RST.low()
+ print('RST low')
+ run(2)
+ update_buses_and_print_state()
+ RST.high()
+ print('RST high')
+ run(7)
+ elif cmd == 'm':
+ # Write to emulated RAM
+ warn_if_memory_not_emulated()
+ addr = int(args[0], 16)
+ data = list(map(lambda x: int(x, 16), args[1:]))
+ mem[addr:addr+len(data)] = data
+ elif cmd == 'd':
+ # Dump emulated RAM
+ warn_if_memory_not_emulated()
+ pp_mem(int(args[0], 16), int(args[1], 16))
+ elif cmd == 'w':
+ # Write emulated RAM to file
+ warn_if_memory_not_emulated()
+ with open(args[0], 'wb') as f:
+ f.write(mem)
+ elif cmd == 'l':
+ # Read emulated RAM from file
+ warn_if_memory_not_emulated()
+ with open(args[0], 'rb') as f:
+ mem = bytearray(f.read())
+ elif cmd == 'b':
+ # Execute batch file
+ with open(args[0], 'rt') as f:
+ script = f.read().split('\n')
+ elif cmd == 'n':
+ # Turn RAM emulation off
+ emulate_memory = False
+ print('Memory emulation off; listening to buses only.')
+ elif cmd == 'e':
+ # Turn RAM emulation on
+ emulate_memory = True
+ print('Memory emulation on.')
+ elif cmd == 'h':
+ print_help_msg()
+ elif cmd == 'x':
+ # Exit
+ sys.exit(0)
+ else:
+ print('Unknown command')
+ repl(script)
+
+if __name__ == '__main__':
+ # Create objects for Pins
+ A = list(map(Pin, A_Pins))
+ D = list(map(Pin, D_Pins))
+ (RW, PHI2, RST) = map(Pin, [RW_Pin, PHI2_Pin, RST_Pin])
+ print("** 6502 Monitor **\n")
+ print_help_msg()
+ repl()
diff --git a/notizen.txt b/notizen.txt
@@ -0,0 +1,152 @@
+* Design
+
+6502 + nvSRAM, Bus-Transceiver vor RAM und I/O-Pins
+
+(Bus-Transceiver vor RAM wirklich notwendig?
+http://wilsonminesco.com/6502primer/LogicFamilies.html sagt nein)
+
+Wir brauche RAM sowohl bei $0000, wg. Zero-Page und Stack, als
+auch bei $FFFA-$FFFF, weil dort Interrupt- und Reset-Vektoren erwartet
+werden. Außerdem sollte die Bus-Logik möglichst einfach sein.
+
+Ganz wichtig: RAM Schreibzugriff darf erst ermöglicht werden, wenn der
+Takt High ist. Erst dann ist der Datenbus stabil!
+
+Mögliche Lösung: IO-Bereich wird angewählt durch MSB Adresse = b10
+ist:
+(A15 & ~A14) = ~(A15 ~& ~A14) = (A15 ~& (A14 ~& A14)) ~& (A15 ~& (A14 ~& A14))
+Besser wäre, ein NAND-Gate weniger in der Kaskade zu haben.
+Siehe http://wilsonminesco.com/6502primer/addr_decoding.html für Vorschläge.
+
+Frage: Sind meine NAND-Chips schnell genug? Laut Datenblatt
+(<https://docs.rs-online.com/d108/0900766b806350fe.pdf>) ist
+Propagation Dealy Time 260ns. Bei 1 Mhz dauert ein Takt 1us = 1000ns.
+Nach 500ns geht der Takt hoch. Speicherchip braucht ab ~OE 70 ns um Daten zu
+liefern. Wenn nur ein Gate zwischen Takt hoch und Chip/Output enable
+low, also 260 ns+ 70 ns = 340 ns. Sollte passen.
+
+Zusammenfassung: Max. 3 Gatter für Adress-Kodierung, Takt high auf low
+um Lese/Schreiboperationen zu ermöglichen darf nur durch 1 Gatter
+laufen.
+
+
+
+Problem: Meine NAND-Chips sind vermutlich zu langsam. (Siehe
+https://docs.rs-online.com/d108/0900766b806350fe.pdf in Kombination
+mit "Note that 100ns memory is not fast enough for 10MHz on a 6502!
+..." http://wilsonminesco.com/6502primer/addr_decoding.html)
+
+Allgemein zu Adress-Dekodierung:
+http://wilsonminesco.com/6502primer/addr_decoding.html
+
+Flashen des RAMs, indem ESP32 Datenbus übernimmt und LDA-STA-Kaskaden
+sendet.
+
+
+Nicht Teil des Hardware-Designs, in Software umsetzen:
+Serielle Kommunikation per Bit-Banging
+
+
+* Bauteilliste
+
+- CPU
+ W65C02S6TPG-14
+ https://www.mouser.de/ProductDetail/Western-Design-Center-WDC/W65C02S6TPG-14?qs=opBjA1TV903lvWo9AEKH5w%3D%3D
+- Sockel CPU
+- Sockel nvRAM
+- Sockel Bus Transceiver
+- nvSRAM
+ M48Z02-70PC1
+ https://www.mouser.de/ProductDetail/STMicroelectronics/M48Z02-70PC1?qs=at%2FPaO0D8JYWx4lwBc2dJg%3D%3D
+ M48Z58Y-70PC1
+ https://www.mouser.de/ProductDetail/STMicroelectronics/M48Z58Y-70PC1?qs=swDD%252BF%252Bps7f2yvdR7wWBQg%3D%3D
+ M48Z35-70PC1
+ https://www.mouser.de/ProductDetail/STMicroelectronics/M48Z35-70PC1?qs=yAkVQ3mwCG1bjdHDg%2FtPWA%3D%3D
+- Oszillator 1 MHz
+ https://www.mouser.de/ProductDetail/ECS/ECS-100A-010?qs=GxOUx7aO6nwDnc3%252B6Ta2Kw%3D%3DECS-100A-010
+
+- Lochrasterplatine(n)
+- IC-Zange
+ Aries T-90
+ https://www.mouser.de/ProductDetail/Aries-Electronics/T-90?qs=WZRMhwwaLl%2FGFiSK29n7Tw%3D%3D
+- Antistatik-Armband
+ SCS ECWS61M-1
+ https://www.mouser.de/ProductDetail/SCS/ECWS61M-1?qs=atelM%2FHH1ECjg%252Bvoti9NeQ%3D%3D
+- Kabel weiblich-weiblich
+- Steckbrett
+- Stiftleistungen als Socke für ESP32
+ 310-47-120-41-001000
+ https://www.mouser.de/ProductDetail/Mill-Max/310-47-120-41-001000?qs=5aG0NVq1C4zOej%252BDTv0V5A==
+- Abisolierzange
+- EEPROM
+- RAM
+- DIP-Schalter
+- 3,3 kOhm Widerstände
+- Reset-Logik
+ DS1813-5+
+ https://www.mouser.de/ProductDetail/Maxim-Integrated/DS1813-5%2B?qs=Jw2w9zrI6w%2Fv9tYN5eKaiw%3D%3D
+- I/O Chip
+ W65C51N6TPG-14
+ https://eu.mouser.com/ProductDetail/Western-Design-Center-WDC/W65C51N6TPG-14?qs=AgbsAOSw7WDdUCKSkUixbw%3D%3D
+
+* Vorgehen
+
+- Schaltung entwerfen
+- Mit Bus-Transceiver experimentieren
+- Busmonitor schreiben: MCU-Programm zum Schreiben/Lesen von Datenbus und Takt
+[Ab hier mit CPU]
+- 6502 mit MCU verbinden & NOPs ausführen, Busse
+ beobachten.
+- Takt für 6502 von Oszillator generieren lassen statt MCU
+- 6502 mit MCU verbinden & Programm ausführen, Busse
+ beobachten.
+- RAM-Monitor schreiben: MCU-Programm zum Lesen & Schreiben von RAM.
+- RAM mit MCU verbinden
+- Flasher schreiben
+- Programme schreiben, flashen, ausführen
+
+* Weitere Kommentare
+
+Maßnahmen gegen statische Entladung treffen! Matte, Armband
+
+
+* Offen Fragen
+
+Sprechen die ICs miteinander oder brauchen wir da noch Glue Logic?
+Habe ich alle Widerstände, die ich brauche?
+Alle benötigten ICs?
+Sockel für alle ICs?
+Wie viele redundante Chips kaufen (und welche?)
+
+* Links
+
+http://wilsonminesco.com/6502primer/
+https://www.grappendorf.net/projects/6502-home-computer/
+
+* Tools
+
+** 6502_monitor
+
+Little tool to control the address and databus of a 6502. This allows
+to run the 6502 without any additional peripheral. This is a
+MicroPython script for the ESP32. The pin are connected as follows:
+
+- Address Bus (16 pins - only lower 14 connected)
+- Data bus (8 pins)
+- Clock (1 pin)
+- R/W (1 pin)
+
+Note that ESP32 pins 34 to 39 are read only, pins 6 to 11 (SPI) and 34
+& 36 (UART) cannot be used, thus we have 24 pins available. Therefore
+we only connect the lower 14 lines of the address bus. Actually, it
+looks like PIN 10 can be used both as input and output. At least the
+MCU did not crash ...
+
+See http://wilsonminesco.com/6502primer/MysteryPins.html and 65c02
+data sheet on how to connect the pins.
+
+** mem_monitor
+
+Little tool to read and write memory. Since we have 24 pins available
+on the ESP32, we use one pin for E, and G. A not gate ensure that one
+is turned on when the other is turned off.
diff --git a/skizze.kra b/skizze.kra
Binary files differ.