Please consider a donation to the Higher Intellect project. See https://preterhuman.net/donate.php or the Donate to Higher Intellect page for more info.

AN OBJECT-ORIENTED LOGIC SIMULATOR

From Higher Intellect Vintage Wiki
Jump to navigation Jump to search
_AN OBJECT-ORIENTED LOGIC SIMULATOR_
by Kenneth E. Ayers


[LISTINÇ ONE]

Object subclass: #LogicLab
  instanceVariableNames: 
    'devices signals switches clashes changed topPane analyzer breadboard 
                                               listSelector currentComponent '
  classVariableNames: ''
  poolDictionaries: 
    'FunctionKeys CharacterConstants ' !

!LogicLab class methods !
description
         "Answer a String describing the
         application and version."
      ^'LogicLab (Version 1.0 -- 06/26/88)'.!
new
        "Answer an initialized LogicLab application."
    | logicLab |
    logicLab := super new.
    logicLab initialize.
    ^logicLab.! !

!LogicLab methods !
addComponent: aComponent
           "Add aComponent to the circuit description.
         If there is an error, answer nil;  otherwise
         answer aComponent."
    | name |
       name := aComponent name.
    name size == 0
         ifTrue: [
           "
                User is installing -- get a name.
            "
         name := self getNewName.
          name isNil
            ifTrue: [^nil].
          aComponent name: name]
        ifFalse: [
           "
                A name has been supplied -- this implies
                that the component is being installed from
                a file.  Need to check for a clash with
                an existing name.
            "
          ((self componentNamed: name) isNil)
          ifFalse: [
                "
                        Had a name clash -- get a synonym
                        from the user and stash both of them
                        away in the clashes table.  Then
                        rename the component.
                    "
                 name := self getNewName.
                name isNil
Š               ifTrue: [^nil].
              clashes
                 at: aComponent name
                    put: name.
              aComponent name: name]].
    changed := true.
    aComponent isDevice
           ifTrue:  [devices add: aComponent]
     ifFalse: [
           aComponent isSignal
           ifTrue:  [signals add: aComponent]
             ifFalse: [
                switches add: aComponent.
                analyzer isNil
                   ifFalse: [analyzer addSwitch: aComponent]]].
    ^aComponent.!
allNames
      "Answer an array of all of the
         names of installed components."
    ^((self deviceNames), (self signalNames), (self switchNames)).!
analyzer: aModel
       "Set the LogicAnalyzer Application model
         to aModel."
       analyzer := aModel.!
breadboardList
         "Answer an array of strings according to the
         current list selector."
    listSelector isNil
           ifTrue: [listSelector := #listDevices].
      ^(self perform: listSelector).!
breadboardMenu
           "Private -- answer the menu that processes
         breadboard functions."
    MenuPosition := Cursor position.
    ^(Menu
          labels: ('Load\Save\Erase\List\',
                 'Install\Connect\Remove\Disconnect\',
               'Simulate\',
            'Quit') withCrs
      lines: #(4 8 9)
     selectors: #(load     save     erase   list
               install  connect  remove  disconnect
                run
                quit)).!
changed
     "Answer true if the circuit has changed."
    ^changed.!
changed: aBoolean
     "Set the circuit-changed flag to aBoolean."
    changed := aBoolean.!
close
      "Close the LogicLab breadboarding window."
    topPane dispatcher deactivateWindow closeWindow.!
closeIt
        "Close the breadboard application window."
Š    self close.!
componentNamed: aName
           "Answer the component (device, signal, or switch)
         whose name is aName.  If no component can be found
         answer nil."
    | realName |
    realName := aName.
      clashes isNil
           ifFalse: [
         (clashes includesKey: aName)
          ifTrue:  [realName := clashes at: aName]].
    devices do: [:aDevice|
         (aDevice name = realName)
           ifTrue: [^aDevice]].
      signals do: [:aSignal|
        (aSignal name = realName)
         ifTrue: [^aSignal]].
    switches do: [:aSwitch|
        (aSwitch name = realName)
          ifTrue: [^aSwitch]].
     ^nil.!
