[TURBO C, DJGPP] Cursor manipulieren

21/09/2010 - 16:34 von Robert Hartmann | Report spam
Hallo zusammen,

den blinkenden Cursor komplett auszuschalten oder ihn auch wieder an
zuschalten ging ganz einfach.

Nun will ich zwischen dem blinkenden Unterstrich bzw der Box hin und her
schalten.


Das Abfragen der Cursorfüllhöhe also die benutzten Scanlines
geht mit

/*
* VIDEO - GET CURSOR POSITION AND SIZE [int 10/AHh]
* http://www.ctyme.com/intr/rb-0088.htm
*
* AH = 03h
* BH = page number
* 0-3 in modes 2&3
* 0-7 in modes 0&1
* 0 in graphics modes
*
* Return:
* AX = 0000h (Phoenix BIOS) CH = start scan line CL = end scan line
* DH = row (00h is top) DL = column (00h is left)
*
*/

Mit der Information über die Anzahl der Scanlines,
die für ein Zeichen benutzt werden, -- die Anzahl mit 2 Byte
findet man in der BIOS-DATA-AREA bei 0040:0000h + 85h Offset
[vgl. http://www.bioscentral.com/misc/bda.htm ] --
kann man leicht die prozentuale Befüllung der Zelle durch den
Cursor berechnen:

procent = ((regs.h.cl - regs.h.ch)*100)/NumberOfScanlinesPerCharacter;



Soweit so gut.

Nun probiere ich den Weg andersherum. Aus einer prozentualen Füllhöhe
möchte ich die notwendigen ScanLines bestimmen, damit ich alle Parameter
zusammen habe, um folgendes auszuführen.

/* VIDEO - SET TEXT-MODE CURSOR SHAPE [Int 10/AHh]
* http://www.ctyme.com/intr/rb-0086.htm
*
* AH = 01h
* CH = cursor start and options [options see below]
* CL = bottom scan line containing cursor (bits 0-4)
*
*
* Bitfields for cursor start and options:
* Bit(s) Description (Table 00013)
* 7 should be zero
* 6,5 cursor blink.
* (00=normal, 01=invisible, 10=erratic, 11=slow).
* (00=normal, other=invisible on EGA/VGA)
* 4-0 topmost scan line containing cursor
*
*/

/* Befüllung der Scanline von start(unten,bottom) nach end(oben,top).*/


Was mich stutzig macht, nachdem mein Code (s.u.) nicht so funktioniert
wie ich es dachte, ist :

Man hat der Beschreibung nach jeweils 4 Bit um Start- und End-Scanline
des Cursors festzulegen.

Die Anzahl der Scanlines eines Zeichen jedoch brauchen 2Byte (2*8 Bit =
16 Bit)

Die Rückgabe der ScanLines sind jeweils 2 8bit (unsigned char) Werte.

Irgendwie passt das nicht alles zusammen ...


Und hier ist mein Code. Wo ist der algorithmische oder Wertebereichsfehler?

Danke und Gruß Robert

/********* CODE hier **********/

#include<stddef.h>
#include<stdlib.h>
#include<assert.h>

#ifdef __STRICT_ANSI
#error For some reasons you cannot compile this code in strict_ansi_c
#endif


#include<stdio.h>
#include<string.h>
#include<limits.h>




#ifdef __MSDOS__
#include<dos.h>

#ifdef __DJGPP__
#include<dpmi.h>
#include<go32.h>
#include<sys/farptr.h>
#include<sys/segments.h>
#endif /*DJGPP*/

#elif defined(__WIN32__) || defined(_MSC_VER)
#include<windows.h>
#include<malloc.h>

#elif defined(__unix__) && !defined(__MSDOS__) && !defined(__WIN32__)
#error kommt spaeter dran :-)
#else
#error Code for your system or compiler is not implemented jet. feel
free to do :-)
#endif






enum CURSORMODE {_NOCURSOR = 0,
_SOLIDCURSOR = 1,
_NORMALCURSOR = 2};

typedef enum CURSORMODE CURSORMODE;

struct CURSOR_INFO {
unsigned long procent;
CURSORMODE mode;
};

