2026年2月

前言

在8086汇编中,屏幕输出常用的中断是int 10h(BIOS视频服务中断),其功能涵盖文字模式(如文本显示)和图形模式(可模拟简单视频输出)。以下实现一个模拟int 10h核心功能的自定义屏幕输出中断程序,支持文字模式字符显示、光标控制和图形模式像素点绘制,并添加详细注释。

代码实现

; 常量定义
VIDEO_SEG_TEXT equ 0b800h ; 文本模式显存段地址(80x25彩色文本)
VIDEO_SEG_GRAPH equ 0a000h ; 图形模式显存段地址(320x200 256色)
MAX_ROW_TEXT equ 24 ; 文本模式最大行号(0-24)
MAX_COL_TEXT equ 79 ; 文本模式最大列号(0-79)
CURSOR_PORT_H equ 03d4h ; 光标控制端口(高8位)
CURSOR_PORT_L equ 03d5h ; 光标控制端口(低8位)
 
code segment
    assume cs:code, ds:data
 
; 自定义屏幕输出中断服务程序(入口:AH=功能号)
; 功能:
; AH=00h:设置显示模式(0=文本模式,13h=320x200图形模式)
; AH=02h:设置光标位置(BH=页号,DH=行,DL=列)
; AH=09h:在当前位置显示字符(AL=字符,BH=页号,BL=属性,CX=重复次数)
; AH=0Ch:图形模式画点(AL=颜色,CX=X坐标,DX=Y坐标)
video_int proc far
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push es
    push ds ; 保存所有寄存器
    push bp
    mov bp, sp ; 建立栈帧(便于访问参数)
 
    ; 读取功能号(AH),跳转至对应处理逻辑
    cmp ah, 00h
    je set_mode ; 设置显示模式
    cmp ah, 02h
    je set_cursor ; 设置光标位置
    cmp ah, 09h
    je print_char ; 文本模式显示字符
    cmp ah, 0Ch
    je draw_pixel ; 图形模式画点
    jmp int_end ; 不支持的功能,直接返回
 
; 功能00h:设置显示模式
; 入口:AL=模式号(0=80x25文本,13h=320x200图形)
set_mode:
    ; 保存当前模式(实际中可存入内存变量)
    mov [current_mode], al
    ; 调用BIOS原int 10h完成硬件模式切换(简化处理,实际需直接操作显卡端口)
    int 10h
    jmp int_end
 
; 功能02h:设置光标位置(仅文本模式有效)
; 入口:BH=页号(文本模式),DH=行号,DL=列号
set_cursor:
    ; 检查当前模式是否为文本模式(0)
    cmp byte ptr [current_mode], 0
    jne int_end ; 非文本模式不处理
 
    ; 计算光标位置(80列/行,公式:位置=行*80 + 列)
    mov al, dh ; AL=行号
    mov cl, 80 ; CL=每行字符数
    mul cl ; AX=行*80
    add ax, dx ; AX=行*80 + 列(光标位置)
 
    ; 向光标端口写入位置(高8位+低8位)
    mov dx, CURSOR_PORT_H ; 端口:光标高8位
    mov al, 0Eh ; 命令:设置光标高8位
    out dx, al
    mov dx, CURSOR_PORT_L ; 端口:光标低8位
    mov al, ah ; 高8位数据
    out dx, al
 
    mov dx, CURSOR_PORT_H
    mov al, 0Fh ; 命令:设置光标低8位
    out dx, al
    mov dx, CURSOR_PORT_L
    mov al, al ; 低8位数据(AX低8位)
    out dx, al
    jmp int_end
 
