2011年8月26日星期五

让Windows 1.0x支持VGA分辨率

Windows 1.0x出来的时候,VGA并未定标准,因此M$并没有支持它。20多年后看来,EGA分辨率的Windows 1.0x是非常难看的(虽然这个分辨率在现代16:10或者16:9正方形像素显示的宽屏上的显示效果比4:3非正方形像素的EGA更正确)。怎样让它支持VGA呢?答案有两种。一是修改Windows 2.x的VGA驱动程序;二是将Windows 1.0x的EGA驱动程序改为支持VGA模式。
第一种方法我也试过,但是似乎有难以解决的逻辑问题存在(位图字体无法显示)。因此试试第二种。
首先最好取一个可以不安装而更改驱动程序的Windows 1.0x版本作为调试用。我使用了Windows 1.03 SDK中的debug版Windows 1.03内核,制作了一个这样的版本。

首先解决显示模式问题。DOS下切换显示模式有很多方法,最可靠和常用的方法是使用BIOS INT 10H中断的00号功能。这个调用相当简单,AH寄存器存显示模式,AL寄存器存00H,INT 10H即可。用反汇编器找到EGAHIRES.DRV(在“慢启动”免安装版本中的DISPLAY.DRV)中的此调用。

根据INT 10H文档(http://webpages.charter.net/danrollins/techhelp/0114.HTM),10H模式为EGA 640x350 16色。改为12H(VGA 640x480 16色)。后面那个CMP AL,10H也要改成12H,否则启动时切换显示模式会判定为切换失败而死机。
可以看到系统已进入VGA模式,但仍只能显示上半350像素,显示区域下方还有几行内容随鼠标移动而改变的花屏像素。仍需要修改。

驱动程序如何对GDI确定其显示模式呢?查找了一下Win3.1 DDK,发现了这个GDIINFO结构体,还有一个PBITMAP(硬件位图)结构体。

typedef struct tagGDIINFO {
   short int dpVersion;
   short int dpTechnology;
   short int dpHorzSize;
   short int dpVertSize;
   short int dpHorzRes;
   short int dpVertRes;
   short int dpBitsPixel;
   short int dpPlanes;
   short int dpNumBrushes;
   short int dpNumPens;
   short int futureuse;
   short int dpNumFonts;
   short int dpNumColors;
   unsigned short int dpDEVICEsize;
   unsigned short int dpCurves;
   unsigned short int dpLines;
   unsigned short int dpPolygonals
   unsigned short int dpText;
   unsigned short int dpClip;
   unsigned short int dpRaster;
   short int dpAspectX;
   short int dpAspectY;
   short int dpAspectXY;
   short int dpStyleLen;
   POINT dpMLoWin;
   POINT dpMLoVpt;
   POINT dpMHiWin;
   POINT dpMHiVpt;
   POINT dpELoWin;
   POINT dpELoVpt;
   POINT dpEHiWin;
   POINT dpEHiVpt;
   POINT dpTwpWin;
   POINT dpTwpVpt;
   short int dpLogPixelsX;
   short int dpLogPixelsY;
   short int dpDCManage;
   short int dpCaps1;
   long int dpSpotSizeX;
   long int dpSpotSizeY;
   short int dpPalColors;
   short int dpPalReserved;
   short int dpPalResolution;
} GDIINFO;

typedef struct tagPBITMAP {
   short bmType;
   short bmWidth;
   short bmHeight;
   short bmWidthBytes;
   BYTE bmPlanes;
   BYTE bmBitsPixel;
   long bmBits;
   long bmWidthPlanes;
   long bmlpPDevice;
   short bmSegmentIndex;
   short bmScanSegment;
   short bmFillBytes;
   short reserved1;
   short reserved2;
} PBITMAP;

两个结构体都包含了长、宽信息,2个连续的字长。对于640x350,最后按x86字节序拼成16进制应该是80 02 5E 01。
虽然Win3.1的显示模型和1.x相比已经有了巨大的变动,根据GDIINFO和PBITMAP结构在display.drv中都找不到完全对应的,但是80 02 5E 01却找到了2处。
干脆把这两处都改成80 02 E0 01,也就是640x480。
这下好,显示能扩展到屏幕底部了。但是,鼠标移动到350行以下时仍然不显示,而且350线下方仍然有一些花版的区域。

看来这有涉及逻辑的地方,我退缩了。
后来经过洋大人John Elliott的指点,才知道里面还有几处硬编码了屏幕高度和屏幕缓冲区大小,屏幕的乱区就是因为驱动用显存缓冲区后面的冗余空间做鼠标覆盖缓冲了。
这个减去屏幕高度应该是鼠标移动到350下面就不显示了。

把5E 01直接改为E0 01,鼠标就可以显示到350行以下去了。但是花屏区域仍然存在。
至于这个屏幕缓冲区大小,本来一开始也想到了的,但是思维被框死在按字节计算这个值(640x350x4位=112000字节)上了,结果居然是单个位面的大小28000字节(60 6D)。应该是EGA16色存储的4个位面是分开而不是交叉的,因此一次可以完整写一个位面。在这里找到这个:

没看明白这个call的用途,不过nop掉这个call,开菜单时鼠标轨迹就不擦掉了。看来果然这个和鼠标有关。改掉为38400字节( 00 96 )。
这样下来开菜单时鼠标箭头的背景就变成黑色了,估计是擦鼠标箭头读了更新后那一块地址上应有的内容作为鼠标箭头背景,而其实原有屏幕内容却还保存在原位置(就是那些花版的地方)。

继续查这个28000,还发现2处一样的指令。


但三处都修改后,花版仍然存在。几乎又要放弃的时候,偶然往后跟踪那三个鼠标相关的调用,都在其中某个子程序发现了一个奇怪的常数28160(00 6E)。

我没搞懂这的逻辑(汇编不好+没有Win1 DDK+Win3.1 DDK的实例显卡驱动没认真看过),但是直觉上认为这个28160就是28000加上两行(一行640位=80字节)。改成38400+2x640/8=38560(A0 96)呢?果断试试看。三处全部改完,好,奇迹发生,乱屏部分终于消失了。

总结一下,把EGAHIRES.DRV进行修改,先把INT 10H部分改掉(B8 10 00 CD 10改成B8 12 00 CD 10,下面的3C 10 B8 00 00 改成3C 12 B8 00 00),那一处硬编码改掉(81 E9 5E 01改成81 E9 E0 01),把所有的BF 60 6D替换为BF 00 96,81 C0 00 6E替换为81 C0 A0 96就能让这个EGA驱动变成VGA的了。
把安装盘里面的这个EGAHIRES.DRV如法炮制,就能制作出可以支持VGA的标准安装版Windows 1.0x。
直接修改装好的1.01的WIN100.BIN,也可以实现。而且这个改法对于Windows 1.01-1.04都有效。

虽然还没搞懂最后那几个关于鼠标描绘部分修改的逻辑,但是至少我们又研究出了微软都没有几个人还记得的东西。
下次有时间把Windows 2.1/286的VGA驱动改到Win1.03使用的方法也写一下(不完善,位图字体不能用)。
这里是改过的Windows 1.03 EGAHIRES.drv和改过的已安装版Windows 1.01(支持PS/2鼠标)的下载。
http://www.mediafire.com/?fw1c10q7bxgyw
感谢 kalakala(尝试Hack Windows 2.0x 驱动的高人),John Elliott的支持和指教。
John Elliott还实现了SVGA 800x600x16色,写了自动补丁程序。请看:
http://www.seasip.info/Misc/win1.html

2011年8月19日星期五

有关山寨PSP

山寨PSP自从丁果A320开始草创,除开之前未流行起来的杂七杂八的之外,到今天已经发展了两代。
最初是06,07年左右,一些MP4方案商开始开发MP4用的FC模拟器,然后很快就初步取得成功。但是当时FC模拟不过是那些MP4的一些附加功能,而且那些MP4的按键布局仍然是传统布局,因此并不能称为山寨PSP。
2008年中,这个形势有了很大的改变。炬力、凌阳的MP4方案都加入了模拟器的支持,如果我没记错,瑞芯微和君正也有。当MP4方案固件支持的游戏模拟器多到一定程度(FC,GB,SFC,MD和GBA)时,强大的东西出现了。
2009年初,一种叫做丁果A320的机器横空出世。它外观神似没有上屏的NDSL,2.8 QVGA屏,似乎使用了某个非常强大的方案,官方固件能模拟FC,GB/GBC,GBA,SFC,MD,NEOGEO之类。这机器很快就流传到了洋大人手里,在被逆向后,进行自制开发者不少,甚至有洋大人为它自制了一个Linux系统Dingux。由于该机的可研究性,出来时这机器500多块,至今全新机还要400块左右。这样的机器仍然不便宜,而且那个强大的方案商(只知道主控是君正4732)似乎并没有把这个作为主打产品后续开发。不过就过了几个月,真正的山寨PSP就出现了。
金星JXD,一个MP4市场上有点名气的公司;申江科技,一个貌似也出现了几年的MP4类方案商;凌阳,一个已经在低端播放器市场摸爬滚打了多年的DSP公司。三者一拍即合,SPMP8000芯片,申江的固件,金星JXD的设计和对固件的修改(JXD和申江的关系很特别,能做到很多申江公版没有的东西),打造了第一代PSP外形MP4。这种机器,以ARM9为核心,貌似是eCOS为OS,以以往做的FC,GB模拟器和抄开源代码的GBA(具体是gPSP),SFC之类模拟器为其支持游戏功能的关键部分。首先是金星JXD的JXD1000,然后是申江板、申江方案的一批山寨机器。这些机器就是山寨PSP的第一代。
第一代机器基本性能和视频音频播放性能都还过得去,但问题是对H264视频支持一般(早期版固件不支持),另外固件中的模拟器除了金星后期对GBA模拟器进行了一些更新外,从未改动过。导致该机模拟只有FC和GB效果较好(但也不支持多数FC的扩展卡带),SFC模拟器拖慢严重,不支持任何扩展卡带;GBA模拟器除了金星后期版外,全都有时快时慢的问题,而且很多大量使用mode5的游戏无法运行。当然在市场上来看,JXD1000以300多块/4G,山寨公版以200块出头/4G来算,在山寨市场如华强北,和淘宝上销路都不错。至今在淘宝搜索PSP,仍然能看到一大批此类一代山寨PSP。
一年过去了。申江/凌阳方案的山寨PSP一代几乎占领了市场,金星甚至把触摸屏都开发到这方案上了。但是,机器模拟的问题始终没有真正解决。此时,炬力开始给力了。虽然之前凌阳方案威武的时候,炬力早有支持FC/GB/GBC/SFC的方案,但是问题远远比凌阳多,也不支持480x272的PSP标准4.3屏。而到了2010年中,炬力升级了其方案(所谓G100,ATJ22xx系的改进版),这次CPU达到500MHz,支持H264,以比当年君正方案A320低得多的价格实现了16位街机甚至PS游戏的模拟。申江等几家公司很快就跟进推出了板子,金星最早出成品JXD3000,然后到4,5月左右开始出现公版的。这就是所谓的二代山寨PSP。
二代山寨PSP在性能上有巨大的提高,但是价格也至少提高了几十块钱。目前模拟PS,纯2D的东西还可以,但是问题太多太多(比如DQ7引擎完全不行,3D不仅慢,而且对于很多游戏会出现贴图错误)。街机,能跑的都还可以,但是有frameskip,声音奇差。倒是此方案模拟GBA已经能够满帧了,这不错。
至于瑞芯微,已经全面转向Android低端方案;君正虽然继续出,丁果跟进出了一些后续机,但都曲高和寡。
其实,在一线市场非智能设备的前途越来越窄了。山寨PSP这种怪物,面对的主要是国内二三线市场,以及向第三世界国家出口。山寨货有个特点,功能不求精而求多,重噱头不重细节。山寨PSP在很多地方也体现了这个特点。最首要的是其游戏模拟器功能,不出新方案绝对不改进,完全在浪费CPU的性能;然后就是游戏手感方面,看起来此类固件的编写和测试者只要游戏可以运行就通过了,完全没有考虑过真正的掌机开发要在人体工程学上下多少功夫,所谓手感对游戏性的影响等方面,导致很多人因为手感而放弃山寨PSP。另外,这些固件设计者只是一味山寨界面,殊不知UI设计对使用者感受的影响。现在PSP的正统后继者PSVita也马上要出了,掌机从GB进化到GBA,进化出NDS和PSP,到现在的3DS和PSVita,如今已经跨入了第四代。在小鬼子们的掌机在和洋大人老乔的iOS设备作生死搏斗的时候,深圳制造和东莞制造的山寨们在出这种四不像的MP4来抢占穷人的市场。可以说,除了成熟稳定的MTK山寨机之外,山寨PSP,山寨本之类的东西,山寨PSP便宜就有人要,山寨本成本和性能永远无法和二手本相比,现在接近死亡也是必然的了,成本已成了此类山寨货的唯一优势。这是一个玩了3台山寨机,2台山寨本,5个山寨PSP和MP4的人的感受。