当前位置: 首页 > news >正文

自制操作系统(二、输入输出和shell的简易实现)

先实现io.asm

[bits 32]; ***********************
; * 硬件端口和常量定义 *
; ***********************
VIDEO_MEMORY         equ 0xB8000
LINE_WIDTH           equ 80
SCREEN_HEIGHT        equ 25
WHITE_ON_BLACK       equ 0x0FKEYBOARD_PORT        equ 0x60
KEYBOARD_STATUS_PORT equ 0x64
KEYBOARD_BUF_SIZE    equ 256; 键盘状态标志
CAPS_LOCK    equ 0x01
SHIFT_DOWN   equ 0x02
CTRL_DOWN    equ 0x04
ALT_DOWN     equ 0x08VGA_CRTC_INDEX  equ 0x3D4
VGA_CRTC_DATA   equ 0x3D5
CURSOR_START    equ 0x0A
CURSOR_END      equ 0x0B; ***********************
; * 全局数据定义        *
; ***********************
[section .data]
key_flags      db 0     ; 键盘状态标志
cursor_x       dd 0     ; 当前光标列
cursor_y       dd 0     ; 当前光标行
keyboard_buffer times KEYBOARD_BUF_SIZE db 0
keyboard_head  dd 0
keyboard_tail  dd 0; 扫描码转换表 (小写)
scancode_lower:db 0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0x08db 0, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 0x0Adb 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', "'", '`', 0db '\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 0, 0, ' '; 扫描码转换表 (大写/Shift)
scancode_upper:db 0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0x08db 0, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 0x0Adb 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0db '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, 0, 0, ' '; ***********************
; * 显示功能函数        *
; ***********************
[section .text]; 函数:hide_cursor - 隐藏文本模式光标
global hide_cursor
hide_cursor:push eaxpush edx; 设置光标起始寄存器 (索引0x0A)mov al, CURSOR_STARTmov dx, VGA_CRTC_INDEXout dx, al; 写入值0x20到数据端口(禁用光标)mov al, 0x20mov dx, VGA_CRTC_DATAout dx, al; 可选:为了兼容性,也设置结束寄存器mov al, CURSOR_ENDmov dx, VGA_CRTC_INDEXout dx, almov al, 0x00        ; 结束扫描线设为0mov dx, VGA_CRTC_DATAout dx, alpop edxpop eaxret; 在指定位置输出字符
; 输入: EBX=行, ECX=列, AL=字符, AH=属性
global put_char
put_char:push edimov edi, ebximul edi, LINE_WIDTHadd edi, ecxshl edi, 1mov [gs:edi], axpop ediret; 输出字符串 (自动换行)
; 输入: EBX=起始行, ECX=起始列, ESI=字符串, AH=属性
global print_str
print_str:pushacld
.print_loop:lodsbtest al, aljz .donepush ebxpush ecxcall put_charpop ecxpop ebxinc ecxcmp ecx, LINE_WIDTHjb .same_linemov ecx, 0inc ebxcmp ebx, SCREEN_HEIGHTjb .same_linedec ebxdec ecxcall scroll_screen
.same_line:jmp .print_loop
.done:poparet; 清屏
global clear_screen
clear_screen:pushamov edi, VIDEO_MEMORYmov ecx, LINE_WIDTH * SCREEN_HEIGHTmov ax, (WHITE_ON_BLACK << 8) | ' 'rep stoswmov dword [cursor_x], 0mov dword [cursor_y], 0poparet; 屏幕向上滚动一行
global scroll_screen
scroll_screen:pusha; 1. 将第2行到最后行的内容复制到第1行到倒数第2行mov esi, VIDEO_MEMORY + LINE_WIDTH * 2    ; 源地址 = 第2行开始mov edi, VIDEO_MEMORY                     ; 目标地址 = 第1行开始mov ecx, LINE_WIDTH * (SCREEN_HEIGHT-1)   ; 复制 (高度-1) 行; 使用DWORD移动提高效率shr ecx, 1                                ; 双字计数 = 字数/2rep movsd; 如果字符数为奇数,复制最后一个字;test ecx, 1;jz .clear_last_line;movsw.clear_last_line:; 2. 清空最后一行mov edi, VIDEO_MEMORY + LINE_WIDTH * (SCREEN_HEIGHT-1) * 2mov ecx, LINE_WIDTHmov ax, (WHITE_ON_BLACK << 8) | ' '       ; 空格字符+属性rep stosw; 3. 更新光标位置(保持在最后一行开头)mov dword [cursor_x], 0mov dword [cursor_y], SCREEN_HEIGHTpoparet; ***********************
; * 键盘功能函数        *
; ***********************; 键盘中断处理程序 (IRQ1)
global get_key
get_key:push edxpush ebx.wait_key:; 等待键盘缓冲区有数据in al, KEYBOARD_STATUS_PORTtest al, 1jz .wait_key; 读取键盘扫描码in al, KEYBOARD_PORTmov ah, al           ; 保存扫描码; 处理特殊键 (Shift/Ctrl/Alt/CapsLock)cmp al, 0x2A         ; 左Shift按下je .shift_presscmp al, 0xAA         ; 左Shift释放je .shift_releasecmp al, 0x36         ; 右Shift按下je .shift_presscmp al, 0xB6         ; 右Shift释放je .shift_releasecmp al, 0x3A         ; CapsLockje .caps_togglecmp al, 0x1D         ; Ctrlje .ctrl_presscmp al, 0x9D         ; Ctrl释放je .ctrl_releasecmp al, 0x38         ; Altje .alt_presscmp al, 0xB8         ; Alt释放je .alt_release; 检查是否是释放事件test al, 0x80jnz .key_release; 转换为ASCIImovzx ebx, alcmp ebx, 58          ; 检查是否在转换表范围内ja .special_key; 选择转换表 (根据Shift/Caps状态)test byte [key_flags], SHIFT_DOWNjnz .use_uppertest byte [key_flags], CAPS_LOCKjz .use_lower
.use_upper:mov al, [scancode_upper + ebx]jmp .check_valid
.use_lower:mov al, [scancode_lower + ebx]
.check_valid:test al, aljz .special_keystc                  ; CF=1表示按下事件jmp .done.key_release:;clc                  ; CF=0表示释放事件xor al, aljmp .done.special_key:xor al, al           ; AL=0表示特殊键stcjmp .done.shift_press:or byte [key_flags], SHIFT_DOWNxor al, alstcjmp .done.shift_release:and byte [key_flags], ~SHIFT_DOWNxor al, alstcjmp .done.caps_toggle:xor byte [key_flags], CAPS_LOCK; 更新CapsLock LEDcall update_ledsxor al, alstcjmp .done.ctrl_press:or byte [key_flags], CTRL_DOWNxor al, alstcjmp .done.ctrl_release:and byte [key_flags], ~CTRL_DOWNxor al, alstcjmp .done.alt_press:or byte [key_flags], ALT_DOWNxor al, alstcjmp .done.alt_release:and byte [key_flags], ~ALT_DOWNxor al, alstcjmp .done.done:pop ebxpop edxret; 更新键盘LED状态 (CapsLock/NumLock/ScrollLock)
update_leds:pushad; 等待键盘可接受命令mov ecx, 1000
.wait_ready:in al, KEYBOARD_STATUS_PORTtest al, 0x02loopnz .wait_ready; 发送LED更新命令mov al, 0xED         ; LED命令out KEYBOARD_PORT, al; 等待应答mov ecx, 1000
.wait_ack:in al, KEYBOARD_PORTcmp al, 0xFA         ; ACKloopne .wait_ack; 发送LED状态mov al, [key_flags]and al, CAPS_LOCK    ; 只设置CapsLockout KEYBOARD_PORT, alpopadret; 获取一个按键 (非阻塞)
; 输出: AL=ASCII字符 (0表示无输入)
global get_char
get_char:mov eax, [keyboard_head]cmp eax, [keyboard_tail]je .no_inputmov al, [keyboard_buffer + eax]inc eaxcmp eax, KEYBOARD_BUF_SIZEjne .no_wrapxor eax, eax
.no_wrap:mov [keyboard_head], eaxret
.no_input:xor al, alret; 读取一行输入 (带回显)
; 输入: EDI=缓冲区, ECX=最大长度
; 输出: EAX=读取字符数
global read_line
read_line:push ebxpush ecxpush edxpush edixor ebx, ebx         ; 字符计数mov edx, [cursor_y]shl edx, 16or edx, [cursor_x]   ; EDX高16位=行, 低16位=列.read_loop:call get_chartest al, aljz .read_loopcmp al, 0x0A         ; 回车je .line_endcmp al, 0x08         ; 退格je .backspace; 检查缓冲区是否满cmp ebx, ecxjae .read_loop; 存储并显示字符mov [edi + ebx], alinc ebxpush ebxpush ecxmovzx ebx, dx        ; 当前列movzx ecx, dh        ; 当前行mov ah, WHITE_ON_BLACKcall put_charpop ecxpop ebxinc dl               ; 列位置+1cmp dl, LINE_WIDTHjb .read_loopmov dl, 0            ; 换行处理inc dhcmp dh, SCREEN_HEIGHTjb .read_loopdec dhcall scroll_screenjmp .read_loop.backspace:test ebx, ebxjz .read_loopdec ebxpush ebxpush ecxmovzx ebx, dxmovzx ecx, dhmov al, ' 'mov ah, WHITE_ON_BLACKcall put_charpop ecxpop ebxdec dljns .read_loopmov dl, LINE_WIDTH-1dec dhjns .read_loopxor dh, dhjmp .read_loop.line_end:mov byte [edi + ebx], 0mov eax, ebx; 更新光标位置mov [cursor_x], edxshr edx, 16mov [cursor_y], edxpop edipop edxpop ecxpop ebxret