; 功能09h:文本模式显示字符
; 入口:AL=字符,BH=页号,BL=属性(高4位背景,低4位前景),CX=重复次数
print_char:
    cmp byte ptr [current_mode], 0
    jne int_end ; 非文本模式不处理
 
    ; 获取当前光标位置(简化:从内存变量读取,实际应读光标端口)
    mov dh, [cursor_row] ; DH=当前行
    mov dl, [cursor_col] ; DL=当前列
 
    ; 计算显存地址:文本模式显存 = 0b800h:[(行*80 + 列)*2]
    mov ax, VIDEO_SEG_TEXT
    mov es, ax ; ES=显存段地址
    mov al, dh
    mov cl, 80
    mul cl ; AX=行*80
    add ax, dx ; AX=行*80 + 列
    shl ax, 1 ; AX=地址偏移量(每个字符占2字节:ASCII+属性)
    mov di, ax ; DI=显存偏移
 
    ; 循环显示字符(CX=重复次数)
print_loop:
    mov es:[di], al ; 写入字符ASCII码
    mov es:[di+1], bl ; 写入属性(颜色)
    add di, 2 ; 下一个字符位置
    inc dl ; 列号+1
    cmp dl, MAX_COL_TEXT+1 ; 判断是否超出列边界
    jle next_char
    mov dl, 0 ; 列号归零
    inc dh ; 行号+1
    cmp dh, MAX_ROW_TEXT+1 ; 判断是否超出行边界
    jle next_char
    mov dh, 0 ; 行号归零(滚屏简化处理)
next_char:
    loop print_loop
 
    ; 更新光标位置
    mov [cursor_row], dh
    mov [cursor_col], dl
    jmp int_end

; 功能0Ch:图形模式画点(320x200 256色)
; 入口:AL=颜色,CX=X坐标(0-319),DX=Y坐标(0-199)
draw_pixel:
    cmp byte ptr [current_mode], 13h
    jne int_end ; 非13h图形模式不处理
 
    ; 计算像素地址:320x200模式下,地址=Y*320 + X
    mov ax, VIDEO_SEG_GRAPH
    mov es, ax ; ES=图形显存段地址
    mov ax, dx ; AX=Y坐标
    mov bx, 320
    mul bx ; AX=Y*320
    add ax, cx ; AX=Y*320 + X(像素偏移量)
    mov di, ax ; DI=显存偏移
    mov es:[di], al ; 写入颜色值
    jmp int_end
 
; 中断结束:恢复寄存器并返回
int_end:
    pop bp
    pop ds
    pop es
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    iret ; 中断返回
video_int endp
 
; 安装自定义中断向量(替换int 10h)
install proc
    cli ; 关中断,防止安装过程被打断
    mov ax, 0
    mov es, ax ; ES指向中断向量表(0段)
    mov di, 10*4 ; int 10h的向量地址:10*4=40(0000:0028)
    mov ax, offset video_int ; 中断服务程序偏移地址
    stosw ; 写入偏移量到向量表
    mov ax, cs ; 中断服务程序段地址(当前代码段)
    stosw ; 写入段地址到向量表
    sti ; 开中断
    ret
install endp
 
; 主程序:初始化并测试自定义中断
main:
    mov ax, data
    mov ds, ax ; 初始化数据段
 
    call install ; 安装自定义屏幕输出中断
 
    ; 测试1:设置文本模式(00h)
    mov ah, 00h
    mov al, 00h
    int 10h ; 调用自定义中断
 
    ; 测试2:显示字符"Hello, World!"(文本模式)
    mov ah, 09h
    mov al, 'H'
    mov bl, 07h ; 属性:黑底白字
    mov cx, 1
    int 10h
    ; 此处可继续添加其他字符的显示代码...
 
    ; 测试3:切换到图形模式(13h)并画点
    mov ah, 00h
    mov al, 13h
    int 10h
    mov ah, 0Ch
    mov al, 0Ch ; 颜色:红色
    mov cx, 100 ; X=100
    mov dx, 50 ; Y=50
    int 10h ; 画点
 
    jmp $ ; 无限循环,保持程序运行
 
code ends
 
; 数据段:保存当前模式和光标位置
data segment
    current_mode db 0 ; 当前显示模式(0=文本,13h=图形)
    cursor_row db 0 ; 当前光标行号
    cursor_col db 0 ; 当前光标列号
