diff options
author | Alexis211 <alexis211@gmail.com> | 2009-11-11 17:32:01 +0100 |
---|---|---|
committer | Alexis211 <alexis211@gmail.com> | 2009-11-11 17:32:01 +0100 |
commit | 7c5f4adcc8725efa0f0476e33732873a4167f54d (patch) | |
tree | 11034d19d05408aa493fc737a7730a7c33e62617 /Source | |
parent | 8a3323d48aef03a749db36bf58bbd3bca5c4eff2 (diff) | |
download | Melon-7c5f4adcc8725efa0f0476e33732873a4167f54d.tar.gz Melon-7c5f4adcc8725efa0f0476e33732873a4167f54d.zip |
Modified the way of calling V86 procedures, and calling BIOS ints
Diffstat (limited to 'Source')
-rw-r--r-- | Source/Kernel/Core/kmain.wtf.cpp | 8 | ||||
-rw-r--r-- | Source/Kernel/DeviceManager/Disp.ns.cpp | 4 | ||||
-rw-r--r-- | Source/Kernel/DeviceManager/Disp.ns.h | 1 | ||||
-rw-r--r-- | Source/Kernel/Devices/Display/Display.proto.h | 1 | ||||
-rw-r--r-- | Source/Kernel/Devices/Display/VESADisplay.class.cpp | 37 | ||||
-rw-r--r-- | Source/Kernel/Devices/Display/VGATextOutput.class.cpp | 8 | ||||
-rw-r--r-- | Source/Kernel/Devices/Display/vga-vesa.wtf.asm | 22 | ||||
-rw-r--r-- | Source/Kernel/Makefile | 2 | ||||
-rw-r--r-- | Source/Kernel/TaskManager/V86/V86.ns.cpp | 23 | ||||
-rw-r--r-- | Source/Kernel/TaskManager/V86/V86.ns.h | 7 | ||||
-rw-r--r-- | Source/Kernel/TaskManager/V86/V86Thread.class.cpp | 127 | ||||
-rw-r--r-- | Source/Kernel/TaskManager/V86/V86Thread.class.h | 25 | ||||
-rw-r--r-- | Source/Kernel/TaskManager/V86/v86.wtf.asm | 48 | ||||
-rw-r--r-- | Source/Kernel/VTManager/SimpleVT.class.cpp | 6 |
14 files changed, 176 insertions, 143 deletions
diff --git a/Source/Kernel/Core/kmain.wtf.cpp b/Source/Kernel/Core/kmain.wtf.cpp index a59149b..28c65d1 100644 --- a/Source/Kernel/Core/kmain.wtf.cpp +++ b/Source/Kernel/Core/kmain.wtf.cpp @@ -173,9 +173,15 @@ void kmain(multiboot_info_t* mbd, u32int magic) { asm volatile("sti"); - selectVideoMode(*kvt); + selectVideoMode(*kvt); //////////////////////// SETUP VIDEO MODE kvt->unmap(); + for (int x = 0; x < 256; x++) { + for (int y = 0; y < 256; y++) { + Disp::mode.device->putPix(x, y, (x << 8) | y); + } + } + //Create a VT for handling the Melon bootup logo SimpleVT *melonLogoVT = new SimpleVT(melonLogoLines, melonLogoCols, TXTLOGO_FGCOLOR, TXTLOGO_BGCOLOR); melonLogoVT->map(1); diff --git a/Source/Kernel/DeviceManager/Disp.ns.cpp b/Source/Kernel/DeviceManager/Disp.ns.cpp index 246e959..2f1ebcd 100644 --- a/Source/Kernel/DeviceManager/Disp.ns.cpp +++ b/Source/Kernel/DeviceManager/Disp.ns.cpp @@ -24,6 +24,10 @@ void moveCursor(u16int line, u16int col) { mode.device->moveCursor(line, col); } +bool textScroll(u16int line, u16int col, u16int height, u16int width, u8int color) { + return mode.device->textScroll(line, col, height, width, color); +} + void clear() { mode.device->clear(); } diff --git a/Source/Kernel/DeviceManager/Disp.ns.h b/Source/Kernel/DeviceManager/Disp.ns.h index 0d56cf0..2a10358 100644 --- a/Source/Kernel/DeviceManager/Disp.ns.h +++ b/Source/Kernel/DeviceManager/Disp.ns.h @@ -20,6 +20,7 @@ namespace Disp { u16int textRows(); void putChar(u16int line, u16int col, WChar c, u8int color); void moveCursor(u16int line, u16int col); + bool textScroll(u16int line, u16int col, u16int height, u16int width, u8int color); void clear(); void getModes(); diff --git a/Source/Kernel/Devices/Display/Display.proto.h b/Source/Kernel/Devices/Display/Display.proto.h index 8401e0b..26c3696 100644 --- a/Source/Kernel/Devices/Display/Display.proto.h +++ b/Source/Kernel/Devices/Display/Display.proto.h @@ -18,6 +18,7 @@ class Display : public Device { //Text functions virtual void putChar(u16int line, u16int col, WChar c, u8int color) = 0; //Color : <bg 4bits><fg 4bits> virtual void moveCursor(u16int line, u16int col) = 0; + virtual bool textScroll(u16int line, u16int col, u16int height, u16int width, u8int color) { return false; } //Graphic functions, can stay unimplemented for textual displays virtual void putPix(u16int x, u16int y, u32int color) {} diff --git a/Source/Kernel/Devices/Display/VESADisplay.class.cpp b/Source/Kernel/Devices/Display/VESADisplay.class.cpp index 4c3041a..ae64403 100644 --- a/Source/Kernel/Devices/Display/VESADisplay.class.cpp +++ b/Source/Kernel/Devices/Display/VESADisplay.class.cpp @@ -3,8 +3,6 @@ #include <MemoryManager/PhysMem.ns.h> -extern v86_function_t vesa_int; //in vga-vesa.wtf.asm - using namespace Disp; String VESADisplay::getClass() { @@ -16,15 +14,16 @@ String VESADisplay::getName() { } vbe_controller_info_t VESADisplay::getCtrlrInfo() { + V86::map(); vbe_controller_info_t *info = (vbe_controller_info_t*)V86::alloc(sizeof(vbe_controller_info_t)); info->signature[0] = 'V'; info->signature[1] = 'B'; info->signature[2] = 'E'; info->signature[3] = '2'; info->videomodes = 0; - registers_t regs; - regs.eax = 0x00004F00; - regs.esi = LIN_SEG(info); - regs.edi = LIN_OFF(info); - V86::run(vesa_int, regs, 0); - if (regs.eax != 0x004F) PANIC("Something went wrong in detecting VBE modes."); + v86_regs_t regs; + regs.ax = 0x4F00; + regs.es = LIN_SEG(info); + regs.di = LIN_OFF(info); + V86::biosInt(0x10, regs); + if (regs.ax != 0x004F) PANIC("Something went wrong in detecting VBE modes."); if (info->signature[3] != 'A') PANIC("No vesa sinature"); return *info; } @@ -33,12 +32,12 @@ vbe_mode_info_t VESADisplay::getModeInfo(u16int id) { V86::map(); vbe_mode_info_t *mode = (vbe_mode_info_t*)V86::alloc(sizeof(vbe_mode_info_t)); CMem::memset((u8int*)mode, 0, sizeof(vbe_mode_info_t)); - registers_t regs; - regs.eax = 0x00004F01; - regs.ecx = id; - regs.esi = LIN_SEG(mode); - regs.edi = LIN_OFF(mode); - V86::run(vesa_int, regs, 0); + v86_regs_t regs; + regs.ax = 0x00004F01; + regs.cx = id; + regs.es = LIN_SEG(mode); + regs.di = LIN_OFF(mode); + V86::biosInt(0x10, regs); return *mode; } @@ -52,7 +51,7 @@ void VESADisplay::getModes(Vector<mode_t> &to) { if ((mode.attributes & 0x90) != 0x90) continue; if (mode.memory_model != 4 and mode.memory_model != 6) continue; - if (mode.bpp != 24) continue; + //if (mode.bpp != 24) continue; mode_t m; m.device = this; m.textCols = mode.Xres / C_FONT_WIDTH; m.textRows = mode.Yres / C_FONT_HEIGHT; m.identifier = modes[i]; @@ -64,10 +63,10 @@ void VESADisplay::getModes(Vector<mode_t> &to) { bool VESADisplay::setMode(mode_t &mode) { if (mode.device != this) return false; m_currMode = getModeInfo(mode.identifier); - registers_t regs; - regs.eax = 0x00004F02; - regs.ebx = mode.identifier | 0x4000; - V86::run(vesa_int, regs, 0); + v86_regs_t regs; + regs.ax = 0x00004F02; + regs.bx = mode.identifier | 0x4000; + V86::biosInt(0x10, regs); m_fb = (u8int*)0xF0000000; for (u32int i = 0; i < (u32int)(m_currMode.Yres * m_currMode.pitch); i += 0x1000) { diff --git a/Source/Kernel/Devices/Display/VGATextOutput.class.cpp b/Source/Kernel/Devices/Display/VGATextOutput.class.cpp index d307f60..8c687ba 100644 --- a/Source/Kernel/Devices/Display/VGATextOutput.class.cpp +++ b/Source/Kernel/Devices/Display/VGATextOutput.class.cpp @@ -3,8 +3,6 @@ #include <TaskManager/V86/V86.ns.h> -extern v86_function_t setvgamode; - //Virtual address in higher half #define RAM_ADDR 0xC00B8000 @@ -35,10 +33,10 @@ void VGATextOutput::getModes(Vector<mode_t> &to) { bool VGATextOutput::setMode(mode_t& mode) { if (mode.device == this && (mode.identifier == 3 or mode.identifier == 1)) { - registers_t r; - r.eax = mode.identifier; + v86_regs_t r; + r.ax = mode.identifier; m_cols = mode.textCols; - V86::run(setvgamode, r, 0); + V86::biosInt(0x10, r); clear(); return true; } diff --git a/Source/Kernel/Devices/Display/vga-vesa.wtf.asm b/Source/Kernel/Devices/Display/vga-vesa.wtf.asm deleted file mode 100644 index 4a6a1d6..0000000 --- a/Source/Kernel/Devices/Display/vga-vesa.wtf.asm +++ /dev/null @@ -1,22 +0,0 @@ -[BITS 16] - -[GLOBAL setvgamode] -setvgamode: - dw setvgamode_end - setvgamode_start -setvgamode_start: - int 60 - int 10h - int 3 -setvgamode_end: - db 0 - -[GLOBAL vesa_int] -vesa_int: - dw vesa_int_end - vesa_int_start -vesa_int_start: - int 60 - mov es, si - int 10h - int 3 -vesa_int_end: - db 0 diff --git a/Source/Kernel/Makefile b/Source/Kernel/Makefile index 0126c43..82498b2 100644 --- a/Source/Kernel/Makefile +++ b/Source/Kernel/Makefile @@ -30,6 +30,7 @@ Objects = Core/loader.wtf.o \ TaskManager/Thread.class.o \ TaskManager/V86/V86Thread.class.o \ TaskManager/V86/V86.ns.o \ + TaskManager/V86/v86.wtf.o \ TaskManager/Task.ns.o \ TaskManager/Task.wtf.o \ VTManager/VirtualTerminal.proto.o \ @@ -72,7 +73,6 @@ Objects = Core/loader.wtf.o \ Devices/Display/VGATextOutput.class.o \ Devices/Display/GraphicDisplay.proto.o \ Devices/Display/VESADisplay.class.o \ - Devices/Display/vga-vesa.wtf.o \ Devices/Keyboard/PS2Keyboard.class.o \ Devices/Floppy/FloppyController.class.o \ Devices/Floppy/FloppyDrive.class.o \ diff --git a/Source/Kernel/TaskManager/V86/V86.ns.cpp b/Source/Kernel/TaskManager/V86/V86.ns.cpp index 93d51f2..f70728c 100644 --- a/Source/Kernel/TaskManager/V86/V86.ns.cpp +++ b/Source/Kernel/TaskManager/V86/V86.ns.cpp @@ -5,10 +5,17 @@ namespace V86 { u16int seg = V86_ALLOC_START; -void run(v86_function_t& entry, registers_t ®s, u32int data) { +void run(v86_function_t& entry, v86_regs_t ®s) { v86_retval_t ret; ret.regs = ®s; - new V86Thread(&entry, &ret, data); + new V86Thread(&entry, &ret); + while (!ret.finished) Task::currThread()->sleep(10); +} + +void biosInt(u8int int_no, v86_regs_t ®s) { + v86_retval_t ret; + ret.regs = ®s; + new V86Thread(int_no, &ret); while (!ret.finished) Task::currThread()->sleep(10); } @@ -19,8 +26,7 @@ void map(Process* p) { } } -u16int allocSeg(u16int length, Process* p) { - if (p == 0) p = Task::currProcess(); +u16int allocSeg(u16int length) { if (length & 0xF) length = (length & 0xFFFF0) + 0x10; u16int segments = length / 16; if (seg < V86_ALLOC_START) seg = V86_ALLOC_START; @@ -28,16 +34,11 @@ u16int allocSeg(u16int length, Process* p) { u16int ret = seg; seg += segments; - void* ptr = FP_TO_LINEAR(ret, 0); - for (u32int i = (u32int)ptr & 0xFFFFF000; i < (u32int)ptr + length; i += 0x1000) { - p->getPagedir()->allocFrame(i, true, true); - } - return ret; } -void* alloc(u16int length, Process* p) { - return FP_TO_LINEAR(allocSeg(length, p), 0); +void* alloc(u16int length) { + return FP_TO_LINEAR(allocSeg(length), 0); } } diff --git a/Source/Kernel/TaskManager/V86/V86.ns.h b/Source/Kernel/TaskManager/V86/V86.ns.h index 9fa132b..ec8e928 100644 --- a/Source/Kernel/TaskManager/V86/V86.ns.h +++ b/Source/Kernel/TaskManager/V86/V86.ns.h @@ -23,11 +23,12 @@ inline FARPTR LINEAR_TO_FP(void* ptr) { #define V86_ALLOC_END 0x9000 namespace V86 { - void run(v86_function_t& entry, registers_t ®s, u32int data); + void run(v86_function_t& entry, v86_regs_t ®s); + void biosInt(u8int int_no, v86_regs_t ®s); void map(Process* p = 0); //Maps lower 1MB of virtual memory to physical memory (lower 1MB as well) - u16int allocSeg(u16int size, Process* p = 0); - void* alloc(u16int size, Process* p = 0); + u16int allocSeg(u16int size); + void* alloc(u16int size); } #endif diff --git a/Source/Kernel/TaskManager/V86/V86Thread.class.cpp b/Source/Kernel/TaskManager/V86/V86Thread.class.cpp index 26fca63..5002225 100644 --- a/Source/Kernel/TaskManager/V86/V86Thread.class.cpp +++ b/Source/Kernel/TaskManager/V86/V86Thread.class.cpp @@ -3,85 +3,75 @@ #include <TaskManager/V86/V86.ns.h> -void V86Thread::runV86(V86Thread* thread, u32int data, u32int ss, u32int cs) { - thread->m_process->getPagedir()->switchTo(); - //Setup values on user stack - u32int *stack = (u32int*)(FP_TO_LINEAR(ss, V86_STACKSIZE)); - stack--; *stack = data; - - u32int sp = V86_STACKSIZE - 4; - - //Setup a false iret structure on the kernel stack, containing (first pushed first) : - // - gs = cs - // - fs = cs - // - ds = cs - // - es = cs - // - stack segment = ss (temporarily in ecx) - // - stack pointer = sp (temp in ebx) - // - flags (OR'ed with EFLAGS.VM) - // - code segment = cs (temp in eax) - // - instruction pointer = ip (is 0) - asm volatile(" \ - pushl %%eax; \ - pushl %%eax; \ - pushl %%eax; \ - pushl %%eax; \ - pushl %%ecx; \ - pushl %%ebx; \ - pushf; \ - pop %%ebx; \ - or $0x200, %%ebx; \ - or $0x20000, %%ebx; \ - push %%ebx; \ - pushl %%eax; \ - pushl $0; \ - iret; \ - " : : "a"(cs), "c"(ss), "b"(sp)); -} +//in v86.wtf.asm +extern "C" void v86_run(u32int pd_phys, //A page directory's physical address + v86_regs_t* regs); /* * Set up a V86 task : * Allocate space for the kernel stack - * Map frames in lower 1MB : IVT (first page), BDA (0xA0000 to 0xFFFFF) + * Map frames in lower 1MB * Find somewhere to put the stack and the code, still in lower 1MB - * Map that space, and copy the 16bit code to that place + * Copy the 16bit code to that place * Setup values on the kernel stack for starting the thread (V86Thread::runV86), * giving it entry point and stack location */ -V86Thread::V86Thread(v86_function_t* entry, v86_retval_t* ret, u32int data) : Thread() { - m_ret = ret; m_ret->finished = false; - m_xchgspace = 0; - m_isKernel = true; - m_if = true; +V86Thread::V86Thread(v86_function_t* entry, v86_retval_t* ret) : Thread() { + m_ret = ret; + setup(); + m_continueOnIret = true; + + m_ret->regs->cs = V86::allocSeg(entry->size); //Alocate segments for the code to run in + u8int* codeptr = (u8int*)(FP_TO_LINEAR(m_ret->regs->cs, 0)); + memcpy(codeptr, entry->data, entry->size); //Copy the code there + + m_ret->regs->ip = 0; + + m_state = T_RUNNING; + m_process->registerThread(this); + Task::registerThread(this); +} + +V86Thread::V86Thread(u8int int_no, v86_retval_t* ret) : Thread() { + m_ret = ret; + setup(); + m_continueOnIret = false; + + //setup CS:IP for running interrupt + u16int* ivt = 0; + + m_ret->regs->cs = ivt[int_no * 2 + 1]; + m_ret->regs->ip = ivt[int_no * 2]; + + m_state = T_RUNNING; + m_process->registerThread(this); + Task::registerThread(this); +} + +void V86Thread::setup() { + m_ret->finished = false; m_xchgspace = 0; m_isKernel = true; m_process = Task::currProcess(); - m_kernelStack.addr = Mem::alloc(STACKSIZE); - m_kernelStack.size = STACKSIZE; + m_if = true; //Set virtual interrupt flag + m_kernelStack.addr = Mem::alloc(STACKSIZE); m_kernelStack.size = STACKSIZE; //Setup kernel stack m_process->getPagedir()->switchTo(); //Map all lower memory V86::map(); - u16int cs = V86::allocSeg(entry->size); //Alocate segments for the code to run in - u8int* codeptr = (u8int*)(FP_TO_LINEAR(cs, 0)); - memcpy(codeptr, entry->data, entry->size); //Copy the code there - - u16int ss = V86::allocSeg(V86_STACKSIZE); + //Allocate space for v86 stack + m_ret->regs->ss = V86::allocSeg(V86_STACKSIZE); + m_ret->regs->sp = V86_STACKSIZE - 4; + //Setup kernel stack for running v86_run (entry procedure) u32int* stack = (u32int*)((u32int)m_kernelStack.addr + m_kernelStack.size); - stack--; *stack = cs; //Pass code segment (ip = 0) - stack--; *stack = ss; //Pass stack segment (sp = V86_STACKSIZE) - stack--; *stack = data; //Pass data for thread - stack--; *stack = (u32int)this; //Pass thread pointer + stack--; *stack = (u32int)m_ret->regs; + stack--; *stack = m_process->getPagedir()->physicalAddr; stack--; *stack = 0; m_esp = (u32int)stack; - m_ebp = m_esp + 8; - m_eip = (u32int)runV86; - - m_state = T_RUNNING; - m_process->registerThread(this); - Task::registerThread(this); + m_ebp = m_esp + 4; + m_eip = (u32int)v86_run; } bool V86Thread::handleV86GPF(registers_t *regs) { @@ -135,16 +125,6 @@ bool V86Thread::handleV86GPF(registers_t *regs) { return true; case 0xCD: // INT N if (ip[1] == 3) return false; //Breakpoint exception, here used for telling that thread has ended - if (ip[1] == 60) { //INT 60 is used so that the real mode code can retrieve some regs from caller - regs->eax = m_ret->regs->eax; - regs->ebx = m_ret->regs->ebx; - regs->ecx = m_ret->regs->ecx; - regs->edx = m_ret->regs->edx; - regs->edi = m_ret->regs->edi; - regs->esi = m_ret->regs->esi; - regs->eip = (u16int)(regs->eip + 2); - return true; - } stack -= 3; regs->useresp = ((regs->useresp & 0xFFFF) - 6) & 0xFFFF; @@ -162,7 +142,7 @@ bool V86Thread::handleV86GPF(registers_t *regs) { regs->eflags = EFLAGS_IF | EFLAGS_VM | stack[2]; m_if = (stack[2] & EFLAGS_IF) != 0; regs->useresp = ((regs->useresp & 0xFFFF) + 6) & 0xFFFF; - return false; + return m_continueOnIret; case 0xFA: // CLI m_if = false; regs->eip = (u16int)(regs->eip + 1); @@ -181,7 +161,12 @@ void V86Thread::handleException(registers_t *regs, int no) { if (no == 13) { //General protection fault if (!handleV86GPF(regs)) { m_ret->finished = true; - *(m_ret->regs) = *regs; + m_ret->regs->ax = (u16int)regs->eax; + m_ret->regs->bx = (u16int)regs->ebx; + m_ret->regs->cx = (u16int)regs->ecx; + m_ret->regs->dx = (u16int)regs->edx; + m_ret->regs->di = (u16int)regs->edi; + m_ret->regs->si = (u16int)regs->esi; Task::currentThreadExits(0); return; } diff --git a/Source/Kernel/TaskManager/V86/V86Thread.class.h b/Source/Kernel/TaskManager/V86/V86Thread.class.h index 591bea6..7ca55b1 100644 --- a/Source/Kernel/TaskManager/V86/V86Thread.class.h +++ b/Source/Kernel/TaskManager/V86/V86Thread.class.h @@ -3,16 +3,22 @@ #include <TaskManager/Thread.class.h> -struct v86_retval_t { - registers_t *regs; - bool finished; -}; - struct v86_function_t { u16int size; u8int data[]; }; +struct v86_regs_t { + u16int ax, bx, cx, dx, di, si; + u16int cs, ds, es, fs, gs, ss; + u16int ip, sp; +} __attribute__((packed)); + +struct v86_retval_t { + v86_regs_t *regs; + bool finished; +}; + #define V86_STACKSIZE 1024 #define EFLAGS_IF 0x200 @@ -26,12 +32,13 @@ class V86Thread : public Thread { v86_retval_t* m_ret; - bool m_if; //V86 IF flag + bool m_if, m_continueOnIret; //V86 IF flag + + void setup(); - static void runV86(V86Thread* t, u32int data, u32int ss, u32int cs); - public: - V86Thread(v86_function_t* entry, v86_retval_t* ret, u32int data); + V86Thread(v86_function_t* entry, v86_retval_t* ret); + V86Thread(u8int int_no, v86_retval_t* ret); bool handleV86GPF(registers_t *regs); void handleException(registers_t *regs, int no); diff --git a/Source/Kernel/TaskManager/V86/v86.wtf.asm b/Source/Kernel/TaskManager/V86/v86.wtf.asm new file mode 100644 index 0000000..09b08a2 --- /dev/null +++ b/Source/Kernel/TaskManager/V86/v86.wtf.asm @@ -0,0 +1,48 @@ +; This is the procedure that is first run when starting a V86 thread +; Parameters : esp+4 = page directory to switch to, esp+8 = v86_regs_t pointer +; Algorithm : +; - Switch page directory +; - Create a false IRET structure, with : (first pushed first) +; GS, FS, DS, ES, SS, SP, EFLAGS (with eflags.if and eflags.vm), CS, IP +; - Set general purpose regisers : ax bx cx dx di si +; - Call iret, switch to V86 mode. + +[BITS 32] + +[GLOBAL v86_run] +v86_run: + mov eax, [esp + 4] + mov cr3, eax + mov eax, [esp + 8] + + xor ebx, ebx + mov bx, [eax + 20] ; get GS + push ebx + mov bx, [eax + 18] ; get FS + push ebx + mov bx, [eax + 14] ; get DS + push ebx + mov bx, [eax + 16] ; get ES + push ebx + mov bx, [eax + 22] ; get SS + push ebx + mov bx, [eax + 26] ; get SP + push ebx + pushf + pop ebx + or ebx, 20200h + push ebx + xor ebx, ebx + mov bx, [eax + 12] ; get CS + push ebx + mov bx, [eax + 24] ; get IP + push ebx + + mov bx, [eax + 2] + mov cx, [eax + 4] + mov dx, [eax + 6] + mov di, [eax + 8] + mov si, [eax + 10] + mov ax, [eax] + + iret diff --git a/Source/Kernel/VTManager/SimpleVT.class.cpp b/Source/Kernel/VTManager/SimpleVT.class.cpp index d304a5f..4a16b54 100644 --- a/Source/Kernel/VTManager/SimpleVT.class.cpp +++ b/Source/Kernel/VTManager/SimpleVT.class.cpp @@ -78,7 +78,11 @@ void SimpleVT::scroll() { BUFCHR(m_rows - 1, c).c = ' '; BUFCHR(m_rows - 1, c).color = m_color; } - if (m_mapped) redraw(); + if (m_mapped) { + if (!Disp::textScroll(m_maprow, m_mapcol, m_rows, m_cols, m_color)) { + redraw(); + } + } } void SimpleVT::updateCursor() { |