2025-10-28
- 一生一芯
- 2025-10-28
- 42热度
- 0评论
键盘实现也很简单,代码如下
<span class="line"><span style="color: #af00db;">#define</span>
<span style="color: #0000ff;"> KEYDOWN_MASK </span>
<span style="color: #098658;">0x8000</span></span>
<span class="line"><span style="color: #0000ff;">void</span> <span style="color: #795e26;">__am_input_keybrd</span>
<span style="color: #000000;">(AM_INPUT_KEYBRD_T *</span>
<span style="color: #001080;">kbd</span>
<span style="color: #000000;">) {</span></span>
<span class="line"> <span style="color: #0000ff;">uint32_t</span>
<span style="color: #000000;"> KEY;</span></span>
<span class="line"><span style="color: #000000;"> KEY=</span>
<span style="color: #795e26;">inl</span>
<span style="color: #000000;">(KBD_ADDR);</span></span>
<span class="line"> <span style="color: #001080;">kbd</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">keydown</span>
<span style="color: #000000;"> = </span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">;</span></span>
<span class="line"> <span style="color: #001080;">kbd</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">keycode</span>
<span style="color: #000000;"> = AM_KEY_NONE;</span></span>
<span class="line"> <span style="color: #af00db;">if</span>
<span style="color: #000000;">(KEY&KEYDOWN_MASK){</span></span>
<span class="line"> <span style="color: #001080;">kbd</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">keydown</span>
<span style="color: #000000;"> = </span>
<span style="color: #098658;">1</span>
<span style="color: #000000;">;</span></span>
<span class="line"> <span style="color: #001080;">kbd</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">keycode</span>
<span style="color: #000000;"> = (</span>
<span style="color: #0000ff;">int</span>
<span style="color: #000000;">)(KEY&~KEYDOWN_MASK);</span></span>
<span class="line"><span style="color: #000000;"> }</span></span>
<span class="line"> <span style="color: #af00db;">else</span>
<span style="color: #000000;"> {</span></span>
<span class="line"> <span style="color: #001080;">kbd</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">keydown</span>
<span style="color: #000000;"> = </span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">;</span></span>
<span class="line"> <span style="color: #001080;">kbd</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">keycode</span>
<span style="color: #000000;"> = (</span>
<span style="color: #0000ff;">int</span>
<span style="color: #000000;">)(KEY&~KEYDOWN_MASK);</span></span>
<span class="line"><span style="color: #000000;"> }</span></span>
<span class="line"><span style="color: #000000;">}</span></span>
C需要注意从寄存器获取的值最高位代表是按下还是松开,需要去掉最高位再返回给应用程序,否则会产生数组越界。
VGA这部分做起来意外的顺利。屏幕大小寄存器就是vgactl_port_base[0]寄存器,宽度在高16位,高度在低16位,那么自然同步寄存器就是vga_port_base[1]了,按照nemu中vga的注释,同步寄存器这样使用
<span class="line"><span style="color: #0000ff;">void</span> <span style="color: #795e26;">vga_update_screen</span>
<span style="color: #000000;">(){</span></span>
<span class="line"> <span style="color: #af00db;">if</span>
<span style="color: #000000;">(</span>
<span style="color: #001080;">vgactl_port_base</span>
<span style="color: #000000;">[</span>
<span style="color: #098658;">1</span>
<span style="color: #000000;">]&</span>
<span style="color: #098658;">0x1</span>
<span style="color: #000000;">){</span></span>
<span class="line"> <span style="color: #795e26;">update_screen</span>
<span style="color: #000000;">();</span></span>
<span class="line"> <span style="color: #001080;">vgactl_port_base</span>
<span style="color: #000000;">[</span>
<span style="color: #098658;">1</span>
<span style="color: #000000;">]=</span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">;</span></span>
<span class="line"><span style="color: #000000;"> }</span></span>
<span class="line"><span style="color: #000000;">}</span></span>
C一开始我以为这个也要做成回调函数,但是看函数的声明不是这样的,只需要这样写就能实现屏幕的更新。另外现在AM_GPU_FBDRAW的功能也不对,现在的代码只向nemu发出更新命令,没有把图像送到显存,添加如下代码
<span class="line"><span style="color: #0000ff;">void</span> <span style="color: #795e26;">__am_gpu_fbdraw</span>
<span style="color: #000000;">(AM_GPU_FBDRAW_T *</span>
<span style="color: #001080;">ctl</span>
<span style="color: #000000;">) {</span></span>
<span class="line"> <span style="color: #0000ff;">uint32_t</span>
<span style="color: #000000;"> ctrl0;</span></span>
<span class="line"> <span style="color: #0000ff;">int</span>
<span style="color: #000000;"> w=</span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">;</span></span>
<span class="line"><span style="color: #000000;"> ctrl0 = </span>
<span style="color: #795e26;">inl</span>
<span style="color: #000000;">(VGACTL_ADDR);</span></span>
<span class="line"><span style="color: #000000;"> w = ctrl0>></span>
<span style="color: #098658;">16</span>
<span style="color: #000000;">;</span></span>
<span class="line"> <span style="color: #0000ff;">uint32_t</span>
<span style="color: #000000;"> *fb=(</span>
<span style="color: #0000ff;">uint32_t</span>
<span style="color: #000000;"> *)(</span>
<span style="color: #0000ff;">uintptr_t</span>
<span style="color: #000000;">)FB_ADDR;</span></span>
<span class="line"> <span style="color: #0000ff;">int32_t</span>
<span style="color: #000000;"> *pixel=(</span>
<span style="color: #0000ff;">int32_t</span>
<span style="color: #000000;">*)</span>
<span style="color: #001080;">ctl</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">pixels</span>
<span style="color: #000000;">;</span></span>
<span class="line"> <span style="color: #af00db;">for</span>
<span style="color: #000000;">(</span>
<span style="color: #0000ff;">int</span>
<span style="color: #000000;"> i=</span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">;i<</span>
<span style="color: #001080;">ctl</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">h</span>
<span style="color: #000000;">;i++){</span></span>
<span class="line"> <span style="color: #af00db;">for</span>
<span style="color: #000000;">(</span>
<span style="color: #0000ff;">int</span>
<span style="color: #000000;"> j=</span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">;j<</span>
<span style="color: #001080;">ctl</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">w</span>
<span style="color: #000000;">;j++){</span></span>
<span class="line"> <span style="color: #001080;">fb</span>
<span style="color: #000000;">[ </span>
<span style="color: #001080;">ctl</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">y</span>
<span style="color: #000000;">*w+</span>
<span style="color: #001080;">ctl</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">x</span>
<span style="color: #000000;"> + i*w+j]=*pixel;</span></span>
<span class="line"><span style="color: #000000;"> pixel++;</span></span>
<span class="line"><span style="color: #000000;"> }</span></span>
<span class="line"><span style="color: #000000;"> }</span></span>
<span class="line"> <span style="color: #af00db;">if</span>
<span style="color: #000000;"> (</span>
<span style="color: #001080;">ctl</span>
<span style="color: #000000;">-></span>
<span style="color: #001080;">sync</span>
<span style="color: #000000;">) {</span></span>
<span class="line"> <span style="color: #795e26;">outl</span>
<span style="color: #000000;">(SYNC_ADDR, </span>
<span style="color: #098658;">1</span>
<span style="color: #000000;">);</span></span>
<span class="line"><span style="color: #000000;"> }</span></span>
<span class="line"><span style="color: #000000;">}</span></span>
C这里我默认屏幕左上角是坐标原点,x轴向右延申,y轴向下延申,这个影响不大,就算不正确应该也只是导致图像反转。完成后gpu就可以显示图像了,去掉__am_gpu_init()中的测试代码,运行display test的结果如下

