学习笔记

X86学习笔记二 字符串修改

2019-04-16  本文已影响0人  闭门造折

今天的任务是对字符串实现增、删、改、查等等操作

假设数据空间总长度为M,已经占去部分为N。
增操作有两种:
1、在数据末尾添加新的数据段
2、在某个字符串中间添加新的数据段。
前者很简单,不存在覆盖问题。后者需要先移动后续内容,再将新内容覆盖进去。

删操作也有两种:
1、删除末尾字符串
2、删除中间字符串
后者存在无用空白问题,需要将后续内容依次前移

改操作分为三种:
1、新的字符串长度<旧的字符串长度
2、新的字符串长度=旧的字符串长度
3、新的字符串长度>旧的字符串长度
第一类需要将后续内容前移,第三类需要提前将后续内容后移

查操作比较简单,从开始到末尾顺序筛查即可,可以设计为报多个坐标,也可以设计为只报出现总数或头一个的位置

首先是改操作,由于笔者很懒,所以并没有将后续内容依次前移,而是留下了一些内容渣渣。也没有考虑提前将内容后移,通过在readme中写明输入要求,降低了自己的工作量。
主体的main函数

STACK   SEGMENT PARA    STACK
        DW  100H DUP(?)        ;不需要栈空间顶部地址
STACK   ENDS
            
DATA    SEGMENT PARA
STRING1 DB  '15061125 ZHAOZE',00H        ;随意的两个字符串变量
STRING2 DB  'STRING BEFORE STRCPY',00H
DATA    ENDS

CODE    SEGMENT PARA
        ASSUME  CS:CODE, DS:DATA, SS:STACK, ES:DATA

MAIN    PROC    FAR
        MOV     AX,DATA
        MOV     DS,AX
        MOV     ES,AX           ;ES用于STOSB操作

        CALL    DISP_STR        ;无参数调用,所以不用参数压栈

        MOV     DX,OFFSET STRING2
        PUSH    DX              ;第二个参数,字符串2的地址偏移
        MOV     DX,OFFSET STRING1
        PUSH    DX              ;第一个参数,字符串1的地址偏移
        CALL    FAR PTR STRCPY  ;调用修改函数
        CALL    DISP_STR        ;调用输出函数

        MOV     AX,4C00H        ;结束
        INT     21H

输出函数就不贴代码了,仅贴修改函数的代码。由于已经降低了很多难度,所以逻辑看起来非常的简单

STRCPY  PROC    FAR
        PUSH    BP        ;把此时的BP值压栈
        MOV     BP,SP     ;将SP的值传给BP:由于后续有栈操作
                          ;   SP的值可能改变,但是参数位置又是固定的
                          ;   所以用BP过渡
        PUSH    DI        ;函数中要用到临时寄存器DI,SI,将初值保留
        PUSH    SI

        MOV     SI,[BP+6]   ;BP=初始时SP位置。BP+6是第一个参数
        MOV     DI,[BP+8]   ;BP+8是第二个参数
        CLD                 ;时钟置1
STRC:
        LODSB
        STOSB
        CMP     AL,0
        JNZ     STRC
        POP     SI         ;还原现场
        POP     DI
        POP     BP
        RET     4          ;栈空间还原,返回2N,N为参数个数
STRCPY  ENDP

这次的代码与上次相比,涉及到了两个新的知识点
第一个是LODSB和STOSB,
对应的修改在main函数中也有体现,MOV ES,AX。即将额外段也指向DATA
LODSB = 从DS:[SI]地址处取一个字节,存入到AL中,并且SI++
STOSB = 将AL中的内容,存到ES:[DI]所指向的那个字节,且DI++
需要注意的是,ES:[DI],指的是以ES为基地址,偏移量为DI的位置,所以需要设置ES的初始值
CLD表示的是时钟为1,每次SI和DI自增的值为1而不是2

第二个是参数压栈。了解过栈知识,并实现过编译器的同学应该知道,参数压栈的时候应该逆序压栈,如此才能正序调用。

堆栈 SP的值
...
BP 2K-10
跳转前IP地址 2K-8
跳转前CS值(段内调用不需要存入) 2K-6
参数1 2K-4
参数2 2K-2
... 2K

当我们执行压栈操作的时候,此时SP指向的是2K
压入参数2,SP指向2K-2
压入参数1,SP指向2K-4
执行CALL指令,压入CS和IP,SP指向2K-8
进入函数内部,压入BP,SP指向2K-10
将此时SP的值赋给BP,BP=2K-10
则BP+6 = 2K-4,取出参数1
BP+8 = 2K-2,取出参数2
RET时,除了自动释放的4个栈空间,还需要释放参数所占空间2N

上一篇 下一篇

猜你喜欢

热点阅读