componentTypeMenu: selectorArray
           "Answer a user-selected action for a
         component type."
    ^((Menu
           labels: 'Device\Signal\Switch' withCrs
         lines: #()
           selectors: selectorArray) popUpAt: MenuPosition).!
connect
        "Make a user-specified connection."
    | from to |
    from := self getNode.
    from isNil
     ifTrue: [^nil].
    to := self getNode.
       to isNil
       ifTrue: [^nil].
    from connect: to.
       changed := true.
    currentComponent := from model.
      listSelector := #listComponentConnections.
    breadboard update.!
description
          "Answer a string with a description of the receiver."
    ^(self class description).!
deviceNames
         "Answer a collection of all of the
         names of installed devices."
    | list |
    list := OrderedCollection new: (devices size).
    devices  do: [:aDevice| list add: aDevice name].
      ^list.!
devices
        "Answer the list of installed devices."
       ^devices.!
Šdisconnect
           "Remove a user-specified connection."
    | node |
    node := self getNode.
    node isNil
           ifTrue: [^nil].
      node disconnect.
    changed := true.
      currentComponent := node model.
    listSelector := #listComponentConnections.
       breadboard update.!
erase
       "After user-verification, erase
         the circuit description."
    Cursor offset: MenuPosition.
    (self verify: 'Erase circuit description?')
       ifFalse: [^nil].
    self eraseCircuit.
    listSelector := #listDevices.
    changed := true.
      breadboard update.!
eraseCircuit
     "Erase the circuit description."
    devices  do: [:aDevice|
        self removeComponent: aDevice].
    signals  do: [:aSignal|
          self removeComponent: aSignal].
     switches do: [:aSwitch|
        self removeComponent: aSwitch].
       self initialize.!
getExistingComponent
        "Answer a user-specified component."
    | name component reply list |
     name := self getName.
      name isNil
        ifTrue: [^nil].
    component := self componentNamed: name.
      component isNil
     ifFalse: [^component].
       Cursor offset: MenuPosition.
       (Menu message:
         (name, ' not installed -- select from list?')) isNil
     ifTrue: [^nil].
    Cursor offset: MenuPosition.
    reply := self componentTypeMenu:
                 #(deviceNames signalNames switchNames).
       Cursor offset: MenuPosition.
       reply isNil
          ifTrue: [^nil].
     list := self perform: reply.
     (list size == 0)
     ifTrue: [
          Menu message: 'None installed'.
          Cursor offset: MenuPosition.
          ^nil].
Š       name := VariableMenu selectFrom: list.
    name isNil
           ifTrue: [^nil].
      name := list at: name.
    ^(self componentNamed: name).!
getExistingName
     "Answer a user-specified name of
         an existing component."
    | component |
    component := self getExistingComponent.
     component isNil
        ifTrue: [^nil].
       ^(component name).!
getFile
         "Answer a FileStream for a
         user-specified filename."
    | name |
     name := self getFilename.
    name isNil
        ifTrue: [^nil].
       ^(File pathName: name).!
getFilename
          "Answer a user-specified filename."
    | name |
     Cursor offset: MenuPosition.
     name :=
        Prompter
         prompt: 'Enter filename'
            default: ''.
    Cursor offset: MenuPosition.
    ^name.!
getName
          "Answer a user-specified name."
     | name |
    Cursor offset: MenuPosition.
    name :=
        Prompter
            prompt: 'Enter component name'
           default: ''.
      Cursor offset: MenuPosition.
      ^name.!
