commit 9138f242ad71e949398212e19a32670699e03a8d
parent 255bb36f28a68e2e07a532b2c660f0ec72c58d53
Author: Gerd Beuster <gerd@frombelow.net>
Date: Fri, 16 Oct 2020 18:28:16 +0200
Serial line input, including upload and reset by DTR toggling
Diffstat:
19 files changed, 462 insertions(+), 591 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -11,11 +11,8 @@ 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
+sw/**/*.bin
+sw/**/*.l
+roms/boot/liba.h
+roms/**/*.bin
+roms/**/*.l
diff --git a/doc/Documentation.txt b/doc/Documentation.txt
@@ -11,6 +11,24 @@ 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.)
+** Program upload and reset logic
+
+Resets can be triggered in two ways: A reset button is connected to an
+NE555 in a monostable configuration. Alternatively, a reset can be
+triggered by setting DTR of the serial interface to high. The latter
+is used for program upload into RAM. See section Boot Sequence.
+
+This implies that the computer will remain in reset state until a
+serial terminal is opened if DTR is connected. In order to avoid this,
+e.g. because you want to run a program while not serial interface is
+attached, you can either configure your serial interface to keep DTR
+low at all times (stty -F <serial interface> -hup) or you do not
+connect the DTR line of the USB2Serial converter.
+
+
+Note that the reset line of the 65C02 is active on low. We use the
+AFT16V8B - which mainly provides the bus logic - as an inverter.
+
** Memory Map
Lowest 32K are RAM, highest 8K are ROM. ACAI is in between. See
@@ -25,12 +43,18 @@ hw/bus_logic/BUS_LOGIC.PLD for details.
- sw/ - "Userland" software to be loaded into RAM by a suitable ROM. See
sw/SW.txt for a description.
+** Standard Library
+
+A standard library is part of the boot ROM located at roms/boot/. In
+order to use it, import roms/boot/liba.h into your program.
+
** 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
+The ROM at roms/boot/boot.py loads data via the serial interface to
+RAM starting at address $0300 and executes it. The first byte 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.
+rom/boot/boot.py on the host PC to upload a program. This python
+script triggers a reset by setting DTR to high; see section Reset Logic.
** Tools
@@ -58,19 +82,11 @@ contains some experiments interfacing the SPI interface of an SD card.
*** Write library functions
-- String input via serial line
- Multiplication and division
- Random number generator
- CRC generator
+- Interrupt based serial I/O
-*** 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:
diff --git a/roms/ROMS.txt b/roms/ROMS.txt
@@ -14,11 +14,6 @@ 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
diff --git a/roms/boot/Makefile b/roms/boot/Makefile
@@ -1,19 +1,20 @@
-TARGET=boot
+TARGET=boot.bin
+TARGET_BASENAME=$(basename $(TARGET))
SYMON=java -jar ../../emulator/symon-1.3.1.jar -cpu 65c02
-all: $(TARGET) $(TARGET)_symon
+all: $(TARGET) $(TARGET_BASENAME)_symon.bin liba.h
-$(TARGET): $(TARGET).s
- xa -l $(TARGET).l -r -o $(TARGET) $<
+liba.h: $(TARGET_BASENAME).l $(TARGET_BASENAME)_symon.l $(TARGET_BASENAME).s
+ ./export_symbols.py
-$(TARGET)_symon: $(TARGET).s
- xa -DSYMON -l $(TARGET)_symon.l -r -o $(TARGET)_symon $<
+%.bin: %.s
+ xa -l "$(basename $@).l" -r -o "$@" "$<"
-emulation: $(TARGET)_symon
- $(SYMON) -cpu 65c02 -rom ./$(TARGET)_symon
+%_symon.bin: %.s
+ xa -DSYMON -l "$(basename $@).l" -r -o "$@" "$<"
flash: $(TARGET)
sudo ~/opt/minipro-0.3/minipro -p AT28C64B -w $<
clean:
- rm -f $(TARGET) $(TARGET).l $(TARGET)_symon $(TARGET)_symon.l
+ rm -f *.bin *.l liba.h
diff --git a/roms/boot/boot.h b/roms/boot/boot.h
@@ -0,0 +1,173 @@
+;;; 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
+
+
+
+;;; gets
+;;; Read up to 255 characters terminated by CR via acia
+;;; Input:
+;;; gets_len:
+;;; Max. length of input.
+;;; gets_str, gets_str+1:
+;;; 2 bytes on zero page containing destination address
+;;; +-------------------------------------------------------+
+;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! |
+;;; +-------------------------------------------------------+
+;;; Note that a terminating zero is added to the string.
+;;; Therefore gets_len+1 bytes may be written!
+;;; Output:
+;;; Zero terminated string at gets_str, gets_str+1
+;;; Changes:
+;;; a, y, ACAI registers
+
+ gets_len = $10
+ gets_str = $11
+
+;;; 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
+
+;;; INPUTS(addr, len)
+;;; Read up to len characters from acai and store them at addr
+;;; This is a wrapper for gets
+;;; Input:
+;;; addr:
+;;; Target address for zero-terminated string
+;;; len:
+;;; Maximal length of string to be read (not terminating zero!)
+;;; +-------------------------------------------------------+
+;;; |IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! |
+;;; +-------------------------------------------------------+
+;;; Note that a terminating zero is added to the string.
+;;; Therefore len+1 bytes may be written!
+;;; Output:
+;;; -
+;;; Changes:
+;;; a, y, ACAI registers
+
+
+#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: \
+ .)
+
+#define INPUTS(addr,len) \
+ lda #<addr: \
+ sta gets_str: \
+ lda #>addr: \
+ sta gets_str+1: \
+ lda len: \
+ sta gets_len: \
+ jsr gets
+
+;;; The lines below are automatically generated by export_symbols.py
+
diff --git a/roms/boot/boot.py b/roms/boot/boot.py
@@ -6,29 +6,12 @@ import sys
import argparse
import time
import pdb
+import os
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()
@@ -38,27 +21,39 @@ def upload_program(filename):
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("Triggering reset.")
+ os.system('stty -F ' + SERIAL_PORT + ' -hup')
+ l = ""
+ while l != b'READY!\r\n':
+ s.setDTR(False)
+ s.setDTR(True)
+ l = s.readline()
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)
-
+
+ # While transferring data, we calculate a two byte
+ # checksum. The first byte is the addition of all bytes
+ # transferred. The second byte is the xor of all bytes
+ # transferred.
+ checksum_add = 0
+ checksum_xor = 0
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)
+ # Update checksums
+ checksum_add = (checksum_add + d) % 256
+ checksum_xor = checksum_xor ^ d
+ # Receive checksums from 8 bit computer and compare to our checksum
+ cmp_checksum_add = ord(s.read(1))
+ cmp_checksum_xor = ord(s.read(1))
+ if ((cmp_checksum_add != checksum_add) or
+ (cmp_checksum_xor != checksum_xor)):
+ print('\nERROR: Wrong checksum!')
+ 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,
diff --git a/roms/boot/boot.s b/roms/boot/boot.s
@@ -1,230 +1,136 @@
;;; Receive data from termina. Store it in RAM & execute it.
-// #define BPS_300
-#define BPS_19200
+#include "boot.h"
#ifdef SYMON
- * = $c000 ; Symon
- acia_base = $8800 ; Symon
+ * = $c000
#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
+;;; ----------------------------------------------------------
+;;; RESET
+;;;
+;;; Initialize serial interface, read code from serial
+;;; interface into RAM, execute it.
+;;; ----------------------------------------------------------
init:
+ cld
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
-
+;;; Downalod program to RAM and start executing it.
+;;; The first byte transmitted is the number of $100 byte
+;;; blocks to receive. Then the program is received.
+;;; Finally, a two byte checksum is send back. The first
+;;; byte of the checksum is the addition of all bytes received.
+;;; The second byte of the checksum is the xor of all bytes
+;;; received.
+
+ ;; Program is loaded to this memory address
+ start_addr = $0300
+ ;; Location of temporary variables
+ addr_pointer = $12 ; addr_pointer+1 stores MSB
+ msb_last_addr = $14
+ add_checksum = $15
+ xor_checksum = $16
download_program:
- ;; Print start banner
- lda #<ready_str
- sta puts_str
- lda #>ready_str
- sta puts_str+1
- jsr puts
+ PRINTSNL("READY!")
;; 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
+ ;; The msb of the last block to be written is the msb
+ ;; of the start address plus the number of blocks
clc
- adc #$03
- sta $12
+ adc #>start_addr
+ sta msb_last_addr
;; Read n * 0x100 bytes from acia
- ;; and store them at 0x0300..
- ;; We use address $10 to store the target
- ;; address
+ lda #<start_addr
+ sta addr_pointer
+ lda #>start_addr
+ sta addr_pointer+1
+ ;; We calculate a two checksums over the data:
+ ;; The first byte is the addition of all bytes
+ ;; received. The second byte is the xor of all
+ ;; bytes received.
lda #$00
- sta $10
- lda #$03
- sta $11
+ sta add_checksum
+ sta xor_checksum
transmit_block:
jsr getc
+ ;; Store data
ldy #$00
- sta ($10), y
- jsr putc
- inc $10
+ sta (addr_pointer), y
+ ;; Update checksums
+ pha
+ clc
+ adc add_checksum
+ sta add_checksum
+ pla
+ eor xor_checksum
+ sta xor_checksum
+ ;; Update LSB of target address and check if
+ ;; transmission of block is completed.
+ inc addr_pointer
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
+ inc addr_pointer+1
+ lda addr_pointer+1
+ cmp msb_last_addr
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
+ ;; Transmission completed.
+ ;; Transmit result of checksum calculation
+ lda add_checksum
jsr putc
- jmp cont
-
-memory_print_location:
- lda memory_test_pointer+1
- jsr puth
- lda memory_test_pointer
- jsr puth
- ;; NL
- lda #$0d
+ lda xor_checksum
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
+ ;; Start program
+ jmp $0300
-getc:
- ;; Read character from acia
- lda acia_status_reg
- and #%00001000
- beq getc
- lda acia_data_reg
+init_acia: ; EXPORT
+ ;; 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
-endless_a:
- lda #'A'
- jsr putc
- jmp endless_a
+;;; ----------------------------------------------------------
+;;; STANDARD LIBRARY FUNCTIONS
+;;;
+;;; These functions may be used by user programs. Labels
+;;; marked with 'EXPORT' are exported to liba.h. When
+;;; the user program imports this header file, the functions
+;;; are available.
+;;; ----------------------------------------------------------
-puts:
+puts: ; EXPORT
;; 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
+
+putc: ; EXPORT
+ ;; Send character
+ sta acia_data_reg
+ ;; Length of delay loop determined experimentally
+ ldx #$88
+_putc_loop:
+ dex
+ bne _putc_loop
+ rts
-puth:
+puth: ; EXPORT
;; Send a as hex number
pha
lsr
@@ -240,39 +146,48 @@ _puth_nibble:
clc
adc #$30 ; Decimal number
cmp #$3A ; >10 ?
- bmi _puth_puts
+ bmi _puth_putc
adc #$26
-_puth_puts:
+_puth_putc:
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
+
+putnl: ; EXPORT
+ lda #$0d
+ jsr putc
+ lda #$0a
+ jsr putc
rts
+getc: ; EXPORT
+ ;; Read character from acia
+ lda acia_status_reg
+ and #%00001000
+ beq getc
+ lda acia_data_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
+gets: ; EXPORT
+ .(
+ ;; Read string terminated by CR
+ ldy #$00
+loop:
+ jsr getc
+ jsr putc
+ cmp #$0d ; GOT CR?
+ beq terminate_string ; if not
+ sta (gets_str), y ; store character
+ iny ; if max length
+ cpy gets_len ; not reached,
+ bne loop ; continue loop.
+terminate_string:
+ lda #$00
+ sta (gets_str), y
rts
+ .)
+
- ;; Vectors
+;;; Vectors
.dsb $fffa-*, $ff
.word $0000 ; nmi
.word init ; reset
diff --git a/roms/boot/export_symbols.py b/roms/boot/export_symbols.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+"""Extract labels from boot.s
+
+This script extracts exported labels from boot.s. liba.h is created
+from boot.h and the exported templates. This allows user programs
+to access ROM functions by importing liba.h.
+"""
+
+import re
+import pdb
+
+
+def get_labels_from_file(filename):
+ labels = {}
+ with open(filename, 'r') as f:
+ # Get addresses of labels
+ for l in f:
+ m = re.search("^(.*?), 0x(.*?),", l)
+ if m != None:
+ labels.update({m[1] : m[2]})
+ return labels
+
+
+def get_exports_from_file(filename, labels):
+ out = ""
+ with open(filename, 'r') as f:
+ # Get list of exported symbols
+ for l in f:
+ m = re.search("^(.*?)[: ].*;.*EXPORT", l)
+ if m != None:
+ if m[1] in labels:
+ out += " {} = ${}\n".format(m[1], labels[m[1]])
+ return out
+
+
+with open('liba.h', 'w') as fout:
+ with open('boot.h', 'r') as fin:
+ for l in fin:
+ fout.write(l)
+ fout.write('#ifdef SYMON\n')
+ labels = get_labels_from_file('boot_symon.l')
+ fout.write(get_exports_from_file('boot.s', labels))
+ fout.write('#else\n')
+ labels = get_labels_from_file('boot.l')
+ fout.write(get_exports_from_file('boot.s', labels))
+ fout.write('#endif // SYMON\n')
diff --git a/roms/serial_char_out/Makefile b/roms/serial_char_out/Makefile
@@ -1,6 +1,7 @@
-TARGET=serial_char_out
+TARGET=serial_char_out.bin
+TARGET_BASENAME=$(basename $(TARGET))
-all: $(TARGET).s
+all: $(TARGET_BASENAME).s
xa -o $(TARGET) $<
flash: $(TARGET)
diff --git a/roms/serial_echo/Makefile b/roms/serial_echo/Makefile
@@ -1,15 +0,0 @@
-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
@@ -1,103 +0,0 @@
- ;; 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
@@ -1,9 +1,10 @@
-TARGET=loop
+TARGET=loop.bin
+TARGET_BASENAME=$(basename $(TARGET))
-$TARGET: $(TARGET).s
+all: $(TARGET_BASENAME).s
xa -o $(TARGET) $<
-upload: $(TARGET)
+flash: $(TARGET)
sudo ~/opt/minipro-0.3/minipro -p AT28C64B -w $<
clean:
diff --git a/sw/SW.txt b/sw/SW.txt
@@ -2,3 +2,7 @@ mem_test/
Tests RAM write and read operations. Used to test if the CPU interacts
with the RAM.
+
+serial_line_echo/
+
+Test serial input/output.
diff --git a/sw/mem_test/Makefile b/sw/mem_test/Makefile
@@ -1,15 +1,16 @@
-TARGET=mem_test
+TARGET=mem_test.bin
+TARGET_BASENAME=$(basename $(TARGET))
-all: $(TARGET) $(TARGET)_symon
+all: $(TARGET) $(TARGET_BASENAME)_symon.bin
-$(TARGET): $(TARGET).s
- xa -l $(TARGET).l -r -o $(TARGET) $<
+%.bin: %.s
+ xa -l "$(basename $@).l" -r -o "$@" "$<"
-$(TARGET)_symon: $(TARGET).s
- xa -DSYMON -l $(TARGET)_symon.l -r -o $(TARGET)_symon $<
+%_symon.bin: %.s
+ xa -DSYMON -l "$(basename $@).l" -r -o "$@" "$<"
upload: $(TARGET)
../../roms/boot/boot.py $(TARGET)
clean:
- rm -f $(TARGET) $(TARGET)_symon $(TARGET).l $(TARGET)_symon.l
+ rm -f *.bin *.l
diff --git a/sw/mem_test/liba.h b/sw/mem_test/liba.h
@@ -1,123 +0,0 @@
-;;; 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
@@ -1,60 +0,0 @@
-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
@@ -1,4 +1,4 @@
-#include "liba.h"
+#include "../../roms/boot/liba.h"
* = $0300
init:
@@ -54,6 +54,3 @@ fail:
PRINTSNL(" Fail!")
rts
.)
-
-
-#include "liba.s"
diff --git a/sw/serial_line_echo/Makefile b/sw/serial_line_echo/Makefile
@@ -0,0 +1,16 @@
+TARGET=serial_line_echo.bin
+TARGET_BASENAME=$(basename $(TARGET))
+
+all: $(TARGET) $(TARGET_BASENAME)_symon.bin
+
+%.bin: %.s
+ xa -l "$(basename $@).l" -r -o "$@" "$<"
+
+%_symon.bin: %.s
+ xa -DSYMON -l "$(basename $@).l" -r -o "$@" "$<"
+
+upload: $(TARGET)
+ ../../roms/boot/boot.py $(TARGET)
+
+clean:
+ rm -f *.bin *.l
diff --git a/sw/serial_line_echo/serial_line_echo.s b/sw/serial_line_echo/serial_line_echo.s
@@ -0,0 +1,14 @@
+#include "../../roms/boot/liba.h"
+
+ * = $0300
+init:
+ jsr init_acia
+ .(
+loop:
+ PRINTSNL("Serial line echo")
+ INPUTS($1000, #$10)
+ PRINTSNL("")
+ PRINT($1000)
+ PRINTSNL("")
+ jmp loop
+ .)