20221126-随笔

工作日记

对问题,认真研究,开研思想,能发表意见,题出看法。解决问题有自己办法。发浑出创造性。

——一个人的才能

每个人活在这个世界上都非常不容易的
每个人用自己有限的资源条件和能力
去创造属于自己独一无二的生活

20221125-python给视频添加字幕

前言
最近尝试用python实现给一个视频添加字幕和语音,查了很多博客后有了一个拼拼凑凑的方法:

使用”pyttsx3”来实现“文字到音频”的转换;
使用”pydub”来对得到的音频进行加工和处理;
使用”moviepy”来合成字幕和语音文件。
(注:本篇仅作为个人小白的学习记录)

一、”pyttsx3”实现“文字到音频”的转换
pyttsx3是一个很方便的实现从“文字”到“语音”的库,安装和使用都很简单,windows下安装只需要:

1
pip install pyttsx3

参考链接

https://blog.csdn.net/qq_48047296/article/details/124221568

20221120-色域马蹄图是怎么来的-CIE1931XYZ色彩空间详解

前言

在这个一年就要发布十几台旗舰级手机的时代,手机屏幕的色彩的精准性与范围总是被强调,所以相信绝大多数关注数码领域的爱好者都见过与封面上的这张图类似的色域图。

起先虽然我也见过这张图,但真正想要去搞清楚这张图的含义倒并不是因为看到手机屏幕参数的时候产生疑惑,而是源于一个关于色彩的问题而产生的好奇驱使我去了解这张图背后的故事。

经过几天的文献查阅与思考,今天我就来详细解释一下这张色域图来源的细节。

开始之前你需要了解的几个概念

色刺激:进入人眼能引起有彩色或无彩色感觉的可见光辐射。

三刺激值:在三色系统中,与待测光达到色匹配所需的三种原刺激的量。

光谱三刺激值:在三色系统中,等能单色辐射的三刺激值。

色品坐标:三刺激之值与它们之和的比。

色彩匹配实验

我们知道不同的色光混合起来会产生其他的颜色,这其实是人眼的一种生理反应。为了了解到底人眼是怎么融合不同颜色色光的,有人设计了一个实验来定量测量到底参入多少红、绿、蓝三原色会让人眼觉得待测色光和三色光混合色感觉完全一样。实验原理如下图所示:

img

图1:色彩匹配实验

上图中红绿蓝三色光照在白屏的右侧,待测色照在白屏的左侧,左侧和右侧之间有一个黑色的档片。人眼透过一个小孔观察白屏,人眼的观测视角大概为 2∘ ,观测到的视场如上图右下角所示。当我们加载待测色之后,通过调节红绿蓝三色光的强度,最后使得人眼看到的颜色分界线感觉消失,待测色光即与三原色混合色光达到匹配,记录此时三色光的强度。

这样不同颜色的色光都能通过这个实验得到其对应的三原色光强度值,我们把其称之为三刺激值。

颜色匹配过程可以用公式表述如下:

C(C)≡R(R)+G(G)+B(B) (1)

式中“ ≡ ”号表示视觉上相等,即颜色匹配; 、、R、G、B 为代数量,可为负值。 C 为待测光的代数量。

颜色匹配也可以用几何表示:

img

图2: 颜色匹配矢量

上图表示待测色光C可以分解为三原色R,G,B三个矢量相加。

img

图3: 常用的颜色几何匹配方式:设置坐标的单位向量的模长为1

通常为了后续表示方便,R,G,B三色的单位矢量模长数量都取1。我们把单位矢量的顶端连接起来可以得到单位平面,每个颜色矢量 C 只会与单位平面有一个交点 Cp ,且此时交点的坐标满足单位平面的方程, |OCp¯|=1 。

光谱三刺激值

如果待测色光是辐射能量都相同的单色光(光谱色,光波段为380-760nm),我们把得到的三原色光强度值称之为:光谱三刺激值,用 r¯,g¯,b¯ 表示。颜色匹配过程可用公式表示如下:

Cλ=r¯(R)+g¯(G)+b¯(B) (2)

根据光谱色的颜色匹配实验结果可以得到以下曲线:

img

图4: 光谱三刺激值曲线图

从上图我们看到,光谱三刺激值红光刺激值很大一部分出现负值。这是因为当投射到半视场的某些光谱色用无论怎么也无法用另一半视场的三原色来匹配时,只有在光谱色半视场加入红色原色才能达到匹配。也就是此时的红色光是加载到图1中 左侧白板上的,如果规定往右侧加入的色光量为正值,那么左侧就相应的为负值。

色品坐标

当 C=1 时,也就是该待测色光在单位平面上,方程(1)可以写为:

C=RR+G+B(R)+GR+G+B(G)+BR+G+B(B) (3)

上式说明对于一个单位颜色 C 的色品只决定于三原色的刺激值在R+G+B总量中的相对比例,我们把这个比例叫做色品坐标,用 ,,r,g,b 表示:

,,r=RR+G+B,g=GR+G+B,b=BR+G+B (4)

色品坐标因为在单位平面上所以满足: r+g+b=1 ,根据这个关系,我们其实只要知道两个量就能得到第三个量的值。

公式(3)也可以写成以下形式:

C=r(R)+g(G)+b(B) (5)

CIE 1931-RGB 色度系统

CIE使用的三原色波长分别为700nm (R)、546.1nm (G)、435.8nm (B)(因为这三种波长能够精确的产生),对应的光谱三刺激值如图4所示。

img

图4: 光谱三刺激值曲线图

光谱三刺激值与色品坐标的对应关系如下:

r=r¯r¯+g¯+b¯,g=g¯r¯+g¯+b¯,b=b¯r¯+g¯+b¯ (6)

通过上式我们可以从光谱三刺激值计算出其对应的色品坐标,最后连接各个波长的色品坐标形成光谱轨迹得到下图:

img

图5: CIE 1931 r,g色品图的光谱轨迹

上图中380nm的波长那里其实包括了380-435.8nm的波长,700nm的波长其实包括了700-780nm的波长。

上图中的 E 代表了等能白点,该点的r,g,b坐标为(0.33,0.33,0.33),它的意思是等量R,G,B原色混合可以产生等能的白光。

从上图我们看到,由于光谱的三刺激值中有负值,所以造成上图中光迹曲线坐标有很大一部分在 r 的负轴上,不便于我们对色彩进行理解。因此我们需要对其进行坐标变换以使得色品图内的曲线上的所有点在新坐标下对应的值为正值。

