盗賊の極意

Feed Rss

滚屏实现完了,效果如下:

 

期间修复了一处笔误,当时是把0xff3f算错了,写成了0xffcf, 两个0bit歪了两位,害我排查了老半天,惨。

不过这种bug很难避免,只能继续提高debug能力了。幸好我有正确答案可以参考,修起来比较容易。

有这么优越的条件,这次一定要坚持做完这个项目呀!

后面还差手柄控制和音频输出和执行效率优化了。加油!

nes

如图,我正在开发nes模拟器,也就是2018年5月份这篇日志里说的黄卡游戏机的模拟器。
本来想等到做完之后再突然贴出来的,不过这个个人项目实在是拖的太久了,其中有很多感想我担心到最后写日志的时候已经忘记了,所以还是以定期总结记录的形式发出来吧。

关于nes模拟器的开发难度。
开发nes模拟器对技术深度没有要求,可以说会写程序,会显示图像,会播放声音的人就能开发。只是模拟器的细节比较多,所以做起来耗时比较长,有点累而已。和自己设计一个应用程序呀游戏什么的比起来,制作模拟器的好处在于完全省略了设计部分,所有的设计细节都是现成的,你只需要充分施展你的工程能力即可。对于我这种没有产品能力没有营销能力唯独喜欢搞工程的人来说是最适合的业余项目了。说起来自己重新实现一些rfc协议也有类似的乐趣,还是我那个提了一万回的自制http服务器项目,就是重新实现rfc2616,也是乐趣无穷。

再介绍一下我现在的进度。
如图所示,我现在做到了正确显示游戏内的背景图像了。具体来说就是实现了全部的cpu指令,实现了显示背景nametable中的内容。
没实现的呢,主要是音频完全没做,手柄操作完全没做,屏幕滚动还没做,甚至按固定帧率执行游戏都还没做。上面的截图是我顺序执行了大约350000条汇编指令后的效果。不过这个固定帧率不难,我因为一直在修一些低级bug,都是跑几帧就出问题的地方,所以就没着急做按帧率执行。

关于这个项目的预期时间投入。
目前看来总时间投入要在200小时甚至以上了。当初觉得自己两个礼拜就能搞定,真是太可笑了。
时间分配上,阅读和查阅文档和参考项目要占70%的时间,代码实现占10%的时间,修bug占剩余的20%的时间,大概是这样。因为nes模拟器是一个比较常见的个人项目选题,github上有很多别人实现好的项目可以参考,我甚至可以实际运行别人的模拟器,逐命令比较我和他执行结果的不同,所以调试方面已经比早期那些第一代做模拟器的人们简单很多了。但越做到后面遇到的硬件细节越多,而且错误越来越隐蔽,还是花掉了我两倍于开发的时间来调试bug。如果这个趋势继续恶化的话,我接下来要考虑先做一个高效率的模拟器调试器了。

