</p>
<p>
- In 1967, Marvin Minsky addressed this very topic in saying: "We need not think of the machine's tape as infinite. We imagine instead that the machine begins with a finite tape, but that, whenever an end is encountered, another unit of tape is attached." <RT-cite ref="Marvin L. Minsky, Computation: Finite and Infinite Machines, Prentice Hall, 1967, p. 167"></RT-cite> In 1967, this was a perfectly natural thing to suggest, as computers utilized magnetic tape memory on manually mounted reels, and it was entirely possible for a computation to stop and request a new reel of tape to be mounted. Contemporary computer architectures could achieve similar effects by swapping memory pages out to ever-expanding pools of networked auxiliary storage. In practice, however, once local storage is depleted, the operating system abruptly terminates the process rather than pausing to autonomously provision additional capacity. This observation highlights the necessity to consider the OS as part of the <em>architecture</em>, i.e. the programmer's view of the machine.
+ In 1967, Marvin Minsky addressed this very topic in saying: "We need not think of the machine's tape as infinite. We imagine instead that the machine begins with a finite tape, but that, whenever an end is encountered, another unit of tape is attached." <RT-cite ref="Marvin L. Minsky, Computation: Finite and Infinite Machines, Prentice Hall, 1967, p. 167"></RT-cite> In 1967, this was a perfectly natural thing to suggest, as computers utilized magnetic tape memory on manually mounted reels, and it was entirely possible for a computation to stop and request a new reel of tape to be mounted. Contemporary computer architectures could achieve similar effects by swapping memory pages out to ever-expanding pools of networked auxiliary storage. In practice, however, once local storage is depleted, the operating system abruptly terminates the process rather than pausing to autonomously provision additional capacity. This observation highlights the necessity to consider the OS as part of the <em>architecture</em>, i.e. information that is valuable to programmers when they design the logic of programs.
</p>
<p>
</p>
<p>
- An <RT-term-em>architecture</RT-term-em> provides the programmer's view of the computer. This includes all ilks of programmers, including OS programmers, driver programmers, and application programmers. This is sometimes called the instruction set architecture. In addition to specifying the instructions, it includes specifying the behavior of the interrupt subsystem, the method of doing I/O (typically memory-mapped or dedicated I/O instructions), DMA, the special registers and their effects, and the standards to be followed for each if any. More recently, this also includes specifying how programs can make use of secure areas. The architecture is specified by an <RT-term>architect</RT-term>.
+ An <RT-term-em>architecture</RT-term-em> provides the programmer with information that is valuable when designing the logic of programs. This includes all ilks of programmers, including OS programmers, driver programmers, and application programmers. This is sometimes called the instruction set architecture. In addition to specifying the instructions, it includes specifying the behavior of the interrupt subsystem, the method of doing I/O (typically memory-mapped or dedicated I/O instructions), DMA, the special registers and their effects, and the standards to be followed for each if any. More recently, this also includes specifying how programs can make use of secure areas. The architecture is specified by an <RT-term>architect</RT-term>.
</p>
<p>
</p>
<p>
- The Turing Machine is a computation theory object that is suggestive of a simple architecture; however, a little work is needed to complete architecture analog. The fundamentals are present, the read/write head, the tape, the procedure for using the tape, but other components are missing. The manipulation of symbols remains ungrounded. The tape is not well defined. The use of emptiness is non-architecture like. The tape transport is not articulated, though it is implied. The read buffer that holds a value read, so the state controller can use it over multiple non-read steps, is not identified as a component. As we proceed, we will likely discover other missing components.
+ The Turing Machine is a computation theory object that is suggestive of a simple architecture; however, a little work is needed to complete architecture analog. The fundamentals are present, the read/write head, the tape, the procedure for using the tape, but other components are missing. The manipulation of symbols remains ungrounded. The tape is not well defined. The use of emptiness is non-architecture like. The tape transport is not articulated, though it is implied. The read buffer that holds a value read, so the programmed controller can do write without clobbering the read data needed for the next transition is not identified as a component. As we proceed, we will likely discover other missing components.
</p>
<p>
</p>
-<h1>Symbol</h1>
+ <h1>Symbol</h1>
-<p>
- A <RT-term>symbol</RT-term> is a distinct mathematical object capable of being instantiated. Within a given context, any instance of a specific symbol evaluates as equal to any other instance of that identical symbol, and evaluates as not equal to any instance of a different symbol. (As a matter of convenience in this document, sequences of letters and/or digits are utilized for symbol instances.)
-</p>
+ <p>
+ A <RT-term>symbol</RT-term> is a distinct mathematical object capable of being instantiated. Within a given context, any instance of a specific symbol evaluates as equal to any other instance of that identical symbol, and evaluates as not equal to any instance of a different symbol. (As a matter of convenience in this document, sequences of letters and/or digits are utilized for symbol instances.)
+ </p>
-<p>
- Put more formally, given a set of instantiable objects and a collection of instances made from them, for these objects to be symbols, two structural conditions must be met. First, it must be possible to define an instance comparison operation, denoted <RT-math>=</RT-math>, that acts as an equivalence relation to partition the collection into discrete equivalence classes. There must be a one-to-one correspondence between the resulting equivalence classes and the original instantiable objects from which the member instances were derived.
-</p>
+ <p>
+ Put more formally, given a set of instantiable objects and a collection of instances made from them, for these objects to be symbols, two structural conditions must be met. First, it must be possible to define an instance comparison operation, denoted <RT-math>=</RT-math>, that acts as an equivalence relation to partition the collection into discrete equivalence classes. There must be a one-to-one correspondence between the resulting equivalence classes and the original instantiable objects from which the member instances were derived.
+ </p>
-<p>
- It follows from this definition that the distinct equivalence classes can be used as a proxy for the instantiable objects themselves. That is, a person can name either the instantiable object or the equivalence class, and then through this correspondence, find the other.
-</p>
+ <p>
+ It follows from this definition that the distinct equivalence classes can be used as a proxy for the instantiable objects themselves. That is, a person can name either the instantiable object or the equivalence class, and then through this correspondence, find the other.
+ </p>
-<h2>The factory interpretation</h2>
+ <h2>The factory interpretation</h2>
-<p>
- In the context of real machines, the symbol itself can be defined as a factory that produces symbol instances. A new symbol instance of the given symbol is then made, say, by calling the factory's <RT-code>make</RT-code> function. All of the symbol instances made by the factory constitute the members of the corresponding equivalence class.
-</p>
+ <p>
+ In the context of real machines, the symbol itself can be defined as a factory that produces symbol instances. A new symbol instance of the given symbol is then made, say, by calling the factory's <RT-code>make</RT-code> function. All of the symbol instances made by the factory constitute the members of the corresponding equivalence class.
+ </p>
-<p>
- A <RT-term>symbol instance</RT-term> newly minted by the factory is said to <RT-neologism>come direct from the factory</RT-neologism>. A symbol instance direct from the factory is also called an <RT-neologism>original</RT-neologism>.
-</p>
+ <p>
+ A <RT-term>symbol instance</RT-term> newly minted by the factory is said to <RT-neologism>come direct from the factory</RT-neologism>. A symbol instance direct from the factory is also called an <RT-neologism>original</RT-neologism>.
+ </p>
-<h3>Required properties of symbol factories</h3>
+ <h3>Required properties of symbol factories</h3>
-<p>
- Any two symbol instances returned directly from two distinct factories will always evaluate to <RT-code>False</RT-code> during an equality comparison. In other words, two distinct originals will always be not equal.
-</p>
+ <p>
+ Any two symbol instances returned directly from two distinct factories will always evaluate to <RT-code>False</RT-code> during an equality comparison. In other words, two distinct originals will always be not equal.
+ </p>
-<p>
- Given an original, all copies <RT-neologism>stemming from</RT-neologism> it will be equal to each other and to the original. By <RT-neologism-em>stemming from</RT-neologism-em>, this definition includes all direct copies and copies of copies.
-</p>
+ <p>
+ Given an original, all copies <RT-neologism>stemming from</RT-neologism> it will be equal to each other and to the original. By <RT-neologism-em>stemming from</RT-neologism-em>, this definition includes all direct copies and copies of copies.
+ </p>
-<p>
- Given any two originals, say <var>A</var> and <var>B</var>, it is established that <var>A</var> is not equal to <var>B</var>, as discussed above. Note also that <var>A</var> is not equal to any copy stemming from <var>B</var>, and <var>B</var> is not equal to any copy stemming from <var>A</var>.
-</p>
+ <p>
+ Given any two originals, say <var>A</var> and <var>B</var>, it is established that <var>A</var> is not equal to <var>B</var>, as discussed above. Note also that <var>A</var> is not equal to any copy stemming from <var>B</var>, and <var>B</var> is not equal to any copy stemming from <var>A</var>.
+ </p>
-<p>
- Though symbol instances are integer-like in that copy and equality comparison operations can be used with them, symbol instances are strictly disallowed from being used with other integer operators. Symbols cannot be compared for greater-than or less-than; they cannot be incremented, added, nor subtracted, etc.
-</p>
+ <p>
+ Though symbol instances are integer-like in that copy and equality comparison operations can be used with them, symbol instances are strictly disallowed from being used with other integer operators. Symbols cannot be compared for greater-than or less-than; they cannot be incremented, added, nor subtracted, etc.
+ </p>
-<h2>Instance implementation</h2>
+ <h2>Instance implementation</h2>
-<p>
- Within a process, a reference to the factory can be used as a symbol instance, which will cause the factory to become trivial. Making a new instance will merely require copying the factory reference, and there will be nothing in memory that the base factory reference is pointing to.
-</p>
+ <p>
+ Within a process, a reference to the factory can be used as a symbol instance, which will cause the factory to become trivial. Making a new instance will merely require copying the factory reference, and there will be nothing in memory that the base factory reference is pointing to.
+ </p>
-<p>
- In general, memory addresses are built-in symbol instances, hence within the context of a single process run, a program can make use of these symbols. However, this diminishes the size of the address space and leaves the memory at those addresses unused. A common hedge is then to use references into a dictionary, where the data looked up in the dictionary is the <RT-term>name</RT-term> of the symbol.
-</p>
+ <p>
+ In general, memory addresses are built-in symbol instances, hence within the context of a single process run, a program can make use of these symbols. However, this diminishes the size of the address space and leaves the memory at those addresses unused. A common hedge is then to use references into a dictionary, where the data looked up in the dictionary is the <RT-term>name</RT-term> of the symbol.
+ </p>
-<p>
- Such symbol names are non-structural strings, so they do not need to follow the rules of symbols. For example, a program written where references to strings were used as symbol instances, could give multiple, or all, strings the same name, and the program would function. Conventionally, the names are made to be distinct so as to avoid confusion. The hazard here is that a programmer will then conflate the string name with the symbol instance, and perform symbol operations with it.
-</p>
+ <p>
+ Such symbol names are non-structural strings, so they do not need to follow the rules of symbols. For example, a program written where references to strings were used as symbol instances, could give multiple, or all, strings the same name, and the program would function. Conventionally, the names are made to be distinct so as to avoid confusion. The hazard here is that a programmer will then conflate the string name with the symbol instance, and perform symbol operations with it.
+ </p>
-<p>
- An alternative implementation is to have the factory return an integer value. Each factory has a base integer that is distinct from that of other factories. Calling <RT-code>make</RT-code> then returns the base integer.
-</p>
+ <p>
+ An alternative implementation is to have the factory return an integer value. Each factory has a base integer that is distinct from that of other factories. Calling <RT-code>make</RT-code> then returns the base integer.
+ </p>
-<p>
- As another alternative, each factory can be given a base string, and then <RT-code>make</RT-code> returns a copy of the base string. Here we refer literally to the string as the symbol instance. There is no separate name, and the string data, not the reference to the string, becomes the symbol instance. This is however merely an architectural constraint, under the hood an implementation could use string references as long as it always appears to the programmer that the string value is being used.
-</p>
+ <p>
+ As another alternative, each factory can be given a base string, and then <RT-code>make</RT-code> returns a copy of the base string. Here we refer literally to the string as the symbol instance. There is no separate name, and the string data, not the reference to the string, becomes the symbol instance. This is however merely an architectural constraint, under the hood an implementation could use string references as long as it always appears to the programmer that the string value is being used.
+ </p>
-<p>
- At the time of this writing many machines use 64 bit words. This is equivalent to 8 ASCII characters, while the average size of an identifier is about 5 characters. Hence the approach of using a string as a symbol might not be as inefficient as it seems to be at first. Using strings has advantages. Symbol instances can carry semantic clues for the programmer. There is no hazard of conflating the string instance with the name, as they are the same. Also, a string instance will have integrity across contexts, such as between invocations or when passed between processes (note the section below on crossing context boundaries). The drawback is that the strings are typically ad hoc so the guarantee of distinctness becomes merely a contract with the programmer. Attempts at strengthening that contract can lead to frustrating errors and add complexity to programs.
-</p>
+ <p>
+ At the time of this writing many machines use 64 bit words. This is equivalent to 8 ASCII characters, while the average size of an identifier is about 5 characters. Hence the approach of using a string as a symbol might not be as inefficient as it seems to be at first. Using strings has advantages. Symbol instances can carry semantic clues for the programmer. There is no hazard of conflating the string instance with the name, as they are the same. Also, a string instance will have integrity across contexts, such as between invocations or when passed between processes (note the section below on crossing context boundaries). The drawback is that the strings are typically ad hoc so the guarantee of distinctness becomes merely a contract with the programmer. Attempts at strengthening that contract can lead to frustrating errors and add complexity to programs.
+ </p>
-<h3>Consequentiality</h3>
+ <h3>Consequentiality</h3>
-<p>
- The time that a Turing Machine takes to copy a symbol to or from tape is considered to be a single machine step. The step itself is part of the step count, and the count certainly is consequential, but the copy itself is a constant of 1. It will never affect time complexity results.
-</p>
+ <p>
+ The time that a Turing Machine takes to copy a symbol to or from tape is considered to be a single machine step. The step itself is part of the step count, and the count certainly is consequential, but the copy itself is a constant of 1. It will never affect time complexity results.
+ </p>
-<p>
- The Turing Machine is defined with a finite alphabet and a couple of additional symbols. As these are part of the machine definition, and thus do not change at run time, the time to make them is inconsequential.
-</p>
+ <p>
+ The Turing Machine is defined with a finite alphabet and a couple of additional symbols. As these are part of the machine definition, and thus do not change at run time, the time to make them is inconsequential.
+ </p>
-<p>
- On a real machine, the factory would be used to make the alphabet and a couple of additional symbols, but as the alphabet is finite, and set in advance, making it is similarly inconsequential.
-</p>
+ <p>
+ On a real machine, the factory would be used to make the alphabet and a couple of additional symbols, but as the alphabet is finite, and set in advance, making it is similarly inconsequential.
+ </p>
-<p>
- This leaves the question of the time to copy an instance. Such a copy is done when reading and writing a tape on the Turing Machine, and reading and writing memory on a real machine. As the alphabet is finite, each symbol can be encoded in a fixed number of bits. Hence, a copy time is constant. Typically the number of symbols involved is so small, that a single read or write of memory is required for doing a copy, but even if the number of symbols is enormous, it will be a fixed constant time. There can be extenuating circumstances, such as cache misses and page faults. So generally symbol copy time is inconsequential, but it is possible in address aliasing conditions, say with page faults, can muddy the picture.
-</p>
+ <p>
+ This leaves the question of the time to copy an instance. Such a copy is done when reading and writing a tape on the Turing Machine, and reading and writing memory on a real machine. As the alphabet is finite, each symbol can be encoded in a fixed number of bits. Hence, a copy time is constant. Typically the number of symbols involved is so small, that a single read or write of memory is required for doing a copy, but even if the number of symbols is enormous, it will be a fixed constant time. There can be extenuating circumstances, such as cache misses and page faults. So generally symbol copy time is inconsequential, but it is possible in address aliasing conditions, say with page faults, can muddy the picture.
+ </p>
-<h2>Distinctness across contexts</h2>
+ <h2>Distinctness across contexts</h2>
-<p>
- If a symbol persists across contexts (such as across scopes or processes), it must remain distinct from all other symbols in its new context.
-</p>
+ <p>
+ If a symbol persists across contexts (such as across scopes or processes), it must remain distinct from all other symbols in its new context.
+ </p>
-<p>
- One way to meet this requirement is to find a scope encompassing both contexts and to place the symbol factory there. Another solution is to give each context a distinct root symbol and to use an array of symbols in place of the imported symbol. Yet another approach is to associate an imported symbol with a new symbol in the given new context using a correspondence map.
-</p>
+ <p>
+ One way to meet this requirement is to find a scope encompassing both contexts and to place the symbol factory there. Another solution is to give each context a distinct root symbol and to use an array of symbols in place of the imported symbol. Yet another approach is to associate an imported symbol with a new symbol in the given new context using a correspondence map.
+ </p>
-<p>
- When utilizing memory addresses as symbols in a virtual memory environment, the convention is to disallow addresses in one process from being used in another. If that isolation is insufficient, it is often adequate to use indexes instead of addresses, taking the address to the base of the data structure. Though the absolute address of the data structure might differ across contexts, the relative offset remains constant. Another approach is to reserve memory address blocks and to guarantee imported pages have the exact same addresses as before, though they might be imported sequentially to reuse the memory block. In architecture, this is generally known as the <RT-term>pointer swizzling problem</RT-term>.
-</p>
+ <p>
+ When utilizing memory addresses as symbols in a virtual memory environment, the convention is to disallow addresses in one process from being used in another. If that isolation is insufficient, it is often adequate to use indexes instead of addresses, taking the address to the base of the data structure. Though the absolute address of the data structure might differ across contexts, the relative offset remains constant. Another approach is to reserve memory address blocks and to guarantee imported pages have the exact same addresses as before, though they might be imported sequentially to reuse the memory block. In architecture, this is generally known as the <RT-term>pointer swizzling problem</RT-term>.
+ </p>
-<h2>Alphabet</h2>
+ <h2>Alphabet</h2>
-<p>
- An alphabet is another name for 'a set of symbols'. Because it is simply a set, an infinite number of distinct alphabets can exist. The Turing Machine specification discusses '<em>the</em> alphabet' strictly because a given Turing Machine instance utilizes only one specific alphabet. A set of symbols could be finite or infinite. The Turing Machine alphabet is finite.
-</p>
+ <p>
+ An alphabet is another name for 'a set of symbols'. Because it is simply a set, an infinite number of distinct alphabets can exist. Papers on Turing Machine typically speak of '<em>the</em> alphabet', as the alphabet of data symbols. However, other alphabets can be defined for Turing Machines, such as the alphabet of states in the programmed controller. A set of symbols could be finite or infinite. The Turing Machine alphabets are finite.
+ </p>
-<h2>Examples</h2>
+ <h2>Examples</h2>
-<p>
- The <RT-code>enum</RT-code> of C is used to make alphabets of named symbols. Each entry in the enum is a static symbol factory, and instances are distinct integers.
-</p>
+ <p>
+ The <RT-code>enum</RT-code> of C is used to make alphabets of named symbols. Each entry in the enum is a static symbol factory, and instances are distinct integers.
+ </p>
-<RT-code>
-typedef enum {
- SYMBOL_EMPTY = 0,
- SYMBOL_ZERO = 1,
- SYMBOL_ONE = 2,
- SYMBOL_A = 3,
- SYMBOL_B = 4
-} TapeAlphabet;
-
-/* The enum definition acts as the factory. */
-/* Instantiating copies of the symbols: */
-TapeAlphabet cell_1 = SYMBOL_A;
-TapeAlphabet cell_2 = SYMBOL_A;
-
-/* Equality comparison over instances */
-if (cell_1 == cell_2) {
- /* Evaluates to True */
-}
-</RT-code>
+ <RT-code>
+ typedef enum {
+ SYMBOL_EMPTY = 0,
+ SYMBOL_ZERO = 1,
+ SYMBOL_ONE = 2,
+ SYMBOL_A = 3,
+ SYMBOL_B = 4
+ } TapeAlphabet;
+
+ /* The enum definition acts as the factory. */
+ /* Instantiating copies of the symbols: */
+ TapeAlphabet cell_1 = SYMBOL_A;
+ TapeAlphabet cell_2 = SYMBOL_A;
+
+ /* Equality comparison over instances */
+ if (cell_1 == cell_2) {
+ /* Evaluates to True */
+ }
+ </RT-code>
-<p>
- The <RT-code>enum</RT-code> is a static alphabet made by the compiler, where symbol instances are integers. In the following example, the alphabet is made dynamically, where each symbol instance is a string pointer.
-</p>
+ <p>
+ The <RT-code>enum</RT-code> is a static alphabet made by the compiler, where symbol instances are integers. In the following example, the alphabet is made dynamically, where each symbol instance is a string pointer.
+ </p>
-<RT-code>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-// maximum legal index into the symbol list
-#define LIST_EXTENT 3
-
-typedef const char *Instance;
-typedef struct{
- Instance *head;
- Instance *tail;
- Instance *extent;
-} List;
-
-static List SYM_LIST = {NULL ,NULL ,NULL};
-
-Instance make_list(const char *name){
- size_t size = LIST_EXTENT + 1;
- SYM_LIST.head = (Instance *)malloc( size * sizeof(Instance) );
- SYM_LIST.tail = SYM_LIST.head;
- SYM_LIST.extent = SYM_LIST.head + LIST_EXTENT;
- *SYM_LIST.head = strdup(name);
- return *SYM_LIST.head;
-}
-
-Instance make_symbol(const char *name){
- if(!SYM_LIST.head) return make_list(name);
-
- Instance *pt = SYM_LIST.head;
- while(1){
- if( strcmp(*pt ,name) == 0 ) return *pt;
-
- if(pt == SYM_LIST.extent){
- fprintf(stderr ,"symbol list overflow for %s\n" ,name);
- return NULL;
- }
-
- if(pt == SYM_LIST.tail){
- *++SYM_LIST.tail = strdup(name);
- return *SYM_LIST.tail;
- }
-
- pt++;
- }
-}
-
-int main(){
- Instance a = make_symbol("a");
- Instance b = make_symbol("b");
- Instance c = make_symbol("c");
- Instance d = make_symbol("d");
- Instance e = make_symbol("e"); // overflows table
-
- Instance *pt = SYM_LIST.head;
- Instance *pt_tail = SYM_LIST.tail;
- while(1){
- puts(*pt);
- if(pt == pt_tail) break;
- pt++;
- }
-
- if(e == NULL) printf("e is NULL\n");
-}
-</RT-code>
+ <RT-code>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <stdio.h>
+
+ // maximum legal index into the symbol list
+ #define LIST_EXTENT 3
+
+ typedef const char *Instance;
+ typedef struct{
+ Instance *head;
+ Instance *tail;
+ Instance *extent;
+ } List;
+
+ static List SYM_LIST = {NULL ,NULL ,NULL};
+
+ Instance make_list(const char *name){
+ size_t size = LIST_EXTENT + 1;
+ SYM_LIST.head = (Instance *)malloc( size * sizeof(Instance) );
+ SYM_LIST.tail = SYM_LIST.head;
+ SYM_LIST.extent = SYM_LIST.head + LIST_EXTENT;
+ *SYM_LIST.head = strdup(name);
+ return *SYM_LIST.head;
+ }
+
+ Instance make_symbol(const char *name){
+ if(!SYM_LIST.head) return make_list(name);
+
+ Instance *pt = SYM_LIST.head;
+ while(1){
+ if( strcmp(*pt ,name) == 0 ) return *pt;
+
+ if(pt == SYM_LIST.extent){
+ fprintf(stderr ,"symbol list overflow for %s\n" ,name);
+ return NULL;
+ }
+
+ if(pt == SYM_LIST.tail){
+ *++SYM_LIST.tail = strdup(name);
+ return *SYM_LIST.tail;
+ }
+
+ pt++;
+ }
+ }
+
+ int main(){
+ Instance a = make_symbol("a");
+ Instance b = make_symbol("b");
+ Instance c = make_symbol("c");
+ Instance d = make_symbol("d");
+ Instance e = make_symbol("e"); // overflows table
+
+ Instance *pt = SYM_LIST.head;
+ Instance *pt_tail = SYM_LIST.tail;
+ while(1){
+ puts(*pt);
+ if(pt == pt_tail) break;
+ pt++;
+ }
+
+ if(e == NULL) printf("e is NULL\n");
+ }
+ </RT-code>
<h1>The Turing Machine as an architecture</h1>
</p>
<p>
- Furthermore, this definition adopts the language of hardware specification rather than formal mathematics. Because this is strictly a terminology change with direct correspondences between operations, the resulting model remains isomorphic to the Hopcroft and Ullman definition. Adopting this architectural perspective facilitates the overarching goal of building downward toward a computer architecture, rather than strictly preparing for highly abstract mathematical analysis, though it certainly does not preclude such analysis. In this model, the structural components are read-only, while the memory components will have data flowing through them. As detailed in the control procedures below, data flows dynamically at the command of the state controller.
+ Furthermore, this definition adopts the language of hardware specification rather than formal mathematics. Because this is strictly a terminology change with direct correspondences between operations, the resulting model remains isomorphic to the Hopcroft and Ullman definition. Adopting this architectural perspective facilitates the overarching goal of building downward toward a computer architecture, rather than strictly preparing for highly abstract mathematical analysis, though it certainly does not preclude such analysis. In this model, the structural components are read-only, while the memory components will have data flowing through them. As detailed in the control procedures below, data flows dynamically at the command of the programmed controller.
</p>
<figure>
<li>a read-only left from leftmost <RT-term>error symbol</RT-term> that instances can be made from</li>
<li>a <RT-term>head</RT-term> bearing tape transport mechanism</li>
<li>a read-writable single symbol <RT-term>read data buffer</RT-term></li>
- <li>a constant state machine <RT-term>controller</RT-term> with states that can be referenced</li>
+ <li>a constant state machine <RT-term>programmed controller</RT-term> with states that can be referenced</li>
<li>a constant <RT-term>initial-state</RT-term> reference</li>
<li>a constant <RT-term>halt-state</RT-term> reference</li>
<li>a read-writable <RT-term>current-state</RT-term> reference variable</li>
<li>a <RT-term>reset button</RT-term> that activates logic that initializes the machine</li>
</ul>
- <p>We will each highlighted terms as a short name the associated item.</p>
+ <p>Each highlighted term is a short name for the associated item.</p>
<p>
The distinct empty-symbol can be any symbol that is excluded from the alphabet. Only instances of alphabet symbols or the empty-symbol are permitted to be written to the tape.
</p>
<p>
- Mathematically, a Turing Machine tape can be expressed as a path graph. However, they imply different ontological contexts. The neighbor property of a Turing Machine tape cell specifically informs a clock-driven atomic step function where to place the machine head next. The machine only has defined meaning at the state points on the controller. In contrast, a path graph exists in the wider context of graph theory. A path graph has edges and each edge can be focused on, said to be traversed over, and given general properties. These are things we explicitly excluded in the tape definition. If we were to move the tape from the context of the machine and into the more abstract mathematical context by modeling the tape with a path graph, then we would do so for the purpose of analyzing the tape, which is a higher order and more abstract activity. However, in the current exercise we are not reaching towards the more abstract; rather, we are reaching in the other direction, towards machine architecture. This is one of the reasons that Alan Turing's use of a tape, rather than a path graph, so elegantly suggested that computation theory applied to real machines and was not merely limited to the Entscheidungsproblem.
+ Mathematically, a Turing Machine tape can be expressed as a path graph. However, a tape mode and a path graph model imply different ontological contexts. The neighbor property of a Turing Machine tape cell specifically informs a clock-driven atomic step function where to place the machine head next. The machine only has defined meaning at the state points on of the programmed controller. In contrast, a path graph exists in the wider context of graph theory. A path graph has edges and each edge can be focused on, said to be traversed over, and given general properties. These are things we explicitly excluded in the tape definition. If we were to move the tape from the context of the machine and into the more abstract mathematical context by modeling the tape with a path graph, then we would do so for the purpose of analyzing the tape, which is a higher order and more abstract activity. However, in the current exercise we are not reaching towards the more abstract; rather, we are reaching in the other direction, towards machine architecture. This is one of the reasons that Alan Turing's use of a tape, rather than a path graph, so elegantly suggested that computation theory applied to real machines and was not merely limited to the Entscheidungsproblem.
</p>
<h2>The Turing Machine tape head</h2>
</p>
-<h2>The controlling state machine</h2>
+<h2>The programmed controller</h2>
-<p>The controlling state machine can be either a Mealy or Moore style state machine graph, as these are equally expressive. In this volume we describe a Moore style machine and corresponding procedure here.
+<p>
+ There are two distinct centers of logical control for the Turing Machine. The one we are most familiar with from the many descriptions of the Turing Machine in papers is called the 'state controller'; however, this term becomes confusing when we realize there are additional controllers involved. Hence, we call this component the <RT-term-em>programmed controller</RT-term-em>. It is customized for each different problem the Turing Machine will work on, while the other controllers give the Turing Machine its fixed characteristics.
</p>
-<p>The state machine graph is a set containing three elements:</p>
+<h3>Components</h3>
+
+<p>The programmed controller contains the following components:</p>
<ul>
- <li>set of states</li>
- <li>set of <state, command> pairings</li>
- <li>set of read buffer value gated arcs</li>
+ <li>alphabet of states</li>
+ <li>a reset control line</li>
+ <li>a step control line</li>
+ <li>an initial state</li>
+ <li>a halt state</li>
+ <li>a current state register</li>
+ <li>a comparator</li>
+ <li>an instruction table</li>
+ <li>a next state table</li>
+ <li>default next state table</li>
+ <li>error state symbol</li>
</ul>
<p>
- A state is a symbol. Instances of state symbols appear in a different context than those of alphabet symbols or the empty-symbol, and thus they do not need to be distinct from them.
+ Each state corresponds to a symbol. Here, instances of the state symbols appear in a different context than that of the data alphabet symbols or the empty-symbol, and thus they do not need to be distinct from them. In real machines, state symbol instances are unsigned integers.
+</p>
+
+<p>
+ The alphabet of states is specified in the architecture, and in the design stage becomes a table in a document where each row relates a bit vector with a name. It remains a documented abstraction to the designers, but can become embodied in programs where meaningful print statements are needed. Perhaps in a CAD tool such as a hardware debugger.
+</p>
+
+<p>
+ The current state register holds an instance of the current state symbol. During reset its value is forced to be an instance of the initial state symbol. During normal operation it is successively updated with the prior next state choice.
</p>
<p>
- Each state has a command attached to it, <state, command>. Each state appears in exactly one such pairing.
+ The current state register output is wired to the comparator, along with the halt symbol. A positive match across the comparator asserts a gating signal that halts the machine clock. This halts the internal procedures (discussed in a later chapter), which guarantees that no further step pulses will arrive at the programmed controller.
</p>
<p>
- We have a fixed set of commands: { do-nothing, step-left, step-right, and write(value) }, where the value parameter is set when the controller is defined.
+ The current state register is used to lookup a row in the instruction table. Each defined row contains an instruction that is sent to the tape transport unit. A retrieved instruction provides the specific control code and, in the case of a write instruction, the argument symbol to be written. If the current state yields no match in the instruction table, the logic defaults to issuing a 'no-op', no operation instruction. While a 'no-op' need not be physically acted upon by the tape transport, the fixed wiring typically propagates the signal regardless.
</p>
+<p>
+ The current state register, concatenated with the output of the read buffer, is used to lookup a row in the next state table. It yields the subsequent state, or an indication that the given input is not found.
+</p>
+
+<p>
+ The current state register is also used to lookup a row in the default next state table while ignoring the read buffer output. An entry for a given state might not exist here either.
+</p>
+
+<p>
+ The 'lookup' operation can be implemented in a number of ways, at the election of the design engineers. In the simplest form a lookup indexes into an array, though this can be inefficient. For such small machines a hash table is an unlikely alternative. More likely is content addressable memory, a programmable logic array, or custom combinational logic.
+</p>
+
+<p>
+ The programmer must provide the state alphabet, the instruction table, and the next state table. To do this, the programmer benefits from understanding how the programmed controller works. When preparing to provide the tables, the programmer can draw a state machine graph. There are two styles of state machine graphs: the Mealy style and the Moore style. The Mealy style has outputs specified on the edges, while the Moore machine has outputs associated with states. They are equivalently expressive, though the Moore style more easily leads to the table values for the programmed controller described in this section.
+</p>
+
+<p>
+ Each defined row of the instruction table specifies a state-specific instruction to be given to the tape transport unit. The instruction will be one from the set: { no-op, step-left, step-right, and write(value) }, where the value parameter for a write instruction is coded directly as part of the instruction.
+</p>
+
+<h3>Control logic</h3>
+
+<p>
+ The following procedure is embodied as further control logic in the Turing Machine. This procedure is fired upon receiving a step pulse. At the time the procedure is entered, the head is stable upon a cell. We list phases so as to avoid any apparent race conditions. This does not dictate to the designers that the clock must have phases, though that isn't excluded either.
+</p>
+
+<p><strong>Deterministic (Uniplex) programmed control procedure</strong></p>
+
+<p><em>Upon each step pulse:</em></p>
+
+<p>Phase 1:</p>
+<ol>
+ <li>read the symbol instance indicated by the head into the read data buffer</li>
+</ol>
+
+<p>Phase 2:</p>
+<ol>
+ <li>lookup the current state in the instruction table</li>
+ <li>lookup the current state concatenated with the read data buffer in the next state table</li>
+ <li>lookup the current state in the default next state table</li>
+</ol>
+
+<p>Phase 3:</p>
+<ol>
+ <li>if the current state is found in the next state table, use the retrieved value as the next state. Otherwise, if the current state is found in the default next state table, use the default state as the next state. Otherwise, use the error state as the next state.</li>
+ <li>if an instruction was retrieved from the instruction table, the tape transport executes it. Otherwise, the tape transport executes the default 'no-op' instruction.</li>
+</ol>
+
+<p>Phase 4:</p>
+<ol>
+ <li>write the next state to the current state register</li>
+ <li>controller remains quiescent waiting for the next step pulse</li>
+</ol>
+
+
+<h3>Theoretical significance</h3>
+
+<p>
+ In common books and papers about the Turing Machine, a step is defined as one step of the programmed controller, i.e. one pass through the four phase procedure given above. Decider proofs ask if the comparator will match the halt state within a finite number of steps. Time complexity proofs take a formulation of step count to reach the halt state, parameterized against the size of the input, and report the order of the highest term as it is asymptotically dominant. Hence we speak of constant, linear, polynomial, and exponential time complexity algorithms. A similar method of analysis, that of memory usage with step count, parameterized against input size, is used for space complexity.
+</p>
+
+<p>
+ For a real machine, the step pulse will be derived from the machine clock. The clock will have a constant period, so there is a constant duration of time that will be the same for each pass through the execution procedure. Thus, if we replace the step count with a count of clock ticks, we will get the same decider and complexity results as we would have from step counts. This fits the definition we have been using for <RT-term-em>inconsequential</RT-term-em>.
+</p>
+
+
+<h2>An alternative: stored program and sequencer</h2>
+
+<p>
+ The <RT-term-em>Universal Turing Machine</RT-term-em>, proposed by Alan Turing, introduced a profound architectural inversion: relocating the defining state tables from hardwired logic, or manually configured patch panels, directly onto the tape itself. This enables replacing the custom programmed controller with a fixed controller that derives its behavior dynamically from the tape data. Consequently, a single, immutable hardware architecture can simulate the execution of any conceivable Turing Machine.
+</p>
+
+<p>
+ In addition, encoding a machine's control logic as parseable data on tape establishes an ontology of analysis, a framework where a machine can analyze another machine to establish some properties the other machine might have. We say 'some' because at least one limitation has been proven. Alan Turing proved that such an analyst cannot in general determine if said other machine has the property that it would halt for any input when run.
+</p>
+
+<p>
+ We can optimize this representation. Instead of storing the state tables verbatim, we can list a sequence of instructions directly on the tape. To achieve this, the architecture expands to support two distinct categories of instructions: the physical tape transport instructions we defined previously, and a newly introduced category of <RT-term-em>control instructions</RT-term-em>. The <RT-term>programmed controller</RT-term> is then replaced with a fixed hardware controller called a <RT-term>sequencer</RT-term>.
+</p>
+
+<p>
+ Because the original state tables allowed for non-linear execution paths, the instruction sequence on the tape cannot always execute in a straight line. Therefore, the architect must include at least two control instructions: a halt instruction and a test-and-branch instruction. The sequencer starts at the first instruction in the program, perhaps at the leftmost cell on the tape, and evaluates it. If it is a control instruction, the sequencer acts upon it directly to alter the flow of execution or stop the machine. Otherwise, if it is a physical instruction for the head unit, the sequencer passes it down to the tape transport.
+</p>
+
+<p>
+ Because we have not yet derived Natural Numbers or memory addresses in this architecture, a test-and-branch instruction cannot jump to a numerical address. Instead, it must operate topologically. A topological branch instruction simply commands the sequencer to scan the tape for a specific target symbol, and resume executing instructions from that physical location.
+</p>
+
+<p>
+ As noted in the prior section, an instruction consists of an instruction code and potentially an argument. There are many choices that can be made in instruction set design. Among those choices, almost all will be inconsequential from a computation-theoretic point of view, but almost all will introduce strict efficiency trade-offs in physical hardware.
+</p>
+---
<p>Data must be an instance of an alphabet symbol or an instance of the empty-symbol.</p>
<p>In addition, we should not forget the implied read command that returns a value from the tape at the start of each step, and buffers it for use by the fixed procedure.</p>
<p><strong>Deterministic (Uniplex) Procedure</strong></p>
-<p><em>Initialize:</em></p>
+<p><em>Setup (manual):</em></p>
<ol>
<li>mount the tape</li>
- <li>place the head on the leftmost cell</li>
- <li>set the current-state to the initial-state</li>
+ <li>push the reset button</li>
+</ol>
+
+<p><em>Initialize (reset procedure):</em></p>
+<ol>
+ <li>step the head left until an 'left of leftmost' error from the tape transport unit, the head will then be on the leftmost cell</li>
+ <li>set the current-state of the provided state machine controller to the initial-state</li>
<li>set the state controller stepping counter to 0</li>
+ <li>upon release of reset, perform 'Step'</li>
</ol>
-<p><em>Take a Step:</em></p>
+<p><em>Step:</em></p>
<ol>
<li>read the symbol instance indicated by the head into the read data buffer</li>
<li>execute the command corresponding to the current-state</li>