Misaka-xxw的博客

昨日种种,皆成今我


计科期末考试出题

  1. 读程序题
  2. 程序填空题
  3. 写程序题:计算
  4. 写程序题:循环分支
  5. 写程序题:中断

第3章 80x86的指令系统和寻址方式

80x86的寻址方式


80x86的指令系统

数据传送指令(p47)

通用数据传送指令

mov
  1. mov mem/reg1,mem/reg2
    先略。。。直接看书
push进栈指令
1
push src

(sp) ← (sp)-2
((sp)+1,(sp)) ← (src)

格式

push reg
push mem
push data8086不行
push segreg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
push ax      ; 推送 AX 寄存器
push bx ; 推送 BX 寄存器
push cx ; 推送 CX 寄存器
push dx ; 推送 DX 寄存器
push sp ; 推送 SP 寄存器
push bp ; 推送 BP 寄存器
push si ; 推送 SI 寄存器
push di ; 推送 DI 寄存器

push word ptr [address] ; 推送内存地址中的值

push cs ; 推送 CS 段寄存器
push ds ; 推送 DS 段寄存器
push es ; 推送 ES 段寄存器
push ss ; 推送 SS 段寄存器
pop出栈指令

注意要画栈图

1
pop dst

(dst) ← ((sp)+1,(sp))
(sp) ← (sp)+2

pop reg
pop mem
pop data8086不行
pop segreg

累加器专用传送指令

这些指令不影响标志位

in
1
2
3
4
in al,port ;字节, (al) ← (port)
in ax,port ;字, (ax) ← (port+1,port)
in al,dx ;字节, (al) ← ((dx))
in ax,dx ;字, (ax) ← ((dx)+1,(dx))
out
1
2
3
4
out port,al ;字节, port ← (al)
out port,ax ;字, (port+1,port) ← (ax)
out dx,al ;字节, ((dx)) ← (al)
out dx,ax ;字, ((dx)+1,(dx)) ← (ax)
xlat换码指令

al=[bx+al]

地址传送指令

lea有效地址送寄存器
1
lea reg,sec ;(reg)<-src

异同:lea bx,listmov bx,offset list, mov更快

lds, les, lfs, lgs, lss指针送寄存器和?s

p56, 实验暂时不用

标志寄存器传送指令

p57, 实验暂时不用

类型转换指令

cbw

将al的内容符号扩展到ah,形成ax的字。
若(AL)的最高有效位为0,则(AH)= 00H
若(AL)的最高有效位为1,则(AH)= 0FFH

cwd/cwde

将ax的内容符号扩展到dx,形成dx:ax中的双字。
若(AX)的最高有效位为0,则(DX) = 0000H
若(AX)的最高有效位为1,则(DX) = 0FFFFH


算术指令

加法指令

add加法
adc带进位加法
inc 加1指令

减法指令

sub减法
sbb带借位减法
dec减1
neg求补

按位求反+1

cmp比较

前操作数-后操作数,但是不保存结果,只设置条件标志位

乘法指令

目的操作数必须是累加器

mul无符号数乘法
1
mul src

(ax)<-(al)(src) ;字节操作数
(dx,ax)<-(ax)
(src) ;字操作数

imul带符号数乘法

同上,但是是带符号数

除法指令

div无符号数除法

div src

字节操作:
(ax)/(src)=(al)……(ah)
字操作:
(dx,ax)/(src)=(ax)……(dx)
注意进制转换的代码

idiv带符号数除法

同上

十进制调整指令

有压缩的BCD码调整指令:
DAA 加法的十进制调整指令
DAS 减法的十进制调整指令

非压缩的BCD码调整指令:
AAA 加法的ASCII调整指令
AAS 减法的ASCII调整指令
AAM 乘法的ASCII调整指令
AAD 除法的ASCII调整指令

逻辑指令

逻辑运算指令

and 逻辑与指令
or 逻辑或指令
not 逻辑非指令
xor 异或指令

要使操作数的某些位变反,使用xor,那些位设为1

test 测试指令

做and运算,但是不保存结果,只置条件码
要测试操作数的某位是都为1,可以先将操作数取反,再用test测试。

移位指令(p73)

e.g. shl opr,cnt
在8086中,cnt可以是cl或者1

shl 逻辑左移指令

无符号数乘法

sal 算数左移指令

有符号数乘法

shr 逻辑右移指令

无符号数除法

sal 算数右移指令

有符号数除法

rol 循环左移指令
ror 循环右移指令
rcl 带进位循环左移指令
rcr 带进位循环右移指令

串处理指令

这里要写一下字符串代码

与rep相配合工作的movs, stos, lods, ins, outs

rep重复串操作

重复串操作直到计数器Count Reg的内容为0位为止
操作:

1
2
3
while Count_Reg != 0:
Count_Reg-=1
执行后面的串指令
movs 串传送指令

格式

1
2
3
movs dst,src
movsb ;字节
movsw ;字

第一种格式要在操作数里标名是字还是字节操作。
movs es:byte ptr[di],ds:[si]

相当于strcpy
movs可以把由源变址寄存器指向的数据段中的一个字(或字节),传到目的变址寄存器指向的附加段中的一个字(或字节)去,同时根据方向标志以及数据格式(字or字节),对源变址寄存器和目的变址寄存器进行修改。