CIE 1931 XYX 色度系统

那么问题来了,我们怎样选取新的三原色才能使得图5中的光谱轨迹曲线全部都落在新坐标系内的第一象限?(也就是光谱轨迹曲线色品坐标全部为正)

CIE主要考虑了以下三点内容:

  1. 规定(X),(Z)两原色只代表色度(颜色中红色和蓝色的比例),没有亮度。光度量只与三刺激值(Y) 成比例。XZ 线称为无亮度线。
  2. 新的三原色(X),(Y),(Z)连成的三角形要包含光迹曲线。
  3. 光谱轨迹从 540nm 附近至 700nm,在 RGB 色品图上基本是一段直线,用这段线上的两个颜色相混合可以得到两色之间的各种光谱色,新的 XYZ 三角形的 XY 边应与这段直线重合,因为在这段线上光谱轨迹只涉及(X)原色和(Y)原色的变化,不涉及(Z)原色。

这三点为什么这样设置,我自己的理解如下:

对于第一点:为什么亮度只设置与(Y)原色有关?而(X),(Z)原色不包含亮度?

因为人眼对绿光波长最敏感,RGB系统三原色的相对亮度(不是数量的多少)比为: lR:lG:lB=1.000:4.5907:0.0601 (这意味着蓝色给人的感觉非常暗,绿色给人感觉最亮)。因此这里设置原色Y代表亮度和绿色的在颜色中的比例也比较也合理(颜色Y值越大,人眼感觉越亮,也代表颜色中绿色的比例越高)。

另外由于颜色匹配也包括其亮度的匹配,因此方程(1)可以将三原色相对亮度代入可得到亮度方程 l(C)=r+4.5907g+0.0601b ,如果颜色在无亮度l(C)=0 线上,则r+4.5907g+0.0601b=0 , 代入b=1−r−g ,整理后可得 XZ 线的方程。

对于第二点:应该大家都能理解,新的坐标系肯定是在得让光谱轨迹曲线只落在第一象限,保证其值为正。

对于第三点:这意味着这条直线的两端对应的肯定对应着两种原色,也就是(Y)和(X)。XY线的方程很容易获得。

现在,我们已经确定了两条由新三原色组成的线即,无亮度线XZ,与XY线。而第三条线YZ,CIE选取的为波长503nm出的点相切的直线。其方程也很容易得到。

所以根据上面,我们可以在图5中看到由这三条新由三原色连成的三角形。虽然这里看到的是二维的三角形,其实它是在三维空间中:

img

图6: CIE 1931 RGB向XYZ系统转换

此外,通过这三条直线,我们可以确定新三原色在R,G,B色品系统下的坐标,进而得到新色品坐标系与旧色品坐标系之间的转换矩阵。然后根据转换矩阵我们就能将图5的光谱轨迹曲线转换到由新三原色构成的新坐标的第一象限中。转换后的光谱轨迹曲线,新三原色三刺激值曲线结果如下图所示:

img

图7: CIE 1931 x,y色品图上的光谱轨迹

img

图8: CIE 1931 XYZ系统光谱三刺激值曲线图

图7中的x,y为新系统的色品坐标,其值为:

x=XX+Y+Z,y=YX+Y+Z (7)

等能白点的坐标 E 为(0.33,0.33)。

现在我们将RGB系统转换到了XYZ系统,且光谱轨迹全部落在了第一象限。

待测色的CIE 1931 XYZ色品坐标计算

虽然我们得到了光谱在CIE x,y色品坐标系下的值,但是光谱轨迹曲线所包含的面积内的颜色的色品坐标值如何计算呢?

我们可以使用以下积分公式计算待测色的CIE三刺激值:

X=k∫380780φ(λ)x¯dλ,Y=k∫380780φ(λ)y¯dλ,Z=k∫380780φ(λ)z¯dλ (8)

上式 k 为常数(归化系数), φ(λ)为光源发出或经过物体反射投射进入人眼产生颜色感觉的光谱能量。当待测色是光源直接发出的, φ(λ) 则为光源的辐射的相对光谱功率分布 P(λ) (可以通过测量获得,得到的图类似于下图)。由图8我们可以获得 x¯,y¯,z¯ 。

img

图9: 光源的辐射的相对光谱功率分布示意图

因此待测色的三刺激值可以通过公式8来进行计算,然后通过三刺激值到色品坐标的转换,这样光谱曲线所包围的曲面内的所有颜色的色品坐标我们都可以通过公式(8)计算得到。

而对于混合色我们还可以根据格拉斯曼颜色混合定律得到(具体的方法主要有直接计算法,画图法)。

最终得到的色度图如下:

img

图10: CIE 1931 xy色度图

图10,CIE x,y色品图上的光谱轨迹具有以下颜色视觉特点:

  1. 靠近波长末端 700-770nm 光谱波段具有一个恒定的色品值,故在色品图上只由一个点来代表。
  2. 光谱轨迹 540-700nm 段是一条与 XY 边基本重合的直线。在这段光谱范围内的任何光谱色都可通过 540nm 和 700nm 两种波长的光以一定比例相加混合产生。
  3. 光谱轨迹 380-540nm 段是曲线,在此范围内的一对光谱色的混合不能产生二者之间位于光谱轨迹上的颜色,而只能产生光谱轨迹所包围面积内的混合色。光谱轨迹上的颜色饱和度最高。图上越靠近等能白点E 点的颜色饱和度越低。
  4. 连接色度点 400nm 和 700nm 的直线称为紫红轨迹,也称紫线。因为将 400nm 的蓝色刺激与 700nm 的红色刺激混合后会产生紫色(也就是说紫色和红色之间还有已知的颜色,而不是断裂的)。
  5. Y=0 的直线(XZ)与亮度没有关系,即无亮度线。光谱轨迹的短波段紧靠这条线,意味着短波端的光虽然能够引起标准观察者的反应,但 380-420nm 波长在视觉上引起的亮度感觉很低。
  6. 曲线内包含的色彩是我们人眼全部能感知的色彩。
  7. 曲线是三维曲线,我们看到的是其在x,y平面上的投影。

最后

本文只是对CIE 1931 XYZ色彩空间的来源进行了介绍,限于篇幅与我所了解的内容的多少,这里就没有对其应用与扩展内容进行介绍。

