Pattern (1)

Chapter 1 Introduce of Patterns
Factory pattern :根据提供给工厂的数据,从一系列相关的类中选择一个类实例并返回。
Abstract Factory:用于返回一组类中的一个,在某些情况下,它实际上为一组类返回了一个工厂。
Bulider Pattern :根据提供给它的数据及其表示,将一系列对象组装成一个新对象。通常选择何种方式组装对象由工厂决定。
当创建新实例代价较高的时候,原型模式拷贝或克隆一个现有的类,而不是创建一个新实例。
单件模式可以保证有切只有一个对象实例,并提供一个该实例的全局访问点。
Adapter Pattern :用于讲一个类的接口转换成另一个类的接口。
桥接模式:可以将一个类的接口和它的实现分离,这样可以不用修改客户端代码就能改变或替换实现过程。
组合模式:是一个对象的集合,其中的任何一个对象既可以是一个组合,也可以时一个叶子对象。
装饰模式:用一个类包装给定的类,并为它添加新的功能,将所有未改动的方法传递给下面的类。
外观模式:将一系列复杂的对象放在一起,并提供一个新的、更简单的访问这些数据的接口。
享元模式:把一部分的类数据移到类外部。在执行方法是将数据传递进来,通过这种方式限制那些又小又相似的实力的增加数量。
代理模式:为一个比较复杂的对象提供一个简单的占位对象,实例画该复杂对象在某种程度上比较浪费时间或代价较高。
行为型模式(Behavioral Pattern)通常和对象之间的通信有关。包括几种模式:
职责链(Chain of Responsibility)把请求从链中的一个对象传递到下一个对象,直到请求被响应为止。通过这种方式在对象之间去除耦合。
命令模式(Command Pattern)用简单的对象表示软件命令的执行,支持等如和取消操作。
 
解释器模式(Iterpreter Pattern)提供一个如何把语言元素包含在程序中的定义。
迭代器模式(Iterator pattern)提供了一种顺序访问一个类中的一系列数据的方式。
中介者模式(Mediator Pattern)定义了如何用一个对象简化对象之间的通信,使对象之间不必相互了解。
备忘录模式(Memento Pattern)定义了如何保存一个类实例的内容一边以后能恢复它。
观察者模式(Observer Pattern)定义了一种把改动通知给多个对象的方式。
状态模式(State Pattern)允许一个对象在其内部状态改变时修改它的行为。
策略模式(Strategy Pattern)将算法封装到类里。
莫板方法模式(Template Method Pattern)提供了算法的一个抽象定义。
访问者模式(Visitor Pattern)在不改变类的前提下,为一个类添加多种操作。
 
 
Chapter 2 Factory Pattern
 
Simple Factory Pattern
Question:什么时候可以使用类工厂?使用类工厂有无类似的结构框架?使用该工厂还有什么其它问题吗?
在需要向系统添加一些新类型的时候,最明智的做法就是先利用多态为这些新类型创建一个公共接口,而后就可以通过多态调用新的类。然而这仍然存在一个问题,如果用与创建对象的那部分代码分散在整个程序里(如再某种条件下创建A,当条件更改时创建B),你仍然需要找出所有与类型相关的代码。类工厂就为将创建一个类的代码集中到一块提供了实现。这使得系统的其余部分的代码与新加入的那些特定类型相分离。最为直接的例子就是向工厂方法传入具体的Type信息返回指定的实例:
public class ClassFactory
{
 public object GetClass(Type ){ //返回Type对应的类型实例}
}
如果我们不使用类工厂,创建对象时,我们可能面对if-else语句:
if( type = type_A){ ClassA obj = new ClassA();}else{ ….}
 
Polymorphic Factories
在《设计模式》中强调Factory Method是为了使不同类型的工厂可以由基本类型的工厂派生出来。
public interface IMyInterface{ void DoSomething();}
public abstract class MyFactory
{
 protected abstract IMyInterface Create();
 static Hashtable factories = new Hashtable();
public static void AddFactory
(string id, MyFactory f){factories. Add(id ,f);}
public static IMyInterface CreateClass(string id)
{
   return (MyFactory)factories.Key[id].Create();
}
}
public class A : IMyInterface
{ public void DoSomething(){}}
class FactoryA :MyFactory
{
protected IMyInterface Create(){return new A();}
public FactoryA()
{
 if(! MyFactory.factories.ContainsKey[“FactoryA”])
{ MyFactory.AddFactory(“FactoryA”,new FactoryA());}
}
}
将新类完全中同其它的代码分离开,这便是多态工厂的基本思想。当你想向系统添加新类,首先要做的就是派生出一个子类工厂,该子类工厂的create方法返回实现了IMyInterface的对象。而后将该新类工厂添加到factories表中。
 
Factory Method Pattern
该模式不是用一个专门的类来决定实例化哪一个子类。相反,超类把这种决定延迟到每个子类。这种模式实际上没有决策略点,即没有直接选择一个子类实例化的决策。按这种模式编写的程序定义了一个抽象类,它去定义Factory Method,但这工厂方法由其子类覆盖。
public abstract class Factory { public abstract ReturnType GetClass(); }
public abstract class FactoryA
{ public override ReturnType GetClass(){ return new ReturnType_A();}}
对这中模式我持有怀疑的态度,这也能算时工厂模式吗?对于两个超类,其派生类之间的关系若是一对多,则可以用上面的代码来表示。
 
Abstract Factory Pattern
当要返回一些列相关类中的某一个,而每个类都能根据需要返回不同的对象时,可以使用这种模式。在某些情况下,它实际上为一组类返回了一个工厂。
基本思想:在创建工厂对象的地方,由你来决定如何使用该工厂对象创建那些对象。例:
    //创建一个通用的游戏环境,而且还向要支持不同类型的游戏
    public interface IObstacle { void Action();}
public interface IPlayer { void InteractWith(IObstacle o);}
    public class Kitty :IPlayer { public void InteractWith(IObstacle o){ o.Action(); }}
    public class Puzzle :IObstacle{ public void Action(){Console.WriteLine("Puzzle");}}
 