执行操作:

  1. ((DI)) ← ((SI))
  2. 字节操作:(SI)←(SI)±1, (DI)←(DI)±1
    字操作: (SI)←(SI)±2, (DI)←(DI)±2
    方向标志DF=0时用 +,DF=1时用 - 。

执行REP MOVS之前,应先做好:

  1. 源串首地址(末地址)→ SI
  2. 目的串首地址(末地址)→ DI
  3. 串长度 → CX
  4. 建立方向标志(CLD使DF=0,STD使DF=1)

方向标志

  • cld: df=0, 地址自动增量
  • std: df=1, 地址自动减量
stos 存入串指令

格式

1
2
3
stos dst
stosb ;字节
stosw ;字

相当于memset
将al、ax的内容存入由目的变址寄存器指向的附加段的某单元中,并根据……
初始化某一个缓冲区时很有用

执行操作:

  1. 字节操作:((DI))←(AL), (DI)←(DI)±1
  2. 字操作:((DI))←(AX), (DI)←(DI)±2

例:把附加段中的5个字节缓冲区置为20H

1
2
3
4
5
lea  di, mess2
mov al, 20H
mov cx, 5
cld
rep stosb

lods 从串取指令

格式

1
2
3
lods src
lodsb ;字节
lodsw ;字

LODS指令一般不与REP联用
源串必须在数据段中,目的串必须在附加段中,
但源串允许使用段跨越前缀来修改。
不影响条件标志位

执行操作:
字节操作:(AL)←((SI)),(SI)←(SI)±1
字操作:(AX)←((SI)),(SI)←(SI)±2

ins 串输入

格式

1
2
3
ins dst,dx
insb ;字节
insw ;字

执行操作:
字节操作:(DI)←((DX)), (DI)←(DI)±1
字操作:(DI+1,DI)←((DX)), (DI)←(DI)±2

outs 串输出

格式

1
2
3
outs dx,src
outsb ;字节
outsw ;字

执行操作:
字节操作:((DX)) ← (SI), (SI)←(SI)±1
字操作:((DX)) ←(SI+1,SI), (SI)←(SI)±2

repe/repz,repne/repnz,cmps,scas

repe/repz 为相等/为零时重复串操作
1
2
3
while Count_Reg!=0 and zf!=0:
Count_Reg-=1
执行其后的串指令
repne/repnz 为不相等/不为零时重复串操作
1
2
3
while Count_Reg!=0 and zf!=1:
Count_Reg-=1
执行其后的串指令
cmps 串比较指令

格式

1
2
3
cmps src,dst
cmpsb ;字节
cmpsw ;字

相当于strcmp

执行操作:
(1) ((SI)) - ((DI))
根据比较结果设置条件标志位:相等 ZF=1
(2) 字节操作:(SI)←(SI)±1, (DI)←(DI)±1
字操作:(SI)←(SI)±2, (DI)←(DI)±2

scas串扫描指令

格式

1
2
3
scas dst
scasb ;字节
scasw ;字

执行操作:
字节操作:(AL) - ((DI)), (DI)←(DI)±1
字操作:(AX) - ((DI)), (DI)←(DI)±2

控制转移指令

后面再看

无条件转移指令

jmp

条件转移指令(p89)

jz/je
jnz
js
jns
jo
jno
jp/jpe
jnp/jpo
jb/jnae
jnb/jae/jnc

循环指令

loop

Count_Reg!=0

loopz/loope

Count_Reg!=0 && zf=1

loopnz/loopne

Count_Reg!=0 && zf=0

子程序

call
ret

第8章 输入输出程序设计

(面向考试复习)

程序直接控制I/O方式(p283)

I/O指令(p284)

in和out

in 完成I/O -> CPU的信息传送
out完成CPU -> I/O的信息传送

0~0ffh,即0~255端口可以这样写:

1
2
3
4
5
6
;port是端口地址,一个8位的立即数
in al,port ;(al)<-(port)
in ax,port ;(ax)<-(port+1,port)

out port,al ;(port)<-(al)
out port,ax ;(port+1,port)<-(ax)

0~0ffffh,即0~255端口可以这样写(当端口>0ffh时,必须这样写!)

1
2
3
4
5
6
;dx里存了端口地址
in al,dx ;(al)<-((dx))
in ax,dx ;(ax)<-((dx)+1,(dx))

out dx,al ;((dx))<-(al)
out dx,ax ;((dx)+1,(dx))<-(ax)

注意端口是否带“h”,例如126h是大于十进制的255的,就得用dx

用法示例

  1. 把一个字,从端口地址28和29,传到存储器的字单元data_word中

    1
    2
    in  ax,28h
    mov data_word,ax
  2. 检测端口地址27h的第2位是否为1,如果1就是转到错误处理
    注意这里的第几位是从右到左从0数起
    因为test就是只改变flags的and,所以用来查1

    1
    2
    3
    in   al,27h
    test al,00000100b
    jnz error
  3. 将端口地址为126h的命令寄存器,第7位置1

    1
    2
    3
    4
    mov dx,126h
    in al,dx
    or al,80h ;80h=10000000b
    out dx,al

中断传送方式

8086的中断分类(p290)

中断标志位if

设置中断允许位(if=1)sti set interupt flag
清除中断允许位(if=0)cli clear interupt flag

中断屏蔽寄存器(端口21h)