但是如果你感兴趣深入可以去看参考中所列出的书籍,可以找到关于混合色的色品坐标如何计算,颜色空间均匀性的讨论,颜色主波长如何确定,色差的评价,同色异谱现象等等关于色彩的详尽内容。

你还可以以看看参考中其他人对于色彩的回答,相信对你了解色域图的应用会有帮助。

此外本文图片均来源与wiki百科和参考中所列出的两本书籍,本人只对其进行了标注。

20221119-debug命令详细说明

启动DEBUG

1.打开Windows命令窗口

在Windows 95/98的环境中,打开命令窗口的步骤为:点击“开始”→“运行”,输入“command”命令; 在WindowsXP及WIN7的环境中,打开命令窗口的步骤为:点击“开始”→“运行”,输入“cmd”命令;

2.启动DEBUG

在命令窗口中启动DEBUG,启动命令一般为:DEBUG [文件名] [参数表]。其中:文件名指定被调试的文件,其包括名和后缀,参数表是被调试文件运行时所需要的参数。被调试的文件可以是系统中的任何文件,但通常它们的后缀为.EXE或.COM。 当DEBUG启动成功后,将显示连接符“-”,这时,可输入各种DEBUG命令。DEBUG中所有命令及其含义如DEBUG各命令功能说明表所示。 关于使用命令的几点说明:在提示符“-”下才能输入命令,在按“回车”键后,该命令才开始执行命令是单个字母,命令和参数的大小写可混合输入可用F1、F2、F3、Ins、Del、左移键、右移键等编辑键来编辑本行命令当命令出现语法错误时,将在出错位置显示“^ Error”可用Ctrl+C或Ctrl+Break来终止当前命令的执行,还可用Ctrl+S或Ctrl+Num Lock来暂停屏幕显示(当连续不断地显示信息时)
以下通过实现十九个示例来熟悉DEBUG的命令集和基本的汇编指令。

R命令的使用

R命令作用:观看和修改寄存器的值。

在提示符“-”下输入以下命令:R。DEBUG将会显示出当前所有寄存器和标志位的状态。

接下来再输入命令RCX。在提示符“:”后输入100。该命令的作用是将寄存器CX的值设置为100(注意:DEBUG使用的是十六进制,这里的100相当于十进制的256。)

最后再执行R命令,观看修改后的寄存器值。

H命令的使用

H命令作用:计算两个十六进制数的和与差。

在提示符“–”下输入以下命令:H 10 1。观看命令执行结果。

debug-h

运行结果的前一个数是计算出来的和,后一个数是计算出来的差。计算结果均用十六进制形式表示。

D命令的使用

D命令作用:显示内存区域的内容。

在提示符“–”下连续执行命令R、D、D。观看命令执行结果。

前面已经介绍过了,命令R的作用是显示当前寄存器的值。而命令D的作用是显示内存区域的内容,最左边是内存的起始地址,中间以十六进制的形式显示内存值,最右边是以ASCII码的形式显示内存值。每行最多显示16个字节的内容。

命令D可以带参数也可省略参数。设DEBUG启动时DS的值为X,当省略参数时,命令D显示内容以X:100为起始,每次显示128个字节的内容。以后再执行不带参数的命令D时,DEBUG将按上次的位置接着显示下去。

带参数时DEBUG能够显示指定地址范围的内容。带参数的方式有三种:

方式一:d [起始位置]。DEBUG从起始位置开始显示128个字节的内容。在提示符“-”下执行命令D 1AF5:100。观看命令执行结果。

方式二:d [起始位置] [结束位置]。DEBUG从起始位置开始一直显示到结束位置。在提示符“-”下执行命令D DS:100 1FF。观看命令执行结果。

方式三:d [起始位置] [L长度],长度以L参数为标识。DEBUG从起始位置开始显示指定长度的内容。在提示符“-”下执行命令D DS:100 L10。观看命令执行结果。

E命令的使用

E命令作用:改变内存单位的内容。

E命令的使用方式为:E [起始位置]。

在提示符“-”下输入以下命令:E 1AF5:100。

debug-e

DEBUG首先显示[1AF5:0000]的内容00.,这时可以修改该字节的值。如果还要修改后续的内容,可以按空格键继续。当要跳过某个字节时,可以按连续的两个空格跳到后一个字节去。

F命令的使用

F命令作用:使用指定的值填充指定内存区域中的地址。

F命令的使用方式为:F [范围] [填充列表]。

在提示符“-”下输入以下命令:F 1AF5:100 L20 1 2 3 4 5。执行命令D 1AF5:100观看命令执行结果。

说明:该命令是用字节序列01、02、03、04、05轮流填充从1AF5:100开始长度为20H的内存区域。

在提示符“-”下输入以下命令:F 1AF5:100 13F 41 42 43 44。

说明:该命令是用字节序列41、42、43、44轮流填充从1AF5:100开始一直到1AF5:13F的内存区域。

M命令的使用

M命令作用:将指定内存区域的数据复制到指定的地址去。

M命令的使用方式为:M [范围] [指定地址]。

在提示符“-”下输入以下命令:M 1AF5:100 13F 1AF5:140。执行命令D 1AF5:100观看命令执行结果。

C命令的使用

C命令作用:将两块内存的内容进行比较。

C命令的使用方式为:C [范围] [指定地址],意思就是将指定范围的内存区域与从指定地址开始的相同长度的内存区域逐个字节进行比较,列出不同的内容。

在提示符“-”下输入以下命令:C 1AF5:100 13F 1AF5:140。由于两块内容完全相同,所以命令执行后没有任何显示。

在提示符“-”下输入以下命令:C 1AF5:100 107 1AF5:180,比较的区域长度为8个字节。命令执行后列出比较结果不同的各个字节。

S命令的使用

S命令作用:在指定的内存区域中搜索指定的串。

S命令的使用方式为:S [范围] [指定串]。

在提示符“-”下输入以下命令:D 1AF5:100 11F。显示该区域的内存值。

在提示符“-”下输入以下命令:S 1AF5:100 11F 41 42 43 44。搜索该区域是否存在字节串41 42 43 44,并将搜索结果一一列出。

从执行结果可以看出,总共搜索到八处。

A命令的使用

A命令作用:输入汇编指令。

以下的程序要在屏幕上显示“ABCD”四个字符。

首先用E命令将“ABCD$”四个字符预先放在内存CS:200处,然后执行A100命令输入汇编程序代码:

