diff --git a/disk.c b/disk.c index 3051343..1b7d2fc 100644 --- a/disk.c +++ b/disk.c @@ -100,7 +100,7 @@ uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_ // TODO Do error handling if (!useCHS) { - // LBA Read + // LBA Write v86disk_addr_packet.start_block = sector; v86disk_addr_packet.blocks = count; v86disk_addr_packet.transfer_buffer = diff --git a/kernel.c b/kernel.c index b1d8f49..de29253 100644 --- a/kernel.c +++ b/kernel.c @@ -309,6 +309,7 @@ void FileSelect() { reload = 1; break; case KEY_P: + if (IsDir(&entries[fileHovered])) break; File83ToPath((char*)entries[fileHovered].name, (char*)¤t_path[current_path_end]); create_child(0x380000, (uintptr_t)ProgramLoadTest, 2, current_path, &vi); SetVideo25Lines(); @@ -316,6 +317,7 @@ void FileSelect() { reload = 1; break; case KEY_X: + if (IsDir(&entries[fileHovered])) break; File83ToPath((char*)entries[fileHovered].name, (char*)¤t_path[current_path_end]); //HexViewTest(path, &vi); create_child(0x380000, (uintptr_t)HexViewTest, 2, current_path, &vi); @@ -324,6 +326,7 @@ void FileSelect() { reload = 1; break; case KEY_T: + if (IsDir(&entries[fileHovered])) break; File83ToPath((char*)entries[fileHovered].name, (char*)¤t_path[current_path_end]); //TextViewTest(path, &vi); create_child(0x380000, (uintptr_t)TextViewTest, 2, current_path, &vi); diff --git a/progs.c b/progs.c index 67d4320..da49f07 100644 --- a/progs.c +++ b/progs.c @@ -5,62 +5,275 @@ void HexViewTest(uint8_t *path, VOLINFO *vi) { uint32_t err; uint16_t *vga_text = (uint16_t *)0xb8000; + uint32_t screenSize = 80*25; uint8_t *scratch = (uint8_t *)0x20000; + + const uint32_t blockSize = 0x10000; // 64K + const uint32_t blockMask = ~0xffff; // block = (offset & blockMask) + const uint32_t blockShift = 16; // blockSize = 1 << blockShift + const uint32_t maxFileSize = 0x80000000; // 2GB + const uint32_t totalBlocks = maxFileSize / blockSize; + // TODO This hackish code means we should really be loading these + // programs rather than having them all be in the kernel itself + uint16_t (*writtenMap)[totalBlocks] = + (uint16_t (*)[totalBlocks])0x400000; + uint32_t (*blockLenMap)[totalBlocks] = + (uint32_t (*)[totalBlocks])((uintptr_t)writtenMap + sizeof(*writtenMap)); + uint8_t (*writeStore)[blockSize] = + (uint8_t (*)[blockSize])((uintptr_t)blockLenMap + sizeof(*blockLenMap)); + for (int i = 0; i < totalBlocks; i++) + (*writtenMap)[i] = 0; + uint8_t *screenBuff = *writeStore; + // First two blocks are screen buffer + uint32_t nextFreeBlock = 2; + FILEINFO fi; - err = DFS_OpenFile(vi, path, DFS_READ, scratch, &fi); + vga_text = (uint16_t *)0xb8000; + for (int i = 0; i < 80*50; i++) + vga_text[i] = 0x0f00; + err = DFS_OpenFile(vi, path, DFS_READ | DFS_WRITE, scratch, &fi); if (err) { vga_text += printStr("Open Error: ", vga_text); printDword(err, vga_text); + kbd_wait(); return; } - uint32_t successcount; - uint32_t readOffset = 0, lastReadOffset = -1; + if (fi.filelen == 0) { + vga_text += printStr("File ", vga_text); + vga_text += printStr((char*)path, vga_text); + vga_text += printStr(" has no data.", vga_text); + kbd_wait(); + return; + } + if (fi.filelen > maxFileSize) { + vga_text += printStr("File ", vga_text); + vga_text += printStr((char*)path, vga_text); + vga_text += printStr(" is too large (> 2GB).", vga_text); + kbd_wait(); + return; + } + uint32_t drawOffset = 0, lastDrawOffset = -1; char cont = 1; - uint32_t byteCount = 16*24, lastByteCount; - uint32_t screenSize = 80*25; - char reread; + uint32_t byteCount = 16*24, lastByteCount = 0; + // Cursor offset from *drawOff* + int32_t cursorScreenOff = 0, lastCursorScreenOff = -1; + // Pointer to last cursor in *VGA mem* + uint8_t *lastCursorScreenPtr = (uint8_t*)0xb8000; + char cursorNibble = 1, lastCursorNibble = 0; + char cursorRedraw = 1; + char reread = 1, redraw = 1; + uint8_t *currBuff = 0, *nextBuff = 0; + uint32_t currBuffLength, nextBuffLength, totalBuffLength; + uint16_t currLoadedBlock = -1, nextLoadedBlock = -1; + char currInMap = 0, nextInMap = 0; + uint32_t changeOffset = -1, lastChangeOffset = -1; + char fileChanged = 0; for (;cont;) { - uint8_t diskReadBuf[byteCount]; - if (readOffset != lastReadOffset) { - lastReadOffset = readOffset; + // Check if we need to scroll screen for cursor + // Should never be more than 16 away, if it is, + // things will be caught by sanity checks. + // Scroll Back + if (cursorScreenOff < 0) { + if (drawOffset - 16 < fi.filelen) + drawOffset -= 16; + cursorScreenOff += 16; + } + // Scroll Forward + if (cursorScreenOff >= byteCount) { + if (drawOffset + 16 < fi.filelen) + drawOffset += 16; + cursorScreenOff -= 16; + } + + // Sanity checks + if (cursorScreenOff >= byteCount) + cursorScreenOff = byteCount - 1; + if (cursorScreenOff + drawOffset >= fi.filelen) + cursorScreenOff = fi.filelen - drawOffset - 1; + if (cursorScreenOff < 0) cursorScreenOff = 0; + + if (cursorNibble != lastCursorNibble) + cursorRedraw = 1; + if (cursorScreenOff != lastCursorScreenOff) + cursorRedraw = 1; + if (cursorRedraw) { + const uint32_t hexViewScreenOff = 10 * 2; + const uint32_t asciiViewScreenOff = 61 * 2; + uint16_t *screen = (uint16_t*)0xb8000; + // Byte draw starts on first line + uint16_t *line = &screen[80 * (1 + (cursorScreenOff >> 4))]; + uint8_t *cursorPtr = (uint8_t*)line; + if (cursorNibble == 0) { + // Each byte takes 3 chars on screen + cursorPtr += hexViewScreenOff + (cursorScreenOff & 0xF) * 3 * 2; + if ((cursorScreenOff & 0xF) > 7) + cursorPtr += 2; + // Low nibble + cursorPtr += 2; + } else if (cursorNibble == 1) { + // Each byte takes 3 chars on screen + cursorPtr += hexViewScreenOff + (cursorScreenOff & 0xF) * 3 * 2; + if ((cursorScreenOff & 0xF) > 7) + cursorPtr += 2; + // ASCII area + } else { + // Each byte takes 1 char on screen + cursorPtr += asciiViewScreenOff + (cursorScreenOff & 0xF) * 2; + } + // We want the color byte, not char + cursorPtr++; + if (cursorPtr != lastCursorScreenPtr) { + *lastCursorScreenPtr = 0x0f; + *cursorPtr = 0xf0; + lastCursorScreenPtr = cursorPtr; + } + lastCursorScreenOff = cursorScreenOff; + lastCursorNibble = cursorNibble; + cursorRedraw = 0; + } + + if ((drawOffset & blockMask) != (lastDrawOffset & blockMask)) { + lastDrawOffset = (drawOffset & blockMask); reread = 1; + redraw = 1; + } + if (drawOffset != lastDrawOffset) { + lastDrawOffset = drawOffset; + redraw = 1; } if (byteCount != lastByteCount) { lastByteCount = byteCount; - reread = 1; + redraw = 1; } + if (changeOffset != lastChangeOffset) { + redraw = 1; + lastChangeOffset = changeOffset; + } + // Check for changes to block + if (changeOffset != -1) { + char isCurr = changeOffset < blockSize; + char changeInMap = isCurr ? currInMap : nextInMap; + uint8_t *changedBlock = isCurr ? currBuff : nextBuff; + // If changes are in map we don't need to do anything + if (!changeInMap) { + uint16_t loadedBlock = isCurr ? currLoadedBlock : nextLoadedBlock; + uint16_t storeBlockIdx = (*writtenMap)[loadedBlock]; + // Add a new map entry + if (!storeBlockIdx) { + (*writtenMap)[loadedBlock] = storeBlockIdx = nextFreeBlock++; + (*blockLenMap)[loadedBlock] = isCurr ? currBuffLength : nextBuffLength; + } + uint8_t *storeBlock = writeStore[storeBlockIdx]; + for (int i = 0; i < blockSize; i++) + storeBlock[i] = changedBlock[i]; + // Move the current buffer to map + if (isCurr) { currBuff = storeBlock; currInMap = 1; } + else { nextBuff = storeBlock; nextInMap = 1; } + } + changeOffset = -1; + fileChanged = 1; + redraw = 1; + } + if (reread) { + // TODO We should probably check if we already have + // the block to read in nextBuff from previous stuff + // We are in a modified block + uint16_t newBlock = drawOffset >> blockShift; + if ((*writtenMap)[newBlock]) { + currBuff = writeStore[(*writtenMap)[newBlock]]; + currBuffLength = (*blockLenMap)[newBlock]; + totalBuffLength = currBuffLength; + currInMap = 1; + // We are in an unmodified block + } else { + uint32_t blockOffset = drawOffset & blockMask; + vga_text = &((uint16_t*)0xb8000)[80]; + DFS_Seek(&fi, blockOffset, scratch); + if (fi.pointer != blockOffset) { + vga_text += printStr("Seek Error", vga_text); + kbd_wait(); + return; + } + err = DFS_ReadFile(&fi, scratch, screenBuff, &currBuffLength, blockSize); + if (err && err != DFS_EOF) { + vga_text += printStr("Read Error: ", vga_text); + printDword(err, vga_text); + kbd_wait(); + return; + } + currBuff = screenBuff; + totalBuffLength = currBuffLength; + currInMap = 0; + } + currLoadedBlock = newBlock; + reread = 0; + nextInMap = 0; + } + // We will be drawing stuff in the next block + // TODO This would be better served with nice functions! + if (currBuffLength == blockSize && (drawOffset & ~blockMask) > (blockSize - byteCount)) { + // TODO We should probably check if we already have + // the next block in currBuff from previous stuff + // Next is a modified block + uint16_t newBlock = (drawOffset >> blockShift) + 1; + if (nextLoadedBlock != newBlock) { + if ((*writtenMap)[newBlock]) { + nextBuffLength = (*blockLenMap)[newBlock]; + nextBuff = writeStore[(*writtenMap)[newBlock]]; + nextInMap = 1; + // Next is an unmodified block + } else { + uint32_t blockOffset = (drawOffset & blockMask) + blockSize; + vga_text = &((uint16_t*)0xb8000)[80]; + DFS_Seek(&fi, blockOffset, scratch); + if (fi.pointer != blockOffset) { + vga_text += printStr("Seek Error", vga_text); + kbd_wait(); + return; + } + err = DFS_ReadFile(&fi, scratch, &screenBuff[blockSize], &nextBuffLength, blockSize); + if (err && err != DFS_EOF) { + vga_text += printStr("Read Error: ", vga_text); + printDword(err, vga_text); + kbd_wait(); + return; + } + nextBuff = &screenBuff[blockSize]; + nextInMap = 0; + } + nextLoadedBlock = newBlock; + } + totalBuffLength = currBuffLength + nextBuffLength; + } + if (redraw) { vga_text = (uint16_t *)0xb8000; - for (int i = 0; i < screenSize; i++) - vga_text[i] = 0x0f00; vga_text += printStr((char*)path, vga_text); + vga_text += printChar(fileChanged ? '*' : ' ', vga_text); { - const char prnt[] = "Scroll: Up/Down PgUp/PgDown Home/End Exit: E "; + const char prnt[] = "Scroll: Up/Down PgUp/PgDown Home/End Exit: F1"; vga_text = &((uint16_t*)0xb8000)[80-sizeof(prnt)]; vga_text += printStr((char*)prnt, vga_text); } vga_text = &((uint16_t*)0xb8000)[80]; - DFS_Seek(&fi, readOffset, scratch); - if (fi.pointer != readOffset) { - vga_text += printStr("Seek Error", vga_text); - return; - } - err = DFS_ReadFile(&fi, scratch, diskReadBuf, &successcount, byteCount); - if (err && err != DFS_EOF) { - vga_text += printStr("Read Error: ", vga_text); - printDword(err, vga_text); - return; - } - for (uint32_t i = 0; i < successcount && i < byteCount; i += 16) { - vga_text += printDword(i + readOffset, vga_text); + uint32_t offsetInBuff = drawOffset & ~blockMask; + uint8_t *drawFromBuff = &currBuff[offsetInBuff]; + uint32_t maxDrawLen = totalBuffLength - (drawOffset & ~blockMask); + for (int i = 0; i < maxDrawLen && i < byteCount; i += 16) { + // Blocks are blockSize aligned, so 16-byte alignment should work + if (i + offsetInBuff >= blockSize) { + drawFromBuff = &nextBuff[-i]; + offsetInBuff = 0; + } + vga_text += printDword(i + drawOffset, vga_text); vga_text += printChar(' ', vga_text); vga_text += printChar(' ', vga_text); for (uint32_t j = 0; j < 16; j++) { - if (i + j < successcount) - vga_text += printByte(diskReadBuf[i + j], vga_text); + if (i + j < maxDrawLen) + vga_text += printByte(drawFromBuff[i + j], vga_text); else { vga_text += printChar(' ', vga_text); - vga_text += printChar(' ', vga_text); + vga_text += printChar(' ', vga_text); } vga_text += printChar(' ', vga_text); if (j == 7) @@ -69,49 +282,88 @@ void HexViewTest(uint8_t *path, VOLINFO *vi) { vga_text += printChar(' ', vga_text); vga_text += printChar('|', vga_text); for (uint32_t j = 0; j < 16; j++) { - if (i + j < successcount) - vga_text += printChar(diskReadBuf[i + j], vga_text); + if (i + j < maxDrawLen) + vga_text += printChar(drawFromBuff[i + j], vga_text); else vga_text += printChar(' ', vga_text); } vga_text += printChar('|', vga_text); vga_text = nextLine(vga_text); } - reread = 0; + // Clear remainder of screen + for (;vga_text < &((uint16_t*)0xb8000)[screenSize]; vga_text++) + *vga_text = 0x0f00; + redraw = 0; } uint16_t key = get_scancode(); union V86Regs_t regs; FARPTR v86_entry; + char try_edit = 0; switch (key & 0xff) { - case KEY_DOWN: // down - if ((readOffset + byteCount) < fi.filelen) - readOffset += byteCount; - else goto end; + case KEY_DOWN: + // Stay in file + if ((cursorScreenOff + 16 + drawOffset) < fi.filelen) + cursorScreenOff += 16; break; - case KEY_UP: // up - if ((readOffset - byteCount) < fi.filelen) - readOffset -= byteCount; - else goto home; + case KEY_UP: + // Stay in file + if ((uint32_t)(cursorScreenOff - 16 + drawOffset) < fi.filelen) + cursorScreenOff -= 16; + break; + case KEY_LEFT: + if (cursorNibble == 0) { cursorNibble = 1; break; } + // Exiting text area + else if (cursorNibble == 2 && (cursorScreenOff & 0xF) == 0) { + cursorNibble = 0; + cursorScreenOff |= 0xF; + // Stay in file + } else if ((cursorScreenOff - 1 + drawOffset) < fi.filelen) { + cursorScreenOff--; + if (cursorNibble == 1) cursorNibble = 0; + } + break; + case KEY_RIGHT: + if (cursorNibble == 1) { cursorNibble = 0; break; } + // Entering text area + else if (cursorNibble == 0 && (cursorScreenOff & 0xF) == 0xF) { + cursorNibble = 2; + cursorScreenOff &= ~0xF; + // Stay in file + } else if ((cursorScreenOff + 1 + drawOffset) < fi.filelen) { + cursorScreenOff++; + if (cursorNibble == 0) cursorNibble = 1; + } break; case KEY_PGDOWN: - if ((readOffset + (byteCount*4)) < fi.filelen) - readOffset += (byteCount*4); - else goto end; + if (drawOffset + byteCount < fi.filelen) + drawOffset += byteCount; + else if ((fi.filelen / byteCount) * byteCount > drawOffset) + drawOffset = (fi.filelen / byteCount) * byteCount; break; case KEY_PGUP: - if ((readOffset - (byteCount*4)) < fi.filelen) - readOffset -= (byteCount*4); - else goto home; + if (drawOffset - byteCount < fi.filelen) + drawOffset -= byteCount; + else drawOffset = 0; break; - case KEY_HOME: home: - readOffset = 0; + case KEY_HOME: + cursorScreenOff &= ~0xF; + if (cursorNibble == 0) cursorNibble = 1; break; - case KEY_END: end: - if ((fi.filelen / byteCount) * byteCount > readOffset) - readOffset = (fi.filelen / byteCount) * byteCount; + case KEY_END: + cursorScreenOff |= 0xF; + if (cursorNibble == 1) cursorNibble = 0; break; - case KEY_E: // e + case KEY_F1: cont = 0; break; + case KEY_F3: // start of file + drawOffset = 0; + break; + case KEY_F4: // end of file + if ((fi.filelen / byteCount) * byteCount > drawOffset) + drawOffset = (fi.filelen / byteCount) * byteCount; + break; + case KEY_F6: // TODO write file + break; case KEY_F2: if (byteCount != 16*24) { SetVideo25Lines(); @@ -129,8 +381,68 @@ void HexViewTest(uint8_t *path, VOLINFO *vi) { } break; default: + try_edit = 1; break; } + if (!try_edit) continue; + // Check if in range + char k = key >> 8; // ASCII portion + char v; + if (cursorNibble == 2 && k >= 0x20 && k <= 0x7E) v = k; + else if (k >= '0' && k <= '9') + v = k - '0'; + else if (k >= 'a' && k <= 'f') + v = k - 'a' + 0xA; + else if (k >= 'A' && k <= 'F') + v = k - 'A' + 0xA; + else continue; + // Change buffer at cursor + uint32_t screenOffsetInBuff = drawOffset & ~blockMask; + changeOffset = screenOffsetInBuff + cursorScreenOff; + char isCurr = changeOffset < blockSize; + uint8_t *changeBuff = isCurr ? currBuff : nextBuff; + uint32_t changeOffsetInBuff = isCurr ? changeOffset : changeOffset - blockSize; + if (cursorNibble == 0) + changeBuff[changeOffsetInBuff] = (changeBuff[changeOffsetInBuff] & 0xF0) | v; + else if (cursorNibble == 1) + changeBuff[changeOffsetInBuff] = (changeBuff[changeOffsetInBuff] & 0x0F) | (v << 4); + else changeBuff[changeOffsetInBuff] = v; + redraw = 1; + } + if (!fileChanged) return; + vga_text = (uint16_t*)0xb8000; + vga_text += printStr((char*)path, vga_text); + vga_text += printChar(fileChanged ? '*' : ' ', vga_text); + vga_text += printChar(' ', vga_text); + vga_text += printStr("Save changes to file? (Y/N)", vga_text); + for (;vga_text < &((uint16_t*)0xb8000)[80];vga_text++) + *vga_text = 0x0f00; + uint16_t key=0; + for(;(key & 0xff) != KEY_N && (key & 0xff) != KEY_Y;key = get_scancode()); + if ((key & 0xff) != KEY_Y) return; + // Write changes + for (int i = 0; i < totalBlocks && (i << blockShift) < fi.filelen; i++) { + // No change in current block + uint16_t blockIdx = (*writtenMap)[i]; + uint32_t blockLen = (*blockLenMap)[i]; + if (!blockIdx) continue; + // Write block to file + uint32_t successcount; + uint32_t blockOff = i << blockShift; + DFS_Seek(&fi, blockOff, scratch); + if (fi.pointer != blockOff) { + vga_text = (uint16_t*)0xb8000; + vga_text += printStr("Seek Error ", vga_text); + kbd_wait(); + return; + } + uint32_t err = DFS_WriteFile(&fi, scratch, writeStore[blockIdx], &successcount, blockLen); + if (successcount < blockLen || err) { + vga_text = (uint16_t*)0xb8000; + vga_text += printStr("Write Error ", vga_text); + kbd_wait(); + return; + } } } void TextViewTest(uint8_t *path, VOLINFO *vi) {