Please read this page about taking my exams!

## Exam format

**When/where**- During class, here, like normal
- 75 minutes
- it is not going to be “too long to finish”
**no calculator**

**Closed-note**- You may not have any notes, cheat sheets etc. to take the exam
- The open-note thing was just for when we were remote

**Length**- 3 sheets of paper, double-sided
- there are A Number of Questions and I cannot tell you how many because it is not a useful thing to tell you because they are all different kinds and sizes.

**Topic point distribution**- More credit for earlier topics (e.g. memory addresses, arrays, MIPS programming)
- Less credit for more recent ones (e.g. multiplication, division)
- More credit for
**things I expect you to know because of your experience**(labs, project) **VERY ROUGHLY:**- ~15% multiple choice/answer and fill-in-the-blank
- ~20% short answer (just writing stuff)
- ~20% math (bases, ranges, +/-)
- ~20% MIPS ASM (tracing, interpreting, filling in blanks)
- ~15% understanding memory
- ~10% bitfields

**Kinds of questions***A few***multiple choice and/or “pick**(but not many)*n*“- Some
**fill in the blanks**- mostly for
**vocabulary** - or things that I want you to be able to recognize, even if you don’t know the details

- mostly for
**Application**questions about numbers and arithmetic (i.e.*math problems, basically*)- Base conversion
- Interpreting patterns of bits in different ways (signed, unsigned, bitfields, floats etc)
- Unsigned and signed (2’s complement) addition

- Several
**short answer**questions- again, read that page above about answering short answer questions!!

**No writing code from scratch,***but:***tracing**(reading code and saying what it does)**debugging**(spot the mistake)- interpreting
**asm as HLL**code (identifying common asm patterns) **fill in the blanks**(e.g. picking right registers, right branch instructions)

## Things people asked about in the reviews

This is not an exhaustive list of topics on the exam. This is only *what people asked about.*

**CISC vs RISC****CISC:***Complex*Instruction Set Computer- made for
*humans*to write programs directly in assembly

- made for
**RISC:***Reduced*Instruction Set Computer- made for
*compilers*to produce assembly/machine code from high-level languages

- made for
- The differences are really in the name:
**CISC**has*complex,*flexible, multi-step instructions that are great for humans (do more stuff with fewer instructions!) but terrible for performance**RISC**has*reduced,*simple, single-step instructions that are great for compilers (so easy to write algorithms to write RISC code!) but more awkward for humans to write

**Accessing arrays in MIPS**- An array is multiple variables of the
**same type and size, equidistantly spaced apart in memory**- E.g.
`arr: .word 1, 2, 3`

is 3 words/12 bytes of memory; each item of the array is 4 bytes apart because a word is 4 bytes.

- E.g.
- The address of
`A[i]`

is`A + S×i`

where:`A`

is the address of the array (in asm, the label*is*the address)`S`

is the size of one item in bytes (so for`.word`

it’s 4,`.byte`

it’s 1, etc)`i`

is the index you want to access (in asm, typically a register)

- The “long form” of array access looks like:

`# ASSUMING that s0 is the index (maybe we're in a for loop and s0 is the loop counter): la t0, arr # t0 = address of arr mul t1, s0, 4 # t1 = s0 * 4 add t0, t0, t1 # t0 = address of arr + (s0 * 4) # Now you can load/store using (t0) as the address lw a0, (t0) # a0 = arr[s0]`

- The “short form” folds the
`la`

and`add`

into the`lw`

instruction, but you still have to multiply the index:

`mul t1, s0, 4 # t1 = s0 * 4 lw a0, arr(t1) # a0 = arr[s0]`

- The stupid
`arr(t1)`

syntax means “add the address of`arr`

and`t1`

together, and use that as the address to load from”

- An array is multiple variables of the
**The stack**- A region of memory that contains
**information about function calls** *Pushing*puts a value on top of the stack,*popping*removes a value from the top of the stack- A stack is a perfect match for the way function calls work:
- Whenever a function is called, it
*pushes*its**activation record (AR)**-saved registers, local variables in HLLs - Whenever a function is about to return, it
*pops*the AR - ARs are removed in the opposite order from when they are created, so stack is exactly what is needed
- In-progress functions’ data is safe on the stack (in memory)

- Whenever a function is called, it

- A region of memory that contains
**Calling Convention**- Honor system used to let
**multiple functions work together**- Remember that
**all functions share the registers**so this is important!

- Remember that
- Makes them agree on:
- How arguments are passed from caller to callee
- How values are returned from callee to caller
- How control flows from caller to callee, and then back again
- What goes on the stack
- Who is allowed to use which registers, and for what purposes
- Which registers must be preserved across calls, and which can be trashed

- In MIPS, part of this is the
`s`

register contract- If you want to use some
`s`

register`sx`

, you:`push sx`

at the beginning of the function that wants to use it`pop sx`

at the end of the function that wants to use it

- By following this protocol, it’s as if every function gets its
**own**`s`

registers- But
*everyone*has to follow the protocol, or the guarantee is gone!

- But

- If you want to use some

- Honor system used to let
**Endianness**- it is a rule which is used to decide the order of BYTES
- when going from things bigger than a byte to bytes
- or vice versa.

- it comes up in…
**memory**(cause it’s an array of**bytes**)**files**(also arrays of bytes)- networking

**big endian**stores the big end (**most significant byte**) first.- “read it in order”
`0xDEADBEEF`

is stored as`0xDE, 0xAD, 0xBE, 0xEF`

**little endian**does the opposite, stores the**least significant byte**first.- “swap the order”
`0xDEADBEEF`

is stored as`0xEF, 0xBE, 0xAD, 0xDE`