最后记录几个我踩过的坑,留给未来的自己回顾时回忆。
1. cpu读取当前显存地址的内容时,得到的永远是自己上一次读取显存时的缓存值! (https://wiki.nesdev.com/w/index.php/PPU_registers#The_PPUDATA_read_buffer_.28post-fetch.29
这个太反直觉了,一定是当时的硬件限制导致的。没有正确实现这个细节导致我的地图贴图序号部分出现了错误,甚至我的贴图图案都被污染了一部分。

broken-nes

比如下图的上边两张是原始的贴图,下边是被污染后的贴图。为了清楚分辨,我给他们上了比较艳丽的颜色。可以看到右下的©中间被污染了,显示在屏幕上就是中间出现了一些多余的黑色的像素。

broken-texture

调查这个bug花了我大概一整个上午的时间,期间发现用chrome调试面板在高频执行处下断点会极大的影响程序执行速度,甚至有时候会内存使用量过大导致页面崩溃。就是那时候我开始考虑是不是应该做一个模拟器专用调试器了。

2. 2d canvas 的绘制效率。
html5的canvas我工作中经常接触,但大多是在使用现有绘制库的基础上。徒手操作canvas绘制时的绘制效率方面我之前并没有研究。
最开始我按照我知道的方法, ctx.fillStyle="rgb(r,g,b)"; ctx.fillRect(x, y, 1, 1); 来绘制每一个像素。一帧两帧还好,当我执行到一口气绘制30多帧的时候,绘制部分的耗时陡增到5秒多。当时我就意识到方法错了。毕竟有现成的jsnes模拟器,每秒跑60帧都没问题的,所以肯定有更高效的绘制方法。
稍微搜了一下,找到了创建 imagedata,然后直接修改 imagedata.data 数组里的数字来设置每个像素的颜色,最后把imagedata整个画到 canvas 上的方法。换这个方法以后绘制部分耗时只有原来的1/10了。虽然离60帧每秒还有一点距离,但至少不影响我调试了,暂时先这样吧。

 

rss

我现在关注的新闻类微博有点多,已经超过10个了。导致的结果就是时间线混乱拥挤,把不常发微博但我很在意的人的信息冲走了!
这可忍不了了。todo list里加一项:清理新闻类微博关注到rss阅读器中,重新开始使用rss阅读器。
自从google reader关闭了以后,我的rss阅读习惯就没能再继续下去。一方面是因为主流网站放弃对rss的支持,另一方面是现有rss阅读服务都会把30天以前的全部未读强制标记为已读,彻底搞乱了我的阅读计划。
主流网站放弃rss,有人手写各个网站的网页转rss工具,我自己写也不难。rss阅读器这个釜底抽薪式的破坏用户体验的做法,就只有用self-hosted的私人定制rss阅读器来解决了。
feedly的人解释过30天这个限制是出于服务器性能开销的考虑,这个理由我可以理解但无法接受,结果就是分手只是时间问题了。

self-hosted的rss阅读器一直在我的个人项目列表里,但一直没有什么特别紧迫的压力去做它。
目前业余项目的优先级是: NES模拟器 > 微信公众号网站头像设置功能收尾 > 和亲友一起做一个预计工期3个月的小游戏上线。
和rss阅读器一样想做但动力不足以给它设定优先级的候选项目还有其他好几个,加上我业余做兴趣项目本来就是三天打鱼两周晒网,清理微博关注的事不知道什么时候才能上线呢。
毕竟这混乱的时间线已经忍了两年了。再说twitter已经把恢复时间线顺序的功能添加到首页了,搞不好我出手之前微博就跟进这个功能了呢!

博客不再流行真的是有他的道理的。文采好的,去各大平台开专栏开公众号了。只是想记录生活的,微博朋友圈更轻松,还能收获更多评论,开心。

我对我博客的定义比较模糊。

首先作为程序员,写博客的第一印象肯定是技术博客。可是翻了翻我至今为止的日志内容,称得上技术的真是少得可怜,就算有,很多也是我学会或者攻克某个技术细节之后的文字总结,并不是通常意义上给新手入门用的技术日志。

因为想听听其他熟人的评价,所以曾经把博客介绍给同事看了,结果被说都是“无病呻吟”,没有技术含量...

虽然有点受伤,不过人家说得没错啊,想在我这里学到技术,还不如问自己波棱盖儿呢(突然想起这句老话,强行用上=v=)。

最初把博客改名盗贼的极意,本来是想敦促自己要从公司或者其他可以接触到成熟解决方案的地方“偷师”,即使与自己无关的部门的技术框架也要偷学回来的。但是现在看来,我这其实不是个讲技术的地儿啊。所以这个新名字,就单纯的用来耍帅吧。

 

技术博客是做不了了,那就记录生活吧。

说记录生活也有点说大了,我这里更多的是记录一些心情,想法。

翻翻五六年前的日志,看到当时我的希望在工作中不计较工资,以吸取技术经验为主,现在看来真是惭愧得很。对现在的我来说,技术已经不是一个遥远到需要虔诚仰望的存在了。比起追求模糊的“技术”这一概念,现在的想法更加具体。我知道我需要学习什么,也知道怎么学,甚至也知道自己肯定能学会。当一切变得唾手可得时,它也同时失去了曾经的神奇魅力。

现在的我,更想做事。想用自己的技术去做成些什么。公司内的项目也好,兴趣项目,玩具app,网站,游戏,什么都好。而做成一件事所需要的能力很多,技术只是其中最容易入手的一种,容易到你甚至可以不懂技术,只靠外包来完成项目。

说远了。还是说说记录心情吧。

我是一个行动力执行力自控力很差的人,日语叫对自己太好(自分に甘い),想放弃就放弃,我甚至很想在简历里添一句“擅长放弃”。因此日志里有很大一批自我激励的鸡汤类内容,也就是同事说的“无病呻吟”了。说实在的,总是自我反省,总是立大志,然后总是放弃,连我自己都不好意思再写这类日志了。我也想像其他我佩服的人一样,某一天冷不丁甩出一篇日志说,我最近做成了这个这个这个,然后附上github链接,超吊。

可能立志这件事,说出来就不灵了吧。

每次多开一点tab他就隐藏网站图标,让我摸黑使用,怎么用嘛!

useless-tab

 

本来前一版(67.0?)那个锯齿状tab的解决方案蛮好的,怎么这两天又给改没啦?!

 

锯齿状tab

 

好吧,在这里立下Flag,等我的nes模拟器写完,下一个就是定制自己的锯齿状tab的chromium!

 

donkey-kong

最近突然想知道怎么做游戏模拟器。
这个念头从我学程序开始就每隔几年冒出来一次。从当时毫无头绪,到后来知道simulator和emulator的区别,再到前些天看到这篇博客,我终于觉得我已经可以实现这一目标了。

这时候就不得不提一下我的一个坏习惯了。是这样的,我面对一个难题或者个人项目时,总是一开始非常有热情有行动力,但是当我知道我能完整的解决这一难题,或者我已经了完成个人项目中最大的技术挑战时,我的热情会瞬间退却,最终导致项目停滞,最终弃坑。
这次为了防止弃坑,我决定连游戏主机也自己diy一个。这样模拟器的实现就只是项目中的一小部分,有可能我最终弃了diy主机的坑,但完成了模拟器呢(笑)。

diy游戏主机,具体来说是掌机,其实就是想做一个塑料的机壳,里面跑个树莓派,整体上有屏幕有按钮有电池有张sd卡就够了。
这里最大的困难是这个外壳大概要自己定做。不过既然已经时这个时代了,我猜个人3D打印应该足够满足我的需求了。

说是这么说,心里还是没底,于是查了查3D打印方面的行情。第一个想到的当然是万能的淘宝啦。上去搜了搜,cad来图定制似乎挺成熟的了,做这门生意的人很多,生意也不错。那么下一步就是找到这个行业里最主流的供应商了。淘宝上销量好的很多,似乎没有谁是统治级的,但有趣的事,我看到很多卖家下面的买家秀的包装盒都是同一款。这什么情况?搜了一下盒子上大大的“wenext”的logo,找到了未来工场,一个3D打印服务提供商。在他们的网站上传stl后缀名的cad文件可以实时算出预算,运送方面用的是顺丰,我特意问了一下,可以选择稍微慢一点但便宜很多的顺丰国际特惠来送到日本。价格来说是可接受范围内的,即使算上国际运费。看来这就是我要找的“行业里最主流的供应商”之一了。至于淘宝为什么用这家的包装盒,我猜可能是淘宝店家接单后转手去未来工场订货,再转卖给买家的吧。虽然之后又搜到其他几家国内的可以在线计算预算在线下单的3D打印网站,不过考虑到淘宝卖家一致的选择,我也决定随大流选未来工场了。

(后来我甚至发现了一个未来工场的山寨网站“未来工厂”!我问未来工场的客服说这个是你们的分部吗?他说我们没有关系,我们是未来工“场”,他们是未来工“厂”。哈哈哈哈,简直大家来找茬啊!他说了我才发现真不是同一个场字。会被同行山寨更坚定了我选他们的决心!)

为什么不直接在日本找3D打印公司呢?因为贵啊!随便搜到几家,根据同个cad图得出的费用是6万到8万日元,折合人民币要三四千块了。未来工场这边只要几百人民币,完全碾压。
其实美国也有一家主流的3d打印网站(all3dp.com)。它上面是以3d打印信息文章新闻之类的为主,但是也有在线上传图纸,下面列出各家供应商给出的价格,类似一个卖家聚合网站。不过算上运费,美国供应商提供服务的性价比也完全比不上中国,同一产品成本大概是国内两三倍的样子。

打印商定好之后,下一步就是要选一个适合的cad软件,做我的设计图了。能学一下cad,感觉不错啊,以后能做的事就多多了。

unnamed

工作上遇到 android app 意外发生 Bitmap 内存泄漏的问题。把 vm heap dump 下来一看不得了,有大量 byte[] 数组驻留在 vm 内存内,但却没有被任何对象引用到!明明我们的 app 内存不足到都崩溃了,gc 还不回收这些垃圾 byte[],这是为什么?而且这个现象在 android 6.0 上发生,但在 android 7.1 和 8.0 上都不会发生。而且,在我们的 app 退出那个会导致这个内存泄漏的 view 之后,gc又可以正确回收那些幽灵数组了。总觉得,好神秘!
稍微读了一下各个版本 android 的 Bitmap 的 java 和 jni 代码之后,我觉得这是因为内存虽然被标记为虚拟机内内存,但是创建和释放都是由 native 层管理着,而 native 层出于某些原因,不愿意及时释放这批内存所导致的。是一起有组织有预谋的私藏内存事件。
怎么办呢,native debugger attach 上,直接调试 native 层吧,没有 symbols,看不到源码和变量,只能看到进入了哪个函数和任意时刻寄存器的数值,这调试起来真是两眼一抹黑,事倍功半。
想要有源码调试?可以啊,自己编译 aosp (android open source project),用自己编译的 system.img 启动模拟器就可以有源码调试了。于是花了一个星期各种式错后终于用上了自己的 system.img,自豪!结果发现用我的 system.img 之后内存就不泄漏了。耍我哦!
细致调查后发现不仅替换整个 system.img 会这样,就算只是替换掉 android 主机(模拟器)内的 /system/lib/libandroid_runtime.so 为我编译的版本,内存泄漏问题也会消失。这个 .so 里大概都是 android jni 相关的东西,当然我怀疑的 Bitmap native 部分的实现也在里面,所以我的推理方向似乎是正确的。只是没法继续直球出击分胜负了,遗憾。
顺便说一下,用 android studio 的 android profiler 的 memory 面板查看就会发现,用了我的 .so 之后 app 的 native memory 使用量始终非常低,而 原版模拟器会在 load 一个 Bitmap 后 java memory 和 native memory 会一起增减。目前我初步怀疑是我编译的版本没有能够正确访问硬件相关功能导致硬件加速部分产生了差别,但还没有什么证据。
如果是兴趣项目,我肯定会继续研究 aosp 的,可惜这次是工作,不能太任性。下一步我准本静态分析 Bitmap 相关的代码,配合半瞎的 native debugger 和试错性调整相关的 react-native 部分代码来找出避免这一内存泄漏的方法。
之前遇到过 react-native 的圆角图片导致的 android 7.1 以下的 app 会发生 graphics memory 内存无限泄漏的问题,最后也是在没理解根本原因的情况下被我绕过去了,相信这次也能逢凶化吉!

当年年少无知,什么框架都没深入用过,只靠着会写php语法就接活做了网站。

为了避免遇到难以解决的问题来不及交货,于是决定不用任何框架,结果最后徒手写了一百多个php文件硬凑起一个网站来。

随着规模的增大,每增加一个新功能都提心吊胆。一直想写自动测试写回归测试却无从下手。

现在还要为这个网站做微信网页版适配,真是有种抽积木的自虐快感。

抽积木

现在还能怎么办呢。因自己的无能而酿下的苦果,只能硬着头皮接受了。

以今日之耻为鉴,日后定要练成体系化网站开发的扎实功底!

malware

今天上班,刚打开fiddler就在自己的http log里发现了一条发自chrome扩展程序的可疑request。

打开这个扩展的chrome web store页面发现已经被下架。
网上搜了一圈发现有人有同样疑问,虽然没有完全解开谜团,但基本可以确定是可疑程序了。
打开扩展的js代码看了一下,全部重度混淆,果然是做贼心虚。
最可气的是他的代码中竟然包含 function(data){eval(data);} 这样的执行任意代码的后门,我操!

插件名: "Classic Scrollbar Buttons"
这是一个让chrome重新使用老式右侧滚动条外貌的常驻扩展。我已经用了很多年,竟然直到今天才发现带后门,真是……无语。
暂时先把它发出的request信息原样保存好,插件暂时禁用,等我有空了再回来收拾你。

记与黑马君长聊

11.12.2017, 8 条评论, 未分类, by , 2,322 views.

mariokart8

时隔大约半个decade,终于又一次听到了D-Horse同学的声音。喜闻君亦入行IT,感慨良多。

连打6个小时的电话怕是我个人生涯的最高纪录了,话题竟然够讲这么久,我自己都觉得有点不可思议。

跟你倾诉我社交生活太少感觉寂寞之类凡人的苦恼,现在想想自己都觉得不好意思。这种事还是应该自己内部消化才比较成熟。不过谁让我们就是从幼稚时就相识的呢,这点任性,想你应该可以体谅:)

