Nazačátek bych zde vysvětlil některé techniky zobrazování používané na normálním PC.
Unbuffered mode, popř. direct access - je případ, kdy body scény průběžně vykreslujeme na obrazovku. To lze provést buď funkcí BIOSu (pomalé) nebo přímým zápisem do VRAM. Tento způsob je výhodný v případě, že vykreslujeme malé jednoduché objekty z pár pixelů. Také nemusíme ani mazat celou obrazovku, ale stačí, když si před vykreslením schováme hodnoty původních bodů, překreslíme je, a při dalším frame je opět obnovíme.
Double buffering - protože je zápis do VRAM pomalejší než do jiné oblasti RAM, používá se u složitých scén bufferu v normální RAM, kam se vše kreslí a když je frame hotov, překopíruje se celý buffer do VRAM. To lze provést rychle pomocí MOVSW/MOVSD. Před začátkem kreslení dalšího frame se musí buffer smazat.
Page flipping - Lze použít ve videorežimech nabízejících více stránek. Máme-li dvě stránky ve VRAM, tak jednu necháme zobrazovat na obrazovce a druhou namapujeme do adresního prostoru PC a kreslíme do ní. Když je frame hotov, prohodíme stránky, smažeme předtím zobrazovanou stránku a kreslíme do ní nový frame.
Jak je to na portfoliu? LCD displej portfolia má rozlišení 240x64 bodů hloubky 1bit. VRAM leží na adresách B000:0000 až B000:0FFF, velikost aktivní stránky je 240x64/8 = 1920 B. Z toho plyne, že 1 Byte obsahuje 8 pixelů a to tak, že MSB je pixel nejvíce nalevo a LSB nejvíce napravo. Pokud je bit nastaven na 1, je zobrazen tmavý bod, pokud 0 ne zobrazen světlý bod. Zásadní rozdíl mezi PC a portfoliem je v tom, že pokud něco zapíšeme přímo do VRAM, tak se změna projeví až po 1 nebo 128s, podle nastavení časovače v SETUPu. O vlastní kreslení na LCD se stará řadič Hitachi HD61830, který je přístupný přes I/O porty 8010h a 8011h (viz níže).
Napřed musíme nastavit grafický mód:
void setmode(Byte mode) /* set mode GFX/TXT */ { union REGS regs; regs.h.ah = 0; /* MOV AH,0 - služba 0 */ regs.h.al = mode; /* MOV AL,číslo módu ;07 = text, 04 = grafika*/ int86(0x10, ®s, ®s); /* INT 10h */ }
Jako první si ukážeme přístup přez BIOS, který je stejný jako na PC. BIOS se sám postará o 'okamžité' zobrazení bodu, je to však nejpomalejší způsob; vykreslení všech 1920ti bodů zabere asi 10s.
void putpix(Byte x, Byte y, Byte color) /* putpixel prez BIOS 0/1 */ { union REGS regs; regs.h.ah = 0x0c; /* MOV AH,0Ch - služba 0Ch */ regs.h.al = color; /* MOV AL,barva bodu ;0 = světlý, 1 = tmavý */ regs.h.bh = 0; /* MOV BH,číslo aktivní stránky */ regs.x.cx = x; /* MOV CX,Xová souřadníce bodu ;provádí se clipping */ regs.x.dx = y; /* MOV DX,Yová souřadníce bodu ;provádí se clipping */ int86(0x10, ®s, ®s); /* INT 10h */ } Byte getpix(Byte x, Byte y) /* get pixel color 0/1 */ { union REGS regs; regs.h.ah = 0x0d; /* MOV AH,0Dh - služba 0Dh */ regs.h.bh = 0; /* MOV BH,číslo aktivní stránky */ regs.x.cx = x; /* MOV CX,Xová souřadníce bodu */ regs.x.dx = y; /* MOV DX,Yová souřadníce bodu */ int86(0x10, ®s, ®s); /* INT 10h */ return(regs.h.al); }
Dále jsem pomocí této stručné dokumentace k řadiči LCD dipleje HD61830 naprogramoval vlastní funkci na kreslení bodu, je však jen asi 2x rychlejší. (Bod je zobrazován okamžitě)
#define XRES 240 #define YRES 64 #define VRAMSIZE 1920 #define MAXX (XRES-1) #define MAXY (YRES-1) #define MIDX (XRES/2) #define MIDY (YRES/2) void putpix(Byte x, Byte y, Byte color) /* putpixel prez porty 0/1 */ { register int offset=x+y*XRES; int bit=offset%8; register int alo=offset>>3,ahi=alo>>8; outportb(0x8011,10); outportb(0x8010,alo%256); // low Byte address outportb(0x8011,11); outportb(0x8010,ahi); // high Byte address if (color!=0) outportb(0x8011,15); // set bit else outportb(0x8011,14); // clear bit outportb(0x8010,bit); // bit num in Byte }
A jako poslení uvedu 'doublebuffer' metodu, kde však nealokuji žádný další buffer, ale používám přímo VRAM. Ta je totiž na portfoliu stejně rychlá jako ostatní RAM. Celé se nám to tedy posune tak, že uvažovaný framebuffer je nyní ve VRAM a skutečná VRAM je někde uvnitř řadiče LCD.
Po vykreslení scény je však ještě třeba obsah VRAM zkopírovat do vnitřní paměti LCD řadiče aby se frame objevil. To zajistí tato rutina bratří Hrdličkových (původně psaná v ASM) úžasnou rychlostí za desetinu vteřiny (10fps). Údajně existuje ještě 2x rychlejší rutina nějakého itala, ale není mi o tom nic známo.#define POFOVRAMSEG 0xB000 Byte far *vram=MK_FP(POFOVRAMSEG,0); /* pointer do VRAM */ void putpix(Byte x, Byte y, Byte color) /* putpixel do VRAM bufferu */ { register int offset=x+y*XRES; int bytenum=offset%8; int ofs=offset>>3; vram[ofs]=vram[ofs]|(0x80>>bytenum); } void cls(unsigned int pattern) /* smaze VRAM */ { asm { push es push cx push di mov cx,POFOVRAMSEG // segment VRAM mov es,cx mov cx,VRAMSIZE/2 // pojedeme 240x60/8/2 cyklu xor di,di // offset VRAM=0 mov ax,pattern // AX bude ukladano do VRAM rep stosw // opakuj AX->ES:[DI] dokud CX neni 0 pop di pop cx pop es } }
void refresh(void) /* zkopiruje VRAM na displej (do kontroleru HD) */ { asm { cld push ax push cx push dx push bx push si push di push ds mov si,0 mov ax,0b000h mov ds,ax mov di,64 } refresh_2: asm { mov cx,30 mov bx,si mov al,0ah mov dx,8011h cli out dx,al mov al,bl mov dx,8010h out dx,al sti mov al,0bh mov dx,8011h cli out dx,al mov dx,8010h mov al,bh and al,7 out dx,al sti } refresh_1: asm { lodsb ror al,1 mov ah,al and ah,136 ror al,1 ror al,1 mov bl,al and bl,68 or ah,bl ror al,1 ror al,1 mov bl,al and bl,34 or ah,bl ror al,1 ror al,1 and al,17 or al,ah mov ah,al inc dx mov al,0ch cli out dx,al mov al,ah mov dx,8010h out dx,al sti loop refresh_1 dec di jnz refresh_2 pop ds pop di pop si pop bx pop dx pop cx pop ax } }
Pokud tedy pro portfolio programuje více lidí, zajímaly by mě vaše připomínky a zkušenosti zejména se zvukem a grafikou.