data ends
 
    end main

代码说明

  1. 中断功能设计:模拟int 10h的核心功能,包括显示模式设置(文本/图形)、光标控制、文本字符显示和图形像素绘制,覆盖基本屏幕输出需求。
  2. 文本模式实现:a.显存地址为 0b800h ,每个字符占2字节(低字节为ASCII码,高字节为属性,控制前景/背景色)。b.光标位置通过显卡端口 03d4h 和 03d5h 设置,计算方式为 行号×80 + 列号 (80列文本模式)。
  3. 图形模式实现:- 320x200 256色模式的显存地址为 0a000h ,每个像素直接对应一个字节(存储颜色值),地址计算为 Y×320 + X 。
  4. 中断安装:通过修改中断向量表(0段)中int 10h的入口地址,将其指向自定义服务程序,安装时需关中断( cli )保证操作原子性。
  5. 测试逻辑:主程序安装中断后,依次测试文本模式字符显示和图形模式画点功能,验证中断有效性。

前言

在8086汇编中,磁盘操作主要通过int 13h(BIOS磁盘服务中断)实现,涉及软盘、硬盘的读写、状态查询等功能。下面我们学习模拟int 13h核心功能的自定义磁盘操作中断程序,支持磁盘复位、扇区读写、状态获取等基础功能,基于3.25英寸软盘实现。

代码实现

; 常量定义(基于1.44MB软盘参数)
SECTORS_PER_TRACK equ 18 ; 每磁道扇区数
HEADS_PER_DISK equ 2 ; 磁头数(面数)
BYTES_PER_SECTOR equ 512 ; 每扇区字节数
DISK_STATUS_PORT equ 03F6h ; 磁盘状态端口(模拟)
DISK_DATA_PORT equ 03F5h ; 磁盘数据端口(模拟)
DISK_CMD_PORT equ 03F7h ; 磁盘命令端口(模拟)
STATUS_OK equ 00h ; 操作成功状态
STATUS_ERR equ 01h ; 操作失败状态
 
code segment
    assume cs:code, ds:data
 
; 磁盘操作中断服务程序(入口:AH=功能号)
; 功能:
; AH=00h:磁盘复位(初始化磁盘控制器)
; AH=02h:读扇区(AL=扇区数,CH=柱面号,CL=扇区号,DH=磁头号,DL=驱动器号;ES:BX=缓冲区)
; AH=03h:写扇区(参数同读扇区)
; AH=01h:获取上次操作状态(返回AH=状态码)
disk_int proc far
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push es
    push ds ; 保存所有寄存器
    push bp
    mov bp, sp
 
    ; 根据功能号跳转处理
    cmp ah, 00h
    je disk_reset ; 磁盘复位
    cmp ah, 01h
    je get_disk_status ; 获取状态
    cmp ah, 02h
    je read_sector ; 读扇区
    cmp ah, 03h
    je write_sector ; 写扇区
    jmp int_end ; 不支持的功能
 
; 功能00h:磁盘复位
; 操作:初始化磁盘控制器,重置状态
disk_reset:
    ; 向命令端口发送复位命令(假设命令00h)
    mov al, 00h
    out DISK_CMD_PORT, al
 
    ; 等待复位完成(轮询状态端口,bit7=1表示就绪)
wait_reset:
    in al, DISK_STATUS_PORT
    test al, 80h
    jz wait_reset ; 未就绪则继续等待
 
    mov [last_status], STATUS_OK ; 复位成功
    jmp int_end
 
; 功能01h:获取上次操作状态
; 返回:AH=状态码(0=成功,1=失败)
get_disk_status:
    mov ah, [last_status]
    jmp int_end
 
