汇编中的蛇,使用什么数据结构?

2024-03-22

我对汇编完全陌生,不管你信不信,我们的第一个任务是在汇编中创建蛇。我应该如何储存蛇?我应该把它放在堆栈中,还是应该将它放入某个寄存器中?我已经对这种“可怕的”语言进行了大约 3 天的研究,但无法找到一个好的开始方法。我可能会在 c++ 中使用某种链接列表,但不幸的是这不是 c++。

非常感谢任何帮助


可以使用一对 x,y 坐标来跟踪蛇的头部。当然,您需要头部的当前方向。(尾部需要的不仅仅是一个 x,y 对,可能 x,y 对的循环缓冲区是最好的选择。那么您实际上不需要与历史缓冲区分开的尾部 x,y。)

正如 @Daniel 的回答所解释的那样,当头部移动时,查看您要绘制的下一个像素会告诉您蛇吃什么:什么都没有,苹果,墙壁或它自己。

(如果您对视频 RAM 具有高效的读取访问权限,则可以简单地读取像素,而不必同时保留阴影板阵列。但大多数现代系统don't具有高效的视频 RAM 读取访问权限;在典型的 x86 VGA 内存上,写组合无法缓存。但如果你假装你正在为真正的 8086 编写,VGA RAM 位于 ISA 总线的另一侧,所以速度也很慢。但8086没有任何缓存。一些甚至更早的系统可能将视频 RAM 作为主存储器的一部分,IDK。)

另一种选择是搜索苹果和蛇方块的列表,但这比检查一个数组条目要多得多的工作。编写游戏(或其他实时软件)的一个关键因素是对最坏的情况下表现。当有很多带有蛇的方块,并且步骤时间很短(蛇移动速度很快)时,您不希望游戏变得笨重并且某些步骤比其他步骤花费更长的时间,因为搜索时间太长。那很快就无法播放了。

所以你可能想要一个数组rows * cols存储代码的字节(您对其进行二维索引)0对于空的,1用于墙壁等。如果您使用调色板视频模式,则可以使用与视频 RAM 中的像素相同的代码,如果您直接绘制到 VRAM 中,或者使用现代视频 API、屏幕缓冲区或 OpenGL 纹理。否则,您的状态数组中将只有代码,屏幕缓冲区中只有 24 或 32 位像素。

为了简化索引,您可以使存储格式使用 2 行步幅的幂,即使板宽度不是如此。因此,每行末尾可能会有填充列。但这可能会浪费大量内存,通常您只需要相对于当前位置进行索引(例如,+-1 字节用于左/右下一列,或 +-row_stride 来向下/向上获取下一行。)

头/尾 x,y 坐标could只是平板数组中的单个索引或指针,但我们还需要实际的 x,y 坐标来单独绘制图形。


每走一步还需要清除尾部的一个方块(除非蛇仍在从最近的苹果中生长;您将需要一个计数器来显示有多少待处理的生长)。我们知道在哪里将屏幕涂回黑色,因为我们有尾巴 x,y。

但我们如何update尾部 x,y 所以next步骤将遵循头部实际走过的路径?您可能希望通过观察尾巴周围的方块,可以找出哪一个是第二古老的。我们可以通过两个不同的轨迹导致具有相同头和尾的相同棋盘位置的示例来证明情况并非如此。玩家可以通过水平或垂直之字形来创建此棋盘布局。

   H123        H167          H is the head, T is the tail
    654         258            snake segments are 1..8 in order of moves.
    78T         34T

如果没有 1..8 的历史,所有的方块都只是“蛇”,没有算法可以明确地决定尾巴在擦除后应该向哪个方向移动。 (即使是一个可以环顾整个棋盘的缓慢算法。)

对于仅查看尾部周围 8 个方块的合理算法,还有其他不明确的情况,例如

    54               H12          # defeating a "local" algorithm
    T321H              34
                       T5

所以我们必须以某种方式记录历史。我认为最好和最简单的选择是:

Use a 作为队列的循环缓冲区 https://en.wikipedia.org/wiki/Circular_buffer(x,y) 对记录蛇的路径

你的蛇将在内存地址中爬行,这与蛇在屏幕上爬行的方式非常相似,因此这种数据结构导致简单的实现(每个步骤只需要做很少的工作)是适当的而不是巧合。

该缓冲区的大小将设置我们可以支持的最大蛇长度,此时我们将重用缓冲区条目来记录头部右侧after读尾巴。您可以将其设置为大于行 * 列,因此如果您愿意,游戏不会施加实际限制。

这可能是许多真正的经典贪吃蛇游戏都有最大蛇长度的技术原因。 (除了游戏玩法原因。)由于您的游戏是唯一在 CPU 上运行的东西,因此没有理由不始终使用相同大小的循环缓冲区,即静态分配您需要的大小。这样你就不会因为在成长或其他任何事情后复制它而导致游戏卡顿。

您可以编写类似于此 C 的汇编:

typedef struct xy {uint8_t x, y;} xy_t;
static const unsigned max_snakelen = 512;
struct xy snakepath[max_snakelen];
// uint16_t []  board array offsets is great if we don't also need x,y for graphics

enum board_entry { SQUARE_EMPTY=0, SQUARE_APPLE, SQUARE_SNAKE, SQUARE_WALL };
static const unsigned rows = 40;
static const unsigned cols = 80;   // including walls
board_entry board[rows * cols];  // we'll do 2D indexing manually because asm has to

static inline unsigned dir_to_board_byte_offset(enum cur_direction) { ... }
 // top bit maps to +- rows, or bottom bit to +- 1
static inline xy_t dir_to_xy_offset(enum directions  cur_direction) { ... }
 // map 0..3 to  {+1, 0}, {-1,0}, {0,+1}, {0,-1} in some order.

void step(enum directions cur_direction)
{
    static unsigned tailidx = 0;  // maybe kept in a register
    static unsigned headidx = 5;  // and arrange for the initial snake to be in the buffer somehow

    if (!growth)    // tail stays still while snake grows
        --growth;
        xy_t tail = snakepath[tailidx++];      // movzx edx, byte [snakepath + rbx*2] // movzx ecx, byte [snakepath + rbx*2 + 1]
        tailidx &= max_snakelen - 1;   // wrap the circular buffer idx.  AND ebx, MAX_SNAKELEN - 1

        board[tail.y * cols + tail.x] = SQUARE_EMPTY;   // imul stuff
        // and update graphics
    }

    // Most of the registers used for the above stuff are dead now, and can be reused below

    // Tail segments disappear *before* the head moves: snake can be head-to-tail
    // and go full Ouroboros without crashing.

    xy_t headxy = snakepath[headidx++];  // or track head separately, maybe in a reg so we can more quickly  like our function arg.
    headidx &= max_snakelen - 1;

    headxy += dir_to_xy_offset(cur_direction);  // maybe use a 16-bit add to do both components in parallel, except that `-1` will carry into the upper element.  So that only works with SIMD `paddb` or something. 
    // pretend that's a C++ overload and do the component adds separately

    enum board_entry headtype = board[headxy.y * cols + headxy.x];
    if (headtype != SQUARE_EMPTY) {
       apple or death;
    }

    board[headxy.y * cols + headxy.x] = SQUARE_SNAKE;
 // ADD NEW HEAD to circular buffer
    snakepath[headidx] = headxy;                   // mov [snakepath + 2*rbx], ax
    // and draw graphics for head, using its x,y.
}

