LLVM 4.0 新特性,再也不用恶心的 @synthesize 了

Xcode now includes the Apple LLVM Compiler version 4.0, including the following newObjective-C language features:

 - Default @synthesize: automatically synthesizes an @property when unimplemented 
 - Objective-C literals: create literals for NSArray, NSDictionary, and NSNumber, just the same as the literals for NSString 
 - Objective-C container subscripting: use '[]' syntax to access containers

Android 中控件的ID问题

一直在看IOS,以及MTK等的代码,习惯性的自己定义一个ID,Android下面针对ID的自动生成反而有些茫然,如下:

<TextView
	android:id="@+id/MyTestView02"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_alignParentBottom="true"	
	android:layout_marginBottom="83dp"
	android:layout_marginRight="42dp"	
	android:layout_toLeftOf="@+id/MyTestView01"	
	tools:context=".TestStringActivity" />

这个  android:id="@+id/MyTestView02" 中的 MyTestView02 应该从哪儿指定呢?

刚刚开始是自定义累一个 IDValue.xml文件,然后从文件里面指定ID的编号,结果,当调用findViewById(R.id.MyTestView02) 的时候,总返回NULL

看了看代码发现,其实这个MyTestView02 只要你指定就可以了,至于是什么 ,编译器在扫描XML的时候会自动指定一个数值。关键在这个“+”符号。

看来,写多了代码,不见得是个好事,尤其是养成了习惯。

mac系统的快捷键

几个有用的Mac快捷键,截屏还是蛮好用的。

cmd+A:全选
全屏截图:Command-Shift-3
使用快捷键后会马上截取当前的全屏

指定区域截图:Command-Shift-4
使用快捷键后会出来一个带有座标的瞄准器,用鼠标的拖放可以选择需要截图的区域。

其他常用快捷键:
    * cmd+C:拷贝
    * cmd+V:粘贴
    * cmd+W:关闭窗口
    * cmd+Q:退出程序
    * cmd+I:显示文件/文件夹属性
    * cmd+Backspace:删除
    * cmd+shift+3:捕获整个屏幕
    * cmd+shift+4:捕获选择的区域
    * cmd+shift+4+space:捕获某个应用程序的窗口
    * cmd+F:在应用程序中搜索
    * cmd+Space:用Spotlight进行桌面搜索
    * fn+Backspace:相当于Windows里面的Delete键(笔记本键盘专用,台式机键盘有Delete键)
    * 开机时,听到启动音后,按住Option(相当于Windows的Alt)键,可以选择从Windows或者Mac启动。
    * 开机时,听到启动音后,按住“T”键,将使计算机进入目标磁盘状态,即通过USB连线,可以将苹果机当作USB硬盘使用。

XCode 4.5 的Springs and Structs 去了哪里

这两天把XCode升级到了4.5,新建的项目里面控件对齐的时候,找不到Spring and Structs  视图了,如下图所示,很是奇怪。

正常情况下应该是这样的

顺 便网上查询了一下,刚刚开始无从下手,不久发现XCode,其实是iOS 6.0 新增了一个新的功能 叫做 “自动布局" 英文为“Constraints” ,可以在帮助信息中查询 “Auto Layout: Understanding Constraints” 这部分信息。

究竟是什么东东呢? 下面剽窃部分内容来看看

原文 http://www.entlib.net/?p=2591

剩下的根据下面的链接应该都可以找到,下面黏贴下来,目的是避免链接失效

iOS 6编程(14)-自动布局(Auto Layout)简介

iOS 6 在应用程序用户界面开发中,引入了自动布局(Auto Layout)概念。自动布局使用约束(Constraint)在用户界面描述各类视图的位置、行为和关系。

在Xcode 4.5中,我们可以使用Interface Builder,或者可视化格式语言(Visual Format Language),或者标准的SDK API调用(NSLayoutConstraint类)等等三种方式来创建约束(Constraint)。

Auto Layout用这些Constraints 来对所有的视图进行一些计算,设置视图的位置和尺寸。不再需要设置视图的frame属性,也就是完全基于针对视图或元素所设置的Constraints来布局界面的视图元素。

使用Xcode 4.5 创建App时,Auto Layout功能在所有nib或Storyboard 文件中,默认是enable(开启的)。具体设置的地方,可以查看之前的文章。(如果该链接失效,看下面的 “iOS/Xcode异常:reason: ‘Could not instantiate class named NSLayoutConstraint’”)

在iOS 6 引入自动布局之前,我们使用springs and struts模式来设计可旋转和调整大小的用户界面。简单归纳和回顾一下,主要有三种方法:

1. 自动旋转和自动调整大小

通过在Size Inspector面板中,设置各类视图的Autosizing属性,无需编写代码,但这种仅适用于UI比较简单的应用。

2. 调整视图框架(frame属性)

每一个UI 元素在屏幕上都由一个矩形区域定义,这个矩形区域就是UI 元素的frame属性。可以使用C语言函数CGRectMake(x,y,width,height) 来重新定义视图的frame属性。

如果UI元素比较多,这个需要跟踪每一个UI元素的大小和位置,代码设计起来比较麻烦。

3. 重新设计不同的视图-横向和纵向视图

在 每一个场景中分别定义横向和纵向2个视图,这个每一个视图都需要定义独立的输出口。虽然2个视图和同一个视图控制ViewController关联,但是 不能共享输出口,在视图控制器中需要交互的UI元素数量会变成 2 倍。当然优点是,不同的视图完全独立开了,避免相互影响。

上述三种方式在开发过程中,都存在一些缺点。iOS 6 引入的自动布局(Auto Layout)正是为了改进这个不足之处。个人觉得也可以适应之后不断增多的不同尺寸的iPad、iPhone设备。

iOS/Xcode异常:reason: ‘Could not instantiate class named NSLayoutConstraint’

异常信息:Terminating app due to uncaught exception ‘NSInvalidUnarchiveOperationException’, reason: ‘Could not instantiate class named NSLayoutConstraint