7 6 5 4 3 2 1 0
打印机 软盘 硬盘 串行通信口1 串行通信口2 保留 键盘 定时器

中断屏蔽寄存器(端口20h)

7 6 5 4 3 2 1 0
R SL EOI 0 0 L2 L1 L0

扩充(实验书p92):

键盘的输入寄存器(PA,端口60h)

PB位7 7 6 5 4 3 2 1 0
PB7=1 磁盘驱动器数 显示器类型 系统板上RAM数 未用 非磁盘系统
PB7=0 键盘扫描码

控制寄存器(PB,端口61h)

7 PA选源,1=键盘应答
6 0=禁止键盘时钟
5 0=允许由扩充槽的错误信号
4 0=允许ram
3 0=启开盒式带马达
2 PC0~3选源
1 扬声器脉冲门
0 定时器2与门

1ch中断一秒钟约产生18次中断,具体使用可以看2324期末卷最后一题

中断向量表(p293)

功能 预置 执行 返回
设置中断向量 ah=25h
al=中断类型号
ds:dx=中断向量
int 21h
取中断向量 ah=35h
al=中断类型号
int 21h es:bx=中断向量

中断操作:

  1. 取中断类型号
  2. 计算中断向量地址
  3. 取中断向量,偏移地址送IP,段地址送CS
  4. 转入中断处理程序
  5. 中断返回到int的下一条指令

使用dos功能调用存取中断向量,模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
;保存原来的中断向量
mov al,n ;中断类型号
mov ah,35h ;取中断向量到es:bx
int 21h
push es
push bx
;设置新的中断向量
push ds
mov ax,seg myfunc
mov ds,ax
mov dx,offset myfunc
mov al,n
mov al,25h
int 21h
pop ds
;重新设置原来的中断向量
pop dx ;原来的bx
pop ds ;原来的es
mov al,n
mov ah,25h
int 21h
ret
;...
myfunc proc near
;...
iret
myfunc endp
;...

p300的程序可能会被改成考试题

p301响铃程序

只有铃响:

1
2
3
4
5
6
7
8
9
        mov    dx, 100h
in al, 61h
and al, 11111100b
sound: xor al, 2
out 61h, al
mov cx, 140h
wait1: loop wait1
dec dx
jne sound

完整程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
datarea segment
count dw 1
msg db 'The bell is ringing',0dh,0ah,'$'
datarea ends
prognam segment
main proc far
assume cs:prognam,ds:datarea,es:datarea
start:
push ds
sub ax,ax
push ax
mov ax,datarea
mov ds,ax
mov es,ax

;保存旧的中断向量
mov al,1ch ;1ch中断
mov ah,35h ;取中断向量,保存在es:bx里
int 21h
push bx
push es

;设置新的中断向量
push ds
mov dx,offset ring ;ring的偏移地址
mov ax,seg ring ;ring的段地址
mov ds,ax ;ds:dx=段地址:偏移地址
mov al,1ch ;1ch中断
mov ah,25h ;设置中断向量
int 21h
pop ds

;将21h端口的第0位清零,即打开定时器
in al,21h
and al,11111110b
out 21h,al
sti ;设置中断标志,允许中断。

;循环20000*30000次,因为次数太多一个寄存器装不下
mov di,20000
delay:
mov si,30000
delay1:
dec si
jnz delay1
dec di
jnz delay

;重新设置回旧的中断向量
pop dx
pop ds
mov al,1ch
mov ah,25h
int 21h
ret
main endp
ring proc near
push ds
push ax
push cx
push dx

mov ax,datarea
mov ds,ax
sti

dec count
jnz exit
;打印消息'The bell is ringing'
mov dx,offset msg
mov ah,09h
int 21h

mov dx,100 ;打开关闭的次数
in al,61h
and al,0fch ;61h端口的第0、1位清0,分别是定时器2和扬声器脉冲门
sound:
xor al,02 ;61h端口的第1位转置,扬声器脉冲门
out 61h,al

;等待,因为cpu比端口快的多
mov cx,1400h
wait1:
loop wait1

dec dx
jne sound
mov count,182 ;10s的铃响delay
exit:
cli
pop dx
pop cx
pop ax
pop ds
iret
ring endp
prognam ends
end start

堆栈情况

alt text
堆栈段(SS)没有变化,始终保持为 076B。
堆栈指针(SP)在每次执行PUSH指令后减少2,这是因为每次PUSH操作都会将一个寄存器的值(2 字节)压入堆栈。

  1. 一开始,sp初始化为0
  2. push ds后,sp=0000h-2=0fffeh,压入ds=075Ch
  3. sub ax,ax后,ax=0
  4. push ax后,sp=0fffeh-2=0fffch,压入ax=0

附录 DOS系统功能调用(常用)

AH 功能 调用参数 返回参数
00 程序终止 CS=程序段前缀PSP
01 键盘输入并回显 AL=输入字符
02 显示输出 DL=输出字符
09 显示字符串 DS:DX=串地址 字符串以’$’结尾
0A 键盘输入到缓冲区 DS:DX=缓冲区首址 (DS:DX)=缓冲区最大字符数 (DS:DX+1)=实际输入的字符数
25 设置中断向量 DS:DX=中断向量
AL=中断类型号
35 取中断向量 AL=中断类型号 ES:BX=中断向量