两个人联机马车确实是不如一大群人一起玩有意思,不愧是party game。下次看看有没有什么暗黑2那样两个人可以一起闯关,平时也能自己推进的游戏吧。明年王者荣耀如果评价不错,可以考虑联机一手,如果账号可以跨服联机的话。

关于理想的工作,我也有同样的苦恼,也是正在理想与现实之间难以做出抉择。只恨自己技不如人,年轻时没有花时间去精进自己的手艺,积累作品,落到现在不能随心愿选择工作的处境。不过过去的已经过去,现在的我是疯狂揽活填充业余时间,希望5年后的自己不会再同样怨恨现在的自已吧。

跟你说的我信息成瘾的事,正好在这里记下来提醒自己。今后刷微博刷新闻的时候要提醒自己停止,就像当年成功戒掉了愤世嫉俗关注政治新闻的习惯后,发现我的生活并没有因为缺少政治信息而有丝毫褪色一样,相信停止疯狂吸收信息也不会摧毁我的现有生活。省下来的时间嘛,把自己买的kindle电子书先消化一下吧。拿来玩游戏也很不错。我现在正处在怀疑自己是否真的喜欢游戏行业的自我怀疑阶段,多玩一些主机和PC游戏,让我仔细感受一下自己还会不会为这个行业兴奋吧。每次去秋叶原走一走我都感觉自己的信仰得到了充值,赴日留学时的心情又重新浮现。我对游戏行业的热情也需要像秋叶原这样可以帮我为信仰充能的象征,希望能在玩游戏的时间里找到。

听你讲的德国的福利真让我有点动心。现在日本的养老金制度是交足10年就有资格领取,随便你在日本还是在海外,对外国人非常友好。如果今后有机会去欧洲找一下工作,也许也是不错的生活。不过因为语言等原因,去欧洲工作的话我可能会去英国,到时候去找你玩要靠划船坐飞机了。

趁我还在日本,快来日本玩呀!我也努力提升专业技能,给转职之路攒一些筹码,顺利的话转职间隙也会去找你玩的!