- but
**1-byte values**and**arrays of 1-byte values**are immune to endianness- because they aren’t chopped up

- it is a rule which is used to decide the order of BYTES
**Conversion between hex and binary****4 bits = 1 hex digit (nybble)**- The table is simple - count up in binary from
`0000`

to`1111`

, and count up in hex from`0`

to`F`

next to it. - When going from binary to hexadecimal,
**group the bits into 4***starting from the right (LSB)*- add 0s to the
*left*side as needed to make a group of 4 bits - then each group of 4 bits is 1 hex digit

- add 0s to the

**Conversion from decimal to binary**- I presented one way on the slides, the “long division” method:
**You have to know the binary place values**- From MSB to LSB (left to right):
- If the place value fits into the remainder, put a
`1`

and subtract it off the remainder - Otherwise put a
`0`

- If the place value fits into the remainder, put a

- There’s that other method that involves repeatedly dividing by 2 until you get a quotient of 0, and you
**write every remainder even if it’s a 0,**and the binary representation is the remainders read from top to bottom.- Try doing both methods to see what I mean, if you’re curious.

- I presented one way on the slides, the “long division” method:
**HOW TO DETECT OVERFLOW****AN OVERFLOW OCCURRED IF:**

**Addition****Subtraction****Unsigned**MSB carry out is **1**MSB carry out is **0****Signed**same sign inputs, different sign output same as addition, but *after*negating second input- For signed addition: you get an overflow
**only**if you add two numbers of the same sign and get the opposite sign out (e.g. add two positives, get a negative)- it’s totally possible to add two numbers of the same sign and
*not*have overflow - also if the inputs are opposite signs, then overflow is impossible.

- it’s totally possible to add two numbers of the same sign and
- Remember that detecting overflow is only the first step.
- Once it has been detected, you can respond to it in 3 ways:
**store, ignore,**fall on the**floor**(crash)- in MIPS,
`add/sub`

**crash**on*signed*overflow, and`addu/subu`

**ignore**all overflow. - not all architectures are this limited.

- in MIPS,

- Once it has been detected, you can respond to it in 3 ways:

**Bitwise AND and its uses**- Two main uses:
**masking:**isolating the lowest*n*bits of a number by ANDing with 2^{n}- 1- doing
**fast modulo**by 2^{n}by ANDing with 2^{n}- 1

- Notice both of those are the same operation, just different interpretations
**Do not confuse bitwise AND (**`&`

, works on ints) with logical AND (`&&`

, works on booleans, is lazy)!

- Two main uses:
**Bitfields**- Given the specification for a bitfield, you can determine these for each field:
**Position:**the low bit number (the one on the right)- this indicates how far to shift left/right for encoding/decoding that field

**Size:**high bit + 1 - low bit- this is how many bits the field is

**Mask:**`2^size - 1`

- another way of thinking of it is writing
`size`

1 bits in binary - and then turn that into hex
- e.g. if size = 6, in binary that’s
`11 1111`

(6 1s in a row) - turn that to hex, it’s
`0x3F`

- another way of thinking of it is writing

- Then, to
**decode**(get a field OUT of an encoded bitfield):- shift value right by position and AND with mask
- so,
`field = (encoded >> FIELD_POSITION) & FIELD_MASK`

- e.g. with a position of 7 and a mask of
`0x3F`

,`field = (encoded >> 7) & 0x3F`

- Finally, to
**encode**(put fields together into an encoded bitfield):- shift each field
*left*by position, and*or*them all together - e.g. with 3 fields it might look like
`encoded = (A << 9) | (B << 7) | (C << 0)`

- shift each field

- Given the specification for a bitfield, you can determine these for each field:
**MEMORY****Alignment**- The address of an
*n*-byte value must be a multiple of*n*.- e.g. words are 4 bytes. so, their addresses are multiples of 4 (
`0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C,...`

)

- e.g. words are 4 bytes. so, their addresses are multiples of 4 (
- There are underlying hardware design reasons for this, but some architectures (like MIPS) will
**crash your program**if you don’t respect this rule.

- The address of an
**Zero/sign extension**- when you load a value < 32 bits (byte, half) into a 32-bit register, have to
**extend**it- want to
**preserve the same value,**just represent it with more bits

- want to
`lb/lh`

does sign extension (**copies sign bit (0 OR 1)**to left)`lbu/lhu`

does zero extension (fills extra bits with**0s**)- Does not happen with
`lw`

because you’re loading a 32-bit value into a 32-bit register - same size

- when you load a value < 32 bits (byte, half) into a 32-bit register, have to
**Truncation**- When you store into a half/byte variable,
**only the least significant bits (rightmost bits) of the register are stored** - The rest are truncated (cut off)
`sb`

stores the 8 least significant bits of the register in memory and leaves 24 behind`sh`

stores the 16 least significant bits of the register in memory and leaves 16 behind

- When you store into a half/byte variable,
**Endianness,**but that was discussed above**Does the CPU crash if you**`lw`

a byte or`lb`

a word?**NO!**the only thing it will crash for is address misalignment.- Otherwise it assumes you know what you’re doing and does exactly what you tell it to do.

**The speed of multiplication and division**- Multiplication is made of multiple
**additions** - Subtraction is made of multiple
**subtractions** - Addition is
**commutative and associative,**but**subtraction is not** - This means that the sub-steps of multiplication can be
**reordered**and even**done in parallel** - But the sub-steps of division
**must always be done in order** - SO:
- the slow, sequential multiplication algorithm is
`O(n)`

(n = number of bits) - the fast, parallel multiplication algorithm is
`O(log n)`

- and division is
**always**`O(n)`

- yes, even if you guess with the SRT algorithm

- the slow, sequential multiplication algorithm is

- Multiplication is made of multiple