具体场景:Xcode 4.5 选择iPhone、iPad 5.0/5.1 Simulator(模拟器)

解决办法:需要关闭storyboard或xib界面文件的Use Auto Layout 选项,这是因为Auto Layout特性是iOS 6 新增加的,在之前的 5.0/5.1 Simulator模拟器中不支持。

iOS 6编程(15)-创建自动布局(Auto Layout)简单应用 

创建自动布局(Auto Layout)简单应用

在 深入研究Interface Builder的自动布局特性之前,我们先创建一个简单的App,演示自动布局的基本概念。使用Xcode的Single View Application模板创建一个项目,项目名称为AutoLayout,类前缀也设置为AutoLayout,选择Storyboard和 Automatic Reference Counting选项。

在Interface Builder中启动和禁用自动布局功能

默 认情况下,针对Storyboard和单独的NIB文件都会启用自动布局特性。我们从项目导航栏选择MainStoryboard.storyboard 文件,接着显示File Inspector面板,在该监视器面板中,找到Use Autolayout 复选框,如下图所示:

在File Inspector面板中,关闭Use Autolayout选项,我们首先演示没有使用Autolayout特性的情况。

在Autolayout关闭之后,我们拖拉一个按钮到场景视图中,放置在视图的底部水平中心位置,垂直蓝色引导线会出现,表示按钮将放置在水平中心位置。实际上,按钮视图的位置是使用屏幕上硬编码的x和y坐标来定义位置的。只要设备保持纵向模式,按钮位置将非常准确完好。

 

然 而,在设备转向横向模式时,就出现问题了。可以编译和运行App,然后转向设备。或者在Interface Builder 环境,使用simulated metrics功能来测试转向效果。在文档大纲面板,选择视图控制器(Auto Layout View Controller),然后打开Attributes Inspector面板。在Simulated Metrics栏目,选择Orientation选项,设置为Landscape纵向模式,如下图所示。

另外,针对不同屏幕大小的用户界面布局测试,也可使用上面面板的Size 属性。例如,在4寸iPhone 5屏幕的测试,可设置Size属性为Retina 4 Full Screen。

这个是快速且有效检查布局是否如预期工作的方法。从上图中可以看出,按钮找不到了。这是因为按钮仍然保持着在父视图中相同的位置坐标,在横向模式下,这个位置超出了父视图可视范围。
在之前的iOS 开发中,我们可使用springs and struts,或者编写代码检测设备的转向,将按钮移动到屏幕的新位置。但是,在iOS 6 中,这个问题可以使用自动布局来解决。

现 在,我们将设计界面重新切换会纵向模式,这样按钮会重新显示出来。选择Storyboard文件,在File Inspector面板中,启用Use Autolayout选项。选择按钮,移开当前位置,然后重新移回来,让Interface Builder知道为按钮视图创建约束。默认情况下,Interface Builder将创建约束提供预期的布局行为。当然,这些约束可以查看和修改,但是在本范例程序中,默认创建的约束足够实现自动布局的演示需要的。

再次切换视图到横向模式,这次按钮可以正常显示出来了。

显然,Interface Builder 预想到了用户界面的布局要求。然而,这并不总是符合预期的。针对这一情况,Interface Builder提供了大量的选项和可视化提示,有助于创建自动布局的约束。

iOS 6编程(16)-Interface Builder自动布局功能

Interface Builder自动布局功能

为了帮助基于约束来实现自动布局,Interface Builder增加了大量的功能,下面将阐述这些新功能。

自动化约束

从 前一个范例中,我们知道在视图布局设计过程中,Interface Builder自动给子视图设置了约束。另外,需要知道Interface Builder自动添加的约束和开发人员手工添加的约束是有区别的。手动添加的约束认为是用户约束(user constraints)。Interface Builder 自动添加的约束首先需要调整为user constraints,然后才能删除。user constaints也可以配置为标准间距(standard spacing)。这些在后面的教程中会具体演示。

子 视图在布局上的定位操作,也是指示Interface Builder 配置相应的约束。如在父视图中移动按钮视图到水平中心位置,将出现中心指示线,将按钮放置在此处,将自动创建一个水平中心的约束(center constraint)。移动按钮视图到父视图的边缘位置,将出现边界指示线(margin guideline),如下图所示。如果放置在此处,Interface Builder 将创建一个约束,使用Apple建议的标准间隔距离(standard spacing distance)来固定按钮视图底部和父视图底部之间的间隔。

如果一个视图移动接近另外一个视图的边缘时,垂直指示线也会出现,相同标准设置的约束也会创建。下图是Save按钮接近Cancel按钮时,垂直指示线出现在Save按钮的右侧边缘。

可视化提示

Interface Builder在布局画布中包含了一些可视化提示,突出显示当前视图上配置的约束。当在Interface Builder布局画布中选择一个视图时,该视图相关的约束将可视化显示。例如,在AutoLayout范例程序中,当我们选择画布中的按钮时,一些额外 的线条会显示,如下所示:

穿过按钮中心位置的垂直线条表示设置了按钮视图在父视图水平中心位置的约束(类似于NSLayoutAttributeCenterX 属性)。如果用一个等式描述,可以理解为:
label.NSLayoutAttributeCenterX = superview.NSLayoutAttributeCenterX

按钮下面的I线条,从按钮视图的底部到父视图的底部,表示在2个视图之间设置了一个垂直间隔约束,线条上没有额外的可视化信息,表示这是一个等式约束(equality constraint)。下图显示2个按钮视图之间设置了一个大于等于的水平间距约束:

在 按钮视图中的文本下面的水平线表示设置了2个按钮之间水平对齐文本基线(content baseline)的约束(可用NSLayoutAttributeBaseline表示)。另外,我们注意到水平间距约束线条比水平对齐线条要更粗一 些,比较粗的线条表示这是一个用户约束(user constraint),比较细的线条表示是Interface Builder 自动添加的约束。
宽度约束显示为和视图平行放置的I线条。如下图文本视图对象,设置了大于等于的宽度约束。

