我是ASM的新手,我正在努力研究如何为以下代码创建延迟:
org $1000
loop: inc $d021
jmp loop
Run Code Online (Sandbox Code Playgroud) 为了好玩,我正在实现NES模拟器。我目前正在阅读有关6502 CPU的文档,我有些困惑。
我已经看到文档说明,因为6502是低位优先的,因此在使用绝对寻址模式时,您需要交换字节。我是在x86机上写的,它也是低位字节序的,所以我不明白为什么我不能简单地转换为uint16_t *,取消引用,然后让编译器确定细节。
我已经在Google测试中写了一些简单的测试,他们似乎同意我的观点。
// implementation of READ16
#define READ16(addr) (*(uint16_t*)addr)
TEST(MemMacro, READ16) {
uint8_t arr[] = {0xFF,0xCC};
uint8_t *mem = (&arr[0]);
EXPECT_EQ(0xCCFF, READ16(mem));
}
Run Code Online (Sandbox Code Playgroud)
这通过了,所以看来我的假设是正确的,但是我想我会问比我有更多经验的人。
这对于在6502绝对寻址模式下提取操作数是否正确?我可能会错过什么吗?
我在没有找到答案的情况下扫描互联网和旧的C64书籍,所以最后我不得不在这里发布.我喜欢C64编码的美好时光,虽然我目前还没有在这个平台上编写游戏,但我想知道当时是如何克服某些硬件限制的.
在所有现代游戏编程书籍和教程中,找到向玩家发射敌人子弹的正确方向的方法是使用向量数学与浮点数,或多或少像这个伪代码:
bullet_velocity = (player.position - bullet.position).normalize();
Run Code Online (Sandbox Code Playgroud)
现在,考虑到C64的限制,我在源代码中大量使用正弦表来提高速度,也许我分心了,但是当我从C64程序员那里阅读旧的C64书籍或评论程序时,我从未见过关于矢量数学的一个词,我想知道如何在同一时间获得同样的目标.
请回答,我有这样的一千个疑问,但回答这个问题也许我可以发现自己对其余的回应!:-)
编辑:只是一个注释:用针对玩家的子弹的C64游戏的例子是Silkworm和Cybernoid.
大多数仿真器存储特定指令在查找表中占用的周期数,然后根据需要添加任何条件周期(例如,当跨越页边界时).
我想知道是否有办法在程序上确定指令将仅基于寻址模式和存储器读/写的周期数.
举个例子,我注意到所有使用立即或相对寻址的指令都需要2个周期.
所有零页指令需要3个周期,如果就地改变存储器,则需要额外的2个周期.
所有索引的零页指令需要4个周期,如果就地更改存储器,则需要额外的2个周期.
...等等.
那么,是否有一些完整的文档化程序方法来确定上述指令的周期数?是否存在会破坏这种公式中的决定论的例外情况?
我正在查看此站点的一些代码示例:
看着它我看到他们有一些指令,而不是直接使用内存位置,他们使用标签,例如,在alive.asm中:
lda ypos,x
Run Code Online (Sandbox Code Playgroud)
而ypos是
ypos:
dcb $00,$02,$20,$02,$40,$02,$60,$02
dcb $80,$02,$a0,$02,$c0,$02,$e0,$02
dcb $00,$03,$20,$03,$40,$03,$60,$03
dcb $80,$03,$a0,$03,$c0,$03,$e0,$03
dcb $00,$04,$20,$04,$40,$04,$60,$04
dcb $80,$04,$a0,$04,$c0,$04,$e0,$04
dcb $00,$05,$20,$05,$40,$05,$60,$05
dcb $80,$05,$a0,$05,$c0,$05,$e0,$05
Run Code Online (Sandbox Code Playgroud)
我知道标签的不同取决于汇编程序,但我假设它正在通过该列表,但它的特殊性如何工作
为了学习如何使用ca65汇编程序,我一直在努力使include guards起作用。谷歌搜索和阅读《ca65用户指南》无济于事。这是产生错误的最小示例。
$ ls -l
total 16
-rw-r--r-- 1 me staff 60 Oct 22 19:40 65.inc
-rw-r--r-- 1 me staff 55 Oct 22 20:01 test.s
$
$ cat 65.inc
.ifndef _65_INC_
.define _65_INC_
.define NUMBER 1
.endif
$
$ cat test.s
.include "65.inc"
.include "65.inc"
lda #NUMBER
rts
$
$ ca65 test.s
65.inc(1): Error: Identifier expected
65.inc(2): Error: Identifier expected
65.inc(4): Error: Identifier expected
65.inc(4): Note: Macro was defined here
$
$ ls -l
total 16 …Run Code Online (Sandbox Code Playgroud) 我目前正在为 NES 制作一个模拟器(像许多其他模拟器一样),并在针对 Kevtris 的 Nestest ROM 测试我的模拟时(在这里找到: https: //wiki.nesdev.com/w/index.php/Emulator_tests),有这是我在nestest日志的指令877处遇到的一个奇怪的错误(这个: http: //www.qmtpro.com/~nes/misc/nestest.log,在CE42行)。
该指令是一条 PLA,它从堆栈中取出累加器,同时堆栈指针位于 $7E 的开头。(我使用 1 字节值作为堆栈指针,因为它从 0x0100 到 0x01FF ,所以当我写 $7E 谈论堆栈时,它是 0x017E ,而不是 Zeropage ;))
因此,当 PLA 在第 877 行执行时,堆栈指针移动到 $7F 并检索第一个字节并将其存储到累加器中。
问题就在这里:在嵌套日志上,该字节是 0x39 ,然后,在也是 PLA 的指令 878 上,在 $80(堆栈指针递增 + 1)处检索到的字节是 0xCE,这已经反转了低字节和高字节。
写入堆栈 (0xCE39) 的值源自 CE37 行的 JSR 指令,以下是我对 JSR 操作码的实现:
uint8_t JSR(){
get() ; // fetch the data of the opcode , like an absolute address operand or a value
uint16_t newPC = …Run Code Online (Sandbox Code Playgroud) 据该网站称,在“主要用途”下CLC,它指出:
如果要进行一系列加法(多字节加法),则只有第一个加法
ADC前面有 ,CLC因为进位功能是必要的。
在其“主要用途”下SBC指出:
您总是在操作
SEC之前(设置进位标志)SBC,以便您可以判断是否需要“借位”。
换句话说,对于一系列连续的ADC操作,您只需要CLC在第一个之前有一个,但在一系列连续的SBC操作之前,您应该SEC在每个操作之前有一个。它是否正确?
如果在变址寻址模式下执行加法的结果大于 ,会发生什么情况0xFFFF?需要明确的是,我指的不是从低字节到高字节的进位,而是指从高字节的进位。
例如,对于使用绝对 X 索引寻址的指令,0xFFAA当 x 索引寄存器包含 时,使用操作数调用它会产生什么结果0xFF?这会溢出到吗0x00AA?结果会0x100A9被截断为吗0x00A9?处理器会冻结吗?
由于某种原因,任何地方都没有这方面的信息。这一点在 MOS 的 650X 系列官方编程指南中似乎并未提及。
I have a snippet that clears memory before initializing a game in NES 6502 assembly. When I leave the code inside the reset proc like so, it works:
.proc reset
SEI
CLD
LDX #0
ClearRAM:
STA $000,x
STA $100,x
STA $200,x
STA $300,x
STA $400,x
STA $500,x
STA $600,x
STA $700,x
INX
BNE ClearRAM
.endproc
Run Code Online (Sandbox Code Playgroud)
However, if I try to move this ClearRAM snippet inside a scoped proc:
.scope Memory
.proc clear
LDX #0
ClearRAM:
STA $000,x
STA $100,x
STA …Run Code Online (Sandbox Code Playgroud)