type str10 = string[10]; const hx:array[0..15] of char='0123456789ABCDEF'; Debug:boolean=false; {If set step through video tests one by one} Auto_test:boolean=false; {If set run tests automatically} {Keys:} Ch_Cr = $0D; Ch_Esc = $1B; Ch_F1 = $13B; Ch_F2 = $13C; Ch_F3 = $13D; Ch_F4 = $13E; Ch_F5 = $13F; Ch_F6 = $140; Ch_F7 = $141; Ch_F8 = $142; Ch_Home = $147; Ch_ArUp = $148; Ch_PgUp = $149; Ch_ArLeft = $14B; Ch_ArRight = $14D; Ch_End = $14F; Ch_ArDown = $150; Ch_PgDn = $151; Ch_Ins = $152; Ch_Del = $153; {Standard segment defines} Seg0000 = $0000; {Interupt table} Seg0040 = $0040; {BIOS data segment} SegA000 = $A000; {Graphics Video buffer} SegA800 = $A800; {Graphics Video buffer - upper half} SegB000 = $B000; {Mono Text mode buffer} SegB800 = $B800; {Color Text mode buffer} SegC000 = $C000; {BIOS ROM segment} {Gamma correction types} GAM_None = 0; {No Gamma correction} GAM_CanDo = 1; {} GAM_LeftJ = 2; {left justify Red&Blue 1bit each} GAM_Left8 = 4; {Left justify to 8bits} GAM_8bit = 8; {DAC Gamma registers are 8bit (not 6)} type CursorType=Array[0..31] of longint; {32 lines of 32 pixels} charr =array[1..255] of char; chptr =^charr; var rp:registers; video:string[20]; _crt:string[20]; secondary:string[20]; planes:word; {number of video planes} dacHWcursor:boolean; {True if we use the DAC cursor, rather than the VGA one} vseg:word; {Video buffer base segment} biosseg:word; curmode:word; {Current mode number} memmode:byte; {current memory mode} crtc:word; {I/O address of CRTC registers} pixels:word; {Pixels in a scanline in current mode} lins:word; {lines in current mode} bytes:longint; {bytes in a scanline} force_chip:byte; force_mm:word; {Forced memory size in Kbytes} force_version:word; {Forced chip version} clocktest:boolean; {Set false to disable clocktesting.} extpixfact:word; {The number of times each pixel is shown} extlinfact:word; {The number of times each scan line is shown} charwid :word; {Character width in pixels} charhigh :word; {Character height in scanlines} calcvseg:word; calcpixels, {Calculated displayed pixels per scanline} calclines, { " displayed scanlines} calchtot, { " total pixels/scanline} calcvtot, { " total lines/frame} calchblks, { " Hor. Blanking Start} calchblke, { " Hor Blanking End (see hblkmask)} calchrtrs, { " Hor Retrace Start} calchrtre, { " Hor Retrace End (see hrtrmask)} calcvblks, { " Vert Blanking Start} calcvblke, { " Vert Blanking End (see vblkmask)} calcvrtrs, { " Vert Retrace Start} calcvrtre, { " Vert Retrace End (see vrtrmask)} hblkmask, { " } hrtrmask, { " } vblkmask, { " } vrtrmask, { " } calcbytes:word; calcmmode:byte; vclk,hclk,fclk:longint; {Pixel (kHz), Line (Hz) & Frame (mHz) clocks} ilace:boolean; daccomm:word; {The result of the last dac2comm} BWlow,BWhigh:longint; {Bandwidth requirement - low & high in Kbytes/sec} (* Interface declarations for functions. In DEFVGA.PAS *) (* Utility & User interfrace functions*) procedure disable; {Disable interupts} procedure enable; {Enable interrupts} function gtstr(var cp:char):string; function getkey:word; {Waits for a key, and returns the keyID} function peekkey:word; {Checks for a key, and returns the keyID} procedure pushkey(k:word); {Simulates a keystroke} {Pretend the last key was pushed again} procedure repeatkey; function strip(s:string):string; {strip leading and trailing spaces} function upstr(s:string):string; {convert a string to upper case} function istr(w:longint):str10; {convert number to string} function dehex(s:string):longint; {Hex string to number} function hex2(w:word):str10; {convert number to 2digit hex string} function hex4(w:word):str10; {convert number to 4digit hex string} function hex8(w:longint):str10; {convert number to 4digit hex string} procedure swapbyte(var a,b:byte); {Swap the 2 bytes} function clipstr(var s:string):string; {Cuts & returns the first non-space substring from s} {BIOS & lowlevel I/O functions} procedure vio(ax:word); {INT 10h reg ax=AX. other reg. set from RP on return rp.ax=reg AX} procedure viop(ax,bx,cx,dx:word;p:pointer); {INT 10h reg AX-DX, ES:DI = p} function inp(reg:word):byte; {Reads a byte from I/O port REG} function inpw(reg:word):word; {Reads a word from I/O port REG} function inpl(reg:word):longint; {Reads a DWORD from I/O port REG} procedure outp(reg,val:word); {Write the low byte of VAL to I/O port REG} procedure outpw(reg,val:word); {Write the word byte of VAL to I/O port REG} procedure outpl(reg:word;val:longint); {Write the word byte of VAL to I/O port REG} {Outputs a 32bit value as a single OUT DX,EAX - requires 386 or better} procedure outplong(reg:word;val:longint); {Inputs a 32bit value as a single IN EAX,DX - requires 386 or better} function inplong(reg:word):longint; function rdinx(pt,inx:word):word; {read register PT index INX} procedure wrinx(pt,inx,val:word); {write VAL to register PT index INX} procedure wrinx2(pt,inx,val:word); {write VAL to register PT index INX} procedure wrinx2m(pt,inx,val:word); {write VAL to register PT index INX} procedure wrinx3(pt,inx:word;val:longint); {write VAL to register PT index INX} procedure wrinx3m(pt,inx:word;val:longint); {write VAL to register PT index INX} procedure modinx(pt,inx,mask,nwv:word); {In register PT index INX sets the bits in MASK as in NWV the other are left unchanged} procedure setinx(pt,inx,val:word); procedure clrinx(pt,inx,val:word); procedure modreg(reg,mask,nwv:word); {In register PT index INX sets the bits in MASK as in NWV the other are left unchanged} procedure setreg(reg,val:word); procedure clrreg(reg,val:word); procedure modregw(reg,mask,nwv:word); {In register PT index INX sets the bits in MASK as in NWV the other are left unchanged} procedure setregw(reg,val:word); procedure clrregw(reg,val:word); {Lowlevel DAC stuff} function trigdac:word; {Reads $3C6 4 times} procedure setDACstd; procedure setdac8(on:boolean); function setdac15:boolean; function setdac16:boolean; function setdac24:boolean; function setdac32:boolean; function setDACgamma(on:boolean):word; function setDACpage(index:word):word; procedure clearDACpage; function rdDACreg(index:word):word; procedure wrDACreg(index,val:word); procedure clrDACreg(index,val:word); procedure setDACreg(index,val:word); procedure modDACreg(index,msk,val:word); function getdaccomm:word; procedure dac2comm; procedure dac2pel; {Probe clocks, should really be in IDVGA ??} procedure findclocks; {The LOG functions writes output data to both the screen and the file WHATVGA.TXT, to provide a log in case of lockup} procedure openlog(scr:boolean); procedure wrlog(s:string); procedure closelog; (* HW cursor, BitBLT, linedraw and clock function in BITBLT.PAS *) procedure setHWcurmap(VAR map:CursorType); procedure HWcuronoff(on:boolean); procedure setHWcurpos(X,Y:word); procedure setHWcurcol(fgcol,bkcol:longint); procedure setZoomWindow(Xs,Ys,Xe,Ye:word); procedure setZoomAdr(AdrX,AdrY:word); procedure ZoomOnOff(On:boolean); procedure setZoomFactor(Fx,Fy:word); procedure vesamodeinfo(md:word;var vbedata); procedure fillrect(xst,yst,dx,dy:word;col:longint); procedure copyrect(srcX,srcY,dstX,dstY,dx,dy:word); procedure line(x0,y0,x1,y1:integer;col:longint); procedure setclk(Nbr,divi:word); function getclk(var divisor,divid:word):word; function getClockFreq:longint; {Effective pixel clock in kHz} (* Bank, mode and Vstart rutines, in SUPERVGA.PAS *) procedure setbank(bank:word); procedure setRbank(bank:word); procedure setvstart(x,y:word); {Set the display start to (x,y)} function setmode(md:word;clear:boolean):boolean; procedure SetTextMode; procedure SetRGBPal(inx,r,g,b:word); procedure SelectVideo(Item:word); function rgb(r,g,b:word):longint; {Converts RGB values to pixel in the current pixelformat } {Returns the pixel BIT address} function pixeladdress(x,y:word):longint; implementation uses idvga; var clocktbl:array[0..31] of longint; procedure disable; (* Disable interupts *) begin inline($fa); (* CLI instruction *) end; procedure enable; (* Enable interrupts *) begin inline($fb); (* STI instruction *) end; function gtstr(var cp:char):string; var x:word; s:string; str:chptr; begin str:=chptr(@cp); s:='';x:=1; if str<>NIL then while (x<255) and (str^[x]<>#0) do begin if str^[x]<>#7 then s:=s+str^[x]; inc(x); end; gtstr:=s; end; const key_stack:word=0; {Stored key stroke 0=none} lastkey:word=0; function getkey:word; var c:char; begin if key_stack<>0 then begin lastkey:=key_stack; key_stack:=0; end else begin c:=readkey; if c=#0 then lastkey:=$100+ord(readkey) else lastkey:=ord(c); end; getkey:=lastkey; end; function peekkey:word; begin if (key_stack=0) and not keypressed then peekkey:=0 else peekkey:=getkey; end; procedure pushkey(k:word); {Simulates a key stroke} var ch:char; begin key_stack:=k; while keypressed do ch:=readkey; end; {Pretend the last key was pushed again} procedure repeatkey; begin pushkey(lastkey); end; {Swap the 2 bytes} procedure swapbyte(var a,b:byte); var c:byte; begin c:=a; a:=b; b:=c; end; function strip(s:string):string; {strip leading and trailing spaces} begin while s[length(s)]=' ' do dec(s[0]); while copy(s,1,1)=' ' do delete(s,1,1); strip:=s; end; function upstr(s:string):string; {convert a string to upper case} var x:word; begin for x:=1 to length(s) do s[x]:=upcase(s[x]); upstr:=s; end; function istr(w:longint):str10; var s:str10; begin str(w,s); istr:=s; end; function hex2(w:word):str10; begin hex2:=hx[(w shr 4) and 15]+hx[w and 15]; end; function hex4(w:word):str10; begin hex4:=hex2(hi(w))+hex2(lo(w)); end; function hex8(w:longint):str10; begin hex8:=hex4(w shr 16)+hex4(w); end; function dehex(s:string):longint; var x:word; l:longint; c:char; begin l:=0; for x:=1 to length(s) do begin c:=s[x]; case c of '0'..'9':l:=(l shl 4)+(ord(c) and 15); 'a'..'f','A'..'F': l:=(l shl 4)+(ord(c) and 15 +9); end; end; dehex:=l; end; function clipstr(var s:string):string; {Cuts & returns the first non-space substring from s} var i:integer; begin i:=0; while s[i+1]=' ' do inc(i); delete(s,1,i); i:=0; while (i' ') do inc(i); clipstr:=copy(s,1,i); delete(s,1,i); end; procedure vio(ax:word); {INT 10h reg ax=AX. other reg. set from RP on return rp.ax=reg AX} begin rp.ax:=ax; intr($10,rp); end; procedure viop(ax,bx,cx,dx:word;p:pointer); begin {INT 10h reg AX-DX, ES:DI = p} rp.ax:=ax; rp.bx:=bx; rp.cx:=cx; rp.dx:=dx; rp.di:=ofs(p^); rp.es:=seg(p^); intr($10,rp); end; function inp(reg:word):byte; {Reads a byte from I/O port REG} begin reg:=port[reg]; inp:=reg; end; function inpw(reg:word):word; {Reads a word from I/O port REG} begin reg:=portw[reg]; inpw:=reg; end; function inpl(reg:word):longint; {Reads a word from I/O port REG} var l:longint; begin l:=portw[reg]; inpl:=l+(longint(portw[reg+2]) shl 16); end; {Inputs a 32bit value as a single IN EAX,DX - requires 386 or better} function inplong(reg:word):longint; var l:longint; begin inline($8B/$56/=CPQ_QV then md:=$32 else md:=$12; } __AGX:begin md:=$64; setstdmode:=44900; end; else md:=$12; end; if setmode(md,false) then; end; function Vretrace:boolean; begin case cv.chip of __Mach64:VRetrace:=memw[cv.Xseg:$12]>=memw[cv.Xseg:$0A]; __Mach32:VRetrace:=inpw($CEEE)>=inpw($CAEE); {Hm!!} __AGX:if (inp(cv.IOadr+5) and 1)>0 then begin outp(cv.IOadr+5,1); {Reset blanking flag} VRetrace:=true; end else Vretrace:=false; else VRetrace:=(inp(crtc+6) and 8)>0; {3D4h/3B4h} end; end; function getticks:longint; var cnt,stp:longint; stat,x:word; begin stat:=crtc+6; disable; stp:=200000; cnt:=0; while not VRetrace and (stp>0) do dec(stp); while VRetrace and (stp>0) do dec(stp); while not VRetrace and (stp>0) do dec(stp); if stp>0 then for x:=1 to 5 do begin while VRetrace and (cnt<1000000) do inc(cnt); while not VRetrace and (cnt<1000000) do inc(cnt); end; enable; getticks:=cnt; end; procedure progICD2061reg(clk:longint); const ser_clk=4; ser_dta=8; var old,dta,bit:word; procedure setbits(bits:word); begin outp($3C2,bits); for bits:=1 to 5 do; {delay} end; begin if cv.chip=__S3 then {Needs to enable the ICD for the STB Pegasus...} begin outpw(crtc,$4838); outpw(crtc,$A539); {Enable S3 Ext} modinx(crtc,$42,$F,3); end; old:=inp($3CC); outpw(SEQ,$100); dta:=(old and $F3)+ser_dta; for bit:=1 to 6 do begin setbits(dta+ser_clk); setbits(dta); end; dta:=dta and $F3; setbits(dta); setbits(dta+ser_clk); setbits(dta); setbits(dta+ser_clk); for bit:=1 to 24 do begin dta:=dta and $F3; if (clk and 1)=0 then dta:=dta+ser_dta; setbits(dta+ser_clk); setbits(dta); dta:=dta xor ser_dta; setbits(dta); setbits(dta+ser_clk); clk:=clk shr 1; end; dta:=dta or ser_dta; setbits(dta+ser_clk); setbits(dta); setbits(dta+ser_clk); setbits(dta); outp($3C2,old); if cv.chip=__S3 then begin modinx(crtc,$5C,3,2); outpw(crtc,$5A39); {Disable S3 Ext} outpw(crtc,$38); end; outpw(SEQ,$300); delay(15); end; const clkperm:integer=0; function ClockPermission:boolean; begin if clkperm=0 then begin settextmode; writeln('WHATVGA is about to test the clock chip or crystals on your'); writeln('board. This can cause strange behavior on the display.'); writeln('If your monitor is fixed-frequency (MDA, CGA, EGA or original'); writeln('VGA, in fact anything that can''t handle at least 800x600) this'); writeln('could in extreme situations potentionally hurt your monitor.'); writeln('Press Y to continue clock testing, any other key to skip it:'); if (getkey and $DF)=ord('Y') then clkperm:=1 else clkperm:=2; end; ClockPermission:=clkperm=1; end; procedure findclocks; var clks,x,y,divi,divid:word; basefreq,baselevel,l,l0,l1:longint; progcheck:boolean; {Should we check for programmable clocks??} begin if (inp($3CC) and 1)>0 then crtc:=$3D4 else crtc:=$3B4; progcheck:=true; clks:=4; case cv.clktype of clk_ext3:clks:=8; clk_ext4:clks:=16; clk_ext5:clks:=32; clk_ext6:clks:=64; clk_sdac:progcheck:=false; clk_TVP302x:begin progcheck:=false; clks:=0; end; end; if (clks>0) and ClockPermission then begin memmode:=_PL4; basefreq:=SetStdMode; {Usually mode 12h, but...} y:=getclk(divi,divid); baselevel:=getticks; if baselevel>0 then for x:=0 to clks-1 do begin if (x=8) and (cv.chip=__compaq) and (cv.version>=CPQ_QV) then vio($32); {Hack to get at last 8 clock of QVision} setclk(x,divid); delay(50); {Let clock settle} l:=getticks; if l>0 then cv.clks[x]:=((basefreq*baselevel) div l)*(divi div 12); end; setclk(y,divid); end; if progcheck and ClockPermission then begin outp($3C2,(inp($3CC) and $F3) or $8); {Clk 2} delay(150); progICD2061reg($C00000); progICD2061reg($41A83C); {14.318MHz* 2 * 109/62 = 50.35 MHz} l0:=getticks; progICD2061reg($41A8BC); {14.318MHz* 2/2 * 109/62 = 25.175 MHz} l1:=getticks; if (l0<>0) and (abs(l1-l0*2)<25) then begin {Found an ICD2061} cv.clktype:=clk_ICD2061; progICD2061reg($C04000); {Set prescale bit to *4} progICD2061reg($59A8BC); {14.318MHz* 4/2 * 109/62 = 50.35 MHz} l:=getticks; if abs(l1-l*2)<25 then {Prescale bit exists = ICD2061A} cv.clktype:=clk_ICD2061A; progICD2061reg($C00000); {Restore ?} end; setclk(y,divid); end; end; procedure SelectVideo(item:word); begin cv:=vid[item]; loadmodes; video:=header[cv.chip]; settextmode; end; procedure dac2pel; {Force DAC back to PEL mode} begin if inp($3c8)=0 then; end; function trigdac:word; {Reads $3C6 4 times} var x:word; begin x:=inp($3c6); x:=inp($3c6); x:=inp($3c6); if (cv.dactype=_dacMU1880) then x:=inp($3C6); trigdac:=inp($3c6); end; procedure dac2comm; {Enter command mode of HiColor DACs} begin dac2pel; daccomm:=trigdac; end; function getdaccomm:word; begin {if cv.DAC_RS2<>0 then getdaccomm:=inp($3C6+cv.DAC_RS2) else} begin dac2comm; getdaccomm:=inp($3C6); dac2pel; end; end; const SavedDACpage:word=0; {DAC page state saved by SaveDACpage, reset by clearDACpage} procedure SaveDACpage; begin SavedDACpage:=0; {default} if (cv.flags and FLG_ExtDAC)>0 then {RS2/3 addressing ?} case cv.chip of __S3:begin outpw(crtc,$4838); {Unlock S3 regs} outpw(crtc,$A539); SavedDACpage:=(rdinx(crtc,$43) and 2) shl 1; if (cv.version>S3_924) and (SavedDACpage=0) then SavedDACpage:=(rdinx(crtc,$55) and 3) shl 2; if (rdinx(crtc,$5C) and $20)>0 then inc(SavedDACpage,16); outpw(crtc,$5A39); outpw(crtc,$38); {Lock S3 regs} end; end; end; const DACpage:boolean=false; {Set if DAC registers enabled (MGA,Weitek..)} {Returns the address of the DAC register selected by index (0..3 for standard DACs, 0..7 or 0..15 for advanced DACs), and sets any necessary flags. } function setDACpage(index:word):word; const DACadr:array[0..3] of word=($3C8,$3C9,$3C6,$3C7); M32DACadr:array[0..3] of word=($2EC,$2ED,$2EA,$2EB); var ret,x:word; found:boolean; begin found:=true; ret:=DACadr[index and 3]; if cv.chip=__AGX then outp(cv.IOadr,1); {Enable VGA regs} if (cv.flags and FLG_ExtDAC)>0 then {RS2/3 addressing ?} case cv.chip of __AGX:begin if index>7 then ret:=cv.spcreg+(index and 3); if (index and 4)>0 then outp(cv.IOadr+10,$51) else outp(cv.IOadr+10,0); end; __ATI:if cv.Version0 then inc(ret,$8000); if (index and 8)>0 then inc(ret,$1000); end; __Mach32:begin { modinx(cv.IOadr,$A0,$60,index shl 3);} x:=inp($8EEF) and $CF; outp($7AEF,x+((index and $C) shl 2)); ret:=ret-$DC; {3C8 -> 2EC} end; __Mach64:begin outp($62EC,index shr 2); ret:=$5EEC+(index and 3); end; __MGA:begin if (not DACpage) and (cv.PCIid>0) then begin wPCIlong($10,$AC000); {Map ACC regs at AC000h} cv.Xseg:=$AC00; DACpage:=true; end; ret:=0; end; __NCR:ret:=ret+((index and 4) shl 13); {A15 = $8000} __S3:begin outpw(crtc,$4838); {Unlock S3 regs} outpw(crtc,$A539); if cv.version>S3_924 then begin clrinx(crtc,$43,2); {Just in case} modinx(crtc,$55,3,index shr 2); modinx(crtc,$5C,$20,index shl 1); {TVP3025 control} end else modinx(crtc,$43,2,index shr 1); outpw(crtc,$5A39); outpw(crtc,$38); {Lock S3 regs} end; __Tseng:begin outp($3BF,3); outp(crtc+4,$A0); modinx(crtc,$31,$40,index shl 4); {Chrontel DAC} end; {Diamond Viper w/ OAK } __OAK:ret:=ret+(index and $C) shl 12; __Weitek,__WeitekP9: if (cv.version0 then x:=setDACpage(SavedDACpage) else begin if (cv.flags and FLG_ExtDAC)>0 then {RS2/3 addressing ?} case cv.chip of __AGX:outp(cv.IOadr+10,0); __ATI:clrinx(cv.IOadr,$A0,$60); __Mach64:outp($62EC,0); __MGA:if DACpage then wPCIlong($10,PCIrec[cv.PCIid].l[4]); __S3:begin outpw(crtc,$4838); {Unlock S3 regs} outpw(crtc,$A539); if cv.version>S3_924 then clrinx(crtc,$55,3); clrinx(crtc,$43,2); outpw(crtc,$5A39); outpw(crtc,$38); {Lock S3 regs} end; __Tseng:begin outp($3BF,3); outp(crtc+4,$A0); clrinx(crtc,$31,$40); {Chrontel DAC} end; __Weitek,__WeitekP9: if DACpage then begin outp($9100,$41); x:=inp($9104); outp($9100,$41); outp($9104,x and $F3); {Disable Acc regs at A000h} end; else dac2pel; end else dac2pel; end; DACpage:=false; end; function rdDACreg(index:word):word; var inx:word; begin inx:=SetDACpage(index); if inx=0 then case cv.chip of __MGA:rdDACreg:=mem[cv.Xseg:$3C00+index*4]; __Weitek,__WeitekP9: begin if mem[SegA000:$198]=0 then; {Wait ?} rdDACreg:=mem[SegA000:$200+4*index]; end; end else rdDACreg:=inp(inx); end; procedure wrDACreg(index,val:word); var inx:word; begin inx:=SetDACpage(index); if inx=0 then case cv.chip of __MGA:mem[cv.Xseg:$3C00+index*4]:=val; __Weitek,__WeitekP9: mem[SegA000:$200+4*index]:=val; end else outp(inx,val); end; procedure clrDACreg(index,val:word); begin wrDACreg(index,rdDACreg(index) and not val); end; procedure setDACreg(index,val:word); begin wrDACreg(index,rdDACreg(index) or val); end; procedure modDACreg(index,msk,val:word); begin wrDACreg(index,(rdDACreg(index) and not msk) or (msk and val)); end; function rgb(r,g,b:word):longint; begin r:=lo(r);g:=lo(g);b:=lo(b); case memmode of _PL1,_PL1E,_CGA1: rgb:=r and 1; _PL2,_CGA2: rgb:=r and 3; _PL4,_PK4:rgb:=r and 15; _P8:rgb:=r; _P15:rgb:=((r shr 3) shl 5+(g shr 3)) shl 5+(b shr 3); _P16:rgb:=((r shr 3) shl 6+(g shr 2)) shl 5+(b shr 3); _P24,_P32:rgb:=(longint(r) shl 8+g) shl 8 +b; _P24b,_P32b:rgb:=(longint(b) shl 8+g) shl 8 +r; _p32c:rgb:=((longint(r) shl 8+g) shl 8 +b) shl 8; _P32d:rgb:=((longint(b) shl 8+g) shl 8 +r) shl 8; end; end; {Writes a 32bit value to a DWORD at offset ADR in Xseg} procedure write32(adr:word;val:longint); var w:word; begin w:=cv.Xseg; {mov es,[cv.Xseg] mov di,[BP+adr] mov eax,[BP+val] mov es:[di],eax} inline($8E/$46/