查看和编辑约束

所有对视图设置的约束都显示在文档大纲面板中。当视图层次中有多个视图容器时,每一个视图容器(container view)都将有一个单独的约束列表。例如,下图显示了包含2个按钮的用户界面的约束列表:

当在列表中选择任何一个约束时,相应地在布局画布中将显示对应的可视化提示。
可以使用多种方法来显示和编辑特定的约束,其中一种方法是从布局画布(layout canvas)或者文档大纲面板中一个约束,然后在Attributes Inspector面板查看和编辑约束的属性。下图显示了等式间距约束的设置。

在上图中,我们可以修改Relation、Constant、Direction和Priority等等约束的属性值。可以选择Standard复选框,就可以设置间距为Apple推荐的标准间距。
通 过在布局画布选择一个特定视图,在Size Inspector面板中将显示该视图相关的约束列表。如下图所示,列出了当前选择视图的4个相关的约束。点击人一个约束的设置图标,将显示编辑或者删除 约束的选项。如果约束类型不是用户约束(user constraint),则需要首先选择Promote to User Constraint 选项,然后Delete 选项才可以使用。

除 了这些约束外,还有Content Hugging Priority和Content Compression Resistance Priority 属性值也显示在这里。以一个按钮视图为例,可以水平扩展(水平hugging为低优先级),但是不能垂直增长(设置vertical hugging为高优先级)。相似地,按钮文本内容不应该压缩或者省略一部分,因此Content Compression Resistance Priority 的水平和垂直参数都应该设置为比较高优先级。一般而言,我们不必经常调整Interface Builder添加的这些默认设置。

在Interface Builder中创建新的约束

在Interface Builder中有多种方法创建新的用户约束(user constraints),需要记住,约束可以一次关联多个视图。

针对单一视图添加约束(如宽度约束)的一种方法是,在布局画布中选择该视图,然后选择Xcode的Editor > Pin 菜单。Pin 菜单提供了当前选择视图的可用约束列表,如宽度、高度和连接到父视图的所有边界等等。

Editor 菜单也可用来设置多个视图的约束。选择多个视图,然后选择Editor > Pin菜单设置约束。此时设置的约束将应用到所有选择的视图上。

当选择多个视图时,Editor > Align 菜单也可用来对齐选择的视图,如左边界、右边界,baseline等等。
除了使用Editor 菜单之外,还可用快速Interface Builder右下角的迷你工具条,如下图所示:

本节通过一个非常简单的范例,演示了在iOS 6 用户界面设计过程中,使用自动布局所带来的好处。后面,我们还会深入介绍Interface Builder所提供的自动布局的功能。

iOS 6编程(17)-自动布局深入演示

前面我们简单学习了自动布局的一些基础知识和Interface Builder中自动布局的一些功能。这里,我们进行通过一个示例App演示使用Interface Builder创建自动布局的约束,同时也演示了约束的优先级。

进一步在布局画布上,添加了按钮和UIImageView图像视图,通过iOS 6 引入的自动布局和约束,可以方便灵活设计出纵向、横向模式都适应的应用程序。

Android资源文件中的"+"号

Android 资源文件中经常看到如下描述

<TextView
	android:id="@+id/textView" 
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:text="@string/hello"
/>

对于其中的 “+”不甚理解,查询了一些资料,得到如下结论

@+id/textView的加号(+)表示,textView可能还不存在,如果确实是这样,则创建一个新id并将其命名为textView

注意这个 textView可能还不存在,如果确实是这样,则创建一个新id并将其命名为textView 这句话是说在R.id 这个文件中创建一个名为textView 的变量数字

特此标记

关于Android模拟器键盘不能使用的解决方法

很多朋友遇到一个问题,自己搭建完了Android环境后,启动模拟器体验Android系统,但是发现不能使用键盘方便的输入内容,如下图:

同时,使用笔记本的键盘也无法输入内容,只能通过模拟器内置的输入法输入内容,遇到这个问题怎么办呢?

解决方案,编辑模拟器

构建AVD的时候,在Hardware选项中,有个New按钮,选中,其中的Property选项中有设置很多模拟器所支持的东西。有个“keyboard support”,选中,OK后。设置其指为“Yes”。问题解决了。

IBOutlet与IBAction

附上鸟文,翻译的话,还是有点差距的

Apress - Learn Cocoa on the Mac (Feb 2010) (ATTiCA)
You might be wondering just what IBAction and IBOutlet are. Are they part of the Objective-C language?
Nope. They’re good old-fashioned C pre-processor macros. If you go into the AppKit.framework and look at the NSNibDeclarations.h header file, you’ll see that they’re defined like this:

#ifndef IBOutlet
#define IBOutlet
#endif

#ifndef IBAction
#define IBAction void
#endif

Confused? These two keywords do absolutely nothing as far as the compiler is concerned. IBOutlet gets entirely removed from the code before the compiler ever sees it. IBAction resolves to a void return type, which just means that action methods do not return a value. So, what’s going on here?

The answer is simple, really: IBOutlet and IBAction are not used by the compiler. They are used by Interface Builder. Interface Builder uses these keywords to parse out the outlets and actions available to it. Interface Builder can only see methods that are prefaced with IBAction and can only see variables or properties that are prefaced with IBOutlet. Also, the presence of these keywords tells other programmers, looking at your code in the future, that the variables and methods in question aren’t dealt with entirely in code. They’ll need to delve into the relevant nib file to see how things are hooked up and used.

IOS atomic与nonatomic,assign,copy与retain的定义和区别

剽窃自 http://blog.csdn.net/mars2639/article/details/7352540

atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。

       atomic

设置成员变量的@property属性时,默认为atomic,提供多线程安全。

在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面这样:

{lock}
if (property != newValue) {
[property release];
property = [newValue retain];
}
{unlock}

       nonatomic

禁止多线程,变量保护,提高性能。

atomic是Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。

