PMFloppy - a Floppy disk utility
Public Domain software
Copyright 1990, Greg Bryant
PMFloppy is a generic floppy disk utility. It currently can copy and compare
disks, and save their "images" into a file on a hard drive. It is a
multi-threaded application, using background threads to prevent slowing down
Presentation Manager. It allows many disks (up to twenty) to be read into
memory at the same time. It does not require any disk swapping. Look for
further additions in the future.
PMFloppy was written as an exercise in PM programming. I wanted to put to
use some of the things I learned at the PM Apps class, and extend it a little
to some topics not covered. I was looking for a task that would lend itself
well to multi-threading, when I ran across a PD program called DSKCPY by
Brady Flowers. Since DSKCPY was character-oriented, I decided to wrap a
PM interface around it and use the lengthy disk interactions as background
All the standard disclaimers about damage to data, hardware, personal sanity,
etc caused by the program apply. This is public domain software, which means
you may not be charged any fees other than shipping and handling costs to
PMFloppy requires a system running OS/2 1.2 or higher. Both FAT and HPFS
file systems are supported. Copy the file "PMFLOPPY.EXE" to your OS2
subdirectory. From PM open the Utilities Group from the Desktop Manager.
Pull down the Program Menu and select New. You will be presented with a
dialog window. Enter "PMFloppy" in the Program Title box, and
"C:\OS2\PMFLOPPY.EXE" in the Path and File Name box. If you wish to put the
executable file elsewhere, you may, but be sure to change the path accordingly.
Click on Add, and you're done.
Full source code is being distributed. The extra files are not necessary to
run the program, but are included if you wish to make modifications to the
code, or use the code as a resource for your own PM programs.
If you wish to rearrange the order of the items in the group, click on the
icon of the file you would like to move with the right mouse button, and drag
it to the desired position in the list. Release the button, and the programs
will shift to make room for the change.
The distribution disk or archive should include the following files:
pmfloppy.exe ; executable file
pmfloppy.doc ; this documentation
pmfloppy.rc ; main resource file
pmfloppy.c ; front end source code
pmfloppy.h ; defines for main program
pmfloppy.def ; MSC define file
pmfloppy.ico ; incredible artwork
pmfloppy ; make file
copydlgs.c ; dialog procs source code
copydlgs.dlg ; resource file for dialog windows
copydlgs.h ; defines for dialog windows
dskim.c ; read and write the disk image
dskim.h ; header for disk image
m.cmd ; make command file
dskcpy2a.zip ; Brady's original program - requires PKUNZIP 1.02 or higher
If all these files are not present, please feel free to request the complete
file set. I am currently on Internet at [email protected]
various BBSs such as:
Kyle's BBS (513) 236-7085
Channel 1 (617) 354-8873
Gilmore Systems (805) 582-9306
III. Running PMFloppy
When PMFloppy is running, you are faced with a simple menu bar. You can select
Disk, Image, or Exit. At the bottom of the window, the status of the "images"
is displayed. Images are the internal representations of the data on the
disk, as well as some information about the disk itself. At program start,
all images will be empty, and the status will reflect that. You should never
see an empty screen.
If you select Disk, a drop-down menu will give you three options: Read, Write,
and About. The Read option allows you to read a disk into an image. The
Write option will allow you to write an image back out to a floppy disk. The
About option pops up an about box containing copyright information and the
current version number. At program start, the Write option will not be
selectable, because there are no images to write.
The Image selection produces a drop-down menu with four options: Load, Save,
Delete, and Compare. The Load option allows you to load an image from a file
on a disk, and the Save option will produce one of those files from an image.
The Delete option allows you to remove an image from memory. This _will_not_
erase any files from any disk. The program has room for twenty images, and if
you use all of those, or if you run out of swap space, you may need to delete
one or more of the images already loaded. The Compare option allows you to
compare two images.
The Exit option closes the program. If an image is in use, reading, writing,
or whatever, a warning box will be displayed. Select Cancel to return to the
Keyboard Accellerators have been set for all drop-down menu items. To bypass
the menus, use the control key and the first letter of the option you want to
invoke. For example, to read a disk into memory, hit ^R (control-R).
All of the dialog boxes that you will see bear certain features in common. All
have OK and Cancel buttons. If the OK button is clicked when an incorrect
value has been entered for one of the fields, a message box will be displayed
to tell you what the program doesn't like. Many of these boxes also have
suggestions for correcting the error.
In any situation where there is a list box (a box with a single vertical scroll
bar), there will invariably be an edit box directly above it. You may enter
text in any edit box. You can only select text from a list box. There are two
types of scroll box-edit box combinations in the program, the image set, and
the file set.
The first is the image set. You may enter an image name into the image edit
field, or select an existing image from the list box. A single click on a name
from the list box will move that name to the edit box, and a double click
selects that name and exits the dialog. This is the same as a single click
each on the list box and then on the OK button. If the program detects an
error, an appropriate message will be displayed.
Next is the file set. In the file set, there are two list boxes side by side,
and two boxes above - one for editting and one for display only. The display
box will show the current directory and can only be changed by double-clicking
on an entry in the left list box. The edit box displays the current file. You
can either type in a file name, or select one from the right list box. Double
clicking in the right list box is the same as a single click on the list box
and then on the OK button.
Changes from 1.00
o Multiple buffers
o Load, Save, Delete, and Compare functions
o better management of threads
o better memory usage
o better usage of messages
o removed multiple access to a single image
o removed format teaser
o an error during a drive read or write will leave the status display in an
o opening the drive door during a disk operation doesn't bring up an error
until it's closed again. This is due to the way the driver handles the
"disk change" line rather than an actual bug in the program.
some thoughts for the future:
o show graphically the percentage done (modify the DisplayStatus routine)
o anonymous copies (no explicit image) disk-to-disk or disk-to-file.
o compress the disk image as saved to a file.
o provide a Format option
This program was compiled with MS C 6.00 running under NCR's version of MS OS/2
1.2 beta on an NCR 386sx/MCA. This program is not supported or endorsed
V. Technical Discussion
The changes to Brady's code are relatively minor. Apparently the
BIOSPARAMETERBLOCK bug has been fixed in 1.2, because my header file matches
what he had defined locally. Since they were the same, I removed the local
copy. The OpenDrive funtion has been integrated into the disk functions.
This is to keep the drive handles local to the thread. Also, errors are now
handled through a common error handler, which posts a message to the parent
window - this is because background threads aren't allowed into the
presentation space (or, if they are, it wasn't obvious to me). Thus, I have
three classes of messages being passed forward - Error, Status, and General.
Currently the "General" messages are only passing Done up, but I wanted to
leave it open for future use.
Ah, the format. This has caused me quite a bit of pain. I really thought I
had this one beaten, but I'm out of "play time", and it still doesn't work.
If you enable the menu option (in the WM_INITMENU case for the main window),
you can see what it does currently. It will go through the disk, formatting
each track (and dieing on errors - perfect disks only). It will then build
the root track based on an algorithm from Writing OS/2 Device Drivers (by
Raymond Westwater, Addison-Wesley, p. 496), but when I go to write out the
track, the IOCtl returns an invalid parameter. Other unsolved format issues
involve detecting a low density disk (unformatted) in a high density drive.
I leave the resolution to this problem as an exercise to the reader.
Having only recently come to the OS/2 world (from a wide variety of other OSs),
and being relatively unwashed in the intricacies of MSC and PM, I ran into
a few oddities only to be resolved by Spy and Logitech's Multiscope. These I
thought I would share, hopefully to prevent others from following the same
road. First, the disappearing window. When I got to the point that my
main window was coming up fine, and I was spinning off threads that didn't do
anything beyond opening the drive, I added the first real task - the IOCtl to
get the BPB, and then calculate the disk parameters. This is just straight
out of what Brady had done, so I felt relatively secure. Well, for some
reason, shortly after the IOCtl, the window would close and the program would
shut down. No messages, no warnings, no phone calls, just gone. I set Spy
to work, and found that my window was receiving a WM_DESTROY message. Well,
I certainly wasn't sending it, so it had to be PM. According to multiscope,
I was just doing some assignment statements in my background thread when it
died. Not til I looked closely at the assignments did I realize there was a
divide by zero.
Conclusion 1: Certain run-time errors will cause a PM app to shut down without
Now, why was there a divide by zero? This was just straight out of Brady's
original program. I went back and recompiled his code under my make, and
found that his did the same thing. Again, multiscope to the rescue, the
IOCtl was returning fine, the data was there and fine, but the structure wasn't
interpreting it correctly. Linker alignment? No, actually compiler alignment.
As I said, I'm new to MSC, and they have a -Zp parameter for packing. Not
that it's documented anywhere on the IOCtl call, but Brady's program used it,
and when I created my make, I left it off. Problem solved.
Conclusion 2: Beware of compiler alignment as well as Linker alignment.
The invisible dialog: This one was actually simple, but again, it might save
someone a few minutes. A couple times I had dialog boxes that looked fine,
all the code was in place, but they just wouldn't appear. It turned out that
one of my resource IDs was incorrect in the .DLG file. Nobody would complain,
but they also wouldn't comply.
PM programming is actually quite interesting. The quality of the tools is
very good. I've been playing recently with other programs, like Smalltalk/V
PM and Object/1. I've done some great prototypes very quickly with ST/V, but
both products seem to need to mature a bit in the PM world. Thanks again to
Brady Flowers for the disk handling routines. Now I'd better get back to
something that'll make the company money - unfortunately, I can't spend all
my time just learning about the new stuff.
You may have noticed that the format option is gone. Completely. If I get
new information, I may try it again, put I decided it was silly to have two
versions go by with the same teaser.
I've made two main design changes in the program. The first is the primary
data structures. There are now two. First is the Thread Context structure.
This allows me to keep track of errors and threads. The second is the Disk
Image. This stores status information about the image as well as the disk
data itself. They are relatively straight forward. The second main change
involves memory allocation. In Brady's original code, he was allocating
space on a track-by-track basis. However, when you try to allocate pointers
for more than two disks (at 160 per disk), things start to get lost. For
some reason, around track 120 on the third disk (not a consistent location),
the program would GP fault during the read. Not during the allocate - that
(seemingly) worked fine. So I ended up doing a single allocation for each
disk via the DosAllocHuge. For a 1.44MB floppy, this means 23 selectors per
disk. You'll notice I have two new routines - one to do the allocation and
one to create a pointer for each track. If you're working with something like
this that has many parts, these two routines (or a variation thereon) should
come in handy.
The only bug I found in version 1.0 involved not releasing stack memory after
a thread had completed. As the fix was part of the data structure redesign,
I decided not to do a point release, but just point out that it has been
corrected. The fix also brought out a shortcoming in Thread Management. I
needed a way to wait until a background had completely died before freeing
its stack allocation. Without some kind of wait, the foreground thread would
often beat the background thread, so when the BG thread went to exit, it no
longer had an execution context, causing a GP fault. Since the only wait
functions are for child processes rather than threads (and they don't work for
threads, I tried), I ended up doing a busy wait, checking the thread's
priority. It's not real pretty, but it does work. I'm open to a more elegant
I've changed the user defined messages to a simpler design, one I think is a
bit more elegant than the brute-force method I used in 1.00. The General
message is now an official "Done" message, and the Status and Error messages
are now generic to all background operations. There are two new messages
being used for Compare. Compare is a very special case, because it uses two
images, where all other operations use one. This compromised me design a
little, but I think inclusion of the function was worth it.
A further note on the disappearing dialog from version 1. I've noticed that
if any resources that a program expects are not present, it just doesn't run.
Again, no messages. I noticed this on a commercial package that required its
own font. At first I forgot to install the font. The program wouldn't run,
and OS/2 wouldn't tell me why it wouldn't.
Incidentally, there is a typo in Quickhelp in the Memory Manager Overview
section. The example shows the digit two being shifted by the shift count.
It should be a one. The text preceeding the example is correct.
Discovered that you couldn't use the keyboard to select a directory.
An incorrect check of the Busy flag in the WriteDlgProc prevented a buffer
from ever being written. I had changed my busy checks several times, and
this one must have slipped by. Actually found by someone in our QA group
here. On a non-compare thread, FreeThread would overrun the ImageBuffers
array. This didn't *appear* to be causing any problems, but I'd rather be
safe . . .
It seems the latest PM driver from MS (177) cares a little more about the
EXPORTS statement in the DEF file. I didn't have all my dialog procs exported,
and under earlier versions, it worked fine. Now, however, if I try to call
a proc via the WinCreateWindow, or any of its derivatives, that hasn't been
exported, the entire system starts beeping and locks up. The mouse responds,
but the app locks - I don't even get the 10 second timeout from Switch To.