• 26
  • 4月

今天很高兴成功写出了生平第一个汇编程序,程序很简单:从键盘读入一个数字,判断正负,正数输出1,负数输出-1,0时输出0。

因为汇编不是高级语言,所有的分枝都得靠跳转指令。于是老老实实地用流程图画了一下,别看就这么点功能,但输入的数字得一个一个地接收、判断(想起有穷状 态自动机来了,不知道这能不能算它的一种实现)。输出的时候也要根据正负、是否为0判断。最终这么一个小程序居然用了6处判断跳转!用C写2个if就可以 搞定的东东嘛……呵呵。

按照《代码大全(Code Complete)》上建议的方法,先把程序思路用注释全部写到源文件中去,然后一条条展开成代码,最终经过调试得到了以下可以成功运行的代码:

CODE  SEGMENT
      ASSUME    CS:CODE
START:                        ;init
      XOR       AX ,AX
      MOV       BX ,0030H   ;bh-negative flag, bl non-zero flag
      XOR       CX ,CX      ;input flag
INPUT:                        ;input a charactor
      MOV       AH,01
      INT       21H
      CMP       AL,1BH
      JE        RETURN      ;press ESC to exit
      CMP       AL,0DH
      JE        OUTPUT      ;if it's x0d, jump to OUTPUT
      TEST      CL,01H
      JNE       COMMON      ;if it's not the first charactor, jump to COMMON
      MOV       CL,01H      ;set input flag
      CMP       AL,'-'
      JNZ       COMMON      ;if it's not '-', jump to COMMON
      MOV       BH,01H      ;set negative flag
      JMP       INPUT       ;jump to INPUT
COMMON:
      CMP       AL,'0'
      JE        INPUT       ;if it's '0', jump to INPUT
      MOV       BL,31H      ;set non-zero flag
      JMP       INPUT       ;jump to INPUT
OUTPUT:
      MOV       AH,02H
      MOV       DL,0AH
      INT       21H         ;start a new line
      CMP       BL,'0'
      JE        NZ          ;if it's 0, '-' is of no use
      CMP       BH,01H
      JNE       NZ          ;if negative flag is false, jump to NZ
      MOV       DL,'-'
      INT       21H         ;print '-'
 NZ:  MOV       DL,BL
      INT       21H         ;print the non-zero flag
      MOV       DL,0AH
      INT       21H
      MOV       DL,0DH
      INT       21H         ;move cursor to a new line
      JMP       START       ;get the next number
RETURN:  RET
CODE  ENDS
      END       START

PS:期间犯了两个错误,记录一下
1.忘记了 ASSUME CS:CODE 这一句,没了代码段,造成了所有的跳转语句都报:“ Near jump or call to different CS”错误。没指定自己的代码段在哪里,一跳可不是to different CS嘛!
2.代码最后没有加RET,一接收完输入就报莫名其妙的错误。汇编毕竟是汇编,最后的CODE ENDS和END START只是给编译器看的。程序执行完毕,返回操作系统的指令也得写明。

搞定之后小研究了一个EXE文件的结构,找到一篇不错的文章: com和exe文件结构

  • 26
  • 12月

一学期的课程结束了,这周我们开始了本学期的第三个实验性课程——计算机组成原理实验。

实验课这种东西只要认真去做,确实能学到相当多的东西。EDA让我理解了电路的设计、系统软件分析与设计让我大概看明白了一个操作系统的内核是怎么写的,这一次的软件分析与设计则真的让我明白了计算机到底是怎么工作的。

自打接触计算机那时起,接受的教育都是:计算机是由程序控制执行的。程序是用编程语言写出来的。编程语言分为高级语言、汇编语言、机器语言。机器语言是2进制写的,在计算机底层直接控制计算机的执行——得出的结论是计算机完全是由机器语言控制运行的。

