使用汇编中的getchar获取()函数

Spa*_*ift 1 c assembly dos x86-16

我在为gets()我的一个类做的C代码上创建一个函数时遇到了一些问题.所以我已经有一个getchar()功能,但在汇编,我从C调用它extern的东西是,目前我正在运行代码我输入一个字符串,它不显示完整的字符串而不是一些字符.

这是我的代码: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++);
    }
}
Run Code Online (Sandbox Code Playgroud)

和我的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
Run Code Online (Sandbox Code Playgroud)

我正在运行Arduino Mega上的代码,使用MTTTY和我们老师提供的8086解释器.

任何方式我可以用gets()函数解决这个问题,所以我可以正确显示输入字符串?

例如,如果我输入"hello world",它只打印"l ol"

Pet*_*des 6

gets无论asm getchar实现如何,您的C 实现都会被破坏.您可以使用桌面上的普通调试器在普通的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.
}
Run Code Online (Sandbox Code Playgroud)

这是我写的方式:

#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
}
Run Code Online (Sandbox Code Playgroud)

返回字符串长度而不是将其丢弃在知道它的函数中总是有用的,这只会给编译器带来一些额外的指令.指针增量简化了寻址模式,节省了代码大小.

(与Godbolt上的32位x86的gcc和clang很好地编译,对于x86-16应该非常相似.)

您也可以/而是检查'\n'取决于您的getchar实现,以及它是否规范化行结尾.并且请记住,如果您有DOS 行结尾,那么在读取之后停止\r会留下\n未读的内容"\r\n".

在ISO C中,getchar()应该只'\n'为文本模式下打开的文件提供行结尾,但是你getchar只是在DOS int 21h/ AH = 1(READ STACTER FROM STANDARD INPUT,WITH ECHO)函数中做了一个包装器.这就是设置实现行为的原因.

asm bug:

# in _getchar:
    mov [bp + 4], al         ; clobber memory you don't own.
Run Code Online (Sandbox Code Playgroud)

这将破坏返回地址之上的内存. char getchar(void)没有任何args,所以你的函数不"拥有"那个记忆.您的编译器应该期望AL中的返回值.(如果你认为那是通过引用返回的,不,你只是覆盖指针arg.除了调用者甚至没有传递一个.)

如果您希望getchar能够返回与0xFF字节不同的EOF ,请将其声明为返回int,并在进行系统调用后将其归零.(因此,您可以返回-1AX中的16位,或AX中的零扩展unsigned char(即AL中的值).


顺便说一下gets(),有一个原因被弃用了,并且在ISO C11中实际上被删除:当读取未知长度的输入时,不可能防止缓冲区溢出.

你的函数应该作为第二个arg的大小限制.


编程一个Arduino的AVR或ARM CPU 直接可能会更容易学习,更有效,比使用DOS系统的仿真8086调用如果你要做到这一点,有一个在做它在真实的硬件与一个没有点模拟器.

学习x86作为你的第一个汇编语言是好的,如果你不搞乱分段,你不会尝试编写一个bootloader(A20门有很多古怪的遗留东西,从真实模式切换到保护模式) .除了维护遗留代码库之外,DOS系统调用完全过时了.学习不同AH的细节如何?/ int 21hsystem调用工作与COBOL一样有用.int 10h如果您正在制作传统引导扇区(而不是EFI),则BIOS 和其他系列稍微有用,但您不需要这样做来学习asm.如果您在Linux,Windows,Mac,*BSD或其他任何领域的用户空间中学习asm,那么以后可以很容易地理解/学习与外部世界通信的其他方式(如果您需要),并了解内核的工作方式.

Linux系统调用具有类似的ABI(eax=call number/ int 0x80,sysentersyscall),但Linux系统调用或多或少是POSIX系统调用,对于实际的低级编程而言,它是有用的.

POSIX TTY行缓冲输入sys_read的复杂性不同于DOS字符读取功能和行尾废话的复杂性,但可以说更有用.