常用debug(实验书p186)

  • -d: 显示内存单元内容
  • -r: 寄存器和标志位状态
  • -g: 运行
  • -t: 单步运行
  • -u: 反汇编
  • -q: 退出
标志名 置位 复位
溢出Overflow 是OV 否NV
方向Direction 减量DN 增量UP
中断Interrupt 允许EI 屏蔽DI
符号Sign 负NG 正PL
零Zero 是ZR 否NZ
辅助进位Auxiliary Carry 是AC 否NA
奇偶Parity 偶PE 奇PO
进位Carry 是CY 否NC

实验代码

成绩排名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
datarea segment
grade dw 88,75,95,63,98,78,87,73,90,60
rank dw 10 dup(?)
datarea ends
prognam segment
main proc far
assume cs:prognam,ds:datarea,es:datarea
start:
push ds
sub ax,ax
push ax
mov ax,datarea
mov ds,ax
mov es,ax
call sort
call output
ret
main endp
sort proc
mov si,0
mov cx,10
outer_loop:
push cx
mov cx,10
mov dx,1
mov ax,grade[si]
mov di,offset grade
inner_loop:
cmp ax,[di]
jge no_add
inc dx
no_add:
add di,2
loop inner_loop
pop cx
mov rank[si],dx
add si,2
loop outer_loop
ret
sort endp
output proc
mov cx,10
mov si,0
t1:
push cx
mov ax,rank[si]
call print_dec
mov dl,' '
call putchar
pop cx
add si,2
loop t1
ret
output endp
putchar proc
mov ah,2
int 21h
ret
putchar endp
print_dec proc
;ax/?=al...ah, result:al,ah
mov bl,10
div bl
mov bx,ax
mov dl,bl
add dl,'0'
call putchar
mov dl,bh
add dl,'0'
call putchar
ret
print_dec endp
prognam ends
end start

ASCII码表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
datarea segment
datarea ends
prognam segment
main proc far
assume ds:datarea,cs:prognam,es:datarea
start:
push ds
sub ax,ax
push ax
mov ax,datarea
mov ds,ax
mov es,ax

mov dx,10h
mov bx,0
rotate:
mov ah,2
int 21h
inc bx
inc dx
push dx
cmp bx,16
mov dl,' '
mov ah,2
int 21h
jl continue
xor bx,bx
mov dl,0dh
mov ah,2
int 21h
mov dl,0ah
mov ah,2
int 21h
continue:
pop dx
cmp dx,100h
jl rotate
ret
main endp
prognam ends
end start

反转字符串

我流版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
datarea segment
limit db 30
len db ?
str db 30 dup('$')
datarea ends
prognam segment
main proc far
assume cs:prognam,ds:datarea,es:datarea
start:
push ds
sub ax,ax
push ax
mov ax,datarea
mov ds,ax
mov es,ax

;输入字符串
lea dx,limit
mov ah,0ah
int 21h
;回车换行
mov dl,0dh
mov ah,2
int 21h
mov dl,0ah
mov ah,2
int 21h
;di置为字符串长度减一,cx计数器置为字符串长度除以2
mov si,0
mov bl,len
xor bh,bh
mov di,bx
dec di
mov cx,1
;除以2
shr bx,cl
mov cx,bx
rotate:
mov al,str[si]
mov bl,str[di]
mov str[si],bl
mov str[di],al
inc si
dec di
loop rotate
lea dx,str
mov ah,9
int 21h
ret
main endp
prognam ends
end start

ppt递归版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
datarea segment
str db "123456",'$'
datarea ends
prognam segment
main proc far
assume cs:prognam,ds:datarea,es:datarea
start:
push ds
sub ax,ax
push ax
mov ax,datarea
mov ds,ax
mov es,ax

mov bx, offset str
push bx
call revers
pop bx
mov dl, [bx]
mov ah, 2
int 21h

ret
main endp
revers proc near
push bp
mov bp, sp
push ax
push bx
push dx
mov bx, [bp+4]
mov al, [bx]
cmp al, '$'
je return
re_call:
inc bx
push bx
call revers
pop bx
mov dl, [bx]
mov ah, 2
int 21h
return:
pop dx
pop bx
pop ax
pop bp
ret
revers endp

prognam ends
end start

查找子串位置,十进制、十六进制输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
datarea segment
sentence_label label byte
sentence_max db 50
sentence_len db ?
sentence db 50 dup('$')
key_label label byte
key_max db 30
key_len db ?
key db 30 dup('$')

cmd1 db 'Input a sentence:','$'
cmd2 db 'Input a keyword:','$'
no_match_str db 'No match!',0dh,0ah,'$'
match_str db "The location:",'$'
crlf_str db 0dh,0ah,'$'

