-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdisk.cpp
More file actions
242 lines (209 loc) · 6.19 KB
/
disk.cpp
File metadata and controls
242 lines (209 loc) · 6.19 KB
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
#include <unistd.h>
#include <stdint.h>
#include <pgmspace.h>
#include <machine.h>
#include <memory.h>
#include <CPU.h>
#include <prom.h>
#include <debugging.h>
#include <serialio.h>
#include <filer.h>
#include <flash_filer.h>
#include "config.h"
#include "softswitches.h"
#include "disk.h"
// geometry
#define BYTES_PER_SECTOR 256
#define SECTORS_PER_TRACK 16
#define MAX_TRACK 35
#define CMD_SEEK 0
#define CMD_READ 1
#define CMD_WRITE 2
#define CMD_FORMAT 4
#define NO_ERROR 0x00
#define WRITE_PROTECT 0x10
#define VOLUME_ERROR 0x20
#define DRIVE_ERROR 0x40
#define READ_ERROR 0x80
// see https://gswv.apple2.org.za/a2zine/GS.WorldView/Resources/DOS.3.3.ANATOMY/BOOT.PROCESS.txt
// https://htmlpreview.github.io/?https://github.com/Michaelangel007/apple2_dos33/blob/master/dos33.html
// zpage locations
#define TRACK 0x41
#define SECTOR 0x3d
#define DATAPTR 0x26
#define IOBP 0x48
#define SLOTIDX 0x2b
// the disk is accessed for the first time with PR #6 "permanent redirect to slot #6"
static const uint8_t diskboot[] PROGMEM = {
// .org $c600
// https://6502disassembly.com/a2-rom/C600ROM.html
0xa2, 0x20, // ldx #$20
0xa0, 0x00, // ldy #$00
0xa2, 0x03, // ldx #$03
// CreateDecTabLoop
0x86, 0x3c, // stx bits
0x8a, // txa
0x0a, // asl A
0x24, 0x3c, // bit bits
0xf0, 0x10, // beq :reject
0x05, 0x3c, // ora bits
0x49, 0xff, // eor #$ff
0x29, 0x7e, // and #$7e
// :check_dub0
0xb0, 0x08, // bcs :reject
0x4a, // lsr A
0xd0, 0xfb, // bne :check_dub0
0x98, // tya
0x9d, 0x56, 0x03, // sta CONV_TAB,x
0xc8, // iny
// :reject
0xe8, // inx
0x10, 0xe5, // bpl CreateDecTabLoop
0x20, 0x58, 0xff, // jsr MON_IORTS
0xba, // tsx
0xbd, 0x00, 0x01, // lda STACK,x
0x0a, // asl A
0x0a, // asl A
0x0a, // asl A
0x0a, // asl A
0x85, SLOTIDX, // sta slot_index
0xaa, // tax
// replaced with NOPs (to avoid hitting soft-switches)
0xea, 0xea, 0xea, // lda IWM_Q7_OFF,x
0xea, 0xea, 0xea, // lda IWM_Q6_OFF,x
0xea, 0xea, 0xea, // lda IWM_SEL_DRIVE_1,x
0xea, 0xea, 0xea, // lda IWM_MOTOR_ON,x
// "Blind-seek to track 0."
0xea, 0xea, // replaced with NOPs
// :seek_loop
0xea, 0xea, 0xea,
0xea,
0xea, 0xea,
0xea,
0xea, 0xea,
0xea,
0xea, 0xea, 0xea,
0xea, 0xea,
0xea, 0xea, 0xea,
0xea,
0xa9, 0x00, // lda #$00 (was bpl :seek_loop)
0x85, DATAPTR, // sta data_ptr
0x85, SECTOR, // sta sector
0x85, TRACK, // sta track
0xa9, 0x08, // lda #>BOOT1
0x85, DATAPTR+1, // sta data_ptr+1
// .org $c65c ReadSector (don't change: this is called from BOOT1)
// :another
0xbd, 0x80, 0xc0, // lda $c080,x
0xd0, 0x10, // bne :abort
0xe6, DATAPTR+1, // inc data_ptr+1
0xe6, SECTOR, // inc sector
0xa5, SECTOR, // lda sector
0xcd, 0x00, 0x08, // cmp BOOT1
0xa6, SLOTIDX, // ldx slot_index
0x90, 0xf3, // bcc :another
0x4c, 0x01, 0x08, // jmp BOOT1+1
// :abort
0x4c, 0x00, 0xe0, // jmp $e000
};
DiskII::DiskII(Memory &memory, flash_file &drive1, flash_file &drive2):
bootprom(diskboot, sizeof(diskboot)), _memory(memory)
{
_drives[0] = &drive1;
_drives[1] = &drive2;
}
void DiskII::seek(flash_file *drive, uint8_t trk, uint8_t sec) {
drive->seek(BYTES_PER_SECTOR * (sec + trk * SECTORS_PER_TRACK));
}
uint16_t DiskII::read(flash_file *drive, Memory::address addr, uint16_t bytes) {
uint16_t i;
for (i = 0; i < bytes && drive->more(); i++)
_memory[addr++] = drive->read();
return i;
}
uint16_t DiskII::write(flash_file *drive, Memory::address addr, uint16_t bytes) {
uint16_t i;
for (i = 0; i < bytes; i++)
drive->write(_memory[addr++]);
return i;
}
static const uint8_t reverse_sector_map[] = {
0, 7, 14, 6, 13, 5, 12, 4,
11, 3, 10, 2, 9, 1, 8, 15
};
uint8_t DiskII::boot1() {
uint8_t sector = reverse_sector_map[_memory[SECTOR]];
uint8_t track = _memory[TRACK];
Memory::address data_ptr = _memory[DATAPTR] | (_memory[DATAPTR+1] << 8);
DBG_DISK("boot1: (%d) %02x %02x %04x", _boot, track, sector, data_ptr);
flash_file *drive = _drives[0];
if (!*drive)
return 0x01;
seek(drive, track, sector);
read(drive, data_ptr, BYTES_PER_SECTOR);
_boot++;
if (_boot == 11) {
// now we've read the first sector (BOOT0) _and_
// the first 10 sectors (BOOT1). from now on
// DOS will call RWTS (at $3d00), so patch that.
//
// RWTS begins with this, so keep it:
// 3D00:84 48 STY IOBPL ;UPON ENTRY, A&Y POINT AT THE
// 3D02:85 49 STA IOBPH ;I/O CONTROL BLOCK (IOB)
//
_memory[0x3d04] = 0xad; // lda $c0n1 (soft-switch #1)
_memory[0x3d05] = 0x81 + 16*DISKII_SLOT;
_memory[0x3d06] = 0xc0;
_memory[0x3d07] = 0x18; // clc (= success)
_memory[0x3d08] = 0x60; // rts
}
return 0x00;
}
uint8_t DiskII::boot2(Memory::address rwts) {
Memory::address iobp = _memory[IOBP] | (_memory[IOBP+1] << 8);
uint8_t drive_id = _memory[iobp + 0x02] - 1;
uint8_t vol = _memory[iobp + 0x03];
uint8_t track = _memory[iobp + 0x04];
uint8_t sector = _memory[iobp + 0x05];
uint8_t cmd = _memory[iobp + 0x0c];
Memory::address buf = _memory[iobp + 8] | (_memory[iobp + 9] << 8);
DBG_DISK("boot2: cmd=%d drive=%d vol=%d %02x %02x %04x", cmd, drive_id, vol, track, sector, buf);
flash_file *drive = _drives[drive_id];
if (!*drive) {
_memory[iobp + 0x0d] = DRIVE_ERROR;
_memory[rwts + 0x07] = 0x38; // sec (= error)
return 0x01;
}
if (vol != 0 && vol != _vols[drive_id]) {
_memory[iobp + 0x0d] = VOLUME_ERROR;
_memory[iobp + 0x0e] = _vols[drive_id];
_memory[rwts + 0x07] = 0x38; // sec (= error)
return 0x01;
}
seek(drive, track, sector);
if (cmd == CMD_READ) {
read(drive, buf, BYTES_PER_SECTOR);
if (track == 17 && sector == 0)
_vols[drive_id] = _memory[buf + 6];
} else if (cmd == CMD_WRITE) {
if (track == 17 && sector == 0)
_vols[drive_id] = _memory[buf + 6];
write(drive, buf, BYTES_PER_SECTOR);
} else if (cmd != CMD_SEEK && cmd != CMD_FORMAT)
DBG_DISK("unhandled command %d", cmd);
_memory[iobp + 0x0d] = NO_ERROR;
_memory[iobp + 0x0e] = _vols[drive_id];
_memory[rwts + 0x07] = 0x18; // clc (= success)
return 0x00;
}
DiskII::Switches::operator uint8_t() {
switch (_acc & 0x0f) {
case 0x00:
return _disk.boot1();
case 0x01:
// $3d00 is RWTS in BOOT2, $bd00 is same, after relocation
return _disk.boot2(_cpu.pc() & 0xff00);
}
DBG_DISK("unhandled switch %04x", _acc);
return 0x01; // error
}