MOV AX,CS

MOV DS,AX

MOV DX,200

MOV AH,9

INT 21

INT 20

(说明:前两行汇编指令用于将段寄存器CS的值赋给段寄存器DS。第三到第五行汇编代码的作用是显示以“$”为结尾的字符串。最后一行用于结束程序。

G命令的使用

G命令作用:执行汇编指令。

G命令的使用方法是:G [=起始地址] [断点地址],意思是从起始地址开始执行到断点地址。如果不设置断点,则程序一直运行到中止指令才停止。

在设置完示例九的的内存数据并且输入完示例九的程序后运行这些汇编代码。在DEBUG中执行命令G=100,观看运行结果。

汇编程序运行后在屏幕上显示出“ABCD”四个字符。

接下来在DEBUG中执行G=100 10B,意思是从地址CS:100开始,一直运行到CS:10B停止。观看运行结果。

命令执行后,不但显示出字符串“ABCD”,而且列出当前寄存器和标志位的值。

U命令的使用

U命令作用:对机器代码反汇编显示。

U命令的使用方法是:U [范围]。如果范围参数只输入了起始地址,则只对20H个字节的机器代码反汇编。执行命令U100,观看反汇编结果。

执行命令U100 10B,观看反汇编结果。该命令的作用是对从100到10B的机器代码进行反汇编。

N命令的使用

N命令作用:设置文件名,为将刚才编写的汇编程序存盘做准备。

以下的DEBUG命令序列作用将刚才的汇编程序存为磁盘的COM可执行程序。

D200 20F

U100 10C

N E:\FIRST.COM

RCX

:110

W

第一和第二条命令的作用是检查一下刚才编写的汇编指令。第三条命令的作用是设置存盘文件名为E:\FIRST.COM,第四条命令的作用是设置存盘文件大小为110H个字节。最后一条命令是将文件存盘。

文件存盘后执行E:\FIRST.COM,观看存盘的可执行文件的运行效果。

W命令的使用

W命令作用:将文件或者特定扇区写入磁盘。

在示例“N命令的使用”中已经实验了如何使用W命令将文件存盘。

在没有很好地掌握汇编语言和磁盘文件系统前,暂时不要使用W命令写磁盘扇区,否则很容易损坏磁盘文件,甚至破坏整个磁盘的文件系统。

L命令的使用

L命令作用:从磁盘中将文件或扇区内容读入内存。

将文件调入内存必须先用DEBUG的N命令设定文件名。以下例子是将E:\FIRST.COM读入内容。

N FIRST.COM

L

观看调入程序的汇编代码可以使用DEBUG的U命令,用U100观看调入的COM文件。

读取磁盘扇区的方式是:L [内存地址] [磁盘驱动器号] [起始扇区] [扇区数]。“内存地址”指定要在其中加载文件或扇区内容的内存位置,如果不指定“内存地址”的话,DEBUG将使用CS寄存器中的当前地址。“磁盘驱动器号”指定包含读取指定扇区的磁盘的驱动器,该值是数值型:0=A,1=B,2=C等。“起始扇区”指定要加载其内容的第一个扇区的十六进制数。“扇区数”指定要加载其内容的连续扇区的十六进制数。

只有要加载特定扇区的内容而不是加载文件时,才能使用[磁盘驱动器号] [起始扇区] [扇区数]参数。

例如:要将C盘第一扇区读取到内存DS:300的位置,相应的DEBUG命令为L DS:300 2 1 1。但是由于Windows操作系统对文件系统的保护,这条命令可能会被操作系统禁止运行。

T命令的使用

T命令作用:执行汇编程序,单步跟踪。

T命令的使用方式是T [=地址] [指令数]。如果忽略“地址”的话,T命令从CS:IP处开始运行。“指令数”是要单步执行的指令的数量。

以下示例对E:\FIRST.COM进行单步跟踪。

N E:\FIRST.COM

L

U100 10B

R

T=100

T

第一、二条命令是装入文件,第三条命令是列出程序反汇编代码,第四条命令是显示当前寄存器值,第五条命令是从CS:100处开始单步跟踪,第六条命令是继续跟踪后续的指令。

P命令的使用

P命令作用:执行汇编程序,单步跟踪。与T命令不同的是:P命令不会跟踪进入子程序或软中断。

P命令的使用方式与T命令的使用方式完全相同。

I命令的使用

I命令作用:从计算机输入端口读取数据并显示。

I命令的用法是I [端口地址]。例如从3F8号端口读取数据并显示的命令为:I 3F8。这里不对该命令做解释。

O命令的使用

O命令作用:向计算机输出端口送出数据。

O命令的用法是O [端口地址] [字节值]。例如向278号端口发出数据20H的命令为:I 278 20。这里不对该命令做解释。

Q命令的使用

Q命令的作用是退出DEBUG,回到DOS状态。

20221119-即时通讯IM记录的存储

原文链接 https://juejin.cn/post/6983824498201985037

背景

即时通讯(Instant Messaging),也就是我们常说的 IM,其实在很多业务场景上都会有或多或少的应用,有的会是核心,有的会是辅助。

既然是聊天,那么必然就会产生聊天记录,而且聊天记录随着人数的增加和时间的推移,很容易出现爆炸式的增长,这个对存储其实压力是很大的。

举个大家都很熟悉的例子,一个群聊,几分钟不看,再打开就是 99+ 的未读消息。

把即时通讯这个技术,放到医疗环境下,也是同样适用的。

患者去线下医院看病,肯定离不开和医生的问答,这些问答,对系统来说,其实都是聊天记录。

如果把这个场景放到线上进行,也就是正常的和我们在微信聊天那样了。

说了那么多有的没的,也算是把大背景交代了一下,那么接下来就看看这个聊天记录存储的选型吧。

技术选型

既然要存储,那么肯定就会有很多选择,关系型数据库,非关系型数据库等。

当然这个很大程度上是和具体业务场景挂钩的,离开了业务场景,基本就是在空谈。

在医患关系里面的聊天记录,是一个十分十分核心的内容,并且必需要长期保存,不能丢,可查询。

并且这些聊天记录是和某次问诊强关联的,所以单独拿出几条聊天记录出来,是没有意义的,因为他们没有关联,串联不起来。

按照以往的经验看,一次问诊,医生和患者之间的聊天记录在 50 条之内的占据了大部分,超过50条的,占少数。

这个和其他的 IM 情景可能不太一样。

MySQL

业务开始,大概率就是选用 MySQL 去存了这些数据了,单库单表,但是这种情况下很容易达到单表千万和上亿的级别。

后面面临的基本就是分库分表的操作了。

分库分表,基本就是根据问诊号去进行哈希,然后放到不同的库不同的表。

这里会有一个不确定因素,分多少个库和分多少张表?

分的多,囊中羞涩;分的少,再一次达到量级的时候又要重新分,大动干戈,这个时候最怕的就是动到了哈希的规则。

所以选 MySQL 的话,到了中后期确实还是会有一点力不从心。线性扩展对这一块还是非常重要的。

如果想改动小,避免分库分表,或许可以试试 TiDB,但是它要的配置,也不是中小企业所能接受的,80% 以上的概率会被 Pass 掉。

docs.pingcap.com/zh/tidb/sta…

Cassandra

Cassandra 是一个分布式、无中心、弹性可扩展的 NoSQL 数据库,基于 Amazon Dynamo 的分布式设计和 Google Bigtable 的数据模型。

cassandra.apache.org/doc/latest/…

img

为什么考虑选型 Cassandra 呢? 对上面说的医患场景,严格上是属于写多读少的,查询基本只会基于问诊号去查询,这个是相对比较明确的。

Discord 在 2017 年的时候有一篇博客讲述了他们是怎么存储数十亿消息记录的 ,说的比较详细了。

blog.discord.com/how-discord…

其实他们选数据库的诉求,也是符合大部分涉及 IM 这一块的。

老黄也是从这里受到了启发,认识了这个数据库。

经常拿来比较的话,应该是 HBase,一个在国内火,一个在国外受欢迎。

可以看看这个对比,了解一下两者的异同: www.scnsoft.com/blog/cassan…

如果选择要用 Cassandra, 那么数据模型的设计,一定是所有环节中最为重要的一步,如果这一步没有做好的话,那后面基本上会是灾难级别,基本不能愉快的玩耍。

那么对医患关系里面的这个聊天模型其实比较简单。

1
2
3
4
5
6
7
8
9
10
CREATE TABLE IF NOT EXISTS messages(
inq_id text,
send_time bigint,
sender_id text,
sender_role tinyint,
msg_type tinyint,
msg_body text,
PRIMARY KEY (inq_id, send_time)
) WITH CLUSTERING ORDER BY (send_time ASC)
复制代码

对照正常的 IM 群聊, 这个问诊号 (inq_id) 就可以认为是一个群聊,一个频道。

为什么没有消息Id这样的字段呢?多来源,非自研,无实际意义。

下面再来看看如何在 C# 里面进行操作, 这里用的是 DataStax 提供的 CassandraCSharpDriver 客户端。

写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var cluster = Cassandra.Cluster.Builder()
.AddContactPoints("127.0.0.1")
.WithDefaultKeyspace("messaging")
.Build();

var inqId = "xxxxxx";
var sendTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var senderId = "xxxx";
var senderRole = 1;
var msgType = 1;
var msgBody = "zzzz";

string INSERT_SQL = @" INSERT INTO messages(inq_id, send_time, sender_id, sender_role, msg_type, msg_body)
VALUES (?, ?, ?, ?, ?, ?) ";

var session = cluster.Connect();

var stmt = session.Prepare(INSERT_SQL).Bind(inqId, sendTime, senderId, senderRole, msgType, msgBody));