指出访问器不是原子操作,而默认地,访问器是原子操作。这也就是说,在多线程环境下,解析的访问器提供一个对属性的安全访问,从获取器得到的返回值或者通过设置器设置的值可以一次完成,即便是别的线程也正在对其进行访问。如果你不指定 nonatomic ,在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了 nonatomic ,那么访问器只是简单地返回这个值。

assign
对基础数据类型 (NSInteger,CGFloat)和C数据类型(int, float, double, char)等等。
此标记说明设置器直接进行赋值,这也是默认值。在使用垃圾收集的应用程序中,如果你要一个属性使用assign,且这个类符合NSCopying协议,你就要明确指出这个标记,而不是简单地使用默认值,否则的话,你将得到一个编译警告。这再次向编译器说明你确实需要赋值,即使它是可拷贝的。

retain
对其他NSObject和其子类对参数进行release旧值,再retain新值指定retain会在赋值时唤醒传入值的retain消息。此属性只能用于Objective-C对象类型,而不能用于Core Foundation对象。(原因很明显,retain会增加对象的引用计数,而基本数据类型或者Core Foundation对象都没有引用计数——译者注)。

注意: 把对象添加到数组中时,引用计数将增加对象的引用次数+1。

copy
对NSString 它指出,在赋值时使用传入值的一份拷贝。拷贝工作由copy方法执行,此属性只对那些实行了NSCopying协议的对象类型有效。更深入的讨论,请参考“复制”部分。

copy与retain:

Copy其实是建立了一个相同的对象,而retain不是:
1.比如一个NSString 对象,地址为0×1111 ,内容为@”STR”,Copy 到另外一个NSString 之后,地址为0×2222 ,内容相同。

2.新的对象retain为1 ,旧有对象没有变化retain 到另外一个NSString 之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1。
总结:retain 是指针拷贝,copy 是内容拷贝。

assign与retain:

1. 接触过C,那么假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给 (assign)了b。此时a和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的,因为a并不知道b是否还在使用这块内存,如 果a释放了,那么b在使用这块内存的时候会引起程序crash掉。
2. 了解到1中assign的问题,那么如何解决?最简单的一个方法就是使用引用计数(reference counting),还是上面的那个例子,我们给那块内存设一个引用计数,当内存被分配并且赋值给a时,引用计数是1。当把a赋值给b时引用计数增加到 2。这时如果a不再使用这块内存,它只需要把引用计数减1,表明自己不再拥有这块内存。b不再使用这块内存时也把引用计数减1。当引用计数变为0的时候, 代表该内存不再被任何指针所引用,系统可以把它直接释放掉。
总 结:上面两点其实就是assign和retain的区别,assign就是直接赋值,从而可能引起1中的问题,当数据为int, float等原生类型时,可以使用assign。retain就如2中所述,使用了引用计数,retain引起引用计数加1, release引起引用计数减1,当引用计数为0时,dealloc函数被调用,内存被回收。

王爽 汇编语言 课程设计2

;本程序在VMWARE中调试通过,可以写入软盘,并正常执行
;可以使用Bochs 但是操作细节比较复杂
;程序界面不够友好
;程序的切换方式是 ESC 注意不要进入时间设置就退不出来了.
;在时间设置界面没有设置提示语句,进去就是黑屏幕,不过只要输入数字就会显示.注意输入 的
;数字的大小,世界上没有24:00:00以后的时间.我也懒得去判断输入了.
;输入后按回车或者超过6个有效 数字,程序会自动设置时间,并切换回主界面
;在Windows 下看不到时间的更改,原因是,Windows只在系统引导的时候读取CMOS时间,并把它的
;初值给8253,其余时间都由8253为操作系统提供时钟,因此尽管在Windows状态下可以更改CMOS
;中的时间,但是右下脚的时间是不会产生变化的,也就是说更改时间是看不到的.关机的时候,
;系统把8253中的时间经过计算后写回CMOS。这点在WINDOWS下调试程序应当注意.

;在Windows 2000以后的操作系统中,系统内核不允许直接端口操作,因此,在DEBUG中执行IN,OUT
;操作是无效的.因此端口的调试应在9X或DOS下进行,比如在虚拟机中的Windows 98中.
;中断嵌套问题。
;以一个8253作为中断处理芯片来算,则同时可能发生的最大中断数量为8级,因此堆栈必须至少足够
;8个以上中断的最大数目才不会发生可能的问题。
;286 以上系统使用两片8259A级联管理15级可屏蔽中断。从中断0到中断15。比较常见的分配方式:
;       IRQ0  系统定时器
;      IRQ1  键盘
;      IRQ2  可编程中断控制器8259A
;      IRQ3  COM2 ( 串口 )
;      IRQ4  COM1 ( 串口 )
;      IRQ6  软盘控制器
;      IRQ7  并行口LPT1
;      IRQ8  系统CMOS/实时钟
;      IRQ12  PS/2鼠标
;       IRQ13  数学协处理器
;      IRQ14  第一IDE控制器(硬盘)
;      IRQ15  第二IDE控制器  (CDROM ) 
;                        IRQ5    可用     (如声卡) 
;                        IRQ9    可用     (如网卡)
 ;                       IRQ10  可用     (如USB)
 ;                       IRQ11  可用     (如SCSI主适配器)
;按照16级中断来处理,那么,在极端的情况下如果16级同时发生,则事先准备的堆栈区域必须满足
;16级中断的要求。每级中断至少14个寄存器。即14*16/8*16=448B, 堆栈应大于此数.

;CPU工作在 8086模式下,BIOS在初始化完系统后,在1M内存的使用情况

