├── ArduinoFile-Mega-PCB-v1.0.zip
├── ArduinoFile-Mega-Schematic-v1.0.pdf
├── README.md
├── SDTemplate
├── profile.image
└── rescue
│ ├── selector.3.5inch.dc42
│ ├── selector.image
│ └── selector.twiggy.dc42
├── proFileEmulator.ino
└── proFileTester.ino
/ArduinoFile-Mega-PCB-v1.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexthecat123/ArduinoFile/67fb143f26d29ebab256e298651cc72e0090b837/ArduinoFile-Mega-PCB-v1.0.zip
--------------------------------------------------------------------------------
/ArduinoFile-Mega-Schematic-v1.0.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexthecat123/ArduinoFile/67fb143f26d29ebab256e298651cc72e0090b837/ArduinoFile-Mega-Schematic-v1.0.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ArduinoFile
2 | An Arduino-based device for testing and emulating ProFile hard drives.
3 |
4 | ## This repository is now deprecated!
5 | As of 1/20/2025, ArduinoFile is now deprecated, and I probably won't be updating this repo anymore unless people really want me to make some really minor bug fix or add a minor feature. But the good news is that I've developed an ArduinoFile replacement! It's called [ESProFile](https://github.com/alexthecat123/ESProFile), and it's better than ArduinoFile in literally every way. Go and check it out; I'd highly recommend building one!
6 |
7 | 
8 |
9 | ## An Important Note
10 | ArduinoFile emulator mode support on the Lisa 2/10 is experimental at the moment. The emulator was previously nonfunctional on the 2/10 because of its faster transfer speeds that the Arduino couldn't keep up with, but it's now just barely fast enough after some optimizations if you use the code and PCB found in the 2/10-Testing branch. But this is still experimental (although it appears to be pretty solid), so just keep that in mind!ZzZZZZZzzzzzzzzzz
11 |
12 | ## Introduction
13 | I recently built myself a USBWidEx to troubleshoot one of my ProFile hard drives and I'm really happy with it, but I found the surface-mount soldering to be really difficult (I fried three chips in the process) and the Renesas processors used in the device aren't made anymore, so I wanted a cheaper and more simplistic solution.
14 |
15 | The result was the ArduinoFile, which uses an Arduino Mega and a simple PCB to interface with Apple parallel port hard drives. It doesn't have nearly as much functionality as the NeoWidEx yet (for instance, no Widget-specific commands are supported at the moment), but I'll keep adding more capabilities over time.
16 |
17 | The device is also capable of emulating a ProFile hard drive, using an SD card to store disk images, and has near-complete compatibility with Tom Stepleton's awesome [Cameo/Aphid Selector](https://github.com/stepleton/cameo/tree/master/aphid/selector) program. It's pretty fast too; it takes around 40 seconds to boot into LOS 3.0, which is a full 20 seconds faster than my actual ProFiles, and the emulator only needs two seconds or so to initialize itself after you power it up!
18 |
19 | ## Hardware
20 | You can find EasyEDA project files for the ArduinoFile schematic and PCB in the the PCB folder as well as a PDF version of the schematic and Gerber files for fabricating your own boards. I use [JLCPCB](https://jlcpcb.com/) for all of my boards because of the low cost, but of course you can use any PCB fabrication service that you want.
21 |
22 | The ArduinoFile PCB is an Arduino Mega shield with a 26-pin IDC connector for connecting to the drive and a male header that you can easily connect to your logic analyzer if you end up writing/troubleshooting your own code for the device. The shield stacks right on top of the Arduino, making things nice and compact. It also contains a reset button for quickly aborting an operation and returning to the main menu when in tester mode and for quickly returning to the Selector disk image when in emulator mode. The board also has an RGB status LED that is described in the emulator mode and diagnostic mode sections later in this document.
23 |
24 | The links in the list below will take you to Mouser product pages for each of the parts needed in this project.
25 |
26 | - 1x: [Arduino Mega microcontroller](https://www.ebay.com/itm/203648886575?_trkparms=ispr%3D1&hash=item2f6a6b672f:g:q9sAAOSwQYJhZh6q&amdata=enc%3AAQAGAAAA4LIn7qRqelfTlmbmnI9oOvzMVWy8U%2FMiHdn9%2BWBISCraykUuABsB7Slvn0zLzFILrQWIzl0WpW1cx0X3taaQwuUjAKJi5oYGOefIkmSc4FPSLr8kPkBOvSk6AhKYlIC4Vd459cDaCMeYuG%2FGeXBrDFQDDg6dKQmXUd7QEUIW%2BvimVexYcYxdNO2yoOZNpuapadbgRgsb8txWJg2rJyfjQ7me8p1jLMFmeKdbdARdez96YPw2wp9O17UvtAjyCr%2BgxSIrRotJVbrT4EknhFMbNMvyg1QmhjJn%2BWPU4EnjidvO%7Ctkp%3ABFBM1uTDh7hf).
27 | - 1x: [100 Ohm resistor](https://www.mouser.com/ProductDetail/603-CFR-12JT-52-100R) for R1.
28 | - 2x: [180 Ohm resistor](https://www.mouser.com/ProductDetail/603-CFR-25JR-52-180R) for R2 and R3.
29 | - 1x: [Common cathode RGB LED](https://www.mouser.com/ProductDetail/604-WP154A4SUREQBFZW) for LED1.
30 | - 1x: [6mm x 5mm tactile switch](https://www.mouser.com/ProductDetail/612-TL1105A) for S1.
31 | - 1x: [10K 10-pin bussed resistor network](https://www.mouser.com/ProductDetail/652-4610X-101-103FLF) for RP1.
32 | - 1x: [47K 10-pin bussed resistor network](https://www.mouser.com/ProductDetail/652-4610X-1LF-47K) for RP2.
33 | - 1x: [74LS280 Parity generator](https://www.mouser.com/ProductDetail/595-SN74LS280N) for U2 and an optional (but recommended) [14-pin socket](https://www.mouser.com/ProductDetail/575-1104731441001000) for it.
34 | - 7x: [20-pin breakaway male headers](https://www.mouser.com/ProductDetail/538-22-28-4200) for J2, J3, and all of the Arduino pins around the edge of the shield.
35 | - 1x: [Right angle 26-pin (2 x 13) connector](https://www.mouser.com/ProductDetail/798-HIF3FC26PA254DS1) for J1.
36 | - 1x: [SD card breakout board from eBay.](https://www.ebay.com/itm/234062594843?_trkparms=ispr%3D1&hash=item367f37c31b:g:mK8AAOSwH8VgNZVx&amdata=enc%3AAQAGAAAA4ORZR7Yd4l6ArFQiZiIk3%2FyGJhLpKxK3SLa0%2Fi1LBqRWuCFXp7hqVWOXCno8xNnjBRqfldA2iaf6Q32doBTPlb1WHn9Gp6ftLFAb%2FXrCKf9krHnGv6JRjlLqRwzdWU6p2XKnCuU78pGbvgVRCmOd4XR9H6KsFnP%2BUDONNDjTlKlKlIiRLIhjIjyq4RL1gXs5%2Bj12YX3dFb6dngFGK1j%2B8DI2GJIw3wXgKQDcjbOS4bz1YG4LNAxjK%2BO%2FrmlrCGgxo1UM1wpks%2BMggcK%2FKZHXeihhzHH47%2FKW%2B3eTTSNQ27uH%7Ctkp%3ABFBMzP2jhrhf)
37 |
38 | The cable for interfacing with a Widget is just a 26-pin [ribbon cable](https://www.mouser.com/ProductDetail/517-3365-26FT) with two [26-pin female IDC connectors](https://www.mouser.com/ProductDetail/710-61202623021), one on each end. The ProFile interface cable has a [26-pin female IDC](https://www.mouser.com/ProductDetail/710-61202623021) on one end of the [ribbon cable](https://www.mouser.com/ProductDetail/517-3365-26FT) and a [male DB25 connector](https://www.mouser.com/ProductDetail/523-L117DBFRA25P) on the other. These are the same cables that are used with the Cameo/Aphid, so you can reuse your existing cables if you already have one of those.
39 |
40 | Assembly is pretty straightforward: just put each part in the location indicated above and solder it in. See the picture at the start of this document if you're unsure about anything. Make sure to get the polarity of the LED right based on the markings on the silkscreen and ensure that the dot on one end of each resistor pack aligns with the square pad on the board. Also, make sure that you solder in the headers that allow the shield to stack on top of the Arduino before you install the right angle connector since the connector will cover up some of the holes for the Arduino-stacking pins.
41 |
42 | Now that your ArduinoFile is all put together, we can talk about how to use it!
43 | ## ProFile Emulator Mode
44 |
45 | ### Software
46 | The code is probably really sloppy since I wrote it all in a hurry in between my classes, but it certainly gets the job done.
47 |
48 | The ArduinoFile emulator software requires the SDFat library for SD card accesses, which can be installed by selecting the Library Manager from the Arduino IDE's tools menu and searching for "SDFat".
49 |
50 |
51 |
Next, use the Arduino IDE to upload the proFileEmulator.ino sketch to the Arduino and use a terminal program to connect to the board with a baud rate of 115200. You should see a message that says "Default drive file profile.image not found! Halting...". This means that everything looks good so far and that we just need to prepare the SD card.
52 |
53 | If you plan on using the ArduinoFile without the Cameo/Aphid Selector, just format your microSD card as FAT32 and copy your desired raw disk image (images from the Cameo/Aphid work fine) over to the root directory of the card. Change the file's name to "profile.image", plug the SD card into the Arduino, press the reset button, and you should be up and running!
54 |
55 | If you want to use the Selector (HIGHLY recommended), format the card as FAT32 and copy the contents of the SDTemplate folder into the root of the SD card. This will set up the rescue folder with backups of the Selector in ProFile, 3.5", and Twiggy formats and will place an image of the selector called "profile.image" in the root of the card. You can now plug the SD card into the ArduinoFile and press the reset button to initialize the device.
56 |
57 | ### Status LED
58 | When the ArduinoFile is first initializing the SD card and switching to the default drive image, the status LED will illuminate red. The LED will turn green after these operations have completed (usually around two or three seconds), indicating that the emulator is ready for use. The LED will go dark during a standard read or write operation and will remain green whenever the ArduinoFile is not communicating with the host, just like the "Ready" LED on an actual ProFile. During a Selector "magic block" read or write operation (take a look at the Selector manual for more info about these special commands), the LED will turn blue while the operation occurs instead of just going dark. There isn't really a good reason for this other than that it's cool to be able to see when the ArduinoFile is executing a Selector command instead of a regular read or write! Finally, the LED will turn white if the ArduinoFile receives a "halt emulator" command from the Selector and the reset button will need to be pressed to get the emulator running again.
59 |
60 | ### How to Use It
61 |
62 | To be honest, there's really not a lot to say here. Once you follow the software steps mentioned above, just plug the ArduinoFile into your Lisa and it should work just like a normal ProFile! Note that the ArduinoFile needs to be connected to power (the Lisa doesn't supply power over the parallel port), so keep the USB cable connected while in use. If you don't know how to use the Selector yet, read [this incredibly detailed manual](https://github.com/stepleton/cameo/blob/master/aphid/selector/MANUAL.md) to figure things out. The ArduinoFile is pretty much completely compatible with the Selector, with the exception of a few features that aren't implemented yet (read the note below).
63 |
64 | IMPORTANT NOTE: There are two main parts of the Selector protocol that aren't fully functional on the ArduinoFile at the moment. First, I haven't fully implemented the key-value store because it takes the Arduino way too long to look through a database file for the appropriate key-value pair. However, I have made special provisions for the Autoboot and Moniker functions in the Selector, so these work great even though the entire key-value store doesn't. Since the Selector doesn't seem to use the key-value store for anything else, it's unlikely that I'll ever fully implement it unless someone writes a program that needs it. Second, I can't get the SDFat library to retrieve the modification date and time for the files on the SD card for some reason, so the ArduinoFile isn't able to send modification dates when the Selector requests information about a file. Fortunately, the Selector doesn't seem to use this information for anything, so it's not a big deal.
65 |
66 | Due to the limitations of the Arduino, duplicating a file on the SD card (the "C" command in the Selector) takes around 18 seconds for a 5MB disk image and longer for larger images. This is so long that the Selector will time out while waiting for the ArduinoFile to duplicate a disk image and will say that the operation might have failed. Don't worry, it probably didn't! Just give it some time to finish up (you can see when it's done if you're connected to the ArduinoFile over serial or by waiting for the status LED to turn from blue to green at the end of the operation) and you should be good to go!
67 |
68 | Once you're done with a particular disk image and want to get back to the Selector, you can do this either by unplugging the ArduinoFile and then plugging it back in or by pressing the reset button on the ArduinoFile shield. There isn't an OS or anything running on the Arduino, so the emulator is ready to go within two or three seconds of rebooting it!
69 |
70 | The ArduinoFile supports disk images of pretty much any size you want and will automatically adjust the spare table accordingly, so feel free to use 5MB, 10MB, or even larger images (for use with MacWorks only!) and they should all work just fine!
71 |
72 | If you connect to the ArduinoFile over serial with a baud rate of 115200, you can see a lot of really interesting information about what it's doing. There are status messages for the [Selector magic block commands](https://github.com/stepleton/cameo/blob/master/aphid/selector/PROTOCOL.md) (the status for duplicating a file is especially useful, as discussed above), for when the drive was reset, and for any errors during communication between the Lisa and the ArduinoFile. Perhaps most interesting of all, the ArduinoFile will print out the command bytes for each command it receives and it's oddly mesmerizing to see them scroll by when you boot an operating system! For the sake of making the emulator faster, I was going to remove these status/debugging messages after I worked out all the bugs, but removing them didn't improve performance by any noticable amount, so I decided to leave them in since they look so cool!
73 |
74 |
75 |
76 |
77 | ## ProFile/Widget Diagnostic Mode
78 |
79 | In this mode, the ArduinoFile provides functionality similar to that of USBWidEx and has proven to be very helpful in troubleshooting and backing up ProFiles.
80 |
81 | ### Software
82 |
83 | Upload the proFileTester.ino Arduino sketch to the Arduino using the Arduino IDE and you should be good to go! Connect to the device using a terminal program with a baud rate of 115200. Once connected, you should see a main menu screen with a variety of options.
84 |
85 | ### How to Use It
86 |
87 | NOTE: All numbers entered by the user and displayed by the ArduinoFile are in hex
88 | unless otherwise specified.
89 |
90 | NOTE 2: Whenever you enter a block number, you must add leading zeros to make the
91 | entry three bytes long. For instance, you would enter block 1FFF as 001FFF.
92 |
93 | #### Status LED
94 | In diagnostic mode, the status LED will be green whenever the ArduinoFile is ready to accept a command from the user or whenever an operation completes successfully. The LED will illuminate blue whenever user input is expected (such as confirming the drive type or entering a block number) and will be red whenever an error occurs. The LED will go dark during an actual read or write operation and will be yellow in between reads and writes.
95 | #### Main Menu
96 | Upon connecting to the ArduinoFile, you should see a screen that looks like this:
97 |
98 |
99 |
100 | You can select an option from the menu by typing the corresponding number and pressing return. If you ever want to abort a long operation like an XMODEM transfer or read test, just disconnect and reconnect the serial connection to the ArduinoFile or press the reset button on the ArduinoFile shield.
101 |
102 | #### 1 - Reset Drive
103 | This command does exactly what it says: it resets the drive by briefly lowering /PRES and then raising it again.
104 |
105 |
106 | #### 2 - Read Spare Table
107 | Reads the spare table (block FFFFFF) of the drive and displays the raw data in hex.
108 | The drive status bytes and the contents of the spare table are interpreted for
109 | the user as well.
110 |
111 |
112 | #### 3 - Read Block
113 | Reads the desired block into the buffer and displays the hex data that was read.
114 | The status bytes are interpreted for the user. By pressing return at the
115 | retry count and spare threshold prompts, the default values of 64 and 14 are used.
116 | The user can change these values by answering "n" at these prompts.
117 |
118 |
119 | #### 4 - Backup Entire Drive
120 | Backs up the entire contents of the drive to the modern computer over XMODEM.
121 | The ArduinoFile first reads the spare table to determine the size of the drive
122 | that's connected. Press return if the detected drive type is correct or press "n"
123 | to enter a custom drive size in blocks. After confirming drive size, start
124 | the XMODEM receiver (with CRC, not checksum) on the modern computer and the drive will be backed up to a file. The file format is just the raw data from the drive with no headers or anything else weird. This process usually takes around 15 minutes for a 5MB ProFile or 30 minutes for a 10MB ProFile or Widget.
125 |
126 |
127 | #### 5 - Write Buffer to Block
128 | Writes the current contents of the buffer (whatever you last read from the drive)
129 | to a user-specified block on the disk.
130 |
131 |
132 | #### 6 - Restore Drive From Backup
133 | Restores the drive from a backup made with the backup command. Note that this
134 | currently does NOT work with images that have the BLU header or any other format
135 | that's not just the raw data from the disk. Since this operation will destroy
136 | all information currently on your disk, the ArduinoFile gives a warning about this
137 | before beginning the restore operation. It then uses the spare table to determine
138 | the drive's size, which you can change by pressing "n" and entering a custom size
139 | in blocks. After confirming your drive size, start your XMODEM sender with a
140 | 1K block size and wait for the transfer to complete. Just like the backup operation,
141 | this usually takes around 15 minutes for a 5MB ProFile or 30 minutes for a 10MB ProFile or Widget.
142 |
143 |
144 | #### 7 - Write Zeros to Drive
145 | This command does exactly what it says: it writes zeros to every block on the disk.
146 | Since this destroys all data on the drive, the user gets a warning before the drive
147 | is actually zeroed. The ArduinoFile also reads the spare table to get the size of the
148 | drive so that it knows how many blocks to write zeros to. If you want to use a custom
149 | drive size, you can press "n" when it asks if the detected size is correct and enter
150 | your own size in blocks.
151 |
152 |
153 | #### 8 - Low-Level Format
154 | NOTE: This command only works with 5MB ProFiles at the moment. 10MB ProFiles
155 | and Widgets can't be low-level formatted yet, but I'm working on adding support
156 | for them in the future.
157 |
158 | This command allows you to low-level format your 5MB ProFile, which can sometimes fix your drive if you're getting lots of read/write errors. Make sure to have the diagnostic ROM installed in the drive with a piggyback Z8 before running this command. Since a low-level format erases all data on the drive, the user gets a warning before proceeding. The ArduinoFile then reads the spare table to get the drive's size and will refuse to continue unless it's a 5MB ProFile. The device will then ask you to install the jumper on the ProFile's control board at P7 and press return to continue. The ArduinoFile will then proceed to format the drive and will ask you to remove the jumper once it's finished. It will then scan the drive for bad blocks and initialize an empty spare table before telling the user that the format is complete.
159 |
160 |
161 | #### 9 - Drive Tests
162 | Drive Tests is a submenu that contains a variety of read and write tests for troubleshooting your drive. Each one is described below.
163 |
164 | NOTE: You can choose to loop any of these tests forever, in which case the ArduinoFile will show how many passes of the test have been completed so far.
165 |
166 |
167 |
168 |
169 | ##### 1 - Sequential Read
170 | This test sequentially reads every block on the disk and reports any errors to the user, along with an interpretation of the status bytes when the error occurred. Although the ArduinoFile reads the spare table to determine drive size automatically, you can press "n" and enter a custom drive size in blocks if you want.
171 |
172 |
173 |
174 | ##### 2 - Sequential Write
175 | This test sequentially writes 0x55 and then 0xAA to every block on the disk and reports any errors to the user, along with an interpretation of the status bytes when the error occurred. Since this will destroy all data on the disk, the user receives a warning before continuing. Although the ArduinoFile reads the spare table to determine drive size automatically, you can press "n" and enter a custom drive size in blocks if you want.
176 |
177 |
178 |
179 | ##### 3 - Random Read
180 | This test reads a number of random blocks equal to the size of the disk. Note that it DOES NOT necessarily read every block on the disk; just a number of blocks equal to the disk's size. It also reports any errors to the user, along with an interpretation of the status bytes when the error occurred. This test puts more stress on the head positioning systems than the sequential test does. Although the ArduinoFile reads the spare table to determine drive size automatically, you can press "n" and enter a custom drive size in blocks if you want.
181 |
182 |
183 |
184 | ##### 4 - Random Write
185 | This test writes 0x55 and then 0xAA to a number of random blocks equal to the size of the disk. Note that it DOES NOT necessarily write to every block on the disk; just a number of blocks equal to the disk's size. It also reports any errors to the user, along with an interpretation of the status bytes when the error occurred. This test puts more stress on the head positioning systems than the sequential test does. Since this will destroy all data on the disk, the user receives a warning before continuing. Although the ArduinoFile reads the spare table to determine drive size automatically, you can press "n" and enter a custom drive size in blocks if you want.
186 |
187 |
188 |
189 | ##### 5 - Butterfly Read
190 | This test puts a lot of stress on the positioning system by sweeping the heads across the disk (reading block 0, then 25FE, then 2, then 25FD, etc). It also reports any errors to the user, along with an interpretation of the status bytes when the error occurred. Although the ArduinoFile reads the spare table to determine drive size automatically, you can press "n" and enter a custom drive size in blocks if you want.
191 |
192 |
193 |
194 | ##### 6 - Read-Write-Read
195 | This test was inspired by the similar option on USBWidEx. Since this destroys all data on your drive, the ArduinoFile gives you a warning before proceeding. As with many of the other commands, the ArduinoFile automatically detects drive size from the spare table, but you can press "n" and enter your own drive size in blocks if you want. The test then starts with Phase 1 in which it reads every block on the disk. In Phase 2, the ArduinoFile writes a test pattern of 555AAA to every block on the disk, with a 16-bit checksum in the last two bytes of each block. Phase 3 consists of the ArduinoFile rereading every block and checking to make sure that the data matches the pattern just written to the drive. Throughout all three phases of the test, the ArduinoFile will report any errors to the user.
196 |
197 |
198 |
199 | ##### 7 - Return to Main Menu
200 | It would be very concerning if you couldn't figure out what this option did.
201 |
202 | ## Wishlist/Areas For Improvement
203 | The items on the wishlist are listed in order of priority. The ones that I deem most important are closer to the top of the lists.
204 | If you find any bugs, please email me at alexelectronicsguy@gmail.com so that I can work on getting them fixed!
205 | ### Tester Mode
206 | - Allow the user to manually enter custom commands.
207 | - Add Widget-specific commands!
208 | - Low-level format Widgets and 10MB ProFiles!
209 | - Make it check the ROM revision to make sure that you have the diagnostic Z8 fitted before doing a low-level format.
210 | - Read the RAM buffer after a low-level format to see which blocks are bad.
211 | - Read and write to the drive's RAM (requires diagnostic Z8).
212 | - Add individual commands for format, scan, and init spare table (requires diagnostic Z8).
213 | - Allow the user to disable the stepper in a ProFile (requires diagnostic Z8).
214 |
215 | ### Emulator Mode
216 | - Utilize the status bytes if an SD card read/write error occurs (currently, the status bytes are just always zero).
217 | - Get the ArduinoFile to send modification dates and times to the Selector upon request.
218 | - Make the Selector uptime counter do something more interesting since it can't be used for keeping time in this situation.
219 |
220 | ### Both
221 | - Maybe add a switch that the user can toggle to switch between emulator and tester modes on the fly without having to upload new code?
222 | - Add comments to the code.
223 | - Add support for BLU images and images with 5:1 interleave.
224 |
225 | ## Known Bugs
226 | ### Tester Mode
227 | - Sometimes the first read or write to the disk will fail and you have to select "Reset Drive" to get things working properly. After that first read/write, everything works fine.
228 | - Sometimes the terminal freaks out when the ArduinoFile tries to move the cursor back to the beginning of the line and prints the text shifted to the right. I'm not sure if my terminal software or the ArduinoFile itself is to blame for this.
229 |
230 | ### Emulator Mode
231 | - None!
232 |
233 | ## Changelog
234 | - 1.0 - Initial Release
235 | - 1.1 - Fixed a timing issue where the ArduinoFile doesn't wait long enough for the Lisa to put an 0x55 on the bus, causing timeouts on some Lisas.
236 | - 1.2 - Fixed an issue where the status LED doesn't light back up after a read or write, added blue and white LED indications for magic block and halt commands, and implemented the Autoboot and Moniker portions of the key-value store.
237 |
238 | ## Acknowledgements
239 | - This project would not have been possible without the help of the LisaList community, especially Tom Stepleton and his awesome [Cameo/Aphid project](https://github.com/stepleton/cameo/tree/master/aphid).
240 | - I would also like to thank James Denton from [ArcaneByte](http://www.arcanebyte.com/) for helping me find and fix bugs in the ProFile emulator software.
241 | - Dr. Patrick Schäfer's [reverse-engineering of the ProFile communications protocol](http://john.ccac.rwth-aachen.de:8000/patrick/idefile.htm) for his IDEFile project was really invaluable too!
242 |
--------------------------------------------------------------------------------
/SDTemplate/profile.image:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexthecat123/ArduinoFile/67fb143f26d29ebab256e298651cc72e0090b837/SDTemplate/profile.image
--------------------------------------------------------------------------------
/SDTemplate/rescue/selector.3.5inch.dc42:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexthecat123/ArduinoFile/67fb143f26d29ebab256e298651cc72e0090b837/SDTemplate/rescue/selector.3.5inch.dc42
--------------------------------------------------------------------------------
/SDTemplate/rescue/selector.image:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexthecat123/ArduinoFile/67fb143f26d29ebab256e298651cc72e0090b837/SDTemplate/rescue/selector.image
--------------------------------------------------------------------------------
/SDTemplate/rescue/selector.twiggy.dc42:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alexthecat123/ArduinoFile/67fb143f26d29ebab256e298651cc72e0090b837/SDTemplate/rescue/selector.twiggy.dc42
--------------------------------------------------------------------------------
/proFileEmulator.ino:
--------------------------------------------------------------------------------
1 | //***********************************************************************************
2 | //* ArduinoFile ProFile Emulator Software v1.0 *
3 | //* By: Alex Anderson-McLeod *
4 | //* Email address: alexelectronicsguy@gmail.com *
5 | //***********************************************************************************
6 |
7 | //Try to get the creatiom/mod dates to do something
8 | //Add while loops and make sure that there aren't any places where I assume the Lisa will be ready when I am.
9 | //Maybe switch everything over to a 5V Teensy MCU?
10 |
11 | #include
12 | #include
13 | #include "SdFat.h"
14 | #include "sdios.h"
15 | #include
16 |
17 | byte data[532] = {0x54, 0x68, 0x69, 0x73, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x70, 0x75, 0x74, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x62, 0x79, 0x20, 0x73, 0x77, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x65, 0x61, 0x64, 0x73, 0x20, 0x61, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x20, 0x28, 0x72, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x30, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x32, 0x35, 0x46, 0x45, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x32, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x32, 0x35, 0x46, 0x44, 0x2c, 0x20, 0x65, 0x74, 0x63, 0x29, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x64, 0x2e, 0x20, 0x41, 0x6c, 0x74, 0x68, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x61, 0x64, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x61, 0x72, 0x65, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x72, 0x69, 0x76, 0x65, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x2c, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x22, 0x6e, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x61, 0x20, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x20, 0x64, 0x72, 0x69, 0x76, 0x65, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x20, 0x69, 0x66, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x2e, 0x20, 0x41, 0x6c, 0x74, 0x68, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x41, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x72, 0x65, 0x61, 0x64, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x61, 0x72, 0x65, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x20, 0x64, 0x72, 0x69, 0x76, 0x65, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x6c, 0x79, 0x2c, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x22, 0x6e, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x65, 0x6e, 0x74, 0x65}; //the array that holds the block that's currently being read or written
18 |
19 | byte spareTable[48] = {0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x03, 0x98, 0x00, 0x26, 0x00, 0x02, 0x14, 0x20, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x43, 0x61, 0x6D, 0x65, 0x6F, 0x2F, 0x41, 0x70, 0x68, 0x69, 0x64, 0x20, 0x30, 0x30, 0x30, 0x31}; //the array that holds the spare table
20 |
21 | char fileName[256];
22 |
23 | byte commandBuffer[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //the 6-byte command buffer
24 | byte prevState = 1;
25 | byte currentState = 1; //variables that are used for falling edge detection on the strobe line
26 | int index = 0; //an index used when counting strobe pulses
27 | byte *ogPointer;
28 | uint32_t byteNum;
29 | uint32_t startTime;
30 | uint16_t timeout = 10000; //around 18ms
31 | uint16_t currentTime = 0;
32 | SdFat32 SD;
33 | File32 disk;
34 | File32 scratchFile;
35 | File32 sourceFile;
36 | File32 destFile;
37 | //File32 KVStore;
38 | //File32 KVCache;
39 | FatFile rootDir;
40 | int fileCount = 0;
41 | uint16_t nonce;
42 | uint16_t oldNonce;
43 | unsigned long freeSpace;
44 | uint8_t buf[4096];
45 | uint16_t KVMoniker = 1024;
46 | uint16_t KVAutoboot = 1557;
47 | char extension[255] = ".image";
48 | uint32_t uptime = 0; //if this isn't originally zero, then it resets each time we do the stuff mentioned on the next line down.
49 | //uint32_t pointerTest = &uptime; //why is this needed to keep uptime from resetting each time we use the four bytes before data[] to store status?
50 |
51 | //byte KVKey[20];
52 | //byte CorrectKVKey[20] = {0x6D, 0x75, 0x6F, 0x97, 0xCD, 0xDA, 0xDE, 0x35, 0xF2, 0x4E, 0x6B, 0xA5, 0x88, 0x2F, 0x1A, 0x28, 0x34, 0x5B, 0x2D, 0xFA};
53 |
54 | const int red = 22;
55 | const int green = 23;
56 | const int blue = 24;
57 |
58 | void setup(){
59 | setLEDColor(1, 0, 0);
60 | Serial.begin(115200); //start serial communications
61 | nonce = EEPROM.read(5);
62 | EEPROM.write(5, nonce + 1);
63 | pinMode(red, OUTPUT);
64 | pinMode(green, OUTPUT);
65 | pinMode(blue, OUTPUT);
66 | pinMode(25, OUTPUT);
67 | digitalWrite(25, HIGH);
68 |
69 | SD.begin(); //initialize the SD card
70 | rootDir.open("/");
71 | /*if(!KVStore.open("keyvaluestore.db", O_RDWR)){
72 | Serial.println(F("Key-value store not found! Creating it now..."));
73 | if(!KVStore.createContiguous("keyvaluestore.db", 34864620)){
74 | Serial.println(F("Failed to create key-value store! Halting..."));
75 | while(1);
76 | }
77 | Serial.println(F("Done!"));
78 | }
79 |
80 |
81 | Serial.println(F("Wiping key-value cache..."));
82 | rootDir.remove("keyvaluecache.db");
83 | if(!KVCache.createContiguous("keyvaluecache.db", 34864620)){
84 | Serial.println(F("Failed to clear key-value cache! Halting..."));
85 | while(1);
86 | }
87 | Serial.println(F("Done!"));*/
88 |
89 | if(!disk.open("profile.image", O_RDWR)){
90 | Serial.println(F("Default drive file profile.image not found! Halting..."));
91 | while(1);
92 | }
93 |
94 | /*KVCache.seekSet(532*65530);
95 | KVCache.read(KVKey, 20);
96 | for(int i = 0; i < 20; i++){
97 | printDataNoSpace(KVKey[i]);
98 | }
99 | Serial.println();
100 | int correct = 0;
101 | for(int i = 0; i < 65535; i++){
102 | Serial.println(i);
103 | KVCache.seekSet(i * 532);
104 | KVCache.read(KVKey, 20);
105 | for(int i = 0; i < 20; i++){
106 | if(KVKey[i] == CorrectKVKey[i]){
107 | correct++;
108 | }
109 | if(correct == 20){
110 | break;
111 | }
112 | correct = 0;
113 | }
114 | }
115 | Serial.print("Found it at index ");
116 | Serial.println(KVCache.curPosition());*/
117 | updateSpareTable();
118 | setLEDColor(0, 1, 0);
119 | Serial.println(F("ArduinoFile is ready!"));
120 | disk.seekSet(0);
121 | disk.read(data, 532);
122 | PORTE = PORTE & B11001111; //set the two pins that we're going to use for interrupts to inputs
123 |
124 | EICRB |= B00001010;
125 | EICRB &= B11111010; //set INT4 and INT5 to trigger on the falling edge
126 | EIMSK |= (0 << INT4);
127 | EIMSK |= (0 << INT5); //make sure that both interrupts are disabled initially (we actually never use INT5, but I might use it to speed things up in the future)
128 | delay(10);
129 | }
130 |
131 | void loop() {
132 | initPins(); //set all pins to their idle states
133 | cli(); //disable interrupts to keep them from slowing things down
134 | while(readCMD() == 1); //wait for CMD to go low
135 | if((PINL & B00010000) == B00000000){
136 | sei();
137 | delay(10);
138 | Serial.println(F("Resetting drive..."));
139 | delay(10);
140 | cli();
141 | return;
142 | }
143 | cli();
144 | sendData(0x01); //send an 0x01 to the host
145 | //PORTC = PORTC & B11011111;
146 | setBSY(); //and lower BSY to acknowledge our presence
147 | //startTime = millis();
148 | currentTime = 0;
149 | while((PINC & B00000001) == B00000000){
150 | currentTime++;
151 | if(currentTime >= timeout){
152 | /*sei();
153 | Serial.println("Timeout: Initial Handshake");
154 | cli();*/
155 | return;
156 | }
157 | } //wait for the host to raise CMD
158 | //while(readCMD() == 0);
159 | DDRL = B00000000; //set the bus into input mode so we can read from it
160 | //PORTC = PORTC | B00100000;
161 |
162 | currentTime = 0;
163 | while(PINL != 0x55){ //wait for the host to respond with an 0x55 and timeout if it doesn't
164 | currentTime++;
165 | if(currentTime >= timeout){
166 | sei();
167 | delay(10);
168 | Serial.println(F("Phase 1: Host didn't respond with a 55! Maybe the drive was reset?"));
169 | delay(10);
170 | cli();
171 | return;
172 | }
173 | }
174 | //!!!TIMEOUT IF R/W IS HIGH!!!
175 |
176 | //pointer = commandBuffer; //make the pointer point to the command buffer
177 | byte *pointer = commandBuffer; //make the pointer point to the data array
178 | unsigned int value;
179 | ogPointer = pointer;
180 | PORTC = PORTC | B00000010; //if everything checks out, raise BSY
181 | setLEDColor(0, 1, 0);
182 | currentTime = 0;
183 | byte oldPIN = 0;
184 | while((PINC & B00000001) == B00000001){ //do this for each of the remaining data bytes and ((PINC | B11111011)) == B11111011
185 | currentState = PINC & B00001000;
186 | if(currentState == B00000000 and prevState == B00001000){ //if we're on the falling edge of the strobe, put the next data byte on the bus and increment the pointer
187 | *pointer++ = PINL;
188 |
189 | }
190 | currentTime++;
191 | if(currentTime >= timeout){
192 | /*sei();
193 | Serial.println("Timeout: Command Bytes");
194 | cli();*/
195 | return;
196 | }
197 | prevState = currentState;
198 | }
199 |
200 | sei();
201 | for(int i = 0; i < 6; i++){
202 | printDataNoSpace(commandBuffer[i]);
203 | }
204 | Serial.println();
205 | cli();
206 |
207 | if(commandBuffer[0] == 0x00){
208 | readDrive(); //if the first byte of the command is 0, we need to do a read
209 | }
210 | else if(commandBuffer[0] == 0x01 or commandBuffer[0] == 0x02 or commandBuffer[0] == 0x03){
211 | writeDrive(commandBuffer[0] + 0x02); //if the first byte is a 1, 2, or 3, we need to do a write
212 | }
213 | //this emulator treats both writes and write-verifys exactly the same
214 |
215 | else{ //if we get some other command, print an error message and wait for the next handshake from the host
216 | sei();
217 | delay(10);
218 | Serial.println(F("Bad Command!"));
219 | for(int i = 0; i < 6; i++){
220 | printDataNoSpace(commandBuffer[i]);
221 | }
222 | Serial.println();
223 | Serial.println(pointer - ogPointer);
224 | Serial.println();
225 | sendData(0x55);
226 | setBSY();
227 | delay(10);
228 | cli();
229 | }
230 | sei();
231 |
232 | cli(); //if we don't enable and then disable interrupts here, weird behavior results for some reason
233 |
234 | }
235 |
236 | void readDrive(){
237 | sendData(0x02);
238 | setBSY(); //put the read command confirmation of 0x02 on the bus and lower BSY
239 | currentTime = 0;
240 | while(readCMD() == 0){
241 | currentTime++;
242 | if(currentTime >= timeout){
243 | /*sei();
244 | Serial.println("Timeout: Read Command Confirmation");
245 | cli();*/
246 | return;
247 | }
248 | } //wait for the host to raise CMD
249 | DDRL = B00000000; //set the bus into input mode
250 | currentTime = 0;
251 | while(PINL != 0x55){ //wait for the host to respond with an 0x55 and timeout if it doesn't
252 | currentTime++;
253 | if(currentTime >= timeout){
254 | sei();
255 | delay(10);
256 | Serial.println(F("Read phase 2: Host didn't respond with a 55! Maybe the drive was reset?"));
257 | delay(10);
258 | cli();
259 | return;
260 | }
261 | }
262 | byteNum = ((uint32_t)commandBuffer[1] << 16 | (uint32_t)commandBuffer[2] << 8 | (uint32_t)commandBuffer[3]);
263 | if(commandBuffer[1] == 0xFF and commandBuffer[2] == 0xFF and commandBuffer[3] == 0xFF){
264 | for(int i = 0; i < 48; i++){
265 | data[i] = spareTable[i];
266 | }
267 | for(int i = 48; i < 532; i++){
268 | data[i] = 0xFF;
269 | }
270 | }
271 | else if(commandBuffer[1] == 0xFF and commandBuffer[2] == 0xFE and commandBuffer[3] == 0xFD){ //emulator status
272 | setLEDColor(0, 0, 1); //fix LED issue
273 | sei();
274 | char days[5];
275 | char hours[3];
276 | char minutes[3];
277 | char seconds[3];
278 | sprintf(days, "%4d", ((uptime%(86400*30))/86400));
279 | sprintf(hours, "%02d", ((uptime%86400)/3600));
280 | sprintf(minutes, "%02d", ((uptime%3600)/60));
281 | sprintf(seconds, "%02d", uptime%60);
282 | for(int i = 0; i < 4; i++){ //uptime
283 | data[i] = days[i % 4];
284 | }
285 | for(int i = 4; i < 6; i++){ //uptime
286 | data[i] = hours[i % 2];
287 | }
288 | for(int i = 6; i < 8; i++){ //uptime
289 | data[i] = minutes[i % 2];
290 | }
291 | for(int i = 8; i < 10; i++){ //uptime
292 | data[i] = seconds[i % 2];
293 | }
294 | if(nonce != oldNonce){
295 | freeSpace = SD.vol()->freeClusterCount() * SD.vol()->sectorsPerCluster() * 512;
296 | }
297 |
298 | uptime += 2;
299 |
300 | oldNonce = nonce;
301 | char bytesFree[16];
302 | ultoa(freeSpace, bytesFree, 10);
303 | int i;
304 | for(i = 0; i < 15; i++){
305 | if(bytesFree[i] == 0x00){
306 | i--;
307 | break;
308 | }
309 | }
310 | int dataIndex = 24;
311 | for(int j = i; j >= 0; j--){ //bytes free
312 | data[dataIndex] = bytesFree[j];
313 | dataIndex--;
314 | }
315 | for(i = 14 - i; i > 0; i--){ //bytes free
316 | data[dataIndex] = 0x20;
317 | dataIndex--;
318 | }
319 | data[25] = 0x30; //load
320 | data[26] = 0x00; //load
321 | data[32] = 0x30; //load
322 | data[33] = 0x00; //load
323 | data[39] = 0x30; //load
324 | data[40] = 0x00; //load
325 | data[46] = 0x31; //processes
326 | data[47] = 0x00; //processes
327 | data[51] = 0x31; //processes
328 | data[52] = 0x00; //processes
329 | for(int i = 56; i < 532; i++){ //null terminate for the rest of the block
330 | data[i] = 0x00;
331 | }
332 | cli();
333 | }
334 | else if(commandBuffer[1] == 0xFF and commandBuffer[2] == 0xFE and commandBuffer[3] == 0xFE){ //get file info
335 | setLEDColor(0, 0, 1); //fix LED issue
336 | sei();
337 | fileCount = 0;
338 | rootDir.rewind();
339 | while(scratchFile.openNext(&rootDir, O_RDONLY)){
340 | fileName[scratchFile.getName7(fileName, 256)] = 0x00;
341 | if(not scratchFile.isDir() and fileName[0] != '.' and strstr(fileName, extension) != 0){
342 | fileCount += 1;
343 | }
344 | scratchFile.close();
345 | }
346 | for(int i = 0; i < 4; i++){ //nonce
347 | data[i] = nonce;
348 | }
349 | data[4] = fileCount << 8; //num of files
350 | data[5] = fileCount;
351 |
352 | uint32_t fileSize;
353 | uint16_t dateModified[50];
354 | uint16_t timeModified[50];
355 | rootDir.rewind();
356 | if(((commandBuffer[4] << 8) + commandBuffer[5]) < fileCount){
357 | fileCount = 0;
358 | while(fileCount <= (commandBuffer[4] << 8) + commandBuffer[5]){
359 | scratchFile.openNext(&rootDir, O_RDONLY);
360 | fileName[scratchFile.getName7(fileName, 256)] = 0x00;
361 | if(not scratchFile.isDir() and fileName[0] != '.' and strstr(fileName, extension) != 0){
362 | fileCount += 1;
363 | fileSize = scratchFile.fileSize();
364 | }
365 | //scratchFile.getModifyDateTime(dateModified, timeModified);
366 | scratchFile.close();
367 | }
368 | for(int i = 6; i < 20; i++){ //last modified time
369 | //Serial.print(dateModified[i - 6]);
370 | //Serial.print(" ");
371 | data[i] = 0x42;
372 | }
373 | //Serial.println();
374 |
375 | data[20] = fileSize >> 72;
376 | data[21] = fileSize >> 64;
377 | data[22] = fileSize >> 56;
378 | data[23] = fileSize >> 48;
379 | data[24] = fileSize >> 40;
380 | data[25] = fileSize >> 32;
381 | data[26] = fileSize >> 24;
382 | data[27] = fileSize >> 16;
383 | data[28] = fileSize >> 8;
384 | data[29] = fileSize;
385 |
386 | char sizeString[5];
387 | if(fileSize == 5175296){
388 | data[30] = 0x20;
389 | data[31] = 0x20;
390 | data[32] = 0x35;
391 | data[33] = 0x4D;
392 | }
393 | else if(fileSize == 10350592){
394 | data[30] = 0x20;
395 | data[31] = 0x31;
396 | data[32] = 0x30;
397 | data[33] = 0x4D;
398 | }
399 | else{
400 | if(fileSize > 1089003999999){
401 | data[30] = 0x48;
402 | data[31] = 0x55;
403 | data[32] = 0x47;
404 | data[33] = 0x45;
405 | }
406 | else if(fileSize > 1089003999){
407 | sprintf(sizeString, "%3d", (fileSize/1089004000));
408 | strlcat(sizeString, "G", sizeof(sizeString));
409 | data[30] = sizeString[0];
410 | data[31] = sizeString[1];
411 | data[32] = sizeString[2];
412 | data[33] = sizeString[3];
413 | }
414 | else if(fileSize > 1089004){
415 | sprintf(sizeString, "%3d", (fileSize/1089004));
416 | strlcat(sizeString, "M", sizeof(sizeString));
417 | data[30] = sizeString[0];
418 | data[31] = sizeString[1];
419 | data[32] = sizeString[2];
420 | data[33] = sizeString[3];
421 | }
422 | else if(fileSize > 9999){
423 | sprintf(sizeString, "%3d", (fileSize/1090));
424 | strlcat(sizeString, "K", sizeof(sizeString));
425 | data[30] = sizeString[0];
426 | data[31] = sizeString[1];
427 | data[32] = sizeString[2];
428 | data[33] = sizeString[3];
429 | }
430 | else{
431 | sprintf(sizeString, "%4d", fileSize);
432 | data[30] = sizeString[0];
433 | data[31] = sizeString[1];
434 | data[32] = sizeString[2];
435 | data[33] = sizeString[3];
436 | }
437 | }
438 | for(int i = 20; data[i] == 0x00; i++){
439 | data[i] = 0x20;
440 | }
441 | int i = 0;
442 | for(i = 276; fileName[i - 276] != 0x00; i++){ //filename
443 | data[i] = fileName[i - 276];
444 | }
445 | for(; i < 532; i++){
446 | data[i] = 0x00;
447 | }
448 | }
449 | else{
450 | for(int i = 0; i < 532; i++){
451 | data[i] = 0x00;
452 | }
453 | }
454 | delay(10);
455 | cli();
456 | }
457 |
458 | else if(commandBuffer[1] == 0xFF and commandBuffer[2] == 0xFE and commandBuffer[3] == 0xFF){ //retrieve key-value entry from the cache
459 | setLEDColor(0, 0, 1); //fix LED issue
460 | sei();
461 | if(commandBuffer[4] == 0x53 and commandBuffer[5] == 0x43){
462 | Serial.println(F("Loaded moniker/autoboot key-value pair!"));
463 | for(int i = 0; i < 532; i++){
464 | data[i] = EEPROM.read(KVMoniker + i);
465 | }
466 | }
467 | else if(commandBuffer[4] == 0x53 and commandBuffer[5] == 0x61){
468 | Serial.println(F("Loaded autoboot password key-value pair!"));
469 | for(int i = 0; i < 532; i++){
470 | data[i] = EEPROM.read(KVAutoboot + i);
471 | }
472 | }
473 | else{
474 | Serial.println(F("Error: Unsupported key-value load operation!"));
475 | }
476 | //Moniker address is 5343 in the cache and 53656C6563746F723A20636F6E666967202020D7 in the store
477 |
478 | //Serial.println(F("Warning: The attempted key-value cache read failed because key-value operations are not currently supported!"));
479 | /*KVCache.seekSet((commandBuffer[4] << 8) | (commandBuffer[5]));
480 | Serial.print(F("Reading entry "));
481 | printDataNoSpace(commandBuffer[4]);
482 | printDataNoSpace(commandBuffer[5]);
483 | Serial.println(F(" from the key-value cache..."));
484 | KVCache.read(data, 532);
485 | Serial.print(F("Key: "));
486 | for(int i = 0; i < 20; i++){
487 | printDataNoSpace(data[i]);
488 | }
489 | Serial.println();
490 | Serial.print(F("Value: "));
491 | for(int i = 20; i < 532; i++){
492 | printDataNoSpace(data[i]);
493 | }
494 | Serial.println();*/
495 | delay(1);
496 | cli();
497 | }
498 |
499 | else if(commandBuffer[1] == 0xFF and commandBuffer[2] == 0xFE and commandBuffer[3] == 0xFC){ //selector rescue
500 | setLEDColor(0, 0, 1); //fix LED issue
501 | sei();
502 | if(commandBuffer[4] == 0xFF and commandBuffer[5] == 0xFF){ //replace selector with a spare from the rescue folder
503 | int replacementIndex = 0;
504 | char buffer[30];
505 | sprintf(buffer, "profile-backup-%d.image", replacementIndex);
506 | while(rootDir.exists(buffer)){
507 | replacementIndex++;
508 | sprintf(buffer, "profile-backup-%d.image", replacementIndex);
509 | }
510 | Serial.print(replacementIndex);
511 | Serial.println(F(" does not exist! Creating it..."));
512 | disk.close();
513 | if(!sourceFile.open("profile.image", O_RDWR)){
514 | Serial.println(F("Error opening current profile.image!"));
515 | }
516 | else if(!sourceFile.rename(buffer)){
517 | Serial.println(F("Error creating backup of current profile.image!"));
518 | }
519 | else if(!sourceFile.close() or !sourceFile.open("/rescue/selector.image", O_RDONLY)){
520 | Serial.println(F("Error opening backup selector image!"));
521 | }
522 | else if(!destFile.createContiguous("profile.image", sourceFile.fileSize())){
523 | Serial.println(F("Error creating new profile.image!"));
524 | }
525 | else{
526 | size_t n;
527 | uint32_t index = 0;
528 | while ((n = sourceFile.read(buf, sizeof(buf))) > 0){
529 | destFile.write(buf, n);
530 | }
531 | nonce = EEPROM.read(5);
532 | EEPROM.write(5, nonce + 1);
533 | Serial.println(F("Done backing up old image and restoring new image!"));
534 | }
535 | sourceFile.close();
536 | destFile.close();
537 | if(!disk.open("profile.image", O_RDWR)){
538 | Serial.println(F("Failed to reopen profile.image!"));
539 | }
540 | updateSpareTable();
541 | }
542 | else if((commandBuffer[4] >> 4) == 0x00){ //send a block of a selector ProFile image
543 | byteNum = ((commandBuffer[4] & B00001111) << 8 | commandBuffer[5]) * 532;
544 | if(!sourceFile.open("/rescue/selector.image", O_RDONLY)){
545 | Serial.println(F("Error opening backup Selector ProFile image!"));
546 | }
547 | else if((byteNum + 531) >= sourceFile.fileSize()){
548 | Serial.println(F("Error: Block is past the end of the file!"));
549 | for(int i = 0; i < 532; i++){
550 | data[i] = 0x00;
551 | }
552 | }
553 | else{
554 | disk.seekSet(byteNum);
555 | sourceFile.read(data, 532);
556 | Serial.print(F("Successfully read block "));
557 | printDataNoSpace(commandBuffer[4] & B00001111);
558 | printDataNoSpace(commandBuffer[5]);
559 | Serial.println(F(" of backup Selector ProFile image!"));
560 | }
561 | sourceFile.close();
562 | }
563 | else if((commandBuffer[4] >> 4) == 0x01){ //send a block of a selector 3.5 inch floppy image
564 | byteNum = ((commandBuffer[4] & B00001111) << 8 | commandBuffer[5]) * 532;
565 | if(!sourceFile.open("/rescue/selector.3.5inch.dc42", O_RDONLY)){
566 | Serial.println(F("Error opening backup Selector 3.5 image!"));
567 | }
568 | else if((byteNum + 531) >= sourceFile.fileSize()){
569 | Serial.println(F("Error: Block is past the end of the file!"));
570 | for(int i = 0; i < 532; i++){
571 | data[i] = 0x00;
572 | }
573 | }
574 | else{
575 | disk.seekSet(byteNum);
576 | sourceFile.read(data, 532);
577 | Serial.print(F("Successfully read block "));
578 | printDataNoSpace(commandBuffer[4] & B00001111);
579 | printDataNoSpace(commandBuffer[5]);
580 | Serial.println(F(" of backup Selector 3.5 DC42 image!"));
581 | }
582 | sourceFile.close();
583 | }
584 | else if((commandBuffer[4] >> 4) == 0x02){ //send a block of a selector Twiggy image
585 | byteNum = ((commandBuffer[4] & B00001111) << 8 | commandBuffer[5]) * 532;
586 | if(!sourceFile.open("/rescue/selector.twiggy.dc42", O_RDONLY)){
587 | Serial.println(F("Error opening backup Selector Twiggy image!"));
588 | }
589 | else if((byteNum + 531) >= sourceFile.fileSize()){
590 | Serial.println(F("Error: Block is past the end of the file!"));
591 | for(int i = 0; i < 532; i++){
592 | data[i] = 0x00;
593 | }
594 | }
595 | else{
596 | disk.seekSet(byteNum);
597 | sourceFile.read(data, 532);
598 | Serial.print(F("Successfully read block "));
599 | printDataNoSpace(commandBuffer[4] & B00001111);
600 | printDataNoSpace(commandBuffer[5]);
601 | Serial.println(F(" of backup Selector Twiggy DC42 image!"));
602 | }
603 | sourceFile.close();
604 | }
605 | else{
606 | Serial.println(F("Bad selector rescue command!"));
607 | for(int i = 0; i < 532; i++){
608 | data[i] = 0x00;
609 | }
610 | }
611 | delay(10);
612 | cli();
613 | }
614 | else if(byteNum < disk.fileSize()){
615 | sei();
616 | byteNum *= 532;
617 | disk.seekSet(byteNum);
618 | disk.read(data, 532);
619 | delay(1);
620 | cli();
621 | }
622 | else{
623 | sei();
624 | Serial.println(F("Error: Requested block is out of range!"));
625 | for(int i = 0; i < 532; i++){
626 | data[i] = 0x00;
627 | }
628 | delay(1);
629 | cli();
630 | }
631 | DDRL = B11111111; //set the bus into output mode
632 | PORTL = 0x00; //and put the first status byte on the bus.
633 | index = 0; //clear out the index
634 | for(byte *i = data - 4; i < data; i++){
635 | *i = 0x00;
636 | }
637 | clearBSY(); //and raise BSY
638 | //startTime = millis();
639 | currentTime = 0;
640 | byte *pointer = data - 4; //make the pointer point to the data array
641 | PORTL = *pointer++; //put the first data byte on the bus and increment the value of the pointer
642 | while((PINC & B00000001) == B00000001){ //do this for each of the remaining data bytes
643 | currentState = PINC & B00001000;
644 | if(currentState == B00000000 and prevState == B00001000){ //if we're on the falling edge of the strobe, put the next data byte on the bus and increment the pointer
645 | PORTL = *pointer++;
646 | }
647 | /*currentTime++;
648 | if(currentTime >= timeout){
649 | sei();
650 | Serial.println("Timeout: Read Command Data Bytes");
651 | cli();
652 | return;
653 | }*/
654 | prevState = currentState;
655 | }
656 | }
657 |
658 | void writeDrive(byte response){
659 | sendData(response); //send the appropriate response byte (the command byte plus two)
660 | setBSY(); //and lower BSY
661 | currentTime = 0;
662 | while(readCMD() == 0){
663 | currentTime++;
664 | if(currentTime >= timeout){
665 | /*sei();
666 | Serial.println("Timeout: Write Command Confirmation");
667 | cli();*/
668 | return;
669 | }
670 | } //wait for the host to raise CMD
671 | DDRL = B00000000; //set the bus into input mode
672 | currentTime = 0;
673 | while(PINL != 0x55){ //wait for the host to respond with an 0x55 and timeout if it doesn't
674 | currentTime++;
675 | if(currentTime >= timeout){
676 | sei();
677 | delay(10);
678 | Serial.println(F("Write phase 2: Host didn't respond with a 55! Maybe the drive was reset?"));
679 | delay(10);
680 | cli();
681 | return;
682 | }
683 | }
684 | clearBSY(); //if everything looks good, raise BSY
685 |
686 | byte *pointer = data; //make the pointer point toward our data array
687 | currentTime = 0;
688 | while((PINC & B00000001) == B00000001){ //do this for each of the 532 bytes that we're receiving
689 | currentState = PINC & B00001000;
690 | if(currentState == B00000000 and prevState == B00001000){ //when we detect a falling edge on the strobe line, read the data bus, save it contents into the data array, and increment the pointer
691 | *pointer++ = PINL;
692 | }
693 | /*currentTime++;
694 | if(currentTime >= timeout){
695 | sei();
696 | Serial.println("Timeout: Write Command Data Bytes");
697 | cli();
698 | return;
699 | }*/
700 | prevState = currentState;
701 | }
702 | currentTime = 0;
703 | while(readCMD() == 1){
704 | currentTime++;
705 | if(currentTime >= timeout){
706 | /*sei();
707 | Serial.println("Timeout: Write Command Second Handshake");
708 | cli();*/
709 | return;
710 | }
711 | } //wait for the host to lower CMD
712 | sendData(0x06); //send the appropriate response of 0x06
713 | setBSY(); //and lower BSY
714 | currentTime = 0;
715 | while(readCMD() == 0){
716 | currentTime++;
717 | if(currentTime >= timeout){
718 | /*sei();
719 | Serial.println("Timeout: Write Command Second Handshake Part 2");
720 | cli();*/
721 | return;
722 | }
723 | } //wait for the host to raise CMD
724 | DDRL = B00000000; //set the bus into input mode
725 | currentTime = 0;
726 | while(PINL != 0x55){ //wait for the host to respond with an 0x55 and timeout if it doesn't
727 | currentTime++;
728 | if(currentTime >= timeout){
729 | sei();
730 | delay(10);
731 | Serial.println(F("Write phase 3: Host didn't respond with a 55! Maybe the drive was reset?"));
732 | delay(10);
733 | cli();
734 | return;
735 | }
736 | }
737 | bool halt = false;
738 | byteNum = ((uint32_t)commandBuffer[1] << 16 | (uint32_t)commandBuffer[2] << 8 | (uint32_t)commandBuffer[3]);
739 | if(commandBuffer[1] == 0xFF and commandBuffer[2] == 0xFF and commandBuffer[3] == 0xFD and commandBuffer[4] == 0xFE and commandBuffer[5] == 0xAF){ //built-in command
740 | sei();
741 | if(data[0] == 0x48 and data[1] == 0x41 and data[2] == 0x4C and data[3] == 0x54){ //halt
742 | halt = true;
743 | Serial.println(F("Halting emulator..."));
744 | setLEDColor(1, 1, 1);
745 | }
746 | if(data[0] == 0x49 and data[1] == 0x4D and data[2] == 0x41 and data[3] == 0x47 and data[4] == 0x45 and data[5] == 0x3A){ //switch image files
747 | setLEDColor(0, 0, 1); //fix LED issue
748 | Serial.print(F("Switching to image file "));
749 | int i = 6;
750 | while(1){
751 | if(data[i] == 0x00){
752 | fileName[i - 6] = 0x00;
753 | break;
754 | }
755 | fileName[i - 6] = data[i];
756 | Serial.write(data[i]);
757 | i++;
758 | }
759 | }
760 | Serial.println();
761 | disk.close();
762 | if(!disk.open(fileName, O_RDWR)){
763 | Serial.println(F("Error opening image file!"));
764 | disk.close();
765 | if(!disk.open("profile.image", O_RDWR)){
766 | Serial.println(F("Failed to reopen profile.image!"));
767 | }
768 | updateSpareTable();
769 | }
770 | else{
771 | updateSpareTable();
772 | Serial.println(F("Success!"));
773 | }
774 | delay(10);
775 | cli();
776 | }
777 | else if(commandBuffer[1] == 0xFF and commandBuffer[2] == 0xFE and commandBuffer[3] == 0xFF){
778 | setLEDColor(0, 0, 1); //fix LED issue
779 | sei();
780 | if(commandBuffer[4] == 0xFF and commandBuffer[5] == 0xFF);
781 | else if(commandBuffer[4] == 0x53 and commandBuffer[5] == 0x43){
782 | Serial.println(F("Wrote to moniker/autoboot key-value pair!"));
783 | for(int i = 0; i < 532; i++){
784 | EEPROM.write(KVMoniker + i, data[i]);
785 | }
786 | }
787 | else if(commandBuffer[4] == 0x53 and commandBuffer[5] == 0x61){
788 | Serial.println(F("Wrote to autoboot password key-value pair!"));
789 | for(int i = 0; i < 532; i++){
790 | EEPROM.write(KVAutoboot + i, data[i]);
791 | }
792 | }
793 | else{
794 | Serial.println(F("Error: Unsupported key-value write operation!"));
795 | }
796 | delay(1);
797 | cli();
798 | }
799 | else if(commandBuffer[1] == 0xFF and commandBuffer[2] == 0xFE and commandBuffer[3] == 0xFE){ //FS commands
800 | setLEDColor(0, 0, 1); //fix LED issue
801 | sei();
802 | if(commandBuffer[4] == 0x63 and commandBuffer[5] == 0x70){ //copy
803 | Serial.print(F("Copying "));
804 | int i = 0;
805 | while(1){
806 | if(data[i] == 0x00){
807 | fileName[i] = 0x00;
808 | break;
809 | }
810 | fileName[i] = data[i];
811 | Serial.write(data[i]);
812 | i++;
813 | }
814 | i++;
815 | if(!sourceFile.open(fileName, O_RDONLY)){
816 | Serial.println(F("Error opening source file!"));
817 | }
818 | else{
819 | Serial.print(F(" to "));
820 | int offset = i;
821 | while(1){
822 | if(data[i] == 0x00){
823 | fileName[i - offset] = 0x00;
824 | break;
825 | }
826 | fileName[i - offset] = data[i];
827 | Serial.write(fileName[i - offset]);
828 | i++;
829 | }
830 | Serial.print(F(" size "));
831 | Serial.println(sourceFile.fileSize());
832 | if(!destFile.createContiguous(fileName, sourceFile.fileSize())){
833 | Serial.println(F("Error creating destination file!"));
834 | }
835 | else{
836 | size_t n;
837 | uint32_t index = 0;
838 | while ((n = sourceFile.read(buf, sizeof(buf))) > 0){
839 | destFile.write(buf, n);
840 | }
841 | nonce = EEPROM.read(5);
842 | EEPROM.write(5, nonce + 1);
843 | Serial.println(F("Done!"));
844 | }
845 | }
846 | sourceFile.close();
847 | destFile.close();
848 | }
849 | else if(commandBuffer[4] == 0x6D and commandBuffer[5] == 0x6B){ //create new image, normal
850 | setLEDColor(0, 0, 1); //fix LED issue
851 | Serial.print(F("Making new 5MB image called "));
852 | int i = 0;
853 | while(1){
854 | if(data[i] == 0x00){
855 | fileName[i] = 0x00;
856 | break;
857 | }
858 | fileName[i] = data[i];
859 | i++;
860 | }
861 | Serial.println();
862 | if(!destFile.createContiguous(fileName, 5175296)){
863 | Serial.println(F("Error creating destination file!"));
864 | }
865 | else{
866 | Serial.println(F("Done!"));
867 | }
868 | nonce = EEPROM.read(5);
869 | EEPROM.write(5, nonce + 1);
870 | destFile.close();
871 | }
872 | else if(commandBuffer[4] == 0x6D and commandBuffer[5] == 0x78){ //create new image, extended
873 | setLEDColor(0, 0, 1); //fix LED issue
874 | Serial.print(F("Making new image of size "));
875 | int i = 0;
876 | while(1){
877 | if(data[i] == 0x00){
878 | fileName[i] = 0x00;
879 | break;
880 | }
881 | fileName[i] = data[i];
882 | i++;
883 | }
884 | uint32_t size = strtol(fileName, NULL, 10);
885 | Serial.print(size);
886 | i++;
887 | int offset = i;
888 | Serial.print(F(" called "));
889 | while(1){
890 | if(data[i] == 0x00){
891 | fileName[i - offset] = 0x00;
892 | break;
893 | }
894 | fileName[i - offset] = data[i];
895 | Serial.write(fileName[i - offset]);
896 | i++;
897 | }
898 | Serial.println();
899 | if(!destFile.createContiguous(fileName, size)){
900 | Serial.println(F("Error creating destination file!"));
901 | }
902 | else{
903 | Serial.println(F("Done!"));
904 | }
905 | nonce = EEPROM.read(5);
906 | EEPROM.write(5, nonce + 1);
907 | destFile.close();
908 | }
909 | else if(commandBuffer[4] == 0x72 and commandBuffer[5] == 0x6D){ //delete
910 | setLEDColor(0, 0, 1); //fix LED issue
911 | int i = 0;
912 | Serial.print(F("Deleting file "));
913 | while(1){
914 | if(data[i] == 0x00){
915 | fileName[i] = 0x00;
916 | break;
917 | }
918 | fileName[i] = data[i];
919 | Serial.write(data[i]);
920 | i++;
921 | }
922 | Serial.println();
923 | if(!rootDir.remove(fileName)){
924 | Serial.println(F("Failed to delete file!"));
925 | }
926 | nonce = EEPROM.read(5);
927 | EEPROM.write(5, nonce + 1);
928 | }
929 | else if(commandBuffer[4] == 0x6D and commandBuffer[5] == 0x76){ //move aka rename
930 | setLEDColor(0, 0, 1); //fix LED issue
931 | Serial.print(F("Renaming "));
932 | int i = 0;
933 | while(1){
934 | if(data[i] == 0x00){
935 | fileName[i] = 0x00;
936 | break;
937 | }
938 | fileName[i] = data[i];
939 | Serial.write(data[i]);
940 | i++;
941 | }
942 | i++;
943 | if(!sourceFile.open(fileName, O_RDWR)){
944 | Serial.println();
945 | Serial.println(F("Error opening source file!"));
946 | }
947 | else{
948 | Serial.print(" to ");
949 | int offset = i;
950 | while(1){
951 | if(data[i] == 0x00){
952 | fileName[i - offset] = 0x00;
953 | break;
954 | }
955 | fileName[i - offset] = data[i];
956 | Serial.write(data[i]);
957 | i++;
958 | }
959 | Serial.println();
960 | if(!sourceFile.rename(fileName)){
961 | Serial.println(F("Error renaming file!"));
962 | }
963 | }
964 | sourceFile.close();
965 | nonce = EEPROM.read(5);
966 | EEPROM.write(5, nonce + 1);
967 | }
968 | else if(commandBuffer[4] == 0x73 and commandBuffer[5] == 0x78){ //sx (set extension)
969 | setLEDColor(0, 0, 1); //fix LED issue
970 | int i = 0;
971 | while(1){
972 | if(data[i] == 0x00){
973 | extension[i] = 0x00;
974 | break;
975 | }
976 | extension[i] = data[i];
977 | i++;
978 | }
979 | Serial.println(F("Changing file extension to "));
980 | Serial.print(extension);
981 | Serial.println();
982 | nonce = EEPROM.read(5);
983 | EEPROM.write(5, nonce + 1);
984 | }
985 | delay(10);
986 | cli();
987 | }
988 | else if(byteNum < disk.fileSize()){
989 | sei();
990 | byteNum *= 532;
991 | disk.seekSet(byteNum);
992 | disk.write(data, 532);
993 | disk.flush();
994 | delay(1);
995 | cli();
996 | }
997 | else{
998 | sei();
999 | Serial.println(F("Error: Requested block is out of range!"));
1000 | delay(5);
1001 | cli();
1002 | }
1003 | clearBSY(); //if things look good, raise BSY
1004 | DDRL = B11111111; //and set the bus into output mode
1005 | currentTime = 0;
1006 | index = 0; //zero out the index
1007 | PORTL = 0x00; //and put the first status byte on the bus
1008 | while((PINC & B00000001) == B00000001){ //do this for the remaining three status bytes
1009 | currentState = PINC & B00001000;
1010 | if(currentState == B00000000 and prevState == B00001000){ //if we're on the falling edge of the strobe, send the next status byte and increment the index
1011 | PORTL = 0x00;
1012 | index += 1;
1013 | }
1014 | currentTime++;
1015 | if(currentTime >= timeout){
1016 | return;
1017 | }
1018 | prevState = currentState;
1019 | }
1020 | if(halt == true){
1021 | while(1);
1022 | }
1023 | }
1024 |
1025 | void initPins(){
1026 | DDRC = B11000010; //set CHK, OCD, and BSY to outputs and the rest of the control signals to inputs
1027 | clearBSY(); //make sure that BSY is raised
1028 | DDRL = B00000000; //set the bus to input mode
1029 | setPCHK();
1030 | setPOCD(); //and lower CHK and OCD (is this really necessary?)
1031 | }
1032 |
1033 | void sendData(byte parallelBits){ //makes it more user-friendly to put data on the bus
1034 | DDRL = B11111111; //set the bus to output mode
1035 | PORTL = parallelBits; //and write the parallelBits to the bus
1036 | }
1037 |
1038 | byte receiveData(){ //makes it more user-friendly to receive data
1039 | DDRL = B00000000; //set the bus to input mode
1040 | return PINL; //and return whatever's on the bus
1041 | }
1042 |
1043 | void updateSpareTable(){
1044 | if(disk.fileSize() == 5175296){
1045 | spareTable[8] = 0x20; //Name
1046 | spareTable[9] = 0x20;
1047 | spareTable[10] = 0x20;
1048 | spareTable[15] = 0x00; //Device Number
1049 | spareTable[16] = 0x03; //ROM Revision
1050 | spareTable[17] = 0x98;
1051 | spareTable[18] = 0x00; //Drive Size
1052 | spareTable[19] = 0x26;
1053 | spareTable[20] = 0x00;
1054 | Serial.println(F("Switching to the 5MB ProFile spare table."));
1055 | }
1056 | else if(disk.fileSize() == 10350592){
1057 | spareTable[8] = 0x31; //Name
1058 | spareTable[9] = 0x30;
1059 | spareTable[10] = 0x4D;
1060 | spareTable[15] = 0x10; //Device Number
1061 | spareTable[16] = 0x04; //ROM Revision
1062 | spareTable[17] = 0x04;
1063 | spareTable[18] = 0x00; //Drive Size
1064 | spareTable[19] = 0x4C;
1065 | spareTable[20] = 0x00;
1066 | Serial.println(F("Switching to the 10MB ProFile spare table."));
1067 | }
1068 | else{
1069 | spareTable[8] = 0x20; //Name
1070 | spareTable[9] = 0x20;
1071 | spareTable[10] = 0x20;
1072 | spareTable[15] = 0x00; //Device Number
1073 | spareTable[16] = 0x03; //ROM Revision
1074 | spareTable[17] = 0x98;
1075 | spareTable[18] = (disk.fileSize()/532) >> 16; //Drive Size
1076 | spareTable[19] = (disk.fileSize()/532) >> 8;
1077 | spareTable[20] = (disk.fileSize()/532);
1078 | Serial.print(F("Switching to a custom spare table for drive of size "));
1079 | printDataNoSpace(spareTable[18]);
1080 | printDataNoSpace(spareTable[19]);
1081 | printDataNoSpace(spareTable[20]);
1082 | Serial.println(F(" blocks."));
1083 | }
1084 | }
1085 |
1086 |
1087 | //all of these functions just make it easier to set, clear, and read the control signals for the drive
1088 |
1089 | void setBSY(){
1090 | setLEDColor(0, 0, 0);
1091 | PORTC = PORTC & B11111101;
1092 | }
1093 |
1094 | void clearBSY(){
1095 | setLEDColor(0, 1, 0);
1096 | PORTC = PORTC | B00000010;
1097 | }
1098 |
1099 | void setPARITY(){
1100 | PORTC = PORTC & B11011111;
1101 | }
1102 |
1103 | void clearPARITY(){
1104 | PORTC = PORTC | B00100000;
1105 | }
1106 | void setPCHK(){
1107 | PORTC = PORTC & B10111111;
1108 | }
1109 |
1110 | void clearPCHK(){
1111 | PORTC = PORTC | B01000000;
1112 | }
1113 |
1114 | void setPOCD(){
1115 | PORTC = PORTC & B01111111;
1116 | }
1117 |
1118 | void clearPOCD(){
1119 | PORTC = PORTC | B10000000;
1120 | }
1121 |
1122 | bool readCMD(){
1123 | return bitRead(PINC, 0);
1124 | }
1125 |
1126 | bool readRW(){
1127 | return bitRead(PINC, 2);
1128 | }
1129 |
1130 | bool readSTRB(){
1131 | return bitRead(PINC, 3);
1132 | }
1133 |
1134 | bool readPRES(){
1135 | return bitRead(PINC, 4);
1136 | }
1137 |
1138 | bool readParity(){
1139 | return bitRead(PINC, 5);
1140 | }
1141 |
1142 | void setLEDColor(bool r, bool g, bool b){
1143 | digitalWrite(red, r);
1144 | digitalWrite(green, g);
1145 | digitalWrite(blue, b);
1146 | }
1147 |
1148 |
1149 | //the following functions just format hex data for printing over serial and are only used for debugging
1150 |
1151 |
1152 | void printDataNoSpace(byte data){
1153 | Serial.print(data>>4, HEX);
1154 | Serial.print(data&0x0F, HEX);
1155 | }
1156 |
1157 | void printDataSpace(byte data){
1158 | Serial.print(data>>4, HEX);
1159 | Serial.print(data&0x0F, HEX);
1160 | Serial.print(F(" "));
1161 | }
1162 |
1163 | void printRawData(){
1164 | Serial.println(F("Raw Data:"));
1165 | Serial.print(F("0000: "));
1166 | for(int i = 0; i <= 531; i++){
1167 | Serial.print(data[i]>>4, HEX);
1168 | Serial.print(data[i]&0x0F, HEX);
1169 | Serial.print(F(" "));
1170 | if((i + 1) % 8 == 0 and (i + 1) % 16 != 0){
1171 | Serial.print(F(" "));
1172 | }
1173 | if((i + 1) % 16 == 0){
1174 | Serial.print(F(" "));
1175 | for(int j = i - 15; j <= i; j++){
1176 | if(data[j] <= 0x1F){
1177 | Serial.print(F("."));
1178 | }
1179 | else{
1180 | Serial.write(data[j]);
1181 | }
1182 | }
1183 | Serial.println();
1184 | if((i + 1) < 0x100){
1185 | Serial.print(F("00"));
1186 | }
1187 | if((i + 1) >= 0x100){
1188 | Serial.print(F("0"));
1189 | }
1190 | Serial.print((i + 1), HEX);
1191 | Serial.print(F(": "));
1192 | }
1193 | }
1194 | Serial.print(" ");
1195 | for(int i = 528; i < 532; i++){
1196 | if(data[i] <= 0x1F){
1197 | Serial.print(F("."));
1198 | }
1199 | else{
1200 | Serial.write(data[i]);
1201 | }
1202 | }
1203 | Serial.println();
1204 | }
1205 |
--------------------------------------------------------------------------------
/proFileTester.ino:
--------------------------------------------------------------------------------
1 | //***********************************************************************************
2 | //* ArduinoFile ProFile Diagnostic Software v1.0 *
3 | //* By: Alex Anderson-McLeod *
4 | //* Email address: alexelectronicsguy@gmail.com *
5 | //***********************************************************************************
6 |
7 |
8 |
9 |
10 | byte packetNum = 0x01;
11 | bool testMenu = false;
12 | bool confirmOperation = false;
13 | byte notPacketNum = 0xFE;
14 | bool failed = false;
15 | bool repeat = false;
16 | byte SOH = 0x01;
17 | byte STX = 0x02;
18 | byte ACK = 0x06;
19 | byte NAK = 0x15;
20 | byte EOT = 0x04;
21 | byte CAN = 0x18;
22 | byte driveSize[3];
23 | byte padding = 0x1A; //0x1A
24 | byte inData;
25 | byte data[1064];
26 | byte crcArray[1024];
27 | uint16_t backupErrors = 0;
28 | uint32_t currentIndex;
29 | uint32_t writeIndex;
30 | uint32_t dataIndex = 0;
31 | byte lowCRC;
32 | byte highCRC;
33 | byte checksum = 0x00;
34 | uint16_t CRC;
35 | uint16_t actualCRC;
36 | uint32_t currentBlock = 0;
37 | uint32_t totalBlocks = 0;
38 | byte ackStatus = 0x02;
39 | byte rawData[1029];
40 | long int start;
41 | //byte dataStripped[1024];
42 | bool done = false;
43 | //File xModemData;
44 | uint16_t highestBlock = 0;
45 |
46 |
47 | byte status[5];
48 | //byte data[532];
49 | char input[6];
50 | uint8_t serialBytes[50];
51 | byte address[3];
52 | //byte parity[532];
53 |
54 |
55 | char *statusMessages[3][8] = {{"Operation Unsuccessful", "This bit is unused", "Timeout Error", "CRC Error", "Seek Error - Cannot Read Header", "Resend Data", "More Than 532 Bytes Sent", "Host Acknowledge Was Not 0x55"},
56 | {"This bit is unused", "Seek Error - Wrong Track", "Sparing Occured", "Cannot Read Spare Table", "Bad Block Table Full", "This bit is unused", "Spare Table Full", "Seek Error - Cannot Read Error"},
57 | {"This bit is unused", "This bit is unused", "This bit is unused", "This bit is unused", "This bit is unused", "This bit is unused", "Invalid Block Number", "Drive Has Been Reset"}};
58 |
59 | char acceptableHex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f'};
60 |
61 |
62 | byte correctInfo[532] = {0x00, 0x00, 0x00, 0x22, 0xAA, 0xAA, 0x82, 0x00, 0xFF, 0xFF, 0xFF, 0x55, 0x00, 0x00, 0xFF, 0xFF,
63 | 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0xFA, 0x00, 0x16, 0xAA, 0xAA, 0x08, 0x50, 0x22, 0x4A, 0x2C, 0xF8,
64 | 0x28, 0x76, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x38, 0x02, 0xA8,
65 | 0xE2, 0x88, 0x21, 0xC0, 0x02, 0x1C, 0x22, 0x40, 0x24, 0x7C, 0x00, 0x02, 0x00, 0x00, 0x47, 0xFA,
66 | 0xFF, 0xD4, 0xD7, 0xFC, 0x00, 0x00, 0x02, 0x00, 0xB5, 0xCB, 0x6E, 0x04, 0x22, 0xDA, 0x60, 0xF8,
67 | 0x49, 0xFA, 0x00, 0x0C, 0xD9, 0xC0, 0x99, 0xFC, 0x00, 0x02, 0x00, 0x00, 0x4E, 0xD4, 0x43, 0xFA,
68 | 0xFF, 0xC8, 0x22, 0x88, 0x43, 0xFA, 0xFF, 0xBE, 0x22, 0x97, 0xD1, 0x91, 0x04, 0x91, 0x00, 0x02,
69 | 0x00, 0x00, 0x2E, 0x40, 0x9E, 0xFC, 0x01, 0x00, 0x2E, 0x3C, 0x00, 0xFF, 0xFF, 0xFF, 0x2C, 0x7C,
70 | 0x00, 0x00, 0x08, 0x00, 0x31, 0xFC, 0x00, 0x01, 0x02, 0x2E, 0x61, 0x00, 0x00, 0xC0, 0x10, 0x2E,
71 | 0x02, 0x0E, 0x67, 0x06, 0x31, 0xFC, 0x00, 0x03, 0x02, 0x2E, 0x4D, 0xFA, 0xFF, 0x78, 0x20, 0x4E,
72 | 0xDD, 0xFC, 0x00, 0x00, 0x02, 0x00, 0xD0, 0xFC, 0x00, 0x0A, 0x3C, 0x10, 0x04, 0x46, 0x02, 0x00,
73 | 0x7E, 0x08, 0x31, 0xC7, 0x02, 0x16, 0x52, 0x47, 0x0C, 0x78, 0x00, 0x03, 0x02, 0x2E, 0x66, 0x06,
74 | 0x9D, 0xFC, 0x00, 0x00, 0x00, 0x14, 0x3C, 0x06, 0x6B, 0x00, 0x01, 0x18, 0x61, 0x38, 0xDC, 0xFC,
75 | 0x02, 0x00, 0x04, 0x46, 0x02, 0x00, 0x52, 0x47, 0x4E, 0xFA, 0xFF, 0xEC, 0x2C, 0x00, 0x24, 0x7C,
76 | 0x00, 0xFC, 0xD9, 0x01, 0x08, 0x12, 0x00, 0x01, 0x67, 0xFA, 0x20, 0x38, 0x02, 0xA8, 0x04, 0x80,
77 | 0x00, 0x00, 0x80, 0x00, 0x21, 0xC0, 0x01, 0x10, 0x95, 0xCA, 0x20, 0x0B, 0x22, 0x00, 0x97, 0xCB,
78 | 0x4E, 0xF9, 0x00, 0xFE, 0x00, 0x84, 0x61, 0x44, 0x30, 0x2E, 0x02, 0x06, 0x0C, 0x78, 0x00, 0x03,
79 | 0x02, 0x2E, 0x66, 0x04, 0x30, 0x2E, 0x02, 0x04, 0x02, 0x40, 0x80, 0x00, 0x67, 0x2C, 0x42, 0x80,
80 | 0x22, 0x4E, 0x34, 0x3C, 0x02, 0x13, 0xE4, 0x4A, 0x22, 0x19, 0xB3, 0x80, 0x51, 0xCA, 0xFF, 0xFA,
81 | 0x32, 0x00, 0xE0, 0x88, 0xE0, 0x88, 0xB3, 0x40, 0x12, 0x00, 0xE0, 0x48, 0xB3, 0x00, 0x67, 0x0A,
82 | 0x26, 0x7C, 0x00, 0x00, 0x29, 0xE6, 0x70, 0xFF, 0x60, 0x92, 0x4E, 0x75, 0x22, 0x07, 0x24, 0x4E,
83 | 0x22, 0x4E, 0x0C, 0x78, 0x00, 0x03, 0x02, 0x2E, 0x66, 0x06, 0xD4, 0xFC, 0x00, 0x14, 0x60, 0x04,
84 | 0xD2, 0xFC, 0x02, 0x00, 0x36, 0x3C, 0x00, 0x0A, 0x38, 0x3C, 0x00, 0x03, 0x24, 0x3C, 0x00, 0x90,
85 | 0x00, 0x00, 0x0C, 0x81, 0x00, 0xFF, 0xFF, 0xFF, 0x67, 0x0A, 0x0C, 0x78, 0x00, 0x03, 0x02, 0x2E,
86 | 0x67, 0x02, 0x61, 0x34, 0x48, 0xE7, 0x03, 0x62, 0x0C, 0x38, 0x00, 0x02, 0x01, 0xB3, 0x6E, 0x08,
87 | 0x4E, 0xB9, 0x00, 0xFE, 0x00, 0x90, 0x60, 0x0E, 0x41, 0xFA, 0xFE, 0x8A, 0x26, 0x50, 0x41, 0xFA,
88 | 0xFE, 0x88, 0x20, 0x50, 0x4E, 0x93, 0x4C, 0xDF, 0x46, 0xC0, 0x64, 0x0A, 0x26, 0x7C, 0x00, 0x00,
89 | 0x29, 0xE6, 0x60, 0x00, 0xFF, 0x28, 0x4E, 0x75, 0x2F, 0x00, 0x2F, 0x01, 0x70, 0xF0, 0xC0, 0x01,
90 | 0x02, 0x41, 0x00, 0x0F, 0xD0, 0x3B, 0x10, 0x0C, 0x1F, 0x40, 0x00, 0x03, 0x22, 0x1F, 0x20, 0x1F,
91 | 0x4E, 0x75, 0x00, 0x05, 0x0A, 0x0F, 0x04, 0x09, 0x0E, 0x03, 0x08, 0x0D, 0x02, 0x07, 0x0C, 0x01,
92 | 0x06, 0x0B, 0x20, 0x7C, 0x00, 0x00, 0x01, 0xB3, 0x22, 0x7C, 0x00, 0xFC, 0xC0, 0x31, 0x10, 0x11,
93 | 0x6A, 0x08, 0x10, 0x10, 0x66, 0x04, 0x10, 0xBC, 0x00, 0x02, 0x41, 0xFA, 0xFE, 0x18, 0xD0, 0xFC,
94 | 0x00, 0x0E, 0x31, 0xD0, 0x02, 0x10, 0x42, 0xA7, 0x42, 0xA7, 0x42, 0xA7, 0x41, 0xFA, 0xFE, 0x06,
95 | 0xD0, 0xFC, 0x00, 0x08};
96 |
97 |
98 |
99 |
100 |
101 | byte num = 0x00;
102 |
103 |
104 | byte read = 0x00;
105 | byte write = 0x01;
106 | byte writeVerify = 0x02;
107 | byte writeForceSparing = 0x03;
108 |
109 | byte defaultRetries = 0x64;
110 | byte defaultSpareThreshold = 0x14;
111 |
112 | String command;
113 | String inputCommand;
114 | String name;
115 | char userInput;
116 |
117 | const int red = 22;
118 | const int green = 23;
119 | const int blue = 24;
120 |
121 | void setup(){
122 | pinMode(red, OUTPUT);
123 | pinMode(green, OUTPUT);
124 | pinMode(blue, OUTPUT);
125 | Serial.begin(115200);
126 | Serial.setTimeout(10);
127 | setParallelDir('IN');
128 | DDRC = B00011101;
129 | initPins();
130 | mainMenu();
131 | }
132 |
133 |
134 |
135 |
136 | bool profileRead(byte address0, byte address1, byte address2, byte retryCount=defaultRetries, byte spareThreshold=defaultSpareThreshold){
137 | setLEDColor(0, 0, 0);
138 | bool handshakeSuccessful = profileHandshake();
139 | byte commandResponse = sendCommandBytes(read, address0, address1, address2, retryCount, spareThreshold);
140 | readStatusBytes();
141 | readData();
142 | if(handshakeSuccessful == 0){
143 | Serial.println(F("Handshake failed!"));
144 | setLEDColor(1, 0, 0);
145 | return false;
146 | }
147 | if(commandResponse != 0x02){
148 | Serial.println(F("Command confirmation failed!"));
149 | setLEDColor(1, 0, 0);
150 | return false;
151 | }
152 | setLEDColor(1, 1, 0);
153 | return true;
154 | }
155 |
156 |
157 | bool profileWrite(byte address0, byte address1, byte address2, byte retryCount=defaultRetries, byte spareThreshold=defaultSpareThreshold){
158 | setLEDColor(0, 0, 0);
159 | bool handshakeSuccessful = profileHandshake();
160 | byte commandResponse = sendCommandBytes(write, address0, address1, address2, retryCount, spareThreshold);
161 | writeData();
162 | readStatusBytes();
163 | if(handshakeSuccessful == 0){
164 | Serial.println(F("Handshake failed!"));
165 | setLEDColor(1, 0, 0);
166 | return false;
167 | }
168 | if(commandResponse != 0x03){
169 | Serial.println(F("Command confirmation failed!"));
170 | setLEDColor(1, 0, 0);
171 | return false;
172 | }
173 | setLEDColor(1, 1, 0);
174 | return true;
175 | }
176 |
177 |
178 | void mainMenu(){
179 | clearScreen();
180 | setLEDColor(0, 1, 0);
181 | Serial.println(F("Welcome to the ArduinoFile!"));
182 | Serial.println(F("By: Alex Anderson-McLeod"));
183 | Serial.println();
184 | Serial.println(F("1 - Reset Drive"));
185 | Serial.println(F("2 - Read Spare Table"));
186 | Serial.println(F("3 - Read Block"));
187 | Serial.println(F("4 - Backup Entire Drive"));
188 | Serial.println(F("5 - Write Buffer to Block"));
189 | Serial.println(F("6 - Restore Drive From Backup"));
190 | Serial.println(F("7 - Write Zeros to Drive"));
191 | Serial.println(F("8 - Low-Level Format"));
192 | Serial.println(F("9 - Drive Tests"));
193 | Serial.println();
194 | Serial.println(F("Note: All numbers are in hex unless otherwise specified."));
195 | Serial.print(F("Please select an option: "));
196 | }
197 |
198 | void testSubMenu(){
199 | clearScreen();
200 | setLEDColor(0, 1, 0);
201 | Serial.println(F("Welcome to the ArduinoFile!"));
202 | Serial.println(F("By: Alex Anderson-McLeod"));
203 | Serial.println();
204 | Serial.println(F("Drive Tests"));
205 | Serial.println(F("1 - Sequential Read"));
206 | Serial.println(F("2 - Sequential Write"));
207 | Serial.println(F("3 - Random Read"));
208 | Serial.println(F("4 - Random Write"));
209 | Serial.println(F("5 - Butterfly Read"));
210 | Serial.println(F("6 - Read-Write-Read"));
211 | Serial.println(F("7 - Return to Main Menu"));
212 | Serial.println();
213 | Serial.println(F("Note: All numbers are in hex unless otherwise specified."));
214 | Serial.print(F("Please select an option: "));
215 | }
216 |
217 | int readDelay = 1;
218 |
219 | void clearScreen(){
220 | Serial.write(27);
221 | Serial.print(F("[2J"));
222 | Serial.write(27);
223 | Serial.print(F("[H"));
224 | }
225 |
226 | void flushInput(){
227 | while(Serial.available()){
228 | char x = Serial.read();
229 | }
230 | }
231 |
232 |
233 | void printStatus(){
234 | bool statusGood = 1;
235 | Serial.print(F("Drive Status: "));
236 | for(int i = 0; i < 4; i++){
237 | Serial.print(status[i]>>4, HEX);
238 | Serial.print(status[i]&0x0F, HEX);
239 | Serial.print(F(" "));
240 | }
241 | Serial.println();
242 | Serial.println();
243 | Serial.println(F("Status Interpretation:"));
244 | for(int i = 0; i < 3; i++){
245 | for(int j = 0; j < 8; j++){
246 | if(bitRead(status[i], j) == 1){
247 | statusGood = 0;
248 | Serial.println(statusMessages[i][j]);
249 | }
250 | }
251 | }
252 | if(statusGood == 1){
253 | Serial.println(F("Status looks good!"));
254 | }
255 | if(status[3] | B00000000 != B00000000){
256 | Serial.print(F("Number of Retries: "));
257 | Serial.print(status[3]>>4, HEX);
258 | Serial.print(status[3]&0x0F, HEX);
259 | Serial.println();
260 | }
261 | Serial.println();
262 | }
263 |
264 | void loop() {
265 | if(Serial.available()){
266 | command = Serial.readStringUntil("\r");
267 | command.trim();
268 | if(command.equals("1") and testMenu == false){
269 | setLEDColor(0, 0, 0);
270 | clearScreen();
271 | Serial.println(F("Resetting drive..."));
272 | resetDrive();
273 | Serial.print(F("Reset successful! Press return to continue..."));
274 | setLEDColor(0, 1, 0);
275 | flushInput();
276 | while(!Serial.available());
277 | mainMenu();
278 | flushInput();
279 | }
280 | if(command.equals("2") and testMenu == false){
281 | clearScreen();
282 | Serial.println(F("Reading spare table..."));
283 | Serial.println(F("Command: 00 FF FF FF 64 14"));
284 | bool readSuccess = profileRead(0xFF, 0xFF, 0xFF);
285 | if(readSuccess == 0){
286 | Serial.println();
287 | Serial.println(F("WARNING: Errors were encountered during the read operation. The following data may be incorrect."));
288 | }
289 | /*for(int i = 0; i < 532; i++){
290 | if(checkParity(data[i], parity[i]) == false){
291 | Serial.println("The parity was all screwed up!");
292 | printDataNoSpace(data[i]);
293 | Serial.println();
294 | }
295 | }*/
296 | Serial.println();
297 | printRawData();
298 | Serial.println();
299 | //printRawParity();
300 | //Serial.println();
301 | printStatus();
302 | Serial.println(F("Data Analysis:"));
303 | Serial.print(F("Device Name: "));
304 | for(int i = 0; i < 13; i++){
305 | Serial.write(data[i]);
306 | }
307 | Serial.println();
308 | Serial.print(F("Device Number: "));
309 | for(int i = 13; i < 16; i++){
310 | Serial.print(data[i]>>4, HEX);
311 | Serial.print(data[i]&0x0F, HEX);
312 | }
313 | if(data[13] == 0x00 and data[14] == 0x00 and data[15] == 0x00){
314 | Serial.println(F(" (5MB ProFile)"));
315 | }
316 | else if(data[13] == 0x00 and data[14] == 0x00 and data[15] == 0x10){
317 | Serial.println(F(" (10MB ProFile)"));
318 | }
319 | else if(data[13] == 0x00 and data[14] == 0x01 and data[15] == 0x00){
320 | Serial.println(F(" (10MB Widget)"));
321 | }
322 | else{
323 | Serial.println(F(" (Unknown Drive Type)"));
324 | }
325 | Serial.print(F("Firmware Revision: "));
326 | Serial.print(data[16], HEX);
327 | Serial.print(F("."));
328 | Serial.print(data[17]>>4, HEX);
329 | Serial.println(data[17]&0x0F, HEX);
330 | Serial.print(F("Total Blocks: "));
331 | for(int i = 18; i < 21; i++){
332 | Serial.print(data[i]>>4, HEX);
333 | Serial.print(data[i]&0x0F, HEX);
334 | }
335 | Serial.println();
336 | Serial.print(F("Bytes Per Block: "));
337 | for(int i = 21; i < 23; i++){
338 | Serial.print(data[i]>>4, HEX);
339 | Serial.print(data[i]&0x0F, HEX);
340 | }
341 | Serial.println();
342 | Serial.print(F("Total Spares: "));
343 | Serial.print(data[23]>>4, HEX);
344 | Serial.print(data[23]&0x0F, HEX);
345 | Serial.println();
346 | Serial.print(F("Spares Allocated: "));
347 | Serial.print(data[24]>>4, HEX);
348 | Serial.print(data[24]&0x0F, HEX);
349 | Serial.println();
350 | Serial.print(F("Bad Blocks: "));
351 | Serial.print(data[25]>>4, HEX);
352 | Serial.print(data[25]&0x0F, HEX);
353 | Serial.println();
354 | Serial.println();
355 | setLEDColor(0, 1, 0);
356 | Serial.print(F("Press return to continue..."));
357 | flushInput();
358 | while(!Serial.available());
359 | mainMenu();
360 | flushInput();
361 | }
362 | if(command.equals("3") and testMenu == false){
363 | clearScreen();
364 | setLEDColor(0, 0, 1);
365 | Serial.print(F("Please enter the block number that you want to read: "));
366 | while(1){
367 | if(readSerialValue(6) == true){
368 | break;
369 | }
370 | else{
371 | Serial.print(F("Please enter the block number that you want to read: "));
372 | }
373 | }
374 | for(int i = 0; i < 3; i++){
375 | address[i] = serialBytes[i];
376 | }
377 | int retries = defaultRetries;
378 | int spare = defaultSpareThreshold;
379 | Serial.print(F("Use the default retry count of "));
380 | printDataNoSpace(retries);
381 | Serial.print(F(" (return for yes, 'n' for no)? "));
382 | while(1){
383 | if(Serial.available()) {
384 | delay(50);
385 | userInput = Serial.read();
386 | flushInput();
387 | if(userInput == 'n'){
388 | Serial.print(F("Please enter the desired retry count: "));
389 | while(1){
390 | if(readSerialValue(2) == true){
391 | retries = serialBytes[0];
392 | break;
393 | }
394 | else{
395 | Serial.print(F("Please enter the desired retry count: "));
396 | }
397 | }
398 | break;
399 | }
400 | else if(userInput == '\r'){
401 | break;
402 | }
403 | else{
404 | Serial.print(F("Use the default retry count of "));
405 | printDataNoSpace(retries);
406 | Serial.print(F(" (return for yes, 'n' for no)? "));
407 | }
408 | }
409 | }
410 | Serial.print(F("Use the default spare threshold of "));
411 | printDataNoSpace(spare);
412 | Serial.print(F(" (return for yes, 'n' for no)? "));
413 | while(1){
414 | if(Serial.available()) {
415 | delay(50);
416 | userInput = Serial.read();
417 | flushInput();
418 | if(userInput == 'n'){
419 | Serial.print(F("Please enter the desired spare threshold: "));
420 | while(1){
421 | if(readSerialValue(2) == true){
422 | spare = serialBytes[0];
423 | break;
424 | }
425 | else{
426 | Serial.print(F("Please enter the desired spare threshold: "));
427 | }
428 | }
429 | break;
430 | }
431 | else if(userInput == '\r'){
432 | break;
433 | }
434 | else{
435 | Serial.print(F("Use the default spare threshold of "));
436 | printDataNoSpace(spare);
437 | Serial.print(F(" (return for yes, 'n' for no)? "));
438 | }
439 | }
440 | }
441 | Serial.println();
442 | Serial.print(F("Reading block "));
443 | for(int i = 0; i < 3; i++){
444 | printDataNoSpace(address[i]);
445 | }
446 | Serial.println(F("..."));
447 | Serial.print(F("Command: 00 "));
448 | for(int i = 0; i < 3; i++){
449 | printDataSpace(address[i]);
450 | }
451 | printDataSpace(retries);
452 | printDataNoSpace(spare);
453 | Serial.println();
454 | Serial.println();
455 | bool readSuccess = profileRead(address[0], address[1], address[2], retries, spare);
456 | if(readSuccess == 0){
457 | Serial.println(F("WARNING: Errors were encountered during the read operation. The following data may be incorrect."));
458 | Serial.println();
459 | }
460 | printRawData();
461 | Serial.println();
462 | printStatus();
463 | Serial.println();
464 |
465 | //2 and 8 places of MSB are weird
466 |
467 | /*for(int i = 0; i < 532; i++){
468 | if(data[i] != correctInfo[i]){
469 | data[i] = correctInfo[i];
470 | }
471 | else{
472 | data[i] = 0xFF;
473 | }
474 | }
475 |
476 | printRawData();
477 | Serial.println();*/
478 |
479 |
480 |
481 | Serial.print(F("Press return to continue..."));
482 | setLEDColor(0, 1, 0);
483 | flushInput();
484 | while(!Serial.available());
485 | mainMenu();
486 | flushInput();
487 | }
488 | if(command.equals("4") and testMenu == false){
489 | clearScreen();
490 | backupErrors = 0;
491 | getDriveType();
492 | setLEDColor(0, 0, 1);
493 | Serial.println();
494 | Serial.println(F("Start XMODEM receiver now..."));
495 | delay(2000);
496 | packetNum = 0x01;
497 | uint16_t highestBlock = 0;
498 | notPacketNum = 0xFE;
499 | uint32_t totalBlocks = 0;
500 | currentIndex = 0;
501 | currentBlock = 0;
502 | totalBlocks = (driveSize[0]<<16 | driveSize[1]<<8 | driveSize[2]);
503 | driveSize[0] = currentBlock >> 16;
504 | driveSize[1] = currentBlock >> 8;
505 | driveSize[2] = currentBlock;
506 | byte successPacket;
507 | bool failure = false;
508 | if(startTransmission() == false){
509 | failure = true;
510 | }
511 | while(failure == false){
512 | startNewPacket();
513 | for(int i = 0; i < 1024; i++){
514 | if(currentIndex % 1064 == 0 and currentBlock != totalBlocks + 2){
515 | driveSize[0] = currentBlock >> 16;
516 | driveSize[1] = currentBlock >> 8;
517 | driveSize[2] = currentBlock;
518 | readTwoBlocks(driveSize[0], driveSize[1], driveSize[2]);
519 | currentBlock += 2;
520 | }
521 | if(currentIndex % 1064 == 0 and currentBlock == totalBlocks + 2){
522 | for(int j = 0; j < 1064; j++){
523 | data[j] = padding;
524 | }
525 | currentBlock += 2;
526 | }
527 | crcArray[i] = data[currentIndex % 1064];
528 | Serial.write(data[currentIndex % 1064]);
529 | currentIndex += 1;
530 | }
531 | successPacket = finishPacket();
532 | while(successPacket == 0x01){
533 | startNewPacket();
534 | for(int i = 0; i < 1024; i++){
535 | Serial.write(crcArray[i]);
536 | }
537 | successPacket = finishPacket();
538 | }
539 | if(successPacket == 0x00){
540 | failure = true;
541 | }
542 | if(currentBlock >= totalBlocks + 2){
543 | break;
544 | }
545 | }
546 | if(failure == false){
547 | finishTransmission();
548 | delay(2000);
549 | if(backupErrors != 0){
550 | setLEDColor(1, 0, 0);
551 | Serial.print(F("Warning: "));
552 | Serial.print(backupErrors);
553 | Serial.print(F(" disk errors were encountered during the operation!"));
554 | Serial.println();
555 | }
556 | else{
557 | setLEDColor(0, 1, 0);
558 | }
559 | Serial.println();
560 | Serial.println(F("XMODEM transfer complete!"));
561 | }
562 | else{
563 | setLEDColor(1, 0, 0);
564 | Serial.println();
565 | Serial.println(F("Transfer timed out!"));
566 | }
567 | while(Serial.available()){
568 | byte data = Serial.read();
569 | }
570 | Serial.print(F("Press return to continue..."));
571 | flushInput();
572 | while(!Serial.available());
573 | mainMenu();
574 | flushInput();
575 | //}
576 | }
577 | if(command.equals("5") and testMenu == false){
578 | clearScreen();
579 | setLEDColor(0, 0, 1);
580 | Serial.print(F("Please enter the block number that you want to write to: "));
581 | while(1){
582 | if(readSerialValue(6) == true){
583 | break;
584 | }
585 | else{
586 | Serial.print(F("Please enter the block number that you want to write to: "));
587 | }
588 | }
589 | for(int i = 0; i < 3; i++){
590 | address[i] = serialBytes[i];
591 | }
592 | Serial.println();
593 | Serial.print(F("Writing the buffer to block "));
594 | for(int i = 0; i < 3; i++){
595 | printDataNoSpace(address[i]);
596 | }
597 | Serial.println(F("..."));
598 | Serial.print(F("Command: 01 "));
599 | for(int i = 0; i < 3; i++){
600 | printDataSpace(address[i]);
601 | }
602 | printDataSpace(defaultRetries);
603 | printDataNoSpace(defaultSpareThreshold);
604 | Serial.println();
605 | Serial.println();
606 | bool writeSuccess = profileWrite(address[0], address[1], address[2]);
607 | if(writeSuccess == 0){
608 | Serial.println(F("WARNING: Errors were encountered during the write operation. The data may have been written incorrectly."));
609 | Serial.println();
610 | }
611 | printStatus();
612 | Serial.println();
613 | setLEDColor(0, 1, 0);
614 | Serial.print(F("Press return to continue..."));
615 | flushInput();
616 | while(!Serial.available());
617 | mainMenu();
618 | flushInput();
619 | }
620 |
621 | if(command.equals("6") and testMenu == false){
622 | setLEDColor(0, 0, 1);
623 | clearScreen();
624 | confirm();
625 | uint16_t highestBlock = 0;
626 | backupErrors = 0;
627 | failed = false;
628 | packetNum = 0x01;
629 | notPacketNum = 0xFE;
630 | currentIndex = 0;
631 | dataIndex = 0;
632 | writeIndex = 0;
633 | actualCRC = 0;
634 | CRC = 0;
635 | currentBlock = 0;
636 | uint32_t totalBlocks = 0;
637 | done = false;
638 | byte writeData[1064]; //THIS IS DATA
639 | ackStatus = 0x02;
640 | failed = false;
641 | if(confirmOperation == true){
642 | getDriveType();
643 |
644 | Serial.println();
645 | /*Serial.print("Initializing SD card...");
646 | if (!SD.begin(53)) {
647 | Serial.println("initialization failed!");
648 | while (1);
649 | }
650 | Serial.println("initialization done.");
651 | xModemData = SD.open("rx5.bin", FILE_WRITE);*/
652 |
653 | highestBlock = (driveSize[0]<<16) | (driveSize[1]<<8) | (driveSize[2]);
654 | Serial.println(F("Start XMODEM sender when ready..."));
655 | while(1){
656 | for(writeIndex = 0; writeIndex < 1064; writeIndex++){
657 | if(currentIndex % 1024 == 0){
658 | receivePacket();
659 | }
660 | if(done == true){
661 | delay(2000);
662 | break;
663 | }
664 | data[writeIndex] = crcArray[currentIndex % 1024];
665 | currentIndex += 1;
666 | }
667 | if(done == true){
668 | break;
669 | }
670 | /*for(int i = 0; i < 1064; i++){
671 | xModemData.write(data[i]);
672 | }*/
673 | driveSize[0] = currentBlock >> 16;
674 | driveSize[1] = currentBlock >> 8;
675 | driveSize[2] = currentBlock;
676 | writeTwoBlocks(driveSize[2], driveSize[1], driveSize[0]);
677 | currentBlock += 2;
678 | }
679 | flushInput();
680 | delay(500);
681 | flushInput();
682 | if((currentIndex * 2) / 1064 != highestBlock and failed == false){
683 | setLEDColor(1, 0, 0);
684 | Serial.print(F("File Size Mismatch: Drive size is "));
685 | printDataNoSpace((highestBlock - 1) >> 16);
686 | printDataNoSpace((highestBlock - 1) >> 8);
687 | printDataNoSpace(highestBlock - 1);
688 | Serial.print(F(" blocks, but received file was "));
689 | printDataNoSpace(((currentIndex * 2) / 1064) >> 16);
690 | printDataNoSpace(((currentIndex * 2) / 1064) >> 8);
691 | printDataNoSpace(((currentIndex * 2) / 1064));
692 | Serial.println(F(" blocks!"));
693 | }
694 | Serial.println();
695 | if(backupErrors != 0){
696 | setLEDColor(1, 0, 0);
697 | Serial.print(F("Warning: "));
698 | Serial.print(backupErrors);
699 | Serial.print(F(" disk errors were encountered during the operation!"));
700 | Serial.println();
701 | }
702 | if(failed == false){
703 | setLEDColor(0, 1, 0);
704 | Serial.println(F("Drive restore succeeded!"));
705 | }
706 | else{
707 | setLEDColor(1, 0, 0);
708 | Serial.println(F("Drive restore failed!"));
709 | }
710 | }
711 |
712 | Serial.print(F("Press return to continue..."));
713 | flushInput();
714 | while(!Serial.available());
715 | mainMenu();
716 | flushInput();
717 | }
718 |
719 |
720 |
721 | if(command.equals("7") and testMenu == false){
722 | clearScreen();
723 | setLEDColor(0, 0, 1);
724 | confirm();
725 | uint16_t highestBlock = 0;
726 | if(confirmOperation == true){
727 | getDriveType();
728 | Serial.println();
729 |
730 | highestBlock = (driveSize[0]<<16) | (driveSize[1]<<8) | (driveSize[2]);
731 | for(long int i = 0; i < highestBlock; i++){
732 | for(int j = 0; j < 532; j++){
733 | data[j] = 0x00;
734 | }
735 | driveSize[0] = i >> 16;
736 | driveSize[1] = i >> 8;
737 | driveSize[2] = i;
738 | profileWrite(driveSize[0], driveSize[1], driveSize[2]);
739 | Serial.print(F("Now writing zeros to block "));
740 | for(int j = 0; j < 3; j++){
741 | printDataNoSpace(driveSize[j]);
742 | }
743 | Serial.print(F(" of "));
744 | printDataNoSpace((highestBlock - 1) >> 16);
745 | printDataNoSpace((highestBlock - 1) >> 8);
746 | printDataNoSpace(highestBlock - 1);
747 | Serial.print(F(". Progress: "));
748 | Serial.print(((float)i/(highestBlock - 1))*100);
749 | Serial.print(F("%"));
750 | Serial.write("\033[1000D");
751 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0){ //Make it so that we go back up to the progress line after printing an error
752 | Serial.println();
753 | Serial.println();
754 | Serial.print(F("Error writing block "));
755 | printDataNoSpace(i >> 16);
756 | printDataNoSpace(i >> 8);
757 | printDataNoSpace(i);
758 | Serial.println(F("!"));
759 | Serial.println(F("Status Interpretation:"));
760 | for(int k = 0; k < 3; k++){
761 | for(int l = 0; l < 8; l++){
762 | if(bitRead(status[k], l) == 1){
763 | //statusGood = 0;
764 | Serial.println(statusMessages[k][l]); //All status errors show up every time
765 | }
766 | }
767 | }
768 | Serial.println();
769 | }
770 |
771 |
772 | }
773 | }
774 |
775 | Serial.println();
776 | setLEDColor(0, 1, 0);
777 | Serial.print(F("Press return to continue..."));
778 | flushInput();
779 | while(!Serial.available());
780 | mainMenu();
781 | flushInput();
782 | }
783 | if(command.equals("8") and testMenu == false){
784 | clearScreen();
785 | setLEDColor(0, 0, 1);
786 | confirm();
787 | uint16_t highestBlock = 0;
788 | if(confirmOperation == true){
789 | Serial.println(F("Reading spare table to determine drive size..."));
790 | Serial.println(F("Command: 00 FF FF FF 64 14"));
791 | //profileRead(0xFF, 0xFF, 0xFF);
792 | data[13] = 0x00;
793 | data[14] = 0x00;
794 | data[15] = 0x00;
795 | if(data[13] == 0x00 and data[14] == 0x00 and data[15] == 0x00){
796 | Serial.print(F("Drive is a 5MB ProFile with "));
797 | }
798 | else if(data[13] == 0x00 and data[14] == 0x00 and data[15] == 0x10){
799 | Serial.print(F("Drive is a 10MB ProFile with "));
800 | }
801 | else if(data[13] == 0x00 and data[14] == 0x01 and data[15] == 0x00){
802 | Serial.print(F("Drive is a 10MB Widget with "));
803 | }
804 | else{
805 | Serial.print(F("Drive type is unknown with "));
806 | }
807 | //byte driveSize[3];
808 | for(int i = 18; i < 21; i++){
809 | Serial.print(data[i]>>4, HEX);
810 | Serial.print(data[i]&0x0F, HEX);
811 | driveSize[i - 18] = data[i];
812 | }
813 |
814 | bool selectedSize = false;
815 | Serial.print(F(" blocks. Is this correct (return for yes, 'n' for no)? "));
816 | flushInput();
817 | while(1){
818 | if(Serial.available()){
819 | delay(50);
820 | userInput = Serial.read();
821 | flushInput();
822 | if(userInput == 'n'){
823 | Serial.println();
824 | Serial.println(F("1 - 5MB ProFile"));
825 | Serial.println(F("2 - 10MB ProFile"));
826 | Serial.println(F("3 - 10MB Widget"));
827 | Serial.print(F("Please select your drive from this list: "));
828 | while(selectedSize == false){
829 | if(Serial.available()){
830 | delay(50);
831 | userInput = Serial.read();
832 | flushInput();
833 | if(userInput == '1'){
834 | selectedSize = true;
835 | driveSize[0] = 0x00;
836 | driveSize[1] = 0x26;
837 | driveSize[2] = 0x00;
838 | }
839 | else if(userInput == '2'){
840 | selectedSize = true;
841 | driveSize[0] = 0x00;
842 | driveSize[1] = 0x4C;
843 | driveSize[2] = 0x00;
844 | }
845 | else if(userInput == '3'){
846 | selectedSize = true;
847 | driveSize[0] = 0x00;
848 | driveSize[1] = 0x4C;
849 | driveSize[2] = 0x00;
850 | }
851 | else{
852 | Serial.println();
853 | Serial.println(F("1 - 5MB ProFile"));
854 | Serial.println(F("2 - 10MB ProFile"));
855 | Serial.println(F("3 - 10MB Widget"));
856 | Serial.print(F("Please select your drive from this list: "));
857 | }
858 | }
859 | }
860 | break;
861 | }
862 |
863 | else if(userInput == '\r'){
864 | break;
865 | }
866 | else{
867 | if(data[13] == 0x00 and data[14] == 0x00 and data[15] == 0x00){
868 | Serial.print(F("Drive is a 5MB ProFile with "));
869 | }
870 | else if(data[13] == 0x00 and data[14] == 0x00 and data[15] == 0x10){
871 | Serial.print(F("Drive is a 10MB ProFile with "));
872 | }
873 | else if(data[13] == 0x00 and data[14] == 0x01 and data[15] == 0x00){
874 | Serial.print(F(" Drive is a 10MB Widget with "));
875 | }
876 | else{
877 | Serial.print(F(" (Unknown Drive Type)"));
878 | }
879 | for(int i = 18; i < 21; i++){
880 | Serial.print(data[i]>>4, HEX);
881 | Serial.print(data[i]&0x0F, HEX);
882 | driveSize[i - 18] = data[i];
883 | }
884 | Serial.print(F(" blocks. Is this correct (return for yes, 'n' for no)? "));
885 | }
886 | }
887 |
888 | }
889 | }
890 |
891 | Serial.println();
892 |
893 | driveSize[0] = 0x00;
894 | driveSize[1] = 0x26;
895 | driveSize[2] = 0x00;
896 |
897 | highestBlock = (driveSize[0]<<16) | (driveSize[1]<<8) | (driveSize[2]);
898 | if(highestBlock != 0x2600 and confirmOperation == true){
899 | confirmOperation = false;
900 | setLEDColor(1, 0, 0);
901 | Serial.println(F("This device only currently supports low-level formatting 5MB ProFiles."));
902 | }
903 | if(confirmOperation == true){
904 | Serial.print(F("Install jumper and press return to continue..."));
905 | flushInput();
906 | while(!Serial.available());
907 | flushInput();
908 | Serial.println(F("Low-level formatting drive..."));
909 | setLEDColor(0, 0, 0);
910 | profileHandshake();
911 | sendCommandBytes(0x03, 0x00, 0x00, 0x00, defaultRetries, defaultSpareThreshold);
912 | //clearCMD();
913 | while(readBsy() != 1);
914 | setLEDColor(0, 0, 1);
915 | Serial.print(F("Remove jumper and press return to continue..."));
916 | flushInput();
917 | while(!Serial.available());
918 | flushInput();
919 | Serial.println(F("Scanning drive for bad blocks..."));
920 | setLEDColor(0, 0, 0);
921 | profileHandshake();
922 | sendCommandBytes(0x04, 0x00, 0x00, 0x00, defaultRetries, defaultSpareThreshold);
923 | //clearCMD();
924 | while(readBsy() != 1);
925 | Serial.println(F("Creating spare table..."));
926 | profileHandshake();
927 | sendCommandBytes(0x05, 0x00, 0x00, 0x00, defaultRetries, defaultSpareThreshold);
928 | //clearCMD();
929 | while(readBsy() != 1);
930 | setLEDColor(0, 1, 0);
931 | Serial.print(F("Low-level format successful! "));
932 | }
933 | Serial.print(F("Press return to continue..."));
934 | flushInput();
935 | while(!Serial.available());
936 | mainMenu();
937 | flushInput();
938 | }
939 | if(command.equals("9") and testMenu == false){
940 | testMenu = true;
941 | clearScreen();
942 | testSubMenu();
943 | flushInput();
944 | }
945 | if(command.equals("7") and testMenu == true){
946 | testMenu = false;
947 | clearScreen();
948 | mainMenu();
949 | flushInput();
950 | }
951 | if(command.equals("1") and testMenu == true){
952 | clearScreen();
953 | getDriveType();
954 | setLEDColor(0, 0, 1);
955 | repeatTest();
956 | uint16_t highestBlock = 0;
957 | bool firstTime = true;
958 | long int passes = 1;
959 | Serial.println();
960 | highestBlock = (driveSize[0]<<16) | (driveSize[1]<<8) | (driveSize[2]);
961 | while(repeat == true or firstTime == true){
962 | for(long int i = 0; i < highestBlock; i++){
963 | driveSize[0] = i >> 16;
964 | driveSize[1] = i >> 8;
965 | driveSize[2] = i;
966 | profileRead(driveSize[0], driveSize[1], driveSize[2]);
967 | Serial.print(F("Now reading block "));
968 | for(int j = 0; j < 3; j++){
969 | printDataNoSpace(driveSize[j]);
970 | }
971 | Serial.print(F(" of "));
972 | printDataNoSpace((highestBlock - 1) >> 16);
973 | printDataNoSpace((highestBlock - 1) >> 8);
974 | printDataNoSpace(highestBlock - 1);
975 | Serial.print(F(". Progress: "));
976 | Serial.print(((float)i/(highestBlock - 1))*100);
977 | Serial.print(F("%"));
978 | if(repeat == true){
979 | Serial.print(F(" - Pass "));
980 | Serial.print(passes);
981 | }
982 | Serial.write("\033[1000D");
983 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0){ //Make it so that we go back up to the progress line after printing an error
984 | setLEDColor(1, 0, 0);
985 | Serial.println();
986 | Serial.println();
987 | Serial.print(F("Error reading block "));
988 | printDataNoSpace(i >> 16);
989 | printDataNoSpace(i >> 8);
990 | printDataNoSpace(i);
991 | Serial.println(F("!"));
992 | Serial.println(F("Status Interpretation:"));
993 | for(int k = 0; k < 3; k++){
994 | for(int l = 0; l < 8; l++){
995 | if(bitRead(status[k], l) == 1){
996 | //statusGood = 0;
997 | Serial.println(statusMessages[k][l]);
998 | }
999 | }
1000 | }
1001 | Serial.println();
1002 | }
1003 | }
1004 | firstTime = false;
1005 | passes += 1;
1006 | }
1007 |
1008 |
1009 | Serial.println();
1010 | Serial.print(F("Read test completed. Press return to continue..."));
1011 | setLEDColor(0, 1, 0);
1012 | flushInput();
1013 | while(!Serial.available());
1014 | mainMenu();
1015 | flushInput();
1016 | }
1017 |
1018 |
1019 |
1020 | if(command.equals("6") and testMenu == true){
1021 | clearScreen();
1022 | setLEDColor(0, 0, 1);
1023 | confirm();
1024 | uint16_t highestBlock = 0;
1025 | if(confirmOperation == true){
1026 | getDriveType();
1027 | repeatTest();
1028 | bool firstTime = true;
1029 | long int passes = 1;
1030 | Serial.println();
1031 | highestBlock = (driveSize[0]<<16) | (driveSize[1]<<8) | (driveSize[2]);
1032 | while(repeat == true or firstTime == true){
1033 | for(long int i = 0; i < highestBlock; i++){
1034 | driveSize[0] = i >> 16;
1035 | driveSize[1] = i >> 8;
1036 | driveSize[2] = i;
1037 | profileRead(driveSize[0], driveSize[1], driveSize[2]);
1038 | Serial.print(F("Phase 1 - Now reading block "));
1039 | for(int j = 0; j < 3; j++){
1040 | printDataNoSpace(driveSize[j]);
1041 | }
1042 | Serial.print(F(" of "));
1043 | printDataNoSpace((highestBlock - 1) >> 16);
1044 | printDataNoSpace((highestBlock - 1) >> 8);
1045 | printDataNoSpace(highestBlock - 1);
1046 | Serial.print(F(". Progress: "));
1047 | Serial.print(((float)i/(highestBlock - 1))*100);
1048 | Serial.print(F("%"));
1049 | if(repeat == true){
1050 | Serial.print(F(" - Pass "));
1051 | Serial.print(passes);
1052 | }
1053 | Serial.write("\033[1000D");
1054 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0){ //Make it so that we go back up to the progress line after printing an error
1055 | setLEDColor(1, 0, 0);
1056 | Serial.println();
1057 | Serial.println();
1058 | Serial.print(F("Error reading block "));
1059 | printDataNoSpace(i >> 16);
1060 | printDataNoSpace(i >> 8);
1061 | printDataNoSpace(i);
1062 | Serial.println(F("!"));
1063 | Serial.println(F("Status Interpretation:"));
1064 | for(int k = 0; k < 3; k++){
1065 | for(int l = 0; l < 8; l++){
1066 | if(bitRead(status[k], l) == 1){
1067 | //statusGood = 0;
1068 | Serial.println(statusMessages[k][l]); //All status errors show up every time
1069 | }
1070 | }
1071 | }
1072 | Serial.println();
1073 | }
1074 | }
1075 | for(int i = 0; i < 532; i+=3){
1076 | data[i] = 0x55;
1077 | crcArray[i] = 0x55;
1078 | }
1079 | for(int i = 1; i < 532; i+=3){
1080 | data[i] = 0x5A;
1081 | crcArray[i] = 0x5A;
1082 | }
1083 | for(int i = 2; i < 532; i+=3){
1084 | data[i] = 0xAA;
1085 | crcArray[i] = 0xAA;
1086 | }
1087 | data[530] = 0x70;
1088 | data[531] = 0x75;
1089 | crcArray[530] = 0x70;
1090 | crcArray[531] = 0x75;
1091 | Serial.println();
1092 | for(long int i = 0; i < highestBlock; i++){
1093 | driveSize[0] = i >> 16;
1094 | driveSize[1] = i >> 8;
1095 | driveSize[2] = i;
1096 | profileWrite(driveSize[0], driveSize[1], driveSize[2]);
1097 | Serial.print(F("Phase 2 - Now writing test pattern to block "));
1098 | for(int j = 0; j < 3; j++){
1099 | printDataNoSpace(driveSize[j]);
1100 | }
1101 | Serial.print(F(" of "));
1102 | printDataNoSpace((highestBlock - 1) >> 16);
1103 | printDataNoSpace((highestBlock - 1) >> 8);
1104 | printDataNoSpace(highestBlock - 1);
1105 | Serial.print(F(". Progress: "));
1106 | Serial.print(((float)i/(highestBlock - 1))*100);
1107 | Serial.print(F("%"));
1108 | if(repeat == true){
1109 | Serial.print(F(" - Pass "));
1110 | Serial.print(passes);
1111 | }
1112 | Serial.write("\033[1000D");
1113 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0){ //Make it so that we go back up to the progress line after printing an error
1114 | setLEDColor(1, 0, 0);
1115 | Serial.println();
1116 | Serial.println();
1117 | Serial.print(F("Error writing block "));
1118 | printDataNoSpace(i >> 16);
1119 | printDataNoSpace(i >> 8);
1120 | printDataNoSpace(i);
1121 | Serial.println(F("!"));
1122 | Serial.println(F("Status Interpretation:"));
1123 | for(int k = 0; k < 3; k++){
1124 | for(int l = 0; l < 8; l++){
1125 | if(bitRead(status[k], l) == 1){
1126 | //statusGood = 0;
1127 | Serial.println(statusMessages[k][l]); //All status errors show up every time
1128 | }
1129 | }
1130 | }
1131 | Serial.println();
1132 | }
1133 | }
1134 | bool blockError = false;
1135 | Serial.println();
1136 | for(long int i = 0; i < highestBlock; i++){
1137 | driveSize[0] = i >> 16;
1138 | driveSize[1] = i >> 8;
1139 | driveSize[2] = i;
1140 | profileRead(driveSize[0], driveSize[1], driveSize[2]);
1141 | Serial.print(F("Phase 3 - Now rereading block "));
1142 | for(int j = 0; j < 3; j++){
1143 | printDataNoSpace(driveSize[j]);
1144 | }
1145 | Serial.print(F(" of "));
1146 | printDataNoSpace((highestBlock - 1) >> 16);
1147 | printDataNoSpace((highestBlock - 1) >> 8);
1148 | printDataNoSpace(highestBlock - 1);
1149 | Serial.print(F(". Progress: "));
1150 | Serial.print(((float)i/(highestBlock - 1))*100);
1151 | Serial.print(F("%"));
1152 | if(repeat == true){
1153 | Serial.print(F(" - Pass "));
1154 | Serial.print(passes);
1155 | }
1156 | Serial.write("\033[1000D");
1157 | for(int m = 0; m < 532; m++){
1158 | if(data[m] != crcArray[m]){
1159 | blockError = true;
1160 | }
1161 | }
1162 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0 or blockError == true){ //Make it so that we go back up to the progress line after printing an error
1163 | setLEDColor(1, 0, 0);
1164 | Serial.println();
1165 | Serial.println();
1166 | blockError = false;
1167 | Serial.print(F("Error reading block "));
1168 | printDataNoSpace(i >> 16);
1169 | printDataNoSpace(i >> 8);
1170 | printDataNoSpace(i);
1171 | Serial.println(F("!"));
1172 | Serial.println(F("Status Interpretation:"));
1173 | for(int k = 0; k < 3; k++){
1174 | for(int l = 0; l < 8; l++){
1175 | if(bitRead(status[k], l) == 1){
1176 | //statusGood = 0;
1177 | Serial.println(statusMessages[k][l]); //All status errors show up every time
1178 | }
1179 | }
1180 | }
1181 | Serial.println();
1182 | }
1183 | }
1184 | firstTime = false;
1185 | passes += 1;
1186 | }
1187 | }
1188 |
1189 |
1190 |
1191 | Serial.println();
1192 | Serial.print(F("Read-Write-Read test completed. Press return to continue..."));
1193 | setLEDColor(0, 1, 0);
1194 | flushInput();
1195 | while(!Serial.available());
1196 | mainMenu();
1197 | flushInput();
1198 | }
1199 |
1200 |
1201 |
1202 |
1203 | if(command.equals("2") and testMenu == true){
1204 | clearScreen();
1205 | setLEDColor(0, 0, 1);
1206 | confirm();
1207 | uint16_t highestBlock = 0;
1208 | if(confirmOperation == true){
1209 | getDriveType();
1210 | repeatTest();
1211 | bool firstTime = true;
1212 | long int passes = 1;
1213 | Serial.println();
1214 | highestBlock = (driveSize[0]<<16) | (driveSize[1]<<8) | (driveSize[2]);
1215 | while(repeat == true or firstTime == true){
1216 | for(long int i = 0; i < highestBlock; i++){
1217 | driveSize[0] = i >> 16;
1218 | driveSize[1] = i >> 8;
1219 | driveSize[2] = i;
1220 | for(int i = 0; i < 532; i++){
1221 | data[i] = B01010101;
1222 | }
1223 | profileWrite(driveSize[0], driveSize[1], driveSize[2]);
1224 | Serial.print(F("Now writing block "));
1225 | for(int j = 0; j < 3; j++){
1226 | printDataNoSpace(driveSize[j]);
1227 | }
1228 | Serial.print(F(" of "));
1229 | printDataNoSpace((highestBlock - 1) >> 16);
1230 | printDataNoSpace((highestBlock - 1) >> 8);
1231 | printDataNoSpace(highestBlock - 1);
1232 | Serial.print(F(". Progress: "));
1233 | Serial.print(((float)i/(highestBlock - 1))*100);
1234 | Serial.print(F("%"));
1235 | if(repeat == true){
1236 | Serial.print(F(" - Pass "));
1237 | Serial.print(passes);
1238 | }
1239 | Serial.write("\033[1000D");
1240 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0){ //Make it so that we go back up to the progress line after printing an error
1241 | setLEDColor(1, 0, 0);
1242 | Serial.println();
1243 | Serial.println();
1244 | Serial.print(F("Error writing block with pattern 01010101"));
1245 | printDataNoSpace(i >> 16);
1246 | printDataNoSpace(i >> 8);
1247 | printDataNoSpace(i);
1248 | Serial.println(F("!"));
1249 | Serial.println(F("Status Interpretation:"));
1250 | for(int k = 0; k < 3; k++){
1251 | for(int l = 0; l < 8; l++){
1252 | if(bitRead(status[k], l) == 1){
1253 | //statusGood = 0;
1254 | Serial.println(statusMessages[k][l]); //All status errors show up every time
1255 | }
1256 | }
1257 | }
1258 | Serial.println();
1259 | }
1260 | for(int i = 0; i < 532; i++){
1261 | data[i] = B10101010;
1262 | }
1263 | profileWrite(driveSize[0], driveSize[1], driveSize[2]);
1264 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0){ //Make it so that we go back up to the progress line after printing an error
1265 | setLEDColor(1, 0, 0);
1266 | Serial.println();
1267 | Serial.println();
1268 | Serial.print(F("Error writing block with pattern 10101010"));
1269 | printDataNoSpace(i >> 16);
1270 | printDataNoSpace(i >> 8);
1271 | printDataNoSpace(i);
1272 | Serial.println(F("!"));
1273 | Serial.println(F("Status Interpretation:"));
1274 | for(int k = 0; k < 3; k++){
1275 | for(int l = 0; l < 8; l++){
1276 | if(bitRead(status[k], l) == 1){
1277 | //statusGood = 0;
1278 | Serial.println(statusMessages[k][l]); //All status errors show up every time
1279 | }
1280 | }
1281 | }
1282 | Serial.println();
1283 | }
1284 | }
1285 | firstTime = false;
1286 | passes += 1;
1287 | }
1288 | }
1289 |
1290 |
1291 |
1292 | Serial.println();
1293 | Serial.print(F("Write test completed. Press return to continue..."));
1294 | setLEDColor(0, 1, 0);
1295 | flushInput();
1296 | while(!Serial.available());
1297 | mainMenu();
1298 | flushInput();
1299 | }
1300 |
1301 | if(command.equals("3") and testMenu == true){
1302 | clearScreen();
1303 | getDriveType();
1304 | setLEDColor(0, 0, 1);
1305 | repeatTest();
1306 | uint16_t highestBlock = 0;
1307 | bool firstTime = true;
1308 | randomSeed(analogRead(0));
1309 | long int passes = 1;
1310 | uint16_t randomBlock = 0;
1311 | Serial.println();
1312 | highestBlock = (driveSize[0]<<16) | (driveSize[1]<<8) | (driveSize[2]);
1313 | while(repeat == true or firstTime == true){
1314 | for(long int i = 0; i < highestBlock; i++){
1315 | randomBlock = random(0, highestBlock);
1316 | driveSize[0] = randomBlock >> 16;
1317 | driveSize[1] = randomBlock >> 8;
1318 | driveSize[2] = randomBlock;
1319 | profileRead(driveSize[0], driveSize[1], driveSize[2]);
1320 | Serial.print(F("Now reading randomly selected block "));
1321 | for(int j = 0; j < 3; j++){
1322 | printDataNoSpace(driveSize[j]);
1323 | }
1324 | Serial.print(F(" of "));
1325 | printDataNoSpace((highestBlock - 1) >> 16);
1326 | printDataNoSpace((highestBlock - 1) >> 8);
1327 | printDataNoSpace(highestBlock - 1);
1328 | Serial.print(F(". Progress: "));
1329 | Serial.print(((float)i/(highestBlock - 1))*100);
1330 | Serial.print(F("%"));
1331 | if(repeat == true){
1332 | Serial.print(F(" - Pass "));
1333 | Serial.print(passes);
1334 | }
1335 | Serial.write("\033[1000D");
1336 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0){ //Make it so that we go back up to the progress line after printing an error
1337 | setLEDColor(1, 0, 0);
1338 | Serial.println();
1339 | Serial.println();
1340 | Serial.print(F("Error reading block "));
1341 | printDataNoSpace(randomBlock >> 16);
1342 | printDataNoSpace(randomBlock >> 8);
1343 | printDataNoSpace(randomBlock);
1344 | Serial.println(F("!"));
1345 | Serial.println(F("Status Interpretation:"));
1346 | for(int k = 0; k < 3; k++){
1347 | for(int l = 0; l < 8; l++){
1348 | if(bitRead(status[k], l) == 1){
1349 | //statusGood = 0;
1350 | Serial.println(statusMessages[k][l]); //All status errors show up every time
1351 | }
1352 | }
1353 | }
1354 | Serial.println();
1355 | }
1356 | }
1357 | firstTime = false;
1358 | passes += 1;
1359 | }
1360 |
1361 |
1362 | Serial.println();
1363 | Serial.print(F("Read test completed. Press return to continue..."));
1364 | setLEDColor(0, 1, 0);
1365 | flushInput();
1366 | while(!Serial.available());
1367 | mainMenu();
1368 | flushInput();
1369 | }
1370 |
1371 |
1372 |
1373 |
1374 |
1375 |
1376 | if(command.equals("4") and testMenu == true){
1377 | clearScreen();
1378 | setLEDColor(0, 0, 1);
1379 | confirm();
1380 | uint16_t highestBlock = 0;
1381 | if(confirmOperation == true){
1382 | getDriveType();
1383 | repeatTest();
1384 | bool firstTime = true;
1385 | randomSeed(analogRead(0));
1386 | long int passes = 1;
1387 | uint16_t randomBlock = 0;
1388 | Serial.println();
1389 | highestBlock = (driveSize[0]<<16) | (driveSize[1]<<8) | (driveSize[2]);
1390 | while(repeat == true or firstTime == true){
1391 | for(long int i = 0; i < highestBlock; i++){
1392 | randomBlock = random(0, highestBlock);
1393 | driveSize[0] = randomBlock >> 16;
1394 | driveSize[1] = randomBlock >> 8;
1395 | driveSize[2] = randomBlock;
1396 | for(int i = 0; i < 532; i++){
1397 | data[i] = B01010101;
1398 | }
1399 | profileWrite(driveSize[0], driveSize[1], driveSize[2]);
1400 | Serial.print(F("Now writing randomly selected block "));
1401 | for(int j = 0; j < 3; j++){
1402 | printDataNoSpace(driveSize[j]);
1403 | }
1404 | Serial.print(F(" of "));
1405 | printDataNoSpace((highestBlock - 1) >> 16);
1406 | printDataNoSpace((highestBlock - 1) >> 8);
1407 | printDataNoSpace(highestBlock - 1);
1408 | Serial.print(F(". Progress: "));
1409 | Serial.print(((float)i/(highestBlock - 1))*100);
1410 | Serial.print(F("%"));
1411 | if(repeat == true){
1412 | Serial.print(F(" - Pass "));
1413 | Serial.print(passes);
1414 | }
1415 | Serial.write("\033[1000D");
1416 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0){ //Make it so that we go back up to the progress line after printing an error
1417 | setLEDColor(1, 0, 0);
1418 | Serial.println();
1419 | Serial.println();
1420 | Serial.print(F("Error writing block "));
1421 | printDataNoSpace(randomBlock >> 16);
1422 | printDataNoSpace(randomBlock >> 8);
1423 | printDataNoSpace(randomBlock);
1424 | Serial.print(F(" with pattern 01010101"));
1425 | Serial.println(F("!"));
1426 | Serial.println(F("Status Interpretation:"));
1427 | for(int k = 0; k < 3; k++){
1428 | for(int l = 0; l < 8; l++){
1429 | if(bitRead(status[k], l) == 1){
1430 | //statusGood = 0;
1431 | Serial.println(statusMessages[k][l]); //All status errors show up every time
1432 | }
1433 | }
1434 | }
1435 | Serial.println();
1436 | }
1437 | for(int i = 0; i < 532; i++){
1438 | data[i] = B10101010;
1439 | }
1440 | profileWrite(driveSize[0], driveSize[1], driveSize[2]);
1441 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0){ //Make it so that we go back up to the progress line after printing an error
1442 | setLEDColor(1, 0, 0);
1443 | Serial.println();
1444 | Serial.println();
1445 | Serial.print(F("Error writing block "));
1446 | printDataNoSpace(i >> 16);
1447 | printDataNoSpace(i >> 8);
1448 | printDataNoSpace(i);
1449 | Serial.print(F(" with pattern 01010101"));
1450 | Serial.println(F("!"));
1451 | Serial.println(F("Status Interpretation:"));
1452 | for(int k = 0; k < 3; k++){
1453 | for(int l = 0; l < 8; l++){
1454 | if(bitRead(status[k], l) == 1){
1455 | //statusGood = 0;
1456 | Serial.println(statusMessages[k][l]); //All status errors show up every time
1457 | }
1458 | }
1459 | }
1460 | Serial.println();
1461 | }
1462 | }
1463 | firstTime = false;
1464 | passes += 1;
1465 | }
1466 | }
1467 |
1468 |
1469 |
1470 | Serial.println();
1471 | Serial.print(F("Write test completed. Press return to continue..."));
1472 | setLEDColor(0, 1, 0);
1473 | flushInput();
1474 | while(!Serial.available());
1475 | mainMenu();
1476 | flushInput();
1477 | }
1478 |
1479 |
1480 | if(command.equals("5") and testMenu == true){
1481 | clearScreen();
1482 | getDriveType();
1483 | setLEDColor(0, 0, 1);
1484 | repeatTest();
1485 | uint16_t highestBlock = 0;
1486 | bool firstTime = true;
1487 | long int passes = 1;
1488 | uint16_t butterflyBlock;
1489 | Serial.println();
1490 | highestBlock = (driveSize[0]<<16) | (driveSize[1]<<8) | (driveSize[2]);
1491 | while(repeat == true or firstTime == true){
1492 | for(long int i = 0; i < highestBlock; i++){
1493 | if(i % 2 == 0){
1494 | butterflyBlock = i;
1495 | }
1496 | else{
1497 | butterflyBlock = highestBlock - i;
1498 | }
1499 | driveSize[0] = butterflyBlock >> 16;
1500 | driveSize[1] = butterflyBlock >> 8;
1501 | driveSize[2] = butterflyBlock;
1502 | profileRead(driveSize[0], driveSize[1], driveSize[2]);
1503 | Serial.print(F("Now performing butterfly read test on block "));
1504 | for(int j = 0; j < 3; j++){
1505 | printDataNoSpace(driveSize[j]);
1506 | }
1507 | Serial.print(F(" of "));
1508 | printDataNoSpace((highestBlock - 1) >> 16);
1509 | printDataNoSpace((highestBlock - 1) >> 8);
1510 | printDataNoSpace(highestBlock - 1);
1511 | Serial.print(F(". Progress: "));
1512 | Serial.print(((float)i/(highestBlock - 1))*100);
1513 | Serial.print(F("%"));
1514 | if(repeat == true){
1515 | Serial.print(F(" - Pass "));
1516 | Serial.print(passes);
1517 | }
1518 | Serial.write("\033[1000D");
1519 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0){ //Make it so that we go back up to the progress line after printing an error
1520 | setLEDColor(1, 0, 0);
1521 | Serial.println();
1522 | Serial.println();
1523 | Serial.print(F("Error reading block "));
1524 | printDataNoSpace(butterflyBlock >> 16);
1525 | printDataNoSpace(butterflyBlock >> 8);
1526 | printDataNoSpace(butterflyBlock);
1527 | Serial.println(F("!"));
1528 | Serial.println(F("Status Interpretation:"));
1529 | for(int k = 0; k < 3; k++){
1530 | for(int l = 0; l < 8; l++){
1531 | if(bitRead(status[k], l) == 1){
1532 | //statusGood = 0;
1533 | Serial.println(statusMessages[k][l]); //All status errors show up every time
1534 | }
1535 | }
1536 | }
1537 | Serial.println();
1538 | }
1539 | }
1540 | firstTime = false;
1541 | passes += 1;
1542 | }
1543 |
1544 |
1545 | Serial.println();
1546 | Serial.print(F("Butterfly read test completed. Press return to continue..."));
1547 | setLEDColor(0, 1, 0);
1548 | flushInput();
1549 | while(!Serial.available());
1550 | mainMenu();
1551 | flushInput();
1552 | }
1553 |
1554 | else{
1555 | if(testMenu == true){
1556 | testSubMenu();
1557 | }
1558 | else{
1559 | mainMenu();
1560 | }
1561 | //SD.begin(53);
1562 | //File root = SD.open("/");
1563 | //printDirectory(root, 0);
1564 |
1565 | }
1566 | }
1567 | }
1568 |
1569 | void confirm(){
1570 | confirmOperation = false;
1571 | Serial.print(F("WARNING: This operation will destroy all data on your drive! Are you sure you want to continue (return for yes, 'n' to cancel)? "));
1572 | while(1){
1573 | if(Serial.available()){
1574 | delay(50);
1575 | userInput = Serial.read();
1576 | flushInput();
1577 | if(userInput == 'n'){
1578 | break;
1579 | }
1580 | else if(userInput == '\r'){
1581 | confirmOperation = true;
1582 | break;
1583 | }
1584 | else{
1585 | Serial.print(F("WARNING: This operation will destroy all data on your drive! Are you sure you want to continue (return for yes, 'n' to cancel)? "));
1586 | break;
1587 | }
1588 | }
1589 |
1590 | }
1591 | }
1592 |
1593 |
1594 | void startReceipt(){
1595 | long int originalStart = millis();
1596 | start = millis();
1597 | while(1){
1598 | if(millis() - start >= 3000){
1599 | Serial.write(0x43);
1600 | start = millis();
1601 | }
1602 | if(Serial.available()){
1603 | break;
1604 | }
1605 | if(millis() - originalStart >= 30000){
1606 | Serial.println();
1607 | Serial.println(F("Operation failed - XMODEM sender never started!"));
1608 | done = true;
1609 | failed = true;
1610 | break;
1611 | }
1612 | }
1613 | }
1614 |
1615 | void receivePacket(){
1616 | if(ackStatus == 0x00){
1617 | Serial.write(NAK);
1618 | }
1619 | else if(ackStatus == 0x01){
1620 | Serial.write(ACK);
1621 | }
1622 | else if(ackStatus == 0x02){
1623 | startReceipt();
1624 | }
1625 | start = millis();
1626 | while(!Serial.available() and done == false){
1627 | if(millis() - start >= 5000){
1628 | delay(2000);
1629 | Serial.println(F("Operation failed - XMODEM timeout error!"));
1630 | done = true;
1631 | failed = true;
1632 | break;
1633 | }
1634 | }
1635 | dataIndex = 0;
1636 | while(dataIndex < 1029 and done == false){
1637 | if(Serial.available()){
1638 | rawData[dataIndex] = Serial.read();
1639 | dataIndex += 1;
1640 | if(rawData[0] == EOT){
1641 | delay(2000);
1642 | //xModemData.close();
1643 | Serial.write(ACK);
1644 | done = true;
1645 | break;
1646 | }
1647 | }
1648 | }
1649 | if(done == false){
1650 | for(int i = 0; i < 1024; i++){
1651 | crcArray[i] = rawData[i + 3];
1652 | }
1653 | CRC = calc_crc(1024);
1654 | /*delay(10000);
1655 | Serial.println(CRC);*/
1656 | actualCRC = rawData[1027] << 8 | rawData[1028];
1657 | /*Serial.println(actualCRC);
1658 | Serial.println(rawData[1027], BIN);
1659 | Serial.println(rawData[1028], BIN);
1660 | delay(5000);*/
1661 |
1662 | if(CRC == actualCRC and packetNum == rawData[1] and notPacketNum == rawData[2] and done == false){
1663 | packetNum += 1;
1664 | notPacketNum -= 1;
1665 | //Serial.write(ACK);
1666 | ackStatus = 0x01;
1667 | /*for(int i = 0; i < 1024; i++){
1668 | data[i] = rawData[i + 3];
1669 | }*/
1670 | }
1671 | else if((CRC != actualCRC or packetNum != rawData[1] or notPacketNum != rawData[2]) and done == false){
1672 | //Serial.write(NAK);
1673 | ackStatus = 0x00;
1674 | }
1675 | }
1676 | }
1677 |
1678 | void getDriveType(){
1679 | setLEDColor(0, 0, 1);
1680 | Serial.println(F("Reading spare table to determine drive size..."));
1681 | Serial.println(F("Command: 00 FF FF FF 64 14"));
1682 | profileRead(0xFF, 0xFF, 0xFF);
1683 | setLEDColor(0, 0, 1);
1684 | if(data[13] == 0x00 and data[14] == 0x00 and data[15] == 0x00){
1685 | Serial.print(F("Drive is a 5MB ProFile with "));
1686 | }
1687 | else if(data[13] == 0x00 and data[14] == 0x00 and data[15] == 0x10){
1688 | Serial.print(F("Drive is a 10MB ProFile with "));
1689 | }
1690 | else if(data[13] == 0x00 and data[14] == 0x01 and data[15] == 0x00){
1691 | Serial.print(F("Drive is a 10MB Widget with "));
1692 | }
1693 | else{
1694 | Serial.print(F("Drive type is unknown with "));
1695 | }
1696 | //byte driveSize[3];
1697 | for(int i = 18; i < 21; i++){
1698 | Serial.print(data[i]>>4, HEX);
1699 | Serial.print(data[i]&0x0F, HEX);
1700 | driveSize[i - 18] = data[i];
1701 | }
1702 | Serial.print(F(" blocks. Is this correct (return for yes, 'n' for no)? "));
1703 | while(1){
1704 | if(Serial.available()) {
1705 | delay(50);
1706 | userInput = Serial.read();
1707 | flushInput();
1708 | if(userInput == 'n'){
1709 | Serial.print(F("Please enter the size of your drive in blocks: "));
1710 | while(1){
1711 | if(readSerialValue(6) == true and serialBytes[0] % 2 == 0){
1712 | driveSize[0] = serialBytes[0];
1713 | driveSize[1] = serialBytes[1];
1714 | driveSize[2] = serialBytes[2];
1715 | break;
1716 | }
1717 | else{
1718 | Serial.print(F("Please enter the size of your drive in blocks: "));
1719 | }
1720 | }
1721 | break;
1722 | }
1723 | else if(userInput == '\r'){
1724 | break;
1725 | }
1726 | else{
1727 | if(data[13] == 0x00 and data[14] == 0x00 and data[15] == 0x00){
1728 | Serial.print(F("Drive is a 5MB ProFile with "));
1729 | }
1730 | else if(data[13] == 0x00 and data[14] == 0x00 and data[15] == 0x10){
1731 | Serial.print(F("Drive is a 10MB ProFile with "));
1732 | }
1733 | else if(data[13] == 0x00 and data[14] == 0x01 and data[15] == 0x00){
1734 | Serial.print(F(" Drive is a 10MB Widget with "));
1735 | }
1736 | else{
1737 | Serial.print(F(" (Unknown Drive Type)"));
1738 | }
1739 | for(int i = 18; i < 21; i++){
1740 | Serial.print(data[i]>>4, HEX);
1741 | Serial.print(data[i]&0x0F, HEX);
1742 | driveSize[i - 18] = data[i];
1743 | }
1744 | Serial.print(F(" blocks. Is this correct (return for yes, 'n' for no)? "));
1745 | }
1746 | }
1747 | }
1748 | }
1749 |
1750 | void repeatTest(){
1751 | repeat = false;
1752 | Serial.print(F("Loop the test forever (return for no, 'y' for yes)? "));
1753 | while(1){
1754 | if(Serial.available()){
1755 | delay(50);
1756 | userInput = Serial.read();
1757 | flushInput();
1758 | if(userInput == 'y'){
1759 | repeat = true;
1760 | break;
1761 | }
1762 | else if(userInput == '\r'){
1763 | break;
1764 | }
1765 | else{
1766 | Serial.print(F("Loop the test forever (return for no, 'y' for yes)? "));
1767 | }
1768 | }
1769 | }
1770 | }
1771 |
1772 | void writeTwoBlocks(byte address0, byte address1, byte address2){
1773 | int startBackupErrors = backupErrors;
1774 | setLEDColor(0, 0, 0);
1775 | bool handshakeSuccessful = profileHandshake();
1776 | byte commandResponse = sendCommandBytes(write, address0, address1, address2, defaultRetries, defaultSpareThreshold);
1777 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0 or handshakeSuccessful == 0 or commandResponse != 0x03){
1778 | backupErrors += 1;
1779 | }
1780 | uint32_t fullAddress = address0 << 16 | address1 << 8 | address2;
1781 | fullAddress += 1;
1782 | setRW();
1783 | //delay(1);
1784 | clearCMD();
1785 | //delay(1);
1786 | while(readBsy() != 1){
1787 | long int startTime = millis();
1788 | if(millis() - startTime >= 10000){
1789 | break;
1790 | }
1791 | }
1792 | //delay(1);
1793 | for(int i = 0; i < 532; i++){
1794 | sendData(data[i]);
1795 | delayMicroseconds(readDelay);
1796 | setSTRB();
1797 | delayMicroseconds(readDelay);
1798 | clearSTRB();
1799 | delayMicroseconds(readDelay);
1800 | }
1801 | clearRW();
1802 | //delay(1);
1803 | setCMD();
1804 | //delay(1);
1805 | //setSTRB();
1806 | delayMicroseconds(readDelay);
1807 | while(readBsy() != 0 and receiveData != 0x06){
1808 | long int startTime = millis();
1809 | if(millis() - startTime >= 10000){
1810 | break;
1811 | }
1812 | }
1813 | setSTRB();
1814 | //delay(1);
1815 | clearSTRB();
1816 | //delay(1);
1817 | setRW();
1818 | //delay(1);
1819 | sendData(0x55);
1820 | //delay(1);
1821 | clearCMD();
1822 | readStatusBytes();
1823 | handshakeSuccessful = profileHandshake();
1824 | address0 = fullAddress >> 16;
1825 | address1 = fullAddress >> 8;
1826 | address2 = fullAddress;
1827 | commandResponse = sendCommandBytes(write, address0, address1, address2, defaultRetries, defaultSpareThreshold);
1828 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0 or handshakeSuccessful == 0 or commandResponse != 0x03){
1829 | backupErrors += 1;
1830 | }
1831 | setRW();
1832 | //delay(1);
1833 | clearCMD();
1834 | //delay(1);
1835 | while(readBsy() != 1){
1836 | long int startTime = millis();
1837 | if(millis() - startTime >= 10000){
1838 | break;
1839 | }
1840 | if(startBackupErrors != backupErrors){
1841 | setLEDColor(1, 0, 0);
1842 | }
1843 | else{
1844 | setLEDColor(1, 1, 0);
1845 | }
1846 | }
1847 | //delay(1);
1848 | for(int i = 532; i < 1064; i++){
1849 | sendData(data[i]);
1850 | delayMicroseconds(readDelay);
1851 | setSTRB();
1852 | delayMicroseconds(readDelay);
1853 | clearSTRB();
1854 | delayMicroseconds(readDelay);
1855 | }
1856 | clearRW();
1857 | //delay(1);
1858 | setCMD();
1859 | //delay(1);
1860 | //setSTRB();
1861 | delayMicroseconds(readDelay);
1862 | while(readBsy() != 0 and receiveData != 0x06){
1863 | long int startTime = millis();
1864 | if(millis() - startTime >= 10000){
1865 | break;
1866 | }
1867 | }
1868 | setSTRB();
1869 | //delay(1);
1870 | clearSTRB();
1871 | //delay(1);
1872 | setRW();
1873 | //delay(1);
1874 | sendData(0x55);
1875 | //delay(1);
1876 | clearCMD();
1877 | readStatusBytes();
1878 | }
1879 |
1880 | void readTwoBlocks(byte address0, byte address1, byte address2){
1881 | int startBackupErrors = backupErrors;
1882 | setLEDColor(0, 0, 0);
1883 | bool handshakeSuccessful = profileHandshake();
1884 | byte commandResponse = sendCommandBytes(read, address0, address1, address2, defaultRetries, defaultSpareThreshold);
1885 | readStatusBytes();
1886 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0 or handshakeSuccessful == 0 or commandResponse != 0x02){
1887 | backupErrors += 1;
1888 | }
1889 | for(int i = 0; i <= 531; i++){ //Read all 532 bytes from the drive into the data array
1890 | data[i] = receiveData();
1891 | //parity[i] = readParity();
1892 | delayMicroseconds(readDelay);
1893 | setSTRB();
1894 | delayMicroseconds(readDelay);
1895 | clearSTRB();
1896 | delayMicroseconds(readDelay);
1897 | }
1898 | clearCMD();
1899 | delayMicroseconds(readDelay);
1900 | handshakeSuccessful = profileHandshake();
1901 | uint32_t fullAddress = address0 << 16 | address1 << 8 | address2;
1902 | fullAddress += 1;
1903 | commandResponse = sendCommandBytes(read, fullAddress >> 16, fullAddress >> 8, fullAddress, defaultRetries, defaultSpareThreshold);
1904 | readStatusBytes();
1905 | if((status[0] & B11111101) != 0 or (status[1] & B11011110) != 0 or (status[2] & B01000000) != 0 or handshakeSuccessful == 0 or commandResponse != 0x02){
1906 | backupErrors += 1;
1907 | }
1908 | for(int i = 532; i < 1064; i++){ //Read all 532 bytes from the drive into the data array
1909 | data[i] = receiveData();
1910 | //parity[i] = readParity();
1911 | delayMicroseconds(readDelay);
1912 | setSTRB();
1913 | delayMicroseconds(readDelay);
1914 | clearSTRB();
1915 | delayMicroseconds(readDelay);
1916 | }
1917 | clearCMD();
1918 | delayMicroseconds(readDelay);
1919 | if(startBackupErrors != backupErrors){
1920 | setLEDColor(1, 0, 0);
1921 | }
1922 | else{
1923 | setLEDColor(1, 1, 0);
1924 | }
1925 | }
1926 |
1927 |
1928 | bool startTransmission(){
1929 | start = millis();
1930 | while(!Serial.available()){
1931 | if(millis() - start > 30000){
1932 | return false;
1933 | }
1934 | }
1935 | while(Serial.available()){
1936 | inData = Serial.read();
1937 | }
1938 | if(inData != 0x43){
1939 |
1940 | }
1941 | return true;
1942 | }
1943 |
1944 | void startNewPacket(){
1945 | Serial.write(STX);
1946 | Serial.write(packetNum);
1947 | Serial.write(notPacketNum);
1948 | }
1949 | byte finishPacket(){
1950 | CRC = calc_crc(1024);
1951 | lowCRC = CRC;
1952 | highCRC = CRC >> 8;
1953 | Serial.write(highCRC);
1954 | Serial.write(lowCRC);
1955 | packetNum += 0x01;
1956 | notPacketNum -= 0x01;
1957 | checksum = 0x00;
1958 | start = millis();
1959 | while(!Serial.available()){
1960 | if(millis() - start > 5000){
1961 | return 0x00;
1962 | }
1963 | }
1964 | while(Serial.available()){
1965 | inData = Serial.read();
1966 | if(inData == NAK){
1967 | packetNum -= 0x01;
1968 | notPacketNum += 0x01;
1969 | return 0x01;
1970 | }
1971 | }
1972 | return 0x02;
1973 | }
1974 |
1975 | void finishTransmission(){
1976 | Serial.write(EOT);
1977 | }
1978 |
1979 | uint16_t calc_crc(int n){
1980 | // Initial value. xmodem uses 0xFFFF but this example
1981 | // requires an initial value of zero.
1982 | uint16_t x = 0x0000;
1983 |
1984 |
1985 | for(int i = 0; i < n; i++){
1986 | x = crc_xmodem_update(x, (uint16_t)crcArray[i]);
1987 | }
1988 | return(x);
1989 | }
1990 |
1991 | // See bottom of this page: http://www.nongnu.org/avr-libc/user-manual/group__util__crc.html
1992 | // Polynomial: x^16 + x^12 + x^5 + 1 (0x1021)
1993 | uint16_t crc_xmodem_update (uint16_t crc, uint8_t info)
1994 | {
1995 | crc = crc ^ ((uint16_t)info << 8);
1996 | for (int i=0; i<8; i++) {
1997 | if (crc & 0x8000){
1998 | crc = (crc << 1) ^ 0x1021; //(polynomial = 0x1021)
1999 | }
2000 | else{
2001 | crc <<= 1;
2002 | }
2003 | }
2004 | return crc;
2005 | }
2006 |
2007 |
2008 | bool readSerialValue(int desiredLength){
2009 | char buffer[1024];
2010 | int length = 0;
2011 | while(1){
2012 | if(Serial.available()){
2013 | char c = Serial.read();
2014 | bool inArray = false;
2015 | for(int i = 0; i < 23; i++){
2016 | if(c == acceptableHex[i]){
2017 | inArray = true;
2018 | }
2019 | }
2020 | if(c == '\r'){
2021 | inArray = true;
2022 | }
2023 |
2024 |
2025 | if(inArray == false){
2026 | length = 0;
2027 | delay(50);
2028 | flushInput();
2029 | //Serial.println("false not in array");
2030 |
2031 | return false;
2032 | }
2033 |
2034 | if (c == '\r'){
2035 | if(length != desiredLength){
2036 | length = 0;
2037 | flushInput();
2038 | //Serial.println("false wrong length");
2039 | return false;
2040 | }
2041 | buffer[length] = '\0';
2042 | int byte_count = length/2;
2043 | hex2bin(serialBytes, buffer, &byte_count);
2044 | length = 0;
2045 | flushInput();
2046 | return true;
2047 | }
2048 | else if (length < sizeof buffer - 1) {
2049 | buffer[length++] = c;
2050 | }
2051 | }
2052 | }
2053 | }
2054 |
2055 |
2056 | static void hex2bin(uint8_t *out, const char *in, size_t *size)
2057 | {
2058 | size_t sz = 0;
2059 | while (*in) {
2060 | while (*in == ' ') in++; // skip spaces
2061 | if (!*in) break;
2062 | uint8_t c = *in>='a' ? *in-'a'+10 : *in>='A' ? *in-'A'+10 : *in-'0';
2063 | in++;
2064 | c <<= 4;
2065 | if (!*in) break;
2066 | c |= *in>='a' ? *in-'a'+10 : *in>='A' ? *in-'A'+10 : *in-'0';
2067 | in++;
2068 | *out++ = c;
2069 | sz++;
2070 | }
2071 | *size = sz;
2072 | }
2073 |
2074 | void resetDrive(){
2075 | setPRES();
2076 | //delay(1);
2077 | clearPRES();
2078 | //delay(1);
2079 | }
2080 |
2081 | void printRawData(){
2082 | Serial.println(F("Raw Data:"));
2083 | Serial.print(F("0000: "));
2084 | for(int i = 0; i <= 531; i++){
2085 | Serial.print(data[i]>>4, HEX);
2086 | Serial.print(data[i]&0x0F, HEX);
2087 | Serial.print(F(" "));
2088 | if((i + 1) % 8 == 0 and (i + 1) % 16 != 0){
2089 | Serial.print(F(" "));
2090 | }
2091 | if((i + 1) % 16 == 0){
2092 | Serial.print(F(" "));
2093 | for(int j = i - 15; j <= i; j++){
2094 | if(data[j] <= 0x1F){
2095 | Serial.print(F("."));
2096 | }
2097 | else{
2098 | Serial.write(data[j]);
2099 | }
2100 | }
2101 | Serial.println();
2102 | if((i + 1) < 0x100){
2103 | Serial.print(F("00"));
2104 | }
2105 | if((i + 1) >= 0x100){
2106 | Serial.print(F("0"));
2107 | }
2108 | Serial.print((i + 1), HEX);
2109 | Serial.print(F(": "));
2110 | }
2111 | }
2112 | Serial.print(F(" "));
2113 | for(int i = 528; i < 532; i++){
2114 | if(data[i] <= 0x1F){
2115 | Serial.print(F("."));
2116 | }
2117 | else{
2118 | Serial.write(data[i]);
2119 | }
2120 | }
2121 | Serial.println();
2122 | }
2123 |
2124 | /*void printRawParity(){
2125 | Serial.println(F("Raw Parity Data:"));
2126 | Serial.print(F("0000: "));
2127 | for(int i = 0; i <= 531; i++){
2128 | Serial.print(parity[i]>>4, HEX);
2129 | Serial.print(parity[i]&0x0F, HEX);
2130 | Serial.print(F(" "));
2131 | if((i + 1) % 8 == 0 and (i + 1) % 16 != 0){
2132 | Serial.print(F(" "));
2133 | }
2134 | if((i + 1) % 16 == 0){
2135 | Serial.print(F(" "));
2136 | for(int j = i - 15; j <= i; j++){
2137 | if(parity[j] <= 0x1F){
2138 | Serial.print(F("."));
2139 | }
2140 | else{
2141 | Serial.write(parity[j]);
2142 | }
2143 | }
2144 | Serial.println();
2145 | if((i + 1) < 0x100){
2146 | Serial.print(F("00"));
2147 | }
2148 | if((i + 1) >= 0x100){
2149 | Serial.print(F("0"));
2150 | }
2151 | Serial.print((i + 1), HEX);
2152 | Serial.print(F(": "));
2153 | }
2154 | }
2155 | Serial.print(" ");
2156 | for(int i = 528; i < 532; i++){
2157 | if(parity[i] <= 0x1F){
2158 | Serial.print(F("."));
2159 | }
2160 | else{
2161 | Serial.write(parity[i]);
2162 | }
2163 | }
2164 | Serial.println();
2165 | }*/
2166 |
2167 |
2168 | void printDataNoSpace(byte data){
2169 | Serial.print(data>>4, HEX);
2170 | Serial.print(data&0x0F, HEX);
2171 | }
2172 |
2173 | void printDataSpace(byte data){
2174 | Serial.print(data>>4, HEX);
2175 | Serial.print(data&0x0F, HEX);
2176 | Serial.print(F(" "));
2177 | }
2178 |
2179 | /*PD 7 6 5 4 3 2 1 0
2180 | PB 13 12 11 10 9 8*/
2181 |
2182 | //SETTING A BIT ALWAYS MEANS PUTTING IT INTO ITS ACTIVE STATE (MEANING LOW IF IT'S ACTIVE LOW). DO THIS IN THE CONTROL FUNCTIONS
2183 | //ADD ALL THE CONTROL LINES INSTEAD OF JUST CMD AND BSY
2184 | //READING FROM THE CONTROL BITS
2185 | //WHAT SHOULD A FUNCTION DO WHEN IT'S DONE? SHOULD IT LEAVE THE DIRECTION AS WHATEVER IT SET IT TO OR SHOULD IT REVERT IT TO WHAT IT WAS BEFORE?
2186 | //SET DIRECTIONS FOR CONTROL LINE R/W. SOME LINES NEED TO BE INPUTS WHILE OTHERS ARE OUTPUTS
2187 |
2188 | void setParallelDir(char* dir){
2189 | if(strcmp(dir, 'IN') == 0){
2190 | DDRL = B00000000;
2191 | }
2192 | else if(strcmp(dir, 'OUT') == 0){
2193 | DDRL = B11111111;
2194 | }
2195 | }
2196 |
2197 |
2198 | void initPins(){
2199 | clearCMD();
2200 | clearRW();
2201 | clearSTRB();
2202 | clearPRES();
2203 | clearPRES();
2204 | setPRES();
2205 | //delay(1);
2206 | clearPRES();
2207 | //delay(1);
2208 | }
2209 |
2210 | void sendData(byte parallelBits){
2211 | DDRL = B11111111;
2212 | PORTL = parallelBits;
2213 | }
2214 |
2215 | byte receiveData(){
2216 | DDRL = B00000000;
2217 | return PINL;
2218 | }
2219 |
2220 |
2221 | void halt(){
2222 | Serial.println(F("Program halted!"));
2223 | while(1);
2224 | }
2225 |
2226 | void setCMD(){
2227 | PORTC = PORTC & B11111110;
2228 | }
2229 |
2230 | void clearCMD(){
2231 | PORTC = PORTC | B00000001;
2232 | }
2233 |
2234 | void setBSY(){
2235 | DDRC = DDRC | B00000010;
2236 | PORTC = PORTC & B11111101;
2237 | }
2238 |
2239 | void clearBSY(){
2240 | DDRC = DDRC | B00000010;
2241 | PORTC = PORTC | B00000010;
2242 | }
2243 |
2244 | void setRW(){
2245 | PORTC = PORTC & B11111011;
2246 | }
2247 |
2248 | void clearRW(){
2249 | PORTC = PORTC | B00000100;
2250 | }
2251 |
2252 | void setSTRB(){
2253 | PORTC = PORTC & B11110111;
2254 | }
2255 |
2256 | void clearSTRB(){
2257 | PORTC = PORTC | B00001000;
2258 | }
2259 | void setPRES(){
2260 | PORTC = PORTC & B11101111;
2261 | }
2262 |
2263 | void clearPRES(){
2264 | PORTC = PORTC | B00010000;
2265 | }
2266 |
2267 | void setPARITY(){
2268 | PORTC = PORTC & B11011111;
2269 | }
2270 |
2271 | void clearPARITY(){
2272 | PORTC = PORTC | B00100000;
2273 | }
2274 | void setPCHK(){
2275 | PORTC = PORTC & B10111111;
2276 | }
2277 |
2278 | void clearPCHK(){
2279 | PORTC = PORTC | B01000000;
2280 | }
2281 |
2282 | void setPOCD(){
2283 | PORTC = PORTC & B01111111;
2284 | }
2285 |
2286 | void clearPOCD(){
2287 | PORTC = PORTC | B10000000;
2288 | }
2289 |
2290 | bool readBsy(){
2291 | DDRC = DDRC & B11111101;
2292 | return bitRead(PINC, 1);
2293 | }
2294 |
2295 | bool readParity(){
2296 | DDRC = DDRC & B11011111;
2297 | return bitRead(PINC, 5);
2298 | }
2299 |
2300 | bool checkParity(byte data, byte parity){
2301 | byte B_cnt = 0;
2302 | for (; data; data >>= 1) {
2303 | if (data & 1) {
2304 | B_cnt++;
2305 | }
2306 | }
2307 | if(B_cnt % 2 == 1 and parity == 0x00){
2308 | return true;
2309 | }
2310 | else if(B_cnt % 2 == 0 and parity == 0x01){
2311 | return true;
2312 | }
2313 | else{
2314 | return false;
2315 | }
2316 | }
2317 |
2318 | bool profileHandshake(){ //Returns true if the handshake succeeds, false if it fails
2319 | bool success = 1;
2320 | setCMD();
2321 | long int startTime = millis();
2322 | while(receiveData() != B00000001 or readBsy() != 0){
2323 | if(millis() - startTime >= 5000){
2324 | //Serial.println(F("Handshake Failed!!!")); //If more than 5 seconds pass and the drive hasn't responded with a $01, halt the program
2325 | success = 0;
2326 | break;
2327 | }
2328 | }
2329 | setSTRB(); //Pulse strobe to tell the drive that we got its $01
2330 | //delay(1);
2331 | clearSTRB();
2332 | //delay(1);
2333 | sendData(0x55); //Respond to the drive's $01 with a $55
2334 | //delay(1);
2335 | setRW(); //Tell the drive that we're writing to it
2336 | //delay(1);
2337 | clearCMD(); //And raise CMD
2338 | //delay(1);
2339 | return success;
2340 | }
2341 |
2342 | byte sendCommandBytes(byte byte0, byte byte1, byte byte2, byte byte3, byte byte4, byte byte5){ //Returns the response byte from the profile or 0xFF if the repsonse is invalid
2343 | while(readBsy() != 1){
2344 |
2345 | }
2346 | byte success;
2347 | setSTRB(); //For some reason, STRB seems to be active high during the command byte phase of the transfer, so set it low to start this phase
2348 | //delay(1);
2349 | sendData(byte0); //Send command byte 0 (command type) and pulse the strobe
2350 | //delay(1);
2351 | clearSTRB();
2352 | //delay(1);
2353 | setSTRB();
2354 | //delay(1);
2355 | sendData(byte1); //Send command byte 1 (byte 0 of block number) and pulse the strobe
2356 | //delay(1);
2357 | clearSTRB();
2358 | //delay(1);
2359 | setSTRB();
2360 | //delay(1);
2361 | sendData(byte2); //Send command byte 2 (byte 1 of block number) and pulse the strobe
2362 | //delay(1);
2363 | clearSTRB();
2364 | //delay(1);
2365 | setSTRB();
2366 | //delay(1);
2367 | sendData(byte3); //Send command byte 3 (byte 2 of block number) and pulse the strobe
2368 | //delay(1);
2369 | clearSTRB();
2370 | //delay(1);
2371 | setSTRB();
2372 | //delay(1);
2373 | sendData(byte4); //Send command byte 4 (retry count) and pulse the strobe
2374 | //delay(1);
2375 | clearSTRB();
2376 | //delay(1);
2377 | setSTRB();
2378 | //delay(1);
2379 | sendData(byte5); //Send command byte 5 (spare threshold) and pulse the strobe
2380 | //delay(1);
2381 | clearSTRB();
2382 | //delay(1);
2383 | setSTRB();
2384 | //delay(1);
2385 | clearSTRB(); //Now the strobe is back in active-low mode again, so clear it after the command bytes are sent
2386 | //delay(1);
2387 | clearRW(); //We want to read the status bytes from the drive now, so set R/W to read mode
2388 | //delay(1);
2389 | setCMD(); //Lower CMD to tell the drive that we're ready for its confirmation byte
2390 | long int startTime = millis();
2391 | while(1){
2392 | success = receiveData();
2393 | if((success == 0x02 or success == 0x03 or success == 0x04 or success == 0x05 or success == 0x06 or success == 0x07) and readBsy() == 0){
2394 | break;
2395 | }
2396 | if(millis() - startTime >= 5000){
2397 | //Serial.println(F("Command Confirmation Failed!!!")); //If more than 5 seconds pass and the drive hasn't responded with an $02, halt the program
2398 | success = 0xFF;
2399 | break;
2400 | }
2401 | }
2402 |
2403 | //delay(1);
2404 | setSTRB(); //Acknowledge that we read the $02 confirmation by pulsing the strobe
2405 | success = receiveData();
2406 | //delay(1);
2407 | clearSTRB();
2408 | setRW(); //Tell the drive that we're writing to the bus and respond to its $02 with a $55
2409 | sendData(0x55);
2410 | //delay(1);
2411 | clearCMD();
2412 | return success;
2413 | }
2414 |
2415 | void readStatusBytes(){ //Returns the byte array containing the status bytes with a fifth byte at the end that's 0xFF if the operation succeeds and 0x00 if the drive never finishes reading the block
2416 | status[5] = 0xFF;
2417 | clearCMD(); //Raise CMD to tell the drive that it's time to read the desired block
2418 | long int startTime = millis();
2419 | while(readBsy() != 1){ //Wait until the drive finishes reading the block and time out if it doesn't finish within 10 seconds
2420 | if(millis() - startTime >= 10000){
2421 | status[5] = 0x00;
2422 | break;
2423 | }
2424 | }
2425 | clearRW(); //Go into read mode to get the status bytes from the drive
2426 | for(int i = 0; i <= 3; i++){ //Fill all 4 bytes of the status array with the status bytes from the drive, pulsing strobe each time we read a byte from the drive
2427 | //delay(1);
2428 | status[i] = receiveData();
2429 | //delay(1);
2430 | setSTRB();
2431 | //delay(1);
2432 | clearSTRB();
2433 | }
2434 | //delay(1);
2435 | }
2436 |
2437 | void readData(){ //Returns the 532-byte array with the data that was read from the drive
2438 | for(int i = 0; i <= 531; i++){ //Read all 532 bytes from the drive into the data array
2439 | data[i] = receiveData();
2440 | //parity[i] = readParity();
2441 | delayMicroseconds(readDelay);
2442 | setSTRB();
2443 | delayMicroseconds(readDelay);
2444 | clearSTRB();
2445 | delayMicroseconds(readDelay);
2446 | }
2447 | clearCMD();
2448 | delayMicroseconds(readDelay);
2449 | }
2450 |
2451 | void writeData(){
2452 | setRW();
2453 | //delay(1);
2454 | clearCMD();
2455 | //delay(1);
2456 | while(readBsy() != 1){
2457 | long int startTime = millis();
2458 | if(millis() - startTime >= 10000){
2459 | Serial.println(F("Drive Never Said It Was Ready To Receive Data!!!"));
2460 | break;
2461 | }
2462 | }
2463 | //delay(1);
2464 | for(int i = 0; i < 532; i++){
2465 | sendData(data[i]);
2466 | delayMicroseconds(readDelay);
2467 | setSTRB();
2468 | delayMicroseconds(readDelay);
2469 | clearSTRB();
2470 | delayMicroseconds(readDelay);
2471 | }
2472 | clearRW();
2473 | //delay(1);
2474 | setCMD();
2475 | //delay(1);
2476 | //setSTRB();
2477 | delayMicroseconds(readDelay);
2478 | while(readBsy() != 0 and receiveData != 0x06){
2479 | long int startTime = millis();
2480 | if(millis() - startTime >= 10000){
2481 | Serial.println(F("Drive never responded after write operation!"));
2482 | break;
2483 | }
2484 | }
2485 | setSTRB();
2486 | //delay(1);
2487 | clearSTRB();
2488 | //delay(1);
2489 | setRW();
2490 | //delay(1);
2491 | sendData(0x55);
2492 | //delay(1);
2493 | clearCMD();
2494 | }
2495 |
2496 | void setLEDColor(bool r, bool g, bool b){
2497 | digitalWrite(red, r);
2498 | digitalWrite(green, g);
2499 | digitalWrite(blue, b);
2500 | }
2501 |
2502 | /*ProFile PD0-PD7 are Arduino pins D2-D9
2503 | CMD is A0
2504 | BSY is A1
2505 | R/W is A2
2506 | STRB is A3
2507 | PRES is A4
2508 | Parity is A5
2509 | PCHK is A6
2510 | POCD is A7 (or you can just ground POCD with the jumper)
2511 |
2512 | PD0-PD7 is Arduino Mega Port L 0-7
2513 | CMD, BSY, R/W, STRB, PRES, Parity, PCHK, and POCD are Arduino Mega Port C 0-7, respectively.*/
2514 |
--------------------------------------------------------------------------------