public boolean read(long byteStart, int byteCount, int arrayOffset, byte bytes[]) throws IOException
	{
		if(byteStart < 0)
			throw new IllegalArgumentException("negative start position");

		final long limit = byteStart + byteCount;
		if(limit > size)
			throw new IllegalArgumentException("exceeds device size");

		if(bytes.length < arrayOffset + byteCount)
			throw new IllegalArgumentException("byte array too short");
	
		long offset = byteStart;
		
		while(offset < limit)
		{
			int remaining = (int) (limit - offset);
			long blockNumber = offset >> 9;
			int currentReadSize;
			
			if((offset & 0x1ff) != 0)
			{
				// unaligned starting address for read
				
				// read the single block that houses this starting address
				byte block[] = new byte[512];
				boolean result = this.readBlocks(blockNumber, 1, 0, block);
				if(! result)
					return false;
				
				// and write the partial result; copy some bytes from the block into the result array
				int indent = (int) (offset & 0x1ff);
				currentReadSize = Math.min(512 - indent, remaining);
				System.arraycopy(block, indent, bytes, arrayOffset, currentReadSize);
			}
			else
			{
				// is an aligned starting address

				// how much to read?  i.e. can read strides of blocks, or a partial?
				if(remaining < 512)
				{
					// read the single block that contains this
					byte block[] = new byte[512];
					boolean result = this.readBlocks(blockNumber, 1, 0, block);
					if(! result)
						return false;

					// and write the partial result; copy some bytes from the block into the result array
					currentReadSize = remaining;
					System.arraycopy(block, 0, bytes, arrayOffset, currentReadSize);
				}
				else
				{
					// the number of whole blocks to read; i.e. round down remaining byte count to the lower block
					currentReadSize = remaining & ~0x1ff;

					// do multi-block I/O (depends on layer supporting this; the layer might just do block-by-block I/O, after all, but do multi if supported)
					int blockCount = currentReadSize >> 9;
					boolean result = this.readBlocks(blockNumber, blockCount, arrayOffset, bytes);
					if(! result)
						return false;
				}
			}

			// advance source / destination pointers
			offset += currentReadSize;
			arrayOffset += currentReadSize;
		}
		
		return true;
	}