读书笔记:汇编语言 第三版 王爽 清华出版社 章十六 章十七 章十八

第十六章 直接定址表
    16.1 描述了单位长度的标号
        地址标号,表征了位置的偏移地址
            label:
        数据标号,表征了一段内存空间的物理地址和长度,增强型地址标号
            段地址,数据标号所在段的关联段寄存器,assume
                注意要提前准备好相关的段寄存器
            偏移地址,地址标号
            长度,伪指令,[db,dw,dd]
            使用
                定义
                    assume ds:data
                    data segment
                        label dw idata1, idata2
                    data ends
                直接使用
                    mov ax, label
                    等价于
                        mov word ptr ax, ds:[offset label]
                偏移使用
                    mov ax, label[2]
                    mov ax, label[si]
                    等价于
                        mov word ptr ax, ds:[offset label+2]
                        mov word ptr ax, ds:[offset label+si]
    16.2 在其他段中使用数据标号
        数据标号,和地址标号一样支持伪指令
        offset,获取标号的偏移地址
        seg,获取标号的段地址
    16.3 直接定址表
        任务,打印字节的十六进制表示,如256表示为FF
            一个字节可以拆成,高四位和低四位,各表示成一个字节
            如何将一个字节(00~0F)表示为一个字符('0'~'F')
                逻辑比较
                    bx=0~9        ax=bx+30h
                    bx=a~f        ax=bx-a+'a'
                查表法
                    table db '0123456789ABCDEF'
                    mov ax, table[bx]
        利用表,建立下标到数据集合的一种映射关系,便于根据下标获取映射数据
            目的
                为了算法的清晰和简洁
                为了加快运算速度,以空间换时间
                为了使用程序易于扩充,代码变化度分离,开闭原则
        查表法
            用查表的依据数据,直接计算出目标元素的位置
            用于查表法的表,称为直接定址表
    16.4 程序入口地址的字节定址表
        任务
            一个子程序,有多个次级子程序构成
            根据参数,自动调用次级子程序
        方案一,查表法
            fun1:
                jmp short fun1_start
                table dw sub1, sub2, sub3
            fun1_start:
                push bx
                cmp ah, 2
                ja fun1_ret
                mov bl, ah
                mov bh, 0
                add bx, bx
                call word ptr table[bx]
            fun1_ret:
                pop bx
                ret
        方案二,逻辑比较法
            fun1:
                cmp ah, 0
                jne fun1_1
                call  sub1
                jmp fun1_ret
            fun1_1:
                cmp ah, 1
                jne fun1_2
                call  sub2
                jmp fun1_ret
            fun1_2:
                cmp ah, 2
                jne fun1_ret
                call  sub2
            fun1_ret:
                ret
    16.5 实验16 编写包含多个功能子程序的中断例程

