From f614cffe868c65e9546e7a48f72e2d8c69a46f76 Mon Sep 17 00:00:00 2001 From: Piotr Jaczewski Date: Fri, 22 May 2020 09:55:24 +0200 Subject: [PATCH] First commit --- .gitignore | 6 ++ address_decoder.jed | 32 +++++++ addressdecoder.eqn | 20 +++++ build.sh | 3 + firmware/Makefile | 53 ++++++++++++ firmware/main.c | 177 ++++++++++++++++++++++++++++++++++++++ src/apple1serial.xa | 204 ++++++++++++++++++++++++++++++++++++++++++++ src/tests.xa | 67 +++++++++++++++ 8 files changed, 562 insertions(+) create mode 100644 .gitignore create mode 100644 address_decoder.jed create mode 100644 addressdecoder.eqn create mode 100755 build.sh create mode 100644 firmware/Makefile create mode 100644 firmware/main.c create mode 100644 src/apple1serial.xa create mode 100644 src/tests.xa 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 +#include +#include + +#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