但是机器语言为什么能控制计算机运行呢?在本周以前,这对我就是一个黑盒问题。经过这两天的实验,这个黑盒被打开了。原来盒子里还有一个更底层的东西在控制着计算机,那就是微控制器中的微指令(计组课上老师讲过,但直到现在才搞明白-_-b)。计算机也无非就是一个大的EDA电路。运算器、内存、寄存器、输入设备、输出设备等无非都是连在系统总线上的一个个模块,每个模块都有使能端、总线开关等开关控制着它们的工作。

真正控制计算机工作是微控制器中的微代码,计算机的执行最根本上执行的是微代码,由微代码控制各个门的开关,决定数据从哪个模板流到哪个模块,各个模块应该做什么工作。而机器语言不过是微代码执行时查看的参数,微代码读取内存中的机器语言,根据机器语言决定下一步该执行什么,执行完毕后再读取下一条机器语言。

之所以一直说机器语言是控制计算机的执行,是因为在绝大多数计算机中,微代码是固定不变且不可改写的。一旦CPU的设计确定了,这台计算机的所有微代码就完全确定了(微代码存储在CPU的控制器中,固化在CPU的内部)。我们就只能在机器语言的层面上控制计算机了。

不过高校的实验就是高校的实验,我们的实验室中给的实验箱是一台自己连接各个模块,自己编写微代码的计算机。

实验指导书上给了一套可以实现加减法和基本逻辑运算的机器码及对应的微代码实验。不过我和我的搭挡晓亮决定做一个直接支持乘法指令的计算机。从今天下午开始到晚上十点多终于把所有的微代码设计出来了(直接写2进制控制各个模块的使能端确实不是件容易的事……)。明天去调试,期待能够成功。:)

  • 16
  • 4月

刚刚在三表的博客上看了那篇“找(卒瓦)”的文章,发现汉字输入及显示还真是个问题。

顺便看了留言里提到的这篇文章《如何显示和输入七万多汉字?》,先收藏一下。

  • 20
  • 3月

其实这已经不能算是“.net学习日记”了,只是因为刚刚解决了在C#中没有完全搞明白的一个问题,故姑且放在这一类中吧。

这是从《Java2入门经典》这本书中抄来的:我们不能生成一个接口类型的对象,但可以生成一个接口类型的变量。这个变量就可以存储实现该接口的任何类型的对象引用。

“接口”就是接口嘛!

  • 12
  • 2月

刚刚看了《汉化教学文集2006》中的几篇文章,谈到了字符编码问题。终于明白了ASCII,Unicode,Unicode big endian,UTF-8的关系。

ASCII:学过计算机基础的都知道,8bits的英文编码。实际只用低7位,127个字符,32(0x20)为空格,之前是控制字符,之后是有效字符。当>128时,需要使用代码页(code page)来确定表示的字符。在保存文件时一般ANSI项就是指它。

Unicode:使 用两个字节对全球字符进行统一编码,有65536个编码。学名“UCS”——“Universal Multiple-Octet Coded Character Set”.UCS-2为两字节版本,UCS-4为四字节版本,目前两个版本内容是一样的(字符数还没超过2个字节能表示的范围)。

Unicode little endian:将Unicode中表示同一个字符的两个字节顺序返过来。主要目的是为了适应CPU处理字符的方式,加快处理速度。在Windows上为Unicode的默认存储方式(记事本保存对话框中的那个“Unicode”就是这个)。

Unicode big endian:Unicode字符编码以正序存储。非默认。

引用一段原文:

//  “endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,一个皇帝送了命,另一个丢了王位。

我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。  //

应该可以说明问题。

UTF-8:Unicode的一种传输及存储方式,主要是为了消除编码中那些0x0000的字节和减少存储英文时所占用的空间。

以下是Unicode(UCS-4)和UTF-8之间的转换关系表:
U-00000000 – U-0000007F: 0xxxxxxx
U-00000080 – U-000007FF: 110xxxxx 10xxxxxx
U-00000800 – U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 – U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 – U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 – U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