色块会旋转,这里是静态图片展示。估计是因为性能孱弱的原因,FPS只有1
实现VGA之后,再运行demo里的程序,例如兰顿蚂蚁,结果如下。其他的不再演示。

激动人心的时刻到了:用NEMU运行NES模拟器!上来就出错了,提示NES文件打开权限错误,检查了一下模拟器的代码,发现是这里报错的
<span class="line"><span style="color: #0000ff;">bool</span>
<span style="color: #000000;"> read = !</span>
<span style="color: #795e26;">strcmp</span>
<span style="color: #000000;">(mode,</span>
<span style="color: #a31515;">"rb"</span>
<span style="color: #000000;">);</span></span>
<span class="line"><span style="color: #0000ff;">bool</span>
<span style="color: #000000;"> write = !</span>
<span style="color: #795e26;">strcmp</span>
<span style="color: #000000;">(mode,</span>
<span style="color: #a31515;">"wb"</span>
<span style="color: #000000;">);</span></span>
<span class="line"><span style="color: #af00db;">if</span>
<span style="color: #000000;">((read&&write)||(!read&&!write)){</span></span>
<span class="line"> <span style="color: #795e26;">FCEU_PrintError</span>
<span style="color: #000000;">(</span>
<span style="color: #a31515;">"invalid file open mode specified (only wb and rb are supported)"</span>
<span style="color: #000000;">);</span></span>
<span class="line"><span style="color: #000000;"> ..</span></span>
Cprintf大法试了下,发现read和write竟然都是1,那么肯定是klib的strcmp实现错了,检查代码果然如此
<span class="line"><span style="color: #0000ff;">int</span> <span style="color: #795e26;">strcmp</span>
<span style="color: #000000;">(</span>
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span>
<span style="color: #000000;"> *</span>
<span style="color: #001080;">s1</span>
<span style="color: #000000;">, </span>
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span>
<span style="color: #000000;"> *</span>
<span style="color: #001080;">s2</span>
<span style="color: #000000;">) {</span></span>
<span class="line"> <span style="color: #0000ff;">int</span>
<span style="color: #000000;"> cmp=</span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">;</span></span>
<span class="line"> <span style="color: #0000ff;">int8_t</span>
<span style="color: #000000;"> s1_s2=</span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">;</span></span>
<span class="line"> <span style="color: #0000ff;">size_t</span>
<span style="color: #000000;"> cnt=</span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">;</span></span>
<span class="line"> <span style="color: #af00db;">while</span>
<span style="color: #000000;">(*(s1+cnt)!=</span>
<span style="color: #a31515;">'</span>
<span style="color: #ee0000;">\0</span>
<span style="color: #a31515;">'</span>
<span style="color: #000000;">&&*(s2+cnt)!=</span>
<span style="color: #a31515;">'</span>
<span style="color: #ee0000;">\0</span>
<span style="color: #a31515;">'</span>
<span style="color: #000000;">){</span></span>
<span class="line"><span style="color: #000000;"> s1_s2=*(s1+cnt)-*(s2+cnt);</span></span>
<span class="line"><span style="color: #000000;"> cmp = (s1_s2></span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">) ? </span>
<span style="color: #098658;">1</span>
<span style="color: #000000;"> : ( (s1_s2<</span>
<span style="color: #098658;">0</span>
<span style="color: #000000;">) ? -</span>
<span style="color: #098658;">1</span>
<span style="color: #000000;"> : </span>
<span style="color: #098658;">0</span>
<span style="color: #000000;"> );</span></span>
<span class="line"><span style="color: #000000;"> cnt++;</span></span>
<span class="line"><span style="color: #000000;"> }</span></span>
<span class="line"> <span style="color: #af00db;">return</span>
<span style="color: #000000;"> cmp;</span></span>
<span class="line"> <span style="color: #008000;">//panic("Not implemented");</span></span>
<span class="line"><span style="color: #000000;">}</span></span>
C其实是对strcmp的行为理解有误,在对比到第一个不相等的字符的时候比较就结束了才对,而不是一直比较到字符串结束,修改一下,模拟器启动!
加载ROM就用了十几秒,帧率有0帧或者1帧,一开始我以为自己实现错了,屏幕一直没动,过了一分钟终于出现了熟悉的界面:看来设备这块应该是通了

声卡后面我也打算实现一下,不过到这里已经够了,继续往下做吧,实现声卡对目前的我来说没有什么实际用处。
PA2到此结束
kernels里还有个幻灯片播放,运行起来的那一刻仿佛重装机兵通关的那一刻,悟了,舒爽