给自己一个泛泛的学习计划

睡不着,起来写下近期学习计划:(台风好像越来越大了。。)
1.汇编方面:
(1)王爽的汇编入门书
(2)《intel汇编语言程序设计第5版》(这本书的优秀使我放弃了其他基本汇编书籍)
(3)罗云彬的win32汇编
(4)《The Art of Assembly Language》这本书只能以后抽空看看片段了。。
2.操作系统方面:
《dos原理与结构》
3.数据结构
严蔚敏的书
然后是本人极其崇拜的高一凡先生的大作
4.复习数学
5.看点英语(这。。。不知道能不能做到)
远期打算:
学完win32汇编,差不多就要进入c语言sdk编程,大名鼎鼎的《windows程序设计》不得不看。
并发学习的应当是从汇编到windows内核的过渡,《天书夜读》真心不错,之后应当是真正的内核《寒江独钓》
再以后,《自己动手写一个操作系统》和《Orange’S:一个操作系统的实现》
先这样吧,这些已经够我忙n久n久了。
突然想到11月的网络工程师考试,哎,还是算了吧,,推到n久以后再说。。。
继续睡觉。。

dos研究开篇

dos是个古老的操作系统,还记得小学时代看到那个黑乎乎的窗口,就会不知所错,,,看到老师能熟练地敲进去一些字符,然后文字哗哗地往下拉,感觉好神奇。。虽然现在,dos已经不是主流操作系统,但它蕴含了操作系统的设计思想和实现过程。所以还是值得深入学习的。

我一直觉得学计算机应当扎实底层基础,做到对操作系统,甚至对电路结构相当了解,再去学习高层的东西,这样往往事半功倍。自己也一直践行这样的路线,这使我暂时中断了对php,js等web脚本的学习和运用,在汇编api编程完成前也绝不去触碰MFC之类的类库或是bcb的vcl编程,而把数电,汇编,数据结构等看做核心的核心。就像编程忌杂一样,我认为学习其他东西也应该分阶段进行,先让自己沉下去,沉到底,尽管短时间内出不了成果,但五年十年,我认为效果和差别一定会显现出来。

于是我决定将windows放到dos之后,先dos后windows,继而转入linux的内部,我想这一定会是一个漫长的过程。但我也一直坚信,落后者要想成功,只能靠苦干和坚持,这样的过程,趣味也是有的吧。
我不是“学院派”,而且始终觉得仅仅靠课堂吸收的知识远远不够,仅仅将学校的所有课程无选择地全部吸收从而获得成绩的领先或是获得奖学金,我认为这不是个明智的选择。同时我也一直认为学计算机的人,编程强才是真的强,但我依然觉得深厚的理论根基是必要的。实干主义者可能并不认同,认为这与就业无益。但我仍然要反驳,就像很多学计算机的人认为数学无用一样,我认为数学恰是成就大师级的基础,可能五年并不会显现差别,但十年二十年一定会产生差距,巨大的差距。
废话好像有点多了,就这些吧,开始dos原理学习之路。。

int指令笔记