实现一个简易的shell.asm 


;shell.asm
[bits 32]; 常量定义
ATA_BASE         equ 0x1F0
ATA_DRIVE_SELECT equ 0x1F6
ATA_STATUS       equ 0x1F7
ATA_CMD          equ 0x1F7
ATA_DATA         equ 0x1F0
extern scroll_screen
[section .data]
; Shell界面
msg db "[root@Plain]-(/)# ", 0
cmd_buffer times 80 db 0; 命令定义
cmd_echo db "echo", 0
cmd_help db "help", 0
cmd_ls   db "ls", 0
cmd_cat  db "cat", 0
cmd_write db "write", 0
cmd_clear db "clear", 0cmd_time db "time", 0
time_str db "HH:MM:SS", 0
; 帮助信息
help_msg1 db "Available commands:", 0
help_msg2 db "  echo <message> - Display message", 0
help_msg3 db "  help          - Show this help", 0
help_msg4 db "  ls            - List files", 0
help_msg5 db "  cat <file>    - Show file content", 0
help_msg6 db "  write <file> > <content> - Write to file", 0
help_msg7 db "  clear         - Clear screen", 0; 错误和信息消息
not_msg db "Error: Command not found: ", 0
error_msg db "ERROR: Disk operation failed", 0
dir_entry db "  [DIR] ", 0
no_file_msg db "File not found: ", 0
write_success db "Write successful", 0
write_fail db "Write failed", 0
invalid_format_msg db "Invalid write format. Use: write filename > content", 0; 文件系统参数
files_per_sector equ 16  ; FAT12根目录每扇区16个条目[section .bss]
; 磁盘缓冲区
bpb_buf    resb 512
fat_buf    resb 512; 文件系统参数
part_start resd 1
fat_start  resd 1
root_start resd 1
root_sectors resd 1; ==== 文件系统常量定义 ====
ROOT_DIR_SECTORS    equ 14      ; 根目录占用扇区数
ROOT_DIR_START      equ 19      ; 根目录起始扇区
FAT1_START          equ 1       ; FAT1起始扇区
DATA_START          equ 33      ; 数据区起始扇区 = 1 + 9*2 + 14; ==== 目录条目结构 ====
struc DIR_ENTRY.name      resb 8.ext       resb 3.attr      resb 1.reserved  resb 10.time      resw 1.date      resw 1.cluster   resw 1.size      resd 1
endstruc[section .text]
extern print_str, put_char, get_key, clear_screen, fs_list_files, fs_files_count, fs_read_file global shell
shell:cmp ebx, 25ja .scrollmov ecx, 0mov esi, msgmov ah, 0x0Fcall print_str; 初始化命令缓冲区mov edi, cmd_buffermov ecx, 18          ; 从第18列开始输入mov byte [edi], 0    ; 清空缓冲区.input_loop:call get_keytest al, aljz .input_loop; 处理回车cmp al, 0x0Aje .execute; 处理退格cmp al, 0x08je .backspace; 存储并显示字符mov [edi], alinc edimov ah, 0x0Fcall put_charinc ecxjmp .input_loop.backspace:; 退格处理cmp edi, cmd_bufferje .input_loop       ; 忽略空退格dec edidec ecxmov al, ' 'mov ah, 0x0Fcall put_charjmp .input_loop.scroll:call scroll_screendec ebxjmp shell
.execute:; 添加字符串结束符mov byte [edi], 0; 检查空命令mov esi, cmd_buffercall is_emptyje .empty_cmd; 跳过前导空格call skip_spacestest al, aljz .empty_cmd; 检查help命令mov edi, cmd_helpcall cmd_cmpje .show_help; 检查echo命令mov edi, cmd_echocall cmd_cmpje .do_echomov edi, cmd_timecall cmd_cmpje do_time; 检查clear命令mov edi, cmd_clearcall cmd_cmpje .do_clear; 未知命令处理inc ebxmov ecx, 0mov esi, not_msgmov ah, 0x0C        ; 红色错误信息call print_str; 只显示命令部分(第一个空格前的内容)mov esi, cmd_buffercall print_command_partinc ebxjmp shell.empty_cmd:inc ebxmov ecx, 0jmp shell.show_help:; 显示帮助信息inc ebxmov ecx, 0mov esi, help_msg1mov ah, 0x0A        ; 绿色帮助信息call print_strinc ebxmov ecx, 0mov esi, help_msg2call print_strinc ebxmov ecx, 0mov esi, help_msg3call print_strinc ebxmov ecx, 0mov esi, help_msg4call print_strinc ebxmov ecx, 0mov esi, help_msg5call print_strinc ebxmov ecx, 0mov esi, help_msg6call print_strinc ebxmov ecx, 0mov esi, help_msg7call print_strinc ebxjmp shell.do_echo:; 跳过"echo"和后续空格add esi, 4call skip_spacestest al, aljz .no_args1         ; 无参数情况; 显示echo参数inc ebxmov ecx, 0mov ah, 0x0Fcall print_str.no_args1:inc ebx             ; 换行jmp shell; === clear命令实现 ===
.do_clear:call clear_screenmov ebx, 0mov ecx, 0jmp shell; === 辅助函数 ===; 打印命令部分(第一个空格前的内容)
print_command_part:pushamov ecx, 26         ; 错误信息后位置
.loop:lodsbtest al, aljz .donecmp al, ' 'je .donemov ah, 0x0Fcall put_charinc ecxjmp .loop
.done:poparet; 检查字符串是否为空或只有空格
is_empty:push esi
.loop:lodsbcmp al, ' 'je .looptest al, alpop esiret; 跳过字符串中的空格
skip_spaces:lodsbcmp al, ' 'je skip_spacesdec esi             ; 回退到第一个非空格字符ret; 命令比较函数
cmd_cmp:pusha
.compare:mov al, [esi]mov bl, [edi]; 检查命令是否结束(空格或字符串结束)cmp al, ' 'je .check_cmd_endtest al, aljz .check_cmd_end; 转换为小写比较cmp al, 'A'jb .no_change1cmp al, 'Z'ja .no_change1add al, 0x20
.no_change1:cmp bl, 'A'jb .no_change2cmp bl, 'Z'ja .no_change2add bl, 0x20.no_change2:cmp al, bljne .not_equalinc esiinc edijmp .compare.check_cmd_end:; 检查命令字符串是否也结束了cmp byte [edi], 0jne .not_equal.equal:popaxor eax, eax  ; ZF=1ret.not_equal:popaor eax, 1     ; ZF=0ret; 显示固定数量的字符
print_nchars:pushamov ah, 0x0F
.loop:lodsbcall put_charloop .looppoparetprint_hex:pushadmov ecx, 8
.loop:rol eax, 4mov ebx, eaxand ebx, 0x0fmov bl, [hex_chars + ebx]mov ah, 0x0Fcall put_charloop .looppopadretdo_time:call get_timeinc ebx             ; 换行mov ecx, 0mov esi, time_strmov ah, 0x0F        ; 白色文字call print_strjmp shellget_time:pushad; 禁用NMI并读取小时mov al, 0x04        ; 小时寄存器or al, 0x80         ; 禁用NMIout 0x70, alin al, 0x71call bcd_to_asciimov [time_str], dhmov [time_str+1], dl; 读取分钟mov al, 0x02or al, 0x80out 0x70, alin al, 0x71call bcd_to_asciimov [time_str+3], dhmov [time_str+4], dl; 读取秒mov al, 0x00or al, 0x80out 0x70, alin al, 0x71call bcd_to_asciimov [time_str+6], dhmov [time_str+7], dlpopadretbcd_to_ascii:; 将AL中的BCD码转换为两个ASCII字符,存储在DH和DL中mov dh, alshr dh, 4add dh, '0'mov dl, aland dl, 0x0Fadd dl, '0'ret; === 数据区 ===
[section .data]
no_files_msg db "No files found", 0
cat_usage_msg db "Usage: cat <filename>", 0; 文件系统参数(在Shell初始化时设置)
data_start      dd 33   ; 1+9*2+14=33
root_dir_sectors dd 14  ; 根目录扇区数
fat1_start      dd 1    ; FAT1起始扇区file_cluster dw 0
free_entry   dd 0
lba_low      db 0
lba_mid      db 0
lba_high     db 0sectors_per_track dd 18
num_heads        dd 2
boot_drive       db 0
hex_chars db '0123456789ABCDEF'