第十七章 使用BIOS进行键盘输入和磁盘读写
    17.1 int9中断例程对键盘输入的处理
        数据结构
            键盘缓冲区,16个字,15个输入,一个输入包含一个扫描码及其对应的ASCII码
                键盘缓冲区以循环队列实现
                头指针和尾指针,可能存放在剩下的字中
            状态字节,40:17
        行为
            按下字符键
                读出通码,从60h端口
                存入尾指针,尾指针自增
                产生ASCII码,根据状态字节
                存入尾指针,尾指针自增
            松开字符键
                忽略
            按下控制键
                更新状态字节
            松开控制键
                更新状态字节
        概述
            int9中断例程,将当前输入循环写入字符缓冲区
    17.2 使用int16h中断例程读取键盘缓冲区
        例子
            mov ah, 0
            int 16h
        解释
            int16h中断例程中的0号子程序,用于从键盘缓冲区中读取一个键盘输入
            读取的流程
                检测键盘缓冲区,直到有数据
                读取头指针指向的数据,ASCII码进入AL,扫描码进入AH
                头指针循环自增
        在int16h中断例程中,一定设有IF=1的指令
            在等待键盘缓冲区中有数据时,需要产生键盘输入中断,使得int9h中断例程向键盘缓冲区送入键盘输入
    17.3 字符串的输入
        字符串输入程序
            调用int16h读取键盘输入
            若是字符,则压入字符栈,显示字符栈中所有数据
            若是退格键,则字符栈弹出数据,显示字符栈中所有数据,注意擦除上一次的显示
            若是回车键,则将0压入字符栈,返回
        例子
            ; sub-program, char stack push, pop, show
            ; parameters
            ;    ah, function index
            ;        0, push
            ;        1, pop
            ;        2, show
            ;    ds:si, char stack address
            ;    al
            ;        fun0, char will push
            ;        fun1, char will pop
            ;    dh, dl, row and column for show 
            char_stack:
                jmp short char_stack_start
                table dw char_stack_push, char_stack_pop, char_stack_show
                top dw 0
            char_stack_start:
                push bx

                cmp ah, 2
                ja char_stack_ret
                mov bl, ah
                mov bh, 0
                add bx, bx
                jmp word ptr table[bx]

            char_stack_push:
                mov bx, top
                mov [si][bx], al
                inc top
                jmp char_stack_ret

            char_stack_pop:
                cmp top, 0
                je char_stack_ret
                dec top
                mov bx, top
                mov al, [si][bx]
                jmp char_stack_ret

            char_stack_show:
                push di
                push es
                push ax
                push dx
                push cx

                mov bx, 0b800h
                mov es, bx
                mov ah, 0
                mov al, 160
                mul dh
                mov dh, 0
                add ax, dx
                add ax, dx
                mov di, ax

                mov cx, top
                jcxz char_stack_show_e
                mov bx, 0
            char_stack_show_s:
                mov al, [si][bx]
                mov es:[di], al
                add di, 2
                inc bx
                loop char_stack_show_s
            char_stack_show_e:
                mov byte ptr es:[di], ' '

                pop cx
                pop dx
                pop ax
                pop es
                pop di

            char_stack_ret:
                pop bx
                ret

            ; read string
            ; sub-program, char stack push, pop, show
            ; parameters
            ;    ds:si, char stack address
            get_str:
                push ax
            
            get_str_s:
                mov ah, 0
                int 16h

                cmp al, 20h
                jb get_str_not_char
                mov ah, 0
                call char_stack
                mov ah, 2
                call char_stack
                jmp short get_str_s
            
            get_str_not_char:
                cmp ah, 0eh
                je get_str_backspace
                cmp ah, 1ch
                je get_str_enter
                jmp short get_str_s

            get_str_backspace:
                mov ah, 1
                call char_stack
                mov ah, 2
                call char_stack
                jmp short get_str_s

            get_str_enter:
                mov al, 0 
                mov ah, 0
                call char_stack
                mov ah, 2
                call char_stack

                pop ax
                ret
        注意
            mov es:[di], ' ' 
            这句话不会报错,但是会按字进行传送,导致逻辑错误
            直接运行和debug运行结果不一样,因为debug中操作的显存一致在变化
    17.4 使用int13h中断例程对磁盘进行读写
        3.5英寸软盘
            一共上下两面,一面一个磁头
            每面80个磁道
            每个磁道18个扇区
            每个扇区512字节
            软盘共计1440KB,大约1.44MB
            一盘共两面,一面八十道,一道十八区,一区五一二
        磁盘的实际访问,由磁盘驱动程序完成,我们通过指令控制磁盘控制器来访问磁盘
            直接操作磁盘控制器,涉及许多硬件细节,可以使用BIOS中的13h中断例程
            磁盘访问
                以扇区为单位
                读写扇区,需要给出面号、磁道号、扇区号
                    面号和磁道号,从0开始
                    扇区号,从1开始
            int13h
                参数
                    ah  功能号,2为读扇区,3为写扇区
                    al  读写的扇区数
                    dl  驱动器号
                        软驱从0开始,0为软驱A,1为软驱B
                        硬盘从80h开始,80h为硬盘C,81h为硬盘D
                    dh  磁头号
                    ch  磁道号
                    cl  扇区号
                    es:bx  读写扇区的内存区
                返回值
                    成功  ah=0  al=读写的扇区数
                    失败  ah=出错代码
            例子1
                将0面0磁道1扇区,读到0:200h
                assume cs:code

                stack segment stack
                    db 128 dup (0)
                stack ends
                
                data segment
                start:
                    mov ax, 0
                    mov es, ax
                    mov bx, 200h
                    mov ah, 2
                    mov al, 1
                    mov dl, 0
                    mov dh, 0
                    mov ch, 0
                    moc cl, 1
                    int 13h
                data ends

                end start
            例子2
                将当前屏幕数据写入软盘
                assume cs:code

                stack segment stack
                    db 128 dup (0)
                stack ends
                
                data segment
                start:
                    mov ax, 0b800h
                    mov es, ax
                    mov bx, 0
                    mov ah, 3
                    mov al, 8
                    mov dl, 0
                    mov dh, 0
                    mov ch, 0
                    moc cl, 1
                    int 13h

                    mov ax, 4c00h
                    int 21h
                data ends

                end start
            结果
                读取成功
                写入似乎失败了,0~FFF全是FF
    17.5 实验17 编写包含多个功能子程序的中断例程
    17.6 课程设计2
        材料
            开机后,CPU的CS:IP被初始化为FFFF:0,在该处有一条跳转指令
            跳转去执行BIOS中的硬件系统检测和初始化程序
                建立BIOS支持的中断向量表
            调用int19h进行操作系统的引导
                若从0号软驱启动
                    读取其0面0道1扇区的数据(操作系统引导程序),到0:7c00
                    跳转到0:7c00
                    执行程序从而激活操作系统
                若0号软驱没有软盘,或发生软盘IO错误
                    读取硬盘C的0面0道1扇区的数据,到0:7c00
                    跳转到0:7c00,引导操作系统

