mirror of
https://github.com/LIV2/amiga-par-to-spi-adapter.git
synced 2025-12-06 06:32:46 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d56fbaaaf4 | ||
|
|
a8488355cb | ||
|
|
e9278dcc7a | ||
|
|
c64fc9f5a3 | ||
|
|
dadaab55d8 | ||
|
|
4ed29c88d6 | ||
|
|
14b7884aef | ||
|
|
dc0f8cb0cd | ||
|
|
6cd50a311a | ||
|
|
0ae311f713 | ||
|
|
e377afae11 | ||
|
|
30736877bd | ||
|
|
f7493de0b0 | ||
|
|
27d6ad85fa | ||
|
|
e766d41e37 |
16
README.md
16
README.md
@ -2,6 +2,22 @@
|
||||
|
||||
The goal of this project is to make a cheap and easy to build SPI adapter that connects to the parallel port of an Amiga. Furthermore, the performance of the adapter should be as fast as possible.
|
||||
|
||||
## Please note: new hardware incompatible version!
|
||||
|
||||
What you are looking at is a new version of this design. It is hardware
|
||||
incompatible with the original version. Updating the software without making
|
||||
the corresponding hardware changes will not work.
|
||||
|
||||
Old versions of this design are available as tags/releases.
|
||||
|
||||
### Changes with latest version
|
||||
|
||||
Support has been added that allows the SPI device to interrupt the Amiga via the adapter.
|
||||
The SD card example driver has been modified to use this interrupt so that the SD card
|
||||
can be inserted/ejected as part of normal operations.
|
||||
|
||||
A video demonstrating this can be seen here: https://www.youtube.com/watch?v=zJDtiFQgrn0.
|
||||
|
||||
## What parts make up the project?
|
||||
|
||||
- An AVR microcontroller, provided by an Arduino Nano board
|
||||
|
||||
328
avr/main.c
328
avr/main.c
@ -1,119 +1,147 @@
|
||||
/*
|
||||
* Written in the end of April 2020 by Niklas Ekström.
|
||||
* Updated in July 2021 by Niklas Ekström to handle Card Present signal.
|
||||
*/
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/io.h>
|
||||
|
||||
// Amiga pins:
|
||||
// D0 = A0 = PC0
|
||||
// D1 = A1 = PC1
|
||||
// D2 = A2 = PC2
|
||||
// D3 = A3 = PC3
|
||||
// D4 = A4 = PC4
|
||||
// D5 = A5 = PC5
|
||||
// D6 = D6 = PD6
|
||||
// D7 = D7 = PD7
|
||||
// BUSY = D4 = PD4
|
||||
// POUT = D5 = PD5
|
||||
// Par P-name Arduino AVR SD Name Description
|
||||
// 2 D0 A0 PC0 Parallel port data lines
|
||||
// 3 D1 A1 PC1
|
||||
// 4 D2 A2 PC2
|
||||
// 5 D3 A3 PC3
|
||||
// 6 D4 A4 PC4
|
||||
// 7 D5 A5 PC5
|
||||
// 8 D6 D6 PD6
|
||||
// 9 D7 D7 PD7
|
||||
// 10 ACK D9 PB1 IRQ Interrupt request to Amiga
|
||||
// 11 BUSY D4 PD4 ACT Indicate command running
|
||||
// 12 POUT D5 PD5 CLK Clock to advance command
|
||||
// 13 SEL D2 PD2 REQ Amiga wants to execute command
|
||||
// D3 PD3 CD CP Card Present
|
||||
// D10 PB2 SS
|
||||
// D11 PB3 MOSI
|
||||
// D12 PB4 MISO
|
||||
// D13 PB5 SCK
|
||||
|
||||
// SPI pins in port B.
|
||||
#define SCK_BIT 5
|
||||
#define MISO_BIT 4
|
||||
#define MOSI_BIT 3
|
||||
#define SS_BIT 2
|
||||
// Pins in port B.
|
||||
#define SCK_BIT 5 // Output.
|
||||
#define MISO_BIT 4 // Input.
|
||||
#define MOSI_BIT 3 // Output.
|
||||
#define SS_BIT_n 2 // Output, active low.
|
||||
#define IRQ_BIT_n 1 // Output, active low, open collector.
|
||||
|
||||
// These bits are in port D.
|
||||
#define IDLE_BIT 4
|
||||
#define CLOCK_BIT 5
|
||||
// Pins in port D.
|
||||
#define CLK_BIT 5 // Input.
|
||||
#define ACT_BIT_n 4 // Output, active low.
|
||||
#define CP_BIT_n 3 // Input, active low, internal pull-up enabled.
|
||||
#define REQ_BIT_n 2 // Input, active low, internal pull-up enabled.
|
||||
|
||||
int main()
|
||||
void start_command()
|
||||
{
|
||||
DDRB = (1 << SCK_BIT) | (1 << MOSI_BIT) | (1 << SS_BIT);
|
||||
PORTB = (1 << SS_BIT);
|
||||
|
||||
// SPI enabled, master, fosc/64 = 250 kHz
|
||||
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
|
||||
SPSR |= (1 << SPI2X);
|
||||
|
||||
DDRD = (1 << IDLE_BIT);
|
||||
DDRC = 0;
|
||||
|
||||
PORTD = 0;
|
||||
PORTC = 0;
|
||||
|
||||
uint8_t v;
|
||||
uint8_t w;
|
||||
uint8_t dval;
|
||||
uint8_t cval;
|
||||
uint8_t next_port_d;
|
||||
uint8_t next_port_c;
|
||||
uint16_t byte_count;
|
||||
|
||||
main_loop:
|
||||
if (PIND & (1 << CLOCK_BIT))
|
||||
dval = PIND;
|
||||
cval = PINC;
|
||||
|
||||
if (!(dval & 0x80)) // READ1 or WRITE1
|
||||
{
|
||||
while (PIND & (1 << CLOCK_BIT))
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!(PIND & (1 << CLOCK_BIT)))
|
||||
;
|
||||
}
|
||||
byte_count = cval;
|
||||
|
||||
if (!(PIND & 0x80))
|
||||
{
|
||||
if (PIND & 0x40)
|
||||
goto do_read1;
|
||||
else
|
||||
goto do_write1;
|
||||
}
|
||||
PORTD &= ~(1 << ACT_BIT_n);
|
||||
|
||||
v = PIND;
|
||||
w = PINC;
|
||||
|
||||
if (!(v & 0x40)) // READ2 or WRITE2
|
||||
{
|
||||
byte_count = (w & 0x1f) << 8;
|
||||
|
||||
if (v & (1 << CLOCK_BIT))
|
||||
{
|
||||
while (PIND & (1 << CLOCK_BIT))
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!(PIND & (1 << CLOCK_BIT)))
|
||||
;
|
||||
}
|
||||
|
||||
v = PIND;
|
||||
|
||||
byte_count |= (v & 0xc0) | PINC;
|
||||
if (w & 0x20)
|
||||
if (dval & 0x40)
|
||||
goto do_read;
|
||||
else
|
||||
goto do_write;
|
||||
}
|
||||
else if ((w & 0x3e) == 0) // SPEED
|
||||
else if (!(dval & 0x40)) // READ2 or WRITE2
|
||||
{
|
||||
if (w & 1) // Fast
|
||||
SPCR = (1 << SPE) | (1 << MSTR);
|
||||
else // Slow
|
||||
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
|
||||
byte_count = cval << 7;
|
||||
|
||||
PORTD &= ~(1 << ACT_BIT_n);
|
||||
|
||||
if (dval & (1 << CLK_BIT))
|
||||
{
|
||||
while (PIND & (1 << CLK_BIT))
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!(PIND & (1 << CLK_BIT)))
|
||||
;
|
||||
}
|
||||
|
||||
dval = PIND;
|
||||
cval = PINC;
|
||||
|
||||
byte_count |= (dval & 0x40) | cval;
|
||||
if (dval & 0x80)
|
||||
goto do_read;
|
||||
else
|
||||
goto do_write;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t cmd = (cval & 0x3e) >> 1;
|
||||
if (cmd == 0) // SPI_SELECT
|
||||
{
|
||||
if (cval & 1) // Select
|
||||
PORTB &= ~(1 << SS_BIT_n);
|
||||
else // Deselect
|
||||
PORTB |= (1 << SS_BIT_n);
|
||||
|
||||
goto main_loop;
|
||||
PORTD &= ~(1 << ACT_BIT_n);
|
||||
}
|
||||
else if (cmd == 1) // CARD_PRESENT
|
||||
{
|
||||
DDRB &= ~(1 << IRQ_BIT_n);
|
||||
PORTD &= ~(1 << ACT_BIT_n);
|
||||
|
||||
do_read1:
|
||||
byte_count = PINC;
|
||||
if (dval & (1 << CLK_BIT))
|
||||
{
|
||||
while (PIND & (1 << CLK_BIT))
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!(PIND & (1 << CLK_BIT)))
|
||||
;
|
||||
}
|
||||
|
||||
DDRD = 0xc0 | (1 << ACT_BIT_n);
|
||||
DDRC = 0x3f;
|
||||
|
||||
if (!(PIND & (1 << CP_BIT_n)))
|
||||
PORTC = 1;
|
||||
else
|
||||
PORTC = 0;
|
||||
}
|
||||
else if (cmd == 2) // SPEED
|
||||
{
|
||||
if (cval & 1) // Fast
|
||||
SPCR = (1 << SPE) | (1 << MSTR);
|
||||
else // Slow
|
||||
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
|
||||
|
||||
PORTD &= ~(1 << ACT_BIT_n);
|
||||
}
|
||||
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
do_read:
|
||||
SPDR = 0xff;
|
||||
|
||||
v = PIND;
|
||||
PORTD = (dval & 0xc0) | (1 << CP_BIT_n) | (1 << REQ_BIT_n);
|
||||
DDRD = 0xc0 | (1 << ACT_BIT_n);
|
||||
|
||||
PORTD = (v & 0xc0) | (1 << IDLE_BIT);
|
||||
DDRD = 0xc0 | (1 << IDLE_BIT);
|
||||
|
||||
PORTC = byte_count & 0x3f;
|
||||
PORTC = cval;
|
||||
DDRC = 0x3f;
|
||||
|
||||
read_loop:
|
||||
@ -121,23 +149,23 @@ read_loop:
|
||||
;
|
||||
|
||||
next_port_c = SPDR;
|
||||
next_port_d = (next_port_c & 0xc0) | (1 << IDLE_BIT);
|
||||
next_port_d = (next_port_c & 0xc0) | (1 << CP_BIT_n) | (1 << REQ_BIT_n);
|
||||
|
||||
if (v & (1 << CLOCK_BIT))
|
||||
if (dval & (1 << CLK_BIT))
|
||||
{
|
||||
while (PIND & (1 << CLOCK_BIT))
|
||||
while (PIND & (1 << CLK_BIT))
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!(PIND & (1 << CLOCK_BIT)))
|
||||
while (!(PIND & (1 << CLK_BIT)))
|
||||
;
|
||||
}
|
||||
|
||||
PORTD = next_port_d;
|
||||
PORTC = next_port_c;
|
||||
|
||||
v = PIND;
|
||||
dval = PIND;
|
||||
|
||||
if (byte_count)
|
||||
{
|
||||
@ -146,46 +174,24 @@ read_loop:
|
||||
goto read_loop;
|
||||
}
|
||||
|
||||
if (v & (1 << CLOCK_BIT))
|
||||
{
|
||||
while (PIND & (1 << CLOCK_BIT))
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!(PIND & (1 << CLOCK_BIT)))
|
||||
;
|
||||
}
|
||||
|
||||
DDRD = (1 << IDLE_BIT);
|
||||
DDRC = 0;
|
||||
|
||||
PORTD = 0;
|
||||
PORTC = 0;
|
||||
|
||||
goto main_loop;
|
||||
|
||||
do_write1:
|
||||
byte_count = PINC;
|
||||
v = PIND;
|
||||
while (1)
|
||||
;
|
||||
|
||||
do_write:
|
||||
PORTD = (1 << IDLE_BIT);
|
||||
|
||||
write_loop:
|
||||
if (v & (1 << CLOCK_BIT))
|
||||
if (dval & (1 << CLK_BIT))
|
||||
{
|
||||
while (PIND & (1 << CLOCK_BIT))
|
||||
while (PIND & (1 << CLK_BIT))
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!(PIND & (1 << CLOCK_BIT)))
|
||||
while (!(PIND & (1 << CLK_BIT)))
|
||||
;
|
||||
}
|
||||
|
||||
v = PIND;
|
||||
SPDR = (v & 0xc0) | PINC;
|
||||
dval = PIND;
|
||||
SPDR = (dval & 0xc0) | PINC;
|
||||
|
||||
while (!(SPSR & (1 << SPIF)))
|
||||
;
|
||||
@ -198,9 +204,79 @@ write_loop:
|
||||
goto write_loop;
|
||||
}
|
||||
|
||||
PORTD = 0;
|
||||
|
||||
goto main_loop;
|
||||
|
||||
return 0;
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
void busy_wait()
|
||||
{
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
// Interrupt handler for REQ signal changes (INT0).
|
||||
ISR(INT0_vect, ISR_NAKED)
|
||||
{
|
||||
void (*next_fn)();
|
||||
|
||||
if (PIND & (1 << REQ_BIT_n))
|
||||
{
|
||||
DDRD = (1 << ACT_BIT_n);
|
||||
PORTD = (1 << ACT_BIT_n) | (1 << CP_BIT_n) | (1 << REQ_BIT_n);
|
||||
|
||||
DDRC = 0;
|
||||
PORTC = 0;
|
||||
|
||||
EIMSK |= (1 << 1);
|
||||
|
||||
next_fn = &busy_wait;
|
||||
}
|
||||
else
|
||||
{
|
||||
EIMSK &= ~(1 << 1);
|
||||
|
||||
next_fn = &start_command;
|
||||
}
|
||||
|
||||
uint16_t fn_int = (uint16_t)next_fn;
|
||||
uint16_t sp = ((SPH << 8) | SPL) + 1;
|
||||
uint8_t *p = (uint8_t *)sp;
|
||||
*p++ = fn_int >> 8;
|
||||
*p++ = fn_int & 0xff;
|
||||
|
||||
reti();
|
||||
}
|
||||
|
||||
// Interrupt handler for CP signal changes (INT1).
|
||||
ISR(INT1_vect, ISR_NAKED)
|
||||
{
|
||||
DDRB |= (1 << IRQ_BIT_n);
|
||||
|
||||
reti();
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
DDRB = (1 << SCK_BIT) | (1 << MOSI_BIT) | (1 << SS_BIT_n);
|
||||
PORTB = (1 << SS_BIT_n);
|
||||
|
||||
// SPI enabled, master, fosc/64 = 250 kHz
|
||||
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
|
||||
SPSR |= (1 << SPI2X);
|
||||
|
||||
DDRD = (1 << ACT_BIT_n);
|
||||
PORTD = (1 << ACT_BIT_n) | (1 << CP_BIT_n) | (1 << REQ_BIT_n);
|
||||
|
||||
DDRC = 0;
|
||||
PORTC = 0;
|
||||
|
||||
// Enable interrupts
|
||||
EICRA = (1 << 2) | (1 << 0);
|
||||
EIFR = (1 << 1) | (1 << 0);
|
||||
EIMSK = (1 << 1) | (1 << 0);
|
||||
|
||||
sei();
|
||||
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
vc romtag.asm device.c sd.c timer.c ../../spi-lib/spi.c ../../spi-lib/spi_low.asm -I../../spi-lib -nostdlib -o spisd.device
|
||||
vc romtag.c version.c device.c sd.c timer.c ../../spi-lib/spi.c ../../spi-lib/spi_low.asm -I../../spi-lib -O2 -nostdlib -lamiga -o spisd.device
|
||||
|
||||
@ -1,253 +1,429 @@
|
||||
/*
|
||||
* SPI SD device driver for K1208/Amiga 1200
|
||||
* Written by Niklas Ekström in July 2021.
|
||||
*
|
||||
* Copyright (C) 2018 Mike Stirling
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Parts of this file were changed in order for it to compile with VBCC.
|
||||
* The procedures device_get_geometry() and begin_io() are intact.
|
||||
* Previous version of this file (device.c) used code written by
|
||||
* Mike Sterling in 2018 for the SPI SD device driver for K1208/Amiga 1200.
|
||||
*
|
||||
* In order to handle sd card removal this file was rewritten almost entirely.
|
||||
*/
|
||||
|
||||
#include <exec/types.h>
|
||||
#include <exec/execbase.h>
|
||||
#include <exec/devices.h>
|
||||
#include <exec/errors.h>
|
||||
#include <exec/execbase.h>
|
||||
#include <exec/interrupts.h>
|
||||
#include <exec/ports.h>
|
||||
#include <exec/tasks.h>
|
||||
#include <libraries/dos.h>
|
||||
#include <devices/timer.h>
|
||||
#include <devices/trackdisk.h>
|
||||
#include <devices/newstyle.h>
|
||||
#include <proto/exec.h>
|
||||
#include <proto/alib.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "sd.h"
|
||||
#include "spi.h"
|
||||
|
||||
#ifndef TD_GETGEOMETRY
|
||||
// Needed to compile with AmigaOS 1.3 headers.
|
||||
#define TD_GETGEOMETRY (CMD_NONSTD+13)
|
||||
#define TASK_STACK_SIZE 2048
|
||||
#define TASK_PRIORITY 10
|
||||
|
||||
struct DriveGeometry
|
||||
{
|
||||
ULONG dg_SectorSize;
|
||||
ULONG dg_TotalSectors;
|
||||
ULONG dg_Cylinders;
|
||||
ULONG dg_CylSectors;
|
||||
ULONG dg_Heads;
|
||||
ULONG dg_TrackSectors;
|
||||
ULONG dg_BufMemType;
|
||||
UBYTE dg_DeviceType;
|
||||
UBYTE dg_Flags;
|
||||
UWORD dg_Reserved;
|
||||
};
|
||||
#endif
|
||||
#define DEBOUNCE_TIMEOUT_US 100000
|
||||
|
||||
char device_name[] = "spisd.device";
|
||||
char id_string[] = "spisd 0.3 (3 May 2020)";
|
||||
#define SIGB_CARD_CHANGE 30
|
||||
#define SIGB_OP_REQUEST 29
|
||||
#define SIGB_TIMER 28
|
||||
|
||||
#define SIGF_CARD_CHANGE (1 << SIGB_CARD_CHANGE)
|
||||
#define SIGF_OP_REQUEST (1 << SIGB_OP_REQUEST)
|
||||
#define SIGF_OP_TIMER (1 << SIGB_TIMER)
|
||||
|
||||
// How much of struct NSDeviceQueryResult we use/need. It could be extended
|
||||
// and we don't want that to change the behaviour of the code.
|
||||
#define NSD_QUERY_RESULT_LENGTH_REQUIRED 16
|
||||
|
||||
struct ExecBase *SysBase;
|
||||
BPTR saved_seg_list;
|
||||
static BPTR saved_seg_list;
|
||||
static struct timerequest tr;
|
||||
static struct Task *task;
|
||||
static struct MsgPort mp;
|
||||
static struct MsgPort timer_mp;
|
||||
static volatile BOOL card_present;
|
||||
static volatile BOOL card_opened;
|
||||
static volatile ULONG card_change_num;
|
||||
|
||||
BOOL is_open;
|
||||
static struct Interrupt *remove_int;
|
||||
static struct IOStdReq *change_int;
|
||||
|
||||
static struct Library *init_device(__reg("a6") struct ExecBase *sys_base, __reg("a0") BPTR seg_list, __reg("d0") struct Library *dev)
|
||||
static uint32_t device_get_geometry(struct IOStdReq *ior)
|
||||
{
|
||||
SysBase = *(struct ExecBase **)4;
|
||||
saved_seg_list = seg_list;
|
||||
struct DriveGeometry *geom = (struct DriveGeometry*)ior->io_Data;
|
||||
const sd_card_info_t *ci = sd_get_card_info();
|
||||
|
||||
dev->lib_Node.ln_Type = NT_DEVICE;
|
||||
dev->lib_Node.ln_Name = device_name;
|
||||
dev->lib_Flags = LIBF_SUMUSED | LIBF_CHANGED;
|
||||
dev->lib_Version = 0;
|
||||
dev->lib_Revision = 2;
|
||||
dev->lib_IdString = (APTR)id_string;
|
||||
if (ci->type == sdCardType_None)
|
||||
return TDERR_DiskChanged;
|
||||
|
||||
is_open = FALSE;
|
||||
|
||||
return dev;
|
||||
geom->dg_SectorSize = 1 << ci->block_size;
|
||||
geom->dg_TotalSectors = ci->total_sectors;
|
||||
geom->dg_Cylinders = geom->dg_TotalSectors / 4096;
|
||||
geom->dg_CylSectors = 4096;
|
||||
geom->dg_Heads = 16;
|
||||
geom->dg_TrackSectors = geom->dg_Cylinders / 16;
|
||||
geom->dg_BufMemType = MEMF_PUBLIC;
|
||||
geom->dg_DeviceType = DG_DIRECT_ACCESS;
|
||||
geom->dg_Flags = DGF_REMOVABLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BPTR expunge(__reg("a6") struct Library *dev)
|
||||
static void handle_changed()
|
||||
{
|
||||
if (dev->lib_OpenCnt != 0)
|
||||
{
|
||||
dev->lib_Flags |= LIBF_DELEXP;
|
||||
return 0;
|
||||
}
|
||||
// Wait to debounce the card detect switch.
|
||||
tr.tr_node.io_Command = TR_ADDREQUEST;
|
||||
tr.tr_time.tv_secs = 0;
|
||||
tr.tr_time.tv_micro = DEBOUNCE_TIMEOUT_US;
|
||||
DoIO((struct IORequest *)&tr);
|
||||
|
||||
spi_shutdown();
|
||||
int res = spi_get_card_present();
|
||||
|
||||
BPTR seg_list = saved_seg_list;
|
||||
Remove(&dev->lib_Node);
|
||||
FreeMem((char *)dev - dev->lib_NegSize, dev->lib_NegSize + dev->lib_PosSize);
|
||||
return seg_list;
|
||||
if (res == 1 && sd_open() == 0)
|
||||
card_opened = TRUE;
|
||||
else
|
||||
card_opened = FALSE;
|
||||
|
||||
Forbid();
|
||||
card_present = res == 1;
|
||||
card_change_num++;
|
||||
Permit();
|
||||
|
||||
if (remove_int)
|
||||
Cause(remove_int);
|
||||
|
||||
if (change_int)
|
||||
Cause((struct Interrupt *)change_int->io_Data);
|
||||
}
|
||||
|
||||
static void open(__reg("a6") struct Library *dev, __reg("a1") struct IORequest *ior, __reg("d0") ULONG unitnum, __reg("d1") ULONG flags)
|
||||
static uint32_t offset_to_sd_sectors(uint32_t high_offset, uint32_t low_offset)
|
||||
{
|
||||
ior->io_Error = IOERR_OPENFAIL;
|
||||
ior->io_Message.mn_Node.ln_Type = NT_REPLYMSG;
|
||||
|
||||
if (unitnum != 0)
|
||||
return;
|
||||
|
||||
if (!is_open)
|
||||
{
|
||||
spi_initialize();
|
||||
if (sd_open() != 0)
|
||||
return;
|
||||
is_open = TRUE;
|
||||
}
|
||||
|
||||
dev->lib_OpenCnt++;
|
||||
ior->io_Error = 0;
|
||||
return (high_offset << (32 - SD_SECTOR_SHIFT)) | (low_offset >> SD_SECTOR_SHIFT);
|
||||
}
|
||||
|
||||
static BPTR close(__reg("a6") struct Library *dev, __reg("a1") struct IORequest *ior)
|
||||
static void process_request(struct IOStdReq *ior)
|
||||
{
|
||||
ior->io_Device = NULL;
|
||||
ior->io_Unit = NULL;
|
||||
if (!card_present)
|
||||
ior->io_Error = TDERR_DiskChanged;
|
||||
else if (!card_opened)
|
||||
ior->io_Error = TDERR_NotSpecified;
|
||||
else
|
||||
{
|
||||
switch (ior->io_Command)
|
||||
{
|
||||
case TD_GETGEOMETRY:
|
||||
ior->io_Error = device_get_geometry(ior);
|
||||
break;
|
||||
|
||||
dev->lib_OpenCnt--;
|
||||
case TD_FORMAT:
|
||||
case CMD_WRITE:
|
||||
ior->io_Actual = 0;
|
||||
case TD_FORMAT64:
|
||||
case TD_WRITE64:
|
||||
case NSCMD_TD_FORMAT64:
|
||||
case NSCMD_TD_WRITE64:
|
||||
if (sd_write((uint8_t *)ior->io_Data, offset_to_sd_sectors(ior->io_Actual, ior->io_Offset), ior->io_Length >> SD_SECTOR_SHIFT) == 0)
|
||||
ior->io_Actual = ior->io_Length;
|
||||
else
|
||||
ior->io_Error = TDERR_NotSpecified;
|
||||
break;
|
||||
|
||||
if (dev->lib_OpenCnt == 0 && (dev->lib_Flags & LIBF_DELEXP))
|
||||
return expunge(dev);
|
||||
case CMD_READ:
|
||||
ior->io_Actual = 0;
|
||||
case TD_READ64:
|
||||
case NSCMD_TD_READ64:
|
||||
if (sd_read((uint8_t *)ior->io_Data, offset_to_sd_sectors(ior->io_Actual, ior->io_Offset), ior->io_Length >> SD_SECTOR_SHIFT) == 0)
|
||||
ior->io_Actual = ior->io_Length;
|
||||
else
|
||||
ior->io_Error = TDERR_NotSpecified;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
ReplyMsg(&ior->io_Message);
|
||||
}
|
||||
|
||||
static uint32_t device_get_geometry(struct IOStdReq *iostd)
|
||||
static void task_run()
|
||||
{
|
||||
struct DriveGeometry *geom = (struct DriveGeometry*)iostd->io_Data;
|
||||
const sd_card_info_t *ci = sd_get_card_info();
|
||||
if (card_present && sd_open() == 0)
|
||||
card_opened = TRUE;
|
||||
|
||||
if (ci->type != sdCardType_None) {
|
||||
geom->dg_SectorSize = 1 << ci->block_size;
|
||||
geom->dg_TotalSectors = ci->total_sectors; //ci->capacity >> ci->block_size;
|
||||
geom->dg_Cylinders = geom->dg_TotalSectors;
|
||||
geom->dg_CylSectors = 1;
|
||||
geom->dg_Heads = 1;
|
||||
geom->dg_TrackSectors = 1;
|
||||
geom->dg_BufMemType = MEMF_PUBLIC;
|
||||
geom->dg_DeviceType = 0; //DG_DIRECT_ACCESS;
|
||||
geom->dg_Flags = 1; //DGF_REMOVABLE;
|
||||
return 0;
|
||||
} else {
|
||||
return TDERR_DiskChanged;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
ULONG sigs = Wait(SIGF_CARD_CHANGE | SIGF_OP_REQUEST);
|
||||
|
||||
if (sigs & SIGF_CARD_CHANGE)
|
||||
handle_changed();
|
||||
|
||||
if (sigs & SIGF_OP_REQUEST)
|
||||
{
|
||||
BOOL first = TRUE;
|
||||
|
||||
struct IOStdReq *ior;
|
||||
while ((ior = (struct IOStdReq *)GetMsg(&mp)))
|
||||
{
|
||||
if (!first && (SetSignal(0, SIGF_CARD_CHANGE) & SIGF_CARD_CHANGE))
|
||||
handle_changed();
|
||||
|
||||
process_request(ior);
|
||||
first = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void begin_io(__reg("a6") struct Library *dev, __reg("a1") struct IORequest *ioreq)
|
||||
static void change_isr()
|
||||
{
|
||||
struct IOStdReq *iostd = (struct IOStdReq*)ioreq;
|
||||
Signal(task, SIGF_CARD_CHANGE);
|
||||
}
|
||||
|
||||
if (ioreq == NULL) {
|
||||
return;
|
||||
}
|
||||
static const UWORD supported_commands[] =
|
||||
{
|
||||
CMD_RESET,
|
||||
CMD_READ,
|
||||
CMD_WRITE,
|
||||
CMD_UPDATE,
|
||||
CMD_CLEAR,
|
||||
TD_MOTOR,
|
||||
TD_FORMAT,
|
||||
TD_REMOVE,
|
||||
TD_CHANGENUM,
|
||||
TD_CHANGESTATE,
|
||||
TD_PROTSTATUS,
|
||||
TD_GETDRIVETYPE,
|
||||
TD_ADDCHANGEINT,
|
||||
TD_REMCHANGEINT,
|
||||
TD_GETGEOMETRY,
|
||||
TD_READ64,
|
||||
TD_WRITE64,
|
||||
TD_FORMAT64,
|
||||
NSCMD_DEVICEQUERY,
|
||||
NSCMD_TD_READ64,
|
||||
NSCMD_TD_WRITE64,
|
||||
NSCMD_TD_FORMAT64,
|
||||
0
|
||||
};
|
||||
|
||||
iostd->io_Error = 0;
|
||||
static void begin_io(__reg("a6") struct Library *dev, __reg("a1") struct IOStdReq *ior)
|
||||
{
|
||||
if (!ior)
|
||||
return;
|
||||
|
||||
switch (iostd->io_Command) {
|
||||
case CMD_RESET:
|
||||
case CMD_CLEAR:
|
||||
case CMD_UPDATE:
|
||||
case TD_MOTOR:
|
||||
case TD_REMOVE:
|
||||
/* NULL commands */
|
||||
iostd->io_Actual = 0;
|
||||
break;
|
||||
case TD_PROTSTATUS:
|
||||
/* Should return a non-zero value if the card is write protected */
|
||||
iostd->io_Actual = 0;
|
||||
break;
|
||||
case TD_CHANGESTATE:
|
||||
/* Should return a non-zero value if the card is invalid or not inserted */
|
||||
iostd->io_Actual = 0;
|
||||
break;
|
||||
case TD_CHANGENUM:
|
||||
/* This should increment each time a disk is inserted */
|
||||
iostd->io_Actual = 1;
|
||||
break;
|
||||
case TD_GETDRIVETYPE:
|
||||
iostd->io_Actual = 0; //DG_DIRECT_ACCESS;
|
||||
break;
|
||||
case TD_GETGEOMETRY:
|
||||
iostd->io_Actual = 0;
|
||||
iostd->io_Error = device_get_geometry(iostd);
|
||||
break;
|
||||
#if 0
|
||||
case HD_SCSI_CMD:
|
||||
break;
|
||||
case NSCMD_DEVICEQUERY:
|
||||
break;
|
||||
case NSCMD_TD_READ64:
|
||||
break;
|
||||
case NSCMD_WRITE64:
|
||||
break;
|
||||
#endif
|
||||
ior->io_Error = 0;
|
||||
|
||||
case TD_FORMAT:
|
||||
case CMD_WRITE:
|
||||
/* FIXME: Should be deferred to task but this did not work reliably - investigate */
|
||||
if (sd_write((uint8_t *)iostd->io_Data, iostd->io_Offset >> SD_SECTOR_SHIFT, iostd->io_Length >> SD_SECTOR_SHIFT) == 0) {
|
||||
iostd->io_Actual = iostd->io_Length;
|
||||
iostd->io_Error = 0;
|
||||
} else {
|
||||
iostd->io_Actual = 0;
|
||||
iostd->io_Error = TDERR_NotSpecified;
|
||||
}
|
||||
break;
|
||||
case CMD_READ:
|
||||
if (sd_read((uint8_t *)iostd->io_Data, iostd->io_Offset >> SD_SECTOR_SHIFT, iostd->io_Length >> SD_SECTOR_SHIFT) == 0) {
|
||||
iostd->io_Actual = iostd->io_Length;
|
||||
iostd->io_Error = 0;
|
||||
} else {
|
||||
iostd->io_Actual = 0;
|
||||
iostd->io_Error = TDERR_NotSpecified;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
iostd->io_Error = IOERR_NOCMD;
|
||||
}
|
||||
switch (ior->io_Command)
|
||||
{
|
||||
case CMD_RESET:
|
||||
case CMD_CLEAR:
|
||||
case CMD_UPDATE:
|
||||
case TD_MOTOR:
|
||||
case TD_PROTSTATUS:
|
||||
ior->io_Actual = 0;
|
||||
break;
|
||||
|
||||
if (iostd && !(iostd->io_Flags & IOF_QUICK)) {
|
||||
/* Reply to message now unless it was deferred to the task or is IOF_QUICK */
|
||||
ReplyMsg(&iostd->io_Message);
|
||||
}
|
||||
case TD_CHANGESTATE:
|
||||
ior->io_Actual = card_present ? 0 : 1;
|
||||
break;
|
||||
|
||||
case TD_CHANGENUM:
|
||||
ior->io_Actual = card_change_num;
|
||||
break;
|
||||
|
||||
case TD_GETDRIVETYPE:
|
||||
ior->io_Actual = DG_DIRECT_ACCESS;
|
||||
break;
|
||||
|
||||
case TD_REMOVE:
|
||||
remove_int = (struct Interrupt *)ior->io_Data;
|
||||
break;
|
||||
|
||||
case TD_ADDCHANGEINT:
|
||||
if (change_int)
|
||||
ior->io_Error = IOERR_ABORTED;
|
||||
else
|
||||
{
|
||||
change_int = ior;
|
||||
ior->io_Flags &= ~IOF_QUICK;
|
||||
ior = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case TD_REMCHANGEINT:
|
||||
if (change_int == ior)
|
||||
change_int = NULL;
|
||||
break;
|
||||
|
||||
case NSCMD_DEVICEQUERY:
|
||||
if (ior->io_Length >= NSD_QUERY_RESULT_LENGTH_REQUIRED)
|
||||
{
|
||||
struct NSDeviceQueryResult *result = ior->io_Data;
|
||||
result->nsdqr_DevQueryFormat = 0;
|
||||
result->nsdqr_SizeAvailable = NSD_QUERY_RESULT_LENGTH_REQUIRED;
|
||||
result->nsdqr_DeviceType = NSDEVTYPE_TRACKDISK;
|
||||
result->nsdqr_DeviceSubType = 0;
|
||||
result->nsdqr_SupportedCommands = supported_commands;
|
||||
ior->io_Actual = NSD_QUERY_RESULT_LENGTH_REQUIRED;
|
||||
}
|
||||
else {
|
||||
ior->io_Error = IOERR_BADLENGTH;
|
||||
}
|
||||
break;
|
||||
|
||||
case TD_GETGEOMETRY:
|
||||
case TD_FORMAT:
|
||||
case CMD_WRITE:
|
||||
case CMD_READ:
|
||||
case TD_READ64:
|
||||
case TD_WRITE64:
|
||||
case NSCMD_TD_READ64:
|
||||
case NSCMD_TD_WRITE64:
|
||||
case NSCMD_TD_FORMAT64:
|
||||
PutMsg(&mp, (struct Message *)&ior->io_Message);
|
||||
ior->io_Flags &= ~IOF_QUICK;
|
||||
ior = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
ior->io_Error = IOERR_NOCMD;
|
||||
}
|
||||
|
||||
if (ior && !(ior->io_Flags & IOF_QUICK))
|
||||
ReplyMsg(&ior->io_Message);
|
||||
}
|
||||
|
||||
static ULONG abort_io(__reg("a6") struct Library *dev, __reg("a1") struct IORequest *ior)
|
||||
{
|
||||
// There is no asynchronous processing of requests so this is always a no-op.
|
||||
return IOERR_NOCMD;
|
||||
return IOERR_NOCMD;
|
||||
}
|
||||
|
||||
static struct Library *init_device(__reg("a6") struct ExecBase *sys_base, __reg("a0") BPTR seg_list, __reg("d0") struct Library *dev)
|
||||
{
|
||||
SysBase = *(struct ExecBase **)4;
|
||||
saved_seg_list = seg_list;
|
||||
|
||||
dev->lib_Node.ln_Type = NT_DEVICE;
|
||||
dev->lib_Node.ln_Name = device_name;
|
||||
dev->lib_Flags = LIBF_SUMUSED | LIBF_CHANGED;
|
||||
dev->lib_Version = VERSION;
|
||||
dev->lib_Revision = REVISION;
|
||||
dev->lib_IdString = (APTR)id_string;
|
||||
|
||||
Forbid();
|
||||
|
||||
tr.tr_node.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
|
||||
tr.tr_node.io_Message.mn_ReplyPort = &timer_mp;
|
||||
tr.tr_node.io_Message.mn_Length = sizeof(tr);
|
||||
|
||||
if (OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)&tr, 0))
|
||||
goto fail1;
|
||||
|
||||
task = CreateTask(device_name, TASK_PRIORITY, (char *)&task_run, TASK_STACK_SIZE);
|
||||
if (!task)
|
||||
goto fail2;
|
||||
|
||||
int res = spi_initialize(&change_isr);
|
||||
if (res < 0)
|
||||
goto fail3;
|
||||
|
||||
card_present = res == 1;
|
||||
|
||||
mp.mp_Node.ln_Type = NT_MSGPORT;
|
||||
mp.mp_Flags = PA_SIGNAL;
|
||||
mp.mp_SigBit = SIGB_OP_REQUEST;
|
||||
mp.mp_SigTask = task;
|
||||
NewList(&mp.mp_MsgList);
|
||||
|
||||
timer_mp.mp_Node.ln_Type = NT_MSGPORT;
|
||||
timer_mp.mp_Flags = PA_SIGNAL;
|
||||
timer_mp.mp_SigBit = SIGB_TIMER;
|
||||
timer_mp.mp_SigTask = task;
|
||||
NewList(&timer_mp.mp_MsgList);
|
||||
|
||||
Permit();
|
||||
return dev;
|
||||
|
||||
fail3:
|
||||
DeleteTask(task);
|
||||
|
||||
fail2:
|
||||
CloseDevice((struct IORequest *)&tr);
|
||||
|
||||
fail1:
|
||||
Permit();
|
||||
FreeMem((char *)dev - dev->lib_NegSize, dev->lib_NegSize + dev->lib_PosSize);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BPTR expunge(__reg("a6") struct Library *dev)
|
||||
{
|
||||
if (dev->lib_OpenCnt != 0)
|
||||
{
|
||||
dev->lib_Flags |= LIBF_DELEXP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This could be improved on.
|
||||
// There is a risk that the task has an outstanding debounce timer,
|
||||
// and deleting the task at that point will probably cause a crash.
|
||||
|
||||
spi_shutdown();
|
||||
|
||||
DeleteTask(task);
|
||||
|
||||
CloseDevice((struct IORequest *)&tr);
|
||||
|
||||
BPTR seg_list = saved_seg_list;
|
||||
Remove(&dev->lib_Node);
|
||||
FreeMem((char *)dev - dev->lib_NegSize, dev->lib_NegSize + dev->lib_PosSize);
|
||||
return seg_list;
|
||||
}
|
||||
|
||||
static void open(__reg("a6") struct Library *dev, __reg("a1") struct IORequest *ior, __reg("d0") ULONG unitnum, __reg("d1") ULONG flags)
|
||||
{
|
||||
ior->io_Error = IOERR_OPENFAIL;
|
||||
ior->io_Message.mn_Node.ln_Type = NT_REPLYMSG;
|
||||
|
||||
if (unitnum != 0)
|
||||
return;
|
||||
|
||||
dev->lib_OpenCnt++;
|
||||
ior->io_Error = 0;
|
||||
}
|
||||
|
||||
static BPTR close(__reg("a6") struct Library *dev, __reg("a1") struct IORequest *ior)
|
||||
{
|
||||
ior->io_Device = NULL;
|
||||
ior->io_Unit = NULL;
|
||||
|
||||
dev->lib_OpenCnt--;
|
||||
|
||||
if (dev->lib_OpenCnt == 0 && (dev->lib_Flags & LIBF_DELEXP))
|
||||
return expunge(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ULONG device_vectors[] =
|
||||
{
|
||||
(ULONG)open,
|
||||
(ULONG)close,
|
||||
(ULONG)expunge,
|
||||
0,
|
||||
(ULONG)begin_io,
|
||||
(ULONG)abort_io,
|
||||
-1,
|
||||
(ULONG)open,
|
||||
(ULONG)close,
|
||||
(ULONG)expunge,
|
||||
0,
|
||||
(ULONG)begin_io,
|
||||
(ULONG)abort_io,
|
||||
-1,
|
||||
};
|
||||
|
||||
ULONG auto_init_tables[] =
|
||||
{
|
||||
sizeof(struct Library),
|
||||
(ULONG)device_vectors,
|
||||
0,
|
||||
(ULONG)init_device,
|
||||
sizeof(struct Library),
|
||||
(ULONG)device_vectors,
|
||||
0,
|
||||
(ULONG)init_device,
|
||||
};
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
RTC_MATCHWORD: equ $4afc
|
||||
RTF_AUTOINIT: equ (1<<7)
|
||||
NT_DEVICE: equ 3
|
||||
VERSION: equ 1
|
||||
PRIORITY: equ 0
|
||||
|
||||
section code,code
|
||||
|
||||
moveq #-1,d0
|
||||
rts
|
||||
|
||||
romtag:
|
||||
dc.w RTC_MATCHWORD
|
||||
dc.l romtag
|
||||
dc.l endcode
|
||||
dc.b RTF_AUTOINIT
|
||||
dc.b VERSION
|
||||
dc.b NT_DEVICE
|
||||
dc.b PRIORITY
|
||||
dc.l _device_name
|
||||
dc.l _id_string
|
||||
dc.l _auto_init_tables
|
||||
endcode:
|
||||
26
examples/spisd/romtag.c
Normal file
26
examples/spisd/romtag.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include <exec/types.h>
|
||||
#include <exec/resident.h>
|
||||
#include <exec/nodes.h>
|
||||
|
||||
#include "version.h"
|
||||
|
||||
extern ULONG auto_init_tables[];
|
||||
|
||||
LONG noexec(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Need to be const to be placed as constant in code segment
|
||||
const struct Resident romtag =
|
||||
{
|
||||
.rt_MatchWord = RTC_MATCHWORD,
|
||||
.rt_MatchTag = (void *)&romtag,
|
||||
.rt_EndSkip = &romtag + 1,
|
||||
.rt_Flags = RTF_AUTOINIT,
|
||||
.rt_Version = VERSION,
|
||||
.rt_Type = NT_DEVICE,
|
||||
.rt_Pri = 0,
|
||||
.rt_Name = device_name,
|
||||
.rt_IdString = id_string,
|
||||
.rt_Init = auto_init_tables
|
||||
};
|
||||
@ -85,8 +85,7 @@ static int sd_parse_csd(sd_card_info_t *ci, const uint32_t *bits)
|
||||
csd->dsr_implemented = (bits[1] >> 12) & 0x1;
|
||||
|
||||
if (ci->type == sdCardType_SD1_x || ci->type == sdCardType_SD2_0) {
|
||||
csd->device_size = (bits[1] << 2) & 0xffc;
|
||||
csd->device_size |= (bits[2] >> 30) & 0x3;
|
||||
csd->device_size = ((bits[1] & 0x3ff) << 2) | (bits[2] >> 30);
|
||||
|
||||
csd->max_read_current_vdd_min = (bits[2] >> 27) & 0x7;
|
||||
csd->max_read_current_vdd_max = (bits[2] >> 24) & 0x7;
|
||||
@ -97,8 +96,7 @@ static int sd_parse_csd(sd_card_info_t *ci, const uint32_t *bits)
|
||||
//ci->capacity = (uint64_t)(csd->device_size + 1) << (csd->device_size_mult + csd->read_block_len + 2);
|
||||
ci->total_sectors = (uint32_t)(csd->device_size + 1) << (csd->device_size_mult + 2);
|
||||
} else if (ci->type == sdCardType_SDHC) {
|
||||
csd->device_size = (bits[1] << 16) & 0x3f;
|
||||
csd->device_size |= (bits[2] >> 16) & 0xffff;
|
||||
csd->device_size = ((bits[1] & 0x3f) << 16) | (bits[2] >> 16);
|
||||
|
||||
//ci->capacity = (uint64_t)(csd->device_size + 1) << 19;
|
||||
ci->total_sectors = (uint32_t)(csd->device_size + 1) << (19 - csd->read_block_len);
|
||||
@ -241,8 +239,16 @@ static int sd_write_block(const uint8_t *buf, uint8_t token)
|
||||
|
||||
/* Send token */
|
||||
spi_write(&token, 1);
|
||||
if (token != 0xfd) {
|
||||
/* Send data, except for STOP_TRAN */
|
||||
if (token == 0xfd) {
|
||||
/* After sending STOP_TRAN, a byte needs to be read before the card
|
||||
* goes busy. This byte is undefined, so unless we read it now, the
|
||||
* next sd_wait_ready() will read it and could erronously decide
|
||||
* that the card is immediately ready.
|
||||
*/
|
||||
spi_read(&resp, 1);
|
||||
}
|
||||
else {
|
||||
/* Send data */
|
||||
spi_write(buf, SD_SECTOR_SIZE);
|
||||
spi_write(crc, 2); /* dummy */
|
||||
|
||||
@ -468,7 +474,7 @@ int sd_read(uint8_t *buf, uint32_t sector, uint32_t count)
|
||||
} else {
|
||||
err = sdError_BadResponse;
|
||||
}
|
||||
} else {
|
||||
} else if (count > 1) {
|
||||
/* Read multiple sectors */
|
||||
if (sd_send_cmd(CMD18, sector) == 0) {
|
||||
do {
|
||||
@ -514,7 +520,7 @@ int sd_write(const uint8_t *buf, uint32_t sector, uint32_t count)
|
||||
} else {
|
||||
err = sdError_BadResponse;
|
||||
}
|
||||
} else {
|
||||
} else if (count > 1) {
|
||||
if (ci->type == sdCardType_SD1_x || ci->type == sdCardType_SD2_0 || ci->type == sdCardType_SDHC) {
|
||||
/* Pre-defined sector count */
|
||||
sd_send_cmd(ACMD23, count);
|
||||
|
||||
8
examples/spisd/version.c
Normal file
8
examples/spisd/version.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include "version.h"
|
||||
|
||||
#define STR(x) #x
|
||||
#define XSTR(x) STR(x)
|
||||
|
||||
char device_name[] = NAME ".device";
|
||||
char id_string[] = NAME " " XSTR(VERSION) "." XSTR(REVISION) " (" DATE ")\n\r";
|
||||
|
||||
12
examples/spisd/version.h
Normal file
12
examples/spisd/version.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef VERSION_H_
|
||||
#define VERSION_H_
|
||||
|
||||
#define NAME "spisd"
|
||||
#define VERSION 2
|
||||
#define REVISION 2
|
||||
#define DATE "5.3.2023"
|
||||
|
||||
extern char device_name[];
|
||||
extern char id_string[];
|
||||
|
||||
#endif
|
||||
@ -1,24 +1,40 @@
|
||||
# Assembly instructions
|
||||
|
||||
The assembly of the adapter is very simple.
|
||||
The assembly of the adapter is quite simple.
|
||||
You need a parallel port connector and an Arduino Nano.
|
||||
Connect the parallel port to the Nano as follows:
|
||||
|
||||
| Par. pin # | Par. pin name | Nano pin # | Nano pin name |
|
||||
|-----------:|--------------:|-----------:|--------------:|
|
||||
| 2 | D0 | 26 | A0 |
|
||||
| 3 | D1 | 25 | A1 |
|
||||
| 4 | D2 | 24 | A2 |
|
||||
| 5 | D3 | 23 | A3 |
|
||||
| 6 | D4 | 22 | A4 |
|
||||
| 7 | D5 | 21 | A5 |
|
||||
| 8 | D6 | 9 | D6 |
|
||||
| 9 | D7 | 10 | D7 |
|
||||
| 11 | BUSY | 7 | D4 |
|
||||
| 12 | POUT | 8 | D5 |
|
||||
| 20 | GND | 4 | GND |
|
||||
| Par. pin # | Par. pin | Nano pin | Atmega328P |
|
||||
|---:|-----:|----:|----:|
|
||||
| 2 | D0 | A0 | PC0 |
|
||||
| 3 | D1 | A1 | PC1 |
|
||||
| 4 | D2 | A2 | PC2 |
|
||||
| 5 | D3 | A3 | PC3 |
|
||||
| 6 | D4 | A4 | PC4 |
|
||||
| 7 | D5 | A5 | PC5 |
|
||||
| 8 | D6 | D6 | PD6 |
|
||||
| 9 | D7 | D7 | PD7 |
|
||||
| 10 | ACK | D9 | PB1 |
|
||||
| 11 | BUSY | D4 | PD4 |
|
||||
| 12 | POUT | D5 | PD5 |
|
||||
| 13 | SEL | D2 | PD2 |
|
||||
| 18..25 | GND | GND | GND |
|
||||
|
||||
Note that pins D0-D5 are connected to port C on the AVR (pins A0-A5 on the Nano) and pins D6-D7 are connected to port D on the AVR (pins D6-D7 on the Nano).
|
||||
This is identical to how the [plipbox](https://github.com/cnvogelg/plipbox) connects the parallel port to the Arduino Nano, so it should be possible to use an existing plipbox PCB for this adapter (although this has not been tested).
|
||||
## Connecting SD card module to Nano
|
||||
|
||||
If you are connecting an SD card module to the adapter then connect pin 13 on the parallel port connector (SEL) to the chip select (CS) pin on the SD card module, and connect the other SPI pins as you normally would connect an SPI module to the Arduino Nano.
|
||||
To use the adapter with the example SD card driver (spisd.device),
|
||||
connect the SD card module to the Nano as follows:
|
||||
|
||||
| SD card | Nano pin | Atmega328P |
|
||||
|-----:|----:|----:|
|
||||
| SS | D10 | PB2 |
|
||||
| MOSI | D11 | PB3 |
|
||||
| MISO | D12 | PB4 |
|
||||
| SCK | D13 | PB5 |
|
||||
| CD | D3 | PD3 |
|
||||
| VCC | VCC | |
|
||||
| GND | GND | GND |
|
||||
|
||||
CD = Card Detect. Not all modules has this pin available, but since the Micro SD card connectors typically has this pin it should be possible to solder a wire to the connector.
|
||||
|
||||
Note that the SD card module must have voltage level translation between the +5V used by the Amiga and the Nano, and the +3.3V used by the SD card.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 146 KiB |
15
rp2040/CMakeLists.txt
Normal file
15
rp2040/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
include(pico_sdk_import.cmake)
|
||||
|
||||
project(par_spi C CXX ASM)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
pico_sdk_init()
|
||||
|
||||
add_executable(par_spi par_spi.c)
|
||||
|
||||
pico_add_extra_outputs(par_spi)
|
||||
|
||||
target_link_libraries(par_spi pico_stdlib hardware_spi)
|
||||
19
rp2040/README.md
Normal file
19
rp2040/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# RP2040 version
|
||||
|
||||
The code for the AVR microcontroller has been ported to the RP2040 microcontroller.
|
||||
RP2040 is the microcontroller used on the Raspberry Pi Pico board.
|
||||
|
||||
## Build instructions
|
||||
|
||||
The [Raspberry Pi Pico SDK](https://github.com/raspberrypi/pico-sdk) must be installed.
|
||||
Then, standing in the rp2040 subdirectory, execute:
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
export PICO_SDK_PATH=~/pico/pico-sdk
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
Copy the generated file `build/par_spi.uf2` to the microcontroller's flash.
|
||||
206
rp2040/par_spi.c
Normal file
206
rp2040/par_spi.c
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Written in October 2022 by Niklas Ekström.
|
||||
*
|
||||
* Runs on RP2040 microcontroller instead of AVR as before,
|
||||
* but uses the same protocol and Amiga software.
|
||||
*/
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/spi.h"
|
||||
|
||||
// Pin name GPIO Direction Comment Description
|
||||
#define PIN_D(x) (0+x) // In/out
|
||||
#define PIN_IRQ 8 // Output Active low
|
||||
#define PIN_ACT 9 // Output Active low
|
||||
#define PIN_CLK 10 // Input
|
||||
#define PIN_REQ 11 // Input Active low
|
||||
#define PIN_MISO 16 // Input Pull-up
|
||||
#define PIN_SS 17 // Output Active low
|
||||
#define PIN_SCK 18 // Output
|
||||
#define PIN_MOSI 19 // Output
|
||||
#define PIN_CDET 20 // Input Pull-up Card Detect
|
||||
|
||||
#define SPI_SLOW_FREQUENCY (400*1000)
|
||||
#define SPI_FAST_FREQUENCY (16*1000*1000)
|
||||
|
||||
static uint32_t prev_cdet;
|
||||
|
||||
static void handle_request() {
|
||||
uint32_t pins;
|
||||
|
||||
while (1) {
|
||||
pins = gpio_get_all();
|
||||
if (!(pins & (1 << PIN_REQ)))
|
||||
break;
|
||||
|
||||
if ((pins & (1 << PIN_CDET)) != prev_cdet) {
|
||||
gpio_put(PIN_IRQ, false);
|
||||
gpio_set_dir(PIN_IRQ, true);
|
||||
prev_cdet = pins & (1 << PIN_CDET);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t prev_clk = pins & (1 << PIN_CLK);
|
||||
|
||||
if ((pins & 0xc0) != 0xc0) {
|
||||
uint32_t byte_count = 0;
|
||||
bool read = false;
|
||||
|
||||
if (!(pins & 0x80)) { // READ1 or WRITE1
|
||||
read = !!(pins & 0x40);
|
||||
byte_count = pins & 0x3f;
|
||||
|
||||
gpio_put(PIN_ACT, 0);
|
||||
} else { // READ2 or WRITE2
|
||||
byte_count = (pins & 0x3f) << 7;
|
||||
|
||||
gpio_put(PIN_ACT, 0);
|
||||
|
||||
while (1) {
|
||||
pins = gpio_get_all();
|
||||
if ((pins & (1 << PIN_CLK)) != prev_clk)
|
||||
break;
|
||||
|
||||
if (pins & (1 << PIN_REQ))
|
||||
return;
|
||||
}
|
||||
|
||||
read = !!(pins & 0x80);
|
||||
byte_count |= pins & 0x7f;
|
||||
prev_clk = pins & (1 << PIN_CLK);
|
||||
}
|
||||
|
||||
if (read) {
|
||||
spi_get_hw(spi0)->dr = 0xff;
|
||||
|
||||
uint32_t prev_ss = pins & (1 << PIN_SS);
|
||||
|
||||
while (1) {
|
||||
while (!spi_is_readable(spi0))
|
||||
tight_loop_contents();
|
||||
|
||||
uint32_t value = spi_get_hw(spi0)->dr;
|
||||
|
||||
while (1) {
|
||||
pins = gpio_get_all();
|
||||
if ((pins & (1 << PIN_CLK)) != prev_clk)
|
||||
break;
|
||||
|
||||
if (pins & (1 << PIN_REQ))
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_put_all(prev_ss | value);
|
||||
gpio_set_dir_out_masked(0xff);
|
||||
|
||||
if (!byte_count)
|
||||
break;
|
||||
|
||||
spi_get_hw(spi0)->dr = 0xff;
|
||||
prev_clk = pins & (1 << PIN_CLK);
|
||||
byte_count--;
|
||||
}
|
||||
} else {
|
||||
while (1) {
|
||||
while (1) {
|
||||
pins = gpio_get_all();
|
||||
if ((pins & (1 << PIN_CLK)) != prev_clk)
|
||||
break;
|
||||
|
||||
if (pins & (1 << PIN_REQ))
|
||||
return;
|
||||
}
|
||||
|
||||
spi_get_hw(spi0)->dr = pins & 0xff;
|
||||
|
||||
while (!spi_is_readable(spi0))
|
||||
tight_loop_contents();
|
||||
|
||||
(void)spi_get_hw(spi0)->dr;
|
||||
|
||||
if (!byte_count)
|
||||
break;
|
||||
|
||||
prev_clk = pins & (1 << PIN_CLK);
|
||||
byte_count--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch ((pins & 0x3e) >> 1) {
|
||||
case 0: { // SPI_SELECT
|
||||
gpio_put(PIN_SS, !(pins & 1));
|
||||
gpio_put(PIN_ACT, 0);
|
||||
break;
|
||||
}
|
||||
case 1: { // CARD_PRESENT
|
||||
gpio_set_dir(PIN_IRQ, false);
|
||||
gpio_put(PIN_ACT, 0);
|
||||
|
||||
while (1) {
|
||||
pins = gpio_get_all();
|
||||
if ((pins & (1 << PIN_CLK)) != prev_clk)
|
||||
break;
|
||||
|
||||
if (pins & (1 << PIN_REQ))
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_put(PIN_D(0), !gpio_get(PIN_CDET));
|
||||
gpio_set_dir_out_masked(0xff);
|
||||
break;
|
||||
}
|
||||
case 2: { // SPEED
|
||||
spi_set_baudrate(spi0, pins & 1 ?
|
||||
SPI_FAST_FREQUENCY :
|
||||
SPI_SLOW_FREQUENCY);
|
||||
|
||||
gpio_put(PIN_ACT, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
pins = gpio_get_all();
|
||||
if (pins & (1 << PIN_REQ))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
spi_init(spi0, SPI_SLOW_FREQUENCY);
|
||||
|
||||
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
|
||||
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
|
||||
gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
|
||||
gpio_pull_up(PIN_MISO);
|
||||
|
||||
gpio_init(PIN_SS);
|
||||
gpio_put(PIN_SS, 1);
|
||||
gpio_set_dir(PIN_SS, GPIO_OUT);
|
||||
|
||||
gpio_init(PIN_CDET);
|
||||
gpio_pull_up(PIN_CDET);
|
||||
|
||||
for (int i = 0; i < 12; i++)
|
||||
gpio_init(i);
|
||||
|
||||
gpio_put(PIN_ACT, 1);
|
||||
gpio_set_dir(PIN_ACT, GPIO_OUT);
|
||||
|
||||
prev_cdet = gpio_get_all() & (1 << PIN_CDET);
|
||||
|
||||
while (1) {
|
||||
handle_request();
|
||||
|
||||
gpio_set_dir_in_masked(0xff);
|
||||
gpio_clr_mask(0xff);
|
||||
|
||||
gpio_put(PIN_ACT, 1);
|
||||
|
||||
while (spi_is_busy(spi0))
|
||||
tight_loop_contents();
|
||||
|
||||
if (spi_is_readable(spi0))
|
||||
(void)spi_get_hw(spi0)->dr;
|
||||
}
|
||||
}
|
||||
73
rp2040/pico_sdk_import.cmake
Normal file
73
rp2040/pico_sdk_import.cmake
Normal file
@ -0,0 +1,73 @@
|
||||
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||
|
||||
# This can be dropped into an external project to help locate this SDK
|
||||
# It should be include()ed prior to project()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||
|
||||
if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT)
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
# GIT_SUBMODULES_RECURSE was added in 3.17
|
||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
GIT_SUBMODULES_RECURSE FALSE
|
||||
)
|
||||
else ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT pico_sdk)
|
||||
message("Downloading Raspberry Pi Pico SDK")
|
||||
FetchContent_Populate(pico_sdk)
|
||||
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||
endif ()
|
||||
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||
else ()
|
||||
message(FATAL_ERROR
|
||||
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||
|
||||
include(${PICO_SDK_INIT_CMAKE_FILE})
|
||||
16
spi-lib/cia_protos.h
Normal file
16
spi-lib/cia_protos.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef CIA_PROTOS_H
|
||||
#define CIA_PROTOS_H
|
||||
|
||||
#include <exec/types.h>
|
||||
#include <exec/interrupts.h>
|
||||
#include <exec/libraries.h>
|
||||
|
||||
struct Interrupt *AddICRVector(__reg("a6") struct Library *resource, __reg("d0") LONG iCRBit, __reg("a1") struct Interrupt *interrupt)="\tjsr\t-6(a6)";
|
||||
|
||||
void RemICRVector(__reg("a6") struct Library *resource, __reg("d0") LONG iCRBit, __reg("a1") struct Interrupt *interrupt)="\tjsr\t-12(a6)";
|
||||
|
||||
WORD AbleICR(__reg("a6") struct Library *resource, __reg("d0") LONG mask)="\tjsr\t-18(a6)";
|
||||
|
||||
WORD SetICR(__reg("a6") struct Library *resource, __reg("d0") LONG mask)="\tjsr\t-24(a6)";
|
||||
|
||||
#endif /* CIA_PROTOS_H */
|
||||
10
spi-lib/misc_protos.h
Normal file
10
spi-lib/misc_protos.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef MISC_PROTOS_H
|
||||
#define MISC_PROTOS_H
|
||||
|
||||
#include <exec/types.h>
|
||||
|
||||
UBYTE *AllocMiscResource(__reg("a6") struct Library *resource, __reg("d0") ULONG unitNum, __reg("a1") const char *name)="\tjsr\t-6(a6)";
|
||||
|
||||
void FreeMiscResource(__reg("a6") struct Library *resource, __reg("d0") ULONG unitNum)="\tjsr\t-12(a6)";
|
||||
|
||||
#endif /* MISC_PROTOS_H */
|
||||
284
spi-lib/spi.c
284
spi-lib/spi.c
@ -1,22 +1,30 @@
|
||||
/*
|
||||
* Written in the end of April 2020 by Niklas Ekström.
|
||||
* Updated in July 2021 by Niklas Ekström to handle Card Present signal.
|
||||
*/
|
||||
#include <exec/types.h>
|
||||
#include <exec/interrupts.h>
|
||||
#include <exec/libraries.h>
|
||||
#include <hardware/cia.h>
|
||||
#include <resources/cia.h>
|
||||
#include <resources/misc.h>
|
||||
|
||||
#include <proto/exec.h>
|
||||
#include "cia_protos.h"
|
||||
#include "misc_protos.h"
|
||||
|
||||
#include "spi.h"
|
||||
|
||||
#define CIAB_PRTRSEL 2
|
||||
#define CIAB_PRTRPOUT 1
|
||||
#define CIAB_PRTRBUSY 0
|
||||
#define REQ_BIT CIAB_PRTRSEL
|
||||
#define CLK_BIT CIAB_PRTRPOUT
|
||||
#define ACT_BIT CIAB_PRTRBUSY
|
||||
|
||||
#define CS_BIT CIAB_PRTRSEL
|
||||
#define CLOCK_BIT CIAB_PRTRPOUT
|
||||
#define IDLE_BIT CIAB_PRTRBUSY
|
||||
#define REQ_MASK (1 << REQ_BIT)
|
||||
#define CLK_MASK (1 << CLK_BIT)
|
||||
#define ACT_MASK (1 << ACT_BIT)
|
||||
|
||||
#define CS_MASK (1 << CS_BIT)
|
||||
#define CLOCK_MASK (1 << CLOCK_BIT)
|
||||
#define IDLE_MASK (1 << IDLE_BIT)
|
||||
extern void spi_read_fast(__reg("a0") UBYTE *buf, __reg("d0") ULONG size);
|
||||
extern void spi_write_fast(__reg("a0") const UBYTE *buf, __reg("d0") ULONG size);
|
||||
|
||||
static volatile UBYTE *cia_a_prb = (volatile UBYTE *)0xbfe101;
|
||||
static volatile UBYTE *cia_a_ddrb = (volatile UBYTE *)0xbfe301;
|
||||
@ -26,59 +34,93 @@ static volatile UBYTE *cia_b_ddra = (volatile UBYTE *)0xbfd200;
|
||||
|
||||
static long current_speed = SPI_SPEED_SLOW;
|
||||
|
||||
void spi_initialize()
|
||||
static const char spi_lib_name[] = "spi-lib";
|
||||
|
||||
static struct Library *miscbase;
|
||||
static struct Library *ciaabase;
|
||||
|
||||
static struct Interrupt flag_interrupt;
|
||||
|
||||
static int wait_until_active()
|
||||
{
|
||||
// Should allocate the parallel port in a system friendly way?
|
||||
|
||||
*cia_b_pra = (*cia_b_pra & ~IDLE_MASK) | (CS_MASK | CLOCK_MASK);
|
||||
*cia_b_ddra = (*cia_b_ddra & ~IDLE_MASK) | (CS_MASK | CLOCK_MASK);
|
||||
|
||||
*cia_a_prb = 0xff;
|
||||
*cia_a_ddrb = 0;
|
||||
}
|
||||
|
||||
void spi_shutdown()
|
||||
{
|
||||
*cia_b_ddra &= 0xf8;
|
||||
*cia_a_ddrb = 0;
|
||||
}
|
||||
|
||||
static void wait_until_idle()
|
||||
{
|
||||
// This will block forever if the adapter is not present.
|
||||
// TODO: Should eventually timeout and give up.
|
||||
|
||||
int count = 32;
|
||||
UBYTE ctrl = *cia_b_pra;
|
||||
while (ctrl & IDLE_MASK)
|
||||
while (count > 0 && (ctrl & ACT_MASK))
|
||||
{
|
||||
ctrl ^= CLOCK_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
count--;
|
||||
ctrl = *cia_b_pra;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void spi_select()
|
||||
{
|
||||
*cia_a_prb = 0xc1;
|
||||
|
||||
UBYTE prev = *cia_b_pra;
|
||||
*cia_b_pra = prev & ~REQ_MASK;
|
||||
|
||||
wait_until_active();
|
||||
|
||||
*cia_b_pra = prev;
|
||||
}
|
||||
|
||||
void spi_deselect()
|
||||
{
|
||||
*cia_a_prb = 0xc0;
|
||||
|
||||
UBYTE prev = *cia_b_pra;
|
||||
*cia_b_pra = prev & ~REQ_MASK;
|
||||
|
||||
wait_until_active();
|
||||
|
||||
*cia_b_pra = prev;
|
||||
}
|
||||
|
||||
int spi_get_card_present()
|
||||
{
|
||||
*cia_a_prb = 0xc2;
|
||||
|
||||
UBYTE ctrl = *cia_b_pra;
|
||||
ctrl &= ~REQ_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
|
||||
if (!wait_until_active())
|
||||
{
|
||||
ctrl |= REQ_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*cia_a_ddrb = 0x00;
|
||||
|
||||
ctrl ^= CLK_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
|
||||
int present = *cia_a_prb & 1;
|
||||
|
||||
ctrl |= REQ_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
|
||||
*cia_a_ddrb = 0xff;
|
||||
|
||||
return present;
|
||||
}
|
||||
|
||||
void spi_set_speed(long speed)
|
||||
{
|
||||
wait_until_idle();
|
||||
*cia_a_prb = speed == SPI_SPEED_FAST ? 0xc5 : 0xc4;
|
||||
|
||||
*cia_a_ddrb = 0xff;
|
||||
*cia_a_prb = speed == SPI_SPEED_FAST ? 0xc1 : 0xc0;
|
||||
*cia_b_pra ^= CLOCK_MASK;
|
||||
*cia_a_ddrb = 0;
|
||||
UBYTE prev = *cia_b_pra;
|
||||
*cia_b_pra = prev & ~REQ_MASK;
|
||||
|
||||
wait_until_active();
|
||||
|
||||
*cia_b_pra = prev;
|
||||
|
||||
current_speed = speed;
|
||||
}
|
||||
|
||||
void spi_select(void)
|
||||
{
|
||||
*cia_b_pra &= ~CS_MASK;
|
||||
}
|
||||
|
||||
void spi_deselect(void)
|
||||
{
|
||||
*cia_b_pra |= CS_MASK;
|
||||
}
|
||||
|
||||
// A slow SPI transfer takes 32 us (8 bits times 4us (250kHz)).
|
||||
// An E-cycle is 1.4 us.
|
||||
static void wait_40_us()
|
||||
@ -90,29 +132,29 @@ static void wait_40_us()
|
||||
|
||||
static void spi_write_slow(__reg("a0") const UBYTE *buf, __reg("d0") ULONG size)
|
||||
{
|
||||
wait_until_idle();
|
||||
|
||||
*cia_a_ddrb = 0xff;
|
||||
|
||||
UBYTE ctrl = *cia_b_pra;
|
||||
|
||||
if (size <= 64) // WRITE1: 00xxxxxx
|
||||
{
|
||||
*cia_a_prb = (size - 1) & 0x3f;
|
||||
|
||||
ctrl ^= CLOCK_MASK;
|
||||
ctrl &= ~REQ_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
|
||||
wait_until_active();
|
||||
}
|
||||
else // WRITE2: 100xxxxx xxxxxxxx
|
||||
else // WRITE2: 10xxxxxx 0xxxxxxx
|
||||
{
|
||||
*cia_a_prb = 0x80 | (((size - 1) >> 8) & 0x1f);
|
||||
*cia_a_prb = 0x80 | (((size - 1) >> 7) & 0x3f);
|
||||
|
||||
ctrl ^= CLOCK_MASK;
|
||||
ctrl &= ~REQ_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
|
||||
*cia_a_prb = (size - 1) & 0xff;
|
||||
wait_until_active();
|
||||
|
||||
ctrl ^= CLOCK_MASK;
|
||||
*cia_a_prb = (size - 1) & 0x7f;
|
||||
|
||||
ctrl ^= CLK_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
}
|
||||
|
||||
@ -120,40 +162,41 @@ static void spi_write_slow(__reg("a0") const UBYTE *buf, __reg("d0") ULONG size)
|
||||
{
|
||||
*cia_a_prb = *buf++;
|
||||
|
||||
ctrl ^= CLOCK_MASK;
|
||||
ctrl ^= CLK_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
|
||||
wait_40_us();
|
||||
}
|
||||
|
||||
*cia_a_ddrb = 0;
|
||||
ctrl |= REQ_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
}
|
||||
|
||||
static void spi_read_slow(__reg("a0") UBYTE *buf, __reg("d0") ULONG size)
|
||||
{
|
||||
wait_until_idle();
|
||||
|
||||
*cia_a_ddrb = 0xff;
|
||||
|
||||
UBYTE ctrl = *cia_b_pra;
|
||||
|
||||
if (size <= 64) // READ1: 01xxxxxx
|
||||
{
|
||||
*cia_a_prb = 0x40 | ((size - 1) & 0x3f);
|
||||
|
||||
ctrl ^= CLOCK_MASK;
|
||||
ctrl &= ~REQ_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
|
||||
wait_until_active();
|
||||
}
|
||||
else // READ2: 101xxxxx xxxxxxxx
|
||||
else // READ2: 10xxxxxx 1xxxxxxx
|
||||
{
|
||||
*cia_a_prb = 0xa0 | (((size - 1) >> 8) & 0x1f);
|
||||
*cia_a_prb = 0x80 | (((size - 1) >> 7) & 0x3f);
|
||||
|
||||
ctrl ^= CLOCK_MASK;
|
||||
ctrl &= ~REQ_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
|
||||
*cia_a_prb = (size - 1) & 0xff;
|
||||
wait_until_active();
|
||||
|
||||
ctrl ^= CLOCK_MASK;
|
||||
*cia_a_prb = 0x80 | ((size - 1) & 0x7f);
|
||||
|
||||
ctrl ^= CLK_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
}
|
||||
|
||||
@ -163,18 +206,17 @@ static void spi_read_slow(__reg("a0") UBYTE *buf, __reg("d0") ULONG size)
|
||||
{
|
||||
wait_40_us();
|
||||
|
||||
ctrl ^= CLOCK_MASK;
|
||||
ctrl ^= CLK_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
|
||||
*buf++ = *cia_a_prb;
|
||||
}
|
||||
|
||||
ctrl ^= CLOCK_MASK;
|
||||
ctrl |= REQ_MASK;
|
||||
*cia_b_pra = ctrl;
|
||||
}
|
||||
|
||||
extern void spi_read_fast(__reg("a0") UBYTE *buf, __reg("d0") ULONG size);
|
||||
extern void spi_write_fast(__reg("a0") const UBYTE *buf, __reg("d0") ULONG size);
|
||||
*cia_a_ddrb = 0xff;
|
||||
}
|
||||
|
||||
void spi_read(__reg("a0") UBYTE *buf, __reg("d0") ULONG size)
|
||||
{
|
||||
@ -191,3 +233,95 @@ void spi_write(__reg("a0") const UBYTE *buf, __reg("d0") ULONG size)
|
||||
else
|
||||
spi_write_slow(buf, size);
|
||||
}
|
||||
|
||||
int spi_initialize(void (*change_isr)())
|
||||
{
|
||||
int success = 0;
|
||||
|
||||
miscbase = (struct Library *)OpenResource(MISCNAME);
|
||||
if (!miscbase)
|
||||
{
|
||||
success = -1;
|
||||
goto fail_out1;
|
||||
}
|
||||
|
||||
ciaabase = (struct Library *)OpenResource(CIAANAME);
|
||||
if (!ciaabase)
|
||||
{
|
||||
success = -2;
|
||||
goto fail_out1;
|
||||
}
|
||||
|
||||
if (AllocMiscResource(miscbase, MR_PARALLELPORT, spi_lib_name))
|
||||
{
|
||||
success = -3;
|
||||
goto fail_out1;
|
||||
}
|
||||
|
||||
if (AllocMiscResource(miscbase, MR_PARALLELBITS, spi_lib_name))
|
||||
{
|
||||
success = -4;
|
||||
goto fail_out2;
|
||||
}
|
||||
|
||||
flag_interrupt.is_Node.ln_Name = (char *)spi_lib_name;
|
||||
flag_interrupt.is_Node.ln_Type = NT_INTERRUPT;
|
||||
flag_interrupt.is_Code = change_isr;
|
||||
|
||||
Disable();
|
||||
if (AddICRVector(ciaabase, CIAICRB_FLG, &flag_interrupt))
|
||||
{
|
||||
Enable();
|
||||
success = -5;
|
||||
goto fail_out3;
|
||||
}
|
||||
|
||||
AbleICR(ciaabase, CIAICRF_FLG);
|
||||
SetICR(ciaabase, CIAICRF_FLG);
|
||||
Enable();
|
||||
|
||||
*cia_b_pra = (*cia_b_pra & ~ACT_MASK) | (REQ_MASK | CLK_MASK);
|
||||
*cia_b_ddra = (*cia_b_ddra & ~ACT_MASK) | (REQ_MASK | CLK_MASK);
|
||||
|
||||
*cia_a_prb = 0xff;
|
||||
*cia_a_ddrb = 0xff;
|
||||
|
||||
int card_present = spi_get_card_present();
|
||||
if (card_present < 0)
|
||||
{
|
||||
success = -6;
|
||||
goto fail_out4;
|
||||
}
|
||||
|
||||
AbleICR(ciaabase, CIAICRF_SETCLR | CIAICRF_FLG);
|
||||
|
||||
return card_present;
|
||||
|
||||
fail_out4:
|
||||
*cia_b_ddra &= ~(ACT_MASK | REQ_MASK | CLK_MASK);
|
||||
*cia_a_ddrb = 0;
|
||||
|
||||
RemICRVector(ciaabase, CIAICRB_FLG, &flag_interrupt);
|
||||
|
||||
fail_out3:
|
||||
FreeMiscResource(miscbase, MR_PARALLELBITS);
|
||||
|
||||
fail_out2:
|
||||
FreeMiscResource(miscbase, MR_PARALLELPORT);
|
||||
|
||||
fail_out1:
|
||||
return success;
|
||||
}
|
||||
|
||||
void spi_shutdown()
|
||||
{
|
||||
AbleICR(ciaabase, CIAICRF_FLG);
|
||||
|
||||
*cia_b_ddra &= ~(ACT_MASK | REQ_MASK | CLK_MASK);
|
||||
*cia_a_ddrb = 0;
|
||||
|
||||
RemICRVector(ciaabase, CIAICRB_FLG, &flag_interrupt);
|
||||
|
||||
FreeMiscResource(miscbase, MR_PARALLELBITS);
|
||||
FreeMiscResource(miscbase, MR_PARALLELPORT);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Written in the end of April 2020 by Niklas Ekström.
|
||||
* Updated in July 2021 by Niklas Ekström to handle Card Present signal.
|
||||
*/
|
||||
#ifndef SPI_H_
|
||||
#define SPI_H_
|
||||
@ -7,7 +8,8 @@
|
||||
#define SPI_SPEED_SLOW 0
|
||||
#define SPI_SPEED_FAST 1
|
||||
|
||||
void spi_initialize();
|
||||
int spi_initialize(void (*change_isr)());
|
||||
int spi_get_card_present();
|
||||
void spi_shutdown();
|
||||
void spi_set_speed(long speed);
|
||||
void spi_select();
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
; Written in the end of April 2020 by Niklas Ekström.
|
||||
; Updated in July 2021 by Niklas Ekström to handle Card Present signal.
|
||||
|
||||
XDEF _spi_read_fast
|
||||
XDEF _spi_write_fast
|
||||
@ -16,12 +17,9 @@ CIAPRB equ $0100
|
||||
CIADDRA equ $0200
|
||||
CIADDRB equ $0300
|
||||
|
||||
Disable equ -120
|
||||
Enable equ -126
|
||||
|
||||
CS_BIT equ CIAB_PRTRSEL
|
||||
CLOCK_BIT equ CIAB_PRTRPOUT
|
||||
IDLE_BIT equ CIAB_PRTRBUSY
|
||||
REQ_BIT equ CIAB_PRTRSEL
|
||||
CLK_BIT equ CIAB_PRTRPOUT
|
||||
ACT_BIT equ CIAB_PRTRBUSY
|
||||
|
||||
; a0 = unsigned char *buf
|
||||
; d0 = unsigned int size
|
||||
@ -32,53 +30,53 @@ _spi_write_fast:
|
||||
bne.b .not_zero
|
||||
rts
|
||||
.not_zero:
|
||||
movem.l d2/a5-a6,-(a7)
|
||||
move.l 4.w,a6
|
||||
jsr Disable(a6)
|
||||
movem.l d2/a5,-(a7)
|
||||
|
||||
lea.l CIAA_BASE+CIAPRB,a1 ; Data
|
||||
lea.l CIAB_BASE+CIAPRA,a5 ; Control pins
|
||||
|
||||
.idle_wait: move.b (a5),d2
|
||||
btst #IDLE_BIT,d2
|
||||
beq.b .is_idle
|
||||
|
||||
bchg #CLOCK_BIT,d2
|
||||
move.b d2,(a5)
|
||||
bra.b .idle_wait
|
||||
|
||||
.is_idle: move.b #$ff,$200(a1) ; Start driving data pins
|
||||
move.b (a5),d2
|
||||
|
||||
subq #1,d0 ; d0 = size - 1
|
||||
|
||||
cmp #63,d0
|
||||
ble.b .one_byte_cmd
|
||||
|
||||
; WRITE2 = 100xxxxx xxxxxxxx
|
||||
; WRITE2 = 10xxxxxx 0xxxxxxx
|
||||
move d0,d1
|
||||
lsr #8,d1
|
||||
lsr #7,d1
|
||||
or.b #$80,d1
|
||||
move.b d1,(a1)
|
||||
bchg #CLOCK_BIT,d2
|
||||
bclr #REQ_BIT,d2
|
||||
move.b d2,(a5)
|
||||
|
||||
move.b d0,(a1)
|
||||
bchg #CLOCK_BIT,d2
|
||||
.act_wait2: move.b (a5),d2
|
||||
btst #ACT_BIT,d2
|
||||
bne.b .act_wait2
|
||||
|
||||
move.b d0,d1
|
||||
and.b #$7f,d1
|
||||
move.b d1,(a1)
|
||||
bchg #CLK_BIT,d2
|
||||
move.b d2,(a5)
|
||||
bra.b .cmd_sent
|
||||
|
||||
.one_byte_cmd: ; WRITE1 = 00xxxxxx
|
||||
move.b d0,(a1)
|
||||
bchg #CLOCK_BIT,d2
|
||||
bclr #REQ_BIT,d2
|
||||
move.b d2,(a5)
|
||||
|
||||
.act_wait1: move.b (a5),d2
|
||||
btst #ACT_BIT,d2
|
||||
bne.b .act_wait1
|
||||
|
||||
.cmd_sent: addq #1,d0 ; d0 = size
|
||||
|
||||
btst #0,d0
|
||||
beq.b .even
|
||||
|
||||
move.b (a0)+,(a1)
|
||||
bchg #CLOCK_BIT,d2
|
||||
bchg #CLK_BIT,d2
|
||||
move.b d2,(a5)
|
||||
|
||||
.even: lsr #1,d0
|
||||
@ -86,7 +84,7 @@ _spi_write_fast:
|
||||
subq #1,d0
|
||||
|
||||
move.b d2,d1
|
||||
bchg #CLOCK_BIT,d1
|
||||
bchg #CLK_BIT,d1
|
||||
|
||||
.loop: move.b (a0)+,(a1)
|
||||
move.b d1,(a5)
|
||||
@ -94,10 +92,11 @@ _spi_write_fast:
|
||||
move.b d2,(a5)
|
||||
dbra d0,.loop
|
||||
|
||||
.done: move.b #0,$200(a1) ; Stop driving data pins
|
||||
.done: move.b d2,(a5) ; Delay to allow write to complete
|
||||
bset #REQ_BIT,d2
|
||||
move.b d2,(a5)
|
||||
|
||||
jsr Enable(a6)
|
||||
movem.l (a7)+,d2/a5-a6
|
||||
movem.l (a7)+,d2/a5
|
||||
rts
|
||||
|
||||
; a0 = unsigned char *buf
|
||||
@ -109,38 +108,34 @@ _spi_read_fast:
|
||||
bne.b .not_zero
|
||||
rts
|
||||
.not_zero:
|
||||
movem.l d2/a5-a6,-(a7)
|
||||
move.l 4.w,a6
|
||||
jsr Disable(a6)
|
||||
movem.l d2/a5,-(a7)
|
||||
|
||||
lea.l CIAA_BASE+CIAPRB,a1 ; Data
|
||||
lea.l CIAB_BASE+CIAPRA,a5 ; Control pins
|
||||
|
||||
.idle_wait: move.b (a5),d2
|
||||
btst #IDLE_BIT,d2
|
||||
beq.b .is_idle
|
||||
|
||||
bchg #CLOCK_BIT,d2
|
||||
move.b d2,(a5)
|
||||
bra.b .idle_wait
|
||||
|
||||
.is_idle: move.b #$ff,$200(a1) ; Start driving data pins
|
||||
move.b (a5),d2
|
||||
|
||||
subq #1,d0 ; d0 = size - 1
|
||||
|
||||
cmp #63,d0
|
||||
ble.b .one_byte_cmd
|
||||
|
||||
; READ2 = 101xxxxx xxxxxxxx
|
||||
; READ2 = 10xxxxxx 1xxxxxxx
|
||||
move d0,d1
|
||||
lsr #8,d1
|
||||
or.b #$a0,d1
|
||||
lsr #7,d1
|
||||
or.b #$80,d1
|
||||
move.b d1,(a1)
|
||||
bchg #CLOCK_BIT,d2
|
||||
bclr #REQ_BIT,d2
|
||||
move.b d2,(a5)
|
||||
|
||||
move.b d0,(a1)
|
||||
bchg #CLOCK_BIT,d2
|
||||
.act_wait2: move.b (a5),d2
|
||||
btst #ACT_BIT,d2
|
||||
bne.b .act_wait2
|
||||
|
||||
move.b d0,d1
|
||||
or.b #$80,d1
|
||||
move.b d1,(a1)
|
||||
bchg #CLK_BIT,d2
|
||||
move.b d2,(a5)
|
||||
bra.b .cmd_sent
|
||||
|
||||
@ -148,9 +143,13 @@ _spi_read_fast:
|
||||
move.b d0,d1
|
||||
or.b #$40,d1
|
||||
move.b d1,(a1)
|
||||
bchg #CLOCK_BIT,d2
|
||||
bclr #REQ_BIT,d2
|
||||
move.b d2,(a5)
|
||||
|
||||
.act_wait1: move.b (a5),d2
|
||||
btst #ACT_BIT,d2
|
||||
bne.b .act_wait1
|
||||
|
||||
.cmd_sent: move.b #0,$200(a1) ; Stop driving data pins
|
||||
|
||||
addq #1,d0 ; d0 = size
|
||||
@ -158,7 +157,7 @@ _spi_read_fast:
|
||||
btst #0,d0
|
||||
beq.b .even
|
||||
|
||||
bchg #CLOCK_BIT,d2
|
||||
bchg #CLK_BIT,d2
|
||||
move.b d2,(a5)
|
||||
move.b (a1),(a0)+
|
||||
|
||||
@ -167,7 +166,7 @@ _spi_read_fast:
|
||||
subq #1,d0
|
||||
|
||||
move.b d2,d1
|
||||
bchg #CLOCK_BIT,d1
|
||||
bchg #CLK_BIT,d1
|
||||
|
||||
.loop: move.b d1,(a5)
|
||||
move.b (a1),(a0)+
|
||||
@ -175,9 +174,10 @@ _spi_read_fast:
|
||||
move.b (a1),(a0)+
|
||||
dbra d0,.loop
|
||||
|
||||
.done: bchg #CLOCK_BIT,d2
|
||||
.done: bset #REQ_BIT,d2
|
||||
move.b d2,(a5)
|
||||
|
||||
jsr Enable(a6)
|
||||
movem.l (a7)+,d2/a5-a6
|
||||
move.b #$ff,$200(a1) ; Start driving data pins
|
||||
|
||||
movem.l (a7)+,d2/a5
|
||||
rts
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user