A programmable computer

CS-A1120 Programming 2

Lukas Ahrenberg

Department of Computer Science
Aalto University

(Based on material by Petteri Kaski and Tommi Junttila)

Comment your code!

Always, but especially in this round!

Every line of it!

  • For your own sake
    • Assembly language is hard to read, even for the person who wrote it just minutes ago
  • For your fellow students and the sanity of course TAs
    • TAs will tell you to comment the code before they help you (I've instructed them to)

Next lecture (April 3) is exceptionally in TU2

After this round, you

  • can explain the principle architecture stored-program computers and automatic execution
  • have familiarity with the role of assembly languages
  • can implement simple algorithms in the course's armlet assembly language
  • are aware of computational universality

A programmable machine, this far

Binary representation of data

road-to-prog-1.png

A programmable machine, this far

Combinatorial logic

road-to-prog-2.png

A programmable machine, this far

Combinatorial logic

road-to-prog-3.png

A programmable machine, this far

Combinatorial logic

road-to-prog-4.png

A programmable machine, this far

Sequential logic, for state

road-to-prog-5.png

Repetition: the ALU

ALU = Arithmetic Logic Unit

ALU-with-immed.png

  • $0$7 are registers, internal state
  • immed_in allows data input
  • operation encodes which operation to perform
  • L, A, B are desination (L) and source registers (A, B)

What would a program for the ALU be?

  • A sequence of inputs to operation, L, A, B (total 16 bits = one word), and if needed immed_in (16 bits)
  • How could this be stored?
    • I could have it written down on a sheet of paper
    • Then flick switches corresponding to the bits of the input registers (or plug cables)
    • Then clock the ALU and repeat for the next instruction

Boooring (and slow) - let's get the machine to automatically read the next instruction

Let's programs in memory!

memory-module.png

memory-twoword.png

  • A memory with mem_addr size \(n\) can hold \(2^n\) words of data
    • In the case of armlet \(n=16\)
  • Internally, each memory word retains its state (e.g. using feedback) until it is updated by a write
  • Conceptually a 'sequence' of words
    • The memory address can be thought of as the 'index'

So what is a Program?

  • A program is a sequence of instructions (bits) in the memory
  • The machine executes the program automatically, one instruction at a time
  • Machine code

datapath.png

armlet-machine-code.png

Automatic execution?

(Using sequential logic)

  1. Load instructions from memory, one at a time
  2. Direct the instructions to the data path

Control & execution unit

armlet-dp-and-ceu.png

(Armlet is an example of a von Neumann architecture)

Two new ('special') registers: Program Counter and Processor Status

Keeping track of which instruction to fetch

(And in which order)

  • The Program Counter (PC)
    • A special register which tells from which memory address to load the instruction
  • By default, PCt+1 = PCt + 1 (That is, fetch word from next memory location.)
  • But, the default order can be changed by the instructions themselves
    • jump : PCt+1 = <address>
    • branch PCt+1 = <address> if some condition holds, else PCt + 1
  • Processor Status (PS)
    • A special register, the value of which are updated by e.g. comparison operations

Together, PS & PS allows for loops and conditional execution!

Briefly: Halting and Trapping

(How does the processor know when to stop executing?)

  • Special instruction to halt execution (without the possibility of continuing)
  • Instruction to trap execution - breaking execution temporarily (useful for debugging)

Programming

  • Symbolic machine code : "assembly language"

Armlet Machine code(s) Armlet assembly
0111000010000111 sub $2, $0, $7
0000001011011100 0011000000111001 ior $3, $1, 12345

  • Inputs are bits → all instructions (and data) are numbers: Machine Code
  • First computers were programmed by inputting these numbers directly
  • Very hard for a person to follow
  • Assembly language is a one-to-one translation of the instructions into a more human-readable form
  • Processor architecture families has their own assembly dialect
  • First assembly language written by Kathleen Booth

The machine code compiler (assembler)

machine-code-compiler.png

What can we do?

  • Instructions for the Data Path
    • ALU : Boolean operations, addition, and subtraction
    • Memory Interface Unit : load and store data
  • Instructions for control execution : compare values and branch

Conceptually (on armlet):

  • Eight var's (the registers) and one huge array (the memory)
  • Operations that the processor can do in one tick

Our first armlet program

Scala

var t = 10
var i = 1
var s = 0

while t != 0 do
  s = s + i
  i = i + 1
  t = t - 1
// Now the sum is in 's'

Armlet

        mov $0, 10      # $0 = 10
        mov $1, 1       # $1 = 1
        mov $2, 0       # $2 = 0
@loop:                  # This is a label
        cmp $0, 0       # $0 == 0 ? (PS)
        beq >done       # if PS is eq
        add $2, $2, $1  # $2 = $2 + $1
        add $1, $1, 1   # $1 = $1 + 1
        sub $0, $0, 1   # $0 = $0 - 1
        jmp >loop       # Jump back 
@done:                  # Another label
        hlt             # Stop, sum in $2
        

Armlet assembly language - basic syntax

  • Commands on one of the forms
    • kwd $L, $A, $B
    • kwd $L, $A, I
    • kwd $L, $A
    • kwd $A
    • kwd
  • Where
    • kwd is a three letter abbreviation for the instruction
    • $L is the register where the destination should be stored (e.g. $3)
    • $A and $B are input registers
    • I is immediate data (i.e. a constant such as 235 or 0xF3)
  • Comments are written after #
  • For example:
    • add $4, $0, $3 # Add values in $0 and $3 and store result in $4

Data Path Instruction set

armlet-ALU-mem-instr.png

(From the armlet quick reference)

Branching and jumping

  • Recall : Programs are data
    • When the program is assembled it is translated into binary words (data)
    • When a machine runs the program it is loaded into memory, like any other data
    • Program Counter (PC) is set to the address of its first instruction, and execution begins by interpreting data at that location as an instruction
  • By changing the PC we can change which instruction is read next
    • PC is set using the jump and branch instructions

armlet-machine-code.png

Jumping

  • A jump moves the argument address to the program counter register
  • The effect is that the program execution continues from the new address
  • Not to have us deal with absolute addresses the assembler allows for labels in the code
    • A label captures the address where it is written
      • @done: on a line will have the assembler associate that memory address with the label 'done'
   jmp >target    # jump to label @target

   # ... some code here ...

@target:
   # ... some further code here ...

(Note that you can jump both forwards and backwards in the code)

Comparison & branching

  • Often we want to jump depending if some condition holds
  • This is a two-step process
    1. Perform the comparison (register vs register or register vs constant)
    2. Jump depending on result of comparison (branching)
  • The jump is executed if and only if the branching condition is true
  • Otherwise the execution follows the default order
   cmp $7,0     # compare value in $7 to 0
   beq >done    # jump to label @done if left == right

   # ... some code here ...

@done:
   # ... and more code here ...

Comparison & branching – internally

  • cmp updates the Processor Status register
  • That is PS holds the result of the most recent cmp call
  • Branching instructions reads PS and jumps accordingly
  • Therefore there can be code in-between the cmp and the branch!

Instructions affecting execution flow

armlet-branching.png

(From the armlet quick reference)

A perhaps unfair couple of questions…

  1. What does the following program do?
  2. What are the contents of $0, $1, $2 when execution halts?
 mov $0, 18
 mov $1, 15
 cmp $0, $1
 blt >labA
 mov $2, $0
 jmp >labB
@labA:
 mov $2, $1
@labB:
 lsl $2, $2, 1
 hlt

A bit of help

instruction what it does
blt X Jump to X if left < right (in prev. cmp)
cmp $A, $B Compare A and B
hlt Hale execution
jmp X Jump (go to) X
lsl $L, $A, $B L = A << B (left shift)
mov $L, I L = A

Comment your code!

Trying it out

In the exercise material, run the program launchTicker.scala in the armlet sub-project.

Or, open a console: sbt armlet/console and do

import armlet._
new Ticker()

armlet-ticker-window.png

Loading and storing data

  • Most algorithms will work on more data than can fit into CPU registers
  • What is data?
    • Bits that we want to be able to load into registers and perform operations on
  • Need to load/store them from memory
    • loa and sto can be used to move words between memory and registers
    • (Think if it as loa x,y is x = mem(y), and sto x,y is mem(y) = x.)
  • Note: Memory is used for both program and data
    • The difference is how it is interpreted
    • There are no separated memory areas (at least in our architecture)
    • If PC contains a specific address, the word there will be interpreted as an instruction!

(In armlet PC always starts at 0, so data should always come after program.)

Example: Loading data from memory

# Computes (to $0) the sum of the data and then halts.

# Let us first set things up ...
      mov $0, 0                    # the sum starts with 0
      mov $2, >length_of_my_data   # set up memory address where to get length
      loa $2, $2                   # load the length from memory to $2
      mov $1, >my_data             # set up memory address where to get the data

# Now let us loop through the data and accumulate the sum ...
@sum_loop:
      cmp $2, 0                    # compare length with 0
      beq >done                    # ... branch to label @done if $2 == 0
      loa $3, $1                   # load a data item from memory
      add $0, $0, $3               # accumulate the sum in $0
      add $1, $1, 1                # advance to next data item
      sub $2, $2, 1                # decrement length by one
      jmp >sum_loop                # continue the summation

# ... until we are done
@done:
      hlt                          # the processor stops here

# Our data follows the program code in the binary ...
@length_of_my_data:
      %data 20
@my_data:
      %data 296, 573, 291, 415, 825, 674, 426, 632, 793, 701, 884, 1, 989, 912, 254, 869, 462, 296, 767, 220

(%data is a directive - an assembly helper tool; %data 20 will translate the literal 20 to binary and write it as data to memory)

More examples in the course notes

Can we express more in Scala than in Assembly language?

  • In principle no
    • All 'high level' programs are translated to machine code
  • In practice yes
    • Abstractions, data types, programming constructs…
    • You can be more productive in high level languages

So why learn to program assembly?

  • 'Close to metal'
    • Instructions directly to the hardware
  • Unleash the most power of hardware
  • The "mother language"

But, it is hard work as a programmer…

(Comment your code!)

The Mystery of the Computer

Solved?

  • Yes, in a sense,
    • We can (conceptually) build a programmable machine
    • if we can implement the building blocks of sequential logic
  • And, we simulated this by building a model of hardware in software on a computer…

Universality

A programmable machine can, with right programming, simulate another programmable machine

  • Computing and programming are concepts which are independent of the device and the programming language

Fineprint: the physical device (for example amount of memory) and the efficiency of the simulation restricts the above statement slightly

  • Emulators
  • Virtualization
  • Containers
  • But, computers can also be built in other systems as long as these have power equivalent to sequential logic…
  • For instance, computers inside Minecraft

The things we have omitted…

sw-hw.png

How can two program run simultaneously? How can a program wait for input? Print output? How are high level programs translated into machine code?

Summary

  • In the end, everything is bits
  • Combinational logic can be used to transform bits
  • Sequential logic allows us to keep a state
  • Computer Architecture allows us to engineer a machine
  • A programmable machine executes a program automatically
  • A program can change its control flow (branching) depending on state
  • Programming languages (like Scala) are "only" software tools which helps us harness the hardware to do what we as programmers want

Exercises

  1. Evaluating an expression
  2. Word operations
  3. Range of values in a data array
  4. Most frequent value in a data array
  5. Multiplication
  6. Remainder computation
  7. Challenge problem: 32-bit remainder computation
  8. Challenge problem: Greatest common divisor
  • Comment your code
    • For your own sake
    • Assistants may not be able to help you before you do!
  • Write an algorithm (or even a Scala program)
  • Work through the notes examples
  • Use the 'ticker' to debug your code
  • There are no unit tests this round
    • But exercises come with examples in the comments