    //the abstract Factory
    public interface IGameElementFactory
    {
        IPlayer MakePlayer();
        IObstacle MakeObstacle();
    }
    //concrete factories
    public class KittiesAndPuzzles:IGameElementFactory
    {
        public IPlayer MakePlayer() {    return new Kitty();   }
        public IObstacle MakeObstacle()    {     return new Puzzle();      }  
 }
    public class GameEnvironment
    {
        IGameElementFactory gef;
        IPlayer p;
        IObstacle ob;
        public GameEnvironment(IGameElementFactory factory)
        {
            gef = factory;
            p = factory.MakePlayer();
            ob = factory.MakeObstacle();
        }
        public void Play() { p.InteractWith(ob); }
    }
}
抽象工厂IGameFactory将工厂对象的创建交给了实现该接口的具体类,如KittiesAndPuzzles类。而GameEnvironment则封装了工厂对象的调用。
抽象工厂和一般的接口实现又有什么区别呢?什么情况下可以使用抽象工厂呢?首先你的注意到IPlayer、和IObserver接口存在的意义。实现这两个接口的类才是实际上用到的类,它们之间的关系才是所有代码的重点。例中Kitty、Puzzle类就是你开发的游戏环境中Play()操作的执行者和实现者。它们是一个系统的组成部分、是整体的工件,为此抽象出IGameFactory接口。该接口将Kitty、Puzzle这样的相关类对象的创建集中到一起。而实现该接口的类便是具体的关系组合,你可以通过向这些类传入相关参数构造Kitty、Puzzle对象,即Concrete Factory。
抽象工厂模式也可以用抽象类来实现:
public abstract class GameFactory
{
IPlayer p;
        IObstacle ob;
public abstract IPlayer MakePlayer();
public abstract IObserver MakeObserver();
public GameFactory(){ p= MakePlayer(); ob = MakeObserver();}
public void Play(){p.InteractWith( ob); }
}
你可以通过派生出具体的PersonAndGame类具体化该抽象类:
public class PersonAndGame :GameFactory
{
 public IPlay MakePlayer(){ return new Kitty();}
 public IObserver MakeObserver(){return new Puzzly(); }
 }
