eris2010

Documentation: http://frombelow.net/projects/eris2010/
Clone: git clone https://git.frombelow.net/eris2010.git
Log | Files | Refs | Submodules | README | LICENSE

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:
A.gitignore | 3+++
A6502_monitor/machine.py | 22++++++++++++++++++++++
A6502_monitor/monitor.py | 228+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anotizen.txt | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Askizze.kra | 0
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.