datarea ends
prognam segment
main proc far
assume cs:prognam,ds:datarea,es:datarea
start:
push ds
sub ax,ax
push ax
mov ax,datarea
mov ds,ax
mov es,ax
loops:
lea dx,cmd1
call cout
call cin_sentence
call crlf
cmp sentence[0],3 ;比较ctrl+c
je exit
lea dx,cmd2
call cout
call cin_key
call crlf
call compare
jmp loops
exit:
ret
main endp
cin proc
mov ah,0ah
int 21h
ret
cin endp
cin_sentence proc
lea dx,sentence_label
call cin
ret
cin_sentence endp
cin_key proc
lea dx,key_label
call cin
ret
cin_key endp
cout proc
mov ah,09h
int 21h
ret
cout endp
crlf proc
lea dx,crlf_str
call cout
ret
crlf endp
compare proc
;cx=sentence_len-key_len+1
xor ch,ch
xor dh,dh
xor bx,bx
mov cl,sentence_len
mov dl,sentence_len
sub cx,dx
inc cx
rotate:
lea si,sentence[bx]
lea di,key
mov cl,key_len
xor ch,ch
cld
rep cmpsb
jz match
inc bx
loop rotate
no_match:
lea dx,no_match_str
call cout
ret
match:
lea dx,match_str
call cout
call print_dec
call crlf

lea dx,match_str
call cout
call print_hex
call crlf

ret
compare endp
;打印十进制数字
print_dec proc
;ax/?=al...ah
push bx
mov ax,bx
xor cx,cx
div10:
mov bl,10
div bl
push ax
inc cx
xor ah,ah
cmp al,0
jne div10
print10:
pop dx
mov dl,dh
add dl,30h
mov ah,2
int 21h
loop print10
pop bx
ret
print_dec endp
;打印十六进制数字
print_hex proc
push bx
xor ax,ax
div16:
mov dx,0fh
and dx,bx
push dx
inc ax
mov cl,4
shr bx,cl
cmp bx,0
jne div16
mov cx,ax
print16:
pop dx
add dx,'0'
cmp dx,'9'
jle not_letter
add dx,'A'-'0'-10
not_letter:
mov ah,2
int 21h
loop print16
pop bx
ret
print_hex endp
prognam ends
end start

之前Pyinstaller打包失败,所以换成cx-Freeze打包。

官网写的很清楚。
最简单粗暴的打包是这样的,可执行文件和pyinstaller一样生成在dist文件夹里

1
cxFreeze main.py

例如:

1
2
3
4
5
6
7
8
9
10
cxfreeze --script hello.py --target-dir dist

--script=要打包的主py文件
--init-script=开始启动的文件,如果没写绝对路径的话,会去cx_Freeze package的根目录里找
--base=NAME, --base-name=NAME
--target-name=生成可执行文件的名字,不要包含路径
--target-dir=生成可执行文件所在目录,默认为dist
--icon=图标
--shortcut-name=快捷方式的名字(MSI)
--shortcut-dir=MSI安装包

如果不想出现控制台窗口的话:就加上

1
--base-name="win32gui"

e.g.
1
cxfreeze --script main.py --target-dir dist --base-name="win32gui"

只打包了main.py,其它文件没有被打包上。copy到
每次都要敲命令行非常麻烦,新建一个bat文件批量复制,例如:

1
2
3
4
5
6
7
8
9
10
11
set "root_path=%~dp0"
cxFreeze --script main.py --icon=%root_path%Assets\icons\logo_orange.ico --target-dir dist --base-name="win32gui"
set exe_path=%root_path%dist
XCOPY %root_path%\Assets\ %exe_path%\Assets\ /E /I
XCOPY %root_path%\Temp\ %exe_path%\Temp\ /E /I
XCOPY %root_path%\Templates\ %exe_path%\Templates\ /E /I
XCOPY %root_path%\Views\ %exe_path%\Views\ /E /I
XCOPY %root_path%\Sqlite\ %exe_path%\Sqlite\ /E /I
XCOPY %root_path%\.venv\Lib\site-packages\sqlalchemy\ %exe_path%\lib\sqlalchemy\/E /I
echo END!
pause

原生pyqt


fluent

添加designer-fluent启动的外部工具

  • 学到了一个很好的脚本打开方式,新建一个这样的txt记事本文件,然后把后缀名改成.bat,最后放到外部工具里,
1
2
@echo off
start cmd /k "cd /【你放Fluent组件的盘符】 【你clone这个PyQt-Fluent-Widgets的文件夹】 && conda activate 【你的conda环境】 && python tools/designer.py"
  • 比如说,我的是这样的:
1
2
@echo off
start cmd /k "cd /d D:\GitHub\PyQt-Fluent-Widgets && conda activate D:\python\envs\qt5_env && python tools/designer.py"
  • 如果报错了,那么就一句一句复制贴贴到cmd里,看看哪里出问题。比如说,我们那时候,作者似乎不小心把tools文件夹给删了。。。这时候切换分支即可。
    例子:
1
2
3
4
/d
cd D:\GitHub\PyQt-Fluent-Widgets
conda activate D:\python\envs\qt5_env
python tools/designer.py
  • 没问题的话可以添加到pycharm里:
    文件/设置/工具/外部工具/+
    写上你工具的路径名,起个顺眼的名字。
    alt text
    当然你也可以用c/c++等语言写出来,编译,然后放exe文件。

实战问题大全和解决方法

在designer里设置的背景图片和icon在python里打开看不到

因为uic.loadUi 这个方法已经把背景图片和icon加载进去了
其实你可以换另一种方法,就是Designer里面不设置icon和背景图片,但在py里面设置
外部的图片 icon 资源不要在ui文件中引用 应在外部的py文件里写入。这么做有三个好处,一是分离资源,方便后续的管理,二是方便调试和自定义操作,三是可以减少ui文件的体积

解决未铺满问题