放一下makefile

all:make binmake imgmake runbin:nasm boot.asm -o boot.binnasm loader.asm -o loader.bin#gcc -c -O0 -fno-builtin -m32 -fno-stack-protector -o main.o main.cnasm -f elf -o kernel.o kernel.asmnasm -f elf -o io.o io.asmnasm -f elf -o shell.o shell.asmi686-elf-ld -s -Ttext 0x100000 -o kernel.bin kernel.o io.o shell.oimg : boot.bin loader.bin kernel.bin#dd if=boot.bin of=a.img bs=512 count=1#dd if=loader.bin of=a.img bs=512 seek=1 conv=notrunc#python img.pyedimg   imgin:1.img \wbinimg src:boot.bin len:512 from:0 to:0 \copy from:loader.bin to:@: \copy from:kernel.bin to:@: \imgout:a.imgrun : a.img#qemu-system-i386 -fda a.imgqemu-system-i386 -fda a.img #-hda disk.img -boot a

 最后实现kernel.asm

; Plain Kernel
; kernel.asm[section .text]%include "io.inc"global _start
extern shell
extern init_mouse_start:call hide_cursorcall clear_screenmov ebx, 0          ; 行号mov ecx, 0          ; 列号mov esi, hello_msg  ; 字符串地址mov ah, 0x0Fcall print_strxor ecx, ecxmov ebx, 2          ; 行号call shellhello_msg db "Welcome to Plain - OS !", 0input_buffer times 80 db 0[section .bss]
sector_buf resb 512
bpb_buf    resb 512
fat_buf    resb 512
part_start resd 1
fat_start  resd 1
root_start resd 1
data_start resd 1
root_sectors resd 1

