;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; April 26, 2005: I am working on fixing a problem with end of screen where ;; if the text is as lastline and last column, that the screen doesn't scroll ;; up once. This needs to happen. ;; ;; March 29, 2005: Wow, it has been a while, but I'm chuging along. I'm trying ;; figure out what is going wrong in my code that it won't work. ClearScreen ;; appears to be broken. ;; ;; March 7, 2005: It will not work since all the variables are setup as ;; real segment:offset in here, so all references to this file are ;; specifically 16-bit only. A new file was created txtdrvr32.asm that will ;; contain the 32-bit driver. ;; ;; March 1, 2005: Began working on protected mode interfacing (flat-real). ;; After further review, if I insure that the ES stays within the confines ;; of either real or protected modes, I think it can be done. ;; ;; December 14, 2004: I was looking at assembly code and noticed that 32 bit ;; coding is required for protected mode, unfortunately. My assumption of ;; using existing 16 bit code will not work. ;; ;; November 8, 2004: Implemented SetCursorXY. Updated cursor start and stop ;; based on number of pixels high the character set it. Fixed both functions ;; TurnOnCursor and TurnOffCursor with these characteristics. Redo of the ;; function ClearScreen to use the PrintCharacter function based on screen ;; size. ;; ;; November 7, 2004: Discovered that I need to update the BIOS information so ;; that DOS or whatever other OS is using that area for updates will see what ;; has changed. This will be the next programming information to do. Need to ;; also incorporate changing text mode settings. Need to implement SetCursorXY ;; function the very next time I sit to program. Did add looking at BIOS area ;; for information partaining to the video modes and updating our memory ;; information. Our memory information is going to be used for Protected Mode ;; when it is implemented. Need to also consider the number of video pages ;; so that we're currently on the right one. Need to also implement scrolling ;; so that we push text up the screen for streamed input. Minor clean up done. ;; ;; November 3, 2004: This my text driver using the port addresses in both REAL ;; and PROTECTED modes. My first attempt with the help of bubach's code of his ;; BOS Operating System. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Global Data Area ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; use32 align 4 RMTextDriverVersion db '0.00.13.5C',0 ; Current version of this ; file, in real mode. ProtectedMode db 0x00 ; Zero for now, will get ; relocated into a better ; memory location as part of ; the OS memory communication ; area TBD. ;>----------------------------- ; I just learned that 32 bit code is need for protected ; mode, so they will need to be different animals. TextMemoryLocation dd 0h ; Save text memory Location MyEDX dd 0h VIDEO_MONO equ 0b0000h ; Flat memory location VIDEO_COLOR equ 0b8000h ; Flat memory location TextCardStatusPort dw 0x0000 ; Save address of card status ; port for use. TextCardBasePort dw 0x0000 ; Save address of card base ; port for use CurrentCursorRow db 0x00 CurrentCursorColumn db 0x00 NumberOfRows db 0x19 ; 25 Rows is default NumberOfColumns db 0x50 ; 80 Columns is default IsCursorOn db 0x01 ; Cursor is on as default IsCursorInsert db 0x00 ; Cursor in insert mode off ; as default PixelsPerCharacter db 0x00 ; Pixels per char * 8 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Initialize the text mode which sets up some global variables. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; use32 ; This assumes we're in 32-bit PMode align 4 ; Aligning since I was getting faults ; in Bochs 2.1.1 InitializeTextMode: push ebx ; Store all registers that are push edx ; in this routine mov dx,word [00000463h] ; 0x3d4 = color mov [TextCardBasePort],dx ; Save base port add dx,0006h ; Point to card's status port mov [TextCardStatusPort],dx ; Save the status port mov [TextMemoryLocation],dword VIDEO_COLOR ; Default to color card mov bx,word [00000410h] ; Get equipment list and bx,word 0030h ; Only bits 5-4 cmp bx,word 0030h ; Is it monochrome (bits=11)? jne .NumberOfColumns ; No, so keep color mov [TextMemoryLocation],dword VIDEO_MONO ; Yes, set to monochrome .NumberOfColumns: mov bl,[0000044Ah] ; Get Number of Columns (for some reason 44Ah doesn't work) ; (usually 80) mov byte [NumberOfColumns],bl ; Save number of columns mov bl,[00000484h] ; Get Number of Rows ; (usually 24) inc bl ; Add 1 to count for true rows mov byte [NumberOfRows],bl ; Save number of rows .PixelsPerCharacter: mov bl,byte [00000485h] ; Save the number of pixels ; per char mov byte [PixelsPerCharacter],bl ; Save in variable mov bl,[00000450h] ; Save current cursor row mov [CurrentCursorRow],bl mov bl,[00000451h] ; Save current cursor column mov [CurrentCursorColumn],bl ; Might be wise to update the video card or vice versa ; call ClearScreen ; Clear the screen ; Get current Cursor location and update BIOS Comm Area and variables ; call TurnCursorOn ; Put on our cursor pop edx pop ebx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; SetCursorShape ;; ;; Input: ;; CH = cursor start line (bits 0-4) and options (bits 5-7). ;; CL = bottom cursor line (bits 0-4). ;; ;; NOTE: It is a good idea o know what the current character set is. If your ;; characters are only 8 pixels high, then you won't want to end at 16 now ;; would you? ;; ;; When bits 6-5 of CH are set to 00, the cursor is visible, to hide a cursor ;; set these bits to 01 (this CH value will hide a cursor: 28h - 00101000b). ;; Bit 7 should always be zero. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SetCursorShape: push eax push edx mov dx,[TextCardBasePort] mov al,0Ah mov ah,ch out dx,ax inc ax mov ah,cl out dx,ax pop edx pop eax ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Turns cursor on with our attributes. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TurnCursorOn: push eax push ecx mov ch,[PixelsPerCharacter] ; Get number of pixels sub ch,02h ; start - 2 mov cl,[PixelsPerCharacter] ; Get number of pixels dec cl ; end - 1 call SetCursorShape ; Do it mov byte [IsCursorOn],01h ; Update memory mov [460h],cl ; Update BIOS with cursor info mov [461h],ch pop ecx pop eax ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Turns cursor off with our attributes. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TurnCursorOff: push eax push ecx mov ch,[PixelsPerCharacter] ; Get number of pixels add ch,0x10 ; Turn on bit 5 mov cl,[PixelsPerCharacter] ; Get number of pixels call SetCursorShape mov byte [IsCursorOn],0x00 mov [460h],cl ; Update BIOS with cursor info mov [461h],ch pop ecx pop eax ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Clear the screen. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ClearScreen: push eax push ecx ; (((NumberOfRows - 1) * (NumberOfColumns)) + (NumberOfColumns - 1)) * 2 movzx eax,byte [NumberOfRows] dec eax ; NumberOfRows - 1 movzx ecx,byte [NumberOfColumns] mul ecx ; EAX = (NumberOfRows - 1) * NumberOfColumns dec ecx ; ECX = NumberOfColumns - 1 add eax,ecx ; Add ECX to EAX into EAX add eax,eax ; Multiply EAX by 2 mov ecx,eax ; Put counter at the top of video buffer ; Need to reset cursors to 1:1 mov eax,00000101h ; AH = 1 = Column, AL = 1 = Row call SetCursorXY ; Set the cursor location mov eax,0720h ; Character attribute and Character 'space' .loop: call PrintCharacter ; Print a space on all screen locations loop .loop ; Loop until ECX is zero mov byte [CurrentCursorRow],01h ; Replace CurrentCursorRow with 1 mov byte [CurrentCursorColumn],01h ; Replace CurrentCursorColumn with 1 mov eax,00000101h ; Reset cursor to 1:1 call SetCursorXY ; Need a look see mov [es:00000450h],byte 01h ; Update BIOS Data Area Row Page 1 mov [es:00000451h],byte 01h ; Update BIOS Data Area Column Page 1 pop ecx pop eax ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Print a character to the video screen at the current cursor location. ;; ;; Input: AL = ASCII value of character to print ;; AH = Character attribute byte ;; ;; Character attribute byte: ;; bits ;; 7 6 5 4 3 2 1 0 - Meaning of bit settings ;; --------------------------------------------- ;; 0 Normal foreground ;; 1 Blinking foreground ;; b b b Background color ;; f f f f Foreground color ;; ;; Background and foreground colors: ;; bit setting Decimal Hex Values Color ;; ------------------------------------------ ;; 0000 0 0 Black ;; 0001 1 1 Blue ;; 0010 2 2 Green ;; 0011 3 3 Cyan ;; 0100 4 4 Red ;; 0101 5 5 Magenta ;; 0110 6 6 Brown ;; 0111 7 7 White ;; 1000 8 8 Gray ;; 1001 9 9 Light Blue ;; 1010 10 A Light Green ;; 1011 11 B Light Cyan ;; 1100 12 C Light Red ;; 1101 13 D Light Magenta ;; 1110 14 E Yellow ;; 1111 15 F White (High Intensity) ;; ;; Need to trap for special characters: 10h, 13h, 08h, etcetera. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PrintCharacter: push edx push eax mov edi,[TextMemoryLocation] ; Get current video memory location offset push eax ; Save EAX call GetCursor ; Uses EAX for the offset mov ebx,2 ; Multiply EAX by 2 mul ebx add edi,eax pop eax ; Restore what needs to be printed to video ;; Need to check for special characters here cmp al,byte 0Dh ; Carriage Return je .CarriageReturn cmp al,byte 0Ah ; Line Feed je .LineFeed cmp al,byte 08h ; Backspace je .Backspace .DoIt: mov [es:edi],word AX ; OK to write it now inc edi ; Point to attribute inc edi ; Next screen location call IncrementCursor call GetCursorXY cmp al, byte [NumberOfRows] ; Check to see if at maximum already ja .DoScrollUp jmp .PrintDone ; Hop over special character conditions .LineFeed: call GetCursorXY mov ah,01h ; Reset Column to 1 call SetCursorXY jmp .PrintDone .CarriageReturn: call GetCursorXY ; Returns Row in AL, Column in AH cmp al, byte [NumberOfRows] ; Check to see if at maximum already je .DoScrollUp ; ja .DoScrollUp ; Check if Row is equal to maximum, then scroll up screen inc al ; Move to next Row call SetCursorXY jmp .PrintDone .Backspace: mov al,20h ; Put space at last screen location dec edi ; Backup edi dec edi ; to last screen location mov [es:edi],word ax call DecrementCursor ; Put cursor back one place jmp .PrintDone .DoScrollUp: call ScrollUp mov al,byte [NumberOfRows] mov ah,1 call SetCursorXY .PrintDone: pop eax pop edx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Get the cursor pos. OUT: AH = X (Columns) AL = Y (Rows) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GetCursorXY: push edx call GetCursor ; Returns offset in EAX mov dl,[NumberOfColumns] div dl ; Stores result in EAX, remainder in EDX inc al ; Add 1 to remainder mov ah,dl ; Save current Column pop edx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Get the cursor pos. OUT: EAX = Offset ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; GetCursor: push ebx push edx mov dx,[TextCardBasePort] mov al,0x0e ; Cursor address MSB out dx,al inc dx in al,dx mov bh,al mov al,0x0f ; Cursor address LSB dec dx out dx,al inc dx in al,dx mov bl,al movzx eax,bx pop edx pop ebx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Set the cursor to: AH = X = Column AL = Y = Row ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SetCursorXY: push eax push ebx push ecx push edx mov edx,eax push edx ; Save EDX movzx eax,byte dl ; Save Row dec eax ; Row - 1 movzx ebx,byte [NumberOfColumns] ; Save number of screen ; columns mul ebx ; [(Row - 1) * #Col] pop edx ; Refresh EDX movzx ebx,byte dh ; Save Column position wanted dec ebx ; Col - 1 add eax,ebx ; [(Row - 1) * #Col] + (col - ; 1) ; add eax,eax ; Multiply by 2 (Why the fuck?) call SetCursor ; Set cursor to EAX pop edx pop ecx pop ebx pop eax ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Set the cursor to: EAX = Offset ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; SetCursor: push eax push ebx push ecx push edx mov ebx,eax ; Move offset to EBX for convenience mov al,0x0e ; Cursor Address MSB mov ah,bh mov dx,[TextCardBasePort] out dx,ax inc ax ; Cursor Address LSB mov ah,bl out dx,ax ; Need to update BIOS area too. ; Last row and column for now, will update with a calculation ; routine movzx ecx,bx ; Save BX movzx eax,byte [NumberOfColumns] ; Save columns add eax,eax ; Times 2 xor ebx,ebx ; Zero EBX .DetermineRowColumn: cmp ecx,eax ; Is <= columns jb .DoBIOS ; Yes, update BIOS inc bh sub ecx,eax jmp .DetermineRowColumn .DoBIOS: mov eax,ecx shr eax,1 ; Divide by 2 mov bl,al ; Save current column dec bh ; BIOS current row mov [450h],bh ; Save to BIOS current row mov [451h],bl ; Save to BIOS current column pop edx pop ecx pop ebx pop eax ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Set cursor position +1 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; IncrementCursor: push eax call GetCursor ; Change to memory lookup inc eax ; inc eax call SetCursor pop eax ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Set cursor position -1 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DecrementCursor: push eax call GetCursor dec eax ; dec eax call SetCursor pop eax ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; To display a string directly to video memory ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PrintString: push eax push ebx push ecx push edx push edi .PrintString: lodsb ; Load byte at DS:ESI into AL OR AL,AL ; Test if character is 0 ; (end of string) JZ .Done MOV AH,07h ; Standard video attribute call PrintCharacter ; Put character on screen JMP .PrintString .Done: pop edi pop edx pop ecx pop ebx pop eax RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; To scroll up one row ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ScrollUp: push esi movzx eax, byte [NumberOfRows] movzx ebx, byte [NumberOfColumns] mul ebx dec eax add eax,eax ; Maximum text memory location add ebx,ebx ; Start of Row 2 mov edi,ebx add edi,dword [TextMemoryLocation] ; Starting location in memory add eax,dword [TextMemoryLocation] ; Ending Memory Location .Looper: mov esi,edi sub esi,ebx mov dl,byte [es:edi] mov [es:esi],dl ; Copy line to previous line inc edi ; Increment EDI cmp edi,eax ; If EDI is at the end jg .ClearLine ; then clear the end line jmp .Looper ; else keep going .ClearLine: sub edi,ebx ; Reset EDI to begining of last line inc edi .SecondLooper: mov [es:edi],byte 20h ; Place a space there inc edi inc edi cmp edi,eax ; If EDI is at the end jg .Finished ; then clear the end line jmp .SecondLooper ; else keep going .Finished: pop esi RET