这只是为了给您一个总体思路,它相当笨重,而且不是很好的 C 风格。 (并不是intended是。)我知道并非所有事情都已声明。在等待按键和计时器事件的事件循环的 asm 版本中,您在寄存器中保留多少状态取决于您。您调用的函数必须保存/恢复 regs,但是如果您使用标准调用约定并无论如何都这样做,那么让最外层循环将其状态保留在 regs 中并没有什么坏处。

乘法过去很慢,所以你可以考虑保留 x,yand一维数组索引或蛇结构中的实际指针。但现代 CPU 通常具有快速乘法器。 (例如,从 Core 2 左右开始,Intel 上的完全流水线延迟为 3 个周期,而非流水线 8086 上的延迟超过 100 个)。

特别是在 16 位机器上,将绝对地址嵌入到多个指令中不会花费很多代码字节。在 32 位代码中情况会变得更糟。 x86-64 可以在 Linux 上的位置相关代码中使用 32 位绝对地址,仍然允许诸如[snakepath + rbx*2],否则您会像 RISC 一样,在寄存器中获取至少一个基地址并引用与之相关的静态数据。

根据您的目标 ISA,您可能会在寄存器中保留更多或更少的指针。


另一种方法是记录玩家每次转身的时间。但在最坏的情况下,玩家每一步转动一次,因此您仍然需要与蛇的长度一样多的缓冲区条目。 (或者如果你有更少,你有highly不良的游戏玩法:玩家可能会意外地无法转身,因为他们过去转身太多,并令人沮丧地死去。)