alt text
首先要用布局器进行布局 而不是frame
alt text
虽然frame一定程度上能对控件进行分类,但是效果不如布局器的好
尽量用Widget作为新建窗口 使用MainWindow的话,一是语义不符,二是有些臃肿

1
2
3
4
# 在每个窗体的py文件里面加上 这三行代码 保证布局的稳定
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)

聊天气泡框

csdn博客介绍
gitcode开源链接

pyinstaller和pipenv:py打包

踩坑记录:
首先用conda不知道为什么会出错。用了Virtualenv。
其次,它有一些import问题。
还是打包简单的项目文件比较好。后面我使用cx_Freeze配合bat后缀文件打包,解决了打包问题。可以看我的另一篇博客

年少不懂事,做mikumikudance时期,用记事本打开readme点md
我一般用vscode看,其实用jetbrain的软件也可以
vscode配这三个插件:alt text
完美。下载安装和学习这个就可以了
既然这就是个md文件,所以还要简单记录一下格式吗?
要的,我抄一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题

---
上面是划线,如果划线上有字会变成二级标题。一堆=上面有字是一级标题。
各种分割线
***

* * *

*****

- - -

----------
*斜体文本*
_斜体文本_
**粗体文本**
__粗体文本__
***粗斜体文本***
___粗斜体文本___
~浅一点的字体~
~~删除线,非常爱用~~
==高光==

* 第一项
* 第二项
* 第三项

+ 第一项
+ 第二项
+ 第三项


- 第一项
- 第二项
- 第三项

1. 第一项
2. 第二项
3. 第三项

> 最外层
> > 第一层嵌套
> > > 第二层嵌套

还有现在在用的是区块。算了不抄了,直接看菜鸟教程吧。

git分支操作

git分支操作 可以当速查表看看

新建一个空白分支

1
git checkout --orphan newBranchName

签名认证

待填坑

git commit message标准

基本格式

type + 描述

type类型

  • feat 新功能
  • fix/to
    • fix 产生diff并自动修复问题。适合一次提交直接修复格式
    • to 只产生diff不自动修复此问题。适合于多次提交。最终修复问题提交时使用fix
  • docs 提交文档
  • style 格式 不影响代码运行的变动
  • refactor 重构
  • perf 优化
  • test 测试 单元测试
  • chore 构建过程或辅助工具的变动
  • revert 回滚到上一个版本
  • merge 代码合并
  • sync 同步主线或分支的bug

更新pip

1
python.exe -m pip install --upgrade pip

镜像源

  • 经常install时卡顿,一般会用清华大学镜像源
  • 单个安装使用镜像源
    1
    pip install 【要安装的库名】 -i https://pypi.tuna.tsinghua.edu.cn/simple
  • 永久设置镜像源
    1
    pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

    requirement.txt

  • 有一堆要安装的,要一个一个pip Install?
  • 不,把requirement.txtre放到一个文件夹里,cd到这个文件夹,然后
    1
    pip install -r requirements.txt
    导出分享给别人:
    1
    pip list --format=freeze > requirements.txt

写在前面:
我的第一次游戏开发。这里是当年的记录,都是新手踩坑,确实没有什么技术含量。尽可能地保留原汁原味😂。

游戏名:九章绮梦
unity引擎版本:2022.3.23f1
注意:润色部分要在保证程序编完的情况下,再润色。
Unity调用命令行,另开新的一篇写。

程序开发备忘录

开始任务

  1. 导入图片(重要)
    像素素材,为了防止像素模糊,一定要勾选这两项:
    Filter Mode: Point(No filter)
    Compression: None
  2. 润色:第一章还没有做速度控制器。主速度变量在PlayerController。要更改MoveLandscape和SceneScroller已完成
  3. 重要:主摄像头,要固定下来,现在比例不对!据说要用GUI什么的。
  4. 场景滚动,是用我建立的滚动材料Scroller.mat,再加上滚动脚本即可。注意图片素材模式要是multiple,不能是simple。
  5. panel(暂停后的UI)还没做。先写逻辑,这里估计要放点儿知识文字。
  6. 美术缺很多素材!
  7. 音频准备了一点点,最后可能还要收集,去找免版权的,我买了一点。
  8. 润色:加一个跑步公里数。古代单位估计得。
  9. 重要:游戏玩法设计、存档系统。使用json还是……还有一个剧情系统。剧情估计要用json存,但是在脚本存也可以吧。
  10. 捡到金币的音频已经找到了,还没绑定。还有跳跃的音频还没找到合适的。
  11. 说明:tree绑定了随风摇动的shader。
  12. 说明:看剧本,想一下如何融入数学元素。
  13. 润色:live2D系统,chatGLM调用。
  14. 未修正:角色和场景速度不对2023/4/6已写速度

2024/4/6

  1. 学到了oop里的“单例”用法。虽然还没用。

2024/4/11

L要做的部分

  1. 看懂已经写的ButtonPause.cs,MoveLandscape.cs,PlayerController.cs,RandomObject.cs,SpeedController.cs,SceneScroller.cs,TextCoin.cs仿照FangTianScence.unity,做后面的场景:SuMi粟米,Junshu均输,GouGu勾股。其中勾股部分是用屋顶素材拼的,和其它关不一样,经常跳跃。在PlayerController.cs中仿照金币写一个被敌人碰扣血的函数。后面可能要做一个主人公发起攻击的。
  2. 看上面的记录内容。
  3. 收集物品的脚本。
  4. 做一个存档脚本,存下关卡通关情况,每一关的最高纪录、人物金币收集情况、人物“知识点”。
  5. 准备做UI。网上能找到代码怎么写。

    未来一周规划

