mirror of
https://github.com/LIV2/amiga-par-to-spi-adapter.git
synced 2025-12-06 06:32:46 +00:00
Add support for eject/insert sd card
This commit is contained in:
parent
285569663b
commit
e766d41e37
330
avr/main.c
330
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 D8 PB0 ACT Indicate command running
|
||||
// 12 POUT D4 PD4 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.
|
||||
#define ACT_BIT_n 0 // Output, active low.
|
||||
|
||||
// These bits are in port D.
|
||||
#define IDLE_BIT 4
|
||||
#define CLOCK_BIT 5
|
||||
// Pins in port D.
|
||||
#define CLK_BIT 4 // Input.
|
||||
#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;
|
||||
}
|
||||
PORTB &= ~(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;
|
||||
|
||||
PORTB &= ~(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;
|
||||
PORTB &= ~(1 << ACT_BIT_n);
|
||||
}
|
||||
else if (cmd == 1) // CARD_PRESENT
|
||||
{
|
||||
DDRB &= ~(1 << IRQ_BIT_n);
|
||||
PORTB &= ~(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;
|
||||
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);
|
||||
|
||||
PORTB &= ~(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;
|
||||
|
||||
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,81 @@ 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))
|
||||
{
|
||||
PORTB |= (1 << ACT_BIT_n);
|
||||
|
||||
DDRD = 0;
|
||||
PORTD = (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) | (1 << ACT_BIT_n);
|
||||
PORTB = (1 << SS_BIT_n) | (1 << ACT_BIT_n);
|
||||
|
||||
// SPI enabled, master, fosc/64 = 250 kHz
|
||||
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
|
||||
SPSR |= (1 << SPI2X);
|
||||
|
||||
DDRD = 0;
|
||||
PORTD = (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.asm device.c sd.c timer.c ../../spi-lib/spi.c ../../spi-lib/spi_low.asm -I../../spi-lib -Os -nostdlib -lamiga -o spisd.device
|
||||
|
||||
@ -1,253 +1,383 @@
|
||||
/*
|
||||
* 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 <proto/exec.h>
|
||||
|
||||
#include "sd.h"
|
||||
#include "spi.h"
|
||||
|
||||
#define TASK_STACK_SIZE 2048
|
||||
#define TASK_PRIORITY 10
|
||||
|
||||
#define DEBOUNCE_TIMEOUT_US 100000
|
||||
|
||||
#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)
|
||||
|
||||
#ifndef TD_GETGEOMETRY
|
||||
// Needed to compile with AmigaOS 1.3 headers.
|
||||
#define TD_GETGEOMETRY (CMD_NONSTD+13)
|
||||
|
||||
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;
|
||||
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
|
||||
|
||||
char device_name[] = "spisd.device";
|
||||
char id_string[] = "spisd 0.3 (3 May 2020)";
|
||||
|
||||
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)
|
||||
char device_name[] = "spisd.device";
|
||||
char id_string[] = "spisd 2.0 (19 July 2021)";
|
||||
|
||||
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; //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;
|
||||
}
|
||||
|
||||
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 void process_request(struct IOStdReq *ior)
|
||||
{
|
||||
ior->io_Error = IOERR_OPENFAIL;
|
||||
ior->io_Message.mn_Node.ln_Type = NT_REPLYMSG;
|
||||
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;
|
||||
|
||||
if (unitnum != 0)
|
||||
return;
|
||||
case TD_FORMAT:
|
||||
case CMD_WRITE:
|
||||
if (sd_write((uint8_t *)ior->io_Data, ior->io_Offset >> SD_SECTOR_SHIFT, ior->io_Length >> SD_SECTOR_SHIFT) == 0)
|
||||
ior->io_Actual = ior->io_Length;
|
||||
else
|
||||
ior->io_Error = TDERR_NotSpecified;
|
||||
break;
|
||||
|
||||
if (!is_open)
|
||||
{
|
||||
spi_initialize();
|
||||
if (sd_open() != 0)
|
||||
return;
|
||||
is_open = TRUE;
|
||||
}
|
||||
case CMD_READ:
|
||||
if (sd_read((uint8_t *)ior->io_Data, ior->io_Offset >> SD_SECTOR_SHIFT, ior->io_Length >> SD_SECTOR_SHIFT) == 0)
|
||||
ior->io_Actual = ior->io_Length;
|
||||
else
|
||||
ior->io_Error = TDERR_NotSpecified;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dev->lib_OpenCnt++;
|
||||
ior->io_Error = 0;
|
||||
ReplyMsg(&ior->io_Message);
|
||||
}
|
||||
|
||||
static BPTR close(__reg("a6") struct Library *dev, __reg("a1") struct IORequest *ior)
|
||||
static void task_run()
|
||||
{
|
||||
ior->io_Device = NULL;
|
||||
ior->io_Unit = NULL;
|
||||
if (card_present && sd_open() == 0)
|
||||
card_opened = TRUE;
|
||||
|
||||
dev->lib_OpenCnt--;
|
||||
while (1)
|
||||
{
|
||||
ULONG sigs = Wait(SIGF_CARD_CHANGE | SIGF_OP_REQUEST);
|
||||
|
||||
if (dev->lib_OpenCnt == 0 && (dev->lib_Flags & LIBF_DELEXP))
|
||||
return expunge(dev);
|
||||
if (sigs & SIGF_CARD_CHANGE)
|
||||
handle_changed();
|
||||
|
||||
return 0;
|
||||
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 uint32_t device_get_geometry(struct IOStdReq *iostd)
|
||||
static void change_isr()
|
||||
{
|
||||
struct DriveGeometry *geom = (struct DriveGeometry*)iostd->io_Data;
|
||||
const sd_card_info_t *ci = sd_get_card_info();
|
||||
|
||||
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;
|
||||
}
|
||||
Signal(task, SIGF_CARD_CHANGE);
|
||||
}
|
||||
|
||||
static void begin_io(__reg("a6") struct Library *dev, __reg("a1") struct IORequest *ioreq)
|
||||
static void begin_io(__reg("a6") struct Library *dev, __reg("a1") struct IOStdReq *ior)
|
||||
{
|
||||
struct IOStdReq *iostd = (struct IOStdReq*)ioreq;
|
||||
if (!ior)
|
||||
return;
|
||||
|
||||
if (ioreq == NULL) {
|
||||
return;
|
||||
}
|
||||
ior->io_Error = 0;
|
||||
ior->io_Actual = 0;
|
||||
|
||||
iostd->io_Error = 0;
|
||||
switch (ior->io_Command)
|
||||
{
|
||||
case CMD_RESET:
|
||||
case CMD_CLEAR:
|
||||
case CMD_UPDATE:
|
||||
case TD_MOTOR:
|
||||
case TD_PROTSTATUS:
|
||||
break;
|
||||
|
||||
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
|
||||
case TD_CHANGESTATE:
|
||||
ior->io_Actual = card_present ? 0 : 1;
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
case TD_CHANGENUM:
|
||||
ior->io_Actual = card_change_num;
|
||||
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_GETDRIVETYPE:
|
||||
ior->io_Actual = 0; //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 TD_GETGEOMETRY:
|
||||
case TD_FORMAT:
|
||||
case CMD_WRITE:
|
||||
case CMD_READ:
|
||||
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 = 2;
|
||||
dev->lib_Revision = 0;
|
||||
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,
|
||||
};
|
||||
|
||||
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,10 @@ _spi_write_fast:
|
||||
move.b d2,(a5)
|
||||
dbra d0,.loop
|
||||
|
||||
.done: move.b #0,$200(a1) ; Stop driving data pins
|
||||
.done: 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 +107,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 +142,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 +156,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 +165,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 +173,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