typedef struct CURSOR_INFO CURSOR_INFO;








void setCursorMode(CURSOR_INFO cmode){
#ifdef __MSDOS__

#if ((CHAR_BIT != 8)||(UCHAR_MAX != 0xFF))
#error the size of unsigned char must be 1 byte with 8 bit
#endif

#if (USHRT_MAX != 0xFFFF)
#error the size of unsigned short must be 2 byte
#endif

/* BasePointer for BiosDataArea access*/
unsigned BDA_seg;
unsigned BDA_off;
#ifdef __TURBOC__
/*Because we must use a 2-byte-block of BIOS-DATA-AREA as value: */
unsigned short far* BiosDataArea_ShortPointer = NULL;

#elif __DJGPP__
/* in DJGPP we solve it without a special pointer */

/* unsigned short * BiosDataArea_Pointer = NULL; */

#else
#error your compiler has not been testet ... you must modify souce code
here.
#endif

unsigned short NumberOfScanlinesPerCharacter,startline,endline;
unsigned long top;
union REGS regs;

NumberOfScanlinesPerCharacter = startline = endline = 0;
top = 0;

/* set BasePointer to BiosDataArea segment:offset = 0040h:0000h*/
BDA_seg = 0x0040;
BDA_off = 0x0000;

/*
we must use a pointer to 0040:0000h + 85h Offset
(and optain the next 2 bytes as value for NumberOfScanlinesPerCharacter)
*/
BDA_off += 0x0085;

#ifdef __TURBOC__
BiosDataArea_ShortPointer = (unsigned short far*) MK_FP(BDA_seg,BDA_off);
if (BiosDataArea_ShortPointer)
NumberOfScanlinesPerCharacter = BiosDataArea_ShortPointer[0];

#elif __DJGPP__
/* Use _farpeekw to peek at 16-bit shorts and _farpeekl to peek at
32-bit longs.*/
NumberOfScanlinesPerCharacter = _farpeekw( _dos_ds, BDA_seg*16 + BDA_off );
#endif

/* get current information about cursor scanlines: start and end */

regs.h.ah = 3; /* get cursor position and size */
regs.h.bh = 0; /* video page #0 */
int86(0x10, &regs, &regs);

startline = regs.h.ch; /* start scan line (bottom)*/
endline = regs.h.cl; /* end scan line (top) */

/* ... just for documentation
*
* procent = ((top-start)*100) / NumberOfScanlinesPerCharacter
*
* => top = ((procent * NumberOfScanlinesPerCharacter) / 100 ) + start
*
*/


/* VIDEO - SET TEXT-MODE CURSOR SHAPE [Int 10/AHh]
* http://www.ctyme.com/intr/rb-0086.htm
*
* AH = 01h
* CH = cursor start and options [options see below]
* CL = bottom scan line containing cursor (bits 0-4)
*
*
* Bitfields for cursor start and options:
* Bit(s) Description (Table 00013)
* 7 should be zero
* 6,5 cursor blink.
* (00=normal, 01=invisible, 10=erratic, 11=slow).
* (00=normal, other=invisible on EGA/VGA)
* 4-0 topmost scan line containing cursor
*
*
* Befüllung der Scanline von start(unten,bottom) nach end(oben,top).
*/


/* Algorithmus:
* StartScanLine (bottom) bleibt erhalten
* EndSanLine (top) wird entsprechend der Prozentangabe modifiziert
* CursorBlink wird entsprechend cmode.mode veràndert
*/

top = ((cmode.procent * NumberOfScanlinesPerCharacter) / 100 ) +
startline ;
endline = (top<=0x0F? (unsigned short)top : 0x0F );

/* 1000 0000 = 0x80 */
/* 0111 1111 = 0x7F */

/* CURSOR BLINK NORMAL */
/* 0001 0000 = 0x10 */

/* CURSOR BLINK INVISIBLE */
/* 0011 0000 = 0x30 */

/*determine the correct bits for option */
if(cmode.mode == _NOCURSOR){
regs.h.ch = 0x7F & 0x30 ; /* option: cursor invisible */
} else {
regs.h.ch = 0x7F & 0x10 ; /* option: cursor blink normal */
}

regs.x.ax = 0;
regs.x.bx = 0;
regs.h.ah = 1; /* SET TEXT-MODE CURSOR SHAPE */
regs.h.ch = regs.h.ch | ( 0x0F & endline); /* options + top*/
regs.h.cl = (0x0F & startline); /* bottom */



assert((regs.h.ch<=0xFF)&&(regs.h.ch>0x00));

assert((regs.h.cl<=0xFF)&&(regs.h.cl>0x00));

int86(0x10, &regs, &regs);

/* TODO: warum macht es nicht das,
was ich dachte, dass es soll ?? !*/


#elif defined(__WIN32__) || defined(_MSC_VER)
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &CursorInfo);
switch(cmode.mode){
case _NOCURSOR: setCursorOff(); break;
case _SOLIDCURSOR: CursorInfo.dwSize0;
CursorInfo.bVisible=(cmode.mode!=_NOCURSOR); break;
case _NORMALCURSOR: CursorInfo.dwSize%;
CursorInfo.bVisible=(cmode.mode!=_NOCURSOR); break;
default: CursorInfo.dwSize = cmode.procent;
CursorInfo.bVisible=(cmode.mode!=_NOCURSOR); break;
}
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &CursorInfo);