;384KB上位内存----- ************************
;                             *                                        * 
;                             *                                        * 
;保留                       *                                        * 
;                             *                                        *
;                             *                                        *
;640KB常规内存----- ************************------9FFFFH 
;                             *                                        * BIOS扩展数据区 1KB
;                             ************************------9FC00H
;                             *                                        * 
;                             *                                        *  ___607KB
;                             *                                        * 
;                             *                                        * 
;                             ************************------07E00H
;                             *                                        * 系统引导扇区安放位置 512B
;                             ************************ ------07C00H
;                             *                                        * 
;                             *                                        * _______29KB
;                             *                                        * 
;                             ************************------00800H
;                             *                                        *  BIOS数据区 1KB
;                             ************************------003FFH
;                             *                                        *  BIOS中断向量表 1KB
;                             ************************------00000H
;
;只可以使用常规的640K中的没有被占用的空间。

Assume cs:code
Data Segment
Error03 db 'Disk Write-Protect Error','$' ;磁盘写保护错误
Error04 db 'Can Not Find The Right Sector','$'    ;磁道寻找错误,软盘损坏
Error20 db 'Floppy Drive Error ,Maybe It Can Not Work !','$'  ;软盘不存在或工作部正常
Error80 db 'No Answer From Disk Drive!','$' ;驱动器不响应
ErrorExit db 'Press Any Key To Try Again Or Press ESC To Quit','$'
Data  Ends

Code Segment
Start:

;安装程序
;写入以下的程序到第一个和后面的扇区
;不需要确定程序的确切固定长度,因为int 13h只能按照512字节的倍数传递,所以即使比512多
;一个字节,也要传递512个。
mov ax,Data
mov ds,ax

mov ax,cs
mov es,ax   ;要写的内存基址
lea bx,Real ;要写的内存偏移

mov ah,3   ;功能号,写
mov al,((ProgrameEnd-Start)/512+1);写扇区的数目,浪费一个扇区以获得编写的方便。
mov ch,0   ;磁道号
mov cl,1   ;扇区号
mov dl,0   ;驱动器号
mov dh,0   ;磁头号(面)
int 13h   

;磁盘读写容错处理
cmp ah,0
je WriteFinish

cmp ah,03h
je WriteError03

cmp ah,04h
je WriteError04

cmp ah,20h
je WriteError20

cmp ah,80h
je WriteError80

WriteFinish:
mov ah,4ch
int 21h

WriteError03:
mov si,Offset Error03
jmp WriteErrorManage

WriteError04:
mov si,Offset Error04
jmp WriteErrorManage

WriteError20:
mov si,offset Error20
jmp WriteErrorManage

WriteError80:
mov si,Offset Error80
jmp WriteErrorManage

WriteErrorManage:
mov dh,4
mov dl,4
call near ptr Show_Str
mov si,Offset ErrorExit
mov dh,6
mov dl,4
Call near ptr Show_Str
mov ah,0
int 16h
Call near ptr ClearScrean
cmp al,1bh
jne Start
jmp WriteFinish

;实际程序体
Real:
;第一个扇区的数据
;第一个扇区只作为引导第二个扇区的引导程序 作用是把第二个扇区读到0:800处,
;并置cs:ip到0:800,原因是在引导现有的操作系统的时候,需要用到0:7c00内存区
;其中int 9h 需要占用0040:17作为状态字
;必须把0:7c00内存空闲出来。
;由于硬盘引导记录只能被存放到0:7c00处才能被正确引导,因此,需要在引导前把本程序
;移出内存。
;程序转移

;设置系统用堆栈内存区,由于没有操作系统支持,因此必须自己设定堆栈范围
;栈顶设置在9FBFF:0处,9FC00H(BIOS)保留数据区前一个位置.

cli ;避免堆栈设置时发生中断
mov ax,9000H             
mov ss,ax
mov ax,0FBFFH
mov sp,ax
sti

;内存基址设置,一次设置,以后不用更改,因为程序小于64K
mov ax,0
mov ds,ax
mov es,ax  

;读取第二和以后的扇区到内存的0:800处
mov bx,800h ;要读的内存偏移
mov ah,2   ;功能号,读
mov al,((ProgrameEnd-T)/512+1)   ;读扇区的数目,浪费512个字节以获得编写的方便。
mov ch,0   ;磁道号
mov cl,2   ;扇区号
mov dl,0   ;驱动器号
mov dh,0   ;磁头号(面)
int 13h   

;置CS,IP
mov ax,0
mov bx,800h
push ax
push bx
retf ;retf 相当于
     ;pop ip 
     ;pop cs 
     ;cs ip 的值不能通过普通的mov指令修改

;凑足一个扇区,主要满足int 19 对'55AA'标志的要求
;当前指令减去标号指令地址来计算应当的填充数据.
 db 510-($- Real) dup( 'A')

DW  0AA55H

;一扇区以后的扇区的数据
T:
jmp ReturnPoint;实际程序代码区
;Data Segment
Reset        db '1)Reset PC' ,0ah,0ah,0ah ;换行               ;'$'
StartS       db '2)Start System',0ah,0ah,0ah             ;'$'
Clock        db '3)Clock',0ah,0ah ,0ah                ;'$'
Set          db '4)Set Clock','$'
;时钟设置界面提示信息
SetTimeMessage db 'Please Input The Right Time :','$'

TimeBuffer db 3 dup(0),'e'    
                ;'$'的ASCII为36 即 0x24 会出现错误BCD 码中有0x24 e的ASCII为101 即 0x65   
               ;判断是否为终止符号
Counter dw 0 ;用在时钟设置中的字符输入计数器
TimeShowBuffer db 6 dup('0'),'$'
;Data ends

ReturnPoint: ;程序返回点
;设置屏幕显示
;清屏
call ClearScrean
;由于使用静态地址分配,因此只有采用标号差来计算距离数据区的长度,最终确定数据区的位置。
mov si,(Reset-T+800H)
mov dh,4
mov dl,12
call Show_Str

;循环读取键盘缓冲区
ReadKeybordBuffer:
mov ax,0 ;实际参数传递为ah ,但要在循环读取中要清空 al 因此直接清空 ax
int 16h
cmp al,'1'
je RebootFar ;程序超过JE的最大跳转范围
cmp al,'2'
je  StartSystemFar
cmp al,'3'
je TimeShow
cmp al,'4'
je Clock_SetFar
jmp ReadKeybordBuffer

