commit f614cffe868c65e9546e7a48f72e2d8c69a46f76
Author: Piotr Jaczewski <piotr.jaczewski@rtbhouse.com>
Date:   Fri May 22 09:55:24 2020 +0200

    First commit

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9378c10
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+*.xcodeproj
+*.hex
+*.elf
+*.o
+*.bin
+*.label
diff --git a/address_decoder.jed b/address_decoder.jed
new file mode 100644
index 0000000..f467782
--- /dev/null
+++ b/address_decoder.jed
@@ -0,0 +1,32 @@
+
+GAL20V8
+EQN2JED - Boolean Equations to JEDEC file assembler (Version V101)
+Copyright (c) National Semiconductor Corporation 1990-1993
+Assembled from "d:/ADDRES~1.EQN". Date: 3-31-120
+*
+NOTE PINS RW:2 A0:3 A1:4 A2:5 A3:6 A4:7 A5:8 A6:9 A7:10 A8:11*
+NOTE PINS GND:12 A9:14 A10:15 A11:16 SR:17 RD:18 WD:19 ROM:20*
+NOTE PINS NC1:21 NC2:22 R:23 VCC:24*
+NOTE GALMODE SMALL*
+QF2706*QP24*F0*
+L0640
+1111111011111111111111111111111111110111
+1111111011111111111111111111111111011111
+1111111011111111111111111111110111111111
+1111111011111111111111111101111111111111*
+L0960
+1011011010111011101110111010101001101011*
+L1280
+0111101010111011101110111010101001101011*
+L1600
+0111101010111011101110111010101010101011*
+L2560
+11011100*
+L2632
+00000011*
+L2640
+0000000000000000111100001000000010000000100000000000000000000000*
+L2704
+10*
+C1C79*
+0000
diff --git a/addressdecoder.eqn b/addressdecoder.eqn
new file mode 100644
index 0000000..9e86625
--- /dev/null
+++ b/addressdecoder.eqn
@@ -0,0 +1,20 @@
+chip  GAL20V8
+
+NC3=1 RW=2 A0=3 A1=4 A2=5 A3=6 A4=7 A5=8 A6=9 A7=10 A8=11 GND=12
+A9=14 A10=15 A11=16 SR=17 RD=18 WD=19 ROM=20
+R=23 VCC=24
+
+NC1=21 NC2=22
+
+equations
+
+NC1=gnd
+NC2=gnd
+
+SR = /A0 * /A1 * /A2 * /A3 * /A4 * /A5 * /A6 * /A7 * /A8 * /A9 * /A10 * /A11 * /R * RW
+RD = /A0 * /A1 * /A2 * /A3 * /A4 * /A5 * /A6 * A7 * /A8 * /A9 * /A10 * /A11 * /R * RW
+WD = A0 * /A1 * /A2 * /A3 * /A4 * /A5 * /A6 * A7 * /A8 * /A9 * /A10 * /A11 * /R * /RW
+/ROM = A8 * /R * RW
+    + A9 * /R * RW
+    + A10 * /R * RW
+    + A11 * /R * RW
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..2c65080
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+xa -W -C -v -O ASCII -c src/apple1serial.xa -l apple1serial.label -o apple1serial.bin
diff --git a/firmware/Makefile b/firmware/Makefile
new file mode 100644
index 0000000..7defb3b
--- /dev/null
+++ b/firmware/Makefile
@@ -0,0 +1,53 @@
+# Name: apple1serial
+# Author: flovenol@gmail.com
+
+DEVICE     = atmega328p
+CLOCK      = 16000000
+PROGRAMMER = -c stk500v2 -P /dev/tty.usbmodemavrdope1
+OBJECTS    = main.o
+
+# For computing fuse byte values for other devices and options see
+# the fuse bit calculator at http://www.engbedded.com/fusecalc/
+FUSES      = -U hfuse:w:0xde:m -U lfuse:w:0xff:m -U efuse:w:0xfd:m
+
+AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE)
+COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE)
+
+all:	main.hex
+
+.c.o:
+	$(COMPILE) -c $< -o $@
+
+.S.o:
+	$(COMPILE) -x assembler-with-cpp -c $< -o $@
+
+.c.s:
+	$(COMPILE) -S $< -o $@
+
+flash:	all
+	$(AVRDUDE) -U flash:w:main.hex:i
+
+fuse:
+	$(AVRDUDE) $(FUSES)
+
+install: flash fuse
+
+load: all
+	bootloadHID main.hex
+
+clean:
+	rm -f main.hex main.elf $(OBJECTS)
+
+main.elf: $(OBJECTS)
+	$(COMPILE) -o main.elf $(OBJECTS)
+
+main.hex: main.elf
+	rm -f main.hex
+	avr-objcopy -j .text -j .data -O ihex main.elf main.hex
+	avr-size --format=avr --mcu=$(DEVICE) main.elf
+
+disasm:	main.elf
+	avr-objdump -d main.elf
+
+cpp:
+	$(COMPILE) -E main.c
diff --git a/firmware/main.c b/firmware/main.c
new file mode 100644
index 0000000..eac787d
--- /dev/null
+++ b/firmware/main.c
@@ -0,0 +1,177 @@
+#ifndef F_CPU
+#define F_CPU 16000000UL
+#endif
+
+#define BAUD 9600
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/setbaud.h>
+
+#define DATA_READ_LINE PB0
+#define DATA_WRITE_LINE PD7
+#define STATUS PD6
+
+#define DATA0 PB1
+#define DATA1 PB2
+#define DATA2 PB3
+#define DATA3 PB4
+#define DATA4 PB5
+#define DATA5 PD2
+#define DATA6 PD3
+#define DATA7 PD4
+
+typedef enum {NONE, READ, WRITE} mode_t;
+mode_t mode = NONE;
+
+uint8_t data = 0x00;
+uint8_t upper_part = 0x00;
+uint8_t lower_part = 0x00;
+
+uint8_t data_write_interrupt_count = 0;
+uint8_t data_read_interrupt_count = 0;
+
+// Registers: PCMSK0, PCMSK1, PCMSK2 :registers that enable or disable pin-change interrupts
+// on individual pins
+
+// PCICR : a register where the three least significant bits enable or disable pin change interrupts
+// on a range of pins, i.e. {0,0,0,0,0,PCIE2,PCIE1,PCIE0}, where PCIE2 maps to PCMSK2, PCIE1 maps to PCMSK1,
+// and PCIE0 maps to PCMSK0.
+
+inline void ready() {
+    PORTD |= _BV(STATUS);
+}
+
+inline void not_ready() {
+    PORTD &= ~(_BV(STATUS));
+}
+
+void setup() {
+    
+    // set input on DATA_READ_LINE
+    DDRB &= ~(_BV(DATA_READ_LINE));
+    // interrupt on DATA_READ_LINE change
+    PCMSK0 |= _BV(PCINT0);
+    PCICR |= _BV(PCIE0);
+    
+    // set input on DATA_WRITE_LINE
+    DDRD &= ~(_BV(DATA_WRITE_LINE));
+    // interrupt on DATA_WRITE_LINE change
+    PCMSK2 |= _BV(PCINT23);
+    PCICR |= _BV(PCIE2);
+    
+    UBRR0H = UBRRH_VALUE;
+    UBRR0L = UBRRL_VALUE;
+    
+    UCSR0A = 0x00;
+    UCSR0C = 0x00;
+    // 8 bits of data, 1 stop bit, no parity
+    UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);
+    // TXD pullup
+    PORTD |= _BV(PD1);
+    
+    // interrupt on DATA_READ_LINE change
+    PCMSK0 |= _BV(PCINT0);
+    PCICR |= _BV(PCIE0);
+    
+    // set output on status
+    DDRD |= _BV(STATUS);
+    not_ready();
+    
+    // enable global interrupts
+    sei();
+}
+
+// data read line
+ISR(PCINT0_vect) {
+    if (data_read_interrupt_count == 0) {
+        if (mode != READ) {
+            mode = READ;
+            
+            // enable RXD and it's interrupt
+            UCSR0B = _BV(RXEN0) | _BV(RXCIE0);
+            
+            // set output on data pins
+            DDRD |= _BV(DATA5) | _BV(DATA6) | _BV(DATA7);
+            DDRB |= _BV(DATA0) | _BV(DATA1) | _BV(DATA2) | _BV(DATA3) | _BV(DATA4);
+        }
+        not_ready();
+        data_read_interrupt_count = 1;
+    } else {
+        data_read_interrupt_count = 0;
+    }
+}
+
+// data write line
+ISR(PCINT2_vect) {
+    upper_part = PINB;
+    lower_part = PIND;
+    
+    if (data_write_interrupt_count == 0) {
+        if (mode == WRITE) {
+            upper_part = upper_part >> 1;
+            upper_part &= 0x1f;
+            
+            lower_part = lower_part << 3;
+            lower_part &= 0xe0;
+            
+            // set status to not ready
+            PORTD &= ~(_BV(STATUS));
+            
+            // send byte
+            loop_until_bit_is_set(UCSR0A, UDRE0);
+            UDR0 = upper_part | lower_part;
+        } else {
+            if (mode == READ) {
+                // zero all outputs
+                PORTD &= 0xe3;
+                PORTB &= 0xc1;
+            }
+            mode = WRITE;
+            
+            // enable TXD interrupt and it's interrupt
+            UCSR0B =  _BV(TXEN0) | _BV(TXCIE0);
+            
+            // set input on data pins
+            DDRD &= ~(_BV(DATA5) | _BV(DATA6) | _BV(DATA7));
+            DDRB &= ~(_BV(DATA0) | _BV(DATA1) | _BV(DATA2) | _BV(DATA3) | _BV(DATA4));
+            
+            ready();
+        }
+        data_write_interrupt_count = 1;
+    } else {
+        data_write_interrupt_count = 0;
+    }
+}
+
+ISR(USART_RX_vect)
+{
+    if (mode == READ) {
+        data = UDR0;
+        
+        upper_part = (PORTD & 0xe3) | ((0xe0 & data)>>3);
+        lower_part = (PORTB & 0xc1) | ((0x1f & data)<<1);
+        
+        PORTD = upper_part;
+        PORTB = lower_part;
+        
+        ready();
+    }
+}
+
+ISR(USART_TX_vect)
+{
+    if (mode == WRITE) {
+        ready();
+    }
+}
+
+int main(void) {
+    setup();
+    
+    for(;;){
+        // only interrupt driven
+    }
+    return 0;   /* never reached */
+}
+
diff --git a/src/apple1serial.xa b/src/apple1serial.xa
new file mode 100644
index 0000000..feb9d65
--- /dev/null
+++ b/src/apple1serial.xa
@@ -0,0 +1,204 @@
+#define hex1l $50            ;End address of dump block
+#define hex1h $51
+#define hex2l $52            ;Begin address of dump block
+#define hex2h $53
+
+#define input $0200          ;Input buffer
+#define kbd_data $D010       ;PIA.A keyboard input
+#define kbd_cr $D011         ;PIA.A keyboard control register
+#define monitor $FF1A        ;Escape back to monitor
+#define echo $FFEF           ;Echo character to terminal
+#define prbyte $FFDC
+
+#define serial_ready $C000   ;Serial status
+#define serial_read  $C080   ;Data read address
+#define serial_write $C081   ;Data write address
+
+;-------------------------------------------------------------------------
+;  Constants
+;-------------------------------------------------------------------------
+
+#define R_LETTER $D2
+#define W_LETTER $D7
+#define ZERO $B0
+#define SEP $AE
+#define CR $8D          ;Carriage Return
+#define ESC $9B         ;ASCII ESC
+#define SPACE $A0
+
+; pad first 256 bytes with zeroes
+.dsb 256, 0
+
+; this section is almost identical to original WOZ ACI
+; adapted from https://www.sbprojects.net/projects/apple1/aci.php
+* = $C100
+apple_serial
+    lda #CR             ;Drop the cursor one line
+    jsr echo
+    lda #"*"            ;Print prompt
+    jsr echo
+    lda #CR             ;And drop the cursor one line
+    jsr echo
+    ldy #$FF            ;Reset the input buffer index
+
+next_char
+    iny
+
+kbd_wait
+    lda kbd_cr           ;Wait for a key
+    bpl kbd_wait         ;Still no key!
+
+    lda kbd_data         ;Read key from keyboard
+    sta $0200,Y          ;Save it into buffer
+    jsr echo             ;And type it on the screen
+    cmp #ESC
+    beq apple_serial     ;Start from scratch if ESC!
+    cmp #CR
+    bne next_char        ;Read keys until CR
+
+    ldx #$FF             ;Initialize parse buffer pointer
+
+;-------------------------------------------------------------------------
+; Start parsing first or a new tape command
+;-------------------------------------------------------------------------
+
+next_cmd
+    lda #$00              ;Clear begin and end values
+    sta hex1l
+    sta hex1h
+    sta hex2l
+    sta hex2h
+
+next_chr
+    inx                 ;Increment input pointer
+    lda $0200,X         ;Get next char from input lin
+    cmp #R_LETTER       ;Read command?
+    beq read            ;Yes!
+    cmp #W_LETTER       ;Write command?
+    beq write           ;Yes! (note: CY=1)
+    cmp #SEP            ;Separator?
+    beq separator       ;Yes!
+    cmp #CR             ;End of line?
+    beq to_monitor      ;Escape to monitor! We're done
+    cmp #SPACE          ;Ignore spaces
+    beq next_chr
+    eor #ZERO           ;Map digits to 0-9
+    cmp #$0A            ;Is it a decimal digit?
+    bcc digit           ;Yes!
+    clc
+    adc #$89            ;Map letter "A"-"F" to $FA-$FF
+    cmp #$FA            ;Hex letter?
+    bcc apple_serial    ;No! Character not hex!
+
+digit
+    asl                 ;Hex digit to MSD of A
+    asl
+    asl
+    asl
+
+    ldy #4              ;Shift count
+
+hexshift
+    asl                 ;Hex digit left, MSB to carry
+    rol hex1l           ;Rotate into LSD
+    rol hex1h           ;Rotate into MSD
+    dey                 ;Done 4 shifts?
+    bne hexshift        ;No! Loop
+    beq next_chr        ;Handle next character
+
+;-------------------------------------------------------------------------
+; Return to monitor, prints \ first
+;-------------------------------------------------------------------------
+
+to_monitor
+    jmp monitor         ;Escape back to monitor
+
+;-------------------------------------------------------------------------
+; Separating . found. Copy HEX1 to Hex2. Doesn't clear HEX1!!!
+;-------------------------------------------------------------------------
+
+separator
+    lda hex1l           ;Copy hex value 1 to hex value 2
+    sta hex2l
+    lda hex1h
+    sta hex2h
+    lda #$00            ;Original ACI bug (not enough ROM space?) fix
+    sta hex1l
+    sta hex1h
+    jmp next_chr        ;Always taken!
+
+;-------------------------------------------------------------------------
+; Read procedure
+;-------------------------------------------------------------------------
+
+read
+    lda serial_read     ;Enable read mode
+    ldy #$00
+
+read_byte
+    lda serial_ready
+    beq read_byte       ;No data arrived
+    lda serial_read     ;Read byte
+    sta ($52),Y         ;Store byte under address
+
+    lda hex2l
+    cmp hex1l           ;Compare lower destination address half with lower end address half
+    bne read_next       ;If not equal then increment destination address
+
+    lda hex2h
+    cmp hex1h           ;Compare upper destination address half with upper end address half
+    bne read_next       ;If not equal then proceed to read next byte
+
+    jmp next_cmd        ;Read is completed, proceed to next command
+
+read_next
+    ldx #hex2l
+    jsr increment_16bit ;Increment destination address
+    jmp read_byte
+
+;-------------------------------------------------------------------------
+; Write procedure
+;-------------------------------------------------------------------------
+
+write
+    lda serial_read     ;Enable read mode to be sure that we are in "fresh" write mode
+    sta serial_write    ;Enable write mode
+    ldy #$00
+    
+write_byte
+    lda serial_ready
+    beq write_byte
+    lda ($52),Y
+    sta serial_write
+
+    lda hex2l
+    cmp hex1l           ;Compare lower destination address half with lower end address half
+    bne write_next      ;If not equal then increment destination address
+
+    lda hex2h
+    cmp hex1h           ;Compare upper destination address half with upper end address half
+    bne write_next      ;If not equal then proceed to write next byte
+
+    jmp next_cmd        ;Read is completed, proceed to next command
+
+write_next
+    ldx #hex2l
+    jsr increment_16bit ;Increment destination address
+    jmp write_byte
+
+;-------------------------------------------------------------------------
+; tool routines
+;-------------------------------------------------------------------------
+
+increment_16bit
+    inc $00,X
+    bne increment_16bit_done
+    inx
+    inc $00,X
+increment_16bit_done
+    rts
+
+;-------------------------------------------------------------------------
+
+end_of_apple1serial
+;#include "src/tests.xa"
diff --git a/src/tests.xa b/src/tests.xa
new file mode 100644
index 0000000..c1fc583
--- /dev/null
+++ b/src/tests.xa
@@ -0,0 +1,67 @@
+#define echo    $FFEF
+#define kbd_data $D010
+#define kbd_cr $D011
+#define serial_ready $C000
+#define serial_read  $C080
+#define serial_write $C081
+
+* = $C200
+.dsb (*-end_of_apple1serial), 0
+
+; teletype on apple-1
+* = $C200
+teletype_apple1 = $C200
+  lda serial_read
+wait
+  lda serial_ready
+  beq wait
+  lda serial_read
+  jsr echo
+  jmp wait
+end_of_teletype_apple1
+
+; AD 80 C0 AD 00 C0 F0 FB
+; AD 80 C0 20 EF FF 4C 03
+; 00
+
+; teletype on remote
+* = $C300
+.dsb (*-end_of_teletype_apple1), 0
+
+* = $C300
+teletype_remote = $C300
+  lda #$FF
+  sta serial_write
+get_key
+  lda kbd_cr
+  bpl get_key
+  lda kbd_data
+  sec
+  sbc #$A0
+  sta serial_write
+  jmp get_key
+end_of_teletype_remote
+
+; A9 FF 8D 81 C0 AD 11 D0
+; 10 FB AD 10 D0 38 E9 A0
+; 8D 81 C0 4C 85 02
+
+; 0-255 repeating counter over TXD
+* = $C400
+.dsb (*-end_of_teletype_remote), 0
+
+* = $C400
+counter_remote = $C400
+  ldy #$00
+  sta serial_write
+check_ready
+  lda serial_ready
+  beq check_ready
+  tya
+  sta serial_write
+  iny
+  jmp check_ready
+
+; A0 00 8D 81 C0 AD 00 C0
+; F0 FB 98 8D 81 C0 C8 4C
+; 85 02