session.Execute(stmt);
复制代码

读取:

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
var cluster = Cassandra.Cluster.Builder()
.AddContactPoints("127.0.0.1")
.WithDefaultKeyspace("messaging")
.Build();

var inqId = "xxxxxx";

string GET_MSG_SQL = @" SELECT * FROM messages WHERE inq_id = ? ";

var session = cluster.Connect();

var stmt = session.Prepare(GET_MSG_SQL).Bind(inqId);

var rowset = session.Execute(stmt);

Console.WriteLine("患者\t\t\t\t\t医生");

foreach (var row in rowset)
{
// 解析从 cassandra 中返回的行
var msg_body = row.GetValue<string>("msg_body");
var sender_role = row.GetValue<sbyte>("sender_role");

if (sender_role == 0)
{
Console.WriteLine($"{msg_body}\t\t\t\t\t");
}
else
{
Console.WriteLine($"\t\t\t\t\t{msg_body}");
}
}
复制代码

写在最后

存储的选择其实还是有点门道的,根据不同的应用场景,找出比较适合当前场景的几个方案,再选择一个成本没这么高的。

Cassandra 对聊天记录这个场景的存储还是有一定优势的,可以应对高速的数据增长,而不用在业务代码层做过多的适配;部署相对简单,无特殊依赖,运维成本相对较低。

20221119-海康威视网络摄像头购买指南(焦距-像素等参数)

原文链接 https://blog.csdn.net/qq_15029743/article/details/88995210

如果您需要使用网络摄像头进行二次开发或者监控使用,但是不知道如何选取,请您注意网络摄像头的几个参数(主要):

1、分辨率(像素)
从图像质量上来说,网络摄像头的分辨率越高,成像越清晰,具体我们可以看下面这张图片,但是也并不是最大的就是最好的,还有考虑价格(像素越高越贵),存储空间(像素越高对应占用存储空间也就越大),所以需要根据实际使用环境来进行选购,目前市面上100w像素(即720p)和200w像素(即1080p)价格差距不大,建议一次性选购200w像素的摄像头(做人脸识别的童鞋们,近距离100w像素的就够用了,远距离需要选择1080清晰度会好一些):

2、摄像头焦距
买摄像头时会让我们选择镜头焦距,这个值并不是越大越好,也是根据实际需要来选择,这里直接贴一张海康威视的镜头焦距选购指导,图示的已经非常清楚了:

3、 是否支持POE供电
POE供电功能一般是高端一些的摄像头有提供,即网线直接供电。支持POE供电的网络摄像头使用时不需要额外的电源线了,直接插上网线就可以使用

注:POE供电需要配合支持POE供电的交换机来使用

4、视频编码格式:
这里海康威视家的摄像头一般提供两种编码格式即H264和H265(还有最新的smart265),同样H265只有较为高端的摄像头才具有, H265的好处就是更节省存储空间,这里也贴上一张官方图片来说明:

