Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: üäpöol am 04. June 2012, 18:17
-
Hallo,
ich habe mir dieses großartige Tutorial (http://www.lowlevel.eu/wiki/VESA_BIOS_Extensions) angeschaut und wollte als erstes testen, ob LFB bei verschiedenen Grafikmodi vorhanden ist.
Hier der Code:
mov ax, 0x4F01
mov di, 0x0
mov bx, 0x8000
mov es, bx
mov cx, 0x0118
int 0x10
mov al, byte [0x8000]
and al, 10000000b
cmp al, 0
jne LFB
je no_lFB
LFB:
mov al, 'j'
mov ah, 0x0E
int 0x10
no_lFB:
mov al, 'n'
mov ah, 0x0E
int 0x10
jmp $
Ich habe leider keine Ahnung, warum n ausgegeben wird.
-
Du hast Segment und Offset verdreht. Du sagst dem BIOS, dass es nach 8000:0000 (es:di) schreiben soll, aber du liest von irgendwas:8000.
-
OK. Danke schonmal. Aber wie kann ich dann von 0x8000:0000 lesen?
EDIT:
Hat sich erledigt.
mov bx, 0x8000
mov es, bx
mov si, 0x0
mov al, [es:si]
-
Ich habe noch ein kleines Problem. Ich möchte die Startadresse des LFB auslesen. Dazu habe ich folgende Assemblerfunktion geschrieben:
[GLOBAL GetGraficModeInfo]
GetGraficModeInfo:
push ebp
mov ebp, esp
mov si, [ebp+8]
mov bx, 0x8000
mov es, bx
mov eax, [es:si]
mov esp, ebp
pop ebp
ret
Jetzt sieht meine DrawPixel so aus:
void DrawPixel(int x, int y, long color){
*(int *)(GetGraficModeInfo(40) + y * GetGraficModeInfo(50) + x * 4) = color;
}
Leider wird nicht gezeichnet.
EDIT:
QEMU gibt aus: "General Protection Fault". Da scheint etwas gewaltig schief zu gehen.
-
Wenn du im Protected Mode bist, darfst du nicht einfach so die Segmentregister mit Real Mode Segmenten laden. (Der GPF versucht dir zu sagen, dass 0x8000 kein gültiger Selektor ist.) Du solltest mit linearen Adressen arbeiten.
int GetGraficModeInfo(int offset)
{
return *(int *)(0x80000 + offset);
}
Der Wert ist jetzt 0x80000 weil segment * 16 + offset = 0x8000 * 16 + offset = 0x80000 + offset.
Übrigens solltest du für die bytes per scan line nur ein Word lesen, kein DWord.
void DrawPixel(int x, int y, long color){
*(int *)(GetGraficModeInfo(40) + y * (GetGraficModeInfo(50) & 0xffff) + x * 4) = color;
}
Wenn du über den Prototypenstatus hinaus bist, kannst du zum Beispiel ein struct anlegen, dass dem Format der Modusinformationen entspricht. Dann umgehst du Probleme mit den Datentypen. Zum Beispiel wie hier: http://wiki.osdev.org/Getting_VBE_Mode_Info
-
Wow, es funktioniert perfekt! Viele vielen Dank! :) Ich muss mich zwar noch mit den Farben auseinandersetzen, das ist aber glaube ich auch irgendwo beschrieben. :)
-
Es gibt doch noch ein Problem. Es geht um die Funktion DrawCircle.
void DrawCircle(int centerX, int centerY, int radius, short R, short G, short B){
for(int actPoint = 0; actPoint <= radius; actPoint++){
DrawPixel(centerX+actPoint, centerY+sqrt(radius*radius-actPoint*actPoint), R, G, B);
DrawPixel(centerX+sqrt(radius*radius-actPoint*actPoint), centerY+actPoint, R, G, B);
DrawPixel(centerX+actPoint, centerY-sqrt(radius*radius-actPoint*actPoint), R, G, B);
DrawPixel(centerX+sqrt(radius*radius-actPoint*actPoint), centerY-actPoint, R, G, B);
// ZWEITER TEIL, FUNKTIONIERT NICHT RICHTIG
DrawPixel(centerX-actPoint, centerY+sqrt(radius*radius-actPoint*actPoint), R, G, B);
DrawPixel(centerX-sqrt(radius*radius-actPoint*actPoint), centerY+actPoint, R, G, B);
DrawPixel(centerX-actPoint, centerY-sqrt(radius*radius-actPoint*actPoint), R, G, B);
DrawPixel(centerX-sqrt(radius*radius-actPoint*actPoint), centerY-actPoint, R, G, B);
}
}
Der zweite Teil des Kreises sieht dann aber nicht so aus wie erste und recht.
Ein Bild ist angehängt. Ich kann's mir nicht erklären, weil auf der linken Seite dieselben Y-Werte sein sollte wie auf der Rechten.
-
Also ich weiß auch nicht was da verkehrt ist, aber du solltest dir mal den Bresenham-Algorithmus für Kreise angucken.
-
Das ist jetzt hochinteressant. Ich bekomme ein sehr ähnliches Ergebnis mit dem Algorithmus. Es liegt also nicht am Code, sondern an sonst irgendetwas. Woran kann das liegen?
-
Ich habe das Problem jetzt gelöst.
// LINKE SEITE
for(int actPoint = 0; actPoint <= radius; actPoint++){
DrawPixel(centerX-actPoint, centerY+sqrt(radius*radius-actPoint*actPoint), R, G, B, A);
DrawPixel(centerX-sqrt(radius*radius-actPoint*actPoint), centerY+actPoint, R, G, B, A);
DrawPixel(centerX-actPoint, centerY-sqrt(radius*radius-actPoint*actPoint), R, G, B, A);
DrawPixel(centerX-sqrt(radius*radius-actPoint*actPoint), centerY-actPoint, R, G, B, A);
}
for(int actPoint = -radius; actPoint <= 0; actPoint++){
DrawPixel(centerX+actPoint, centerY+sqrt(radius*radius-actPoint*actPoint), R, G, B, A);
DrawPixel(centerX+sqrt(radius*radius-actPoint*actPoint), centerY+actPoint, R, G, B, A);
DrawPixel(centerX+actPoint, centerY-sqrt(radius*radius-actPoint*actPoint), R, G, B, A);
DrawPixel(centerX+sqrt(radius*radius-actPoint*actPoint), centerY-actPoint, R, G, B, A);
}
Vielleicht hat ja noch jemand eine Erklärung für mich. :)
EDIT:
Ich habe leider noch ein Problem mit der Farbtiefe.
short BitsPerPixel = GetGraficModeInfo(25);
switch(BitsPerPixel){
case 24:
x *= 3;
*(int*)(LFB_Start+y*(LinBytesPerScanLine & 0xFFFF)+x) = B;
*(int*)(LFB_Start+y*(LinBytesPerScanLine & 0xFFFF)+x+1) = G;
*(int*)(LFB_Start+y*(LinBytesPerScanLine & 0xFFFF)+x+2) = R;
break;
case 32:
x *= 4;
*(int*)(LFB_Start+y*(LinBytesPerScanLine & 0xFFFF)+x) = A;
*(int*)(LFB_Start+y*(LinBytesPerScanLine & 0xFFFF)+x+1) = B;
*(int*)(LFB_Start+y*(LinBytesPerScanLine & 0xFFFF)+x+2) = G;
*(int*)(LFB_Start+y*(LinBytesPerScanLine & 0xFFFF)+x+3) = R;
break;
default:
break;
};
Ich bin immer noch im Modus 0x11B, aber es wird nichts gezeichnet.
-
Es gibt gleich noch ein Problem. Ich möchte eine Funktion schreiben, die den besten Grafikmodus auswählt. Dazu habe ich eine Funktion geschrieben, die die Informationen zum aktuellen Grafikmodus in 0x8000 schreiben soll. Leider ist cx nie 0xFFFF. Hier der Code:
mov dword [VBEInfoBlock-start], "VBE2"
mov ax, 0x4F00
mov bx, VBEInfoBlock-start
mov es, bx
mov di, 0
int 0x10
; ...
Get_Info_Of_Grafic_Mode:
mov bx, VBEInfoBlock-start
mov es, bx
mov di, 14
mov cx, [es:di]
mov es, cx
mov al, byte [Grafic_Mode_number-start]
mov ah, 0
mov cx, 2
mul cx
mov di, ax
mov cx, [es:di]
mov bx, 0x8000
mov es, bx
mov di, 0
mov ax, 0x4F01
int 0x10
ret
VBE2 funktioniert. Die Funktion Get_Info_Of_Grafic_Mode soll zuerst die Adresse der Liste auslesen und dann den Modusnamen aus der Liste auslesen. Danke im Voraus.
EDIT:
OK, das kann natürlich nicht funktionieren. Ich habe die Funktion jetzt so verändert, dass es imho funktionieren müsste:
mov bx, 0x7E00
mov es, bx
mov di, 0
mov [es:di], dword "VBE2"
mov ax, 0x4F00
int 0x10
; ...
Choose_Best_Grafic_Mode:
call Get_Grafic_Mode_Info
cmp ax, 0x118
je Set_Grafic_Mode
Get_Grafic_Mode_Info:
mov bx, 0x7E00
mov es, bx
mov di, 14
mov es, [es:di]
mov di, word [Grafic_Mode_number-start]
mov bx, 0x8000
mov es, bx
mov di, 0
mov cx, [es:di]
mov ax, 0x4F01
int 0x10
mov ax, cx
ret
Set_Grafic_Mode:
mov ax, 0x4F02
mov bx, 0x11B
int 0x10
jmp GraficModeSet
Das sollte doch eigentlich funktionieren, weil 0x118 normalerweise unterstürzt wird. Sobald der Grafikmodus gefunden wird soll er gesetzt werden. Das ist natürlich nur ein einfacher Test, aber ich weiß nicht mal, warum das nicht funktioniert.
-
OK. Das hat sich erledigt. Aber eine Frage habe ich noch. Wie setzt man "Packed pixel" GraficModeInfo+27 == 0x04?
-
Erstmal sorry für die vielen Posts! :oops: Es wird inzwischen immer der beste Grafikmodus ausgewählt. Allerdings gibt es noch ein Bochsproblem.
romimage: file=Bochs-2.5.1/BIOS-bochs-latest
vgaromimage: file=Bochs-2.5.1/VGABIOS-lgpl-latest
vga: extension=vbe, update_freq=10
Das führt im bochsscr schonmal dazu, dass der Grafikmodus ausgewählt wird. Leider werden keine Pixel gezeichnet. Es kann kaum am Code liegen, weil es in VirtualBox und am echten PC funktioniert. Was muss ich bei Bochs noch einstellen?
-
Die Grafikroutinen erinnern mich an meine ersten Gehversuche in Assembler. Ich war davon begeistert, wie sehr man mit REP MOVSD/STOSD und verschachtelten LOOPs optimieren konnte. Ich bin bis heute kein Fan von selfmodifying Code auf Maschinencode-Ebene, Farbverläufe mit Dithering... toll
Also Bochs bietet VBE, Vesa Bios Extension, google mal danach.
-
Ja... :D Ich bin leider immer noch Anfänger, was OSDev angeht. Was meinst du denn mit "selfmodifying Code auf Maschinencode-Ebene"? Dass Bochs VBE bietet, kann ich ja schon sehen, da in einen Grafikmodus geschaltet wird, aber Pixel kann ich trotzdem nicht zeichnen. Kann das überhaupt sein, oder kann doch etwas an meinem Code falsch sein? Ich könnte ihn mal posten...
-
Ich poste den Code einfach mal. Da es doch relativ viel ist, habe ich es mit Pastebin gemacht:
http://pastebin.com/AMBVz5xK
Ich habe den Code leider noch nicht kommentiert, aber er sollte nicht so schwer zu verstehen sein. Es werden einfach unterstützte Grafikmodi durchgegangen, von denen der beste gespeichert und irgendwann aufgerufen wird.
-
Definiere mal "der beste Grafikmodus". Ist es der mit der höchsten Auflösung, höchsten Farbtiefe oder etwas ganz anderes?
VESA garantiert nicht, dass der Bildschirm die angegebene Auflösung auch darstellen kann. Es gibt neben dem VBE-Standard noch den VBE/DDC-Standard, der dir die EDID (Fähigkeiten) des Bildschirms mitteilen kann, sofern es unterstützt wird (VBE/DDC ist eine Extension). Im Emulator geht natürlich alles, was die virtuelle Grafikkarte kann.
Gruß,
Svenska
-
Rangfolge:
1. Beste X-Auflösung
2. Beste Y-Auflösung
3. Beste Farbtiefe
Verboten:
- Kein LFB
- Textmodus
- "Packed pixel"
VirtaulBox hat keine Probleme, aber Bochs. Aber Bochs müsste doch mindestens 0x115 unterstützen, oder? Es wird aber leider nie ein Pixel gezeichnet.
-
Ich hab's grad mal mit QEMU getestet. Da ist es exakt dasselbe ...
-
Welcher Grafikmodus wird denn ausgewählt?
Vielleicht ist der ja einfach nur kaputt oder unerwartet. Gib einfach mal einen vor, z.B. 800x600x16. Die simulierte Cirrus Logic kann meines Wissens keine 32-Bit-Modi, sondern nur welche mit 24 Bit/Pixel.
-
Das habe ich auch schon einmal versucht. Ändert leider nicht wirklich etwas. Nur, dass eine grüne Linie gezeichnet wird. (nur in Bochs). Es soll aber ein kleines grünes Quadrat (an einer anderen Stelle) und ein großes weißen Rechteck gezeichnet werden. Mit VirtualBox gibt es aber wie immer kein Problem ...
EDIT:
Vielleicht liegt es am bochssrc:
# configuration file generated by Bochs
plugin_ctrl: unmapped=1, biosdev=1, speaker=1, extfpuirq=1, gameport=1
config_interface: win32config
display_library: win32
memory: host=32, guest=32
romimage: file=Bochs-2.5.1/BIOS-bochs-latest
vgaromimage: file=Bochs-2.5.1/VGABIOS-lgpl-latest
vga: extension=vbe, update_freq=10
boot: floppy
floppy_bootsig_check: disabled=0
floppya: type=1_44, 1_44="..\..\files\OS.img", status=inserted, write_protected=0
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata2: enabled=0
ata3: enabled=0
parport1: enabled=1, file=""
parport2: enabled=0
com1: enabled=1, mode=null, dev=""
com2: enabled=0
com3: enabled=0
com4: enabled=0
usb_uhci: enabled=0
usb_ohci: enabled=0
usb_xhci: enabled=0
pci: enabled=1, chipset=i440fx
vga: extension=vbe, update_freq=5
cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=0, cpuid_limit_winnt=0, ignore_bad_msrs=1
cpuid: family=6, model=0x03, stepping=3, mmx=1, apic=xapic, sse=sse2, sse4a=0, sep=1, aes=0, xsave=0, xsaveopt=0, movbe=0, smep=0, x86_64=1, 1g_pages=0, pcid=0, fsgsbase=0, mwait=1, mwait_is_nop=0
cpuid: vendor_string="GenuineIntel"
cpuid: brand_string=" Intel(R) Pentium(R) 4 CPU "
print_timestamps: enabled=0
port_e9_hack: enabled=0
private_colormap: enabled=0
clock: sync=realtime, time0=local
# no cmosimage
ne2k: enabled=1, ioaddr=0x300, irq=9, mac=fe:fd:de:ad:be:ef, ethmod=null, ethdev=xl0, script=none
pnic: enabled=0
sb16: enabled=1, midimode=0, midi=, wavemode=0, wave=, loglevel=0, log=, dmatimer=0
es1370: enabled=0
# no loader
log: log.bochs
logprefix: %t%e%d
panic: action=report
error: action=report
info: action=report
debug: action=report
keyboard_type: mf
keyboard_serial_delay: 250
keyboard_paste_delay: 100000
keyboard_mapping: enabled=0, map=
user_shortcut: keys=none
mouse: enabled=0, type=ps2, toggle=ctrl+mbutton
EDIT2:
Ich hab's rausbekommen. :)
or bx, 0100000000000000b
...
Bit 14 muss natürlich gesetzt sein. Vermutlich macht VirtualBox das automatisch. Nur QEMU zeichnet nichts. Muss ich da noch irgendetwas einstellen?
EDIT3:
QEMU hatte ein Problem mit LinBytesPerScanLine. Deshalb habe ich das einfach per Hand mit BitsPerPixel/8*ScreenWidth ausgerechnet. Jetzt funktioniert alles. :) Vielen Dank für eure Hilfe!
-
Ich habe noch ein (kleines) Problem: Bochs hat eine zu hohe Auflösung. :D Mein PC ist 1280*1024 groß. Bochs zeigt aber unglaubliche 2560*1600 an. Jetzt wird nur die Hälfte angezeigt. Gibt es dafür eine andere Lösung, als temporär die Auflösung zu begrenzen?
-
Meinen Hinweis hast du ja angemessen ignoriert.
Lies ihn einfach nochmal, Post vom 10.06., 19:34 Uhr. ;-)
-
Tut mir Leid. :oops: Das Problem hat sich sowieso von selbst erledigt, da ich jetzt einen PC mit höherer Auflösung habe. Danke.
-
Damit hast du auch das Problem erfolgreich ignoriert...
Nicht jeder hat einen Bildschirm, der 1920x1080 darstellen kann, selbst dann, wenn die Grafikkarte damit wirbt. Viel schlimmer: Es gibt Bildschirme, die kaputt gehen, wenn man solche Auflösungen einfach mal draufwirft. (Wobei die inzwischen entweder 20 Jahre alt oder spezielle Geräte, z.B. Medizintechnik sein dürften. Ändert aber nichts am Fakt.)
Das zieht sich aber durch fast alle Hardware. Du kannst nicht einfach mal drauf rumbasteln und gucken, ob der Emulator deiner Wahl das schluckt. Der Code funktioniert dann halt im Emulator nicht oder der Emulator stürzt ab, im Extremfall vielleicht auch der Host. Aber echte Hardware könnte auf komische Dinge mit Rauchzeichen oder anderen dauerhaften Defekten (gelöschtes Kalibrier-EEPROM oder ähnliches) reagieren.
"Ich hab jetzt einen Computer, wo es geht" ist meist eine sehr schlechte Lösung. Gucke dir DDC an, stell sinnvolle Standardwerte ein (z.B. Xorg hat die Auflösung, wenn kein Autodetect geht, erst vor relativ kurzer Zeit von 800x600 auf 1024x768(!) gestellt, manche Treiber defaulten auf 640x480.) und mach die Werte einstellbar. Wenn dein System beim ersten Start sagt, dass der Bildschirm nicht erkannt wurde und du bitte eine Auflösung angeben sollst, wird dir keiner den Kopf abreißen. Windows tut das übrigens genauso bei der Installation: "Können Sie diesen Text lesen? [ Ja ] [ Nein ]".
Denk mal drüber nach.
Gruß,
Svenska
-
Die Adresse der ModeList besteht aus Offset und Segment.
Function 00h - Return VBE Controller Information; Buffer + 0Eh = VideoModePtr dd ? ; VbeFarPtr to VideoModeList
Im Unterschied dazu ist die Adresse des LFBs eine lineare Adresse.
Function 01h - Return VBE Mode Information; Buffer + 28h PhysBasePtr dd ? ; physical address for flat memory frame buffer
...
Um zu überprüfen welche Kapazität unser Monitor verträgt könnte man auch dessen maximal zulässige KHZ und HZ abfragen.
mov ax, 4F15h ; DDC - INSTALLATION CHECK
xor bl, bl
int 10h
cmp ax, 4Fh
jnz NODDC
mov ax, 4F15h ; DDC - READ EDID
mov bl, 1
xor cx, cx
xor dx, dx
mov di, OFFSET EDID ; es:di 128 byte
int 10h
cmp ax, 4Fh
jnz short NODDC
mov eax, 0FD000000h ; Text-identifier V/H range
mov bx, 36h
cmp eax, [di+bx] ; di+36h detailed timing #1
jz short RANGE
lea bx, [bx+12h]
cmp eax, [di+bx] ; di+48h detailed timing #2
jz short RANGE
lea bx, [bx+12h]
cmp eax, [di+bx] ; di+5Ah detailed timing #3
jz short RANGE
lea bx, [bx+12h]
cmp eax, [di+bx] ; di+6Ch detailed timing #4
jnz short NODDC
RANGE: mov al, [di+bx+6] ; MAXHZ
mov dl, [di+bx+8] ; MAXKHZ
Quelle: "EEDIDguideV1.pdf" von vesa.org
..
Eine beste Auflösung findet man wohl, wenn das "Feature Support Byte" im EDID-Buffer+18h auf 1 gesetzt ist und unser "Prefered Timing Mode"(GTF standard frequency range) in den Detailed Timing Position Offset 36h - 47h zu finden ist.
Ab VBE3 kann man dann die vorhandenen Vesamodi mit einem eigenen Video-Timing einschalten, wenn mehr als nur 60 hz Refreschrate benötigt werden.
Quelle: "vbe3.pdf" von vesa.org
Z.B. für 1280x1024@85 Hz Refreshrate:
; 1280x1024@85 Hz
;---------------------
CRTC DW 1728 ; horizontal Total in Pixel
HORIANF DW 1336 ; horizontal Sync-Start in Pixel
HORIEND DW 208 ; horizontal Sync-End in Pixel
VERTOTA DW 1072 ; vertical Total in Lines
VERTANF DW 1024 ; vertical Sync-Start in Lines
VERTEND DW 44 ; vertical Sync-End in Lines
DOIFLAG DB 0 ; Flag (interlaced,doubleScan,polarity)
PIXCLOC DD 157498000 ; Pixel clock in hz (zusätzlich müssen wir vorher Get/Set Pixelclock (Function 4F0Bh) verwenden)
REFRATE DW 8502 ; Refresh-Rate in 0.01 hz
DB 40 dup (0) ; reserved
;---------------------
Für Experimente (auf eigene Gefahr) eignet sich ein analoger CRT-Monitor der sich bei falschen Einstellungen selber abschaltet und eine "Out of Sync" -Fehlermeldung, oder änhlichen Fehlertext onscreen anzeigt, falls unser Pixelclock dazu nicht richtig eingestellt wurde, oder wir die Parameter falsch berechnet haben...etc..
Wie wäre es mit einer Sinus-Tabelle zu arbeiten?
Grad = 360 * 2
Endtab = 450 * 2
Foktor = 10000h
SINTAB DB Endtab DUP (?,?,?,?)
TEIL DW Grad/2, ?
I DW 0, 0
FAKT DD Foktor
TABLE: xor di, di ; Sinus-Tabelle anlegen
TAB: fldpi ; PI laden
fimul DWORD PTR[I] ; Zaehler mal PI
fidiv DWORD PTR[TEIL] ; durch 180(INT) teilen
fsin ; davon den Sinus
fimul DWORD PTR[FAKT]
fistp DWORD PTR[di] ; in die Sinus-Tabelle schreiben
;-------------------------------------
inc WORD PTR[I] ; Grad-Zähler erhöhen
add di, 4 ; Tabellen-Index erhöhen
cmp WORD PTR[I], Endtab ; Tabellen-Ende erreicht ?
jnz TAB
Dirk
-
Ich habe alle CRT aus meiner Umgebung entfernt.
EDIT: sie sind sperrig und verbrauchen mehr Strom...
-
Ich habe alle CRT aus meiner Umgebung entfernt.
Bis auf eine 19" Röhre habe ich das auch gemacht. Beim Testen eines Vesamodes mit 1024x768x32 und mit eigenen Bildparameter für 100 hz Refreshrate schaltet mein LCD diesen Mode aber mit 60hz Refreshrate ein und ignoriert die angegebenen Parameter mit der höheren Refreschrate und dem dafür höheren vorgesehenen Pixeltakt einfach.
Ich vermute mal wenn ein 100hz-LCD-Monitor verwendet wird, dann wird jeder hohe Vesamode auch ohne die Übergabe eines CRT-Parameter-Blöck in 100 hz angezeigt.
Ich habe leider noch kein 100hz-LCD-Monitor, um das mal auzutesten. Weiss jemand darüber Etwas genaueres?
Dirk