Unicode编码转换到UTF-8,简单的把Unicode字节流套到x中就变成UTF-8了。

BMP:Basic Multilingual Plane,UCS-4中0x0000—-这些字符。

引用一下原文:
//  UCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。下面让我们做一些简单的数学游戏:

UCS-2有2^16=65536个码位,UCS-4有2^31=2147483648个码位。

UCS -4根据最高位为0的最高字节(引注:即第一个字节)分成2^7=128个group。每个group再根据次高字节分为256个plane。每个 plane根据第3个字节分为256行 (rows),每行包含256个cells。当然同一行的cells只是最后一个字节不同,其余都相同。

group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP。

将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。  //

BOM:Byte Order Mark,字节序,用以区分Unicode little endian与Unicode big endian。即使用名为”ZERO WIDTH NO-BREAK SPACE”的字符“0xFEFF”传输及存储时首先处理该字符。若读到FEFF,就表明这个字节流是Big-Endian的;而如果是FFFE,就表明 这个字节流是Little-Endian的。

以下为引用:
//  UTF-8不需要它来表明字节顺序,但但可以用BOM来表明编码方式。字符”ZERO WIDTH NO-BREAK SPACE”的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。 //

Windows就是使用BOM来标记文本文件的编码方式的。

区位码与GB2312之间的关系:
引用原文:
//  再说区位码,啊的区位码是1601,写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的ASCII编码, 我们在区位码的高、低字节上分别加上A0。这样“啊”的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码,虽然GB2312的原文根 本没提到这一点。 //

  • 03
  • 2月

对于一些资源,尤其是那些比较重要的系统级资源(如文件系统对象、网络系统对象、系统图形界面对象),使用后必需及时显式的释放,因为系统的垃圾回收器(GC)并不能保证在合适的时候被触发。
使用方法
1.用完后显式调用Dispose()方法
2.使用using结构,在对象超出作用域时using结构会自动调用Dispose()。
抄一段书上的代码:
using (Graphics g = this.CreateGraphics())
{
g.DrawLine(Pens.Black, new Point(0,0), new Point(3,5));
}
等价于:
Graphics g = this.CreateGraphics();
try{
g.DrawLine(Pens.Black, new Point(0,0), new Point(3,5));
}
finally{
if (g != null)
((IDisposable)g).Dispose();
}

  • 31
  • 1月

玩NorthWind的时候发现其中Order Detail表中有两个主键,但每个主键的列中都存在有相同值的行,由此推断:
一个表中有两个主键表示的意思是由这两个主键共同确定一个记录。
没怎么学过数据库,见笑。
18:17 2007-1-31

已查阅数据库方面的书,这好像是数据库里最基础的知识……
21:20 2007-3-8

  • 30
  • 1月

刚刚突然想起这个问题:为什么书上的代码在连接字符串时往往是使用一个StringBuider的对象,而不是常见的string。查了一下MSDN,终于明白是怎么回事了。
以下是MSDN上的原文:
String 对象是不可改变的。每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的 String 对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用 System.Text.StringBuilder 类。例如,当在一个循环中将许多字符串连接在一起时,使用 StringBuilder 类可以提升性能。

StringBuider还可以通过构造函数传入或设置Capacity属性来指定它的容量。不过这里的容量并不是指它 的最大长度,实际上是存储空间的分配增量。它的意思与当初学数据结构时,“线性表的顺序表示和实现”那部分讲用数组存储线性表时定义的那个增量一致:初始 申请该属性大小的内存空间,在内容的长度在此大小内变化时就不需要进行申请或释放内存的操作,当内容长度大于该属性值时,申请一块比目前空间大 Capacity的空间,将现有数据复制进去,释放原始空间进行操作。
以上是个人理解,尚未经过验证。

所有标签:.net Ajax Java javascript Linux map MySQL RSS TD-SCDMA Ubuntu vim web Win7 乱码 基础知识 备份 奥运会 希望泉 性能 缓存 编程