5、安装方式
一般网络摄像头分为球形机(高端球形机可以选配云台功能,即摄像头可以旋转)和枪型机,这个需要根据您自身的情况来选择,价格也各有不同,枪型机也要注意安装方式,有底部螺丝开孔和后端螺丝开孔,这里放几张图片来说明:

6、是否支持红外、防水、音频收录、WIFI等功能
这部分基本就根据各位的实际需要来进行选购了:

7、帧率(做高速图像处理的童鞋们请注意!)
海康威视的低端网络摄像头通常来说能达到的帧率为,日常监控或者低速图像处理是绝对够用的,一般来说20以上的FPS我们就可以理解为实时了,但是需要更高速度的童鞋们需要谨慎购买网络摄像机(你可能需要购买工业级的摄像机或单反):

20221118-python读取ini配置文件

在详解python读取ini文件之前,我们先说明一个ini文件的组成;

一个ini文件是由多个section组成,每个section中以key=vlaue形式存储数据;

img

然后我们来使用python读取ini文件中的数据;

1导包

复制代码

1
2
3
4
5
6
# 导包
import configparser
config = configparser.ConfigParser() # 类实例化

# 定义文件路径
path = r'D:\Python_Script\new_framework\source_file\broswer_config.ini'

复制代码

2.读取文件内容

复制代码

1
2
3
4
5
6
7
8
9
10
11
# 第一种读取ini文件方式,通过read方法
config.read(path)
value = config['select']['url']
print('第一种方法读取到的值:',value)

# 第二种读取ini文件方式,通过get方法
value = config.get('select','url')
print('第二种方法读取到的值:',value)
# 第三种读取ini文件方式,读取到一个section中的所有数据,返回一个列表
value = config.items('select')
print('第三种方法读取到的值:',value)

复制代码

img

img

3.通过上面两种方式,我们发现读取到的数据全部是str类型。如果我们想要得到指定数据类型的数据可以使用以下方法:

getint() getfloat() getboolean()

1
2
3
value = config.getint('connect_mysql','port')
print('这是第三种方法读取到的值:',value)
print('这是第三种方法读取到的值的数据类型:',type(value))

img

4.写入数据到ini文件中

1
2
3
4
5
# 将数据写入到ini文件中
config.add_section('login') # 首先添加一个新的section
config.set('login','username','admin') # 写入数据
config.set('login','password','123456') # 写入数据
config.write(open(path,'a')) #保存数据

img

5.获取到ini文件所有的section

1
2
3
# 读取ini文件中所有的section
section = config.sections()
print(section)

img

20221117-汇编学习笔记

CPU 对存储器的读写

CPU想要进行数据的读写,必须和外部器件进行三类信息的交互

存储单元的地址
器件的选择 读或写命令

读或写的数据

地址信息
控制信息
数据信息

CPU 对存储器的读写

总线在逻辑上划分的图示

地址总线
数据总线
控制总线

寻址
传输数据
控制 能力

20221117-x86指令集-带注释

X86和X87汇编指令大全(有注释)

汇编指令集太多,如果不用就会忘记,所以将intel处理器官方的指令集整理一下,有需要的人可以参考一下!

—————————————— 一、数据传输指令 ———————————————

它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据。

1. 通用数据传送指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MOV     传送字或字节.  
MOVSX 先符号扩展,再传送.
MOVZX 先零扩展,再传送.
PUSH 把字压入堆栈.
POP 把字弹出堆栈.
PUSHA 把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.
POPA 把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.
PUSHAD 把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.
POPAD 把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.
BSWAP 交换32位寄存器里字节的顺序
XCHG 交换字或字节.(至少有一个操作数为寄存器,段寄存器不可作为操作数)
CMPXCHG 比较并交换操作数.(第二个操作数必须为累加器AL/AX/EAX)
XADD 先交换再累加.(结果在第一个操作数里)
XLAT 字节查表转换.----BX指向一张256字节的表的起点,
AL为表的索引值(0-255,即0-FFH);
返回AL为查表结果.([BX+AL]->AL)

2. 输入输出端口传送指令:

1
2
3
4
IN      I/O端口输入. ( 语法: IN   累加器,    {端口号│DX} )  
OUT I/O端口输出. ( 语法: OUT {端口号│DX},累加器 )
输入输出端口由立即方式指定时,其范围是 0-255;
由寄存器 DX 指定时,其范围是 0-65535.

3. 目的地址传送指令:

1
2
3
4
5
6
LEA     装入有效地址.例: LEA DX,string ;把偏移地址存到DX.  
LDS 传送目标指针,把指针内容装入DS.例: LDS SI,string ;把段地址:偏移地址存到DS:SI.
LES 传送目标指针,把指针内容装入ES.例: LES DI,string ;把段地址:偏移地址存到ES:DI.
LFS 传送目标指针,把指针内容装入FS.例: LFS DI,string ;把段地址:偏移地址存到FS:DI.
LGS 传送目标指针,把指针内容装入GS.例: LGS DI,string ;把段地址:偏移地址存到GS:DI.
LSS 传送目标指针,把指针内容装入SS.例: LSS DI,string ;把段地址:偏移地址存到SS:DI.

4. 标志传送指令:

1
2
3
4
5
6
LAHF    标志寄存器传送,把标志装入AH.  
SAHF 标志寄存器传送,把AH内容装入标志寄存器.
PUSHF 标志入栈.
POPF 标志出栈.
PUSHD 32位标志入栈.
POPD 32位标志出栈.

——————————————- 二、算术运算指令 ——————————————–

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
ADD     加法.  
ADC 带进位加法.
INC 加 1.
AAA 加法的ASCII码调整.
DAA 加法的十进制调整.
SUB 减法.
SBB 带借位减法.
DEC 减 1.
NEG 求反(以 0 减之).
CMP 比较.(两操作数作减法,仅修改标志位,不回送结果).
AAS 减法的ASCII码调整.
DAS 减法的十进制调整.
MUL 无符号乘法.结果回送AH和AL(字节运算),或DX和AX(字运算),
IMUL 整数乘法.结果回送AH和AL(字节运算),或DX和AX(字运算),
AAM 乘法的ASCII码调整.
DIV 无符号除法.结果回送:商回送AL,余数回送AH, (字节运算);或 商回送AX,余数回送DX, (字运算).
IDIV 整数除法.结果回送:商回送AL,余数回送AH, (字节运算);或 商回送AX,余数回送DX, (字运算).
AAD 除法的ASCII码调整.
CBW 字节转换为字. (把AL中字节的符号扩展到AH中去)
CWD 字转换为双字. (把AX中的字的符号扩展到DX中去)
CWDE 字转换为双字. (把AX中的字符号扩展到EAX中去)
CDQ 双字扩展. (把EAX中的字的符号扩展到EDX中去)