这里GameFactory也可以称为抽象工厂。
抽象工厂的一个主要目的是它能隔离要生成的具体类。这些类的实际类名隐藏在工厂里,在客户端根本不需要知道,如上面的kitty、Puzzly类。
 
 
Chapter 3 Singleton Pattern
Singleton 提供一种方法使得某一特定类型存在一个,并且只能是一个对象。它实际上可以认为是对象池的一个特例。它可以提供一个全局的存取点,以及注册、查找功能。注册和查找是相关联的。Singleton Pattern的关键是防止用户以其它任何方式创建对象,而只能用你所提供的方式。所有的构造函数必须被声明为私有的,而且必须至少声明一个构造函数。
public static class Singleton
{
 static Singleton()
{//静态类只能由私有的静态构造函数,因为静态类不能被实例化}
 public static ArrayList arrayList;
 public static void Registry( IMyClass obj){ arrayList.Add(obj)}
 public static bool LookUp(IMyClass obj){ … }
}
我测试了类的静态构造函数,它不但允许你实例化对象,同时更让你能够在调用静态方法做些初始化工作。但由于静态方法必须是无参数的,因此你也就不能重载静态构造函数,也就是说,静态构造函数只能存在一个。
单件模式样例:
    public class Singleton
    {
        static bool isRun = false;
        Singleton() {}
        public static Singleton getSingleton()
        { if (!isRun) { return new Singleton(); } else { return null; } }
}
单件模式应用程序:
public static class SingletonApp
    {
        static Mutex m_Mutex;
        public static void Run(Form mainForm)
        {
            bool first = IsFirstInstance();
            if (first)
            { Application.ApplicationExit += OnExit; Application.Run(mainForm);}
        }
        static bool IsFirstInstance()
        {
            Assembly assembly = Assembly.GetEntryAssembly();
            string name = assembly.FullName;
            m_Mutex = new Mutex(false, name);
            bool owned = false;
            owned = m_Mutex.WaitOne(TimeSpan.Zero, false);
            return owned;
        }
        static void OnExit(object sender, EventArgs args)
        { m_Mutex.ReleaseMutex();   m_Mutex.Close(); }
        //Other overloaded versions of Run( )
    }
当然你也可以向上面代码添加修饰,如异常管理等。
Singleton模式并没有限制只能创建一个对象,也允许你创建固定数量的对象。对于固定数量的对象,你必须处理对象池的对象如何共享这种问题。
对象池管理机制下的单件模式样例:
    public class PoolManager
    {
        static class PoolItem
        {
           public bool inUse = false;   public object item;
           public PoolItem(object item) { this.item = item; }
        }
        ArrayList items = new ArrayList();
        public PoolManager(object item) { items.Add(new PoolItem(item)); }
        public object Get()
        {
            for (int i = 0; i < items.Count; i++)
            { PoolItem pitem = (PoolItem)items[i];
               if (pitem.inUse == false) { pitem.inUse = true; return pitem.item; }
}
return null;
        }
        public void Release(object item)
        {
            for (int i = 0; i < items.Count; i++)
            {   PoolItem pitem = (PoolItem)items[i];
                if (item == pitem.item) { pitem.inUse = false; return; }
            }
        }
 }
在该对象池管理下的单件模式:
    public class PoolSingleton
    {
        static PoolManager pool = new PoolManager();
        public static void AddObject(int number)
       {
            for (int i = 0; i < number; i++) { pool.Add(new PoolSingleton()); }
        }
        public static PoolSingleton GetObject() { return (PoolSingleton )pool.Get(); }
        public static void ReleaseObject(PoolSingleton o) { pool.Release(o); }
 }
这里你只能通过静态方法AddObject创建新的对象,只能通过GetObject方法从对象池中取得单件对象的一个引用。
 
Chapter 4 Builder Pattern
Builder模式:你可以用同样的步骤创建不同的对象体。该模式主要是为了将对象的构造与它的“表示”分离开来,这样对象就可以有多个不同的”表示”。它同Abstract Factory模式的主要区别在于该模式遵循一定的步骤一步步创建对象。
该模式具有以下几个效果:
1、                生成器允许读者改变产品的内部表示,同时也隐藏了产生如何组装的细节;
2、                每个特定的生成器都独立于其它的生成器,同时独立于程序的其它部分,这一点提高了对象的模块性,并使添加其它的生成器变得相对简单。
3、                由于每个生成器是根据数据一步一步构建最终结果,所以能精确地控制生成器的每个结果。
生成器样例:
    public abstract class MyClass{ public abstract void DoSomething(); }
    public abstract class Builder { public abstract void Build(); }//开始构建
    public class A :MyClass{}
public class BuilderA
{A a; public override void Build(){//更具a对象组合处Builder对象}}
    //下面类是:操作、显示MyClass的派生类的控制类。
    public class Director
    {
        Builder b;
        public Director(Builder b){ this.b = b; }
        public returnType ShowOrOperate(){ b.Build(); return returnType_object; }
}
你只要向Director类构造函数传入Builder类或其派生类对象就可以显示或说表示其欲表示的行为特征。Builder类也可以理解为MyClass类的表示类,一般的MyClass类的子类对象有其相对应的Builder类的子类来表示其行为特征。
都说Abstract Factory模式同该模式很相似,而这相似表现在那呢?下面看一个类似定义的Abstract Factory模式程序:
    public abstract class MyClass{ public abstract void DoSomething(); }
    public abstract class Factory { public abstract MyClass GetObject();}
    public class A :MyClass{}
    public class FactoryA { public override MyClass GetObject() { return new A(); } }
    public class WorkEnvironment
    {
        Factory f; MyClass a;
        public WorkEnvironment(Factory f) { f = this.f; a = f.GetObject(); }
        //对a对象的其他操作
 }
从这段代码从看,我觉得二者的区别在于最终返回的对象不同,生成器模式要求操作、显示的对象是Builder类的派生类;而抽象工厂模式则是为了获得MyClass派生类对象进而对其进行操作,而其FactoryA类在这仅仅是作为一种媒介。
 
Chapter 5 Prototype Pattern
该模式通过某个原型的实例来创建对象。当程序中确定了所需要的同要类,但需要将具体类延迟到运行时才能确定时,可以使用这种模式。它同Builder模式一样都由某些类确定组成最终类的部件或细节。然而ProtoType模式中目标类的构建是通过克隆一个或多个原型类,然后按预期的行为更改或补充被克隆类的细节而实现的。
    //对一个数组的克隆
    public class MyData : ICloneable
    {
        public MyData(ArrayList a) { array = a; }
        protected ArrayList array;
        public object Clone()
        {
            ArrayList a = new ArrayList();
            for (int i = 0; i < array.Count; i++)   { a[i] = array[i]; }
            MyData newData = new MyData(a);    return newData;
        }
    }
这里我们返回的是object对象,使用前必须转换为MyData类型对象。克隆该类型对象同数组对象的复制不同之处在于复制的是数组对象的引用指针,而这里则是拷贝数组元素。 如:
ArrayList a = array;
则两个数组具有相同的引用,无论对a或array对象的任何操作同样影响另外一个对象。
    而这里克隆的仅仅是底层数据的不同,数据a同array是两个不同的对象,具有不同的引用指针。只有当你修改了数组对象所引用的底层数据才会影响到另外一个数组。而对数组本身的Sort这类的方法则是互补影响的。
    在需要创建大量的类时,或者在创建类后又要修改时,都可以使用原型模式。只要所有的类都具有相同的接口,它们就都能完成不同的操作。
Prototype模式的另一个较好的例子就是当你想对一个表对象实现类似:Select Col, col2, col3 from table order by ?
的功能。按col1或col2、col3显示的结果都不相同。
当具体类的个数较多时,如对一个表按不同排序显示,你可以考虑使用原型管理器,下面就是一个原型管理器样例:
    public class Table { public Table Clone() { return new Table(); } }
    public class SortByA { Table table; //克隆对象}
    public class SortByB { Table table; //克隆对象}
    public class Prototype
    {
        SortByA a ;    SortByB b;
        public void ShowData(SortByA a) { this.a = a; //显示按A排序的结果 }
        public void ShowData(SortByB b) { this.b = b;  //显示按B排序的结果 }
}
你将原型注册到原型管理其中,而后重载ShowData方法实现具体类操作。
克隆:可以通过克隆一个类,而对数据对象操作。也可以做深层克隆,即克隆类的结构、数据等,而不仅仅是克隆类对象的结构、和指向数据的指针。
在什么时候克隆更好呢?如果每次派生一个特别相似的新类时,都要重新Clone方法,这不太令人满意。更好的方案是取消让每个类都取实现Clone方法的IConeable接口,把这个处理过程改为让每个接受类克隆发送类中的数据。
  
.MODEL SMALL .STACK 100H ; 端口定义 CONTROL_PORT EQU 8006H ; 8255A控制端口 PORT_B EQU 8002H ; PB口(LED输出) PORT_C EQU 8004H ; PC口(开关输入) ; 控制字:方式0,PB口输出,PC口输入 CONTROL_WORD EQU 82H ; 10000010B ; 延时参数(控制循环速度,可通过调整该值修改速度) DELAY_COUNT EQU 0F000H ; 速度参数,值越大速度越慢 .DATA ; 花型初始值 PATTERN1 DB 01H ; 花型1:00000001 PATTERN2 DB 80H ; 花型2:10000000 PATTERN3 DB 01H ; 花型3:初始为00000001 PATTERN4A DB 55H ; 花型4:01010101 PATTERN4B DB 0AAH ; 花型4:10101010 .CODE START: ; 初始化8255A MOV AL, CONTROL_WORD OUT CONTROL_PORT, AL ; 主循环:检测开关状态,执行对应功能 MAIN_LOOP: IN AL, PORT_C ; 读取PC口(开关SW0~SW3状态) AND AL, 0FH ; 只保留低4位(SW0~SW3) CMP AL, 01H ; 检测SW0是否按下(启动) JE START_SYSTEM JMP MAIN_LOOP ; 启动系统后,进入功能选择循环 START_SYSTEM: SYSTEM_LOOP: IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 检测SW1是否按下(暂停) JE MAIN_LOOP CMP AL, 04H ; 检测SW2是否按下(花型1) JE PATTERN1_LOOP CMP AL, 08H ; 检测SW3是否按下(花型2) JE PATTERN2_LOOP CMP AL, 0CH ; 检测SW2+SW3是否按下(花型3) JE PATTERN3_LOOP CMP AL, 00H ; 无开关按下时,默认花型4 JE PATTERN4_LOOP JMP SYSTEM_LOOP ; 花型1:00000001 左循环 PATTERN1_LOOP: MOV AL, [PATTERN1] OUT PORT_B, AL ; 输出到LED CALL DELAY ; 延时 ROL AL, 1 ; 左循环移位 MOV [PATTERN1], AL IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 检测是否暂停 JE MAIN_LOOP CMP AL, 04H ; 检测是否切换花型 JNE PATTERN1_LOOP JMP SYSTEM_LOOP ; 花型2:10000000 右循环 PATTERN2_LOOP: MOV AL, [PATTERN2] OUT PORT_B, AL ; 输出到LED CALL DELAY ; 延时 ROR AL, 1 ; 右循环移位 MOV [PATTERN2], AL IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 检测是否暂停 JE MAIN_LOOP CMP AL, 08H ; 检测是否切换花型 JNE PATTERN2_LOOP JMP SYSTEM_LOOP ; 花型3:00000001→00000011→…→11111111,全亮后闪烁一次循环 PATTERN3_LOOP: MOV BL, [PATTERN3] P3_INCR: MOV AL, BL OUT PORT_B, AL ; 输出到LED CALL DELAY ; 延时 CMP BL, 0FFH ; 判断是否全亮 JE P3_FLASH SHL BL, 1 ; 左移一位,递增亮灯 OR BL, 01H IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 检测是否暂停 JE MAIN_LOOP CMP AL, 0CH ; 检测是否切换花型 JNE P3_INCR JMP SYSTEM_LOOP P3_FLASH: MOV AL, 00H OUT PORT_B, AL ; 全灭 CALL DELAY ; 延时 MOV AL, 0FFH OUT PORT_B, AL ; 全亮 CALL DELAY ; 延时 MOV BL, 01H ; 重置初始值 IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 检测是否暂停 JE MAIN_LOOP CMP AL, 0CH ; 检测是否切换花型 JNE PATTERN3_LOOP JMP SYSTEM_LOOP ; 花型4:0101010110101010交替闪烁 PATTERN4_LOOP: MOV AL, [PATTERN4A] OUT PORT_B, AL ; 输出01010101 CALL DELAY ; 延时 MOV AL, [PATTERN4B] OUT PORT_B, AL ; 输出10101010 CALL DELAY ; 延时 IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 检测是否暂停 JE MAIN_LOOP CMP AL, 00H ; 检测是否切换花型 JNE PATTERN4_LOOP JMP SYSTEM_LOOP ; 延时子程序(通过循环计数实现速度控制) DELAY: MOV CX, DELAY_COUNT DELAY_LOOP: NOP NOP LOOP DELAY_LOOP RET END START 纠正里面的错误
最新发布
11-04
<think>我们正在处理一段8086汇编代码,这段代码用于控制8255A芯片,通过开关输入控制LED显示不同的花型。 根据代码,我们注意到可能存在一些错误,我们将逐一检查并修正。 主要问题分析: 1. 在花型3的循环中,当全亮后闪烁一次循环,但代码中在闪烁后重置BL为01H,然后继续循环。然而,在闪烁后没有立即跳转回递增部分,而是继续执行了检测开关的代码。此外,在闪烁后应该重置PATTERN3的值,否则下次进入时会从全亮开始。 2. 在花型4循环中,检测切换花型的条件为CMP AL, 00H(无开关按下),但实际上,当有开关按下时(如SW1暂停或SW2/SW3切换),应该退出花型4循环。但是,代码中检测到非00H就会跳出循环,这可能导致在按下其他开关(比如暂停)时,无法正确暂停。 3. 在花型1和花型2循环中,检测切换花型的条件可能不正确。例如,在花型1循环中,检测SW2(04H)按下则跳转到SYSTEM_LOOP,但实际上,如果SW2按下,应该切换到花型1(当前已经是花型1)?这里逻辑需要调整:应该是检测到其他花型的开关才切换,或者暂停开关才暂停。但代码中,暂停键(SW1)已经单独检测。 4. 初始花型数据在循环中被修改,但再次进入该花型时,初始值已经改变。例如,PATTERN1在循环中被ROL修改,当退出该花型再进入时,初始值不是01H,而是上次退出时的值。这可能导致花型不是从头开始。我们需要在进入花型循环前初始化花型数据?或者每次进入时重置?代码中没有重置。 具体修正步骤: 问题1:花型3的数据重置问题。 在P3_FLASH后,将BL重置为01H,然后存储到[PATTERN3]中,这样下次进入花型3时,会从01H开始。 修改:在P3_FLASH部分,闪烁完成后,将BL重置为01H,并保存到PATTERN3。 问题2:花型4的退出条件。 当前花型4循环中,检测到非00H(有开关按下)就退出循环。但实际上,我们只关心暂停键(SW1,02H)和切换花型的键(例如SW2/SW3等)。所以应该改为:检测到暂停键则跳转到MAIN_LOOP(暂停),检测到其他花型键则跳转到SYSTEM_LOOP进行切换。但是,当前代码中,花型4循环中只检测了暂停键(02H)和是否不为00H(即任意键按下)。这样,当有任意开关按下(包括暂停和其他花型)都会退出花型4循环。然而,代码中退出后是跳转到哪里?它检测到暂停就跳转到MAIN_LOOP(暂停),否则如果检测到不是00H(即非无键按下)就跳转回花型4循环?不,代码中: CMP AL, 02H JE MAIN_LOOP CMP AL, 00H JNE PATTERN4_LOOP ; 如果不是00H(有键按下)且不是暂停键,则继续PATTERN4_LOOP?这里逻辑错误。 实际上,当有键按下但不是暂停键时,我们希望继续执行花型4直到有切换花型的键(比如SW2/SW3)按下?但代码中并没有检测具体的切换键。按照设计,花型4是默认花型,当没有键按下时执行。所以,当有键按下时,我们需要退出花型4循环,回到SYSTEM_LOOP去判断按下的键。 因此,我们应该这样:在花型4循环中,如果检测到暂停键(02H)则跳转到MAIN_LOOP(暂停),否则如果有任意键按下(非00H)就跳转到SYSTEM_LOOP(因为SYSTEM_LOOP会根据按键切换到其他花型或者暂停)。但是注意,花型4循环中,每次交替闪烁两次后进行检测。所以,修改如下: 在花型4循环中,检测到暂停键(02H)则跳转到MAIN_LOOP,否则只要不是00H(有键),就跳转到SYSTEM_LOOP(因为可能是其他花型键)。 问题3:花型1和花型2的切换逻辑。 在花型1循环中,检测到SW2(04H)按下则跳转到SYSTEM_LOOP,但此时SW2按下表示要切换到花型1(当前已经是花型1)?这显然不合理。实际上,我们在主循环中已经根据按键进入对应的花型。在花型执行循环中,我们只应该检测两种事件:暂停(SW1)和切换花型(其他花型键)。所以,在花型1循环中,我们应该检测: - 暂停键(02H):跳转到MAIN_LOOP(暂停) - 其他花型键(如SW3(08H)或SW2+SW3(0CH))则退出当前花型循环,回到SYSTEM_LOOP(由SYSTEM_LOOP重新判断) 但代码中,花型1循环只检测了SW2(04H)作为切换花型的条件。这样,当按下SW3(08H)时,它不会退出花型1循环,而是继续执行。因此,我们需要修改为:检测到除了暂停键以外的其他键(非02H)且是有效的花型切换键(即我们在SYSTEM_LOOP中定义的键)就退出,但为了简单,我们可以检测任何非暂停键的按键都退出到SYSTEM_LOOP,由SYSTEM_LOOP重新判断。 然而,为了避免误操作(比如抖动),我们也可以只检测我们关心的键。但代码中已经将按键状态与特定值比较,所以我们可以这样改: 花型1循环中: ... 执行花型 IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 暂停键 JE MAIN_LOOP ; 如果按键状态不是04H(即SW2按下,但当前已经是花型1,所以不应该再切换到自己?),所以我们需要检测其他花型键。 ; 实际上,我们可以检测是否有其他花型键按下(即SW3(08H)或SW2+SW3(0CH)),但是这样需要多个比较。 ; 或者,我们可以认为只要不是暂停键且按键状态有变化(非0)就退出到SYSTEM_LOOP。因为SYSTEM_LOOP会重新解释按键。 CMP AL, 00H ; 无按键,则继续当前花型 JE PATTERN1_LOOP ; 注意,这里应该是继续循环,而不是跳转到SYSTEM_LOOP ; 有按键(非暂停且非无键)则退出到SYSTEM_LOOP JMP SYSTEM_LOOP 但是,原代码中是这样处理的: CMP AL, 02H ; 暂停 JE MAIN_LOOP CMP AL, 04H ; 检测是否切换花型(这里指SW2,即花型1的触发键,但当前就在花型1,所以按SW2不应该切换?但实际上,用户可能想切换到其他花型而按了其他键,所以这里只检测SW2是不合理的) JNE PATTERN1_LOOP ; 如果不是04H(SW2),则继续花型1循环 JMP SYSTEM_LOOP ; 如果是04H(SW2),则跳转到SYSTEM_LOOP(但按SW2不应该退出花型1,因为当前就是花型1) 所以,原逻辑中,在花型1循环中,只有按下SW2才会退出到SYSTEM_LOOP(这不合理,因为按SW2可能用户想重新开始花型1?),而按下其他键(包括SW3)则不会退出。 修正思路:在花型循环中,我们只检测暂停键(02H)和退出键(即其他花型键)。由于花型键有多个,我们可以这样:除了暂停键外,其他按键都视为切换请求,退出到SYSTEM_LOOP。所以: IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 暂停 JE MAIN_LOOP CMP AL, 00H ; 没有按键,继续当前花型 JE PATTERN1_LOOP ; 有按键(非暂停且非无键)则退出到SYSTEM_LOOP(让主循环去处理) JMP SYSTEM_LOOP 这样,在花型1循环中,如果有按键(除了暂停键)就退出到SYSTEM_LOOP,暂停键则暂停。 问题4:花型数据初始化问题。 当前代码中,花型数据(PATTERN1, PATTERN2等)在数据段定义,但在循环中被修改。当退出一个花型再进入时,数据是上次退出时的状态。为了让每次进入花型都从头开始,我们应该在进入花型循环之前重置花型数据。 我们可以这样做: 在进入具体花型循环前,将对应的花型数据重置为初始值。 例如,进入花型1循环前,重置PATTERN1为01H。 修改: 在每个花型的入口处,重置对应的花型数据为初始值。 但是,这样会有一个问题:如果用户想暂停后继续,那么重置会导致从头开始,而不是接着上次暂停的状态。但原设计并没有要求保存状态,所以重置也可接受。 或者,我们可以不重置,这样暂停后继续会接着上次的状态。但这样,当用户切换花型再切换回来时,也是接着上次的状态。而设计要求可能是每次进入花型都应该是初始状态。 根据问题描述,我们按照每次进入花型都从头开始(重置)来修改。 具体修改: 1. 花型1循环前重置: PATTERN1_LOOP: MOV BYTE PTR [PATTERN1], 01H ; 重置为初始值 ... 原代码 2. 花型2循环前重置: PATTERN2_LOOP: MOV BYTE PTR [PATTERN2], 80H ; 重置为10000000 3. 花型3循环前重置: PATTERN3_LOOP: MOV BYTE PTR [PATTERN3], 01H ; 重置为00000001 4. 花型4不需要重置,因为它的两个值都是固定的。 但是,注意花型3中使用了BL寄存器,在循环中BL是从[PATTERN3]取出的,所以重置PATTERN3后,在循环中应该重新从PATTERN3取一次。不过我们在循环开始前重置了PATTERN3,然后进入P3_INCR前将[PATTERN3]赋值给BL,所以没问题。 另外,花型3的闪烁部分(P3_FLASH)执行完后,将BL重置为01H,并且存储到[PATTERN3]中,这样下次进入花型3时,初始值就是01H。但是,如果我们已经在循环入口重置了PATTERN3,那么P3_FLASH中重置PATTERN3的步骤可以省略?不过,为了在花型3循环内部能够正确循环(即一次递增到全亮,闪烁后重新开始递增),我们仍然需要重置。所以,P3_FLASH中重置BL并保存到[PATTERN3]是必要的。 但是,如果我们在花型3循环入口重置了[PATTERN3]为01H,那么进入循环后,BL被赋值为01H,然后进入递增。当完成一次循环(到全亮闪烁)后,P3_FLASH中又将[PATTERN3]重置为01H(通过MOV [PATTERN3], BL? 注意,在P3_FLASH中,我们是将BL重置为01H,然后并没有保存到[PATTERN3]?代码中是: MOV BL, 01H ; 重置初始值 然后后面没有存储到PATTERN3。 所以,这里需要将重置的值存入PATTERN3。修改: P3_FLASH: ... MOV BL, 01H MOV [PATTERN3], BL ; 保存重置后的值,这样下次循环(如果退出再进入)会从01H开始,但如果我们已经在循环入口重置,这一步也可以不要?为了保持循环内部正确,我们保存。 但是,考虑到循环内部:在P3_FLASH之后,会跳转回PATTERN3_LOOP吗?代码中是: JNE PATTERN3_LOOP ; 如果按键不是暂停且不是0CH,则继续PATTERN3_LOOP(即从PATTERN3_LOOP开始,重新取[PATTERN3]给BL,所以需要[PATTERN3]是01H) 因此,在P3_FLASH中,我们需要更新[PATTERN3]为01H。所以,在P3_FLASH中,执行: MOV [PATTERN3], 01H ; 这样,即使退出循环再进入,也是01H。同时,在循环内部,下一次递增也会从01H开始。 但是,如果我们已经在PATTERN3_LOOP入口重置了[PATTERN3]为01H,那么P3_FLASH中更新[PATTERN3]为01H也是可以的,只是重复了。所以,我们可以选择在循环入口重置一次(保证每次进入花型3都是初始状态),而在循环内部重置(保证每次循环都是初始状态)。 为了清晰,我们可以在循环入口重置一次,然后在循环内部(P3_FLASH)中,重置BL为01H(用于内部循环),但不更新[PATTERN3](因为退出循环后,下次进入会由入口重置)。这样,内部循环重置BL为01H,然后继续递增,而[PATTERN3]在退出循环后才会被重置为01H(下次进入时)。 但是,在P3_FLASH后,我们紧接着会回到PATTERN3_LOOP吗?不,代码是: P3_FLASH: ... 闪烁 MOV BL, 01H ; 重置BL,用于下一次递增 ; 然后下面是检测按键 IN AL, PORT_C AND AL, 0FH CMP AL, 02H JE MAIN_LOOP CMP AL, 0CH JNE PATTERN3_LOOP ; 注意:这里跳转到PATTERN3_LOOP,而不是P3_INCR JMP SYSTEM_LOOP PATTERN3_LOOP: MOV BL, [PATTERN3] ; 这里会重新从[PATTERN3]取值,而[PATTERN3]在入口被重置为01H,但在循环过程中并没有更新(除了在入口重置,循环内部没有更新过[PATTERN3]) 问题:在循环内部(递增过程中),[PATTERN3]的值并没有更新,所以当循环到P3_FLASH后,跳回PATTERN3_LOOP,会重新从[PATTERN3]取初始值(01H),而我们在P3_FLASH中重置了BL=01H,但并没有存入[PATTERN3],所以[PATTERN3]还是01H(因为入口重置了,并且循环中没修改)?所以这里没有问题。但是,在递增过程中,[PATTERN3]的值并没有更新为当前值,所以当切换到其他花型再切换回来时,会从01H开始(因为入口重置了)。 因此,为了在花型3循环内部能够正确循环(即闪烁后重新从01H开始递增),我们在P3_FLASH中重置BL=01H,然后跳回PATTERN3_LOOP时,会重新从[PATTERN3]中取01H(因为[PATTERN3]在入口已经重置为01H,而且在循环内部没有改变),所以BL还是01H。 所以,在花型3循环中,我们不需要在循环内部更新[PATTERN3]。但是,在花型1和花型2中,我们在每次移位后都更新了[PATTERN1]和[PATTERN2],这样当切换到其他花型再切换回来时,会接着上次移位后的值继续。而如果我们在花型1和花型2的入口重置了初始值,那么进入时会重置,就不会接着上次了。 综上所述,我们进行如下修正: 修正1:在每个花型循环入口重置对应的花型数据为初始值。 修正2:修改花型4循环中的按键检测逻辑:当有按键(非00H)且不是暂停键(02H)时,退出到SYSTEM_LOOP(即视为切换花型请求)。注意:暂停键单独处理。 修正3:修改花型1、花型2、花型3循环中的按键检测逻辑:只要检测到按键(非00H),如果不是暂停键,就退出到SYSTEM_LOOP(让主循环重新判断)。如果是暂停键,则跳转到暂停。 修正4:在花型3的P3_FLASH部分,重置BL为01H(用于内部循环),并且不要忘记在PATTERN3_LOOP入口重置[PATTERN3]为01H(这样切换回来时是初始状态)。 另外,花型3循环中,在P3_FLASH后,跳转回PATTERN3_LOOP,而PATTERN3_LOOP会重新从[PATTERN3]取BL,所以我们在入口重置[PATTERN3]为01H,就可以保证每次进入花型3(包括内部循环回到PATTERN3_LOOP)都从01H开始。 但是,注意:在递增过程中(P3_INCR),[PATTERN3]并没有更新,所以当我们在递增过程中切换到其他花型,下次再进入花型3时,由于入口重置了[PATTERN3]为01H,所以会从头开始。这样符合要求。 具体代码修改: 1. 在花型1循环入口重置PATTERN1PATTERN1_LOOP: MOV BYTE PTR [PATTERN1], 01H ; 重置 ... 原代码 2. 在花型2循环入口重置PATTERN2: PATTERN2_LOOP: MOV BYTE PTR [PATTERN2], 80H ; 重置 3. 在花型3循环入口重置PATTERN3: PATTERN3_LOOP: MOV BYTE PTR [PATTERN3], 01H ; 重置 ... 原代码 4. 修改花型1循环中的按键检测部分(删除原有的检测SW2的代码,改为检测任意按键(非暂停)): IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 检测是否暂停 JE MAIN_LOOP CMP AL, 00H ; 无按键,继续 JE PATTERN1_LOOP ; 继续循环 JMP SYSTEM_LOOP ; 有按键(非暂停)则退出到主循环 注意:原代码中在CMP AL,02H之后,有CMP AL,04H,我们去掉这部分,直接用上面的逻辑。 5. 同样修改花型2循环中的按键检测部分: IN AL, PORT_C AND AL, 0FH CMP AL, 02H JE MAIN_LOOP CMP AL, 00H JE PATTERN2_LOOP JMP SYSTEM_LOOP 6. 修改花型3循环中的按键检测部分(有两处:递增部分和闪烁部分): 在递增部分(P3_INCR): IN AL, PORT_C AND AL, 0FH CMP AL, 02H JE MAIN_LOOP CMP AL, 0CH ; 这里保留检测0CH(因为花型3是SW2+SW3触发的,但我们在SYSTEM_LOOP中检测花型3是0CH,所以这里可以保留?但为了统一,我们也可以改为任意按键(除暂停)退出) ; 但是,原设计中,在花型3循环中,如果按下SW2+SW3(0CH)表示继续花型3,所以不应该退出。所以,这里我们只检测暂停键,其他键不做处理?不,我们要求按其他花型键可以切换。 ; 所以,我们可以采用和花型1/2一样的处理:非暂停键且非无键则退出。 ; 修改: CMP AL, 00H JE P3_INCR ; 这里原代码是JNE P3_INCR,逻辑不对,应该改为:无键则继续递增,有键则判断? ; 我们重新组织: ; 检测到暂停键,则跳MAIN_LOOP ; 否则,检测到有键(非00H)则退出到SYSTEM_LOOP(因为可能是其他花型键) ; 所以: ; CMP AL, 02H ; JE MAIN_LOOP ; CMP AL, 00H ; JNE SYSTEM_LOOP ; 有键(非暂停)则退出到系统循环 ; ; 否则无键,继续递增 ; 继续执行(SHL BL,1等) 注意:在P3_INCR中,我们是在移位前检测按键。所以修改如下(替换原IN AL之后的代码): IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 暂停键 JE MAIN_LOOP CMP AL, 00H ; 无键则继续递增 JNE SYSTEM_LOOP ; 有键(非暂停)则退出 ; 继续递增 CMP BL, 0FFH JE P3_FLASH SHL BL, 1 OR BL, 01H JMP P3_INCR ; 继续递增循环 但是,这样修改后,递增循环中,如果有按键(非暂停)就会退出到SYSTEM_LOOP,而不会继续本次递增后的显示。这样虽然可以,但可能会在按键后立即切换花型。 在闪烁部分(P3_FLASH): 同样,在闪烁后检测按键,我们修改为: IN AL, PORT_C AND AL, 0FH CMP AL, 02H JE MAIN_LOOP CMP AL, 00H JNE SYSTEM_LOOP ; 有键(非暂停)则退出 ; 无键,则继续循环(即执行下一次递增) JMP PATTERN3_LOOP ; 这里跳转到PATTERN3_LOOP(会重置[PATTERN3]为01H,然后取BL=01H,重新递增) 注意:在P3_FLASH中,我们已经重置了BL=01H(用于下一次递增),但并没有更新[PATTERN3](因为PATTERN3_LOOP入口会重置[PATTERN3]为01H,所以这里不更新也可以)。 7. 修改花型4循环中的按键检测部分: 原代码: IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 检测是否暂停 JE MAIN_LOOP CMP AL, 00H ; 检测是否切换花型 JNE PATTERN4_LOOP JMP SYSTEM_LOOP 修改为: IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 暂停 JE MAIN_LOOP CMP AL, 00H ; 无键,继续花型4 JE PATTERN4_LOOP ; 继续循环 JMP SYSTEM_LOOP ; 有键(非暂停)则退出 8. 注意:花型3的循环中,在P3_INCR部分,我们修改后,如果检测到按键(非暂停)会跳转到SYSTEM_LOOP,但此时BL的值并没有保存到[PATTERN3]。而下次进入花型3时,会在入口重置[PATTERN3]为01H,所以没有关系。 9. 另外,在花型3的P3_FLASH部分,我们跳转到PATTERN3_LOOP,这样会重新从[PATTERN3]取BL(01H)然后开始递增。这符合要求。 由于修改较多,我们给出修改后的完整代码。 注意:由于汇编代码较长,我们只给出修改后的部分,但为了清晰,尽量给出完整代码。 修改后的完整代码: </think>以下是纠正后的代码,解决了原程序中的逻辑错误、切换机制和花型初始化问题: ```assembly .MODEL SMALL .STACK 100H ; 端口定义 CONTROL_PORT EQU 8006H ; 8255A控制端口 PORT_B EQU 8002H ; PB口(LED输出) PORT_C EQU 8004H ; PC口(开关输入) ; 控制字:方式0,PB口输出,PC口输入 CONTROL_WORD EQU 82H ; 10000010B ; 延时参数 DELAY_COUNT EQU 0F000H ; 值越大速度越慢 .DATA ; 花型初始值 PATTERN1 DB 01H ; 花型1:00000001 PATTERN2 DB 80H ; 花型2:10000000 PATTERN3 DB 01H ; 花型3:初始为00000001 PATTERN4A DB 55H ; 花型4:01010101 PATTERN4B DB 0AAH ; 花型4:10101010 .CODE START: ; 初始化8255A MOV AX, @DATA MOV DS, AX ; 初始化数据段 MOV AL, CONTROL_WORD OUT CONTROL_PORT, AL ; 主循环:检测启动开关 MAIN_LOOP: IN AL, PORT_C AND AL, 0FH CMP AL, 01H ; 检测SW0启动 JE START_SYSTEM JMP MAIN_LOOP ; 启动后功能选择 START_SYSTEM: SYSTEM_LOOP: IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; SW1暂停 JE MAIN_LOOP CMP AL, 04H ; SW2花型1 JE PATTERN1_LOOP CMP AL, 08H ; SW3花型2 JE PATTERN2_LOOP CMP AL, 0CH ; SW2+SW3花型3 JE PATTERN3_LOOP CMP AL, 00H ; 无按键花型4 JE PATTERN4_LOOP JMP SYSTEM_LOOP ; 花型1:左循环 (修复1: 入口初始化) PATTERN1_LOOP: MOV [PATTERN1], 01H ; 每次进入重置初始值 PAT1_LOOP: MOV AL, [PATTERN1] OUT PORT_B, AL CALL DELAY ROL AL, 1 ; 左移位 MOV [PATTERN1], AL IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 暂停检测 JE MAIN_LOOP CMP AL, 00H ; 无按键继续本花型 (修复3) JE PAT1_LOOP JMP SYSTEM_LOOP ; 其他按键返回选择 ; 花型2:右循环 (修复1: 入口初始化) PATTERN2_LOOP: MOV [PATTERN2], 80H PAT2_LOOP: MOV AL, [PATTERN2] OUT PORT_B, AL CALL DELAY ROR AL, 1 ; 右移位 MOV [PATTERN2], AL IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 暂停 JE MAIN_LOOP CMP AL, 00H ; 无按键继续 (修复3) JE PAT2_LOOP JMP SYSTEM_LOOP ; 花型3:递增+闪烁 (修复1,2,4) PATTERN3_LOOP: MOV [PATTERN3], 01H ; 重置初始值 MOV BL, [PATTERN3] P3_INCR: MOV AL, BL OUT PORT_B, AL CALL DELAY CMP BL, 0FFH ; 是否全亮 JE P3_FLASH SHL BL, 1 ; 左移递增 OR BL, 01H ; 保证最低位为1 IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 暂停 JE MAIN_LOOP CMP AL, 00H ; 无按键继续递增 (修复4) JE P3_INCR ; 继续递增 JMP SYSTEM_LOOP ; 其他按键退出 P3_FLASH: MOV AL, 00H ; 全灭闪烁 OUT PORT_B, AL CALL DELAY MOV AL, 0FFH ; 全亮 OUT PORT_B, AL CALL DELAY IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 暂停 JE MAIN_LOOP CMP AL, 00H ; 无按键则重启花型 (修复2) JNE SYSTEM_LOOP ; 有按键退出 JMP PATTERN3_LOOP ; 无按键重启花型 ; 花型4:交替闪烁 (修复5) PATTERN4_LOOP: MOV AL, [PATTERN4A] OUT PORT_B, AL CALL DELAY MOV AL, [PATTERN4B] OUT PORT_B, AL CALL DELAY IN AL, PORT_C AND AL, 0FH CMP AL, 02H ; 暂停 JE MAIN_LOOP CMP AL, 00H ; 无按键继续 (修复3) JE PATTERN4_LOOP JMP SYSTEM_LOOP ; 有按键退出 ; 延时子程序 DELAY: MOV CX, DELAY_COUNT DELAY_LOOP: NOP NOP LOOP DELAY_LOOP RET END START ``` ### 错误修复说明: 1. **花型初始化问题** - 在每次进入花型循环前重置初始值(如`PATTERN1_LOOP`开头添加`MOV [PATTERN1], 01H`) - 确保每次执行花型都从头开始,避免残留上次状态 2. **花型3逻辑修复** - 修复递增逻辑:使用`SHL BL,1` + `OR BL,01H`正确实现1→3→7→15→...→255序列 - 闪烁后增加按键检测:无按键时跳回`PATTERN3_LOOP`重新开始,有按键则退出 3. **花型切换机制** - 所有花型循环内: - 检测到暂停键(02H) → 返回主暂停循环 - 检测到无按键(00H) → 继续当前花型 - 检测到其他按键 → 返回功能选择循环(SYSTEM_LOOP) 4. **数据段初始化** 添加`MOV AX, @DATA`和`MOV DS, AX`,确保数据段正确加载 5. **花型4优化** 移除冗余跳转,直接通过循环条件控制闪烁 ### 工作原理: 1. **8255A初始化** 设置控制字82H:A口未使用,B口输出,C口输入 2. **扫描逻辑** - SW0(01H):启动系统 - SW1(02H):暂停并返回待机 - SW2/SW3:选择对应花型 3. **花型实现** - 花型1:ROL左移实现LED左循环 - 花型2:ROR右移实现LED右循环 - 花型3:SHL+OR实现LED数量递增,全亮后闪烁 - 花型4:55H和AAH交替输出实现间隔闪烁 ### 使用建议: 1. 连接8个LED到PB0-PB7 2. 连接4位开关到PC0-PC3(SW0-SW3) 3. 调整DELAY_COUNT改变花型速度
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值