; 功能02h:读扇区
; 入口参数:
; AL=要读取的扇区数,CH=柱面号,CL=起始扇区号,DH=磁头号(0-1),DL=驱动器号(0=A盘)
; ES:BX=接收数据的缓冲区
; 返回:CF=0表示成功,AH=0;CF=1表示失败,AH=错误码
read_sector:
    ; 检查参数合法性
    cmp dh, HEADS_PER_DISK ; 磁头号是否超出范围(0-1)
    jge read_err
    cmp cl, SECTORS_PER_TRACK ; 扇区号是否超出范围(1-18)
    jg read_err
    cmp al, 0 ; 扇区数不能为0
    je read_err
 
    ; 模拟读操作:向命令端口发送读命令(假设命令02h)
    mov al, 02h
    out DISK_CMD_PORT, al
 
    ; 发送参数(柱面、扇区、磁头)
    mov al, ch
    out DISK_DATA_PORT, al ; 柱面号
    mov al, cl
    out DISK_DATA_PORT, al ; 扇区号
    mov al, dh
    out DISK_DATA_PORT, al ; 磁头号
 
    ; 等待操作完成(轮询状态端口,bit0=0表示无错误)
wait_read:
    in al, DISK_STATUS_PORT
    test al, 01h
    jnz wait_read ; 操作未完成则等待
 
    ; 读取数据(每扇区512字节,循环读取AL个扇区)
    mov si, 0 ; 扇区计数
read_loop:
    push cx
    mov cx, BYTES_PER_SECTOR / 2 ; 每次读2字节(简化)
read_byte:
    in ax, DISK_DATA_PORT ; 从数据端口读2字节
    mov es:[bx], ax
    add bx, 2
    loop read_byte
    pop cx
 
    inc si
    cmp si, al ; 是否读完所有扇区
    jl read_loop
 
    mov [last_status], STATUS_OK ; 读成功
    clc ; 清除进位标志(表示成功)
    jmp int_end
 
read_err:
    mov [last_status], STATUS_ERR ; 读失败
    stc ; 设置进位标志(表示失败)
    jmp int_end
 
; 功能03h:写扇区
; 入口参数:同读扇区(ES:BX=待写数据缓冲区)
; 返回:CF=0表示成功,AH=0;CF=1表示失败,AH=错误码
write_sector:
    ; 检查参数合法性(同读扇区)
    cmp dh, HEADS_PER_DISK
    jge write_err
    cmp cl, SECTORS_PER_TRACK
    jg write_err
    cmp al, 0
    je write_err
 
    ; 模拟写操作:向命令端口发送写命令(假设命令03h)
    mov al, 03h
    out DISK_CMD_PORT, al
 
    ; 发送参数(柱面、扇区、磁头)
    mov al, ch
    out DISK_DATA_PORT, al
    mov al, cl
    out DISK_DATA_PORT, al
    mov al, dh
    out DISK_DATA_PORT, al
 
    ; 等待控制器就绪
wait_write:
    in al, DISK_STATUS_PORT
    test al, 80h
    jz wait_write ; 未就绪则等待
 
    ; 写入数据(每扇区512字节,循环写入AL个扇区)
    mov si, 0
write_loop:
    push cx
    mov cx, BYTES_PER_SECTOR / 2
write_byte:
    mov ax, es:[bx] ; 从缓冲区取2字节
    out DISK_DATA_PORT, ax ; 写入数据端口
    add bx, 2
    loop write_byte
    pop cx
 
    inc si
    cmp si, al
    jl write_loop
 
    mov [last_status], STATUS_OK ; 写成功
    clc ; 清除进位标志
    jmp int_end
 
write_err:
    mov [last_status], STATUS_ERR ; 写失败
    stc ; 设置进位标志
    jmp int_end
 
; 中断结束:恢复寄存器并返回
int_end:
    pop bp
    pop ds
    pop es
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax
    iret ; 中断返回
disk_int endp
 
