diff --git a/Makefile b/Makefile index 87ed6e0..1245f6f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -objects = entry.o kernel.o task.o handler.o interrupt.o v86.o print.o tss.o dosfs/dosfs.o gdt.o usermode.o paging.o fault.o tests.o kbd.o helper.o progs.o disk.o +objects = entry.o kernel.o task.o handler.o interrupt.o v86.o print.o tss.o dosfs/dosfs.o gdt.o usermode.o paging.o fault.o tests.o kbd.o helper.o progs.o disk.o hexedit.o CFLAGS = -target "i686-elf" -m32 -mgeneral-regs-only -ffreestanding -march=i686 -fno-stack-protector -Wno-int-conversion -nostdlib -c %.o: %.nasm diff --git a/disk.c b/disk.c index c1eb06f..8a5ea7f 100644 --- a/disk.c +++ b/disk.c @@ -5,12 +5,13 @@ extern void *memcpy(void *restrict dest, const void *restrict src, uintptr_t n); -uintptr_t DiskCacheBlockSize = 0x1000; // 512 * 4 -uintptr_t DiskCacheSectorMask = 7; -uintptr_t DiskCacheSectorSize = 8; -uint8_t (*DiskCache)[128][0x1000] = (uint8_t (*)[128][0x1000])0x280000; -uint32_t DiskCacheLastRead[128]; -uint32_t DiskCacheSector[128]; +#define DISKCACHEBLOCKSIZE 0x1000 // 512 * 4 +#define DISKCACHESECTORMASK 7 +#define DISKCACHESECTORSIZE 8 +#define DISKCACHEBLOCKCOUNT 128 +uint8_t (*DiskCache)[DISKCACHEBLOCKCOUNT][DISKCACHEBLOCKSIZE] = (uint8_t (*)[DISKCACHEBLOCKCOUNT][DISKCACHEBLOCKSIZE])0x280000; +uint32_t DiskCacheLastRead[DISKCACHEBLOCKCOUNT]; +uint32_t DiskCacheSector[DISKCACHEBLOCKCOUNT]; extern uint32_t _gpf_eax_save; extern uint32_t _gpf_eflags_save; @@ -49,7 +50,7 @@ void Disk_SetupCHS() { } // Init Cache - for (int i = 0; i < 128; i++) { + for (int i = 0; i < DISKCACHEBLOCKCOUNT; i++) { DiskCacheLastRead[i] = 0; DiskCacheSector[i] = -1; } @@ -57,12 +58,12 @@ void Disk_SetupCHS() { extern uint32_t TIMERVAL; uint8_t *FindInCache(uint32_t sector) { - uint32_t maskedSector = sector & ~DiskCacheSectorMask; - for (int i = 0; i < 128; i++) { + uint32_t maskedSector = sector & ~DISKCACHESECTORMASK; + for (int i = 0; i < DISKCACHEBLOCKCOUNT; i++) { // Found if (DiskCacheSector[i] == maskedSector) { DiskCacheLastRead[i] = TIMERVAL; - return &(*DiskCache)[i][(sector & DiskCacheSectorMask) << 9]; + return &(*DiskCache)[i][(sector & DISKCACHESECTORMASK) << 9]; } } // Not Found @@ -72,13 +73,14 @@ uint8_t *FindInCache(uint32_t sector) { void AddToCache(uint32_t sector, uint8_t *buffer) { uintptr_t lowestFoundTime = DiskCacheLastRead[0]; uintptr_t lowestFoundIdx = 0; - for (int i = 1; i < 32; i++) { + // TODO We should really use something more fast and less lazy + for (int i = 1; i < DISKCACHEBLOCKCOUNT; i++) { if (DiskCacheLastRead[i] < lowestFoundTime) { lowestFoundTime = DiskCacheLastRead[i]; lowestFoundIdx = i; } } - for (int i = 0; i < DiskCacheBlockSize / sizeof(uint32_t); i++) + for (int i = 0; i < DISKCACHEBLOCKSIZE / sizeof(uint32_t); i++) ((uint32_t *)((*DiskCache)[lowestFoundIdx]))[i] = ((uint32_t*)buffer)[i]; DiskCacheLastRead[lowestFoundIdx] = TIMERVAL; DiskCacheSector[lowestFoundIdx] = sector; @@ -116,8 +118,8 @@ uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t // TODO Do error handling if (!useCHS) { // LBA Read - v86disk_addr_packet.start_block = sector & ~DiskCacheSectorMask; - v86disk_addr_packet.blocks = DiskCacheSectorSize; + v86disk_addr_packet.start_block = sector & ~DISKCACHESECTORMASK; + v86disk_addr_packet.blocks = DISKCACHESECTORSIZE; v86disk_addr_packet.transfer_buffer = (uintptr_t)v86buf & 0x000F | (((uintptr_t)v86buf & 0xFFFF0) << 12); @@ -125,15 +127,15 @@ uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t regs.h.ah = 0x42; FARPTR v86_entry = i386LinearToFp(v86DiskOp); enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s); - if (v86disk_addr_packet.blocks != DiskCacheSectorSize) { + if (v86disk_addr_packet.blocks != DISKCACHESECTORSIZE) { uint16_t *vga = error_screen; vga += printStr("INT13 Read Secs: ", vga); vga += printDec(v86disk_addr_packet.blocks, vga); error_environment(); for(;;); } - AddToCache(sector & ~DiskCacheSectorMask, v86buf); - v86buf = &v86buf[(sector & DiskCacheSectorMask) << 9]; + AddToCache(sector & ~DISKCACHESECTORMASK, v86buf); + v86buf = &v86buf[(sector & DISKCACHESECTORMASK) << 9]; } else { uint32_t tmp = sector / secPerTrack; uint32_t sec = (sector % (secPerTrack)) + 1; diff --git a/hexedit.c b/hexedit.c new file mode 100644 index 0000000..6fc46b9 --- /dev/null +++ b/hexedit.c @@ -0,0 +1,444 @@ +#include "progs.h" + +#define BLOCKSIZE 0x10000 // 64K +#define BLOCKMASK ~0xffff // block = (offset & blockMask) +#define BLOCKSHIFT 16 // blockSize = 1 << blockShift +#define MAXFILESIZE 0x80000000 // 2GB +#define TOTALBLOCKS (MAXFILESIZE/BLOCKSIZE) +uint16_t writtenMap[TOTALBLOCKS]; +uint32_t blockLenMap[TOTALBLOCKS]; +// NOTE This is linked at the end of program BSS section, +// so that it can be expanded without telling C how much +// it actually needs +uint8_t writeStoreBase[BLOCKSIZE] __attribute__((section(".bss.end"))); +void HexEditor(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; + uint8_t (*writeStore)[BLOCKSIZE] = &writeStoreBase; + 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; + 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; + } + 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 = 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;) { + // 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; + 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; + vga_text += printStr((char*)path, vga_text); + vga_text += printChar(fileChanged ? '*' : ' ', vga_text); + { + 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]; + 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 < 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); + if (j == 7) + vga_text += printChar(' ', vga_text); + } + vga_text += printChar(' ', vga_text); + vga_text += printChar('|', vga_text); + for (uint32_t j = 0; j < 16; j++) { + 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); + } + // 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: + // Stay in file + if ((cursorScreenOff + 16 + drawOffset) < fi.filelen) + cursorScreenOff += 16; + break; + 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 (drawOffset + byteCount < fi.filelen) + drawOffset += byteCount; + else if ((fi.filelen / byteCount) * byteCount > drawOffset) + drawOffset = (fi.filelen / byteCount) * byteCount; + break; + case KEY_PGUP: + if (drawOffset - byteCount < fi.filelen) + drawOffset -= byteCount; + else drawOffset = 0; + break; + case KEY_HOME: + cursorScreenOff &= ~0xF; + if (cursorNibble == 0) cursorNibble = 1; + break; + case KEY_END: + cursorScreenOff |= 0xF; + if (cursorNibble == 1) cursorNibble = 0; + break; + 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(); + SetCursorDisabled(); + screenSize = 80*25; + byteCount = 16*24; + } + break; + case KEY_F5: + if (byteCount != 16*49) { + SetVideo50Lines(); + SetCursorDisabled(); + screenSize = 80*50; + byteCount = 16*49; + } + 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; + } + } +} + diff --git a/kernel.c b/kernel.c index f637b63..e46bcdb 100644 --- a/kernel.c +++ b/kernel.c @@ -87,6 +87,19 @@ void setup_binary() { *d = 0; } +extern char _bprogstart, _bprogend; +// NOTE This is linked at the same place +// as the load address of usermode code, +// so things linked with bss here *must not* +// call usermode code at the same place +void ClearProgBss() { + // TODO Make sure there's no weird alignment stuff, + // although more BSS should always come after this + // ideally. + for (uint32_t *d = (uint32_t*)&_bprogstart; d < (uint32_t*)&_bprogend; d++) + *d = 0; +} + uint16_t error_screen[80*50]; // 50-line VGA screen of error content extern uint16_t *ivt; @@ -156,6 +169,13 @@ void error_environment(uint32_t stack0, uint32_t stack1, uint32_t stack2, uint32 enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry), ®s); } +uint32_t GetFreeStack() { + uint32_t stack; + asm volatile("mov %%esp,%%eax":"=a"(stack)); + stack = ((stack - 0x4000) / 0x1000) * 0x1000; + return stack; +} + /* Real Mode Accessible (First MB) 00000 - 00400 IVT (1kB) @@ -305,14 +325,14 @@ void FileSelect() { if (fileHovered < 0) fileHovered = fileCount - 1; break; case KEY_F4: - create_child(0x380000, (uintptr_t)RunTests, 0); + create_child(GetFreeStack(), (uintptr_t)RunTests, 0); SetCursorDisabled(); 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); + create_child(GetFreeStack(), (uintptr_t)ProgramLoadTest, 2, current_path, &vi); SetVideo25Lines(); SetCursorDisabled(); reload = 1; @@ -320,8 +340,8 @@ void FileSelect() { 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); + ClearProgBss(); + create_child(GetFreeStack(), (uintptr_t)HexEditor, 2, current_path, &vi); SetVideo25Lines(); SetCursorDisabled(); reload = 1; @@ -330,7 +350,7 @@ void FileSelect() { 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); + create_child(GetFreeStack(), (uintptr_t)TextViewTest, 2, current_path, &vi); SetVideo25Lines(); SetCursorDisabled(); reload = 1; @@ -435,7 +455,7 @@ void start() { vga_text += printStr("Press T for tests, or any key to continue... ", vga_text); uint8_t key = get_key(); if (key == 't' || key == 'T') - create_child(0x380000, (uintptr_t)RunTests, 0); + create_child(GetFreeStack(), (uintptr_t)RunTests, 0); SetVideo25Lines(); SetCursorDisabled(); DrawScreen(); diff --git a/link.ld b/link.ld index d708308..ae135c1 100644 --- a/link.ld +++ b/link.ld @@ -26,6 +26,13 @@ SECTIONS { AT ( ADDR(.data) + SIZEOF(.data) + SIZEOF(.realmode) ) { _usercode = .; *(.user); _eusercode = .; } + .bss 0x400000 : { + _bprogstart = .; + hexedit.o(.bss); + _bprogend = .; + hexedit.o(.bss.end); + } + . = ADDR(.data) + SIZEOF(.data) + SIZEOF(.realmode) + SIZEOF(.usermode); .bss : ALIGN(0x1000) diff --git a/progs.c b/progs.c index da49f07..fbbff33 100644 --- a/progs.c +++ b/progs.c @@ -2,449 +2,6 @@ #include "helper.h" #include "kbd.h" -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; - 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; - } - 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 = 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;) { - // 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; - 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; - vga_text += printStr((char*)path, vga_text); - vga_text += printChar(fileChanged ? '*' : ' ', vga_text); - { - 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]; - 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 < 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); - if (j == 7) - vga_text += printChar(' ', vga_text); - } - vga_text += printChar(' ', vga_text); - vga_text += printChar('|', vga_text); - for (uint32_t j = 0; j < 16; j++) { - 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); - } - // 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: - // Stay in file - if ((cursorScreenOff + 16 + drawOffset) < fi.filelen) - cursorScreenOff += 16; - break; - 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 (drawOffset + byteCount < fi.filelen) - drawOffset += byteCount; - else if ((fi.filelen / byteCount) * byteCount > drawOffset) - drawOffset = (fi.filelen / byteCount) * byteCount; - break; - case KEY_PGUP: - if (drawOffset - byteCount < fi.filelen) - drawOffset -= byteCount; - else drawOffset = 0; - break; - case KEY_HOME: - cursorScreenOff &= ~0xF; - if (cursorNibble == 0) cursorNibble = 1; - break; - case KEY_END: - cursorScreenOff |= 0xF; - if (cursorNibble == 1) cursorNibble = 0; - break; - 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(); - SetCursorDisabled(); - screenSize = 80*25; - byteCount = 16*24; - } - break; - case KEY_F5: - if (byteCount != 16*49) { - SetVideo50Lines(); - SetCursorDisabled(); - screenSize = 80*50; - byteCount = 16*49; - } - 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) { uint16_t *vga_text = (uint16_t *)0xb8000; uint32_t fileLen; diff --git a/progs.h b/progs.h index 604f28d..37ffa85 100644 --- a/progs.h +++ b/progs.h @@ -7,6 +7,6 @@ #include "v86defs.h" #include "helper.h" -void HexViewTest(uint8_t *path, VOLINFO *vi); +void HexEditor(uint8_t *path, VOLINFO *vi); void TextViewTest(uint8_t *path, VOLINFO *vi); void ProgramLoadTest(uint8_t *path, VOLINFO *vi);