程序
  1. 把剧情写进脚本,不用json了,直接用list数据结构包一个class。一个一个读取。然而为了美好的未来,我还是用了json()
美术
  1. 画几张聊天背景图,好,就拍照再加滤镜!
  2. 主页图,经费充足的话用live2d吗?
  3. 地图,这个重要捏
  4. 商店
  5. 像素风:第一关要收集的四个物品,第二关收集的米袋,第四关飞镖。一个暂停界面,只需要个框!最好和按钮一个色卡

    unity live2d使用笔记

    建模过程踩的坑不用提了,因为我忘记记录了
    目线跟踪、眨眼、动画播放要挂载的脚本
    图片
    其中脸部参数可以再调大一点
    图片
    图片
    图片
    这个系列:Unity制作Live2D(三)头部眼睛跟随鼠标
    总之要把动画全部k帧k出来再导入。

    2024/4/12

    关于JSON

    感觉难用,但是就这样吧。
    在老C那儿查到这些避雷的地方:
  6. 用于接收的JSON实体类需要声明[Serializable] 序列化。
  7. 使用Unity自带方法时,实体类如果是属性成员(public bool has_more{get;set;})的话,在序列化的时候会缺失这些成员,导致解析不出来。将属性改为字段即可。
  8. 如果解析的对象是数组,自带的不能成功解析,可以人为将其封装为JSON对象。 对!数组xx的不能不能解析!(愤怒)根本改不好!!!
    但是改着改着它就从这样图片变成了这样图片
    我不理解,完全忘记自己改了什么,并且大受震撼。。。unity我的祖宗,你编译快一点儿
    所以这个有什么用来着?
    1
    {get;set;}
    2024/4/15补充:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    某个人工智能“主动”跟我说:
    In C#, the { get; set; } syntax is used to define a property. Let’s break it down:
    get: This part defines the getter for the property. It allows you to retrieve the value of the property. When you access the property, the code inside the get block is executed, and the result is returned.
    set: This part defines the setter for the property. It allows you to assign a value to the property. When you assign a value to the property, the code inside the set block is executed, and the value is stored.
    Here’s an example of how you might use this syntax to create a simple property in C#:
    ;
    public class Person
    {
    // Private field to store the value
    private string _name;

    // Property with getter and setter
    public string Name
    {
    get { return _name; }
    set { _name = value; }
    }
    }
    In this example:

    The Name property has both a getter and a setter.
    When you read person.Name, it calls the get block and returns the stored _name value.
    When you assign a value to person.Name, it calls the set block and updates the _name field.
    Feel free to adapt this pattern to your specific use case! 😊
    于是莫名其妙的,我们就可以SetTrigger播放人物动画了,好耶!
    顺便一提,不知道哪儿冲突了,小九的微笑眯眯眼和什么冲突了,悲()
    假如此时,能来个很厉害的美工就太美妙了,再来亿点儿live2D模型吧!!

    2024/4/14

    做了剧本一个字一个字播放

    关于c#的oop

    我忘记我要记什么oop了。。。

    2024/4/15

  9. 摄像头比例是Camera.main.aspect。但我还是搞不好啊!
  10. 重新测试了python chatglm角色ai聊天,写了unity和python通信的脚本。
    为什么要用cmd.exe呢?因为太菜了,看到socket的代码望而生畏,觉得cmd比较好搞,遂学之。python那边就是用我们的老朋友sys了。
    这里是用Thread类,网上说不用的话会报错。查了一下概念:
    线程(Thread)是进程中一个单一的顺序控制流程。线程是进程中的实体。一个进程可以有多个线程,一个线程必须有一个父进程。

    2024/4/19

    这几天都在画画。但这是程序开发备忘录。

    2024/4/20

  11. video的导入遇到了困难。还好格式转换解决了。(后面补充一下,还是不行,有bug)
  12. 添加了游戏知识卡,虽然丑丑的。
  13. 比例还是有问题。
  14. 找了画手朋友画了些东西。

    2024/4/忘了

    记得时间戳暂停了恢复回来啊喂!还有ai板块编译出来就寄了,以后要想办法解决!

程序心得记录

累死了,最后一天疯狂修bug,不写啦

记录一次收拾“把超大库放进仓库”的烂摊子

起因

git是放代码的地方,但是我很蠢地把Unity的库给放进去,而且每次提交把全部有用没用的更改全交了。这就是不好好看新手教程的下场啊。
首先,要是直接在github里删除的话,历史记录还在那儿,.git文件依然会特别大。
删了一个library文件夹,.git文件大小: 1.71GB -> 633 MB 。而library自己有多大呢?2.04 GB。。。
于是搜查资料,查到了,按教程做,结果————

1
2
3
4
5
6
7
8
9
10
 git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch Library' --prune-empty --tag-name-filter cat -- --all