其中io.inc


; 常量定义
ATA_BASE         equ 0x1F0
ATA_DRIVE_SELECT equ 0x1F6
ATA_STATUS       equ 0x1F7
ATA_CMD          equ 0x1F7
ATA_DATA         equ 0x1F0; 全局变量VIDEO_MEMORY equ 0xB8000
LINE_WIDTH   equ 80
WHITE_ON_BLACK equ 0x0FKEYBOARD_PORT equ 0x60
KEYBOARD_STATUS_PORT equ 0x64
KEYBOARD_BUFFER_SIZE equ 32extern put_char
extern print_str
;extern clear_screen
extern get_char
extern get_key
extern read_line
extern scroll_screen
extern init_fs
;extern write_file
extern ata_read
extern ata_wait
extern hide_cursor
extern clear_screen

 

相关文章:

  • MySQL中表的增删改查(CRUD)
  • SQL练习(6/81)
  • Day11-苍穹外卖(数据统计篇)
  • 大规模CFD仿真计算中,SIMPLE或者PISO算法中加速压力场方程迭代求解
  • 股票配资平台开发如何判断交易策略是否可靠
  • 实例分割AI数据标注 ISAT自动标注工具使用方法
  • 【未】[启发式算法]含初始解要求的有:TS, GA, SA, DPSO
  • 计算机网络 : 网络基础
  • NAT转换和ICMP
  • AGI大模型(19):下载模型到本地之ModelScope(魔搭社区)
  • 运维实施31-NFS服务
  • 研华服务器ASMB-825主板无法识别PCIE-USB卡(笔记本)
  • 牛客网NC22222:超半的数
  • 【云实验】搭建个人网盘实验
  • leetcode0215. 数组中的第K个最大元素-medium
  • 1基·2台·3空间·6主体——蓝象智联解码可信数据空间的“数智密码”
  • 2025年长三角+山东省赛+ 认证杯二阶段论文发布!
  • 杰理ac696配置mic
  • QT6 源(104)篇一:阅读与注释QAction,其是窗体菜单栏与工具栏里的菜单项,先给出属性测试
  • 【Golang笔记01】Goland基础语法规则
  • 国内规模最大女子赛艇官方赛事在沪启航,中外41支队伍逐浪
  • 新时代,新方志:2025上海地方志论坛暨理论研讨会举办
  • 著名文博专家吴远明因交通事故离世,享年75岁
  • 国宝文物子弹库帛书二、三卷从美启程,18日凌晨抵京
  • 国际乒联主席索林:洛杉矶奥运会增设混团是里程碑事件
  • 海外考古大家访谈|冈村秀典:礼制的形成与早期中国