「杂文」身为 OIer 的我要在第 7 周速通《汇编语言》,我为什么会做这样的梦(雾)
写在前面
编译器为 MASM-v6.11
一坨屎啊上的课是。
如果看得半懂不懂是正常的,因为我写的匆忙写了一坨屎,不会的都可以问我。
斐波那契数列前 50 项
最多支持输出 30 位十进制数。
输出采用了最原始的直接往显存里写数据,所以这个程序可能在官方钦定的 IDE 上运行不了。
复制复制.model large assume cs:code, ss:stack position segment; 在屏幕上的输出位置 dw 00a0h position ends string segment ;将数组转化为字符串输出 db 30 dup (0), 0 string ends data segment ;三个用于递推 fib 的数组 a1 db 1, 29 dup (0) a2 db 1, 29 dup (0) a3 db 1, 29 dup (0) data ends stack segment ;栈 dw 40H dup(0) stack ends code segment main: call init ;初始化栈 call transforming ;首先输出前两项 call show_string call transforming call show_string mov cx, 50 - 2 ;输出之后的 48 项(理论上可以正确输出无限项,仅需调整程序开头的内存空间与程序中的迭代边界即可。 main_s1: call get_fib ;计算下一项 fib 并存入 a3 call move_fib ;将 a2 移动到 a1,a3 移动到 a2 call transforming ;将 a3 转化为字符串并存入 string call show_string ;输出字符串到显存 loop main_s1 mov ax, 4c00h int 21h init: ;初始化栈 mov ax, stack mov ss, ax mov sp, 40H ret get_fib: ;计算下一项 fib 并存入 a3 push dx push si push ax push bx push cx push ds mov ax, data mov ds, ax mov cx, 30 mov si, 0 mov ax, 0 get_fib_s: add al, ds:[si] ;利用上一次计算的商(进位)与该位相加 mov bl, ds:[si + 30] add al, bl mov bl, 10 ;计算这一位整除 10 div bl mov ds:[si + 60], ah ;将余数存入该位 mov ah, 0 ;清空余数 inc si ;将商用于下一次计算 loop get_fib_s pop ds pop cx pop bx pop ax pop si pop dx ret move_fib: ;将 a2 移动到 a1,a3 移动到 a2 push dx push si push ax push bx push cx push ds mov ax, data mov ds, ax mov cx, 30 mov si, 0 move_fib_s1: ;将 a2 移动到 a1 mov al, ds:[si + 30] mov ds:[si], al inc si loop move_fib_s1 mov cx, 30 mov si, 0 move_fib_s2: ;将 a3 移动到 a2 mov al, ds:[si + 60] mov ds:[si + 30], al inc si loop move_fib_s2 pop ds pop cx pop bx pop ax pop si pop dx ret transforming: ;将 a3 转化为字符串并存入 string push cx push dx push si push ax push bx push ds push es mov ax, data mov ds, ax mov ax, string mov es, ax mov cx, 30 mov si, 0 transforming_s1: ;将 a3 转化为字符串并存入 string mov al, ds:[si + 30] add al, '0' ;数字加 '0' 变为数字字符 mov es:[si], al inc si loop transforming_s1 mov cx, 15 mov si, 0 mov bx, 29 transforming_s2: ;将 stirng 中的内容反向以便于输出 mov al, es:[si] mov dl, es:[bx] ;交换首尾元素 mov es:[bx], al mov es:[si], dl inc si sub bx, 1 loop transforming_s2 pop es pop ds pop bx pop ax pop si pop dx pop cx ret show_string: ;输出字符串到显存 push cx push dx push si push ax push bx push ds push es mov ax, position ;当前输出的位置 mov ds, ax mov bx, ds:[0] mov cl, 000fh ;color mov ax, string mov ds, ax mov si, 0 mov ax, 0b800h mov es, ax mov al, cl mov ch, 0 mov si, 0 mov ah, 0 show_string_s: mov cl, ds:[si] jcxz show_string_ok sub cl, '0' add cl, ah jcxz show_string_is_0 ;忽略前导零 sub cl, ah mov ah, 1 add cl, '0' mov es:[bx], cl ;输出该位 inc bx mov es:[bx], al inc bx show_string_is_0: inc si jmp short show_string_s show_string_ok: mov cl, ' ' ;输出空格 mov es:[bx], cl inc bx mov es:[bx], al inc bx mov ax, position ;将当前输出位置存入内存 mov ds, ax mov ds:[0], bx mov cx, 30 mov ax, string mov ds, ax mov si, 0 show_string_s2: ;清空字符串 mov ax, 0 mov ds:[si], ax inc si loop show_string_s2 pop es pop ds pop bx pop ax pop si pop dx pop cx ret code ends end
求 1e7 内的质数
出这么几把提老师是恼弹?
地球人都知道的的数论性质是合数 至少有一个不大于 的大于 1 的因子。考虑反证,另一个地球人都知道的结论是因数都是成对出现的,即若 是 的因子,则 也是 的因子。则若某个合数 的因子均大于 ,则有因数 满足 ,得 ,荒谬的,则原结论成立。
于是如果要使用试除法判断一个数是否是合数,仅需枚举 即可。
回到这个恼弹实验,因为要判断的数很大,于是考虑使用压位高精,把两个 16 位内存当 32 位内存用,手动处理进位和借位。求余运算则用多次减法替代。
复杂度上界 再乘上减法模拟求余的超大常数,跑的超级慢。
加了点优化,从 3 开始枚举并每次加 2,枚举因子试除时从小到大枚举,能快一点是一点呃呃。
如果真的要输出到 1e7 需要跑到下个月或者明年。
这逼东西怎么检查啊、、、
assume cs:code, ss:stack zero segment db 0 zero ends value segment ;存当前要检查是否为质数的值 dw 3, 0 value ends stack segment ;栈 dw 40H dup(0) stack ends code segment main: call init ;初始化栈 mov dl, '2' ;先输出 2 和空格 mov ah, 2 int 21h mov dl, ' ' mov ah, 2 int 21h mov cx, 1000 ;二重循环来枚举 3~1e7 的值 main_s1: push cx mov cx, 10000 main_s2: call check ;检查当前枚举到的 value 是否为质数 push cx mov cx, ax jcxz main_s3 call show main_s3: pop cx call get_next ;value += 2 call get_next sub cx, 1 loop main_s2 pop cx loop main_s1 mov ax, 4c00h int 21h init: ;初始化栈 mov ax, stack mov ss, ax mov sp, 40H ret get_next: ;计算下一项 push dx push si push ax push bx push cx push es push ds mov ax, value mov es, ax mov ax, es:[0] ;ax 为低位 mov bx, es:[2] ;bx 为高位 add ax, 1 cmp ax, 10000 jb get_next_done mov ax, 0 ;低位超过 10000 则进位 add bx, 1 get_next_done: mov es:[0], ax mov es:[2], bx pop ds pop es pop cx pop bx pop ax pop si pop dx ret check: ; mov ax, 1 ; ret push cx push dx push si push bx push es mov ax, value mov es, ax mov ax, es:[0] mov bx, es:[2] mov dx, 2 mov cx, 9999 ;确定试除的上界 cmp bx, 1 jae check_s1 cmp ax, 3 jbe check_is_prime mov cx, ax sub cx, 1 check_s1: mov ax, value mov es, ax mov ax, es:[0] mov bx, es:[2] check_s2: ;使用当前 cx 的值对 value 进行试除,减法模拟求余运算 cmp ax, dx ja check_minus jb check_s3 cmp bx, 0 je check_is_not_prime ;恰好整除则非质数 check_s3: cmp bx, 1 ;向高位借位,借不到则无法整除,检查下一个 cx jb check_s4 sub bx, 1 add ax, 10000 check_minus: sub ax, dx ;做减法模拟求余运算 jmp check_s2 check_s4: sub cx, 1 inc dx cmp cx, 1 ja check_s1 check_is_prime: mov ax, 1 jmp check_done check_is_not_prime: mov ax, 0 check_done: pop es pop bx pop si pop dx pop cx ret show: ;输出字符串 push cx push dx push si push ax push bx push es mov ax, zero mov ds, ax mov ax, 0 mov ds:[0], ax mov ax, value mov es, ax mov bx, es:[2] ;先输出高位 mov cx, 1000 call dout mov cx, 100 call dout mov cx, 10 call dout mov cx, 1 call dout mov bx, es:[0] ;再输出低位 mov cx, 1000 call dout mov cx, 100 call dout mov cx, 10 call dout mov cx, 1 call dout mov dl, ' ' mov ah, 2 int 21h ;输出一个空格 pop es pop bx pop ax pop si pop dx pop cx ret dout: push dx push si push ax push es mov dx,0 ;dx清0,除cx时,被除数为dx,ax mov ax,bx ;将bx值(第一次为输入的数,随后为余数)赋值给ax div cx ;(dx,ax),实际为ax(dx==0)除以cx(cx值在调用程序前设置,作为参数传递进来) xchg ax,dx ;ax与dx交换内容。交换后:ax中为余数,dx中为商 mov bx,ax ;将ax值(余数)赋予bx(进入下一轮运算) ;如果用户前面输入65535,那么在第一轮除以10000后,dx中值为6,bx中值为5535 cmp dl,0 jne outanum ;如果dx中值不为0,则直接输出相应的数值 mov ax, zero mov ds, ax mov ax, 0 cmp ds:[0], ax ;如果dx中值为0,那么判断是前面无意义的0,还是中间有意义的0。 ;如305,那么如果不进行次判断将输入00305。通过此位可以不输出前面两个0,但是输出中间0。 je con ;如果是前面无意义的0 ,则不输出 outanum: mov ax, 1 mov ds:[0], ax ;如果输出了一个大于0的数字,则置标志位为1,使得其后所有0都会被输出 add dl, 30h ;dl中数值加上30h,变成对应的ASCII码。 mov ah, 2 int 21h ;输出该数字 con: pop es pop ax pop si pop dx ret code ends end
任意数量有符号整数冒泡排序
任意数量()的有符号数的排序。
按行输入要排序的数,当输入了空行时停止输入。
.model large assume ds:data, cs:code data segment buf db ? db ? db 10 dup(?) array dw 500 dup(?) number dw 1 dup(?) symbol dw 1 dup(?) data ends code segment main: mov [number], 0 input: lea dx, buf ; 输入字符串,到buf中 mov ah, 0ah int 21h mov dl, 13 ; 打印换行 mov ah, 02h int 21h mov dl, 0ah mov ah, 02h int 21h mov bl, [buf + 2] cmp bl, 13 jz input_end call read ; 调用ReadUInt,该函数将buf转换成整数,存放到ax中 mov si, [number] shl si, 1 ; si = si * 2,一个数字占2个字节,因此乘以2 mov [array + si], ax ; 将读取的数字存放到数组中 shr si, 1 inc si mov [number], si jmp input input_end: push sp mov si, 0 ; 冒泡排序,i=si,j=di mov di, 0 L1: cmp si, [number] jge L1_END mov di, si ; j = i + 1 inc di L2: cmp di, [number] jge L2_END shl si, 1 shl di, 1 mov ax, [array+si] mov bx, [array+di] shr si, 1 shr di, 1 cmp ax, bx jg exchange inc di jmp L2 exchange: shl si, 1 shl di, 1 mov [array+di], ax mov [array+si], bx shr si, 1 shr di, 1 inc di jmp L2 L2_END: inc si jmp L1 L1_END: mov bx, 0 L3: cmp bx, [number] jge L3_END mov si, bx shl si, 1 mov ax, [array+si] call print_int push bx push si mov dl, 13 ; 打印换行 mov ah, 02h int 21h mov dl, 0ah mov ah, 02h int 21h pop si pop bx inc bx jmp L3 L3_END: pop sp mov ah, 4ch int 21h read proc pushf push bx push cx push dx push si push di mov [symbol], 0 mov cx, 0 mov cl, [buf + 1] ; 获取字符串长度 lea si, [buf + 2] ; 获取字符串地址 mov ax, 0 mov di, 0 add si, di mov bl, [si] cmp bl, '-' jne read_s1 mov [symbol], 1 sub cl, 1 inc di read_s1: lea si, [buf + 2] add si, di ; 获取当前处理的字符地址 inc di mov bl, [si] ; 获取字符 sub bl, 48 ; 字符减去'0' mov dl, 10 mul dl ; 乘以10 mov bh, 0 add ax, bx ; 加上数字 loop read_s1 mov bx, [symbol] cmp bx, 1 jne read_s1_end neg ax read_s1_end: pop di pop si pop dx pop cx pop bx popf ret read endp print_int proc pushf ; save eflags and register push bx push cx push dx mov [symbol], 0 cmp ax, 0 jz print_zero js print_minus jmp skip_print_zero_and_minus print_zero: mov dl, '0' mov ah, 02h int 21h jmp print_int_end2 print_minus: neg ax mov [symbol], 1 skip_print_zero_and_minus: mov cx, 0 mov bx, 10 print_int_loop1: mov dx, 0 div bx add dl, 30h push dx inc cx cmp ax, 0 jne print_int_loop1 cmp [symbol], 1 jnz print_int_loop2 mov dl, '-' mov ah, 02h int 21h print_int_loop2: pop dx mov ah, 02h int 21h loop print_int_loop2 print_int_end2: mov dl, 13 ; 打印换行 mov ah, 02h int 21h pop dx pop cx pop bx popf ret print_int endp code ends end main
写在最后
写的一坨屎。
作者@Luckyblock,转载请声明出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!