diff --git a/ProDOS/Bitmap.cpp b/ProDOS/Bitmap.cpp new file mode 100644 index 0000000..57ed601 --- /dev/null +++ b/ProDOS/Bitmap.cpp @@ -0,0 +1,200 @@ +#include + +#include +#include +#include "auto.h" + + +using namespace ProDOS; + +// returns # of 1-bits set (0-8) +inline static unsigned popCount(uint8_t x) +{ + #ifdef __GNUC__ + return __builtin_popcount(x); + #endif + + // Brian Kernighan / Peter Wegner in CACM 3 (1960), 322. + + unsigned count; + for (count = 0; x; ++count) + { + x &= x - 1; + } + return count; +} + + + +Bitmap::Bitmap(unsigned blocks) +{ + _blocks = _freeBlocks = blocks; + + _bitmapBlocks = (blocks + 4095) / 4096; + _freeIndex = 0; + + unsigned bitmapSize = _bitmapBlocks * 512; + unsigned blockSize = blocks / 8; + + auto_array bitmap(new uint8_t[bitmapSize]); + + // mark overflow in use, everything else free. + + std::memset(bitmap, 0xff, blocks / 8); + std::memset(bitmap + blockSize, 0x00, bitmapSize - blockSize); + + // edge case + unsigned tmp = blocks & 0x07; + + bitmap[blocks / 8] = ~(0xff >> tmp); + + _bitmap = bitmap.release(); + + +} + +Bitmap::Bitmap(BlockDevice *device, unsigned keyPointer, unsigned blocks) +{ + _blocks = blocks; + _freeBlocks = 0; + _freeIndex = 0; + + _bitmapBlocks = (blocks + 4095) / 4096; + + unsigned bitmapSize = _bitmapBlocks * 512; + unsigned blockSize = blocks / 8; + + auto_array bitmap(new uint8_t[bitmapSize]); + + for (unsigned i = 0; i < blockSize; ++i) + { + device->read(keyPointer + i, bitmap + 512 * i); + } + + // make sure all trailing bits are marked in use. + + // edge case + unsigned tmp = blocks & 0x07; + + bitmap[blocks / 8] &= ~(0xff >> tmp); + + std::memset(bitmap + blockSize, 0x00, bitmapSize - blockSize); + + // set _freeBlocks and _freeIndex; + for (unsigned i = 0; i < (blocks + 7) / 8; ++i) + { + _freeBlocks += popCount(bitmap[i]); + } + + if (_freeBlocks) + { + for (unsigned i = 0; i < (blocks + 7) / 8; ++i) + { + if (bitmap[i]) + { + _freeIndex = i; + break; + } + } + } + + + _bitmap = bitmap.release(); +} + +Bitmap::~Bitmap() +{ + if (_bitmap) delete []_bitmap; +} + + +void Bitmap::freeBlock(unsigned block) +{ + if (block >= _blocks) return; + + unsigned index = block / 8; + unsigned offset = block & 0x07; + unsigned mask = 0x80 >> offset; + + uint8_t tmp = _bitmap[index]; + + if ((tmp & mask) == 0) + { + ++_freeBlocks; + _bitmap[index] = tmp | mask; + } +} + + +int Bitmap::allocBlock(unsigned block) +{ + if (block >= _blocks) return -1; + + + unsigned index = block / 8; + unsigned offset = block & 0x07; + unsigned mask = 0x80 >> offset; + + uint8_t tmp = _bitmap[index]; + + if ((tmp & mask)) + { + --_freeBlocks; + _bitmap[index] = tmp & ~mask; + return block; + } + + return -1; +} + + +int Bitmap::allocBlock() +{ + if (!_freeBlocks) return -1; + + unsigned freeIndex = _freeIndex; + unsigned maxIndex = (_blocks + 7) / 8; + + + for (unsigned index = _freeIndex; index < maxIndex; ++index) + { + uint8_t tmp = _bitmap[index]; + if (!tmp) continue; + + unsigned mask = 0x80; + for (unsigned offset = 0; offset < 8; ++offset) + { + if (tmp & mask) + { + _freeIndex = index; + _bitmap[index] = tmp & ~mask; + --_freeBlocks; + return index * 8 + offset; + } + mask = mask >> 1; + } + } + + for (unsigned index = 0; index < freeIndex; ++index) + { + uint8_t tmp = _bitmap[index]; + if (!tmp) continue; + + unsigned mask = 0x80; + for (unsigned offset = 0; offset < 8; ++offset) + { + if (tmp & mask) + { + _freeIndex = index; + _bitmap[index] = tmp & ~mask; + --_freeBlocks; + return index * 8 + offset; + } + mask = mask >> 1; + } + } + + + // should never happen... + return -1; +} \ No newline at end of file diff --git a/ProDOS/Bitmap.h b/ProDOS/Bitmap.h new file mode 100644 index 0000000..77388a6 --- /dev/null +++ b/ProDOS/Bitmap.h @@ -0,0 +1,48 @@ +#ifndef __BITMAP_H__ +#define __BITMAP_H__ + +#include + + + +namespace ProDOS { + +class BlockDevice; + + +class Bitmap { +public: + + Bitmap(unsigned blocks); + Bitmap(BlockDevice *device, unsigned keyPointer, unsigned blocks); + //todo -- constructor by loading from, block device... + ~Bitmap(); + + int allocBlock(); + int allocBlock(unsigned block); + + void freeBlock(unsigned block); + + + unsigned freeBlocks() const { return _freeBlocks; } + unsigned blocks() const { return _blocks; } + unsigned bitmapBlocks() const { return _bitmapBlocks; } + unsigned bitmapSize() const { return _bitmapBlocks * 512; } + const void *bitmap() const { return _bitmap; } + +private: + + unsigned _freeIndex; + unsigned _freeBlocks; + + unsigned _blocks; + unsigned _bitmapBlocks; + + uint8_t *_bitmap; +}; + + + +} + +#endif diff --git a/ProDOS/DateTime.cpp b/ProDOS/DateTime.cpp new file mode 100644 index 0000000..7a8ce55 --- /dev/null +++ b/ProDOS/DateTime.cpp @@ -0,0 +1,144 @@ + + +#include +#include +#include + +#include + +using namespace ProDOS; + +/* + * date is a 16 bit value: + * + * 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * | Year | Month | Day | + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * + * time is a 16 bit value: + * + * 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * |0 0 0| Hour | |0 0| Minute | + * +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + * + */ + + +/* + * ProDOS technote 28 + * + * The following definition allows the same range of years that the Apple IIgs + * Control Panel CDA currently does: + * + * o A seven-bit ProDOS year value is in the range 0 to 99 + * (100 through 127 are invalid) + * o Year values from 40 to 99 represent 1940 through 1999 + * o Year values from 0 to 39 represent 2000 through 2039 + */ + + +DateTime::DateTime() : + _yymmdd(0), _hhmm(0) +{ + init(std::time(NULL)); +} + +DateTime::DateTime(uint32_t dtm) : + _yymmdd((dtm >> 16) & 0xff), _hhmm(dtm & 0xff) +{ +} + +DateTime::DateTime(unsigned yymmdd, unsigned hhmm) : + _yymmdd(yymmdd), _hhmm(hhmm) +{ +} + +DateTime::DateTime(std::time_t time) : + _yymmdd(0), _hhmm(0) +{ + init(time); +} + + +DateTime::DateTime(unsigned year, unsigned month, unsigned day, + unsigned hour, unsigned minute) : + _yymmdd(0), _hhmm(0) +{ + init(year, month, day, hour, minute); +} + +void DateTime::init(std::time_t time) +{ + tm t; + ::localtime_r(&time, &t); + init(t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min); +} + +void DateTime::init(unsigned year, unsigned month, unsigned day, unsigned hour, unsigned minute) +{ + + //printf("%d %d %d %d %d\n", year, month, day, hour, minute); + + // 1940 is the earliest year, so clamp to 1940-01-01 00:00 + if (year < 1940) + { + _yymmdd = (40 << 9) | (1 << 5) | 1; + _hhmm = 0; + return; + } + // 2039 is the latest year, so clamp to 2039-12-31 23:59 + if (year > 2039) + { + _yymmdd = (39 << 9) | (12 << 5) | 31; + _hhmm = (23 << 8) | 59; + return; + } + + if (year >= 2000) year -= 2000; + else year -= 1900; + + _hhmm = minute | (hour << 8); + + _yymmdd = day | (month << 5) | (year << 9); +} + +unsigned DateTime::year() const +{ + unsigned tmp = _yymmdd >> 9; + //if (tmp > 100) return 0; + + if (tmp <= 39) tmp += 100; + + return tmp + 1900; +} + +/* + * A positive or 0 value for tm_isdst causes mktime() to presume initially + * that Daylight Savings Time, respectively, is or is not in effect for + * the specified time. A negative value for tm_isdst causes mktime() to + * attempt to determine whether Daylight Saving Time is in effect for the + * specified time. + */ + +std::time_t DateTime::toUnix() const +{ + tm t; + + if (_yymmdd == 0) return 0; + + std::memset(&t, 0, sizeof(tm)); + + t.tm_min = minute(); + t.tm_hour = hour(); + t.tm_isdst = -1; + + t.tm_mday = day(); + t.tm_mon = month() - 1; + t.tm_year = year() - 1900; + + return std::mktime(&t); + // convert back via locatime & fudge for dst? +} + diff --git a/ProDOS/DateTime.h b/ProDOS/DateTime.h new file mode 100644 index 0000000..9f3aa78 --- /dev/null +++ b/ProDOS/DateTime.h @@ -0,0 +1,96 @@ +#ifndef __PRODOS_DATE_TIME__ +#define __PRODOS_DATE_TIME__ + +#include +#include + +namespace ProDOS { + +class DateTime +{ +public: + DateTime(); + DateTime(std::time_t); + DateTime(uint32_t); + DateTime(unsigned, unsigned); + + DateTime(unsigned year, unsigned month, unsigned day, + unsigned hour, unsigned minute); + + std::time_t toUnix() const; + + + operator std::time_t() const; + operator uint32_t() const; + + unsigned date() const; + unsigned time() const; + + unsigned minute() const; + unsigned hour() const; + unsigned day() const; + unsigned month() const; + unsigned year() const; + +private: + void init(std::time_t); + void init(unsigned, unsigned, unsigned, unsigned, unsigned); + + unsigned _yymmdd; + unsigned _hhmm; +}; + + +inline DateTime::operator std::time_t() const +{ + return toUnix(); +} + +inline DateTime::operator uint32_t() const +{ + return (_yymmdd << 16) | _hhmm; +} + +inline unsigned DateTime::date() const +{ + return _yymmdd; +} + +inline unsigned DateTime::time() const +{ + return _hhmm; +} + +inline unsigned DateTime::minute() const +{ + return _hhmm & 0x3f; +} + +inline unsigned DateTime::hour() const +{ + return (_hhmm >> 8) & 0x1f; +} + +inline unsigned DateTime::day() const +{ + return _yymmdd & 0x1f; +} + +inline unsigned DateTime::month() const +{ + return (_yymmdd >> 5) & 0x0f; +} + +/* +inline unsigned DateTime::year() const +{ + unsigned tmp = _yymmdd >> 9; + if (tmp <= 39) tmp += 100; + return tmp + 1900; +} +*/ + +} // namespace + +#endif + diff --git a/ProDOS/Makefile b/ProDOS/Makefile new file mode 100644 index 0000000..84af29e --- /dev/null +++ b/ProDOS/Makefile @@ -0,0 +1,7 @@ +CC = g++ +CPPFLAGS += -g -Wall -I../ + + +all : DateTime.o + +DateTime.o : DateTime.cpp DateTime.h