[010][x86汇编语言]学习用户程序的编写(c08.asm)

源程序来源

https://www.jianshu.com/p/72c151606908

加载程序

  • c08_mbr.asm

用户源程序:增加注释

;
;文件名:c08-2.asm
;文件说明:用户程序
;创建日期:13:08 2018/5/23

;----------------------------------------------------------------------
SECTION header vstart=0                 ;定义用户程序头部段
    program_length  dd  program_end     ;程序总长度[0x00]
    
    ;用户程序入口点
    code_entry      dw  start                   ;偏移地址[0x04] 此处的start来源于自己的命名
                    dd  section.code_1.start    ;段地址[0x06] 此处.start是汇编指令的语法
                        
    realloc_tbl_len dw  (header_end - code_1_segment)/4
                                                ;段重定位表项个数[0x0a]
                                                ;1个表项占4个字节
    ;段重定位表项
    code_1_segment  dd  section.code_1.start    ;[0x0c]
    code_2_segment  dd  section.code_2.start    ;[0x10]
    data_1_segment  dd  section.data_1.start    ;[0x14]
    data_2_segment  dd  section.data_2.start    ;[0x18]
    stack_segment   dd  section.stack.start     ;[0x1c]
    
    header_end:
    
;----------------------------------------------------------------------
SECTION code_1 align=16 vstart=0    ;定义代码段1(16字节对齐)
put_string:                         ;显示串(字符串以0结尾)

            mov cl,[bx]             ;取一个字符
            or cl,cl                ;cl=0?
            jz .exit                ;cl=0时返回主程序
            call put_char           
            inc bx                  ;下一个字符
            jmp put_string
            
        .exit:
            ret
            
;----------------------------------------------------------------------
put_char:                           ;显示一个字符
        
            push ax
            push bx
            push cx
            push dx
            push ds
            push es
            
            ;以下取当前光标:光标位置是一个16位的数值
            mov dx,0x3d4            ;索引寄存器端口号 0x3d4
            mov al,0x0e             ;索引值14
            out dx,al
            mov dx,0x3d5            ;数据端口0x3d5
            in al,dx                ;高8位
            mov ah,al               
            
            mov dx,0x3d4
            mov al,0x0f
            out dx,al
            mov dx,0x3d5
            in al,dx                ;低8位
            mov bx,ax               ;BX 存放代表光标位置的16位数
    
            cmp cl,0x0d             ;回车符?
            jnz .put_0a             ;不是回车,看看是不是换行等字符?
            mov ax,bx               
            mov bl,80
            div bl
            mul bl
            mov bx,ax
            jmp .set_cursor
            
    .put_0a:
            cmp cl,0x0a             ;换行符?
            jnz .put_other          ;不是换行符,则正常显示字符
            add bx,80
            jmp .roll_screen

    .put_other:
            mov ax,0xb800
            mov es,ax
            shl bx,1                ;左移1位相当于乘以2
            mov [es:bx],cl          ;于光标处显示字符
            
            ;以下将光标位置推进一个字符
            shr bx,1                ;右移相当于除以2,换回来bx
            add bx,1                ;推进光标位置
            
    .roll_screen:                   ;VGA文本模式,每行80个字符,25行
            cmp bx,2000             ;光标超出屏幕?滚屏
            jl .set_cursor
            
            mov ax,0xb800
            mov ds,ax
            mov es,ax
            cld
            mov si,0xa0             ;从屏幕第2行第0列开始向上复制一行
            mov di,0x00
            mov cx,1920             ;80*25-80=1920
            rep movsw               ;以字为单位进行复制
            mov bx,3840             ;4000 - 160 = 3840 清楚屏幕最底一行
            mov cx,80               ;
    .cls:   
            mov word [es:bx],0x0720 ;黑底白字的空格
            add bx,2
            loop .cls
            
            mov bx,1920             ;光标位置是最后一行行首
                                    ;设置光标
    .set_cursor:                    ;不同的情况已用不同的方法将光标的新位置计算好
            mov dx,0x3d4
            mov al,0x0e             ;高8位
            out dx,al
            mov dx,0x3d5
            mov al,bh
            out dx,al
            mov dx,0x3d4
            mov al,0x0f             ;低8位
            out dx,al
            mov dx,0x3d5
            mov al,bl
            out dx,al
            
            pop es
            pop ds
            pop dx
            pop cx
            pop bx
            pop ax
            
            ret
            
;----------------------------------------------------------------------
    start:  
            ;初始化执行时,DS和ES指向用户程序头部段
            mov ax,[stack_segment]          ;设置到用户程序到自己的堆栈
            mov ss,ax
            mov sp,stack_end                ;stack段里保留保留256字节的空间 mov sp,256
            
            mov ax,[data_1_segment]         ;设置到用户程序自己的数据段
            mov ds,ax
            
            mov bx,msg0                     ;显示第一段信息
            call put_string                 ;put_string用[bx]取每一个字符
            
            push word [es:code_2_segment]   ;段寄存器DS切换到数据段2
            mov ax,begin
            push ax
            
            retf                            ;转移到代码段2执行
            
    continue:
            mov ax,[es:data_2_segment]      ;段寄存器DS切换到数据段2
            mov ds,ax
            
            mov bx,msg1
            call put_string                 ;显示第二段信息
            
            jmp $
            
;----------------------------------------------------------------------
SECTION code_2 align=16 vstart=0    ;定义代码段2(16字节对齐)

    begin:
            push word [es:code_1_segment]
            mov ax,continue             
            push ax                     
            
            retf                            ;转移到代码段1接着执行    