每个条目至少需要与 x,y 对一样多的空间,并且需要稍多的解码工作。 (在每个回合仅记录 x,y 对可以提供足够的信息,通过查看尾部 x,y 与最后一个回合来重建路径。)

A previous-direction + length could actually be much more compact, though: 1 byte per record. If you pack bits into bitfields, direction takes 2 bits, and we can choose to make new entries early to keep the length at 6 bits even if the board is wider or taller than 63 = 26-1 squares. So each entry can easily be 1 byte. (Decode with dir = x & 3 and len = x >> 2).

您可以通过减少尾部条目的长度直到达到 0(这意味着尾部到达转弯,并且应该开始查看下一个缓冲区条目)来使用此 dir+len 格式。为了提高效率,您可以将当前正在处理的文件解压,或者使用sub byte [rsi], 4并检查进位(以检测递减时长度是否已经为零,从而使其换行)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

汇编中的蛇,使用什么数据结构? 的相关文章

  • 如何定义基于标签的组织结构?

    原标题 有没有办法在基于标签的组织方法上强制建立关系结构 我有一些实体 它们有一系列属性 一些属性影响实体可以具有的其他属性 许多属性被组织成组 并且有时实体被要求具有来自某些组的一定数量的属性 或者可能具有来自某些组的一定范围的属性 有没
  • ConcurrentLinkedDeque 与 LinkedBlockingDeque

    我需要一个线程安全的 LIFO 结构 并发现我可以使用线程安全的实现Deque为了这 Java 7 引入了ConcurrentLinkedDeque http docs oracle com javase 7 docs api java u
  • 链表迭代器实现 C++

    我已经在 C 中创建了一个链接列表 并想为其实现一个迭代器 以便我可以执行范围循环 for const int i list where Linked List
  • CALL指令是否总是将EIP指向的地址压入堆栈?

    x86架构中函数调用时是否存在返回地址不入栈的情况 No CALL根据定义 将在跳转到目标地址之前将返回地址压入堆栈 该返回地址是EIP or RIP sizeof call instruction 通常为 5 个字节 英特尔 64 和 I
  • `ImmutableSortedSet` 和 fsharp `Set` 有什么区别?

    BCL引入了一组Immutable Collections http blogs msdn com b bclteam archive 2012 12 18 preview of immutable collections released
  • 两个基本的 ANTLR 问题

    我正在尝试使用 ANTLR 来获取简单的语法并生成汇编输出 我在 ANTLR 中选择的语言是 Python 许多教程看起来非常复杂或详细阐述与我无关的事情 我真的只需要一些非常简单的功能 所以我有两个问题 将值从一个规则 返回 到另一规则
  • 将非平凡函数应用于 data.table 的有序子集

    Problem 我正在尝试使用我新发现的 data table 功能 永久 来计算一堆数据的频率内容 如下所示 Sample Channel Trial Voltage Class Subject 1 1 1 196 82253 1 1 1
  • 为什么 Java 中的 hashCode() 可以对不同对象返回相同的值?

    引用我正在读的书中的一段话首先Java http www amazon co uk Head First Java Kathy Sierra dp 0596009208 关键是 哈希码可以相同 但不一定保证对象相等 因为使用的 哈希算法 h
  • 从 NASM 调用 C 函数 _printf 会导致分段错误

    我一直在尝试使用 NASM 在 Mac OS 和 Windows 上学习 64 位汇编 我的代码是 extern printf section data msg db Hello World 10 0 section text global
  • 为什么我的空循环在 Intel Skylake CPU 上作为函数调用时运行速度是原来的两倍?

    我正在运行一些测试来比较 C 和 Java 并遇到了一些有趣的事情 在 main 调用的函数中 而不是在 main 本身中 运行具有优化级别 1 O1 的完全相同的基准代码 导致性能大约翻倍 我正在打印 test t 的大小 以毫无疑问地验
  • 使用 (float&)int 进行类型双关可以正常工作,(float const&)int 会像 (float)int 一样转换吗?

    VS2019 发布 x86 template
  • ARMv8 A64 汇编中立即值的范围

    我的理解是 ARMv8 A64 汇编中的立即参数可以是 12 位长 如果是这样的话 为什么这行汇编代码是 AND X12 X10 0xFEF 产生此错误 使用 gcc 编译时 Error immediate out of range at
  • 使用 NEON 优化 Cortex-A8 颜色转换

    我目前正在执行颜色转换例程 以便从 YUY2 转换为 NV12 我有一个相当快的函数 但没有我预期的那么快 主要是由于缓存未命中 void convert hd uint8 t orig uint8 t result uint32 t wi
  • 什么时候使用哈希表?

    什么情况下使用哈希表可以提高性能 什么情况下不能 哪些情况不适合使用哈希表 什么情况下使用哈希表可以提高性能 什么情况下不能 如果您有理由关心 请使用哈希表和您正在考虑的其他任何内容来实现 将您的实际数据放入其中 并衡量哪个性能更好 也就是
  • 节点*链表中的下一个

    我是数据结构和算法的新手 我遇到了以下代码 typedef struct node int data node next 谁能告诉我为什么我们要声明节点 next next 不能声明为 int next 吗 因为你希望能够做到n gt ne
  • 阴影空间示例

    EDIT 我接受了下面的答案 并添加了我自己的代码的最终修订版 希望它向人们展示影子空间分配的实际示例 而不是更多的文字 编辑 2 我还设法在 YouTube 视频 所有内容 的注释中找到了一个调用约定 PDF 的链接 其中有一些关于 Li
  • 设置 IRQ 映射

    我正在遵循一些教程和参考文献来尝试设置我的内核 我在教程中遇到了一些不熟悉的代码 但根本没有解释它 这是我被告知映射的代码16 IRQs 0 15 到 ISR 地点32 47 void irq remap void outportb 0x2
  • 多 AVL 树旋转

    假设我有一个无序集合 s 3 6 5 1 2 4 并且我需要构造一个 AVL 树 就这么多了 我了解基本的旋转 我在这里达到这一点 5 2 6 1 3 但当我尝试插入 4 时 一切都崩溃了 我得到的最终答案是 左边的 4 But the a
  • X86 预取优化:“计算 goto”线程代码

    我有一个相当重要的问题 我的计算图有循环和多个 计算路径 我没有制作一个调度程序循环 其中每个顶点将被一一调用 而是将所有预先分配的 框架对象 放置在堆中 代码 数据 这有点类似于线程代码 甚至更好 CPS 只是在堆中跳转 执行代码 每个代
  • 为什么 clang 使用 -O0 生成低效的 asm(对于这个简单的浮点和)?

    我正在 llvm clang Apple LLVM 版本 8 0 0 clang 800 0 42 1 上反汇编此代码 int main float a 0 151234 float b 0 2 float c a b printf f c

