Prevent overflow of the KernelBase->kb_Interrupts array when initialising IRQs. This...
[AROS.git] / workbench / devs / ramdrive_device.c
blob2961ed0d74a5a660a2a7abfff45fa95db8f0e9a6
1 /*
2 Copyright © 1995-2017, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 /****************************************************************************************/
8 #define NUM_HEADS 2
9 #define NUM_CYL 80
10 #define NUM_SECS 11
11 #define BLOCKSIZE 512
13 #define DISKSIZE (NUM_HEADS * NUM_CYL * NUM_SECS * BLOCKSIZE)
14 #define NUM_TRACKS (NUM_CYL * NUM_HEADS)
16 /****************************************************************************************/
18 #include <devices/trackdisk.h>
19 #include <devices/newstyle.h>
20 #include <exec/resident.h>
21 #include <exec/errors.h>
22 #include <exec/memory.h>
23 #include <exec/initializers.h>
24 #include <proto/exec.h>
25 #include <dos/dosextens.h>
26 #include <dos/dostags.h>
27 #include <proto/dos.h>
28 #include <aros/macros.h>
29 #include <aros/libcall.h>
30 #include <aros/symbolsets.h>
31 #include <string.h>
33 #if defined(__GNUC__) || defined(__INTEL_COMPILER)
34 #include "ramdrive_device_gcc.h"
35 #endif
37 #define DEBUG 0
38 #include <aros/debug.h>
40 #include LC_LIBDEFS_FILE
42 /****************************************************************************************/
44 #define NEWSTYLE_DEVICE 1
46 #if NEWSTYLE_DEVICE
48 static const UWORD SupportedCommands[] =
50 CMD_FLUSH,
51 CMD_READ,
52 CMD_WRITE,
53 CMD_UPDATE,
54 CMD_CLEAR,
55 TD_CHANGENUM,
56 TD_CHANGESTATE,
57 TD_PROTSTATUS,
58 TD_REMOVE,
59 TD_ADDCHANGEINT,
60 TD_REMCHANGEINT,
61 TD_GETDRIVETYPE,
62 TD_GETNUMTRACKS,
63 TD_FORMAT,
64 TD_RAWREAD,
65 TD_RAWWRITE,
66 TD_SEEK,
67 TD_MOTOR,
68 ETD_READ,
69 ETD_WRITE,
70 ETD_UPDATE,
71 ETD_CLEAR,
72 ETD_MOTOR,
73 ETD_SEEK,
74 ETD_FORMAT,
75 ETD_RAWREAD,
76 ETD_RAWWRITE,
77 NSCMD_DEVICEQUERY,
81 #endif
83 /****************************************************************************************/
85 static void FormatOFS(UBYTE *mem, ULONG number, struct unit *unit);
87 /****************************************************************************************/
89 static int GM_UNIQUENAME(Init)(LIBBASETYPEPTR ramdrivebase)
91 D(bug("ramdrive_device: in libinit func\n"));
93 InitSemaphore(&ramdrivebase->sigsem);
94 NEWLIST((struct List *)&ramdrivebase->units);
95 memset( &ramdrivebase->port, 0, sizeof( ramdrivebase->port ) );
96 ramdrivebase->port.mp_Node.ln_Type = NT_MSGPORT;
97 ramdrivebase->port.mp_Flags = PA_SIGNAL;
98 ramdrivebase->port.mp_SigBit = SIGB_SINGLE;
99 NEWLIST((struct List *)&ramdrivebase->port.mp_MsgList);
101 D(bug("ramdrive_device: in libinit func. Returning %x (success) :-)\n", ramdrivebase));
102 return TRUE;
105 /****************************************************************************************/
107 AROS_UFP3(LONG, unitentry,
108 AROS_UFPA(STRPTR, argstr, A0),
109 AROS_UFPA(ULONG, arglen, D0),
110 AROS_UFPA(struct ExecBase *, SysBase, A6));
112 /****************************************************************************************/
114 static int GM_UNIQUENAME(Open)
116 LIBBASETYPEPTR ramdrivebase,
117 struct IOExtTD *iotd,
118 ULONG unitnum,
119 ULONG flags
122 static const struct TagItem tags[] =
124 { NP_Name , (IPTR)"Ram Drive Unit Process"},
125 { NP_Input , 0 },
126 { NP_Output , 0 },
127 { NP_Error , 0 },
128 { NP_CurrentDir , 0 },
129 { NP_Priority , 0 },
130 { NP_HomeDir , 0 },
131 { NP_CopyVars , 0 },
132 { NP_Entry , (IPTR)unitentry },
133 { TAG_END , 0 }
135 struct unit *unit;
137 D(bug("ramdrive_device: in libopen func.\n"));
139 if (iotd->iotd_Req.io_Message.mn_Length < sizeof(struct IOExtTD))
141 D(bug("ramdrive.device/open: IORequest structure passed to OpenDevice is too small!\n"));
142 iotd->iotd_Req.io_Error = IOERR_OPENFAIL;
143 return FALSE;
146 D(bug("ramdrive_device: in libopen func. Looking if unit is already open\n"));
148 ObtainSemaphore(&ramdrivebase->sigsem);
150 for(unit = (struct unit *)ramdrivebase->units.mlh_Head;
151 unit->msg.mn_Node.ln_Succ != NULL;
152 unit = (struct unit *)unit->msg.mn_Node.ln_Succ)
153 if(unit->unitnum == unitnum)
155 unit->usecount++;
156 ReleaseSemaphore(&ramdrivebase->sigsem);
158 iotd->iotd_Req.io_Unit = (struct Unit *)unit;
159 iotd->iotd_Req.io_Error = 0;
160 iotd->iotd_Req.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
162 D(bug("ramdrive_device: in libopen func. Yep. Unit is already open\n"));
164 return TRUE;
167 D(bug("ramdrive_device: in libopen func. No, it is not. So creating new unit ...\n"));
169 unit = (struct unit *)AllocMem(sizeof(struct unit), MEMF_PUBLIC);
170 if(unit != NULL)
172 D(bug("ramdrive_device: in libopen func. Allocation of unit memory okay. Setting up unit and calling CreateNewProc ...\n"));
174 unit->usecount = 1;
175 unit->ramdrivebase = ramdrivebase;
176 unit->unitnum = unitnum;
177 unit->msg.mn_ReplyPort = &ramdrivebase->port;
178 unit->msg.mn_Length = sizeof(struct unit);
179 unit->port.mp_Node.ln_Type = NT_MSGPORT;
180 unit->port.mp_Flags = PA_IGNORE;
181 unit->port.mp_SigTask = CreateNewProc((struct TagItem *)tags);
183 D(bug("ramdrive_device: in libopen func. CreateNewProc called. Proc = %x\n", unit->port.mp_SigTask));
185 if(unit->port.mp_SigTask != NULL)
187 NEWLIST((struct List *)&unit->port.mp_MsgList);
189 /* setup replyport to point to active task */
190 ramdrivebase->port.mp_SigTask = FindTask(NULL);
191 SetSignal(0, SIGF_SINGLE);
193 D(bug("ramdrive_device: in libopen func. Sending startup msg\n"));
194 PutMsg(&((struct Process *)unit->port.mp_SigTask)->pr_MsgPort, &unit->msg);
196 D(bug("ramdrive_device: in libopen func. Waiting for replymsg\n"));
197 WaitPort(&ramdrivebase->port);
198 (void)GetMsg(&ramdrivebase->port);
199 D(bug("ramdrive_device: in libopen func. Received replymsg\n"));
201 if(unit->mem)
203 AddTail((struct List *)&ramdrivebase->units, &unit->msg.mn_Node);
204 iotd->iotd_Req.io_Unit = (struct Unit *)unit;
205 /* Set returncode */
206 iotd->iotd_Req.io_Error = 0;
207 ReleaseSemaphore(&ramdrivebase->sigsem);
208 return TRUE;
209 }else
210 iotd->iotd_Req.io_Error = TDERR_NotSpecified;
211 }else
212 iotd->iotd_Req.io_Error = TDERR_NoMem;
213 FreeMem(unit, sizeof(struct unit));
214 }else
215 iotd->iotd_Req.io_Error = TDERR_NoMem;
217 ReleaseSemaphore(&ramdrivebase->sigsem);
219 return FALSE;
222 /****************************************************************************************/
224 static int GM_UNIQUENAME(Close)
226 LIBBASETYPEPTR ramdrivebase,
227 struct IOExtTD *iotd
230 struct unit *unit;
232 ObtainSemaphore(&ramdrivebase->sigsem);
233 unit = (struct unit *)iotd->iotd_Req.io_Unit;
234 if(!--unit->usecount)
236 Remove(&unit->msg.mn_Node);
237 ramdrivebase->port.mp_SigTask = FindTask(NULL);
238 SetSignal(0, SIGF_SINGLE);
239 PutMsg(&unit->port, &unit->msg);
240 WaitPort(&ramdrivebase->port);
241 (void)GetMsg(&ramdrivebase->port);
242 FreeMem(unit, sizeof(struct unit));
244 ReleaseSemaphore(&ramdrivebase->sigsem);
246 return TRUE;
249 /****************************************************************************************/
251 ADD2INITLIB(GM_UNIQUENAME(Init), 0)
252 ADD2OPENDEV(GM_UNIQUENAME(Open), 0)
253 ADD2CLOSEDEV(GM_UNIQUENAME(Close), 0)
255 /****************************************************************************************/
257 AROS_LH1(void, beginio,
258 AROS_LHA(struct IOExtTD *, iotd, A1),
259 struct ramdrivebase *, ramdrivebase, 5, Ramdrive)
261 AROS_LIBFUNC_INIT
263 switch(iotd->iotd_Req.io_Command)
265 #if NEWSTYLE_DEVICE
266 case NSCMD_DEVICEQUERY:
267 if(iotd->iotd_Req.io_Length < ((LONG)OFFSET(NSDeviceQueryResult, SupportedCommands)) + sizeof(UWORD *))
269 iotd->iotd_Req.io_Error = IOERR_BADLENGTH;
271 else
273 struct NSDeviceQueryResult *d;
275 d = (struct NSDeviceQueryResult *)iotd->iotd_Req.io_Data;
277 d->DevQueryFormat = 0;
278 d->SizeAvailable = sizeof(struct NSDeviceQueryResult);
279 d->DeviceType = NSDEVTYPE_TRACKDISK;
280 d->DeviceSubType = 0;
281 d->SupportedCommands = (UWORD *)SupportedCommands;
283 iotd->iotd_Req.io_Actual = sizeof(struct NSDeviceQueryResult);
284 iotd->iotd_Req.io_Error = 0;
287 break;
288 #endif
290 case TD_CHANGENUM:
291 /* result: io_Actual = disk change counter */
293 case TD_CHANGESTATE:
294 /* result: io_Actual = disk presence indicator (0 = disk is in drive) */
296 case TD_PROTSTATUS:
297 /* result: io_Actual = disk protection status (0 = not protected) */
299 iotd->iotd_Req.io_Actual = 0;
300 iotd->iotd_Req.io_Error = 0;
301 break;
303 case ETD_UPDATE:
304 case CMD_UPDATE:
305 case ETD_CLEAR:
306 case CMD_CLEAR:
307 case TD_REMOVE:
308 case TD_ADDCHANGEINT:
309 case TD_REMCHANGEINT:
310 /* Ignore but don't fail */
311 iotd->iotd_Req.io_Error = 0;
312 break;
314 case TD_GETDRIVETYPE:
315 iotd->iotd_Req.io_Actual = DRIVE3_5;
316 iotd->iotd_Req.io_Error = 0;
317 break;
319 case TD_GETNUMTRACKS:
320 iotd->iotd_Req.io_Actual = NUM_TRACKS;
321 iotd->iotd_Req.io_Error = 0;
322 break;
324 case CMD_FLUSH:
326 struct IOExtTD *flushed_iotd;
327 struct unit *u =(struct unit *)iotd->iotd_Req.io_Unit;
328 Forbid();
329 while((flushed_iotd = (struct IOExtTD *)GetMsg(&u->port)))
331 flushed_iotd->iotd_Req.io_Error = IOERR_ABORTED;
332 ReplyMsg(&flushed_iotd->iotd_Req.io_Message);
334 Permit();
336 break;
338 case ETD_READ:
339 case CMD_READ:
340 case ETD_WRITE:
341 case CMD_WRITE:
342 case ETD_FORMAT:
343 case TD_FORMAT:
344 case ETD_RAWREAD:
345 case TD_RAWREAD:
346 case ETD_RAWWRITE:
347 case TD_RAWWRITE:
348 case ETD_SEEK:
349 case TD_SEEK:
350 case ETD_MOTOR:
351 case TD_MOTOR:
352 /* Not done quick */
353 iotd->iotd_Req.io_Flags &= ~IOF_QUICK;
355 /* Forward to unit thread */
356 PutMsg(&((struct unit *)iotd->iotd_Req.io_Unit)->port,
357 &iotd->iotd_Req.io_Message);
358 return;
360 default:
361 /* Not supported */
362 iotd->iotd_Req.io_Error = IOERR_NOCMD;
363 break;
365 } /* switch(iotd->iotd_Req.io_Command) */
367 /* WaitIO will look into this */
368 iotd->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
370 /* Finish message */
371 if(!(iotd->iotd_Req.io_Flags&IOF_QUICK))
372 ReplyMsg(&iotd->iotd_Req.io_Message);
374 AROS_LIBFUNC_EXIT
377 /****************************************************************************************/
379 AROS_LH1(LONG, abortio,
380 AROS_LHA(struct IOExtTD *, iotd, A1),
381 struct ramdrivebase *, ramdrivebase, 6, Ramdrive)
383 AROS_LIBFUNC_INIT
385 return IOERR_NOCMD;
387 AROS_LIBFUNC_EXIT
390 /****************************************************************************************/
392 AROS_LH0(STRPTR, killrad0,
393 struct ramdrivebase *, ramdrivebase, 7, Ramdrive)
395 AROS_LIBFUNC_INIT
397 /* FIXME: KillRAD0 not implemented yet */
399 return 0;
401 AROS_LIBFUNC_EXIT
404 /****************************************************************************************/
406 AROS_LH1(STRPTR, killrad,
407 AROS_LHA(ULONG, unit, D0),
408 struct ramdrivebase *, ramdrivebase, 8, Ramdrive)
410 AROS_LIBFUNC_INIT
412 /* FIXME: KillRAD not implemented yet */
414 return 0;
416 AROS_LIBFUNC_EXIT
419 /****************************************************************************************/
421 #define ramdrivebase unit->ramdrivebase
423 /****************************************************************************************/
425 static LONG read(struct unit *unit, struct IOExtTD *iotd)
427 STRPTR buf;
428 ULONG offset, size;
430 D(bug("ramdrive_device/read: offset = %d size = %d\n", iotd->iotd_Req.io_Offset, iotd->iotd_Req.io_Length));
432 if(iotd->iotd_SecLabel)
434 D(bug("ramdrive_device/read: iotd->iotd_SecLabel is != NULL -> returning IOERR_NOCMD\n"));
435 return IOERR_NOCMD;
438 buf = iotd->iotd_Req.io_Data;
439 offset = iotd->iotd_Req.io_Offset;
440 size = iotd->iotd_Req.io_Length;
442 unit->headpos = offset;
444 if (offset + size > DISKSIZE)
446 D(bug("ramdrive_device/read: Seek to offset %d failed. Returning TDERR_SeekError\n", offset));
447 return TDERR_SeekError;
450 CopyMem(&unit->mem[offset], buf, size);
452 iotd->iotd_Req.io_Actual = size;
454 #if DEBUG
455 buf = iotd->iotd_Req.io_Data;
456 D(bug("ramdrive_device/read: returning 0. First 4 buffer bytes = [%c%c%c%c]\n", buf[0], buf[1], buf[2], buf[3]));
457 #endif
459 return 0;
462 /****************************************************************************************/
464 static LONG write(struct unit *unit, struct IOExtTD *iotd)
466 STRPTR buf;
467 ULONG offset, size;
469 if(iotd->iotd_SecLabel)
470 return IOERR_NOCMD;
472 buf = iotd->iotd_Req.io_Data;
473 offset = iotd->iotd_Req.io_Offset;
474 size = iotd->iotd_Req.io_Length;
476 unit->headpos = offset;
478 if (offset + size > DISKSIZE)
480 D(bug("ramdrive_device/write: Seek to offset %d failed. Returning TDERR_SeekError\n", offset));
481 return TDERR_SeekError;
484 iotd->iotd_Req.io_Actual = size;
486 CopyMem(buf, &unit->mem[offset], size);
488 return 0;
491 /****************************************************************************************/
493 AROS_UFH3(LONG, unitentry,
494 AROS_UFHA(STRPTR, argstr, A0),
495 AROS_UFHA(ULONG, arglen, D0),
496 AROS_UFHA(struct ExecBase *, SysBase, A6))
498 AROS_USERFUNC_INIT
500 struct Process *me;
501 LONG err = 0L;
502 struct IOExtTD *iotd;
503 struct unit *unit;
505 D(bug("ramdrive_device/unitentry: just started\n"));
507 me = (struct Process *)FindTask(NULL);
509 WaitPort(&me->pr_MsgPort);
510 unit = (struct unit *)GetMsg(&me->pr_MsgPort);
511 unit->port.mp_SigBit = AllocSignal(-1);
512 unit->port.mp_Flags = PA_SIGNAL;
514 D(bug("ramdrive_device/unitentry: Trying to allocate memory disk\n"));
516 unit->mem = AllocVec(DISKSIZE, MEMF_PUBLIC | MEMF_CLEAR);
517 if(!unit->mem)
519 D(bug("ramdrive_device/unitentry: Memory allocation failed :-( Replying startup msg.\n"));
521 Forbid();
522 ReplyMsg(&unit->msg);
523 return 0;
526 D(bug("ramdrive_device/unitentry: Memory allocation okay :-) Replying startup msg.\n"));
528 FormatOFS(unit->mem, unit->unitnum, unit);
530 ReplyMsg(&unit->msg);
532 D(bug("ramdrive_device/unitentry: Now entering main loop\n"));
534 for(;;)
536 while((iotd = (struct IOExtTD *)GetMsg(&unit->port)) != NULL)
538 if(&iotd->iotd_Req.io_Message == &unit->msg)
540 D(bug("ramdrive_device/unitentry: Recevied EXIT message.\n"));
542 FreeVec(unit->mem);
543 Forbid();
544 ReplyMsg(&unit->msg);
545 return 0;
548 switch(iotd->iotd_Req.io_Command)
550 case ETD_RAWREAD:
551 case TD_RAWREAD:
553 ** same as CMD_READ, but offset does not have to be multiple of
554 ** BLOCKSIZE
556 ** fall through
559 case ETD_READ:
560 case CMD_READ:
561 D(bug("ramdrive_device/unitentry: received CMD_READ.\n"));
562 err = read(unit, iotd);
563 break;
565 case ETD_RAWWRITE:
566 case TD_RAWWRITE:
568 ** same as CMD_WRITE, but offset does not have to be multiple of
569 ** BLOCKSIZE
571 ** fall through
574 case ETD_WRITE:
575 case CMD_WRITE:
576 case ETD_FORMAT:
577 case TD_FORMAT:
578 D(bug("ramdrive_device/unitentry: received %s\n", (iotd->iotd_Req.io_Command == CMD_WRITE) ? "CMD_WRITE" : "TD_FORMAT"));
579 err = write(unit, iotd);
580 break;
582 case ETD_MOTOR:
583 case TD_MOTOR:
585 ** DOS wants the previous state in io_Actual.
586 ** We return "!io_Actual"
589 iotd->iotd_Req.io_Actual = (iotd->iotd_Req.io_Actual == 1) ? 0 : 1;
590 err = 0;
591 break;
593 case ETD_SEEK:
594 case TD_SEEK:
595 unit->headpos = iotd->iotd_Req.io_Actual;
596 err = 0;
597 break;
599 } /* switch(iotd->iotd_Req.io_Command) */
601 iotd->iotd_Req.io_Error = err;
602 ReplyMsg(&iotd->iotd_Req.io_Message);
604 } /* while((iotd = (struct IOExtTD *)GetMsg(&unit->port)) != NULL) */
606 WaitPort(&unit->port);
608 } /* for(;;) */
610 AROS_USERFUNC_EXIT
613 /****************************************************************************************/
615 /* The following routines are based on TurboDevice by Thomas Dreibholz */
617 /****************************************************************************************/
619 static ULONG CalcRootBlock(void)
621 return NUM_CYL * NUM_HEADS * NUM_SECS / 2;
624 /****************************************************************************************/
626 static ULONG CalcBitMap(void)
628 return CalcRootBlock() + 1;
631 /****************************************************************************************/
633 VOID RootBlockCheckSum(UBYTE *buf)
635 LONG checksum, *long_ptr;
636 LONG i;
638 long_ptr = (ULONG *)buf;
639 checksum = 0;
641 for(i = 0; i < TD_SECTOR / 4; i++)
643 checksum += AROS_BE2LONG(long_ptr[i]);
645 long_ptr[5] = AROS_LONG2BE(-checksum);
648 /****************************************************************************************/
650 VOID CalcBitMapCheckSum(UBYTE *buf)
652 LONG checksum, i;
653 LONG *long_ptr = (LONG *)buf;
655 for(i = 1, checksum = 0; i < TD_SECTOR / 4; i++)
657 checksum += AROS_BE2LONG(long_ptr[i]);
659 long_ptr[0] = AROS_LONG2BE(-checksum);
662 /****************************************************************************************/
664 VOID InstallRootBlock(UBYTE *buf, STRPTR diskname, ULONG bitmap,
665 struct unit *unit)
667 struct DateStamp ds;
668 ULONG *long_ptr;
669 LONG i;
671 long_ptr = (ULONG *)buf;
672 long_ptr[0] = AROS_LONG2BE(2);
673 long_ptr[3] = AROS_LONG2BE(72);
674 long_ptr[78] = AROS_LONG2BE(-1);
675 long_ptr[79] = AROS_LONG2BE(bitmap);
676 long_ptr[127] = AROS_LONG2BE(1);
678 DateStamp(&ds);
680 long_ptr[121] = AROS_LONG2BE(ds.ds_Days);
681 long_ptr[122] = AROS_LONG2BE(ds.ds_Minute);
682 long_ptr[123] = AROS_LONG2BE(ds.ds_Tick);
684 long_ptr[105] = AROS_LONG2BE(ds.ds_Days);
685 long_ptr[106] = AROS_LONG2BE(ds.ds_Minute);
686 long_ptr[107] = AROS_LONG2BE(ds.ds_Tick);
688 buf[432] = (UBYTE)strlen(diskname);
690 for(i = 0; i < strlen(diskname); i++)
692 buf[433+i] = diskname[i];
695 RootBlockCheckSum(buf);
698 /****************************************************************************************/
700 static ULONG CalcBlocks(void)
702 return NUM_CYL * NUM_HEADS * NUM_SECS - 1;
705 /****************************************************************************************/
707 static void AllocBitMapBlock(LONG block, UBYTE *buf)
709 ULONG *long_ptr = (ULONG *)buf;
710 LONG longword, bit;
711 LONG old_long, new_long;
713 longword = (block - 2) / 32;
714 bit = block - 2 - longword * 32;
715 old_long = AROS_BE2LONG(long_ptr[longword + 1]);
716 new_long = old_long & (0xFFFFFFFF - (1L << bit));
718 long_ptr[longword + 1] = AROS_LONG2BE(new_long);
721 /****************************************************************************************/
723 static void FreeBitMapBlock(LONG block, UBYTE *buf)
725 ULONG *long_ptr = (ULONG *)buf;
726 LONG longword, bit;
727 LONG old_long, new_long;
729 longword = (block - 2) / 32;
730 bit = block - 2 - longword * 32;
731 old_long = AROS_BE2LONG(long_ptr[longword + 1]);
732 new_long = old_long | (1L << bit);
734 long_ptr[longword + 1] = AROS_LONG2BE(new_long);
737 /****************************************************************************************/
739 static void FormatOFS(UBYTE *mem, ULONG number, struct unit *unit)
741 ULONG a,b,c,d;
742 UBYTE *cmem;
743 UBYTE Name[6];
745 mem[0]='D';
746 mem[1]='O';
747 mem[2]='S';
748 mem[3]=0x00;
750 a = CalcRootBlock();
751 b = CalcBitMap();
753 cmem = mem + (a * TD_SECTOR);
754 strcpy(Name, "RAM_#");
755 Name[4] = '0' + number;
757 InstallRootBlock(cmem, Name, b, unit);
758 cmem = mem + (b * TD_SECTOR);
759 d = CalcBlocks();
760 for(c = 2; c <= d; c++)
762 FreeBitMapBlock(c, cmem);
765 AllocBitMapBlock(a, cmem);
766 AllocBitMapBlock(b, cmem);
768 CalcBitMapCheckSum(cmem);
771 /****************************************************************************************/
773 const char end = 0;
775 /****************************************************************************************/