Clock_SetFar:
jmp Clock_Set
RebootFar:
jmp Reboot
StartSystemFar:
jmp StartSystem

;时间显示
TimeShow:
call ClearScrean

;读取时间
TimeShowStart:
mov bx,(TimeBuffer-T+800H)
call ReadTime

;转换时间
mov bx,(TimeBuffer-T+800H)
mov si,(TimeShowBuffer-T+800H);偏移地址
call BCDChange

;向显示时间的格式转换
mov bx,(TimeShowBuffer-T+800H)
mov si,bx
call ChangeForShow
;屏幕显示
mov si,(TimeShowBuffer-T+800H)
mov dh,16
mov dl,20
call Show_Str

;检查键盘输入
mov ah,01h;检查缓冲区,若有字符则ZF=0,否则ZF=1 同时 AH=扫描码,AL=ASCII
int 16h ;在调用int 16h的过程中共需要多次PUSH数据,因此堆栈应满足需要(消耗100字节以内的内存)。 
jnz CheckCharacter
jmp TimeShowStart
CheckCharacter:
mov ah,00h
int 16h
cmp ah,01h ;判断是否为ESC
je ExitTimeShow
jmp TimeShowStart
ExitTimeShow:
call ClearScrean ;清屏

jmp ReturnPoint

Clock_Set:
;清屏
Call ClearScrean

mov si,(SetTimeMessage-T+800H)
mov dh,3
mov dl,6
Call Show_Str

;获取TimeShowBuffer的偏移地址作为字符串输入的接收内存
mov si,(TimeShowBuffer-T+800H)

;清除切换界面后内存的数据残留问题
mov ax,0
mov [si-2],ax
mov al,'0'
mov bx,0
mov cx,6
ClearDump:
mov [si][bx],al
inc bx
loop ClearDump

;字符输入循环控制部分
CharInputControl:
;16号中断获取字符
mov ah,0
int 16h

;判断是否是数字
;判断是否小于0
cmp al,'0'
jnb next

;非数字则判断是否为控制字符
jmp ControlCharInput 
;判断是否大于9
next:
cmp al,'9'

;数字则执行数字的输入
jna NumberCharInput

;非数字则判断是否为控制字符
jmp ControlCharInput

;控制字符判断
ControlCharInput:
;判断是否为左删除键
cmp al,08h
je BackSpace

;判断是否为回车
cmp al,0dh
je SetTimeNow

;判断是否为ESC
cmp al,1bh
je ReturnPointLong

;返回字符输入
jmp CharInputControl

ReturnPointLong:
jmp ReturnPoint

;是有效的数字输入
NumberCharInput:

;判断是否超过6个数字的输入
mov dx,[si-2]
cmp dx,5
ja  SetTimeNow

;数字写入相应得内存区域
mov ah,0
;程序超过CALL最大长度,要使用CALL far PRT或者CALL near PRT 实现跳转,
;但是这样长跳并不能解决所有问题。
;因为长跳是CS IP入栈 并重新设置编译器计算的地址,而本程序的地址不能通过编译器计算,
;此时的call 和JMP 有很大的差别.
;只能实行近跳(Near)
;注意JUMP FAR PTR 和jump near ptr 的区别很大,尤其在静态地址分配问题上.
call near ptr CharactorInputControl

;显示刚才的输入数字
mov ah,2
mov dh,9
mov dl,11
Call near ptr CharactorInputControl
;返回字符输入
jmp CharInputControl

;处理左删除
BackSpace:

;字符出栈
mov ah,1
call near ptr CharactorInputControl
;屏幕显示出栈后剩余的字符
mov ah,2
mov dh,9
mov dl,11
call near ptr CharactorInputControl
;返回字符输入
jmp CharInputControl

;时间设置开始
SetTimeNow:

;设置偏移
mov si,(TimeShowBuffer-T+800H)
mov bx,si
call near ptr ChangeFromKeyboardInput

mov si,(TimeShowBuffer-T+800H)
mov bx,(TimeBuffer-T+800H)
call near ptr ChangeToBCD

mov si,(TimeBuffer-T+800H)
call near ptr WriteToCMOS

jmp ReturnPoint

;屏幕显示子程序,以'$'为结束标志
;以ds:si指向要显示的内存区,dh存放行,dl存放列
Show_Str proc near

;寄存器入栈保护
push es
push ax

push bx
push cx

mov ax,0b800h
mov es,ax
mov al,160
mul dh
mov bx,ax
mov dh,0
add dl,dl
add bx,dx
mov cx,bx ;为换行标志准备
LoopShow_Str:
;判断结束标志
cmp ds:[si],byte ptr '$'
je EndLoopShow_Str
;判断换行标志
cmp byte ptr ds:[si], 0ah
jne NoLF

add cx,160
mov bx,cx 
inc si
jmp LoopShow_Str

NoLF:
mov al,ds:[si]
mov es:[bx],al
add bx,2
inc si
jmp LoopShow_Str

EndLoopShow_Str:
pop cx
pop bx
pop ax
pop es
ret

Show_Str endp

;BCD码向十进制转换得子程序
;ds:[bx]指向要处理的数据的内存地址
;es:[si]指向处理完成后数据存放的地址
;'$'指示处理内存的数据的终止符号。
BCDChange proc near
;寄存器保护
push ax
push cx

BCDChangeStart:
mov al,ds:[bx]
cmp al,'e'     ;'$'的ASCII为36 即 0x24 会出现错误 e的ASCII为101 即 0x65   
               ;判断是否为终止符号
je ExitBCDChange

mov ah,al   ;保护AL
mov cl,4    ;设置移动位数
;高四位,并存储
shr ah,cl
mov es:[si],ah
inc si

;低四位
;清高四位
shl al,cl
shr al,cl

mov es:[si],al
inc si
inc bx
jmp BCDChangeStart

ExitBCDChange:

