「杂文」身为 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 内的质数

出这么几把提老师是恼弹?

地球人都知道的的数论性质是合数 \(x\) 至少有一个不大于 \(\sqrt x\) 的大于 1 的因子。考虑反证,另一个地球人都知道的结论是因数都是成对出现的,即若 \(d\)\(x\) 的因子,则 \(\frac{x}{d}\) 也是 \(x\) 的因子。则若某个合数 \(x\) 的因子均大于 \(\sqrt x\),则有因数 \(d\) 满足 \(d>\sqrt{x}, \frac{x}{d} > \sqrt{x}\),得 \(d\times \frac{x}{d} > x\),荒谬的,则原结论成立。

于是如果要使用试除法判断一个数是否是合数,仅需枚举 \(1\sim \sqrt x\) 即可。

回到这个恼弹实验,因为要判断的数很大,于是考虑使用压位高精,把两个 16 位内存当 32 位内存用,手动处理进位和借位。求余运算则用多次减法替代。

复杂度上界 \(O(n\sqrt n)\) 再乘上减法模拟求余的超大常数,跑的超级慢。

加了点优化,从 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

任意数量有符号整数冒泡排序

任意数量(\(\le 500\))的有符号数的排序。

按行输入要排序的数,当输入了空行时停止输入。

.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       

写在最后

写的一坨屎。

posted @ 2023-10-20 23:32  Luckyblock  阅读(68)  评论(0编辑  收藏  举报