; 安装自定义磁盘中断向量(替换int 13h)
install proc
    cli ; 关中断,防止安装冲突
    mov ax, 0
    mov es, ax ; ES指向中断向量表(0段)
    mov di, 13h*4 ; int 13h向量地址:13h×4=0000:004C
    mov ax, offset disk_int ; 中断服务程序偏移地址
    stosw ; 写入偏移量
    mov ax, cs ; 中断服务程序段地址
    stosw ; 写入段地址
    sti ; 开中断
    ret
install endp
 
; 主程序:测试磁盘操作功能
main:
    mov ax, data
    mov ds, ax ; 初始化数据段
 
    call install ; 安装磁盘中断
 
    ; 测试1:磁盘复位
    mov ah, 00h
    mov dl, 0 ; 驱动器A:
    int 13h
 
    ; 测试2:读扇区(读A盘0柱面,1扇区,0磁头,1个扇区到缓冲区)
    mov ah, 02h
    mov al, 1 ; 读1个扇区
    mov ch, 0 ; 柱面0
    mov cl, 1 ; 扇区1
    mov dh, 0 ; 磁头0
    mov dl, 0 ; 驱动器A:
    mov bx, offset disk_buf ; 缓冲区地址(ES=DS)
    mov es, ds
    int 13h
 
    ; 测试3:获取操作状态并显示(成功显示'0',失败显示'1')
    mov ah, 01h
    int 13h
    add ah, 30h ; 转换为ASCII
    mov dl, ah
    mov ah, 02h
    int 21h ; 调用DOS中断显示
 
    jmp $ ; 暂停程序
 
code ends
 
; 数据段:保存磁盘状态和缓冲区
data segment
    last_status db STATUS_OK ; 上次操作状态
    disk_buf db BYTES_PER_SECTOR dup(0) ; 磁盘缓冲区(512字节)
data ends

    end main

代码说明

  1. 硬件抽象:基于1.44MB软盘的参数(每磁道18扇区、2磁头、每扇区512字节),模拟磁盘控制器的端口(状态、数据、命令端口),简化真实磁盘的硬件交互逻辑。
  2. 核心功能:a.磁盘复位:初始化控制器,重置状态,确保后续操作可用。b.读/写扇区:通过端口发送操作命令和参数(柱面、扇区、磁头),从数据端口读写数据,支持多扇区操作。c.状态查询:返回上次操作的结果(成功/失败),辅助判断操作是否有效。
  3. 中断安装:修改中断向量表中int 13h的入口地址,指向自定义服务程序,安装时关中断( cli )防止被其他中断干扰。
  4. 测试逻辑:主程序依次测试磁盘复位、读扇区和状态查询,并通过DOS中断显示操作结果(0表示成功,1表示失败)。

前言

int 9h是8086系统中键盘中断的中断服务程序,用于处理键盘输入。其功能包括读取键盘扫描码、处理特殊键(如Shift、Ctrl等)、将扫描码转换为ASCII码以及设置键盘缓冲区等。它与面向用户的int 16h相互配合。前面我们说过int 9h涉及到硬件的处理,所以当时没有尝试自己编写。下面我们尝试自己实现这个中断。

代码实现

; 定义段寄存器
code segment
    assume cs:code
 
; 键盘中断服务程序(替代int 9h)
keyboard_int proc far
    push ax
    push bx
    push cx
    push dx
    push si
    push di
    push es
    push ds ; 保存所有寄存器状态
 
    ; 1. 读取键盘扫描码(从端口60h读取)
    in al, 60h ; 读取扫描码到AL(通码:按键按下,断码:按键松开,断码=通码+80h)
 
    ; 2. 向8259A发送中断结束信号(EOI),允许后续中断
    mov al, 20h ; EOI命令字
    out 20h, al ; 发送到主8259A
 
    ; 3. 处理扫描码(此处简化处理:仅响应部分按键,忽略断码和特殊键)
    cmp al, 80h ; 判断是否为断码(若AL>=80h,说明按键松开,不处理)
    jae int_end ; 是断码则直接结束
 
    ; 4. 扫描码转ASCII码(简化映射表,仅举例常用键)
    mov bx, offset scancode_to_ascii ; BX指向映射表
    xlat ; AL = [BX + AL](根据扫描码获取ASCII码,0表示无对应ASCII)
 
    ; 5. 若有有效ASCII码,存入键盘缓冲区(简化,实际需检查缓冲区状态)
    cmp al, 0
    je int_end ; 无对应ASCII,结束
 
    ; 此处仅演示,实际缓冲区操作需更复杂(如使用BIOS数据区的键盘缓冲区)
    mov ah, 0eh ; 调用BIOS中断,在屏幕上显示字符(模拟输入效果)
    int 10h
 