getNewName
       "Answer a user-specified name for
         a new component."
      | name |
    Cursor offset: MenuPosition.
    name :=
         Prompter
          prompt: 'Enter name for new component'
            default: ''.
       Cursor offset: MenuPosition.
       name isNil
         ifTrue: [^nil].
    [(self componentNamed: name) isNil]
       whileFalse: [
Š         name :=
            Prompter
                prompt: 'Name exists -- enter NEW name'
                default: name.
         Cursor offset: MenuPosition.
         name isNil
               ifTrue: [^nil]].
      ^name.!
getNode
        "Answer a user-specified LogicNode."
    | component |
     component := self getExistingComponent.
    component isNil
       ifTrue: [^nil].
    ^(component getNode).!
initialize
          "Private -- initialize a new
         LogicLab application."
    devices  := OrderedCollection new.
       signals  := OrderedCollection new.
     switches := OrderedCollection new.
    changed := true.!
install
           "Install a user-specified component."
    | component |
     component := LogicComponent install.
     component isNil
        ifTrue: [^nil].
       self addComponent: component.
    listSelector := self listSelectorFor: component.
    breadboard update.
    ^component.!
installClassFrom: aStream
       "Install a LogicComponent subclass
         whose name is the next word on aStream."
      | className |
       className := aStream nextWord.
    (Smalltalk includesKey: className asSymbol)
        ifFalse: [
          self error: ('Class: ', className, ' not installed')].!
installComponentFrom: aStream
      "Install a LogicComponent instance
         whose name is the next word on aStream."
     | className class component |
      className := aStream nextWord.
    class := LogicComponent classNamed: className.
    class isNil
     ifTrue: [
          self error: ('Unknown class: ', className).
         ^nil].
    component := class new installFrom: aStream.
    component isNil
          ifTrue: [^nil].
     ^(self addComponent: component).!
installConnectionFrom: aStream
Š        "Install a connection from aStream."
    | fromName from toName to fromNode toNode |
    fromName := aStream nextWord.
    from := self componentNamed: fromName.
      from isNil
        ifTrue: [
          self error: ('Unknown component: ', fromName).
            ^nil].
    fromNode := from getNodeFrom: aStream.
    fromNode isNil
         ifTrue: [
           self error: ('Unknown node on: ', fromName).
           ^nil].
    toName := aStream nextWord.
       to := self componentNamed: toName.
     to isNil
     ifTrue: [
          self error: ('Unknown component: ', toName).
          ^nil].
    toNode := to getNodeFrom: aStream.
    toNode isNil
      ifTrue: [
           self error: ('Unknown node on: ', toName).
         ^nil].
    ^(fromNode connect: toNode).!
installFrom: aStream
        "Load a circuit from the description
         on aStream."
    | keyWord |
    clashes := Dictionary new.
      [(aStream atEnd)
      or: [(keyWord := aStream nextWord) isNil]]
        whileFalse: [
         keyWord = 'LOAD'
              ifTrue:  [
                 self installClassFrom: aStream]
           ifFalse: [
                 keyWord = 'INSTALL'
                 ifTrue:  [
                    self installComponentFrom: aStream]
                  ifFalse: [
                     keyWord = 'CONNECT'
                        ifTrue:  [
                           self installConnectionFrom: aStream]
                          ifFalse: [
                        self error:
                            ('Unknown command: ',
                              keyWord)]]]].
      clashes release.
    clashes := nil.!
list
      "Process a user-specified list request."
    | selection |
    selection :=
       (Menu
Š         labels: ('Components\Connections\',
                  'Circuit Description') withCrs
          lines: #()
         selectors: #(listComponents
                      listConnections
                      listCircuit))
         popUpAt: MenuPosition.
    selection isNil
          ifTrue: [^nil].
     listSelector := selection.
    breadboard update.!
listCircuit
         "Answer a collection of strings with
         the circuit description."
    | name stream list |
    CursorManager execute change.
    name := 'logiclab.tmp'.
       stream := File pathName: name.
    list := OrderedCollection new.
    stream
         nextPutAll: '****  Circuit Description  ****';
       cr;
          cr.
    self storeOn: stream.
    stream flush.
    stream position: 0.
    [stream atEnd]
        whileFalse: [list add: stream nextLine].
    stream close.
     File remove: name.
    CursorManager normal change.
    ^list.!