DS:SI 源串段寄存器 :源串变址.
ES:DI 目标串段寄存器:目标串变址.
CX 重复次数计数器.
AL/AX 扫描值.
D标志 0表示重复操作中SI和DI应自动增量; 1表示应自动减量.
Z标志 用来控制扫描或比较操作的结束.

—————————————— 三、逻辑运算指令 ———————————————

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
AND     与运算.  
OR 或运算.
XOR 异或运算.
NOT 取反.
TEST 测试.(两操作数作与运算,仅修改标志位,不回送结果).
SHL 逻辑左移.
SAL 算术左移.(=SHL)
SHR 逻辑右移.
SAR 算术右移.(=SHR)
ROL 循环左移.
ROR 循环右移.
RCL 通过进位的循环左移.
RCR 通过进位的循环右移.
以上八种移位指令,其移位次数可达255次.
移位一次时, 可直接用操作码. 如 SHL AX,1.
移位>1次时, 则由寄存器CL给出移位次数.
如 MOV CL,04 SHL AX,CL

———————————————— 四、串指令 ———————————————–

1
2
3
4
5
6
7
8
9
10
11
MOVS    串传送.( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )  
CMPS 串比较.( CMPSB 比较字符. CMPSW 比较字. )
SCAS 串扫描.把AL或AX的内容与目标串作比较,比较结果反映在标志位.
LODS 装入串.把源串中的元素(字或字节)逐一装入AL或AX中.
( LODSB 传送字符. LODSW 传送字. LODSD 传送双字. )
STOS 保存串.是LODS的逆过程.
REP 当CX/ECX<>0时重复.
REPE/REPZ 当ZF=1或比较结果相等,且CX/ECX<>0时重复.
REPNE/REPNZ 当ZF=0或比较结果不相等,且CX/ECX<>0时重复.
REPC 当CF=1且CX/ECX<>0时重复.
REPNC 当CF=0且CX/ECX<>0时重复.

—————————————– 五、程序转移指令 ———————————————-

1. 无条件转移指令 (长转移)

1
2
3
JMP         无条件转移指令  
CALL 过程调用
RET/RETF 过程返回

