assembly - What is the correct calling convention to use within a bootloader?
问题描述
I am trying writing a bootloader and an extremely primitive and basic kernel to learn about bare metal coding practices and techniques. Anyways, I am writing my bootloader using NASM. My code is working, but I have a question about the calling conventions to use.
I am compiling my bootloader simply by running NASM: nasm bootloader.asm -o bootloader
.
In my assembly code, I have written functions like BlDisplayString
to display strings via BIOS interrupt int 0x10
with AH = 0x13
. I am trying to emulate the __fastcall
calling convention by passing the parameters in CX, DX, STACK
. Is this the correct calling convention standard to use in 16 bit code? The CPU isn't in protected mode and is still in real mode when I'm calling these functions.
解决方案
The CPU doesn't care, do whatever is convenient and maintainable. The only judge of "correctness" is you, if you're not trying to link to any code generated by a C compiler.
But yes, register args are usually a good idea, with call-clobbered AX, CX, DX. Letting ES be call-clobbered might be convenient to avoid having functions save/restore it, if you're willing to set it before every rep
-string function.
Passing args in registers that line up with where BIOS int
calls want them can maybe save some instructions in wrapper code.
You can even use a custom calling convention on a per-function basis, but that's harder to remember / document. Useful for local helper functions that are only called from one function (but multiple places in that function), or from a couple similar functions in one file. In comments, document which registers for input, output, and clobbered (used as scratch space without save/restore).
Having a couple different calling conventions for different kinds of functions is the middle ground between 1 fixed convention vs. a different one for every function.
Returning boolean conditions in FLAGS is convenient for asm, especially if you expect your caller to branch on it. Or for a function like memcmp
, ending with cmp al, dl
or whatever lets your caller branch on equality, or on greater / less, whichever FLAGS it wants to read. All of this without the cost of actually generating a + / 0 / - return value like the C function.
An answer on CodeGolf.SE Tips for golfing in x86/x64 machine code goes into more details about what you might do if you're going all out for small code without caring at all about maintainability or consistency between functions.
If you want to fit more code into a 512-byte first-stage bootloader, or into fewer extra sectors, you can often save some bytes without hurting readability. Fewer instructions is generally easier to read. (That's not always the same thing as smaller machine-code size, though.)
推荐阅读
- sas - 在 SAS 中创建重复行并更改变量的值
- python - 数据质量 仅限数字列
- php - XMLHttpRequest() 有“此 XML 文件似乎没有任何与之关联的样式信息”错误
- c++ - 带有边界的拉普拉斯方程的 C++ 输出错误
- python - Pyomo:多维索引
- ansible - 我可以在 Ansible 任务中的特定主机或主机组上运行吗?
- stored-procedures - DB2 存储过程 - 更改表结构
- javascript - 如果 div 中有超过 2 行,则执行函数
- python - 如何在多行上旋转并同时在熊猫中创建计算列
- javascript - 带有 JavaScript 的可填写 adobe pdf 并发送给其他用户填写