随机推荐

  • .Net core DI 范围验证,范围与瞬态?

    正在阅读docs https learn microsoft com en us aspnet core fundamentals dependency injection view aspnetcore 2 2 scope validat
  • Mongoose 中的“__v”字段是什么

    我在用着Mongoose版本 3 与MongoDB2 2 版 我注意到一个 v字段已开始出现在我的MongoDB文件 这与版本控制有关吗 它是如何使用的 From here http mongoosejs com docs guide ht
  • 如何将字符串解析为 Haskell 中的函数?

    我想要一个看起来像这样的函数 readFunc String gt Float gt Float 它的操作是这样的 gt readFunc sin pi 2 gt 1 0 gt readFunc 2 3 0 gt 5 0 gt readFu
  • IE 地址栏和源代码中出现奇怪的字符串

    这可能是也可能不是编程问题 但我网站的一两个用户的地址栏中插入了一些奇怪的字符串 地址应该是 http URL 情侣 http URL Couple文件夹 page aspx 但有时同样的事情会变成 http 网址 http URL X 1
  • Python 模块类型输入

    我正在使用动态加载Python模块importlib import module如下 def load module mod name str gt return importlib import module mod name 有人可以告
  • 比较 MAC OSX 中 Bash 中的两个日期

    我是 Bash 新手 提前道歉 Set up 我有一个特定的结束日期end这取决于特定的开始日期s和周期长度p这样 end s p Problem 当且仅当今天的日期早于或等于结束日期时 我想执行命令 即 执行命令iffdate end C
  • 将自定义字符串转换为日期时间格式

    我有一个日期时间数据字符串列表 如下所示 list 2016 08 02T09 20 32 456Z 2016 07 03T09 22 35 129Z 我想将其转换为示例格式 对于第一项 8 2 2016 9 20 32 AM 我试过这个
  • 以编程方式格式化谷歌图表

    使用以下代码如何设置格式以便CurrencyValue1和CurrencyValue2在图表中显示为美元 作为货币值 function drawChart var data new google visualization DataTabl
  • 在 PictureBox 上绘制折线

    我想在以下位置绘制折线 由一条或多条线段组成的连续线 PictureBox 在这里 我们可以通过指定每个线段的端点来创建多条线 并计算每个线段的距离 即每条线的距离 如果您想在图片框上执行此操作 最简单的方法是从PictureBox并提供当
  • 以编程方式从我的 java webapp 读取静态资源 [重复]

    这个问题在这里已经有答案了 目前 我的 war 文件中有一堆图像 如下所示 WAR ROOT WEB INF IMAGES image1 jpg image2 jpg index html 当我通过 servlet jsp etc 生成 h
  • Firebase-perf 与 let 插件冲突

    最近 我们被要求在 Android 应用程序上实现 Firebase 性能监控 但它给我们带来了许多不同的问题 该应用程序曾经工作得很好 但是在添加 firebase perf 后 它可以编译 但在运行时我们发现让插件 https gith
  • 在 Javascript/jQuery 中解码 base64 文件以供下载

    今天我一直在尝试 SQL 二进制对象 我首先将图像存储在表中 发出 AJAX 请求以对图像进行 Base64 编码 然后使用 来显示它 img src 图像显示良好 我正在从事的网络项目也需要文件下载 主要是 PDF 太棒了 我想 我也将
  • 无法创建新的雅虎应用程序

    这个链接过去一直很挑剔 现在似乎完全失效了 https developer yahoo com apps create https developer yahoo com apps create 是不是不能再创建 Yahoo 应用程序了 N
  • PHPhotoLibrary.requestAuthorization() 在 iOS 9 上不触发授权提示

    我有以下功能 显示带有两个不同选项的操作表 目前 唯一实施的选项是标题为 照片 的选项 正确呈现操作表并调用正确的操作 我的问题是 在模拟器和实际设备上 我无法显示请求访问照片库的提示 PHPhotoLibrary authorizatio
  • 由于行高与垂直对齐冲突,文本被截断:顶部

    我有一个表格 其中包含一个表格内格式类似于文本的提交按钮 一般情况下 所有表行都设置为vertical align top 如果我不对格式化文本应用行高 则其底部部分将被切断 如以下字母所示p q等等 小提琴的底部 如果我确实将其应用行高
  • Xcode 12 - 下载更多模拟器运行时为空

    I can t add more simulator OS version in XCode 12 for example iOS 13 the list is empty 如何添加更多不同ios版本的模拟器 我遇到了完全相同的问题 苹果返
  • 以 cweb 或 noweb 样式导出代码块名称?

    在 Org 模式下编写 Lite 程序时 导出类似于在早期的 Lite 编程工具 例如 cweb 或 noweb 中编织 这些工具会将代码块名称添加到编织 导出 输出中 在组织模式下 它看起来像这样 组织文件 NAME mycodebloc
  • Firestore.firestore() 失败并显示“类型‘Firestore’没有成员‘firestore’”

    我正在尝试为我的 Firestore 设置数据库 但是我尝试重新安装 Pod 和许多其他东西 但我仍然无法让它工作 因为它显示了以下错误 Type Firestore has no member firebase 我不知道为什么会这样 因为
  • Javascript 保持 div 隐藏,直到您单击按钮,需要帮助修改

    基本上 我的代码现在隐藏了我网站上的一些 div 然后当您单击链接时 它会使 div 出现 我需要帮助 以便当我单击一个链接时出现一个 div 然后单击另一个链接时 前一个链接会消失 假设我点击 关于 链接 div 出现了 很好 然后我点击
  • 汇编中的蛇,使用什么数据结构?

    我对汇编完全陌生 不管你信不信 我们的第一个任务是在汇编中创建蛇 我应该如何储存蛇 我应该把它放在堆栈中 还是应该将它放入某个寄存器中 我已经对这种 可怕的 语言进行了大约 3 天的研究 但无法找到一个好的开始方法 我可能会在 c 中使用某