从我今天写很多6502的理解来看,并行数组比存储数据的结构要好。6502汇编语言中并行数组结构数组的优点?
想象一下,你想拥有,在C会这样
struct Monster {
unsigned char hitPoints;
unsigned char damage;
unsigned char shieldLevel;
char* name;
};
定义什么怪物统计的表格你可以把它保存为结构
static Monster s_monsters[] = {
{ 5, 1, 0, "orc", },
{ 50, 10, 5, "dragon", },
{ 10, 3, 1, "goblin", },
};
或者你可以存储阵列它作为并行数组(通常使用宏或工具来生成)。注意:我用C语言显示代码,但请想象它是6502程序集。
unsigned char Monster_hitPoints[] = { 5, 50, 10, };
unsigned char Monster_damage[] = { 1, 10, 3, },
unsigned char Monster_sheildLevel[] = { 0, 5, 1, };
unsigned char Monster_nameLow[] = {
&m_orc_name & 0xFF,
&m_dragon_name & 0xFF,
&m_goblin_name & 0xFF,
};
unsigned char Monster_nameHigh[] = {
&m_orc_name >> 8 ,
&m_dragon_name >> 8,
&m_goblin_name >> 8,
};
在6502,给予itemNdx您可以访问所有平行阵列领域这样
ldx itemNdx
lda Monster_hitPoints,x ; access hitpoints
...
lda Monster_damage,x ; access damage
...
lda Monster_shieldLevel,x ; access shieldLevel
...
lda Monster_nameLow,x ; access name
sta pageZeroStringPointer
lda Monster_nameHigh,x
sta pageZeroStringPointer + 1
ldy #0
lda (pageZeroStringPointer),y
在哪里,如果你使用并行阵列的结构,而不是成为
lda itemNdx
clc ; have to compute offset
asl a ; a = itemNdx * 2
asl a ; a = itemNdx * 4
adc itemNdx ; a = itemNdx * 5
tax ; x = itemNdx * 5
lda s_monsters+Monster.hitPoints,x ; access hitpoints
...
lda s_monsters+Monster.damage,x ; access damage
...
lda s_monsters+Monster.shieldLevel,x ; access shieldLevel
...
lda s_monsters+Monster.name,x ; access name
sta pageZeroStringPointer
lda s_monsters+Monster.name+1,x
sta pageZeroStringPointer + 1
ldy #0
lda (pageZeroStringPointer),y ; a is now first char of name
结构版本必须计算每个结构的偏移量。在上述情况下,与并行阵列版本相比,还有5条指令。最重要的是计算偏移量的数学手工编码,这意味着如果结构发生变化,必须在任何时候重新编写它。最重要的是,你只能有一个256/sizeof(Monster)
大的表。如果你有更多的字段(20到30并不少见),这意味着你的表只能有8到12个条目,与平行数组一样,你可以有256个条目。如果您想遍历表格,还有一个好处。与平行阵列,你只需增加一个指令x inx
。随着结构,你不得不添加的sizeof(怪物),它增加只适用于一个将
txa
clc
adC#sizeof(Monster)
tax
这是3个比水货版本的阵列更多的指令。
这似乎是平行阵列是6502汇编语言客观的胜利,但有约翰·卡马克从his plan file
这个不起眼的评论......其实,早了解结构在平行美德一路在苹果II汇编语言阵列.. ...
有谁知道这些优点是什么?
我能想到的唯一优点就是我可以更容易地分配一个带有结构数组的动态数组,但大多数游戏在6502天内没有分配任何东西。他们硬编码修复了大小的内存数组,因此看起来并不像这样。 6502没有缓存,所以没有缓存优势。
此外,如果你去充分的指针却充满了对指针多慢,需要比任何一种,因此上述所示的方法,他们通常是不得已而为之的选择多更多的代码,你可以解决超过256个项目。
; setup pointer
lda itemNdx
ldx #sizeof(Monster)
jsr multiplyAX ; 8->16 bit multiply is around 70 cycles result in A(low), X(high)
clc
adC#s_monster && 0xFF
sta POINTER
txa
adC#s_monster >> 8
sta POINTER + 1
ldy #Monster.hitPoints ; access hitpoints
lda (POINTER),y
...
ldy #Monster.damage ; access damage
lda (POINTER),y
...
ldy #Monster.shieldLevel ; access shieldLevel
lda (POINTER),y
...
ldy #Monster.name ; access name
lda (POINTER),y
sta pageZeroStringPointer
ldy #Monster.name+1
lda (POINTER),y
sta pageZeroStringPointer + 1
ldy #0
lda (pageZeroStringPointer),y ; a is now first char of name
通过为每个项目制作一个平行指针数组,您可以摆脱乘法。你仍然有2行安装程序并行阵列不需要,你仍然会让代码的其他代码变得越来越大。每个访问8个周期vs每个访问5个和5个字节与3.
基本上,如果你绝对必须使用指针。如果你可以选择平行阵列,那么你应该总是选择它们。
问题是X和Y寄存器是8位的,不能用作结构元素的索引。 – i486
这个Q对我来说听起来太泛泛。如果你的任务允许的话,有更多的选择如何硬编码某些技巧,如果你的任务允许的话,我不熟悉6502,而Z80 + x86的差别太大了,但有时我们在内存中做了交错结构,所以每个元素都是在+256地址(通过递增ptr的高8位来移动指针),或者元素具有固定的大小,并且指针在处理过程中部分地增加以遍历每个字段,并且结束指向下一个元素的开始,或者固定大小元素与二的幂,因此乘法只是移位等等...... – Ped7g
当我上次为C64实现一个光栅IRQ表时,我使用了一个结构化数据阵列用于下一个光栅线,以将IRQ和ISR负载设置为跳到。这更简单并且没有任何缺点,因为数据只是以线性顺序使用。也就是说,我不能想到结构化数据的**普遍优势。这真的取决于用例。 –