commit 255bb36f28a68e2e07a532b2c660f0ec72c58d53
parent 823b04b8d5102252002d6fb25c544c8dd7deb785
Author: Gerd Beuster <gerd@frombelow.net>
Date: Mon, 31 Aug 2020 09:27:16 +0200
The computer works!
Diffstat:
33 files changed, 1856 insertions(+), 404 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,3 +1,21 @@
-datasheets/
-6502_monitor/__pycache__/
+doc/datasheets/
+misc/6502_monitor/__pycache__/
*~
+hw/bus_logic/BUS_LOGIC.abs
+hw/bus_logic/BUS_LOGIC.doc
+hw/bus_logic/BUS_LOGIC.jed
+hw/bus_logic/BUS_LOGIC.pdf
+hw/bus_logic/BUS_LOGIC.si
+hw/bus_logic/BUS_LOGIC.sim
+misc/clock_reset_attiny45/clock_and_reset.bin
+misc/clock_reset_attiny45/clock_and_reset.elf
+misc/clock_reset_attiny45/clock_and_reset.hex
+misc/clock_reset_attiny45/clock_and_reset.map
+roms/serial_char_out/serial_char_out
+roms/serial_echo/serial_echo
+roms/serial_echo/serial_echo_symon
+roms/simple_loop/loop
+sw/mem_test/mem_test
+sw/mem_test/mem_test.l
+sw/mem_test/mem_test_symon
+sw/mem_test/mem_test_symon.l
diff --git a/6502_monitor/machine.py b/6502_monitor/machine.py
@@ -1,22 +0,0 @@
-# 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
@@ -1,228 +0,0 @@
-#!/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/README.txt b/README.txt
@@ -0,0 +1 @@
+See doc/Documentation.txt
diff --git a/doc/Documentation.txt b/doc/Documentation.txt
@@ -0,0 +1,82 @@
+* GATE 2010 - An 8-Bit Computer
+
+** Main Components
+
+The computer is based on a 65C02 running at 1 Mhz with 32K RAM
+(AS6C62256-55PCN) and 8 K EEPROM (AT28C64B-15PU). A W65C51N ACAI
+provides a serial communication interface. Bus logic is provided by a
+ATF16V8B EEPLD. The reset logic is based on a NE555 in a monostable
+configuration (with a little help (an inverter of the AFT16V8B).
+
+See hw/bom.ods for details of the components. (This list also contains
+components not used in the current design.)
+
+** Memory Map
+
+Lowest 32K are RAM, highest 8K are ROM. ACAI is in between. See
+hw/bus_logic/BUS_LOGIC.PLD for details.
+
+** Directory Structure
+
+- doc/ - Documentation
+- hw/ - Hardware description (pcb not complete yet)
+- misc/ - Miscellaneous files - See section Tools for a description
+- roms/ - ROM images. See roms/ROMS.txt for a description.
+- sw/ - "Userland" software to be loaded into RAM by a suitable ROM. See
+ sw/SW.txt for a description.
+
+** Boot Sequence
+
+The ROM at roms/boot/boot loads data via the serial interface to RAM
+starting at address $0300 and executes it. The first two bytes of the
+transmission is the number of $100 byte blocks to be transmitted. Run
+rom/boot/boot.py on the host PC to upload a program.
+
+** Tools
+
+Directory misc/ contains some tools and unfinished parts:
+
+*** 6502_monitor/
+
+A ESP32 monitor for the 65C02. This project has never really been
+completed, because it would require some level shifter between the
+3.3v of the ESP32 and the 5v of the 65C02.
+
+*** clock_reset_attiny45/
+
+Generates clocks at different speeds and provides a reset logic. The
+clocks have been used in the early implementation phase, before
+replacing it with a 1 Mhz packaged crystal. The reset logic was
+replaced by a NE555 configured as a monostable multivibrator.
+
+*** sd_card/
+
+So far, the computer system does not have storage. This directory
+contains some experiments interfacing the SPI interface of an SD card.
+
+** TODOs
+
+*** Write library functions
+
+- String input via serial line
+- Multiplication and division
+- Random number generator
+- CRC generator
+
+*** Improve file upload
+
+- Connect dtr and reset lines of usb2serial interface and use them to
+ trigger uploads.
+- Speedup upload process by removing echo of uploaded bytes; use CRC
+ instead for integrity check.
+
+*** Write script to export library functions from boot ROM
+*** Get some software running
+
+Candidates:
+
+- 10PRINT
+- MicroChess
+
+*** Design PCB
+*** Connect SD card
diff --git a/doc/notizen.txt b/doc/notizen.txt
@@ -0,0 +1,67 @@
+* Design
+
+See bus_logic/BUS_LOGIC.PLD for memory layout.
+
+
+
+http://wilsonminesco.com/6502primer/LogicFamilies.html: "You must have
+a way to make sure RAM cannot be written when Ξ¦2 is low!"
+
+=> Write glue logic for rw: rw_ram = rw_cpu # !phi2
+
+If this doesn't solve the problem:
+
+Alliance RAM data sheet:
+"WE#, CE# must be high during all address transitions."
+=> Wire both WE#, CE# to PLD. Perhaps add two NAND gates to slow signal down?
+
+
+Allgemein zu Adress-Dekodierung:
+http://wilsonminesco.com/6502primer/addr_decoding.html
+
+Also: Connect DTR to RTS
+
+* Parts list
+
+- Renesas RAM (32, 64 K)
+- 64 K RAM
+- 8, 16 K ROM
+- GAL
+
+
+* 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.
+
+Note that IO0 must be pulled up (or at least not down) on power-up in
+order to boot into MicroPython.
+
diff --git a/hw/bus_logic/BUS_LOGIC.PLD b/hw/bus_logic/BUS_LOGIC.PLD
@@ -0,0 +1,94 @@
+Name BUS_LOGIC;
+Partno ;
+Revision 01;
+Date 10/01/20;
+Designer gb;
+Company ;
+Location None;
+Assembly None;
+Device g16v8a;
+
+/* Bus logic for 8 bit computer based on 65C02
+ */
+
+pin 1 = rwb; /* cpu_RWB */
+pin 2 = i0; /* cpu_A15 */
+pin 3 = i1; /* cpu_A14 */
+pin 4 = i2; /* cpu_A13 */
+pin 5 = i3; /* cpu_A12 */
+pin 6 = i4; /* cpu_A11 */
+pin 7 = i5; /* cpu_A10 */
+pin 8 = reset_in; /* Reset logic output */
+pin 9 = phi; /* cpu_PHI2 */
+pin 12 = o0; /* eeprom_#ce */
+pin 13 = o1; /* #oe */
+pin 14 = o2; /* acai_#cs0 */
+pin 15 = o3; /* ce (not assigned) */
+pin 16 = o4; /* ce (not assigned) */
+pin 17 = o5; /* ce (not assigned) */
+pin 18 = o6; /* #we */
+pin 19 = o7; /* cpu_RESB */
+
+/* ROM (8k)
+ Address range: 0xe000 - 0xffff
+ Bit pattern: 0b111.............
+ */
+
+o0 = (!i0 # !i1 # !i2);
+
+/* RAM (32k)
+ Address range: 0x0000 - 0x7fff
+ Bit pattern: 0b0...............
+ */
+
+/* Connect a14 directly to ram_#ce */
+
+/* ACIA (1k)
+ Address range: 0xdc00 - 0xdfff
+ Bit pattern: 0b110111..........
+*/
+
+o2 = (!i0 # !i1 # i2 # !i3 # !i4 # !i5);
+
+/* TBA (1k)
+ Address range: 0xd800 - 0xdbff
+ Bit pattern: 0b110110..........
+*/
+
+o3 = (!i0 # !i1 # i2 # !i3 # !i4 # i5);
+
+/* TBA (1k)
+ Address range: 0xd400 - 0xd7ff
+ Bit pattern: 0b110101..........
+*/
+
+o4 = (!i0 # !i1 # i2 # !i3 # i4 # !i5);
+
+/* TBA (1k)
+ Address range: 0xd000 - 0xd3ff
+ Bit pattern: 0b110100..........
+*/
+
+o5 = (!i0 # !i1 # i2 # !i3 # i4 # i5);
+
+
+/* Inverter
+ This is not part of the bus logic.
+ The reset logic needs an inverter.
+ We provide it here.
+*/
+
+o7 = !reset_in;
+
+/*
+oe# and we# for the memory chips must be
+qualified by phi - No memory access
+in the first part of the cycle, while phi
+is low.
+*/
+
+/* oe# */
+o1 = !phi # !rwb;
+
+/* we# */
+o6 = !phi # rwb;
diff --git a/misc/6502_monitor/Makefile b/misc/6502_monitor/Makefile
@@ -0,0 +1,7 @@
+all: install run
+
+install:
+ wb_ampy -p /dev/ttyUSB0 put monitor.py
+ wb_ampy -p /dev/ttyUSB0 reset
+run:
+ konsole -e tio /dev/ttyUSB0
diff --git a/misc/6502_monitor/machine.py b/misc/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, pin, _direction, _pullup=False):
+ pass
+
+ def on(self):
+ pass
+
+ def off(self):
+ pass
+
+ def value(self):
+ return random.choice([0, 1])
+
diff --git a/misc/6502_monitor/monitor.py b/misc/6502_monitor/monitor.py
@@ -0,0 +1,266 @@
+#!/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.
+
+IMPORTANT:
+
+---------------------------------------------------------------
+Remember that ESP32 operates on 3.3v while 6502 operates on 5V!
+---------------------------------------------------------------
+
+"""
+
+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(b'\xea' * 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_number = pin
+ self.mode = None
+ self.set_mode(machine.Pin.IN)
+
+ def set_mode(self, mode):
+ if self.mode != mode:
+ self.mode = mode
+ if self.mode == machine.Pin.IN:
+ self.pin = machine.Pin(self.pin_number, self.mode,
+ machine.Pin.PULL_UP)
+ else:
+ self.pin = machine.Pin(self.pin_number, self.mode)
+ # time.sleep(.01)
+
+ def low(self):
+ self.set_mode(machine.Pin.OUT)
+ self.pin.off()
+
+ def high(self):
+ self.set_mode(machine.Pin.OUT)
+ self.pin.on()
+
+ def set(self, value):
+ self.set_mode(machine.Pin.OUT)
+ if value == 0:
+ self.pin.off()
+ else:
+ self.pin.on()
+
+ def read(self):
+ self.set_mode(machine.Pin.IN)
+ return self.pin.value()
+
+def read_address_bus():
+ rw = RW.read()
+ data = 0
+ for p in reversed(A):
+ data = ((data << 1) + p.read())
+ 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):
+ for p in D:
+ p.set(data & 1)
+ data = data >> 1
+
+def pp_mem(start, end):
+ """Pretty print memory content"""
+ print_flush(' 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_flush(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_flush(format_string.format(address=address, data=data), end=end)
+
+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_flush('WARNING: Memory not emulated; monitoring buses only.')
+
+
+def print_help_msg():
+ print_flush("""\
+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 print_flush(*args, **kwargs):
+ # MicroPython does not support flush=True, therefore
+ # we use this little wrapper
+ if esp32:
+ print(*args, **kwargs)
+ else:
+ print(*args, **kwargs, flush=True)
+
+def repl(script=[]):
+ global mem
+ global emulate_memory
+ while True:
+ update_buses_and_print_state(end='')
+ if len(script) == 0:
+ print_flush(": ", end='')
+ l = ""
+ while True:
+ c = sys.stdin.read(1)
+ if esp32:
+ print_flush(c, end='')
+ if c == '\n':
+ break
+ l += c
+ else:
+ print_flush(": {}".format(script[0]))
+ 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_flush('RST low')
+ run(2)
+ update_buses_and_print_state()
+ RST.high()
+ print_flush('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)] = bytearray(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_flush('Memory emulation off; listening to buses only.')
+ elif cmd == 'e':
+ # Turn RAM emulation on
+ emulate_memory = True
+ print_flush('Memory emulation on.')
+ elif cmd == 'h':
+ print_help_msg()
+ elif cmd == 'x':
+ # Exit
+ sys.exit(0)
+ else:
+ print_flush('Unknown command')
+
+def m():
+ global esp32
+ esp32 = (sys.platform == 'esp32')
+ global A, D, RW, PHI2, RST
+ # 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_flush("** 6502 Monitor **\n")
+ print_help_msg()
+ repl()
+
+if __name__ == '__main__':
+ m()
diff --git a/misc/clock_reset_attiny45/Makefile b/misc/clock_reset_attiny45/Makefile
@@ -0,0 +1,35 @@
+PRG = clock_and_reset
+OBJ = clock_and_reset.c
+AALPATH = /media/gb/m/projects/avr/
+LIBS = -L$(AALPATH)/libaal/ -L$(AALPATH)/libaal/32u4_usb_serial/ -laal_$(MCU_TARGET)_$(CLOCK) -lusb_serial_$(MCU_TARGET)_$(CLOCK)
+DEFS = -std=gnu99 -g -Wall -O2
+
+######################
+# Target Platform #
+######################
+
+#MCU_TARGET = atmega328p
+#MCU_TARGET = arduino_uno
+MCU_TARGET = attiny45
+#MCU_TARGET = arduino_pro_micro
+
+######################
+# Target Clock Speed #
+######################
+
+CLOCK = 1000000L
+#CLOCK = 8000000L
+#CLOCK = 16000000L
+
+######################
+# ISP #
+######################
+
+#PROGRAMMER = arduino # Direct upload to Arduino Uno
+#PROGRAMMER = avrisp # Arduino as ISP
+PROGRAMMER = usbasp # USB ASP Stick
+#PROGRAMMER = avr109 # Direct upload to Arudino ProMicro
+
+###############################################################
+
+include $(AALPATH)/libaal/Makefile.common
diff --git a/misc/clock_reset_attiny45/clock_and_reset.c b/misc/clock_reset_attiny45/clock_and_reset.c
@@ -0,0 +1,52 @@
+#include <libaal.h>
+#include <util/delay.h>
+
+
+// Set rest pin low for this number of iterations if reset is triggered.
+#define RESET_DELAY 0x4F
+
+int main (void){
+ /* usb_init(); */
+ SET_MODE_OUTPUT_DIGITAL(CONTACT_2_DDR_REG,
+ CONTACT_2_DDR_BIT);
+ SET_MODE_OUTPUT_DIGITAL(CONTACT_3_DDR_REG,
+ CONTACT_3_DDR_BIT);
+ SET_MODE_OUTPUT_DIGITAL(CONTACT_5_DDR_REG,
+ CONTACT_5_DDR_BIT);
+ SET_MODE_INPUT_PULLUP(CONTACT_6_DDR_REG,
+ CONTACT_6_DDR_BIT,
+ CONTACT_6_PORT_REG,
+ CONTACT_6_PORT_BIT);
+ SET_MODE_OUTPUT_DIGITAL(CONTACT_7_DDR_REG,
+ CONTACT_7_DDR_BIT);
+ uint8_t clock_counter = 0;
+ uint16_t reset_counter = RESET_DELAY; // power on reset
+ while (1){
+ clock_counter++;
+ // Generate clock
+ DIGITAL_WRITE(CONTACT_2_PORT_REG, CONTACT_2_PORT_BIT,
+ (clock_counter & 0x01) >> 0);
+ DIGITAL_WRITE(CONTACT_3_PORT_REG, CONTACT_3_PORT_BIT,
+ (clock_counter & 0x02) >> 1);
+ DIGITAL_WRITE(CONTACT_5_PORT_REG, CONTACT_5_PORT_BIT,
+ (clock_counter & 0x04) >> 2);
+ // Has a reset been triggered?
+ if (!DIGITAL_READ(CONTACT_6_PIN_REG,
+ CONTACT_6_PIN_BIT)) {
+ reset_counter = 0x4F;
+ }
+ // If a reset has been triggered, keep the reset pin
+ // low for some time
+ if (reset_counter > 0) {
+ DIGITAL_WRITE(CONTACT_7_PORT_REG, CONTACT_7_PORT_BIT,
+ 0);
+ reset_counter--;
+ }
+ else {
+ DIGITAL_WRITE(CONTACT_7_PORT_REG, CONTACT_7_PORT_BIT,
+ 1);
+ }
+ _delay_ms(20);
+ }
+ return 1; // dummy
+}
diff --git a/misc/sd_card/card_info.csv b/misc/sd_card/card_info.csv
@@ -0,0 +1,312 @@
+# See
+# https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/
+# http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf
+# http://ugweb.cs.ualberta.ca/~c274/resources/hardware/SDcards/SD_SDIO_specsv1.pdf
+#https://electronics.stackexchange.com/questions/375357/sd-card-initialization-problem-cmd16-wrong-response
+#http://www.rjhcoding.com/avrc-sd-interface-1.php
+http://chlazza.nfshost.com/sdcardinfo.html
+
+
+# Time [s], Analyzer Name, Decoded Protocol Result
+
+0.000095041666667,SPI,MOSI: 0x74; MISO: 0xFF # CMD52 - IO_RW_DIRECT
+0.000115083333333,SPI,MOSI: 0x80; MISO: 0xFF
+0.000135083333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.000135083333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.000155083333333,SPI,MOSI: 0x0C; MISO: 0xFF
+0.000175083333333,SPI,MOSI: 0x08; MISO: 0xFF
+0.000195083333333,SPI,MOSI: 0x9F; MISO: 0xFF
+0.000215083333333,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.000235083333333,SPI,MOSI: 0xFF; MISO: 0x04 # Answer
+
+0.000798041666667,SPI,MOSI: 0x40; MISO: 0xFF # CMD0 - GO_IDLE_STATE
+0.000818041666667,SPI,MOSI: 0x00; MISO: 0xFF # Argument 0
+0.000838041666667,SPI,MOSI: 0x00; MISO: 0xFF # Argument 1
+0.000858041666667,SPI,MOSI: 0x00; MISO: 0xFF # Argument 2
+0.000878083333333,SPI,MOSI: 0x00; MISO: 0xFF # Argument 3
+0.000898083333333,SPI,MOSI: 0x95; MISO: 0xFF # CRC
+0.000918083333333,SPI,MOSI: 0xFF; MISO: 0xFF # Dummy byte
+
+0.000938083333333,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle
+
+0.015663291666667,SPI,MOSI: 0x40; MISO: 0xFF # CMD0 - GO_IDLE_STATE
+0.015683333333333,SPI,MOSI: 0x00; MISO: 0xFF # Argument 0
+0.015703333333333,SPI,MOSI: 0x00; MISO: 0xFF # Argument 1
+0.015723333333333,SPI,MOSI: 0x00; MISO: 0xFF # Argument 2
+0.015743333333333,SPI,MOSI: 0x00; MISO: 0xFF # Argument 3
+0.015763333333333,SPI,MOSI: 0x95; MISO: 0xFF # CRC
+0.015783333333333,SPI,MOSI: 0xFF; MISO: 0xFF # Dummy byte
+
+0.015803375000000,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle
+
+0.035417000000000,SPI,MOSI: 0x48; MISO: 0xFF # CMD8 - SEND_IF_COND
+0.035437000000000,SPI,MOSI: 0x00; MISO: 0xFF # Argument 0
+0.035457041666667,SPI,MOSI: 0x00; MISO: 0xFF # Argument 1
+0.035477041666667,SPI,MOSI: 0x01; MISO: 0xFF # Argument 2
+0.035497041666667,SPI,MOSI: 0xAA; MISO: 0xFF # Argument 3
+0.035517041666667,SPI,MOSI: 0x87; MISO: 0xFF # CRC
+0.035537041666667,SPI,MOSI: 0xFF; MISO: 0xFF # Dummy byte
+
+0.035557041666667,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle
+0.035577041666667,SPI,MOSI: 0xFF; MISO: 0x00
+0.035597083333333,SPI,MOSI: 0xFF; MISO: 0x00
+0.035617083333333,SPI,MOSI: 0xFF; MISO: 0x01
+0.035637083333333,SPI,MOSI: 0xFF; MISO: 0xAA
+
+0.035871791666667,SPI,MOSI: 0x45; MISO: 0xFF # CMD5 - IO_SEND_OP_COND
+0.035891791666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.035911833333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.035931833333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.035951833333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.035971833333333,SPI,MOSI: 0x5B; MISO: 0xFF
+0.035991833333333,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.036011833333333,SPI,MOSI: 0xFF; MISO: 0x05
+
+0.036242375000000,SPI,MOSI: 0x7B; MISO: 0xFF # CMD59 - CRC_ON_OFF
+0.036262375000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.036282375000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.036302375000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.036322375000000,SPI,MOSI: 0x01; MISO: 0xFF # CRC on
+0.036342416666667,SPI,MOSI: 0x83; MISO: 0xFF
+0.036362416666667,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.036382416666667,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle
+
+0.036613916666667,SPI,MOSI: 0x77; MISO: 0xFF # CMD 55 - APP_CMD
+0.036633916666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.036653916666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.036673916666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.036693916666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.036713958333333,SPI,MOSI: 0x65; MISO: 0xFF
+0.036733958333333,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.036753958333333,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle
+
+0.036955458333333,SPI,MOSI: 0x69; MISO: 0xFF # ACMD41 - SD_SEND_OP_COND
+0.036975458333333,SPI,MOSI: 0x40; MISO: 0xFF
+0.036995458333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.037015500000000,SPI,MOSI: 0x80; MISO: 0xFF
+0.037035500000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.037055500000000,SPI,MOSI: 0x17; MISO: 0xFF
+0.037075500000000,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.037095500000000,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle
+
+0.045396416666667,SPI,MOSI: 0x77; MISO: 0xFF # CMD 55 - APP_CMD
+0.045416416666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.045436416666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.045456416666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.045476416666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.045496416666667,SPI,MOSI: 0x65; MISO: 0xFF
+0.045516416666667,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.045536458333333,SPI,MOSI: 0xFF; MISO: 0x01 # Response - Idle
+
+0.045734000000000,SPI,MOSI: 0x69; MISO: 0xFF # ACMD41 - SD_SEND_OP_COND
+0.045754000000000,SPI,MOSI: 0x40; MISO: 0xFF
+0.045774000000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.045794000000000,SPI,MOSI: 0x80; MISO: 0xFF
+0.045814000000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.045834041666667,SPI,MOSI: 0x17; MISO: 0xFF
+0.045854041666667,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.045874041666667,SPI,MOSI: 0xFF; MISO: 0x00
+
+0.046096583333333,SPI,MOSI: 0x7A; MISO: 0xFF # CMD58 - READ_OCR
+0.046116583333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.046136583333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.046156583333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.046176625000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.046196625000000,SPI,MOSI: 0xFD; MISO: 0xFF
+0.046216625000000,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.046236625000000,SPI,MOSI: 0xFF; MISO: 0x00
+0.046256625000000,SPI,MOSI: 0xFF; MISO: 0x80
+0.046276625000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.046296625000000,SPI,MOSI: 0xFF; MISO: 0x80
+0.046316666666667,SPI,MOSI: 0xFF; MISO: 0x00
+
+0.046575458333333,SPI,MOSI: 0x4A; MISO: 0xFF # CMD10 - SEND_CID
+0.046595458333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.046615458333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.046635458333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.046655458333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.046675458333333,SPI,MOSI: 0x1B; MISO: 0xFF
+0.046695458333333,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.046715500000000,SPI,MOSI: 0xFF; MISO: 0x00
+0.046735500000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.046755500000000,SPI,MOSI: 0xFF; MISO: 0xFE
+0.046775500000000,SPI,MOSI: 0xFF; MISO: 0x1B
+0.046795500000000,SPI,MOSI: 0xFF; MISO: 0x53
+0.046815500000000,SPI,MOSI: 0xFF; MISO: 0x4D
+0.046835541666667,SPI,MOSI: 0xFF; MISO: 0x30
+0.046855541666667,SPI,MOSI: 0xFF; MISO: 0x30
+0.046875541666667,SPI,MOSI: 0xFF; MISO: 0x30
+0.047044625000000,SPI,MOSI: 0xFF; MISO: 0x30
+0.047064625000000,SPI,MOSI: 0xFF; MISO: 0x30
+0.047084666666667,SPI,MOSI: 0xFF; MISO: 0x10
+0.047104666666667,SPI,MOSI: 0xFF; MISO: 0x1A
+0.047124666666667,SPI,MOSI: 0xFF; MISO: 0x9F
+0.047144666666667,SPI,MOSI: 0xFF; MISO: 0x59
+0.047164666666667,SPI,MOSI: 0xFF; MISO: 0x58
+0.047184666666667,SPI,MOSI: 0xFF; MISO: 0x00
+0.047204708333333,SPI,MOSI: 0xFF; MISO: 0xAC
+0.047224708333333,SPI,MOSI: 0xFF; MISO: 0xDB
+0.047244708333333,SPI,MOSI: 0xFF; MISO: 0x42
+0.047264708333333,SPI,MOSI: 0xFF; MISO: 0x13
+
+0.047573750000000,SPI,MOSI: 0x49; MISO: 0xFF # CMD9 - SEND_CSD
+0.047593791666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.047613791666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.047633791666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.047653791666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.047673791666667,SPI,MOSI: 0xAF; MISO: 0xFF
+0.047693791666667,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.047713791666667,SPI,MOSI: 0xFF; MISO: 0x00
+0.047733833333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.047753833333333,SPI,MOSI: 0xFF; MISO: 0xFE
+0.047773833333333,SPI,MOSI: 0xFF; MISO: 0x00
+0.047793833333333,SPI,MOSI: 0xFF; MISO: 0x7F
+0.047813833333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.047833833333333,SPI,MOSI: 0xFF; MISO: 0x32
+0.047853833333333,SPI,MOSI: 0xFF; MISO: 0x5B
+0.047873875000000,SPI,MOSI: 0xFF; MISO: 0x5A
+0.047968083333333,SPI,MOSI: 0xFF; MISO: 0x83
+0.047988125000000,SPI,MOSI: 0xFF; MISO: 0xBA
+0.048008125000000,SPI,MOSI: 0xFF; MISO: 0xF6
+0.048028125000000,SPI,MOSI: 0xFF; MISO: 0xDB
+0.048048125000000,SPI,MOSI: 0xFF; MISO: 0xDF
+0.048068125000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.048088125000000,SPI,MOSI: 0xFF; MISO: 0x0E
+0.048108125000000,SPI,MOSI: 0xFF; MISO: 0x80
+0.048128166666667,SPI,MOSI: 0xFF; MISO: 0x00
+0.048148166666667,SPI,MOSI: 0xFF; MISO: 0xB5
+0.048168166666667,SPI,MOSI: 0xFF; MISO: 0x2F
+0.048188166666667,SPI,MOSI: 0xFF; MISO: 0x8B
+
+0.048452666666667,SPI,MOSI: 0x50; MISO: 0xFF # CMD16 - SET_BLOCKLEN
+0.048472666666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.048492708333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.048512708333333,SPI,MOSI: 0x02; MISO: 0xFF
+0.048532708333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.048552708333333,SPI,MOSI: 0x15; MISO: 0xFF
+0.048572708333333,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.048592708333333,SPI,MOSI: 0xFF; MISO: 0x00
+
+0.048823166666667,SPI,MOSI: 0x77; MISO: 0xFF # CMD55 - APP_CMD
+0.048843166666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.048863166666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.048883166666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.048903166666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.048923166666667,SPI,MOSI: 0x65; MISO: 0xFF
+0.048943208333333,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.048963208333333,SPI,MOSI: 0xFF; MISO: 0x00
+
+0.049160750000000,SPI,MOSI: 0x73; MISO: 0xFF # CMD1 - SEND_SCR
+0.049180750000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.049200750000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.049220750000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.049240750000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.049260750000000,SPI,MOSI: 0xC7; MISO: 0xFF
+0.049280791666667,SPI,MOSI: 0xFF; MISO: 0xFF
+
+0.049300791666667,SPI,MOSI: 0xFF; MISO: 0x00
+
+0.049320791666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049340791666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049360791666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049380791666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049400791666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049420833333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049440833333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049460833333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049564125000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049584125000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049604125000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049624125000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049644166666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049664166666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049684166666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049704166666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049806708333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049826750000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049846750000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049866750000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049886750000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049906750000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049926750000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.049946750000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.050041333333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.050061333333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.050081333333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.050101333333333,SPI,MOSI: 0xFF; MISO: 0xFE
+0.050121375000000,SPI,MOSI: 0xFF; MISO: 0x02
+0.050141375000000,SPI,MOSI: 0xFF; MISO: 0x25
+0.050161375000000,SPI,MOSI: 0xFF; MISO: 0x80
+0.050181375000000,SPI,MOSI: 0xFF; MISO: 0x00
+0.050275958333333,SPI,MOSI: 0xFF; MISO: 0x00
+0.050295958333333,SPI,MOSI: 0xFF; MISO: 0x00
+0.050316000000000,SPI,MOSI: 0xFF; MISO: 0x00
+0.050336000000000,SPI,MOSI: 0xFF; MISO: 0x00
+0.050356000000000,SPI,MOSI: 0xFF; MISO: 0x4C
+0.050376000000000,SPI,MOSI: 0xFF; MISO: 0xD7
+0.050704541666667,SPI,MOSI: 0x77; MISO: 0xFF
+0.050724541666667,SPI,MOSI: 0x00; MISO: 0xFF
+0.050744583333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.050764583333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.050784583333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.050804583333333,SPI,MOSI: 0x65; MISO: 0xFF
+0.050824583333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.050844583333333,SPI,MOSI: 0xFF; MISO: 0x00
+0.051046083333333,SPI,MOSI: 0x73; MISO: 0xFF
+0.051066083333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.051086083333333,SPI,MOSI: 0x00; MISO: 0xFF
+0.051106125000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.051126125000000,SPI,MOSI: 0x00; MISO: 0xFF
+0.051146125000000,SPI,MOSI: 0xC7; MISO: 0xFF
+0.051166125000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051186125000000,SPI,MOSI: 0xFF; MISO: 0x00
+0.051206125000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051226125000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051246166666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051266166666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051286166666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051306166666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051326166666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051346166666667,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051441583333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051461583333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051481583333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051501583333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051521583333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051541583333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051561625000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051581625000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051676208333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051696208333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051716208333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051736208333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051756250000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051776250000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051796250000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051816250000000,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051910833333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051930833333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051950833333333,SPI,MOSI: 0xFF; MISO: 0xFF
+0.051970833333333,SPI,MOSI: 0xFF; MISO: 0xFE
+0.051990833333333,SPI,MOSI: 0xFF; MISO: 0x02
+0.052010833333333,SPI,MOSI: 0xFF; MISO: 0x25
+0.052030875000000,SPI,MOSI: 0xFF; MISO: 0x80
+0.052050875000000,SPI,MOSI: 0xFF; MISO: 0x00
+0.052145458333333,SPI,MOSI: 0xFF; MISO: 0x00
+0.052165458333333,SPI,MOSI: 0xFF; MISO: 0x00
+0.052185458333333,SPI,MOSI: 0xFF; MISO: 0x00
+0.052205458333333,SPI,MOSI: 0xFF; MISO: 0x00
+0.052225458333333,SPI,MOSI: 0xFF; MISO: 0x4C
+0.052245500000000,SPI,MOSI: 0xFF; MISO: 0xD7
diff --git a/misc/sd_card/esp32_sdcard.py b/misc/sd_card/esp32_sdcard.py
@@ -0,0 +1,14 @@
+# See
+# https://github.com/micropython/micropython/blob/master/drivers/sdcard/sdcard.py
+# for protocol
+
+# Mode = 0
+# CPOL = 0 (low idle)
+# CPHA = 0 (first/raising edge)
+import machine, uos
+c = machine.SDCard(slot=2, sck=18, miso=19, mosi=23, cs=5, freq=2000000)
+print(c.info())
+uos.mount(c, "/sd")
+uos.listdir('/sd')
+
+# See card_info.csv for decoded protocol
diff --git a/notizen.txt b/notizen.txt
@@ -1,152 +0,0 @@
-* 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/roms/ROMS.txt b/roms/ROMS.txt
@@ -0,0 +1,25 @@
+boot/
+
+This is the main "operating system" ROM. It provides some library
+functions and most notably a mechanism to load software from the
+serial interface into the RAM and execute it.
+
+nop/
+
+Just a sequence of NOP instructions. Used to test if the CPU
+interacts with the EEPROM.
+
+serial_char_out/
+
+Outputs "A" to the serial interface in an endless loop. Used to test
+if the CPU interacts with the ACAI W65C22.
+
+serial_echo/
+
+Echos all characters input on the serial line. Used to test if the CPU
+interacts with the ACAI W65C22.
+
+simple_loop/
+
+Just endlessly jumping between memory locations. Used to test if the
+CPU interacts with the EEPROM.
diff --git a/roms/boot/Makefile b/roms/boot/Makefile
@@ -0,0 +1,19 @@
+TARGET=boot
+SYMON=java -jar ../../emulator/symon-1.3.1.jar -cpu 65c02
+
+all: $(TARGET) $(TARGET)_symon
+
+$(TARGET): $(TARGET).s
+ xa -l $(TARGET).l -r -o $(TARGET) $<
+
+$(TARGET)_symon: $(TARGET).s
+ xa -DSYMON -l $(TARGET)_symon.l -r -o $(TARGET)_symon $<
+
+emulation: $(TARGET)_symon
+ $(SYMON) -cpu 65c02 -rom ./$(TARGET)_symon
+
+flash: $(TARGET)
+ sudo ~/opt/minipro-0.3/minipro -p AT28C64B -w $<
+
+clean:
+ rm -f $(TARGET) $(TARGET).l $(TARGET)_symon $(TARGET)_symon.l
diff --git a/roms/boot/boot.py b/roms/boot/boot.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+"""Upload software to 8-bit computer"""
+
+import serial
+import sys
+import argparse
+import time
+import pdb
+
+SERIAL_PORT = '/dev/ttyUSB0'
+SERIAL_SPEED = 19200
+
+def test_serial_echo():
+ with serial.Serial(SERIAL_PORT, SERIAL_SPEED) as s:
+ # Wait for '\n'
+ print("Reset 8-bit computer.")
+ s.readline()
+ print("Writing & reading 0x100 bytes.")
+ for i in range(0x00, 0x100):
+ # Serial echo may echo '\r' as '\r\n', therefore
+ # we omit it.
+ if i != 13:
+ print(".", end="", flush=True)
+ s.write(bytes([i]))
+ b = s.read(1)
+ if (ord(b) != i):
+ # pdb.set_trace()
+ print('\nExpected 0x{:02X}, got 0x{:02X}.'.format(i, ord(b)))
+ print()
+
+
+def upload_program(filename):
+ with open(filename, 'rb') as f:
+ data = f.read()
+ # Pad to 256 byte block size
+ pad = 0x100 - (len(data) % 0x100)
+ if (pad != 0x100):
+ data = data + b'\x00'*pad
+ blocks = int(len(data) / 0x100)
+ with serial.Serial(SERIAL_PORT, SERIAL_SPEED) as s:
+ # Wait for '\n'
+ print("Reset 8-bit computer.")
+ s.readline()
+ time.sleep(1)
+ print("Uploading program.")
+ e = str.encode(chr(blocks))
+ s.write(e)
+ r = s.read(1)
+ if (r != e):
+ print('\nERROR: Transmission failed. Expected 0x{:02X}, got 0x{:02X}.'.format(e, r))
+ sys.exit(-1)
+
+ for d in data:
+ print(".", end="", flush=True)
+ s.write(bytes([d]))
+ b = s.read(1)
+ if (ord(b) != d):
+ print('\nERROR: Transmission failed. Expected 0x{:02X}, got 0x{:02X}.'.format(d, ord(b)))
+ sys.exit(-1)
+ print('\nDone.')
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Upload to 8-bit computer')
+ parser.add_argument('filename', metavar='FILENAME', type=str,
+ help='Program to upload')
+ args = parser.parse_args()
+ upload_program(filename=args.filename)
+
diff --git a/roms/boot/boot.s b/roms/boot/boot.s
@@ -0,0 +1,279 @@
+;;; Receive data from termina. Store it in RAM & execute it.
+
+// #define BPS_300
+#define BPS_19200
+
+#ifdef SYMON
+ * = $c000 ; Symon
+ acia_base = $8800 ; Symon
+#else
+ * = $e000
+ acia_base = $dc00
+#endif
+ acia_data_reg = acia_base
+ acia_status_reg = acia_base + 1
+ acia_cmd_reg = acia_base + 2
+ acia_ctrl_reg = acia_base + 3
+
+;;; Function signatures and local variables
+
+;;; puts
+;;; Input:
+;;; puts_str, put_str+1:
+;;; 2 bytes on zero page containing string address
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, puts_str, put_str+1
+
+ puts_str = $20
+
+;;; putc
+;;; Input:
+;;; a:
+;;; Character to be printed
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y
+
+;;; getc
+;;; Input:
+;;; a:
+;;; Character to be send
+;;; Output:
+;;; -
+;;; Changes:
+;;; a
+
+init:
+ jsr init_acia
+/* jsr count_hex
+ jsr memory_test
+ */
+ jmp download_program
+
+count_hex:
+ lda #$00
+_count_hex_loop:
+ pha
+ jsr puth
+ pla
+ inc
+ bne _count_hex_loop
+ rts
+
+download_program:
+ ;; Print start banner
+ lda #<ready_str
+ sta puts_str
+ lda #>ready_str
+ sta puts_str+1
+ jsr puts
+ ;; Get number of 0x100 byte blocks to be read
+ jsr getc
+ jsr putc
+ ;; We start write to $0300. Thus the last block
+ ;; is ($3 + a) * $100
+ clc
+ adc #$03
+ sta $12
+ ;; Read n * 0x100 bytes from acia
+ ;; and store them at 0x0300..
+ ;; We use address $10 to store the target
+ ;; address
+ lda #$00
+ sta $10
+ lda #$03
+ sta $11
+transmit_block:
+ jsr getc
+ ldy #$00
+ sta ($10), y
+ jsr putc
+ inc $10
+ bne transmit_block
+ ;; Transmission of one block completed.
+ ;; If we are not done, increase
+ ;; MSB of target address and continue
+ inc $11
+ lda $11
+ cmp $12
+ bne transmit_block
+ ;; Start program
+ jmp $0300
+
+#define NL $0d, $0a
+#define ZERO .byt $00
+
+memory_test_pointer = $10
+memory_test:
+ lda #<memory_test_str
+ sta puts_str
+ lda #>memory_test_str
+ sta puts_str+1
+ jsr puts
+ lda #$30
+ sta memory_test_pointer
+ lda #$00
+ sta memory_test_pointer+1
+loop:
+ // lda #'.'
+ // jsr putc
+ lda #$00
+ lda (memory_test_pointer)
+ pha
+ sta (memory_test_pointer)
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ lda (memory_test_pointer)
+ cmp #$00
+ bne memory_error
+ lda #$FF
+ sta (memory_test_pointer)
+ lda (memory_test_pointer)
+ cmp #$FF
+ bne memory_error
+cont:
+ pla
+ sta (memory_test_pointer)
+ inc memory_test_pointer
+ bne loop
+b0:
+ inc memory_test_pointer+1
+ lda memory_test_pointer+1
+ cmp #$80
+ bne loop
+ lda #<memory_test_complete_str
+ sta puts_str
+ lda #>memory_test_complete_str
+ sta puts_str+1
+ jsr puts
+ rts
+memory_error:
+ pha
+ lda #<memory_error_str
+ sta puts_str
+ lda #>memory_error_str
+ sta puts_str+1
+ jsr puts
+ jsr memory_print_location
+ pla
+ jsr puth
+ ;; NL
+ lda #$0d
+ jsr putc
+ lda #$0a
+ jsr putc
+ jmp cont
+
+memory_print_location:
+ lda memory_test_pointer+1
+ jsr puth
+ lda memory_test_pointer
+ jsr puth
+ ;; NL
+ lda #$0d
+ jsr putc
+ lda #$0a
+ jsr putc
+ rts
+
+
+memory_test_str:
+ .asc "Performing memory test ...", NL, $00
+memory_test_complete_str:
+ .asc "Memory test completed.", NL, $00
+memory_error_str:
+ .asc "Memory error!", NL, $00
+ready_str:
+ .asc "READY!", NL, $00
+
+getc:
+ ;; Read character from acia
+ lda acia_status_reg
+ and #%00001000
+ beq getc
+ lda acia_data_reg
+ rts
+
+endless_a:
+ lda #'A'
+ jsr putc
+ jmp endless_a
+
+puts:
+ ;; Send string terminated by '\0'
+ ldy #$00
+_puts_loop:
+ lda (puts_str), y
+ beq _puts_end
+ phy
+ jsr putc
+ ply
+ iny
+ jmp _puts_loop
+_puts_end:
+ rts
+
+puth:
+ ;; Send a as hex number
+ pha
+ lsr
+ lsr
+ lsr
+ lsr
+ jsr _puth_nibble
+ pla
+ and #$0F
+ jsr _puth_nibble
+ rts
+_puth_nibble:
+ clc
+ adc #$30 ; Decimal number
+ cmp #$3A ; >10 ?
+ bmi _puth_puts
+ adc #$26
+_puth_puts:
+ jsr putc
+ rts
+
+init_acia:
+ ;; Reset acai
+ sta acia_status_reg
+ lda #%00011111 ; 19200 bps, 8 data bits, 1 stop bit
+ sta acia_ctrl_reg
+ ;; No parity, no echo, no interrupts, DTR ready
+ lda #%11001011
+ sta acia_cmd_reg
+ rts
+
+
+putc:
+ ;; Send character
+ sta acia_data_reg
+ ;; Length of delay loop determined experimentally
+ ldx #$1
+_putc_wait_loop:
+ // ldy #$5e
+ ldy #$88
+_putc_inner_loop:
+ dey
+ bne _putc_inner_loop
+ dex
+ bne _putc_wait_loop
+ rts
+
+ ;; Vectors
+ .dsb $fffa-*, $ff
+ .word $0000 ; nmi
+ .word init ; reset
+ .word $0000 ; irq
diff --git a/roms/nop/nop.bin b/roms/nop/nop.bin
@@ -0,0 +1 @@
+κκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκκ+
\ No newline at end of file
diff --git a/roms/serial_char_out/Makefile b/roms/serial_char_out/Makefile
@@ -0,0 +1,10 @@
+TARGET=serial_char_out
+
+all: $(TARGET).s
+ xa -o $(TARGET) $<
+
+flash: $(TARGET)
+ sudo ~/opt/minipro-0.3/minipro -p AT28C64B -w $<
+
+clean:
+ rm -f $(TARGET)
diff --git a/roms/serial_char_out/count_receive_errors.py b/roms/serial_char_out/count_receive_errors.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+"""Count receive errors."""
+
+import pdb
+import serial
+
+character = b'A'
+block_size = 1000
+SERIAL_PORT = '/dev/ttyUSB0'
+SERIAL_SPEED = 19200
+
+with serial.Serial(SERIAL_PORT, SERIAL_SPEED) as s:
+ errors = 0
+ total = 0
+ while True:
+ for _ in range(block_size):
+ total += 1
+ c = s.read(1)
+ if c != character:
+ # pdb.set_trace()
+ print('Got "{}"'.format(c))
+ errors += 1
+ print('{} errors of {} ({:01.4f} %)'.format(errors, total,
+ float(errors)/total))
+
diff --git a/roms/serial_char_out/serial_char_out.s b/roms/serial_char_out/serial_char_out.s
@@ -0,0 +1,51 @@
+ ;; Output sequence of 'A' via ACIA 65C51
+
+// #define SYMON
+
+#ifdef SYMON
+ * = $c000 ; Symon
+ acia_base = $8800 ; Symon
+#else
+ * = $e000
+ acia_base = $dc00
+#endif
+ acia_data_reg = acia_base
+ acia_status_reg = acia_base + 1
+ acia_cmd_reg = acia_base + 2
+ acia_ctrl_reg = acia_base + 3
+
+init:
+ ;; Reset acai
+ sta acia_status_reg
+ ;; 300 bps, 8 data bits, 1 stop bit
+ lda #%00010110
+ sta acia_ctrl_reg
+ ;; No parity, no echo, no interrupts, DTR ready
+ lda #%11001011
+ sta acia_cmd_reg
+send_loop:
+ lda #"A"
+ sta acia_data_reg
+ ;; 4 cycles per inner loop run
+ ;; At 1 Mhz and 300 bps, transmitting one bit
+ ;; takes 1*10**6/300 = 3333.3 cycles
+ ;; Including start bit and stop bit, we have
+ ;; to transmit 10 bit for 1 byte.
+ ;; Thus, we should run the loop for
+ ;; 3333.3*10/4 = 8333.25 0 $208d iterations.
+ ;; We run for 2100 iterations.
+ ldx #$21 ; 2 cycles
+wait_loop:
+ ldy #$ff ; 2 cycles
+inner_loop:
+ dey ; 2 cycles
+ bne inner_loop ; 2 cycles
+ dex ; 2 cycles
+ bne wait_loop ; 2 cycles
+ jmp send_loop ; 4 cycles
+
+ ;; Vectors
+ .dsb $fffa-*, $ff
+ .word $0000 ; nmi
+ .word init ; reset
+ .word $0000 ; irq
diff --git a/roms/serial_echo/Makefile b/roms/serial_echo/Makefile
@@ -0,0 +1,15 @@
+TARGET=serial_echo
+SYMON=java -jar ../../emulator/symon-1.3.1.jar
+
+all: $(TARGET).s
+ xa -o $(TARGET) $<
+
+emulation: $(TARGET).s
+ xa -DSYMON -o $(TARGET)_symon $<
+ $(SYMON) -rom ./$(TARGET)_symon
+
+upload: $(TARGET)
+ sudo ~/opt/minipro-0.3/minipro -p AT28C64B -w $<
+
+clean:
+ rm -f $(TARGET) $(TARGET)_symon
diff --git a/roms/serial_echo/serial_echo.s b/roms/serial_echo/serial_echo.s
@@ -0,0 +1,103 @@
+ ;; Echo characters read via ACIA 65C51
+
+// #define BPS_300
+#define BPS_19200
+
+#ifdef SYMON
+ * = $c000 ; Symon
+ acia_base = $8800 ; Symon
+#else
+ * = $e000
+ acia_base = $dc00
+#endif
+ acia_data_reg = acia_base
+ acia_status_reg = acia_base + 1
+ acia_cmd_reg = acia_base + 2
+ acia_ctrl_reg = acia_base + 3
+
+init:
+ ;; Reset acai
+ sta acia_status_reg
+#ifdef BPS_300
+ lda #%00010110 ; 300 bps, 8 data bits, 1 stop bit
+#endif
+#ifdef BPS_19200
+ lda #%00011111 ; 19200 bps, 8 data bits, 1 stop bit
+#endif
+ sta acia_ctrl_reg
+ ;; No parity, no echo, no interrupts, DTR ready
+ lda #%11001011
+ sta acia_cmd_reg
+ ;; Print banner
+ lda #'R'
+ jsr send_char
+ lda #'E'
+ jsr send_char
+ lda #'A'
+ jsr send_char
+ lda #'D'
+ jsr send_char
+ lda #'Y'
+ jsr send_char
+ lda #'.'
+ jsr send_char
+ lda #$0d ; '\r'
+ jsr send_char
+ lda #$0a ; '\n'
+ jsr send_char
+read_write_loop:
+ jsr receive_char
+ ;; Replace '\r' by '\r\n'
+ cmp #$0d ; '\r'
+ bne normal_char
+ lda #$0a ; '\n'
+ jsr send_char
+ lda #$0d ; '\r'
+ jsr send_char
+ jmp read_write_loop
+normal_char:
+ jsr send_char
+ jmp read_write_loop
+
+receive_char:
+ ;; Read character from acia
+ lda acia_status_reg
+ and #%00001000
+ beq receive_char
+ lda acia_data_reg
+ rts
+
+send_char:
+ ;; Send character
+ sta acia_data_reg
+ ;; 4 cycles per inner loop run
+#ifdef BPS_300
+ ;; At 1 Mhz and 300 bps, transmitting one bit
+ ;; takes 1*10**6/300 = 3333.3 cycles
+ ;; Including start bit and stop bit, we have
+ ;; to transmit 10 bit for 1 byte.
+ ;; Thus, we should run the loop for
+ ;; 3333.3*10/4 = 8333.25 0 $208d iterations.
+ ;; We run for 2100 iterations.
+ ldx #$21 ; 2 cycles
+wait_loop:
+ ldy #$ff ; 2 cycles
+#endif
+#ifdef BPS_19200
+ ;; 1*10**6/19200*10/4 = 130.21
+ ldx #$01 ; 2 cycles
+wait_loop:
+ ldy #$88 ; 2 cycles
+#endif
+inner_loop:
+ dey ; 2 cycles
+ bne inner_loop ; 2 cycles
+ dex ; 2 cycles
+ bne wait_loop ; 2 cycles
+ rts
+
+ ;; Vectors
+ .dsb $fffa-*, $ff
+ .word $0000 ; nmi
+ .word init ; reset
+ .word $0000 ; irq
diff --git a/roms/simple_loop/Makefile b/roms/simple_loop/Makefile
@@ -0,0 +1,10 @@
+TARGET=loop
+
+$TARGET: $(TARGET).s
+ xa -o $(TARGET) $<
+
+upload: $(TARGET)
+ sudo ~/opt/minipro-0.3/minipro -p AT28C64B -w $<
+
+clean:
+ rm -f $(TARGET)
diff --git a/roms/simple_loop/loop.s b/roms/simple_loop/loop.s
@@ -0,0 +1,16 @@
+ * = $e000 ; ROM starts here
+start:
+ jmp middle
+
+ .dsb $e010-*, $ff
+middle:
+ jmp high
+
+ .dsb $e100-*, $ff
+high: jmp start
+
+ ;; Vectors
+ .dsb $fffa-*, $ff
+ .word $0000 ; nmi
+ .word start ; reset
+ .word $0000 ; irq
diff --git a/skizze.kra b/skizze.kra
Binary files differ.
diff --git a/sw/SW.txt b/sw/SW.txt
@@ -0,0 +1,4 @@
+mem_test/
+
+Tests RAM write and read operations. Used to test if the CPU interacts
+with the RAM.
diff --git a/sw/mem_test/Makefile b/sw/mem_test/Makefile
@@ -0,0 +1,15 @@
+TARGET=mem_test
+
+all: $(TARGET) $(TARGET)_symon
+
+$(TARGET): $(TARGET).s
+ xa -l $(TARGET).l -r -o $(TARGET) $<
+
+$(TARGET)_symon: $(TARGET).s
+ xa -DSYMON -l $(TARGET)_symon.l -r -o $(TARGET)_symon $<
+
+upload: $(TARGET)
+ ../../roms/boot/boot.py $(TARGET)
+
+clean:
+ rm -f $(TARGET) $(TARGET)_symon $(TARGET).l $(TARGET)_symon.l
diff --git a/sw/mem_test/liba.h b/sw/mem_test/liba.h
@@ -0,0 +1,123 @@
+;;; Function signatures and local variables
+
+;;; init_acia
+;;; Initialize acai for communication
+;;; Input:
+;;; -
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, acai-registers
+
+#ifdef SYMON
+ acia_base = $8800 ; Symon
+#else
+ acia_base = $dc00
+#endif
+ acia_data_reg = acia_base
+ acia_status_reg = acia_base + 1
+ acia_cmd_reg = acia_base + 2
+ acia_ctrl_reg = acia_base + 3
+
+;;; puts
+;;; Send up to 256 characters terminated by null via acia
+;;; Input:
+;;; puts_str, put_str+1:
+;;; 2 bytes on zero page containing string address
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, puts_str, put_str+1
+
+ puts_str = $10
+
+;;; putc
+;;; Send character via acia
+;;; Input:
+;;; a:
+;;; Character to be printed
+;;; Output:
+;;; -
+;;; Changes:
+;;; x, acai-registers
+
+;;; puth
+;;; Convert byte two hex and send via acia
+;;; Input:
+;;; a:
+;;; Byte to be printed
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, acai-registers, c-flag
+
+;;; putnl
+;;; Send newline via acia
+;;; Input:
+;;; -
+;;; Output:
+;;; -
+;;; Changes:
+;;; a
+
+;;; Macros
+
+;;; PRINT(addr)
+;;; Send zero terminated string at addr via acia.
+;;; Input:
+;;; addr:
+;;; Address of zero terminated string (<= 256 characters)
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, puts_str, put_str+1
+
+;;; PRINTS(string) (<= 255 characters)
+;;; Send string via acia
+;;; Input:
+;;; string:
+;;; String to be send.
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, puts_str, put_str+1
+
+;;; PRINTSNL(string) (<= 253 characters)
+;;; Send string and newline via acia
+;;; Input:
+;;; string:
+;;; String to be send.
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, x, y, puts_str, put_str+1
+
+
+
+#define PRINT(addr) \
+ .(: \
+ lda #<addr: \
+ sta puts_str: \
+ lda #>addr: \
+ sta puts_str+1: \
+ jsr puts: \
+ .)
+
+#define PRINTS(string) \
+ .(: \
+ PRINT(saddr): \
+ jmp cont: \
+saddr: \
+ .asc string, $00:\
+cont: \
+ .)
+
+#define PRINTSNL(string) \
+ .(: \
+ PRINT(saddr): \
+ jmp cont: \
+saddr: \
+ .asc string, $0d, $0a, $00:\
+cont: \
+ .)
+
diff --git a/sw/mem_test/liba.s b/sw/mem_test/liba.s
@@ -0,0 +1,60 @@
+init_acia:
+ ;; Reset acai
+ sta acia_status_reg
+ lda #%00011111 ; 19200 bps, 8 data bits, 1 stop bit
+ sta acia_ctrl_reg
+ ;; No parity, no echo, no interrupts, DTR ready
+ lda #%11001011
+ sta acia_cmd_reg
+ rts
+
+puts:
+ ;; Send string terminated by '\0'
+ ldy #$00
+_puts_loop:
+ lda (puts_str), y
+ beq _puts_end
+ jsr putc
+ iny
+ jmp _puts_loop
+_puts_end:
+ rts
+
+putc:
+ ;; Send character
+ sta acia_data_reg
+ ;; Length of delay loop determined experimentally
+ ldx #$88
+_putc_loop:
+ dex
+ bne _putc_loop
+ rts
+
+puth:
+ ;; Send a as hex number
+ pha
+ lsr
+ lsr
+ lsr
+ lsr
+ jsr _puth_nibble
+ pla
+ and #$0F
+ jsr _puth_nibble
+ rts
+_puth_nibble:
+ clc
+ adc #$30 ; Decimal number
+ cmp #$3A ; >10 ?
+ bmi _puth_putc
+ adc #$26
+_puth_putc:
+ jsr putc
+ rts
+
+putnl:
+ lda #$0d
+ jsr putc
+ lda #$0a
+ jsr putc
+ rts
diff --git a/sw/mem_test/mem_test.s b/sw/mem_test/mem_test.s
@@ -0,0 +1,59 @@
+#include "liba.h"
+
+ * = $0300
+init:
+ .(
+ jsr init_acia
+loop:
+ jsr mem_test
+end:
+ jmp loop
+ // jmp end
+ .)
+
+mem_test:
+ .(
+ PRINTSNL("Memory test")
+ ;; Address to be tested is stored as 16 bit value at $40.
+ ;; We start the memory test at address $0400.
+ ;; The test runs up to $8000. It fails at the last address,
+ ;; because memory ends at $7fff.
+ start = $0400
+ lda #<start
+ sta $40
+ lda #>start
+ sta $41
+loop:
+ jsr mem_cell_test
+ inc $40
+ bne loop
+ inc $41
+ lda $41
+ cmp #$80
+ bne loop
+ rts
+
+mem_cell_test:
+ lda $41
+ jsr puth
+ lda $40
+ jsr puth
+ lda #$00
+ sta ($40)
+ lda ($40)
+ cmp #$00
+ bne fail
+ lda #$ff
+ sta ($40)
+ lda ($40)
+ cmp #$ff
+ bne fail
+ PRINTSNL(" OK")
+ rts
+fail:
+ PRINTSNL(" Fail!")
+ rts
+ .)
+
+
+#include "liba.s"