-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscsiPhy.cpp
300 lines (254 loc) · 7.19 KB
/
scsiPhy.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// Implements the low level interface to SCSI bus
// Partially derived from scsiPhy.c from SCSI2SD-V6
#include "scsiPhy.h"
#include "ZuluSCSI_platform.h"
#include "ZuluSCSI_log.h"
#include "ZuluSCSI_log_trace.h"
#include "ZuluSCSI_config.h"
#include <scsi2sd.h>
extern "C" {
#include <scsi.h>
#include <scsi2sd_time.h>
}
/***********************/
/* SCSI status signals */
/***********************/
extern "C" bool scsiStatusATN()
{
return SCSI_IN(ATN);
}
extern "C" bool scsiStatusBSY()
{
return SCSI_IN(BSY);
}
/************************/
/* SCSI selection logic */
/************************/
volatile uint8_t g_scsi_sts_selection;
volatile uint8_t g_scsi_ctrl_bsy;
void scsi_bsy_deassert_interrupt()
{
if (SCSI_IN(SEL) && !SCSI_IN(BSY))
{
// Check if any of the targets we simulate is selected
uint8_t sel_bits = SCSI_IN_DATA();
int sel_id = -1;
for (int i = 0; i < S2S_MAX_TARGETS; i++)
{
if (scsiDev.targets[i].targetId <= 7 && scsiDev.targets[i].cfg)
{
if (sel_bits & (1 << scsiDev.targets[i].targetId))
{
sel_id = scsiDev.targets[i].targetId;
break;
}
}
}
if (sel_id >= 0)
{
uint8_t atn_flag = SCSI_IN(ATN) ? SCSI_STS_SELECTION_ATN : 0;
g_scsi_sts_selection = SCSI_STS_SELECTION_SUCCEEDED | atn_flag | sel_id;
}
// selFlag is required for Philips P2000C which releases it after 600ns
// without waiting for BSY.
// Also required for some early Mac Plus roms
scsiDev.selFlag = *SCSI_STS_SELECTED;
}
}
extern "C" bool scsiStatusSEL()
{
if (g_scsi_ctrl_bsy)
{
// We don't have direct register access to BSY bit like SCSI2SD scsi.c expects.
// Instead update the state here.
// Releasing happens with bus release.
g_scsi_ctrl_bsy = 0;
SCSI_OUT(BSY, 1);
}
return SCSI_IN(SEL);
}
/************************/
/* SCSI bus reset logic */
/************************/
static void scsi_rst_assert_interrupt()
{
// Glitch filtering
bool rst1 = SCSI_IN(RST);
delay_ns(500);
bool rst2 = SCSI_IN(RST);
if (rst1 && rst2)
{
dbgmsg("BUS RESET");
scsiDev.resetFlag = 1;
}
}
// This function is called to initialize the phy code.
// It is called after power-on and after SCSI bus reset.
extern "C" void scsiPhyReset(void)
{
SCSI_RELEASE_OUTPUTS();
g_scsi_sts_selection = 0;
g_scsi_ctrl_bsy = 0;
/* Implement here code to enable two interrupts:
* scsi_bsy_deassert_interrupt() on rising edge of BSY pin
* scsi_rst_assert_interrupt() on falling edge of RST pin
*
* For SCSI-1 single-initiator support, also call:
* scsi_bsy_deassert_interrupt() on falling edge of SEL pin
*/
}
/************************/
/* SCSI bus phase logic */
/************************/
static SCSI_PHASE g_scsi_phase;
extern "C" void scsiEnterPhase(int phase)
{
int delay = scsiEnterPhaseImmediate(phase);
if (delay > 0)
{
s2s_delay_ns(delay);
}
}
// Change state and return nanosecond delay to wait
extern "C" uint32_t scsiEnterPhaseImmediate(int phase)
{
if (phase != g_scsi_phase)
{
// ANSI INCITS 362-2002 SPI-3 10.7.1:
// Phase changes are not allowed while REQ or ACK is asserted.
while (likely(!scsiDev.resetFlag) && SCSI_IN(ACK)) {}
if (scsiDev.compatMode < COMPAT_SCSI2 && (phase == DATA_IN || phase == DATA_OUT))
{
// Akai S1000/S3000 seems to need extra delay before changing to data phase
// after a command. The code in ZuluSCSI_disk.cpp tries to do this while waiting
// for SD card, to avoid any extra latency.
s2s_delay_ns(400000);
}
int oldphase = g_scsi_phase;
g_scsi_phase = (SCSI_PHASE)phase;
scsiLogPhaseChange(phase);
if (phase < 0)
{
// Other communication on bus or reset state
SCSI_RELEASE_OUTPUTS();
return 0;
}
else
{
SCSI_OUT(MSG, phase & __scsiphase_msg);
SCSI_OUT(CD, phase & __scsiphase_cd);
SCSI_OUT(IO, phase & __scsiphase_io);
int delayNs = 400; // Bus settle delay
if ((oldphase & __scsiphase_io) != (phase & __scsiphase_io))
{
delayNs += 400; // Data release delay
}
if (scsiDev.compatMode < COMPAT_SCSI2)
{
// EMU EMAX needs 100uS ! 10uS is not enough.
delayNs += 100000;
}
return delayNs;
}
}
else
{
return 0;
}
}
// Release all signals
void scsiEnterBusFree(void)
{
g_scsi_phase = BUS_FREE;
g_scsi_sts_selection = 0;
g_scsi_ctrl_bsy = 0;
scsiDev.cdbLen = 0;
SCSI_RELEASE_OUTPUTS();
}
/********************/
/* Transmit to host */
/********************/
#define SCSI_WAIT_ACTIVE(pin) \
if (!SCSI_IN(pin)) { \
if (!SCSI_IN(pin)) { \
while(!SCSI_IN(pin) && !scsiDev.resetFlag); \
} \
}
#define SCSI_WAIT_INACTIVE(pin) \
if (SCSI_IN(pin)) { \
if (SCSI_IN(pin)) { \
while(SCSI_IN(pin) && !scsiDev.resetFlag); \
} \
}
// Write one byte to SCSI host using the handshake mechanism
static inline void scsiWriteOneByte(uint8_t value)
{
SCSI_OUT_DATA(value);
delay_100ns(); // DB setup time before REQ
SCSI_OUT(REQ, 1);
SCSI_WAIT_ACTIVE(ACK);
SCSI_RELEASE_DATA_REQ();
SCSI_WAIT_INACTIVE(ACK);
}
extern "C" void scsiWriteByte(uint8_t value)
{
scsiLogDataIn(&value, 1);
scsiWriteOneByte(value);
}
extern "C" void scsiWrite(const uint8_t* data, uint32_t count)
{
scsiLogDataIn(data, count);
for (uint32_t i = 0; i < count; i++)
{
if (scsiDev.resetFlag) break;
scsiWriteOneByte(data[i]);
}
}
extern "C" void scsiStartWrite(const uint8_t* data, uint32_t count)
{
// If the platform supports DMA for either SD card access or for SCSI bus,
// this function can be used to execute SD card transfers in parallel with
// SCSI transfers. This usually doubles the transfer speed.
//
// For simplicity, this example only implements blocking writes.
scsiWrite(data, count);
}
extern "C" bool scsiIsWriteFinished(const uint8_t *data)
{
// Asynchronous writes are not implemented in this example.
return true;
}
extern "C" void scsiFinishWrite()
{
// Asynchronous writes are not implemented in this example.
}
/*********************/
/* Receive from host */
/*********************/
// Read one byte from SCSI host using the handshake mechanism.
static inline uint8_t scsiReadOneByte(void)
{
SCSI_OUT(REQ, 1);
SCSI_WAIT_ACTIVE(ACK);
delay_100ns();
uint8_t r = SCSI_IN_DATA();
SCSI_OUT(REQ, 0);
SCSI_WAIT_INACTIVE(ACK);
return r;
}
extern "C" uint8_t scsiReadByte(void)
{
uint8_t r = scsiReadOneByte();
scsiLogDataOut(&r, 1);
return r;
}
extern "C" void scsiRead(uint8_t* data, uint32_t count, int* parityError)
{
*parityError = 0;
for (uint32_t i = 0; i < count; i++)
{
if (scsiDev.resetFlag) break;
data[i] = scsiReadOneByte();
}
scsiLogDataOut(data, count);
}