第十八章 综合研究
    目的
        启示如何进行独立研究和深度思考
        认识到汇编语言对于深入理解其他领域知识的重要性
        融会贯通汇编语言知识
        体验用研究的方法进行学习
    打破常规,怀疑常见,提升认识
    研究试验1 搭建一个精简的C语言开发环境
        只使用必要的文件
            复制tc.exe
            清空目录配置
            编译连接测试,复制依赖的文件
        例子程序
            main()
            {
                prnitf("Hello world!\n");
            }
    研究试验2 使用寄存器
        例1
            main()
            {
                _AX = 1;
                _BX = 1;
                _CX = 1;
                _AX = _BX + _CX;
                _AH = _BL + _CL;
                _AL = _BH + _CH;
            }
            debug查看其代码
        例2
            main()
            {
                _AX = 1;
                _BX = 1;
                _CX = 1;
                _AX = _BX + _CX;
                _AH = _BL + _CL;
                _AL = _BH + _CH;
                printf("%x\n", main);
            }
            打印main函数在代码段中的偏移地址,查看C代码和汇编代码的对应关系
            main的入口地址为1fa
        例3
            void f(void);

            main()
            {
                _AX = 1;
                _BX = 1;
                _CX = 2;

                f();
            }

            void f(void)
            {
                _AX = _BX + _CX;
            }
            main的入口地址为1fa
            函数main和f都被编译为子程序
            部分汇编代码
                208A:01FA   55        PUSH BP
                208A:01FB   8BEC      MOV BP, SP
                208A:01FD   B80100    MOV AX, 0001
                208A:0200   BB0100    MOV BX, 0002
                208A:0203   B90200    MOV CX, 0002
                208A:0206   E80200    CALL 020B
                208A:0209   5D        POP BP
                208A:020A   C3        RET
                208A:020B   55        PUSH BP
                208A:020C   8BEC      MOV BP, SP
                208A:020E   8BC3      MOV AX, BX
                208A:0210   03C1      ADD  AX, CX
                208A:0212   5D        POP BP
                208A:0213   C3        RET
    研究试验3 使用内存空间
        存储空间的信息
            位置
            长度
        寄存器
            位置,寄存器名称
            长度,寄存器名称
        内存空间
            位置,首地址信息
            长度,空间存储数据的类型信息
            例子
                *(char *)0x2000='a'
                    将(char *)0x2000作为内存空间信息,段地址为ds,偏移地址为0x2000,类型为char
                    语句含义,向内存空间((char *)0x2000)写入'a'
                *(char far *)0x20000000='a'
                    将(char far *)0x20000000作为内存空间信息,段地址为0x2000,偏移地址为0,类型为char
                    语句含义,向内存空间((char *)0x20000000)写入'a'
                用地址直接访问内存空间是不安全的,可能会覆盖重要数据
            例1
                mian()
                {
                    *(char *)0x2000 = 'a';
                    // mov ax, 0
                    // mov es, ax
                    // mov byte ptr es:[2000], 'a'
                    *(int *)0x2000 = 0xf;
                    // mov word ptr es:[2000], 0fh
                    *(char far *)0x20001000 = 'a';
                    // mov ax, 02000h
                    // mov es, ax
                    // mov byte ptr es:[1000], 'a'
                    _AX = 0x2000;
                    // mov ax, 02000h
                    *(char *)_AX = 'b';
                    // mov bx, 0
                    // mov es, bx
                    // mov bx, ax
                    // mov byte ptr es:[bx], 'b'

                    _BX = 0x1000;
                    // mov bx, 01000h
                    *(char*)(_BX+_BX) = 'a';
                    // add bx, bx
                    // mov byte ptr es:[bx], 'a'

                    *(char far *)(0x20001000+_BX) = *(char *)_AX;
                    // mov cx, 0
                    // mov es, cx
                    // mov bp, ax
                    // mov dl, es:[bp]

                    // mov cx, 02000h
                    // add bx, 01000h
                    // jnc no_carry
                    // add cx, 0100h
                    // no_carry:
                    // mov es, cx
                    // mov es:[bx], dl
                }

                汇编代码
                    *(char *)0x2000 = 'a';
                    // mov byte ptr [2000], 61h
                    *(int *)0x2000 = 0xf;
                    // mov word ptr [2000], 0fh
                    *(char far *)0x20001000 = 'a';
                    // mov bx, 02000h
                    // mov es, bx
                    // mov bx, 01000h
                    // mov byte ptr es:[bx], 61h
                    _AX = 0x2000;
                    // mov ax, 2000h
                    *(char *)_AX = 'b';
                    // mov bx, ax
                    // mov byte ptr [bx], 62h

                    _BX = 0x1000;
                    // mov bx, 01000h
                    *(char*)(_BX+_BX) = 'a';
                    // add bx, bx
                    // mov byte ptr [bx], 61h

                    *(char far *)(0x20001000+_BX) = *(char *)_AX;
                    // mov bx, ax
                    // mov al, [bx]
                    // xor cx, cx
                    // add bx, 1000h
                    // adc cx, 2000h
                    // mov es, cx
                    // mov es:[bx], al
            例2
                在屏幕中间打印绿色a
                *(int far *)0xb80007d0 = 0x0261;
            例3
                int a1, a2, a3;
                void f(void);
                main()
                {
                    int b1, b2, b3;
                    a1 = 0xa1; a2 = 0xa2; a3 = 0xa3;
                    b1 = 0xb1; b2 = 0xb2; b3 = oxb3;
                }
                void f(void)
                {
                    int c1, c2, c3;
                    a1 = 0x0fa1; a2 = 0x0fa2; a3 = 0x0fa3;
                    c1 = 0xc1; c2 = 0xc2; c3 = 0xc3;
                }
                汇编代码
                    push bp
                    mov bp, sp
                    sub sp, 6
                    mov word ptr ds:[01a6h], 0a1h
                    mov word ptr ds:[01a8h], 0a2h
                    mov word ptr ds:[01aah], 0a3h
                    mov word ptr ss:[bp-6], 0b1h
                    mov word ptr ss:[bp-4], 0b2h
                    mov word ptr ss:[bp-2], 0b3h
                    mov sp, bp
                    pop bp
                    ret
                    push bp
                    mov bp, sp
                    sub sp, 6
                    mov word ptr ds:[01a6h], 0fa1h
                    mov word ptr ds:[01a8h], 0fa2h
                    mov word ptr ds:[01aah], 0fa3h
                    mov word ptr ss:[bp-6], 0c1h
                    mov word ptr ss:[bp-4], 0c2h
                    mov word ptr ss:[bp-2], 0c3h
                C语言将全局变量放在数据段,将局部变量放在栈中
            例4
                int f(void);
                int a, b, ab;
                main()
                {
                    int c;
                    c = f();
                }
                int f(void)
                {
                    ab = a + b;
                    return ab;
                }
                汇编代码
                            push bp
                            mov bp, sp
                            sub bp, 2
                            call 020a
                            mov ss:[bp-2]. ax
                            mov sp, bp
                            pop bp
                            ret
                    020a    push bp
                            mov bp, sp
                            mov ax, ds:[01a6]
                            add ax, ds:[01a8]
                            mov ds:[01aa], ax
                            mov ax, ds:[01aa]
                            jmp 021c
                    021c    pop bp
                            ret
                C语言将函数返回值放在寄存器AX中
            例5
                #define Buffer ((char *)*(int far *)0x200)
                main()
                {
                    Buffer = (char *)malloc(20);
                    Buffer[10] = 0;
                    while (Buffer[10] != 8)
                    {
                        Buffer[Buffer[10]] = 'a' + Buffer[10];
                        Buffer[10]++;
                    }
                    free(Buffer);
                }
                解释
                    *(int far *)0x200
                        地址为200:0的int型内存空间
                    ((char *)*(int far *)0x200)
                        将int型内存空间,转化为char*型内存空间,用于存放char数组首地址
                汇编代码
                    push bp
                    mov bp, sp
                    mov ax, 14h
                    push ax
                    call 04ddh
                    pop cx
                    
                    xor bx, bx
                    mov es, bx
                    mov bx, 200h
                    mov es:[bx], ax

                    xor bx, bx
                    mov es, bx
                    mov bx, 200h
                    mov bx, es:[bx]
                    mov byte ptr [bx+10], 0
                    
                    jmp 025bh
                    
                    xor bx, bx    ;021fh
                    mov es, bx
                    mov bx, 200h
                    mov bx, es:[bx]
                    mov al, [bx+10]
                    
                    add al, 'a'
                    
                    xor bx, bx
                    mov es, bx
                    mov bx, 200h
                    mov bx, es:[bx]

                    push ax
                    push bx
                    
                    xor bx, bx
                    mov es, bx
                    mov bx, 200h
                    mov bx, es:[bx]
                    mov al, [bx+10]
                    CBW

                    pop bx
                    add bx, ax

                    pop ax
                    mov [bx], al
                    
                    xor bx, bx
                    mov es, bx
                    mov bx, 200h
                    mov bx, es:[bx]
                    inc byte ptr [bx+10]
                    
                    xor bx, bx    ;025bh
                    mov es, bx
                    mov bx, 200h
                    mov bx, es:[bx]
                    cmp byte ptr [bx+10], 8
                    jnz 021f

                    xor bx, bx
                    mov es, bx
                    mov bx, 200h
                    push es:[bx]
                    call 06e8h
                    call cx

                    pop bp
                    ret
    研究试验4 不用main函数编程
        试验1
            // t41.c
            f()
            {
                *(char far *) (0xb8000000+12*160+40*2) = 'a'
                *(char far *) (0xb8000000+12*160+40*2 + 1) = 2
            }
            使用TC编译成功,连接失败
                Link Error: Undefined symbol '_main' in module C0S
                C0S.obj中调用了_main函数,但是找不到定义
        试验2
            使用link.exe连接t41.obj成功
            汇编代码
                push bp
                mov bp, sp
                mov bx, 0b800h
                mov es, bx
                mov bx, 07d0h
                mov byte ptr es:[bx], 'a'
                mov bx, 0b800h
                mov es, bx
                mov bx, 07d1h
                mov byte ptr es:[bx], 2
                pop bp
                ret
                一共1d字节(CX)
            C语言函数被编译为子程序,子程序结尾使用ret,而不是int21h,不能正确返回
            f函数被编译在代码段首地址,偏移地址为0
        试验3
            将f函数重命名为main,编译连接成功
            程序长度为0ebe字节
        试验4
            调用main函数的指令的位置,208c:011a
                可以打印出main的偏移地址1fa
                执行程序到main函数开头,g 1fa
                查看栈顶保存的返回地址011d
                反汇编调用指令,u cs:011a
            程序的返回指令位置,208c:0151
                在main函数调用位置,继续往下反汇编
                找到"INT 21"
        试验5
            对main函数的调用指令和程序返回指令来自于C0S.obj
            对C0S.obj单独进行连接,会出现连接错误,但仍然生成了C0S.exe,使用debug查看
                调用main函数的指令的位置相同
                程序返回指令的位置相同
            tc.exe会将C0S.obj和用户的obj进行连接,程序运行过程如下
                C0S.obj中的程序作为程序入口,被首先运行,会进行相关的初始化
                    申请资源、设置DS和SS等
                C0S.obj调用main函数,开始执行用户程序
                用户程序返回,C0S.obj接着执行
                    资源释放,环境恢复
                C0S.obj调用int21h的4ch子程序,程序返回
            在C语言中,main函数算是一个回调函数,是C语言开发系统和用户程序进行衔接的调用约定
                C语言开发系统,会在调用main前,进行系统的初始化,在调用main后,进行系统的资源释放和环境恢复
                C语言开发系统把这组程序,放在了C0S.obj中
            自定义C0S.obj,并连接用户程序t41.obj
                编译C0S.asm
                    assume cs:code
                    data segment stack
                        db 128 dup (0)
                    data ends
                    code segment
                    start: call s
                            mov ax, 4c00h
                            int 21h
                    s:
                    code ends
                    end start
                TC连接并执行成功
                汇编代码
                    call 8
                    mov ax, 4c00h
                    int 21h
                    push bp
                    mov bp, sp
                    mov bx, 0b800h
                    mov es, bx
                    mov bx, 07d0h
                    mov byte ptr es:[bx], 'a'
                    mov bx, 0b800h
                    mov es, bx
                    mov bx, 07d1h
                    mov byte ptr es:[bx], 2
                    pop bp
                    ret
                可以发现,连接后,t41.obj代码段中的指令直接跟在C0S.obj的代码段尾
            使用C0S.obj,并连接新用户程序
                #define Buffer ((char *)*(int far *)0x200)
                main()
                {
                    Buffer = 0;
                    Buffer[10] = 0;
                    while (Buffer[10] != 8)
                    {
                        Buffer[Buffer[10]] = 'a' + Buffer[10];
                        Buffer[10]++;
                    }
                }
    研究试验5 函数如何接收不定数量的参数
        试验1
            t51.c
                void show_char(char a, int b);

                main()
                {
                    show_char('a', 2);
                }

                void show_char(char a, int b)
                {
                    *(char far *) (0xb8000000+12*160+40*2) = a;
                    *(char far *) (0xb8000000+12*160+40*2 + 1) = b;
                }
            汇编代码
                    push bp
                    mov bp, sp
                    
                    mov ax, 2
                    push ax
                    mov ax, 'a'
                    push ax
                    call show_char
                    pop cx
                    pop cx

                    pop bp
                    ret
                show_char:
                    push bp
                    mov sp, sp

                    mov al, [bp+4]
                    mov bx, 0b800h
                    mov es, bx
                    mov bx, 07d0h
                    mov es:[bx], al

                    mov al, [bp+6]
                    mov bx, 0b800h
                    mov es, bx
                    mov bx, 07d1h
                    mov es:[bx], al

                    pop bp
                    ret
            函数调用过程
                将函数参数,从右到左,依次入栈
                call 函数标号
                    call的下一指令地址入栈
                函数调用
                    bp入栈
                    bp = sp
                    sp调整,为局部变量分配空间
                    程序逻辑
                    sp = bp
                    bp出栈
                    ret
                        call的下一指令地址出栈
                函数参数出栈
        试验2
            // t52.c
                void show_char(int, int, ...);
                main()
                {
                    show_char(8,2,'a','b','c','d','e','f','g','h');
                }
                void show_char(int n, int color, ...)
                {
                    int a;
                    for (a = 0; a < n; ++a)
                    {
                        *(char far *) (0xb8000000+12*160+40*2+a+a) = *(int *)(_BP+8+a+a);
                        *(char far *) (0xb8000000+12*160+40*2+a+a+1) = color;
                    }
                }
            汇编代码
                    push bp
                    mov bp, sp

                    mov ax, 68h
                    push ax
                    mov ax, 67h
                    push ax
                    mov ax, 66h
                    push ax
                    mov ax, 65h
                    push ax
                    mov ax, 64h
                    push ax
                    mov ax, 63h
                    push ax
                    mov ax, 62h
                    push ax
                    mov ax, 61h
                    push ax
                    mov ax, 2
                    push ax
                    mov ax, 8
                    push ax
                    call show_char
                    add sp, 14h
                    
                    pop bp
                    ret
                show_char:
                    push bp
                    mov bp, sp
                    push si

                    xor si, si
                    jmp show_char_while_cmp
                
                show_char_while_start:
                    mov bx, bp
                    add bx, 8
                    inc bx
                    inc bx
                    mov al, [bx]

                    push ax
                    mov ax, si
                    cwd
                    push dx
                    push ax
                    mov ax, si
                    cwd
                    pop bx
                    pop cx
                    add bx, ax
                    adc cx, dx
                    add bx, 7d0h
                    adc cx, 0b800
                    mov es, cx
                    pop ax
                    mov es:[bx], al

                    mov al, [bp+6]

                    push ax
                    mov ax, si
                    cwd
                    push dx
                    push ax
                    mov ax, si
                    cwd
                    pop bx
                    pop cx
                    add bx, ax
                    adc cx, dx
                    add bx, 7d1h
                    adc cx, 0b800
                    mov es, cx
                    pop ax
                    mov es:[bx], al

                    inc si
                show_char_while_cmp:
                    cmp si, [bp+4]
                    jl show_char_while_start

                    pop si
                    pop bp
                    ret
        试验3
            void my_printf(char * f, ...)
            {
                int a, b;
                for (a = 0, b = 0; ; a+=2)
                {
                    if (*f == 0) break;
                    if (*f == '%' && *(f+1) == 'c')
                    {
                        *(char far *) (0xb8000000+12*160+40*2+a) = *(int *)(_BP+6+b);
                        f += 2;
                        b += 2;
                    }
                    else
                    {
                        *(char far *) (0xb8000000+12*160+40*2+a) = *f;
                        ++f;
                    }
                }
            }
            main()
            {
                my_printf("Hi %c%c, welcome", 'W', 'R');
            }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值