int_end:
    pop ds
    pop es
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    pop ax ; 恢复寄存器状态
    iret ; 中断返回
keyboard_int endp
 
; 扫描码到ASCII码的简化映射表(仅部分键,通码对应)
; 索引:扫描码(通码),值:ASCII码(0表示无对应)
scancode_to_ascii db 0 ; 00h(无键)
                  db 27 ; 01h(Esc,ASCII 27)
                  db '1' ; 02h(1键)
                  db '2' ; 03h(2键)
                  db '3' ; 04h(3键)
                  db '4' ; 05h(4键)
                  db '5' ; 06h(5键)
                  db '6' ; 07h(6键)
                  db '7' ; 08h(7键)
                  db '8' ; 09h(8键)
                  db '9' ; 0Ah(9键)
                  db '0' ; 0Bh(0键)
                  db '-' ; 0Ch(-键)
                  db '=' ; 0Dh(=键)
                  db 8 ; 0Eh(Backspace,ASCII 8)
                  db 9 ; 0Fh(Tab,ASCII 9)
                  db 'q' ; 10h(Q键)
                  db 'w' ; 11h(W键)
                  db 'e' ; 12h(E键)
                  db 'r' ; 13h(R键)
                  db 't' ; 14h(T键)
                  db 'y' ; 15h(Y键)
                  db 'u' ; 16h(U键)
                  db 'i' ; 17h(I键)
                  db 'o' ; 18h(O键)
                  db 'p' ; 19h(P键)
                  ; 更多键的映射可继续添加...
 
; 安装中断向量(将自定义中断服务程序地址写入中断向量表)
install proc
    cli ; 关中断,防止安装过程被打断
    mov ax, 0
    mov es, ax ; ES指向中断向量表段(0段)
    mov di, 9*4 ; int 9h的向量地址:9*4=36(0000:0024)
    mov ax, offset keyboard_int ; 中断服务程序偏移地址
    stosw ; 写入偏移量到向量表
    mov ax, cs ; 中断服务程序段地址(当前代码段)
    stosw ; 写入段地址到向量表
    sti ; 开中断
    ret
install endp
 
; 主程序:安装中断后等待(实际使用中需保持程序运行以响应中断)
main:
    call install ; 安装自定义int 9h中断
    jmp $ ; 无限循环,等待中断
 
code ends
    end main

代码说明

  1. 中断服务程序结构:遵循中断处理规范,先保存所有寄存器,处理完成后恢复并通过 iret 返回。
  2. 扫描码读取:从端口 60h 读取键盘扫描码(通码/断码),通码表示按键按下,断码(通码+80h)表示按键松开。
  3. 中断结束信号:向8259A中断控制器发送 EOI (20h),允许后续中断。
  4. 扫描码转ASCII:通过映射表将常用按键的通码转换为对应的ASCII码(简化处理,实际需考虑Shift、CapsLock等修饰键)。
  5. 缓冲区与输出:简化演示中直接调用BIOS中断显示字符,实际应将数据存入键盘缓冲区。
  6. 中断安装:修改中断向量表(0段)中int 9h对应的地址,指向自定义服务程序。

汇编环境的搭建

