New bitmap method SetRGBConversionFunction which can be used to
[tangerine.git] / workbench / devs / ramdrive_device.c
blob35a3d108cd45123905ef5c8bace332c04d14ed64
1 /*
2 Copyright © 1995-2006, 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 ramdrivebase->port.mp_Node.ln_Type = NT_MSGPORT;
96 ramdrivebase->port.mp_Flags = PA_SIGNAL;
97 ramdrivebase->port.mp_SigBit = SIGB_SINGLE;
98 NEWLIST((struct List *)&ramdrivebase->port.mp_MsgList);
100 D(bug("ramdrive_device: in libinit func. Returning %x (success) :-)\n", ramdrivebase));
101 return TRUE;
104 /****************************************************************************************/
106 AROS_UFP3(LONG, unitentry,
107 AROS_UFPA(STRPTR, argstr, A0),
108 AROS_UFPA(ULONG, arglen, D0),
109 AROS_UFPA(struct ExecBase *, SysBase, A6));
111 /****************************************************************************************/
113 static int GM_UNIQUENAME(Open)
115 LIBBASETYPEPTR ramdrivebase,
116 struct IOExtTD *iotd,
117 ULONG unitnum,
118 ULONG flags
121 static const struct TagItem tags[] =
123 { NP_Name , (IPTR)"Ram Drive Unit Process"},
124 { NP_Input , 0 },
125 { NP_Output , 0 },
126 { NP_Error , 0 },
127 { NP_CurrentDir , 0 },
128 { NP_Priority , 0 },
129 { NP_HomeDir , 0 },
130 { NP_CopyVars , 0 },
131 { NP_Entry , (IPTR)unitentry },
132 { TAG_END , 0 }
134 struct unit *unit;
136 D(bug("ramdrive_device: in libopen func.\n"));
138 if (iotd->iotd_Req.io_Message.mn_Length < sizeof(struct IOExtTD))
140 D(bug("ramdrive.device/open: IORequest structure passed to OpenDevice is too small!\n"));
141 iotd->iotd_Req.io_Error = IOERR_OPENFAIL;
142 return FALSE;
145 D(bug("ramdrive_device: in libopen func. Looking if unit is already open\n"));
147 ObtainSemaphore(&ramdrivebase->sigsem);
149 for(unit = (struct unit *)ramdrivebase->units.mlh_Head;
150 unit->msg.mn_Node.ln_Succ != NULL;
151 unit = (struct unit *)unit->msg.mn_Node.ln_Succ)
152 if(unit->unitnum == unitnum)
154 unit->usecount++;
155 ReleaseSemaphore(&ramdrivebase->sigsem);
157 iotd->iotd_Req.io_Unit = (struct Unit *)unit;
158 iotd->iotd_Req.io_Error = 0;
159 iotd->iotd_Req.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
161 D(bug("ramdrive_device: in libopen func. Yep. Unit is already open\n"));
163 return TRUE;
166 D(bug("ramdrive_device: in libopen func. No, it is not. So creating new unit ...\n"));
168 unit = (struct unit *)AllocMem(sizeof(struct unit), MEMF_PUBLIC);
169 if(unit != NULL)
171 D(bug("ramdrive_device: in libopen func. Allocation of unit memory okay. Setting up unit and calling CreateNewProc ...\n"));
173 unit->usecount = 1;
174 unit->ramdrivebase = ramdrivebase;
175 unit->unitnum = unitnum;
176 unit->msg.mn_ReplyPort = &ramdrivebase->port;
177 unit->msg.mn_Length = sizeof(struct unit);
178 unit->port.mp_Node.ln_Type = NT_MSGPORT;
179 unit->port.mp_Flags = PA_IGNORE;
180 unit->port.mp_SigTask = CreateNewProc((struct TagItem *)tags);
182 D(bug("ramdrive_device: in libopen func. CreateNewProc called. Proc = %x\n", unit->port.mp_SigTask));
184 if(unit->port.mp_SigTask != NULL)
186 NEWLIST((struct List *)&unit->port.mp_MsgList);
188 /* setup replyport to point to active task */
189 ramdrivebase->port.mp_SigTask = FindTask(NULL);
190 SetSignal(0, SIGF_SINGLE);
192 D(bug("ramdrive_device: in libopen func. Sending startup msg\n"));
193 PutMsg(&((struct Process *)unit->port.mp_SigTask)->pr_MsgPort, &unit->msg);
195 D(bug("ramdrive_device: in libopen func. Waiting for replymsg\n"));
196 WaitPort(&ramdrivebase->port);
197 (void)GetMsg(&ramdrivebase->port);
198 D(bug("ramdrive_device: in libopen func. Received replymsg\n"));
200 if(unit->mem)
202 AddTail((struct List *)&ramdrivebase->units, &unit->msg.mn_Node);
203 iotd->iotd_Req.io_Unit = (struct Unit *)unit;
204 /* Set returncode */
205 iotd->iotd_Req.io_Error = 0;
206 ReleaseSemaphore(&ramdrivebase->sigsem);
207 return TRUE;
208 }else
209 iotd->iotd_Req.io_Error = TDERR_NotSpecified;
210 }else
211 iotd->iotd_Req.io_Error = TDERR_NoMem;
212 FreeMem(unit, sizeof(struct unit));
213 }else
214 iotd->iotd_Req.io_Error = TDERR_NoMem;
216 ReleaseSemaphore(&ramdrivebase->sigsem);
218 return FALSE;
221 /****************************************************************************************/
223 static int GM_UNIQUENAME(Close)
225 LIBBASETYPEPTR ramdrivebase,
226 struct IOExtTD *iotd
229 struct unit *unit;
231 ObtainSemaphore(&ramdrivebase->sigsem);
232 unit = (struct unit *)iotd->iotd_Req.io_Unit;
233 if(!--unit->usecount)
235 Remove(&unit->msg.mn_Node);
236 ramdrivebase->port.mp_SigTask = FindTask(NULL);
237 SetSignal(0, SIGF_SINGLE);
238 PutMsg(&unit->port, &unit->msg);
239 WaitPort(&ramdrivebase->port);
240 (void)GetMsg(&ramdrivebase->port);
241 FreeMem(unit, sizeof(struct unit));
243 ReleaseSemaphore(&ramdrivebase->sigsem);
245 return TRUE;
248 /****************************************************************************************/
250 ADD2INITLIB(GM_UNIQUENAME(Init), 0)
251 ADD2OPENDEV(GM_UNIQUENAME(Open), 0)
252 ADD2CLOSEDEV(GM_UNIQUENAME(Close), 0)
254 /****************************************************************************************/
256 AROS_LH1(void, beginio,
257 AROS_LHA(struct IOExtTD *, iotd, A1),
258 struct ramdrivebase *, ramdrivebase, 5, Ramdrive)
260 AROS_LIBFUNC_INIT
262 switch(iotd->iotd_Req.io_Command)
264 #if NEWSTYLE_DEVICE
265 case NSCMD_DEVICEQUERY:
266 if(iotd->iotd_Req.io_Length < ((LONG)OFFSET(NSDeviceQueryResult, SupportedCommands)) + sizeof(UWORD *))
268 iotd->iotd_Req.io_Error = IOERR_BADLENGTH;
270 else
272 struct NSDeviceQueryResult *d;
274 d = (struct NSDeviceQueryResult *)iotd->iotd_Req.io_Data;
276 d->DevQueryFormat = 0;
277 d->SizeAvailable = sizeof(struct NSDeviceQueryResult);
278 d->DeviceType = NSDEVTYPE_TRACKDISK;
279 d->DeviceSubType = 0;
280 d->SupportedCommands = (UWORD *)SupportedCommands;
282 iotd->iotd_Req.io_Actual = sizeof(struct NSDeviceQueryResult);
283 iotd->iotd_Req.io_Error = 0;
286 break;
287 #endif
289 case TD_CHANGENUM:
290 /* result: io_Actual = disk change counter */
292 case TD_CHANGESTATE:
293 /* result: io_Actual = disk presence indicator (0 = disk is in drive) */
295 case TD_PROTSTATUS:
296 /* result: io_Actual = disk protection status (0 = not protected) */
298 iotd->iotd_Req.io_Actual = 0;
299 iotd->iotd_Req.io_Error = 0;
300 break;
302 case ETD_UPDATE:
303 case CMD_UPDATE:
304 case ETD_CLEAR:
305 case CMD_CLEAR:
306 case TD_REMOVE:
307 case TD_ADDCHANGEINT:
308 case TD_REMCHANGEINT:
309 /* Ignore but don't fail */
310 iotd->iotd_Req.io_Error = 0;
311 break;
313 case TD_GETDRIVETYPE:
314 iotd->iotd_Req.io_Actual = DRIVE3_5;
315 iotd->iotd_Req.io_Error = 0;
316 break;
318 case TD_GETNUMTRACKS:
319 iotd->iotd_Req.io_Actual = NUM_TRACKS;
320 iotd->iotd_Req.io_Error = 0;
321 break;
323 case CMD_FLUSH:
325 struct IOExtTD *flushed_iotd;
326 struct unit *u =(struct unit *)iotd->iotd_Req.io_Unit;
327 Forbid();
328 while((flushed_iotd = (struct IOExtTD *)GetMsg(&u->port)))
330 flushed_iotd->iotd_Req.io_Error = IOERR_ABORTED;
331 ReplyMsg(&flushed_iotd->iotd_Req.io_Message);
333 Permit();
335 break;
337 case ETD_READ:
338 case CMD_READ:
339 case ETD_WRITE:
340 case CMD_WRITE:
341 case ETD_FORMAT:
342 case TD_FORMAT:
343 case ETD_RAWREAD:
344 case TD_RAWREAD:
345 case ETD_RAWWRITE:
346 case TD_RAWWRITE:
347 case ETD_SEEK:
348 case TD_SEEK:
349 case ETD_MOTOR:
350 case TD_MOTOR:
351 /* Not done quick */
352 iotd->iotd_Req.io_Flags &= ~IOF_QUICK;
354 /* Forward to unit thread */
355 PutMsg(&((struct unit *)iotd->iotd_Req.io_Unit)->port,
356 &iotd->iotd_Req.io_Message);
357 return;
359 default:
360 /* Not supported */
361 iotd->iotd_Req.io_Error = IOERR_NOCMD;
362 break;
364 } /* switch(iotd->iotd_Req.io_Command) */
366 /* WaitIO will look into this */
367 iotd->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
369 /* Finish message */
370 if(!(iotd->iotd_Req.io_Flags&IOF_QUICK))
371 ReplyMsg(&iotd->iotd_Req.io_Message);
373 AROS_LIBFUNC_EXIT
376 /****************************************************************************************/
378 AROS_LH1(LONG, abortio,
379 AROS_LHA(struct IOExtTD *, iotd, A1),
380 struct ramdrivebase *, ramdrivebase, 6, Ramdrive)
382 AROS_LIBFUNC_INIT
384 return IOERR_NOCMD;
386 AROS_LIBFUNC_EXIT
389 /****************************************************************************************/
391 AROS_LH0(STRPTR, killrad0,
392 struct ramdrivebase *, ramdrivebase, 7, Ramdrive)
394 AROS_LIBFUNC_INIT
396 #warning KillRAD0 not implemented yet
398 return 0;
400 AROS_LIBFUNC_EXIT
403 /****************************************************************************************/
405 AROS_LH1(STRPTR, killrad,
406 AROS_LHA(ULONG, unit, D0),
407 struct ramdrivebase *, ramdrivebase, 8, Ramdrive)
409 AROS_LIBFUNC_INIT
411 #warning KillRAD not implemented yet
413 return 0;
415 AROS_LIBFUNC_EXIT
418 /****************************************************************************************/
420 #define ramdrivebase unit->ramdrivebase
422 /****************************************************************************************/
424 static LONG read(struct unit *unit, struct IOExtTD *iotd)
426 STRPTR buf;
427 ULONG offset, size;
429 D(bug("ramdrive_device/read: offset = %d size = %d\n", iotd->iotd_Req.io_Offset, iotd->iotd_Req.io_Length));
431 if(iotd->iotd_SecLabel)
433 D(bug("ramdrive_device/read: iotd->iotd_SecLabel is != NULL -> returning IOERR_NOCMD\n"));
434 return IOERR_NOCMD;
437 buf = iotd->iotd_Req.io_Data;
438 offset = iotd->iotd_Req.io_Offset;
439 size = iotd->iotd_Req.io_Length;
441 unit->headpos = offset;
443 if (offset + size > DISKSIZE)
445 D(bug("ramdrive_device/read: Seek to offset %d failed. Returning TDERR_SeekError\n", offset));
446 return TDERR_SeekError;
449 CopyMem(&unit->mem[offset], buf, size);
451 iotd->iotd_Req.io_Actual = size;
453 #if DEBUG
454 buf = iotd->iotd_Req.io_Data;
455 D(bug("ramdrive_device/read: returning 0. First 4 buffer bytes = [%c%c%c%c]\n", buf[0], buf[1], buf[2], buf[3]));
456 #endif
458 return 0;
461 /****************************************************************************************/
463 static LONG write(struct unit *unit, struct IOExtTD *iotd)
465 STRPTR buf;
466 ULONG offset, size;
468 if(iotd->iotd_SecLabel)
469 return IOERR_NOCMD;
471 buf = iotd->iotd_Req.io_Data;
472 offset = iotd->iotd_Req.io_Offset;
473 size = iotd->iotd_Req.io_Length;
475 unit->headpos = offset;
477 if (offset + size > DISKSIZE)
479 D(bug("ramdrive_device/write: Seek to offset %d failed. Returning TDERR_SeekError\n", offset));
480 return TDERR_SeekError;
483 iotd->iotd_Req.io_Actual = size;
485 CopyMem(buf, &unit->mem[offset], size);
487 return 0;
490 /****************************************************************************************/
492 #ifdef SysBase
493 #undef SysBase
494 #endif
496 /****************************************************************************************/
498 AROS_UFH3(LONG, unitentry,
499 AROS_UFHA(STRPTR, argstr, A0),
500 AROS_UFHA(ULONG, arglen, D0),
501 AROS_UFHA(struct ExecBase *, SysBase, A6))
503 AROS_USERFUNC_INIT
505 struct Process *me;
506 LONG err = 0L;
507 struct IOExtTD *iotd;
508 struct unit *unit;
510 D(bug("ramdrive_device/unitentry: just started\n"));
512 me = (struct Process *)FindTask(NULL);
514 WaitPort(&me->pr_MsgPort);
515 unit = (struct unit *)GetMsg(&me->pr_MsgPort);
516 unit->port.mp_SigBit = AllocSignal(-1);
517 unit->port.mp_Flags = PA_SIGNAL;
519 D(bug("ramdrive_device/unitentry: Trying to allocate memory disk\n"));
521 unit->mem = AllocVec(DISKSIZE, MEMF_PUBLIC | MEMF_CLEAR);
522 if(!unit->mem)
524 D(bug("ramdrive_device/unitentry: Memory allocation failed :-( Replying startup msg.\n"));
526 Forbid();
527 ReplyMsg(&unit->msg);
528 return 0;
531 D(bug("ramdrive_device/unitentry: Memory allocation okay :-) Replying startup msg.\n"));
533 FormatOFS(unit->mem, unit->unitnum, unit);
535 ReplyMsg(&unit->msg);
537 D(bug("ramdrive_device/unitentry: Now entering main loop\n"));
539 for(;;)
541 while((iotd = (struct IOExtTD *)GetMsg(&unit->port)) != NULL)
543 if(&iotd->iotd_Req.io_Message == &unit->msg)
545 D(bug("ramdrive_device/unitentry: Recevied EXIT message.\n"));
547 FreeVec(unit->mem);
548 Forbid();
549 ReplyMsg(&unit->msg);
550 return 0;
553 switch(iotd->iotd_Req.io_Command)
555 case ETD_RAWREAD:
556 case TD_RAWREAD:
558 ** same as CMD_READ, but offset does not have to be multiple of
559 ** BLOCKSIZE
561 ** fall through
564 case ETD_READ:
565 case CMD_READ:
566 D(bug("ramdrive_device/unitentry: received CMD_READ.\n"));
567 err = read(unit, iotd);
568 break;
570 case ETD_RAWWRITE:
571 case TD_RAWWRITE:
573 ** same as CMD_WRITE, but offset does not have to be multiple of
574 ** BLOCKSIZE
576 ** fall through
579 case ETD_WRITE:
580 case CMD_WRITE:
581 case ETD_FORMAT:
582 case TD_FORMAT:
583 D(bug("ramdrive_device/unitentry: received %s\n", (iotd->iotd_Req.io_Command == CMD_WRITE) ? "CMD_WRITE" : "TD_FORMAT"));
584 err = write(unit, iotd);
585 break;
587 case ETD_MOTOR:
588 case TD_MOTOR:
590 ** DOS wants the previous state in io_Actual.
591 ** We return "!io_Actual"
594 iotd->iotd_Req.io_Actual = (iotd->iotd_Req.io_Actual == 1) ? 0 : 1;
595 err = 0;
596 break;
598 case ETD_SEEK:
599 case TD_SEEK:
600 unit->headpos = iotd->iotd_Req.io_Actual;
601 err = 0;
602 break;
604 } /* switch(iotd->iotd_Req.io_Command) */
606 iotd->iotd_Req.io_Error = err;
607 ReplyMsg(&iotd->iotd_Req.io_Message);
609 } /* while((iotd = (struct IOExtTD *)GetMsg(&unit->port)) != NULL) */
611 WaitPort(&unit->port);
613 } /* for(;;) */
615 AROS_USERFUNC_EXIT
618 /****************************************************************************************/
620 /* The following routines are based on TurboDevice by Thomas Dreibholz */
622 /****************************************************************************************/
624 static ULONG CalcRootBlock(void)
626 return NUM_CYL * NUM_HEADS * NUM_SECS / 2;
629 /****************************************************************************************/
631 static ULONG CalcBitMap(void)
633 return CalcRootBlock() + 1;
636 /****************************************************************************************/
638 VOID RootBlockCheckSum(UBYTE *buf)
640 LONG checksum, *long_ptr;
641 LONG i;
643 long_ptr = (ULONG *)buf;
644 checksum = 0;
646 for(i = 0; i < TD_SECTOR / 4; i++)
648 checksum += AROS_BE2LONG(long_ptr[i]);
650 long_ptr[5] = AROS_LONG2BE(-checksum);
653 /****************************************************************************************/
655 VOID CalcBitMapCheckSum(UBYTE *buf)
657 LONG checksum, i;
658 LONG *long_ptr = (LONG *)buf;
660 for(i = 1, checksum = 0; i < TD_SECTOR / 4; i++)
662 checksum += AROS_BE2LONG(long_ptr[i]);
664 long_ptr[0] = AROS_LONG2BE(-checksum);
667 /****************************************************************************************/
669 VOID InstallRootBlock(UBYTE *buf, STRPTR diskname, ULONG bitmap,
670 struct unit *unit)
672 struct DateStamp ds;
673 ULONG *long_ptr;
674 LONG i;
676 long_ptr = (ULONG *)buf;
677 long_ptr[0] = AROS_LONG2BE(2);
678 long_ptr[3] = AROS_LONG2BE(72);
679 long_ptr[78] = AROS_LONG2BE(-1);
680 long_ptr[79] = AROS_LONG2BE(bitmap);
681 long_ptr[127] = AROS_LONG2BE(1);
683 DateStamp(&ds);
685 long_ptr[121] = AROS_LONG2BE(ds.ds_Days);
686 long_ptr[122] = AROS_LONG2BE(ds.ds_Minute);
687 long_ptr[123] = AROS_LONG2BE(ds.ds_Tick);
689 long_ptr[105] = AROS_LONG2BE(ds.ds_Days);
690 long_ptr[106] = AROS_LONG2BE(ds.ds_Minute);
691 long_ptr[107] = AROS_LONG2BE(ds.ds_Tick);
693 buf[432] = (UBYTE)strlen(diskname);
695 for(i = 0; i < strlen(diskname); i++)
697 buf[433+i] = diskname[i];
700 RootBlockCheckSum(buf);
703 /****************************************************************************************/
705 static ULONG CalcBlocks(void)
707 return NUM_CYL * NUM_HEADS * NUM_SECS - 1;
710 /****************************************************************************************/
712 static void AllocBitMapBlock(LONG block, UBYTE *buf)
714 ULONG *long_ptr = (ULONG *)buf;
715 LONG longword, bit;
716 LONG old_long, new_long;
718 longword = (block - 2) / 32;
719 bit = block - 2 - longword * 32;
720 old_long = AROS_BE2LONG(long_ptr[longword + 1]);
721 new_long = old_long & (0xFFFFFFFF - (1L << bit));
723 long_ptr[longword + 1] = AROS_LONG2BE(new_long);
726 /****************************************************************************************/
728 static void FreeBitMapBlock(LONG block, UBYTE *buf)
730 ULONG *long_ptr = (ULONG *)buf;
731 LONG longword, bit;
732 LONG old_long, new_long;
734 longword = (block - 2) / 32;
735 bit = block - 2 - longword * 32;
736 old_long = AROS_BE2LONG(long_ptr[longword + 1]);
737 new_long = old_long | (1L << bit);
739 long_ptr[longword + 1] = AROS_LONG2BE(new_long);
742 /****************************************************************************************/
744 static void FormatOFS(UBYTE *mem, ULONG number, struct unit *unit)
746 ULONG a,b,c,d;
747 UBYTE *cmem;
748 UBYTE Name[6];
750 mem[0]='D';
751 mem[1]='O';
752 mem[2]='S';
753 mem[3]=0x00;
755 a = CalcRootBlock();
756 b = CalcBitMap();
758 cmem = mem + (a * TD_SECTOR);
759 strcpy(Name, "RAM_#");
760 Name[4] = '0' + number;
762 InstallRootBlock(cmem, Name, b, unit);
763 cmem = mem + (b * TD_SECTOR);
764 d = CalcBlocks();
765 for(c = 2; c <= d; c++)
767 FreeBitMapBlock(c, cmem);
770 AllocBitMapBlock(a, cmem);
771 AllocBitMapBlock(b, cmem);
773 CalcBitMapCheckSum(cmem);
776 /****************************************************************************************/
778 const char end = 0;
780 /****************************************************************************************/