pop cx
pop ax
ret
BCDChange Endp

;数字向屏幕显示转化子程序
;ds:[bx]指向要处理的数据的内存地址
;es:[si]指向处理完成后数据存放的地址
;'$'指示处理内存的数据的终止符号。
ChangeForShow proc near
push ax

ChangeForShowStart:
mov al,ds:[bx]
cmp al,'$'
je ExitChangeForShow
add al,30h
mov es:[si],al
inc bx
inc si
jmp ChangeForShowStart

ExitChangeForShow:
pop ax
ret
ChangeForShow  Endp

;时间读取子程序
;ds:[bx]指向存放数据的内存地址
ReadTime proc near
push ax
;时
mov al,4
out 70h,al
in al,71h
mov ds:[bx],al 
inc bx
;分
mov al,2
out 70h,al
in al,71h
mov ds:[bx],al
inc bx
;秒
mov al,0
out 70h,al
in al,71h
mov ds:[bx],al
pop ax
ret
ReadTime Endp

;清屏子程序
ClearScrean proc near
push ax
push bx
push cx
push ds
mov ax,0b800h
mov ds,ax
mov cx,2000
mov bx,0
loopClearScrean:
mov ds:[bx],byte ptr ' '
add bx,2
loop loopClearScrean
pop ds
pop cx
pop bx
pop ax
ret 
ClearScrean Endp

;转化用户的键盘输入到实际的数据输入的子程序
;ds:[si]指向要转化的内存区于
;ES:[bx]指向转化完成后的内存地址
;用'$'作为结束的标志
ChangeFromKeyboardInput proc near
push ax
StartChangeFromKeyboardInput:
mov al,ds:[si]
cmp al,'$'
je ExitChangeFromKeyboardInput
sub al,30h
mov es:[bx],al
inc si
inc bx
jmp StartChangeFromKeyboardInput
ExitChangeFromKeyboardInput:
pop ax

ret
ChangeFromKeyboardInput Endp

;十进制向BCD码转化的子程序
;ds:[si]指向要处理的内存区域用'$'作为内存结束标志
;es:[bx]指向处理完的内存区域
ChangeToBCD proc near

push cx
push ax
mov cl,4
StartChangeToBCD:
mov al,ds:[si]
cmp al,'$'
je ExitChangeToBCD
shl al,cl
inc si
mov ah,ds:[si]
add al,ah
inc si
mov es:[bx],al
inc bx
jmp StartChangeToBCD
ExitChangeToBCD:
pop ax
pop cx
ret

ChangeToBCD Endp

;数据输入和屏显子程序
;ah存放功能号,0表示入栈,1表示出栈,2表示显示
;ds:[si]指向字符栈空间
;ds:[si]的前面两个字节用于存放计数器的数值
;对于0号功能:(al)=入栈字符
;对于1号功能:(al)=返回的字符
;对于2号功能:(dh)、(dl)=字符在屏幕上显示的行、列位置
CharactorInputControl proc near
push bx
push dx
push di
push es

cmp ah,0
je charpush
cmp ah,1
je charpop
cmp ah,2
je charshow
jmp sret

charpush:
mov bx,[si-2]
mov [si][bx],al
inc word ptr [si-2]
jmp sret

charpop:
cmp word ptr [si-2],0
je sret
dec word ptr [si-2]
mov bx,[si-2]
mov al,[si][bx]
mov byte ptr [si][bx],0  ;清除数据
jmp sret

charshow:
mov bx,0b800h
mov es,bx
mov al,160
mov ah,0
mul dh
mov di,ax
add dl,dl
mov dh,0
add di,dx
mov bx,0

charshows:
cmp bx,[si-2]
jne noempty
mov byte ptr es:[di],' '
jmp sret
noempty:
mov al,[si][bx]
mov es:[di],al
mov byte ptr es:[di+2],' '
inc bx
add di,2
jmp charshows

sret:
pop es
pop di
pop dx
pop bx
ret

CharactorInputControl Endp

;数据写入CMOS
;ds:[si]指向内存区域
;CPU速度太快,端口来不及取走数据,因此,必须设置CPU等待。
;不等待的话,总是设置错误,调了好几天才注意到.
;究竟等待多长,不清楚,因此干脆写入一个数据后,就一直从端口读出,和写入的比较,直到相同了,
;才设置下一个.
;数据从端口的读入读出时间正好使得CMOS有时间取走数据。
WriteToCMOS proc near
push ax
;时
mov al,4
out 70h,al
mov al,ds:[si]
mov ah,al
out 71h,al

;延时代码,CPU从端口取走数据会自动匹配端口速度,因此下列代码相当于延时,同时又提高了
;程序的执行速度和代码的简化
in al,71h
inc si

;分
mov al,2
out 70h,al
mov al,ds:[si]
mov ah,al
out 71h,al
inc si

in al,71h

;秒
mov al,0
out 70h,al
mov al,ds:[si]
out 71h,al
in al,71h
pop ax
ret
WriteToCmos Endp

Reboot:

mov ax,0ffffh
mov bx,0
push ax
push bx
retf ;retf 相当于
     ;pop ip 
     ;pop cs 
     ;cs ip 的值不能通过普通的mov指令修改

StartSystem:

;读取C盘的第一扇区到内存的0:7c00处

             mov bx,7c00h  ;读到内存中的地址。

             mov dl,80h
             mov ah,2     ;读磁盘操作     
             mov dh,0     ;0盘面
             mov ch,0     ;0磁道
             mov cl,1     ;1扇区
             mov al,1     ;只读一个扇区
             int 13h

;置CS,IP
mov ax,0
mov bx,7c00h
push ax
push bx
retf ;retf 相当于
     ;pop ip 
     ;pop cs 
     ;cs ip 的值不能通过普通的mov指令修改

;计算代码长度
ProgrameEnd:

Code ends

end Start

附上BIOS内存分布图
386工作在实模式下内存情况分布图

CGContextAddCurveToPoint 的深入理解