#elif defined(__unix__) && !defined(__MSDOS__) && !defined(__WIN32__)
#error kommt spaeter dran
#else

#error setCursorColor(CURSORMODE): Code for your system or compiler is
not implemented jet. feel free to do :-)

#endif
}

/********* CODE endet hier **********/
 

Lesen sie die antworten

#1 Robert Hartmann
22/09/2010 - 15:29 | Warnen spam
Am 21.09.2010 16:34, schrieb Robert Hartmann:
Hallo zusammen,

den blinkenden Cursor komplett auszuschalten oder ihn auch wieder an
zuschalten ging ganz einfach.

Nun will ich zwischen dem blinkenden Unterstrich bzw der Box hin und her
schalten.




Ich denke ich habe das Problem selber gelöst.

Die Anzahl der Scanlines, die für die Darstellung eines Zeichens
unter DOS benutzt werden, stehen im BIOS-DATA-AREA bei der
Adresse (Segment:Offset) = (40h:85h) [hexadezimale Notation].

[Mein Debuglauf hat den dezimalen Wert 16 geliefert.]

Warum die Anzahl 2 Byte im BDA belegen ist mir nicht klar.
(Es war evtl. einfacher so?)

Wenn man die aktuell genutzten Scanlines des Cursor-Symbols
abfragt [INT 10 / AHh, BHh], dann bekommt man
als Antwort (jeweils 1 Byte)
CH = start scan line
und
CL = end scan line

[Mein Debuglauf hat ergeben, dass der Wert in
CH kleiner als der von CL ist,
genauer mein Default-Cursor hatte als Scanlines
in dezimale Werte
CH = 13 und CL = 14]


Wenn man nun die Scanlines des Cursors manipulieren will,
dann hat kann mit [Int 10/AHh, BH=0h ] jeweils 5 Bits [*],
genauer die Bits 4 bis 0 (einschließlich), in CH und in CL
die start scan line und die end scan line setzen.


Auch für das Setzen der Scanlines gilt

CH = start scan line = top most scan line
und
CL = end scan line = bottom scan line


Der kleinste Wert einer Scanline ist
binàr 00000 = 0h = 0 dezimal

der größte Wert einer Scanline ist
binàr 11111 = 1Fh = 31 dezimal
[=> man hàtte ein Byte im BDA auch sparen können :-) ]

Aus der Information CH < CL kommt man zur Nummerierung
der Scanlines in einem theoretischen Cursorsymbol mit
maximaler Größe:

+-+
|0. |
|1. |
|2. |
|3. |
|4. |
|5. |
...
|10. |
|11. |
...
|30. |
|31. |
+-+

Gruß Robert


[*] Hier hatte ich zunàchst einen Lesefehler gemacht.

Ähnliche fragen