WARNING: git-filter-branch has a glut of gotchas generating mangled history
rewrites. Hit Ctrl-C before proceeding to abort, then use an
alternative filtering tool such as 'git filter-repo'
(https://github.com/newren/git-filter-repo/) instead. See the
filter-branch manual page for more details; to squelch this warning,
set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...

Cannot rewrite branches: You have unstaged changes.

嗯。。。但是我没保存的那些没用的东西,真是又多又没用。

解决方式

那就按这他给的链接搜索了。

在保证什么什么环境变量没问题的情况下。

1
pip install git-filter-repo

接下来,cd或者进入到文件夹,右键git bash。
这里教程也写了备份,但是我胆子大,没备份。不能这样。

1
cp -r your_repo your_repo_backup

然后就可以愉快地删除了。

1
git filter-repo --force --invert-paths --path 'Library'

清理并优化本地仓库

1
2
git reflog expire --all --expire-unreachable=now --update-reflogs
git gc --prune=now --aggressive

推送更改到远程仓库

1
2
3
4
5
6
git remote add origin <仓库URL>
或者
git remote set-url origin <仓库URL>

git push origin --force --all
git push origin --force --tags

重新clone,好好设置gitignore啊喂!终于理解去年为什么学长禁止我把美术资源的工程文件也push上了,唉。。。

于是去copy了学长的gitignore文件,以后就不会这么逆天了。仅针对unity引擎。

Unity gitignore模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# This .gitignore file should be placed at the root of your Unity project directory
#
# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore
#
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/[Ll]ogs/
/[Uu]ser[Ss]ettings/

# MemoryCaptures can get excessive in size.
# They also could contain extremely sensitive data
/[Mm]emoryCaptures/

# Recordings can get excessive in size
/[Rr]ecordings/

# Uncomment this line if you wish to ignore the asset store tools plugin
# /[Aa]ssets/AssetStoreTools*

# Autogenerated Jetbrains Rider plugin
/[Aa]ssets/Plugins/Editor/JetBrains*

# Visual Studio cache directory
.vs/

# Gradle cache directory
.gradle/

# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
*.mdb
*.opendb
*.VC.db

# Unity3D generated meta files
*.pidb.meta
*.pdb.meta
*.mdb.meta

# Unity3D generated file on crash reports
sysinfo.txt

# Builds
*.apk
*.aab
*.unitypackage
*.app
# Crashlytics generated file
crashlytics-build.properties

# Packed Addressables
/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin*

# Temporary auto-generated Android Assets
/[Aa]ssets/[Ss]treamingAssets/aa.meta
/[Aa]ssets/[Ss]treamingAssets/aa/*

小剧场

大佬:不过这 直接把library放进去了有点离谱
小w:草率了QAQ
大佬:其实有一个比较稳妥的办法,就是新创一个分支,没问题了再合并。这样做还可以减少多余的commit,就是比较麻烦。
小w:我多余的commit估计有几千个文件改动。。。毕竟那可是把library装进去了
大佬:哈哈

参考链接:

github上的安装说明
csdn的一篇博客
知乎教怎么用gitignore的

面向对象

1
super()._init__()

super()用来调用父类(基类)的方法,init()是类的构造方法, super().init() 就是调用父类的init方法, 同样可以使用super()去调用父类的其他方法。

pyqt6

命令行

  1. 编译
    1
    Pyinstaller -F -w -i cat.ico main.py
  2. ui文件转换成py文件
    先进入相应文件夹,然后打开cmd,输入
    1
    python -m PyQt6.uic.pyuic window.ui -o window.py

sqlite3

直接将字符串插入到SQL语句中,这可能会导致SQL注入的问题。SQL注入是一种攻击,攻击者可以通过在输入字段中插入恶意的SQL代码来操纵数据库。
在代码中,如果传入的数据包含SQL语句,那么这些语句将会被执行,这可能会导致数据库中的数据被修改或者删除。
为了防止这种情况,应该使用参数化查询。

错误代码:

1
self.cur.execute(f'INSERT INTO {self.name} (id, English, Chinese, Phonetic) VALUES(NULL, {English}, {Chinese}, {Phonetic})')

正确代码:

1
self.cur.execute(f'INSERT INTO {self.name} (id, English, Chinese, Phonetic) VALUES(NULL, ?, ?, ?)', (English, Chinese, Phonetic))

英语单词小程序——项目实现任务

做成能用的半成品也不背单词

一. 数据库

(1). 单词

1). 增

  • [x] 增加一个单词
  • [ ] 并且注意英语不重复
  • [ ] 根据excel表格,往单词本增加一堆单词
  • [ ] 根据相同或不同的数据库,别的单词本合并进选定的单词本

2). 删

  • [x] 在单词概览中删除一个单词,并更新
  • [ ] 在单词卡片中删除一个单词,并更新

3). 改

  • [ ] 在单词概览中点击,进入单词卡片模式,再点击可修改和保存
  • [ ] 在默写三模式中,根据对错情况修改权值

4). 查

  • [ ] 本地查询中英文
  • [ ] 联网查询中英文
  • [x] 查询全部单词

(2). 单词本

1). 增

  • [x] 新建一个单词本
  • [ ] 并且注意单词本名不重复
  • [ ] 根据excel表格,新建一个单词本
  • [ ] 根据相同或不同的数据库,新建一个单词本

2). 删

  • [x] 删除一个单词本

3). 改

  • [ ] 修改单词本名

4). 查

  • [x] 查询所有单词本名字
  • [ ] 查询单词本单词总数
  • [ ] 根据权重,随机选取单词本中单词
0%