我们先来看垂直镜像,我们可以发现 A 和 C 的地址是一样的,B 和 D 的地址是一样的,那么实际上,这里我们可以转化为一个简单的对于 0x800 的取模运算
那么水平镜像的代码怎么写呢?我们可以这样想一下
我们现在布局可以想象为一个 800 * 800 的矩阵,我们可以先缩小为 400 * 400 的矩阵。即我们 A 到 B 取值范围就缩小为 0x00 到 0x3FF,同时我们 C 到 D 的取值范围也缩小为 0x400 到 0x7FF。这个时候,我们就能发现我们利用位运算 & 的性质,和 0x400 做与运算,我们就能得到 A 和 B 两个区间的基准起始地址 0x00 以及 C 和 D 两个区间的基准起始地址 0x400。最后加上模运算的结果,我们就能得到新的地址
intmain() { // Timing the original function longlong start1 = current_time(); for (int i = 0; i < 100000000; i++) { mirror_lookup(Horizontal, 0x401); } longlong end1 = current_time(); printf("Time taken for original function: %lld microseconds\n", end1 - start1);
// Timing the new function longlong start2 = current_time(); for (int i = 0; i < 100000000; i++) { mirror_lookup_new(Horizontal, 0x401); } longlong end2 = current_time(); printf("Time taken for modified function: %lld microseconds\n", end2 - start2);
return0; }
结果是
1 2
Time taken for original function: 355402 microseconds Time taken for modified function: 251868 microseconds
大概快了百分之30,符合预期
总结
很多时候能发现各种古早的系统里为了性能做的各种的 trick,非常好玩。
这里留个思考题
我们假设 NES 的 CPU 是理光 6502,CPU 频率 1.79 MHz,我们能否再定量分析下我们实现一个 mirror 流程的两种方法各自需要多少时钟周期?