commit a33ba6bc619440ee0f9b704d519255cabdc5dda3
parent 3fceb94389d2d61507b1aeadb13b5a7763772f3f
Author: Gerd Beuster <gerd@frombelow.net>
Date: Sun, 15 Nov 2020 22:17:41 +0100
Updates, bug fixes, interrupt support
Diffstat:
11 files changed, 300 insertions(+), 37 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -7,6 +7,11 @@ hw/bus_logic/BUS_LOGIC.jed
hw/bus_logic/BUS_LOGIC.pdf
hw/bus_logic/BUS_LOGIC.si
hw/bus_logic/BUS_LOGIC.sim
+hw/bus_logic/BUS_LOGIC.lst
+hw/bus_logic/BUS_LOGIC.mx
+hw/bus_logic/BUS_LOGIC.pla
+hw/bus_logic/BUS_LOGIC.so
+hw/bus_logic/BUS_LOGIC.wo
misc/clock_reset_attiny45/clock_and_reset.bin
misc/clock_reset_attiny45/clock_and_reset.elf
misc/clock_reset_attiny45/clock_and_reset.hex
diff --git a/hw/bus_logic/BUS_LOGIC.PLD b/hw/bus_logic/BUS_LOGIC.PLD
@@ -21,9 +21,9 @@ 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 13 = o1; /* ram_#oe */
pin 14 = o2; /* acai_#cs0 */
-pin 15 = o3; /* ce (not assigned) */
+pin 15 = o3; /* ram_#ce */
pin 16 = o4; /* ce (not assigned) */
pin 17 = o5; /* ce (not assigned) */
pin 18 = o6; /* #we */
@@ -41,7 +41,7 @@ o0 = (!i0 # !i1 # !i2);
Bit pattern: 0b0...............
*/
-/* Connect a14 directly to ram_#ce */
+/* See below */
/* ACIA (1k)
Address range: 0xdc00 - 0xdfff
@@ -55,22 +55,14 @@ o2 = (!i0 # !i1 # i2 # !i3 # !i4 # !i5);
Bit pattern: 0b110110..........
*/
-o3 = (!i0 # !i1 # i2 # !i3 # !i4 # i5);
+o4 = (!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);
-
+o5 = (!i0 # !i1 # i2 # !i3 # i4 # !i5);
/* Inverter
This is not part of the bus logic.
@@ -81,14 +73,17 @@ o5 = (!i0 # !i1 # i2 # !i3 # i4 # i5);
o7 = !reset_in;
/*
-oe# and we# for the memory chips must be
+oe# we#, and ce# for the memory chips must be
qualified by phi - No memory access
in the first part of the cycle, while phi
is low.
*/
-/* oe# */
+/* ram_oe# */
o1 = !phi # !rwb;
-/* we# */
+/* ram_ce# */
+o3 = !phi # i0;
+
+/* ram_we# */
o6 = !phi # rwb;
diff --git a/misc/clock_reset_attiny45/clock_and_reset.c b/misc/clock_reset_attiny45/clock_and_reset.c
@@ -5,20 +5,34 @@
// 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);
+
+void fast_clock (void){
SET_MODE_OUTPUT_DIGITAL(CONTACT_5_DDR_REG,
CONTACT_5_DDR_BIT);
+ while(1) {
+ CONTACT_5_PORT_REG ^= 0xFF;
+ // No delay: 842.1 kHz
+ // _delay_us(3); // 516,1 kHz
+ _delay_us(37); // 99,38 kHz
+ }
+}
+
+void clock_and_reset (void){
+ /* usb_init(); */
+ /* Configure Pins for reset logic. */
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);
+ /* Configure pins for clock signal. */
+ 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);
uint8_t clock_counter = 0;
uint16_t reset_counter = RESET_DELAY; // power on reset
while (1){
@@ -48,5 +62,10 @@ int main (void){
}
_delay_ms(20);
}
+}
+
+int main (void){
+ fast_clock(); // Can't get fast PWM working. Using this instead...
+ // clock_and_reset();
return 1; // dummy
}
diff --git a/roms/boot/boot.py b/roms/boot/boot.py
@@ -11,6 +11,10 @@ import os
SERIAL_PORT = '/dev/ttyUSB0'
SERIAL_SPEED = 19200
+# File upload does not work if GATE is run 100 kHz clock frequency
+# unless SLOW_CPU is set.
+SLOW_CPU = False
+# SLOW_CPU = True
def upload_program(filename):
with open(filename, 'rb') as f:
@@ -31,6 +35,7 @@ def upload_program(filename):
print("Uploading program.")
e = str.encode(chr(blocks))
s.write(e)
+
# While transferring data, we calculate a two byte
# checksum. The first byte is the addition of all bytes
@@ -40,6 +45,10 @@ def upload_program(filename):
checksum_xor = 0
for d in data:
print(".", end="", flush=True)
+ if SLOW_CPU:
+ # At slow clock frequencies, the upload has to
+ # to be slowed down.
+ time.sleep(.01)
s.write(bytes([d]))
# Update checksums
checksum_add = (checksum_add + d) % 256
diff --git a/roms/boot/boot.s b/roms/boot/boot.s
@@ -1,6 +1,8 @@
-;;; Receive data from termina. Store it in RAM & execute it.
+;;; Receive data from terminal. Store it in RAM & execute it.
#include "boot.h"
+
+#define CLOCK_4_MHZ
#ifdef SYMON
* = $c000
@@ -8,6 +10,13 @@
* = $e000
#endif
+;;; The actual (hardware) interrupt vectors are located in the ROM
+;;; at $ fffa/fffb (nmi) and $fffe/$ffff (irq). Since we want to be
+;;; able to handle interrupts in RAM, the ROM isr routines jump to the
+;;; addresses given here. After a reset, this RAM address is set to
+;;; default_irq_handler.
+nmi_vector = $7ffc ; EXPORT
+irq_vector = $7ffe ; EXPORT
;;; We have enough space left to
;;; fit in a Tic-Tac-Toe engine!
@@ -21,7 +30,11 @@
;;; ----------------------------------------------------------
init:
- cld
+ sei
+ STORE_WORD(default_irq_handler, irq_vector)
+ STORE_WORD(default_nmi_handler, nmi_vector)
+ cld
+ cli
jsr init_acia
jsr check_for_program_download
bcs no_program_download
@@ -141,22 +154,44 @@ puts: ; EXPORT
_puts_loop:
lda (puts_str), y
beq _puts_end
+ phy
jsr putc
+ ply
iny
jmp _puts_loop
_puts_end:
rts
-putc: ; EXPORT
- ;; Send character
+#ifdef CLOCK_4_MHZ
+ SERIAL_SEND_DELAY_X = $c3
+ SERIAL_SEND_DELAY_Y = $02
+#endif
+#ifdef CLOCK_2_MHZ
+ SERIAL_SEND_DELAY_X = $c2
+ SERIAL_SEND_DELAY_Y = $01
+#endif
+#ifdef CLOCK_1_MHZ
+ SERIAL_SEND_DELAY_X = $88
+ SERIAL_SEND_DELAY_Y = $01
+#endif
+putc: ; EXPORT
+ .(
+ ;; Send character.
+ ;; Due to bugs in the register and interrupt
+ ;; handling of the WDC 65C02, we have to use
+ ;; a manual delay.
sta acia_data_reg
- ;; Length of delay loop determined experimentally
- ldx #$88
-_putc_loop:
- dex
- bne _putc_loop
- rts
-
+ ldy #SERIAL_SEND_DELAY_Y
+outer_loop:
+ ldx #SERIAL_SEND_DELAY_X
+inner_loop:
+ dex
+ bne inner_loop
+ dey
+ bne outer_loop
+ rts
+ .)
+
puth: ; EXPORT
;; Send byte a as hex number
pha
@@ -286,6 +321,27 @@ loop:
.)
#endif // TESTS
+
+;;; Default IRQ handler. Unless the user program changes
+;;; the irq_vector, IRQs are handled here.
+default_irq_handler:
+ rti
+
+derefer_ram_irq:
+ ;; Jump to the address given in the IRQ vector
+ ;; in RAM
+ jmp (irq_vector)
+
+;;; Default NMI handler. Unless the user program changes
+;;; the nmi_vector, NMIs are handled here.
+default_nmi_handler:
+ rti
+
+derefer_ram_nmi:
+ ;; Jump to the address given in the NMI vector
+ ;; in RAM
+ jmp (nmi_vector)
+
;;; Include Tic-Tac-Toe binary
;;; This creates some circular dependencies,
;;; because ttt.s includes liba.h, which
@@ -300,6 +356,6 @@ loop:
;;; Vectors
.dsb $fffa-*, $ff
- .word $0000 ; nmi
+ .word derefer_ram_nmi ; nmi
.word init ; reset
- .word $0000 ; irq
+ .word derefer_ram_irq ; irq
diff --git a/sw/10print/10print.s b/sw/10print/10print.s
@@ -20,11 +20,21 @@ l0:
jsr lfsr_step
and #$01
bne slash
+#ifdef SYMON
+ lda #'\'
+ jsr putc
+#else
PRINTS('╲')
+#endif
jsr putc
jmp ten_print
slash:
+#ifdef SYMON
+ lda #'/'
+ jsr putc
+#else
PRINTS('╱')
+#endif
jsr putc
jmp ten_print
.)
diff --git a/sw/SW.txt b/sw/SW.txt
@@ -1,8 +1,24 @@
-mem_test/
+* mem_test/
Tests RAM write and read operations. Used to test if the CPU interacts
with the RAM.
-serial_line_echo/
+* serial_line_echo/
Test serial input/output.
+
+* 10print/
+
+https://10print.org/
+
+* aaa/
+
+Output stream of 'a'. Used to calliberate serial interface.
+
+* interrupts
+
+Testing interrupt handling.
+
+* ttt/
+
+Tic-Tac-Toe
diff --git a/sw/aaa/Makefile b/sw/aaa/Makefile
@@ -0,0 +1,16 @@
+TARGET=aaa.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/aaa/aaa.s b/sw/aaa/aaa.s
@@ -0,0 +1,44 @@
+#include "../../roms/boot/liba.h"
+
+ * = $0300
+init:
+ jsr init_acia
+ .(
+loop:
+ lda #'A'
+ jsr my_putc
+ jmp loop
+ .)
+
+#define CLOCK_4_MHZ
+
+#ifdef CLOCK_4_MHZ
+ SERIAL_SEND_DELAY_X = $c3
+ SERIAL_SEND_DELAY_Y = $02
+#endif
+#ifdef CLOCK_2_MHZ
+ SERIAL_SEND_DELAY_X = $c2
+ SERIAL_SEND_DELAY_Y = $01
+#endif
+#ifdef CLOCK_1_MHZ
+ SERIAL_SEND_DELAY_X = $88
+ SERIAL_SEND_DELAY_Y = $01
+#endif
+my_putc: ; EXPORT
+ .(
+ ;; Send character.
+ ;; Due to bugs in the register and interrupt
+ ;; handling of the WDC 65C02, we have to use
+ ;; a manual delay.
+ sta acia_data_reg
+ ldy #SERIAL_SEND_DELAY_Y
+outer_loop:
+ ldx #SERIAL_SEND_DELAY_X
+inner_loop:
+ dex
+ bne inner_loop
+ dey
+ bne outer_loop
+ rts
+ .)
+
diff --git a/sw/interrupts/Makefile b/sw/interrupts/Makefile
@@ -0,0 +1,16 @@
+TARGET=interrupts.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/interrupts/interrupts.s b/sw/interrupts/interrupts.s
@@ -0,0 +1,77 @@
+#include "../../roms/boot/liba.h"
+
+ * = $0300
+
+ flag = $30
+ tmp = $31
+
+init:
+ // jmp transmitter_interrupt
+ jmp receiver_interrupt
+
+;;; Looks like interrupt controlled tranmission is buggy.
+;;; The only way to transmit seems to be a delay loop.
+transmitter_interrupt:
+ .(
+ jsr init_acia
+ STORE_WORD(isr, irq_vector)
+ PRINTSNL('Push key to start.')
+ jsr getc
+loop:
+ ;; Transmitter interrupt on
+ lda #%11000111
+ sta acia_cmd_reg
+ ;; Send a byte
+ lda flag
+ jsr putc_irq
+ ;; Transmitter interrupt off
+ lda #%11001011
+ sta acia_cmd_reg
+ jsr wait
+ jmp loop
+
+wait:
+ ldx #$7f
+wait_loop:
+ dex
+ bne wait_loop
+ rts
+ .)
+
+receiver_interrupt:
+ .(
+ jsr init_acia
+ STORE_WORD(isr, irq_vector)
+ ;; Receiver interrupt on
+ lda acia_cmd_reg
+ lda #%11001001
+ sta acia_cmd_reg
+ PRINTSNL("Start typing ...")
+loop:
+ jsr getc
+ lda flag
+ jsr puth
+ PRINTNL
+ jmp loop
+ .)
+
+isr:
+ lda flag
+ inc
+ sta flag
+ ldx #$00 ; Indicate end of transmission
+ lda acia_status_reg
+ rti
+
+
+putc_irq:
+ .(
+ ldx #$ff
+ sta acia_data_reg
+wait_for_transmission_finish:
+ ;; Interrupt upon successful transmission
+ ;; sets X to $00
+ cpx #$00
+ bne wait_for_transmission_finish
+ rts
+ .)