listComponentConnections
          "Answer a collection of strings listing
         the connection chain(s) for the
         'currentComponent'."
    currentComponent isNil
        ifTrue:  [^#()]
       ifFalse: [
          ^(#('****  Connection List  ****' ' '),
              currentComponent connectionList)].!
listComponents
         "Answer a collection of strings containing
         a list of installed components."
    | selection |
    selection :=
     self componentTypeMenu:
             #(listDevices listSignals listSwitches).
    selection isNil
       ifTrue: [^#()].
    ^(self perform: selection).!
listConnections
     "Answer a collection of strings listing
         the connection chain(s) for a
         user-specified component."
Š    | component |
    component := self getExistingComponent.
     component isNil
        ifTrue: [^#()].
       currentComponent := component.
    ^self listComponentConnections.!
listContaining: aComponent
           "Answer the list (devices, signals, or switches)
         that includes aComponent."
    (devices includes: aComponent)
        ifTrue: [^devices].
       (signals includes: aComponent)
     ifTrue: [^signals].
    ^switches.!
listDevices
          "Answer a collection of strings containing
         a list of all the installed devices."
      | size list |
       size := devices size.
    size == 0
         ifTrue: [^#('No devices installed')].
    size := size + 1.
       list := OrderedCollection new: size.
       list add: 'DEVICES'.
       devices do: [:aDevice| list add: ('  ', (aDevice identify))].
    ^list.!
listSelectorFor: aComponent
          "Answer the list selector method used
         to produce the list for aComponent's type."
       aComponent isDevice
          ifTrue: [^#listDevices].
      aComponent isSignal
         ifTrue: [^#listSignals].
     ^#listSwitches.!
listSignals
        "Answer a collection of strings containing
         a list of all the installed input signals."
    | size list |
    size := signals size.
    size == 0
     ifTrue: [^#('No signals installed')].
      size := size + 1.
    list := OrderedCollection new: size.
    list add: 'SIGNALS'.
    signals do: [:aSignal| list add: ('  ', (aSignal identify))].
    ^list.!
listSwitches
       "Answer a collection of strings containing
         a list of all the installed swithces."
    | size list |
     size := switches size.
       size == 0
        ifTrue: [^#('No switches installed')].
    size := size + 1.
       list := OrderedCollection new: size.
Š       list add: 'SWITHCES'.
    switches do: [:aSwitch| list add: ('  ', (aSwitch identify))].
    ^list.!
load
     "Load a circuit description from
         a user-specified file."
    | file |
     file := self getFile.
      file isNil
        ifTrue: [^nil].
    self installFrom: file.
      listSelector := #listDevices.
       breadboard update.!
noDelay
         "Setup all components to ignore
         propagation delays."
    signals  do: [:signal| signal noDelay].
    switches do: [:switch| switch noDelay].
       devices  do: [:device| device noDelay].!
noMenu
     "Private -- answer an empty menu."
    ^(EmptyMenu new).!
open
         "Open the Breadboard and Analyzer windows."
    | size position |
     size := (Display boundingBox extent * 3) // 4.
       position := Display boundingBox center - (size // 2).
    topPane :=
          TopPane new
         label: ((self class description),
                    ' -- Breadboard');
         model: self;
         menu: #noMenu;
          yourself.
       topPane addSubpane:
          (breadboard := ListPane new
         name: #breadboardList;
           model: self;
           menu: #breadboardMenu;
          change: #doNothing:;
          framingRatio: (0 @ 0 extent: 1 @ 1)).
      topPane reframe: (position extent: size).
    topPane dispatcher openWindow scheduleWindow.!
quit
     "Quit this LogicLab."
      (self verify: 'Quit this LogicLab?')
          ifFalse: [^nil].
      self eraseCircuit.
    signals := switches := devices := nil.
      analyzer isNil
        ifFalse: [
          analyzer closeWindow.
           analyzer := nil].
    breadboard dispatcher deactivateWindow closeWindow.
       Scheduler systemDispatcher redraw.
Š     Scheduler resume.!
remove
     "Remove a user-specified component."
     | component |
      component := self getExistingComponent.
    component isNil
        ifTrue: [^nil].
    changed := true.
       listSelector := self listSelectorFor: component.
    self removeComponent: component.
       breadboard update.!
removeComponent: aComponent
     "Remove aComponent from the circuit."
      analyzer isNil
        ifFalse: [analyzer removeComponent: aComponent].
    (self listContaining: aComponent) remove: aComponent.
    aComponent remove.!
reset
         "Reset all components."
    signals     do: [:signal| signal reset].
     switches do: [:switch| switch reset].
      devices  do: [:device| device reset].!
restoreDelay
        "Setup all components to use
         propagation delays."
     signals  do: [:signal| signal restoreDelay].
     switches do: [:switch| switch restoreDelay].
     devices  do: [:device| device restoreDelay].!
resume
        "Resume the breadboarding application
         after running the simulation."
    Cursor offset: breadboard frame center.
    topPane dispatcher scheduleWindow.!
run
         "Invoke the LogicAnalyzer to run the simulation."
      analyzer isNil
        ifTrue: [
         analyzer := LogicAnalyzer new.
           analyzer openOn: self]
         ifFalse: [analyzer activate].!
save
           "Store the circuit description in
         a user-specified file."
    | file |
    file := self getFile.
     file isNil
       ifTrue: [^nil].
    CursorManager execute change.
    self storeOn: file.
    file
      flush;
        close.
      CursorManager normal change.!
selectName
     "Answer a user-selected name from a list
Š         of the names of installed components."
    | names index |
    names := self allNames.
      (names size == 0)
       ifTrue: [^nil].
    index := VariableMenu selectFrom: names.
      index isNil
         ifTrue: [^nil].
    ^(names at: index).!
signalNames
           "Answer a collection of all of the
         names of installed signals."
      | list |
    list := OrderedCollection new: (signals size).
    signals     do: [:aSignal| list add: aSignal name].
    ^list.!
signals
      "Answer the list of installed signals."
    ^signals.!
simulate
           "Simulate one pseudo-time interval."
    signals  do: [:signal| signal simulate].
       switches do: [:switch| switch simulate].
    devices  do: [:device| device simulate].!
storeClassesOn: aStream
           "Write a record of each component class
         used by the circuit on aStream."
       | classes |
      classes := Set new.
     devices  do: [:aDevice| classes add: aDevice class].
     signals  do: [:aSignal| classes add: aSignal class].
     switches do: [:aSwitch| classes add: aSwitch class].
     classes do: [:aClass|
          aStream
          nextPutAll: ('LOAD ', (aClass name));
           cr].!
storeComponentsFrom: aCollection on: aStream
       "Write a record of each logic component from
         aCollection installed in the circuit on aStream."
    aCollection do: [:aComponent|
      aStream nextPutAll: 'INSTALL '.
     aComponent storeOn: aStream.
         aStream cr].!
storeConnectionsOn: aStream
         "Write a record of each connection
         in the circuit on aStream."
    devices  do: [:aDevice| aDevice storeConnectionsOn: aStream].
    signals     do: [:aSignal| aSignal storeConnectionsOn: aStream].
     switches do: [:aSwitch| aSwitch storeConnectionsOn: aStream].
      devices  do: [:aDevice| aDevice unMark].
    signals  do: [:aSignal| aSignal unMark].
      switches do: [:aSwitch| aSwitch unMark].!
storeDevicesOn: aStream
      "Write a record of each logic device
         installed in the circuit on aStream."
Š    self storeComponentsFrom: devices on: aStream.!
storeOn: aStream
           "Write a description of the circuit on
         aStream in a form that can be recovered
         by the 'installOn:' method."
    self
      storeClassesOn: aStream;
      storeDevicesOn: aStream;
      storeSignalsOn: aStream;
      storeSwitchesOn: aStream;
       storeConnectionsOn: aStream.!
storeSignalsOn: aStream
           "Write a record of each logic signal
         installed in the circuit on aStream."
    self storeComponentsFrom: signals on: aStream.!
storeSwitchesOn: aStream
        "Write a record of each logic switch
         installed in the circuit on aStream."
      self storeComponentsFrom: switches on: aStream.!
switches
      "Answer the list of installed switches."
    ^switches.!
switchNames
        "Answer a collection of all of the
         names of installed swithces."
    | list |
    list := OrderedCollection new: (switches size).
    switches     do: [:aSwitch| list add: aSwitch name].
    ^list.!
verify: aPrompt
      "Ask the user to verify some condition."
    Cursor offset: MenuPosition.
    ^((Menu message: aPrompt) notNil).! !
 Ť

[LISTINÇ TWO]

LogicSwitch subclass: #ToggleSwitch
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: '' !

!ToggleSwitch class methods !

type
        "Answer a string with the receiver's type."
    ^'Toggle Switch'.! !
!ToggleSwitch methods !
identify
        "Answer a string identifying the receiver."
    ^((self name),
        ' (', (self type), ')').!
push: aButton
        "Simulate pushing a toggle switch by
         inverting its state."
    node invert.
    node isHigh
        ifTrue:  [aButton lampOn]
        ifFalse: [aButton lampOff].
    (model isNil or: [changeSelector isNil])
        ifFalse: [model perform: changeSelector].!
reset
        "Reset the receiver."
    button isNil
        ifFalse: [
            node isHigh
                ifTrue:  [button lampOn]
                ifFalse: [button lampOff]].!
simulate
        "Simulate a toggle switch."
    node output.! !


[LISTINÇ THREE]

LogicSwitch subclass: #PulserSwitch
  instanceVariableNames: 
    'rest time timer '
  classVariableNames: ''
  poolDictionaries: '' !

!PulserSwitch class methods !

type
        "Answer a string with the receiver's type."
    ^'Pulser'.! !
!PulserSwitch methods !
identify
        "Answer a string identifying the receiver."
    ^((self name),
        ' (', (self type), ' -- ',
            (LogicNode
                statePrintString: (LogicNode not: rest)), ': ',
            (TimeInterval timePrintString: time), ')').!
initialize
        "Initialize a new PulserSwitch."
    super initialize.
    rest := false.
    time := timer := 0.!

install
        "Answer the receiver with user-specified
         rest state and pulse time."
    rest := LogicNode getState.     "User will select pulse state"
    rest isNil
        ifTrue: [^super release].
    rest := LogicNode not: rest.
    time := TimeInterval getTimeFor: 'pulse'.
    time isNil
        ifTrue: [^super release].
    ^self.!
installFrom: aStream
        "Answer a new PulserSwitch initialized with
         parameters read from aStream."
    super installFrom: aStream.
    rest := LogicNode stateNamed: aStream nextWord.
    node state: rest.
    time  := aStream nextWord asInteger.
    ^self.!
push: aButton
        "Simulate pushing a Pulser Switch."
    timer == 0
        ifTrue: [node state: (LogicNode not: rest)].
    timer := time.
    node isHigh
        ifTrue:  [aButton lampOn]
        ifFalse: [aButton lampOff].
    (model isNil or: [changeSelector isNil])
Š        ifFalse: [model perform: changeSelector].!
reset
        "Reset the receiver's state to its resting
         state and its timer to zero."
    node state: rest.
    timer := 0.
    button isNil
        ifFalse: [
            node isHigh
                ifTrue:  [button lampOn]
                ifFalse: [button lampOff]].!
simulate
        "Simulate a Pulser Switch."
    timer == 0
        ifTrue: [
            node state: rest.
            button isNil
                ifFalse: [
                    node isHigh
                        ifTrue:  [button lampOn]
                        ifFalse: [button lampOff]]]
        ifFalse: [timer := timer - 1].
    node output.!
storeOn: aStream
        "Store a record of the receiver on aStream."
    super storeOn: aStream.
    aStream
        nextPutAll: (' ',
                        (LogicNode statePrintString: rest), ' ',
                        (time printString)).! !

[LISTINÇ FOUR]

LogicDevice subclass: #N74LS00
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: '' !

!N74LS00 class methods !
description
        "Answer a string with a description
         of the receiver's function."
    ^'Quad 2-input NAND gate'.!
type
        "Answer a string with the receiver's type."
    ^'74LS00'.! !
!N74LS00 methods !
initialize
        "Private -- initialize the propagation delays
         for a new 74LS00 LogicDevice."
    super
        initialize;
        initializeDelays:
            #(  5  5 10    5  5 10    0
               10  5  5   10  5  5    0 ).!
simulate
        "Simulate a 74LS00 device."
    ((pins at: 1) isHigh and: [(pins at: 2) isHigh])
        ifTrue:  [(pins at: 3) output: false]
        ifFalse: [(pins at: 3) output: true].
    ((pins at: 4) isHigh and: [(pins at: 5) isHigh])
        ifTrue:  [(pins at: 6) output: false]
        ifFalse: [(pins at: 6) output: true].
    ((pins at: 10) isHigh and: [(pins at: 9) isHigh])
        ifTrue:  [(pins at: 8) output: false]
        ifFalse: [(pins at: 8) output: true].
    ((pins at: 13) isHigh and: [(pins at: 12) isHigh])
        ifTrue:  [(pins at: 11) output: false]
        ifFalse: [(pins at: 11) output: true].! !


[LISTINÇ FIVE]

output: aState Ť

        "Generate aState as an output from the node." Ť

    old := int. Ť
    int := aState. Ť
    int == ext Ť
        ifTrue: [ Ť
            "State is stable" Ť
            timer := 0. Ť
            ^self outputToConnections]. Ť
    "State has changed" Ť
    timer == 0 Ť
        ifTrue: [ Ť
            "No delay in progress -- initiate prop delay" Ť
            delay == 0 Ť
                ifTrue: [ Ť
                    "No delay -- just change state" Ť
                    ext := int] Ť
                ifFalse: [ Ť
                    "Arm delay timer" Ť
                    timer := delay]. Ť
            ^self outputToConnections]. Ť
    "Propagation delay in progress" Ť
    timer := timer - 1. Ť
    timer == 0 Ť
        ifTrue: [ Ť
            "Timer has expired -- update state" Ť
            ext := int]. Ť
    self outputToConnections. Ť


[LISTINÇ SIX]

simulate Ť

        "Simulate a 74LS00 device." Ť

    ((pins at: 1) isHigh and: [(pins at: 2) isHigh]) Ť
        ifTrue:  [(pins at: 3) output: false] Ť
        ifFalse: [(pins at: 3) output: true]. Ť
    ((pins at: 4) isHigh and: [(pins at: 5) isHigh]) Ť
        ifTrue:  [(pins at: 6) output: false] Ť
        ifFalse: [(pins at: 6) output: true]. Ť
    ((pins at: 10) isHigh and: [(pins at: 9) isHigh]) Ť
        ifTrue:  [(pins at: 8) output: false] Ť
        ifFalse: [(pins at: 8) output: true]. Ť
    ((pins at: 13) isHigh and: [(pins at: 12) isHigh]) Ť
        ifTrue:  [(pins at: 11) output: false] Ť
        ifFalse: [(pins at: 11) output: true]. Ť
Ť