我们学习的是在8086CPU下编写汇编语言,因此使用MS-DOS自带的记事本就是一个不错的选择。早期汇编语言程序的封装包括编译(翻译成机器码)和链接(套一个DOS下的程序外壳)两步,都有专门的软件实现了这些功能。下面这个链接是我在网上找到的编译器+链接器:https://huaijiuchuang.faryou.eu.org/viewthread.php?tid=289&extra=page%3D1,复制到DOS下即可运行,使用时只需要输入生成程序的文件名即可~

Debug的使用

Debug是DOS自带的一个用于调试程序的工具,它能够一步步执行汇编指令,并且每一步之后都能查看内存、寄存器的状态,使我们可以快速找到程序的问题。下面我们学习Debug的使用。

进入Debug

在命令行下输入以下指令,即可以调试指定的程序:

debug 指定程序文件名(可以是绝对路径或者相对路径)

R命令

用于查看各寄存器的值,并显示当前指令。

U命令

用于查看其他的指令。

T命令

这个命令是Debug的核心功能,实现单步执行。会执行当前指向地址的命令,并更新CS:IP的值。

P命令

单步执行int指令。

前言

学习汇编之后,我们或许能够思考一些之前无法考虑的问题。本文是个人的一些想法,分享给大家,也许可以激发大家的思维。

操作系统的思考

学了汇编,我们可以对一些结构较为简单的操作系统进行研究。
我们来考虑一个DOS操作系统,我们想想一个DOS系统由哪些部分组成,无非是引导、硬件控制(最基本的,磁盘、键盘和屏幕)、提供一些中断程序(即所谓"API"),以及一个基本的命令行。这也是MS-DOS1.25中的全部。
很显然,我们如果要独立制作一个DOS操作系统,就应该从这些方面入手。我们已经学完了汇编的基础知识。很显然,前面三个部分以我们目前的汇编能力,外加学习一点硬件知识,有希望能完成;最后一个部分涉及到字符串的处理,这需要我们学习一些相关的算法(如果想实现复杂点的语法使用C语言更为方便,但这里仅考虑最简单的情况)。
因此,我们不妨将这样一个较为基础的DOS作为练习16位汇编的大作业~

高级语言的思考

我们思考一下C语言里的函数、变量、结构体等概念的实现。
首先,我们在使用函数的时候经常进行嵌套及递归操作,那么如何保存之前函数中的数据就成为了一个问题。汇编语言中,我们使用call调用子程序之后,子程序会先把寄存器入栈。而在C语言的函数中,我们需要暂存局部变量,并且函数过程结束之后,应当及时释放局部变量占用的内存。很显然,函数调用及嵌套具有“先进后出”的性质,即越先进入的函数越靠后结束,这完美契合了栈的特性。如果我们构建一个“栈”,存下局部变量,那么只需要在函数调用前后进出栈,就可以很好的完成内存管理。而我们还有全局变量,两者应该分开存。通常的做法是:在内存中靠前的地方划出一块作为栈,分界线之后的区域存放全局变量,称为“堆”。其他高级语言的内存管理也与此大致相同。这也解释了为什么局部变量不能定义过大——栈的空间有限,会爆栈。
结构体的原理很简单,实质上是一堆挤在一起的变量,只需要用多个寄存器访问即可。

关于CPU

我们前面已经学习了8086CPU的工作原理,选择8086是因为其机制较为简单,同时作为早期CPU的代表,适合新手进行学习,现实生活中应该是见不到这种三十多年前的CPU的。8086CPU为单任务CPU,即只能同时执行一条指令,并且没有任何内存保护机制,也就是说所有程序的内存可以互相看到,非常不安全。因此Intel在之后的CPU版本中加入了保护模式,实现了内存的权限层级管理。保护模式下我们需要学习许多新东西,机制也更复杂。我的观点是:先把8086下的硬件之类的事情折腾清楚搞熟练,再逐步接触保护模式。因此,我后面的教程也会对8086机的其他硬件调用进行详细介绍,以助读者理解。