1.进入中断例程后ds没有变,所以可以正确索引程序数据
2.进入中断例程后,通过修改栈中的ip值可以使返回到指定位置
3.bios中的内容:
(1)硬件系统的检测和初始化程序
(2)外部中断和内部中断例程
(3)用于对硬件设备进行I/O操作的中断例程
(4)其他和硬件系统有关的中断例程
4.在和硬件设备有关的dos中断例程中,一般都调用了bios的中断例程
5.bios和dos提供的中断例程如何安装到内存:
(1)开机,cpu加电,初始化cs=0ffffh,ip=0,自动从ffff:0处开始执行。ffff:0处有一条跳转指令,转去执行bios中的硬件系统检测和初始化程序。
(2)初始化程序建立bios的中断向量(注册)。
注意点:对于bios中断,只需注册,不用安装。因为在ROM中,一直在内存。
(3)int 19h 进行操作系统的引导(控制权转交OS)
(4)dos启动后,安装dos中断例程,并注册。
6.bios中断:
int 10h
(1)ah=2    设置光标
bh    页号
dh    行号
dl    列号
(2)ah=9    在光标处显示字符
al    字符
bl    颜色属性
bh    页号
cx    字符重复个数
7.dos中断:
int 21h
(1)ah=4ch    程序返回
al    返回值
(2)ah=9    在光标处显示字串
ds:dx    指向字符串(字符串用’$'结束)
注意:如果字符串过长,会自动换行;到了最后一行,会自动上卷。
8.写个7ch中断,显示用0结束的字符串。dh=行号,dl=列号,cl=颜色,ds:si指向字符串
程序功能非常简单,代码思路也很清晰,不再加注释
assume cs:code
code segment
start:
mov ax,cs
mov ds,ax
mov si,offset show
mov ax,0
mov es,ax
mov di,200h
mov cx,offset showend-offset show
cld
rep movsb

mov word ptr es:[7ch*4+2],0
mov word ptr es:[7ch*4],200h

mov ax,4c00h
int 21h

show:
push ax
push cx
push dx
push si
push di
mov ax,0b800h
mov es,ax
mov al,160
mul dh
mov dh,0
add dx,dx
add ax,dx
mov di,ax
s:        mov al,[si]
cmp al,0
jz loopend
mov es:[di],al
mov es:[di+1],cl
inc si
add di,2
jmp s
loopend:
pop di
pop si
pop dx
pop cx
pop ax
iret
showend:nop
code ends
end start
9.对实验13(3)的体会:offset可以用在数据段。。这个知识点今天才知道。。
s:    dw    offset s1,offset s2,offset s3,offset s4
似乎也是这本书第一次出现带标号的数据。。
10.接下来有必要买本中断大全了,还是纸质的有感觉。。

内中断笔记

0.操作数可以是表达式(如mov ax,(4+7/2)*5)
1.内中断源于cpu内部
2.下列情况产生内中断:
(1)除法错误(除法溢出)
(2)单步执行
(3)执行into指令
(4)执行int指令
3.中断类型码标识中断来源,为1个字节,可表示256种中断
上述中断的类型码:
(1)除法错误:0
(2)单步执行:1
(3)执行into指令:4
(4)执行int指令:int n   n是字节型立即数,为中断类型码
4.中断向量:中断处理程序的入口地址
5.中断向量表:0000:0000——0000:03ff,共1024个字节(256种*4byte/种)
6.一个中断向量为4字节,高字为cs,低字为ip。
7.中断过程:(由硬件完成)
(1)取得中断类型码n
(2)pushf
(3)TF=0,IF=0
(4)push cs
(5)push ip
(6)(ip)=(n*4),(cs)=(n*4+2)
8.中断处理程序:
(1)保存用到的寄存器
(2)处理中断
(3)恢复用到的寄存器
(4)iret返回
iret等效指令:
pop ip
pop cs
popf
9.中断向量表中0000:0200——0000:02ff的256个字节为空,可用于放代码和数据
10.下面给出重写0号中断的程序:
assume cs:code
code segment
start:
;;;;;;;;;;;;;;;;;;;安装
mov ax,cs
mov ds,ax
mov ax,0
mov es,ax
mov si,offset do0
mov di,200h
mov cx,offset do0end-do0    ;计算代码长度

cld
rep movsb
;;;;;;;;;;;;;;;;;;改写中断向量表,即注册
mov word ptr es:[0*4],200h
mov word ptr es:[0*4+2],0

mov ax,4c00h
int 21h

do0:jmp short do0start
db    ’hello shadow!’
do0start:
push ax                ;书上的程序并未保护现场,但我认为保存用到的寄存器的值更好些?
push bx
push si
push di
push cx
mov ax,cs
mov ds,ax
mov ax,0b800h
mov es,ax
mov si,202h
mov di,12*160+36*2
mov cx,13

s:    mov al,[si]
mov es:[di],al
inc si
add di,2
loop s

pop cx                ;恢复现场
pop di
pop si
pop bx
pop ax

mov ax,4c00h
int 21h
do0end:nop        ;为了计算do0的长度
code ends
end start
11.tf=1时触发单步中断,在中断过程中tf=0这步是为了防止死循环(每步执行完,cpu都会检测tf位)
12.个人认为可以通过在中断处理程序(1号中断)中置tf=1,达到循环执行某段代码的目的(死循环),本菜鸟暂时未想到这个有什么应用(恶作剧程序?。。)。。
13.注意点:执行修改ss段寄存器的指令后,cpu不响应任何中断,所以t单步后ss后的指令还是会被执行。

实验11

写了下这个实验的代码,感觉在简单汇编程序中,这个程序结构已趋于完整,所以发上来给自己作为样例,发现对cmp理解深了,写起来行云流水:

(突然觉得代码换种颜色更好看)

assume cs:code
data segment
db    ”Beginner’s All-purpose Symbolic Instruction Code.”,0
data ends
code segment
start:
    mov ax,data
    mov ds,ax
    mov si,0
    call letterc

    mov ax,4c00h
    int 21h

letterc:
begin:mov al,[si]
    cmp al,0
    je finish
    cmp al,’a’
    jb s
    cmp al,’z’
    ja s
    and al,0dfh
    mov [si],al
s:    inc si
    jmp begin

finish: ret
code ends
end start

 

df位、串传送

昨天高中同学会,今天白天又有事,所以决定把标志寄存器这章结束掉,下面是串操作笔记
movsb:
传送一个字节
相当于:
(1) ((es)*16+di)=((ds)*16+si)
(2) df=0 (si)=(si)+1
(di)=(di)+1
df=1 (si)=(si)-1
(di)=(di)-1
movsw:
传送一个字
rep movsb相当于:
s:movsb
loop s
可见rep是根据cx进行循环的

cld指令:df清零
std指令:df置1

下面写出利用movsb与rep配合copy字符串:
assume cs:code,ds:data,es:extra
data segment
db ‘welcome to masm!’
data ends
extra segment            ;事实上不需要两个段,但这样更清晰
db 16 dup (0)
extra ends
code segment
start:
mov ax,data
mov ds,ax
mov ax,extra
mov es,ax
mov si,0
mov di,0
mov cx,16
cld

rep movsb

mov ax,4c00h
int 21h
code ends
end start

书中未写出对串的其他操作,下面仅写出利用movsb倒序copy字符串的程序:
assume cs:code,ds:data,es:extra
data segment
db ‘welcome to masm!’
data ends
extra segment
db 16 dup (0)
extra ends
code segment
start:
mov ax,data
mov ds,ax
mov ax,extra
mov es,ax
mov si,0
mov di,15
mov cx,16

cld

s:    movsb            ;因为si加1的同时,di要减1,所以不能rep movsb
sub di,2        ;movsb后di加了1,这里手动减2,从而实现di减1的效果
loop s

mov ax,4c00h
int 21h
code ends
end start
接下来就是中断的学习了,事实上,到此为止的内容以前就比较熟悉,所以复习的较快,以前学中断只是水水的看了些片段,接下来要重点学习了,尽管实际上这部分对win32下的反汇编意义不大。。

adc sbb cmp与条件转移

1.adc和sbb是带进(借)位加减法,理论上可以实现任意长度数据加减法
下面是示例:
assume cs:code,ds:data
data segment
dw    1122h,3344h,5566h,7788h
dw    1020h,3040h,5060h,7080h
data ends
code segment
start:
mov ax,data
mov ds,ax
xor si,si
mov di,8
mov cx,4

sub ax,ax        ;CF清零
s:    mov ax,[di]
adc [si],ax
inc si            ;不能用add,add会影响CF,而inc与loop不会
inc si
inc di
inc di
loop s

mov ax,4c00h
int 21h
code ends
end start
2.cmp ax,bx    相当于sub ax,bx 只是仅根据结果改变标志位,并不真正运算写入
3.无符号数条件转移:
je
jne
jb
jnb
ja         zf=0并且cf=0
jna         zf=1或cf=1
4.有符号数条件转移:
JG∶——(有符号数比较)大于转移(等价JNLE)。SF和OF同号,且ZF=0 时转移。(段内直接短转移)

JGE∶ ——(有符号数比较)大于或等于转移(等价于JNL)。 当SF和OF同号,或ZF=1, 则转移(段内直接短转移)。

JL∶ ——(有符号数比较)小于转移(等价于JNGE)。当SF和OF异号,且ZF=0 时转移(段内直接短转移)。

JLE∶——(有符号数比较)小于或等于转移(等价于JNG)。当SF和OF异号或ZF=1时转移(段内直接短转移)。

JNG∶——(有符号数比较)不大于转移(等价于JLE)。当SF和OF异号或ZF=1时转移(段内直接短转移)。

JNGE∶——(有符号数比较)不大于且不等于转移(等价于JL)。当SF和OF异号,且ZF=0 时转移(段内直接短转移)。

JNL∶——(有符号数比较)不小于时转移(等价于JGE)。 当SF和OF同号,或ZF=1, 则转移(段内直接短转移)。

JNLE∶——(有符号数比较)不小于且不等于转移(等价JG)。SF和OF同号,且ZF=0 时转移。
简记口诀:(zf=0时)sf和of:同号>,异号<
5.*溢出时SF位的值和真实结果的符号相反。

链式栈

今天实现了下链式栈,比链表简单了些。

只写了其中的一部分操作

#include
#include
#include
typedef struct Node{
int data;
struct Node*pNext;
}NODE,*PNODE;
typedef struct stack{
PNODE pTop;
PNODE pBottom;
}STACK,*PSTACK;
void init(PSTACK);
bool empty(PSTACK);
void traverse(PSTACK);
void push(PSTACK,int);
int pop(PSTACK);
void clear(PSTACK);
int main()
{
int val;
STACK s;
init(&s);
if(empty(&s))
printf("the stack is emptyn");
push(&s,1);
push(&s,2);
push(&s,3);
push(&s,4);
traverse(&s);
val=pop(&s);
printf("%dn",val);
traverse(&s);
clear(&s);
if(empty(&s))
printf("the stack is emptyn");
system("pause");
return 0;
}
void init(PSTACK s)//初始化,,严格的来说是创建并初始化
{
s->pTop=(PNODE)malloc(sizeof(NODE));
if(s->pTop==NULL)
{
printf("memory errorn");
exit(-1);
}
s->pBottom=s->pTop;
s->pTop->pNext=NULL;
return;
}
bool empty(PSTACK s)
{
if(s->pTop==s->pBottom)
return true;
else
return false;
}
void traverse(PSTACK s)//遍历输出元素值
{
if(empty(s))
return;
PNODE p;
p=s->pTop;
while(p->pNext!=NULL)
{
printf("%d ",p->data);
p=p->pNext;
}
printf("n");
return;
}
void push(PSTACK s,int val)
{
PNODE p=(PNODE)malloc(sizeof(NODE));
if(p==NULL)
{
printf("memory errorn");
exit(-1);

}
p->pNext=s->pTop;
p->data=val;
s->pTop=p;
return;
}
int pop(PSTACK s)
{
int val;
PNODE p=s->pTop;
s->pTop=p->pNext;
val=p->data;
free(p);
p=NULL;
return val;
}
void clear(PSTACK s)//清空栈
{
PNODE p,q;
if(empty(s))
return;
p=s->pTop;
q=NULL;
while(p!=s->Bottom)
{
q=p->pNext;
free(p);
p=q;

}
s->pTop=s->pBottom;
return;
}