c - 使用程序集中的 getchar 获取()函数
问题描述
我在gets()
为我的一个类做的 C 代码上创建函数时遇到了一些问题。所以我已经有一个getchar()
函数,但是在汇编中,我从 C 中调用它extern
事情是,在我运行代码的那一刻,我输入了一个字符串,它没有显示完整的字符串,而是显示了一些字符。
这是我有 atm 的代码: C 代码:
extern char getchar(void);
extern void putchar(char data);
void gets(char *str);
void puts(char *str);
void new_line();
char string[20];
int main(){
while(1){
gets(string);
new_line();
puts(string);
}
return 0;
}
void new_line(){
putchar(0xD);
putchar(0xA);
}
void gets(char *str){
unsigned char i = 0;
while((*str = getchar()) != 0xD){
str[i] = getchar();
i++;
}
}
void puts(char *str){
while(*str){
putchar(*str++);
}
}
和我的 ASM 代码以防万一:
.MODEL tiny
.CODE
public _putchar
public _getchar
_putchar PROC
push bp
mov bp, sp
mov dl, [bp + 4]
mov ah, 2
int 21h
pop bp
ret
_putchar ENDP
_getchar PROC
push bp
mov bp, sp
mov ah, 1
int 21h
mov [bp + 4], al
pop bp
ret
_getchar ENDP
END
我正在使用 MTTTY 和我们老师提供的 8086 解释器在 Arduino Mega 上运行代码。
有什么办法可以用gets()函数解决这个问题,这样我就可以正确显示输入字符串?
例如,如果我输入“hello world”,它只会打印“l ol”
解决方案
gets
无论 asm实现如何,您的 C实现都已损坏getchar
。您可以使用桌面上的普通调试器在普通 C 实现上对其进行调试。
您调用getchar()
了两次,并且只保存每个第二个结果。
第一个结果分配给str[0]
并检查'\r'
.
// your version with comments
void gets_original_buggy (char *str){
unsigned char i = 0; // this is an index; it should be an `int` or `size_t`
while((*str = getchar()) != 0xD){ // overwrite the first byte of the string with an input
str[i] = getchar(); // get ANOTHER new input and save it to the end.
i++;
}
// str[i] = 0; // missing zero terminator.
}
我是这样写的:
#include <stddef.h>
//#include <stdio.h>
extern unsigned char getchar(void);
// returns length.
// negative means EOF. TODO: implement an EOF check if your getchar() supports it.
// FIXME: take a max-length arg to make it possible to prevent buffer overflows.
ptrdiff_t gets(char *str) {
char *start = str; // optional
char tmp; // read chars into a local, and check before assigning anything to *str
while( (tmp = getchar()) != '\r') {
// TODO: also check for EOF
*str++ = tmp; // classic pointer post-increment idiom
}
*str = 0; // terminate the C string.
return str - start; // optional, return the length
}
返回字符串长度而不是将其丢弃在知道它的函数中总是有用的,这只会使编译器花费一些额外的指令。指针增量简化了寻址模式,节省了代码大小。
(在 Godbolt 上使用gcc 和 clang 很好地编译 32 位 x86,对于 x86-16应该非常相似。)
您可能还/而不是检查'\n'
您的 getchar 实现,以及它是否规范化行尾。请记住,如果您有 DOS行结尾,则在读取 a 后停止\r
会留下未读。\n
"\r\n"
在 ISO C 中,getchar()
应该只'\n'
为在文本模式下打开的文件提供行尾,但您getchar
只对 DOS int 21h
/ AH=1 (READ CHARACTER FROM STANDARD INPUT, WITH ECHO) 函数做了一个包装。这就是设置实现行为的原因。
asm 错误:
# in _getchar:
mov [bp + 4], al ; clobber memory you don't own.
这将破坏返回地址上方的内存。 char getchar(void)
不带任何参数,因此您的函数不会“拥有”该内存。您的编译器应该期望 AL 中的返回值。(如果你认为这是通过引用返回的,不,你只是覆盖了指针 arg。除了调用者甚至没有传递一个。)
如果您希望getchar
能够返回与0xFF
字节不同的 EOF,请将其声明为返回int
,并在进行系统调用后将 AH 设为零。(因此您可以在 AX 中返回 16 位-1
,或在 AX 中返回零扩展unsigned char
(即 AL 中的值)。
顺便说一句,不推荐使用是有原因的,实际上在 ISO C11 中已将其删除gets()
:在读取未知长度的输入时,无法防止缓冲区溢出。
您的函数应该将大小限制作为第二个参数。
与在模拟的 8086 上使用 DOS 系统调用相比,直接对 Arduino 的 AVR 或 ARM CPU 进行编程可能更容易学习,也更有用。如果你要这样做,那么在真实硬件和模拟器。
学习 x86 作为你的第一个汇编语言是可以的,如果你不搞乱分段,并且你不尝试编写引导加载程序(A20 门有很多神秘的遗留东西,并且从实模式切换到保护模式) . DOS 系统调用完全过时,除了维护遗留代码库。学习的细节怎么不同啊=?? /int 21h
系统调用的工作原理与 COBOL 一样有用。如果您正在制作传统引导扇区(而不是 EFI), BIOSint 10h
和其他系列会稍微有用一些,但您不需要这样做来学习 asm。如果您在 Linux、Windows、Mac、*BSD 或其他任何平台下的用户空间中学习 asm,那么以后如果需要的话,就很容易理解/学习与外部世界通信的其他方式,并了解内核的工作原理。
Linux 系统调用具有类似的 ABI(eax=call number
/ int 0x80
、sysenter
或syscall
),但 Linux 系统调用或多或少是 POSIX 系统调用,了解这些对于现实世界的低级编程很有用。
POSIX TTY 行缓冲输入sys_read
的复杂性与 DOS 字符读取功能和行尾废话的复杂性不同,但可以说学习起来更有用。
推荐阅读
- javascript - Javascript/Jquery JSON 文件上传
- prolog - Prolog 存储函数的结果
- javascript - 如何使用 ES6 模板文字进行函数调用
- java - 如何将等效的 GO 代码转换为 Java 代码?
- node.js - 无法使用 res.redirect() 方法 express + NodeJS 进行重定向
- ios - bar Button Item text 在moonIcon中选择时会发生变化,如swift 4中的Font Awesome
- javascript - 在 jQuery 中附加外部 PHP 文件
- jwt - 基于 JWT 声明的 Istio Origin 授权
- selenium - 一次创建 Selenium Webdriver 并在 Cucumber 的所有步骤中使用
- python - 内连接只是逐行执行合并?