CGContextAddCurveToPoint 这个函数看上去一般般,仔细琢磨发现不简单,为什么三个点就可以确定一条曲线呢? 网上查了查,小小研究了一下下。

1.关键知识 窃取自 (http://learn.gxtc.edu.cn/NCourse/jxcamcad/cadcam/Mains/main4-2.htm

Bezier曲线

在工程设计中,由给定型值点进行曲线设计往往由于型值点的误差而得不到满意的结果。另一方面,在一些更注重外观的设计中,型值点的精度又不很重要。从 1962年起,法国雷诺汽车公司的Bezier开始构造他的以“逼近”为基础的参数曲线表示法。以这种方法为基础,完成了一种自由型曲线和曲面的设计系统 UNIS-URF,1972年在雷诺汽车公司正式使用。

Bezier曲线的形状是通过一组多边折线(称为特征多边形)的各顶点唯一地定义出来的。在多边形的各顶点中,只有第一点和最后一点在曲线上,其余的顶 点则使用控制曲线的导数、阶次和形式。第一条和最后一条折线则表示出曲线在起点和终点处的切线方向。曲线的形状趋向仿效多边折线的形状。改变控制点与改变 曲线形状有着形象生动的直接联系。如图2.6所示。

1)Bezier曲线的定义

给定 n+ l个空间向量bi(i= 0,l,…,n),称 n次参数曲线段

为Bezier曲线。式中使用了Bernstein多项式Bi,n(u)作为基函数:

u是局部参数,u∈[0,1]。我们给出n=3的Bezier曲线的矩阵表示:



则有 P(u)=UMB

2)Bezier曲线的性质
Bezier曲线的基本数学表达式:


这说明Bezier曲线在始点和终点处的切线方向是与Bezier控制多边形的第一边及最后一边的走向一致。

这 说明曲线在起点和终点处的二阶导数仅与相邻的二点位置有关,而与其余各点的位置关。Bezier曲线的这一特性说明,只需适当移动控制点就能获得满意的曲 线位置和形状。利用这个特性,当采用分段Bezier曲线时,只要保证曲线在接点处的折线共线,就可以得到C1连续性。如图2.7所示的一个公共端点的二 条Bezier曲线,当两段曲线的控制折线在接点处共线时,就保证了它们连成的曲线在公共端点的一阶连续。

Bezier曲线还具有凸包性,即B6zier曲线均落在由它的控制点形成的凸壳内。所谓凸壳是指用橡皮图从外面去套所有控制点所形成的凸多边形。研究表明,分段Bezier曲线的凸包性体现更明显,这对于曲线的分割求交将更为有利。

此外,Bezier曲线的形状仅与特征多边形各控制点bi有关,它不依赖于坐标系的选择,因此具有几何不变性。没有局部控制能力是 Bezier曲线的一个缺点。在工程中,产品的模型常常需要局部修改。缺乏局部控制的能力在工程中是难以接受的。Bezier曲线另一缺点是随着控制点的 增加,曲线的次数也增加,因而计算量增大。下面介绍的B样条曲线可克服这两个缺点。

3)Bezier曲线的计算及分割作图法
Bezier曲线在工程中广泛接受的一个重要原因是其可以采用非常简单的方法制作。下面推导Bezier多项式的递归关系,这种关系可方便用计算机程序实现,而且便于用图形来解释。重写Bezier曲线方程为



可以看到,第一个括号内的项形成对控制点b0,b1,…,bn-1的Bezier多项式,而第二个方括号内的项对b1,b2,…,bn形成多项式。现在引人符号Pk,i(u)表示对bk,bk+l,…,b的Bezier多项式,那么上述
方程可以重写为

上式表明,Bezier多项式可以由另外两个这种多项式求得。其求法为用一条线连接相应的点(即在同一参数U),并与u成比例地分那条线。

我 们再用图2.8所示的例子来看一下Bezier曲线的分割作图法。前例给出的由点b0,b1,b2,b3控制成的一条三次Bezier曲线可在u=l/2 处分段,分成b4的两段均为Bezier曲线,它们由各自的四个控制点控制。在图2.8中,点b0,b1,b2和b3控制形成一条三次,Bezier曲 线。取线段bobl的中点b4,线段b1b2的中点b5,线段 b2b3的中点 b6,线段 b4b5的中点 b7,线段 b5 b6的中点b8,以及线段b7bs的中点bg,则可以证明,b9在由b。,b1,b2,b3确定的三次Bezier曲线上,且为u=l/2时的点 P(l/2)。我们将u=l/2代人矩阵方程就可得到:


图 2.8的分割,我们获得折线b0b4 b7b9b8b6b3,它比折线b0blb2b3更接近该BeZier曲线,且b9在曲线上。继续分别对由b9分成的两段曲线作分割,除获得更接近曲线的 折线外,还获得曲线上的两个点。分割次数越多,新的折线越逼近曲线,当到达某种精度时,可将获得的折线近似的表达Bezier曲线。由于分割操作仅涉及加 法和移位操作,所以便于硬件实现,提高作图速度。读者可根据送推式写出相应的计算机程序,进一步从几何作图的角度,对计算式改进,以便使算法更为简单。

以上部分为算法原理,下面是实际的应用

    使用函数CGContextAddCurveToPoint去 使用一个三次Bezier 曲线,要我们指定control point和endpoint。下图是一个三次的Bezier曲线,有两个控制点。两个控制点的定义了曲线的几何形状。如果两个控制点都在起点和终点的下 面,则则曲线向上供。如果第二个控制点相比第一个控制点更接近起点,则曲线会构成一个循环。
   使用函数CGContextAddQuadCurveToPoint 去使用添加一个二次Bezier曲线,要我们指定一个control point和endpoint。下图展示了相同的endpoint,不同的control point的结果。control point定义了曲线的供的方向。利用二次Bezier曲线不可能创造用三次Bezier曲线那么多的有趣的曲线。例如,用二次不可能创造一个交叉的曲 线。