2. 条件转移指令 (短转移,-128到+127的距离内)( 当且仅当(SF XOR OF)=1时,OP1<OP2 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
JA/JNBE     不小于或不等于时转移.  
JAE/JNB 大于或等于转移.
JB/JNAE 小于转移.
JBE/JNA 小于或等于转移.
以上四条,测试无符号整数运算的结果(标志C和Z).
JG/JNLE 大于转移.
JGE/JNL 大于或等于转移.
JL/JNGE 小于转移.
JLE/JNG 小于或等于转移.
以上四条,测试带符号整数运算的结果(标志S,O和Z).
JE/JZ 等于转移.
JNE/JNZ 不等于时转移.
JC 有进位时转移.
JNC 无进位时转移.
JNO 不溢出时转移.
JNP/JPO 奇偶性为奇数时转移.
JNS 符号位为 "0" 时转移.
JO 溢出转移.
JP/JPE 奇偶性为偶数时转移.
JS 符号位为 "1" 时转移.

3. 循环控制指令(短转移)

1
2
3
4
5
LOOP            CX不为零时循环.  
LOOPE/LOOPZ CX不为零且标志Z=1时循环.
LOOPNE/LOOPNZ CX不为零且标志Z=0时循环.
JCXZ CX为零时转移.
JECXZ ECX为零时转移.

4. 中断指令

1
2
3
INT         中断指令  
INTO 溢出中断
IRET 中断返回

5. 处理器控制指令

1
2
3
4
5
6
7
8
9
10
11
12
HLT         处理器暂停,  直到出现中断或复位信号才继续.  
WAIT 当芯片引线TEST为高电平时使CPU进入等待状态.
ESC 转换到外处理器.
LOCK 封锁总线.
NOP 空操作.
STC 置进位标志位.
CLC 清进位标志位.
CMC 进位标志取反.
STD 置方向标志位.
CLD 清方向标志位.
STI 置中断允许位.
CLI 清中断允许位.

——————————————— 六、伪指令 ————————————————–

1
2
3
4
5
6
7
DW          定义字(2字节).  
PROC 定义过程.
ENDP 过程结束.
SEGMENT 定义段.
ASSUME 建立段寄存器寻址.
ENDS 段结束.
END 程序结束.

——————————— 七、处理机控制指令:标志处理指令 ———————————

1
2
3
4
5
6
7
8
9
10
11
12
CLC     进位位置0指令  
CMC 进位位求反指令
STC 进位位置为1指令
CLD 方向标志置1指令
STD 方向标志位置1指令
CLI 中断标志置0指令
STI 中断标志置1指令
NOP 无操作
HLT 停机
WAIT 等待
ESC 换码
LOCK 封锁

====================== 浮点运算指令集========================

———– 一、控制指令(带9B的控制指令前缀F变为FN时浮点不检查,机器码去掉9B)———-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
                                                 机器码
FINIT 初始化浮点部件 9B DB E3
FCLEX 清除异常 9B DB E2
FDISI 浮点检查禁止中断 9B DB E1
FENI 浮点检查禁止中断二 9B DB E0
WAIT 同步CPU和FPU 9B
FWAIT 同步CPU和FPU D9 D0
FNOP 无操作 DA E9
FXCH 交换ST(0)和ST(1) D9 C9
FXCH ST(i) 交换ST(0)和ST(i) D9 C1iii
FSTSW ax 状态字到ax 9B DF E0
FSTSW word ptr mem 状态字到mem 9B DD mm111mmm
FLDCW word ptr mem mem到状态字 D9 mm101mmm
FSTCW word ptr mem 控制字到mem 9B D9 mm111mmm

FLDENV word ptr mem mem到全环境 D9 mm100mmm
FSTENV word ptr mem 全环境到mem 9B D9 mm110mmm
FRSTOR word ptr mem mem到FPU状态 DD mm100mmm
FSAVE word ptr mem FPU状态到mem 9B DD mm110mmm

FFREE ST(i) 标志ST(i)未使用 DD C0iii
FDECSTP 减少栈指针1->0 2->1 D9 F6
FINCSTP 增加栈指针0->1 1->2 D9 F7
FSETPM 浮点设置保护 DB E4

—————————————– 二、数据传送指令 ——————————————–

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
FLDZ                  将0.0装入ST(0)                  机器码  D9 EE  
FLD1 将1.0装入ST(0) 机器码 D9 E8
FLDPI 将π装入ST(0) 机器码 D9 EB
FLDL2T 将ln10/ln2装入ST(0) 机器码 D9 E9
FLDL2E 将1/ln2装入ST(0) 机器码 D9 EA
FLDLG2 将ln2/ln10装入ST(0) 机器码 D9 EC
FLDLN2 将ln2装入ST(0) 机器码 D9 ED

FLD real4 ptr mem 装入mem的单精度浮点数 机器码 D9 mm000mmm
FLD real8 ptr mem 装入mem的双精度浮点数 机器码 DD mm000mmm
FLD real10 ptr mem 装入mem的十字节浮点数 机器码 DB mm101mmm

FILD word ptr mem 装入mem的二字节整数 机器码 DF mm000mmm
FILD dword ptr mem 装入mem的四字节整数 机器码 DB mm000mmm
FILD qword ptr mem 装入mem的八字节整数 机器码 DF mm101mmm

FBLD tbyte ptr mem 装入mem的十字节BCD数 机器码 DF mm100mmm

FST real4 ptr mem 保存单精度浮点数到mem 机器码 D9 mm010mmm
FST real8 ptr mem 保存双精度浮点数到mem 机器码 DD mm010mmm

FIST word ptr mem 保存二字节整数到mem 机器码 DF mm010mmm
FIST dword ptr mem 保存四字节整数到mem 机器码 DB mm010mmm

FSTP real4 ptr mem 保存单精度浮点数到mem并出栈 机器码 D9 mm011mmm
FSTP real8 ptr mem 保存双精度浮点数到mem并出栈 机器码 DD mm011mmm
FSTP real10 ptr mem 保存十字节浮点数到mem并出栈 机器码 DB mm111mmm

FISTP word ptr mem 保存二字节整数到mem并出栈 机器码 DF mm011mmm
FISTP dword ptr mem 保存四字节整数到mem并出栈 机器码 DB mm011mmm
FISTP qword ptr mem 保存八字节整数到mem并出栈 机器码 DF mm111mmm

FBSTP tbyte ptr mem 保存十字节BCD数到mem并出栈 机器码 DF mm110mmm

FCMOVB ST(0),ST(i) <时传送 机器码 DA C0iii
FCMOVBE ST(0),ST(i) <=时传送 机器码 DA D0iii
FCMOVE ST(0),ST(i) =时传送 机器码 DA C1iii
FCMOVNB ST(0),ST(i) >=时传送 机器码 DB C0iii
FCMOVNBE ST(0),ST(i) >时传送 机器码 DB D0iii
FCMOVNE ST(0),ST(i) !=时传送 机器码 DB C1iii
FCMOVNU ST(0),ST(i) 有序时传送 机器码 DB D1iii
FCMOVU ST(0),ST(i) 无序时传送 机器码 DA D1iii

———————————————– 三、比较指令 ———————————————

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FCOM                  ST(0)-ST(1)                      机器码  D8 D1  
FCOMI ST(0),ST(i) ST(0)-ST(1) 机器码 DB F0iii
FCOMIP ST(0),ST(i) ST(0)-ST(1)并出栈 机器码 DF F0iii
FCOM real4 ptr mem ST(0)-实数mem 机器码 D8 mm010mmm
FCOM real8 ptr mem ST(0)-实数mem 机器码 DC mm010mmm

FICOM word ptr mem ST(0)-整数mem 机器码 DE mm010mmm
FICOM dword ptr mem ST(0)-整数mem 机器码 DA mm010mmm
FICOMP word ptr mem ST(0)-整数mem并出栈 机器码 DE mm011mmm
FICOMP dword ptr mem ST(0)-整数mem并出栈 机器码 DA mm011mmm

FTST ST(0)-0 机器码 D9 E4
FUCOM ST(i) ST(0)-ST(i) 机器码 DD E0iii
FUCOMP ST(i) ST(0)-ST(i)并出栈 机器码 DD E1iii
FUCOMPP ST(0)-ST(1)并二次出栈 机器码 DA E9
FXAM ST(0)规格类型 机器码 D9 E5

———————————————- 四、运算指令 ——————————————–

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
FADD                  把目的操作数 (直接接在指令后的变量或堆栈缓存器) 与来源操作数 
(接在目的操作数后的变量或堆栈缓存器) 相加,并将结果存入目的操作数
FADDP ST(i),ST 这个指令是使目的操作数加上 ST 缓存器,并弹出 ST 缓存器,
而目的操作数必须是堆栈缓存器的其中之一,最后不管目的操作数为何,
经弹出一次后,目的操作数会变成上一个堆栈缓存器了
FIADD FIADD 是把 ST 加上来源操作数,然后再存入 ST 缓存器,
来源操作数必须是字组整数或短整数形态的变数

FSUB 减
FSUBP
FSUBR 减数与被减数互换
FSUBRP
FISUB
FISUBR

FMUL 乘
FMULP
FIMUL

FDIV 除
FDIVP
FDIVR
FDIVRP
FIDIV
FIDIVR

FCHS 改变 ST 的正负值

FABS 把 ST 之值取出,取其绝对值后再存回去。

FSQRT 将 ST 之值取出,开根号后再存回去。

FSCALE 这个指令是计算 ST*2^ST(1)之值,再把结果存入ST里,而 ST(1)之值不变
ST(1)必须是在 -32768 到 32768(-215到215)之间的整数,
如果超过这个范围计算结果无法确定,如果不是整数ST(1)会先向零舍入成整数
再计算。所以为安全起见,最好是由字组整数载入到 ST(1) 里。

FRNDINT 这个指令是把 ST 的数值舍入成整数,FPU提供四种舍入方式,
由 FPU 的控制字组(control word)中的RC两个位决定
RC 舍入控制
00 四舍五入
01 向负无限大舍入
10 向正无限大舍入
11 向零舍去
  • Copyrights © 2015-2024 TeX_baitu
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~