在 Commodore 64 上的乒乓战争
回到文章 2026年7月15日 此文最初于2024年初承诺[0] 在C64上的乒乓战争, @Two9A, 2024年2月22日,但它变得如此冗长,我添加了一个目录。这可能是第一次…… 引言 在我们开始之前 插曲:构建 C64 磁盘映像 游戏循环:运动计算 第二个插曲:表示负数 运动之后是定位 机率的元素 比赛场地 记分 第三个插曲:二进制长除法 渲染分数 未来考虑 脚注和引文 2024年初的某一天,我无聊地翻阅Mastodon时,看到Koen van Gilst的这条推文[1]用JavaScript重新创造的乒乓战争,@vnglst,2024年1月: 图1:由@vnglst发出的推文,引发了目前正在讨论的疯狂之旅。 这是“白昼”球与“夜晚”球之间的永恒战斗的描绘,目的是逆转对方球的场地颜色。根据数以千计的点赞来看,许多人对此感到着迷;我也是其中之一,心中不禁涌现出一个念头:如果van Gilst在网上看到这个并不得不用JavaScript重新创作,而我现在在网上看到了它,我不得不在Commodore 64上重新创造它,对吧?这种事情甚至可能吗? “当然可以!” 好吧,这似乎应该是可行的。这是一个相对简单的动画,在球体接触使其弹起的表面时会加入一个随机元素;定义“使其弹起的表面”似乎是棘手的部分。因此,我们需要进行一些规划。作为第一步,有一些定义: 我们有球(两个)以平滑的直线移动; 有一个方块场:20x20的方块区域,球体在其上移动; 方块场有一个边界,球体无法超越该边界,并会反弹; 还有两个边缘会使球反弹,但被撞到的方块会反转边。 这一切都因以下事实而复杂化:尽管我一生都对黑客C64感兴趣,但截至目前我还没有在该机器上编写过任何实质性的汇编代码。看起来我们需要边做边学。 在那个时代的大多数机器上,视频输出到CRT,正如我几年前关于1541磁盘驱动器的巨型帖子中所解释的那样 [2] "为什么Commodore 64磁盘驱动器如此慢?" , Imran Nazar, 2024年12月。每帧视频输出之后都有一个短暂的垂直消隐期,在此期间,视频芯片不会对内存产生任何需求;我们将尽量限制这次动画的计算,使它们在消隐期内完成,以免在帧渲染期间不适当地影响机器上可能正在发生的其他事情。 在Commodore 64上,BASIC解释器是这段时间内可能运行的主要程序,它为这个效果提供了一些有用的东西。其中之一是在每帧底部触发的钩子[3] "光栅中断" , C64 Wiki, 更新于2024年3月,允许在保持BASIC运行的同时每帧调用自定义代码。因此,我们可以为代码规划出这样的内容: 图2:主循环的流程图 在我们开始之前 所以让我们看看初始化部分:我们将保持方块场状态的内部表示,但也需要设置屏幕上的方块场。C64在其默认文本模式下有40x25的字符区域(每个字符是一个8x8像素的方形),这里有两个值得关注的内存区域: 屏幕RAM默认位于$0400,是一个存储字符值的一千字节区域; 颜色RAM位于$D800,是一千个nybble [A] 一个nybble是四个位,相对于8位字节。 每个位置可以表示C64的16种颜色。屏幕RAM之后紧接着是指向精灵数据的指针,但我们还没有设置任何精灵(还没有),因此一种简便方法是写入1024个字节到屏幕。这会超出屏幕末尾,但使代码变得简单得多: SCRRAM = $0400 BLOCKPTR = $03 ; 一个未被BASIC使用的两个字节的块 init: 子例程 ; 设置指向屏幕RAM的指针,从左上角开始 ; 65xx CPUs是小端模式,先低字节 lda #<SCRRAM sta BLOCKPTR lda #>SCRRAM sta BLOCKPTR + 1 ; 我们将写入SPACE(字符32),而不是字符0(@) lda #32 ; 四组256,写入1024个 ldx #4 ldy #0 .blank: ; 内循环:从Y=0到255,254,...0再次运行 sta (BLOCKPTR), y dey bne .blank ; 外循环:每次循环将指针移动256 inc BLOCKPTR + 1 dex bne .blank (顺便说一句,BLOCKPTR设置为一个“未被BASIC使用”的值,但我们怎么知道呢?事实证明,有一个关于C64的内存详细映射,由STA演示组的Joe Forster提供 [4] Commodore 64 memory map , STA, 可能早已更新,因为该页面为HTML 4.01,说明BASIC和内核ROM在其标准操作中未触及的位置。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