;----------------------------------------------------------------------
SECTION data_1 align=16 vstart=0

    msg0 db '  This is NASM - the famous Netwide Assembler. '
         db 'Back at SourceForge and in intensive development! '
         db 'Get the current versions from http://www.nasm.us/.'
         db 0x0d,0x0a,0x0d,0x0a
         db '  Example code for calculate 1+2+...+1000:',0x0d,0x0a,0x0d,0x0a
         db '     xor dx,dx',0x0d,0x0a
         db '     xor ax,ax',0x0d,0x0a
         db '     xor cx,cx',0x0d,0x0a
         db '  @@:',0x0d,0x0a
         db '     inc cx',0x0d,0x0a
         db '     add ax,cx',0x0d,0x0a
         db '     adc dx,0',0x0d,0x0a
         db '     inc cx',0x0d,0x0a
         db '     cmp cx,1000',0x0d,0x0a
         db '     jle @@',0x0d,0x0a
         db '     ... ...(Some other codes)',0x0d,0x0a,0x0d,0x0a
         db 0

;----------------------------------------------------------------------
SECTION data_2 align=16 vstart=0

    msg1 db '  The above contents is written by LeeChung. '
         db '2011-05-06'
         db 0

;---------------------------------------------------------------------- 
SECTION stack align=16 vstart=0 
    
    resb 256
    
stack_end:  

;---------------------------------------------------------------------- 
SECTION trail align=16
program_end:            ;trail段没有vstart标记,program_end是针对用户程序开头的偏移量

代码说明

  • 循环与子程序的调用关系

循环 
    put_string
参数
   DS 段寄存器器指向数据段
   BX 指向字符串的标号所在(位于数据段内,vstart=0 规定是相对数据段开头的偏移量)
-----------------------------------------------------------
-----------------------------------------------------------

子程序
    put_char
功能
    显示一个字符(注意是一个)
参数
    cl 要显示的字符
过程
    见下方put_char流程图

-----------------------------------------------------------
-----------------------------------------------------------
光标位置的计算与光标的显示分开的,由不同的情况计算出不同的位置数值,再统一由 子程序.setcursor 进行显示。
图8-19 过程put_char的流程图.png

《x86汇编语言:从实模式到保护模式》 第143页

  • 光标的读与写
指定索引寄存器 端口号是 0x3d4
光标位置是16位数,索引值 14(0x0e)和 15(0x0f)提供光标位置的高8位、低8位
指定好寄存器之后,通过数据端口 0x3d5 进行读写


取光标位置,取到的位置16位数值放到 BX里面
===================================================
 ;以下取当前光标:光标位置是一个16位的数值
            mov dx,0x3d4            ;索引寄存器端口号 0x3d4
            mov al,0x0e             ;索引值14
            out dx,al

            mov dx,0x3d5            ;数据端口0x3d5
            in al,dx                ;高8位
            mov ah,al               
            
            mov dx,0x3d4
            mov al,0x0f
            out dx,al

            mov dx,0x3d5
            in al,dx                ;低8位
            mov bx,ax               ;BX 存放代表光标位置的16位数
---------------------------------------------------------------------------------------------



写光标位置,根据不同情况计算好的光标位置通过端口写
===============================================================
                               ;设置光标
    .set_cursor:                    ;不同的情况已用不同的方法将光标的新位置计算好
            mov dx,0x3d4
            mov al,0x0e             ;高8位
            out dx,al

            mov dx,0x3d5
            mov al,bh
            out dx,al

            mov dx,0x3d4
            mov al,0x0f             ;低8位
            out dx,al

            mov dx,0x3d5
            mov al,bl
            out dx,al
-------------------------------------------------------------------------------------------------------------------
  • 显存、光标、字符
VGA 文本模式 一页有25行,每行有80个字符

指定 es = 0xb800 di= 0x0000
假设要在第0行第0列,显示一个字符‘a’

1、需要在偶数地址写字符 
mov es:[di],'a'
2、在奇数地址写颜色属性,黑底白字0x07
mov es:[di+1],0x07

因此可以看到一页能显示 80*25=2000个字符,
但同时也是存了4000个字节的数据
(一半是字符的ASCII码,一半是每个字符的对应属性)

光标的位置是一个16位的数值,
因此,对光标而言,
0表示第0行0列,
1表示第0行1列,
....
20表示第0行20列,
每一行有80列,
79表示第0行79列,
80表示第1行第0行(换行...)
....

==========================================
在子程序
.put_other:
            mov ax,0xb800
            mov es,ax
            shl bx,1                ;左移1位相当于乘以2
            mov [es:bx],cl          ;于光标处显示字符
            
            ;以下将光标位置推进一个字符
            shr bx,1                ;右移相当于除以2,换回来bx
            add bx,1                ;推进光标位置

是通过 光标的位置数值 来确定 字符的显示位置的,
光标在哪里闪烁,字符就写到那里,
【光标位置数值 x 2 = 字符要被送入的那个偶地址】
字符的颜色属性默认是0x07不需要修改。
  • SECTION关键词
    在用户程序中使用SECTION关键词人为的分了很多段,然后把这些段集中放到头部段header,并且借由加载器的回写在执行时变成了都是可以映射到真实物理地址的段地址,需要用到哪个段就去头部段里取,这样少得可怜的段寄存器就够用了,如果全局固定es指向头部段,那么就疯狂复用ds寄存器
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容