eris2010

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

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:
M.gitignore | 13+++++--------
Mdoc/Documentation.txt | 40++++++++++++++++++++++++++++------------
Mroms/ROMS.txt | 5-----
Mroms/boot/Makefile | 19++++++++++---------
Aroms/boot/boot.h | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mroms/boot/boot.py | 59+++++++++++++++++++++++++++--------------------------------
Mroms/boot/boot.s | 331+++++++++++++++++++++++++++++--------------------------------------------------
Aroms/boot/export_symbols.py | 46++++++++++++++++++++++++++++++++++++++++++++++
Mroms/serial_char_out/Makefile | 5+++--
Droms/serial_echo/Makefile | 15---------------
Droms/serial_echo/serial_echo.s | 103-------------------------------------------------------------------------------
Mroms/simple_loop/Makefile | 7++++---
Msw/SW.txt | 4++++
Msw/mem_test/Makefile | 15++++++++-------
Dsw/mem_test/liba.h | 123-------------------------------------------------------------------------------
Dsw/mem_test/liba.s | 60------------------------------------------------------------
Msw/mem_test/mem_test.s | 5+----
Asw/serial_line_echo/Makefile | 16++++++++++++++++
Asw/serial_line_echo/serial_line_echo.s | 14++++++++++++++
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 + .)