#show link: set text(blue) #set text(font: "Calibri") #show raw: set text(font: "Fira Code") #set table.cell(breakable: false) #set table(stroke: (x, y) => ( left: if x > 0 { .1pt }, top: if y == 1 { 0.5pt } else if y > 1 { 0.1pt }, )) #set page(margin: (x: .25in, y: .25in)) #let solve(solution) = { block( inset: 3pt, stroke: blue + .3pt, fill: rgb(0, 149, 255, 15%), radius: 4pt, )[#solution] } #let solvein(solution) = { let outset = 3pt h(outset) box( outset: outset, stroke: blue + .3pt, fill: rgb(0, 149, 255, 15%), radius: 4pt, )[#solution] } #let note(content) = { block( inset: 3pt, stroke: luma(20%) + .3pt, fill: luma(95%), radius: 4pt, )[#content] } #let notein(content) = { let outset = 3pt h(outset) box( outset: outset, stroke: luma(20%) + .3pt, fill: luma(95%), radius: 4pt, )[#content] } #align(center)[ = CS 3843 Computer Organization HW 7\ #underline[Price Hiller] *|* #underline[zfp106] ] #line(length: 100%, stroke: .25pt) 1. (18 points) Show your work. Show how you compute memory addresses by using the effective memory address computation. Assume the following values are stored at the indicated memory addresses and registers: #table( inset: ( x: 15pt, ), columns: (auto, auto, auto, auto), table.header( [Address], [Value], [Register], [Value], ), [`0x100`], [`0xFF`], [`%rax`], [`0x100`], [`0x104`], [`0xAB`], [`%rcx`], [`0x1`], [`0x108`], [`0x13`], [`%rdx`], [`0x3`], [`0x10C`], [`0x11`], [``], [``], ) Fill in the following table showing the values for indicated operands: #table( inset: ( x: 15pt, ), columns: (auto, auto, auto), table.header( [Operand], [Value], [Work], ), [`%rax`], solve[`0x100`], note[ + `%rax` + `0x100` ], [`0x104`], solve[`0xAB`], note[ + `0x104` + `0xAB` ], [`$0x108`], solve[`0x108`], note[ + `$0x108` + `0x108` ], [`(%rax)`], solve[`0xFF`], note[ + `(%rax)` + `(0x100)` + `0xFF` ], [`4(%rax)`], solve[`0xAB`], note[ + `4(%rax)` + `(%rax + 4)` + `(0x100 + 4)` + `(0x104)` + `0xAB` ], [`9(%rax,%rdx)`], solve[`0x11`], note[ + `9(%rax,%rdx)` + `(%rax + %rdx + 9)` + `(0x100 + 0x3 + 9)` + `(0x10C)` + `0x11` ], [`260(%rcx,%rdx)`], solve[`0x11`], note[ + `260(%rcx,%rdx)` + `(%rcx + %rdx + 260)` + `(%rcx + %rdx + 0x104)` + `(0x1 + 0x3 + 0x104)` + `(0x108)` + `0x13` ], [`0xFC(,%rcx,4)`], solve[`0x104`], note[ + `0xFC(,%rcx,4)` + `(%rcx * 4 + 0xFC)` + `(0x1 * 4 + 0xFC)` + `(0x4 + 0xFC)` + `(0x100)` + `0xFF` ], [`(%rax,%rdx,4)`], solve[`0x10C`], note[ + `(%rax,%rdx,4)` + `(%rax + %rdx * 4)` + `(0x100 + 0x3 * 4)` + `(0x100 + 0xC)` + `(0x10C)` + `0x11` ], ) 2. (12 points) Explain for each line why you chose a certain suffix such as `b`, `w`, `l`, or `q`. For each of the following lines of assembly language, determine the appropriate instruction suffix based on the operands. (For example `mov` can be written as `movb`, `movw`, `movl`, or `movq`.) #align(center)[#note[My reasoning? My reasoning is that ChatGPT said so. _Just kidding ;)_.]] #table( inset: ( x: 15pt, ), columns: (auto, auto), table.header( [Line], [Reasoning], ), [`mov`#solvein[`l`] ` %eax, (%rsp)`], [#note[Because we're moving into a memory location pointed to by `%rsp`, we want to focus on the size of `%eax`. `%eax` is a 4 byte register, so we want an instruction that operates on double words, so our suffix will be an `l`.]], [`mov`#solvein[`w`] ` (%rax), %dx`], [#note[We're moving from a value pointed to by an 8 byte register into a 2 byte register. 2 bytes is a word, so we want to use a single word length instruction which is suffixed by a `w`.]], [`mov`#solvein[`b`] ` $0xFF, %bl`], [#note[Here we have a raw hex value representing a singe byte, `1111 1111`, and we're moving it into a single byte length register so we'll want to use a byte length instruction which is suffixed by a `b`.]], [`mov`#solvein[`b`] ` (%rsp,%rdx,4), %dl`], [#note[Importantly, we are moving a value into `%dl` which is a single byte length; therefore, we want to use a single byte length instruction which is suffixed by a `b`.]], [`mov`#solvein[`q`] ` (%rdx), %rax`], [#note[Since we're moving a value pointed to by an 8 byte register into an 8 byte register, we'll want to use a quad word length instruction which is suffixed by a `q`.]], [`mov`#solvein[`w`] ` %dx, (%rax)`], [#note[Because `(%rax)` is is a larger register than `%dx`, the only register that matters here is `%dx`. `%dx` can store 2 bytes which is a single word, so we should use a word length instruction, that begin suffixed by `w`.]], ) 3. (14 points) Explain for answer for each line: Each of the following lines of code generates an error message when we invoke the assembler. Explain the what is wrong with each line. #table( inset: ( x: 15pt, ), columns: (auto, auto), table.header( [Code], [Why it's wrong], ), [`movb $0xF, (%ebx)`], [#solve[This shouldn't be moving `$0xF` into the memory pointed to by `%ebx` as `(%ebx)` cannot be used as an address register in this context. Removing the parentheses would make it somewhat more valid.]], [`movl %rax, (%rsp)`], [#solve[The `l` suffix is intended to move 4 bytes, both `%rax` and `%rsp` are 8 bytes in size and thus `movl` is moving the wrong number of bytes.]], [`movw (%rax),4(%rsp)`], [#solve[Same issue as the previous one. The `w` suffix is intended to move 2 bytes, but both `%rax` and `%rsp` are 8 bytes in size and thus we have a operand size mismatch.]], [`movb %al,%sl`], [#solve[There is no such register `%sl`, there's `%si` or `%sil`, but not `%sl`.]], [`movq %rax,$0x123`], [#solve[We cannot have an *immediate* as a destination to move a value into.]], [`movl %eax,%rdx`], [#solve[Attempting to move a 4 byte value into an 8 byte register may lead to data loss. We should use an opcode that clears the upper bits of `%rdx`, like `movslq`.]], [`movb %si, 8(%rbp)`], [#solve[This is attempting to move a 2 byte value using a 1 byte opcode. Instead of `movb` we should use `movw`.]], ) 4. (12 points) Show your work for each instruction. Show how you compute effective memory addresses. Assume the following values are stored at the indicated memory addresses and registers: #table( inset: ( x: 15pt, ), columns: (auto, auto, auto, auto), table.header( [Address], [Value], [Register], [Value], ), [`0x100`], [`0xFF`], [`%rax`], [`0x100`], [`0x108`], [`0xAB`], [`%rcx`], [`0x1`], [`0x110`], [`0x13`], [`%rdx`], [`0x3`], [`0x118`], [`0x11`], [``], [``], ) Fill in the following table showing the effects of the following instructions, in terms of both register or memory location what will be updated and the resulting value: #table( inset: ( x: 15pt, ), columns: (auto, auto, auto, auto), table.header( [Instruction], [Destination], [Value], [Work], ), [`addq %rcx,(%rax)`], solve[`0x100`], solve[`0x100`], note[ + `(%rax) = %rcx + (%rax)` + `(0x100) = 0x1 + (0x100)` + `(0x100) = 0x1 + 0xFF` + `(0x100) = 0x100` ], [`subq %rdx,8(%rax)`], solve[`0x108`], solve[`0xA8`], note[ + `8(%rax) = 8(%rax) - %rdx` + `(0x100 + 8) = (0x100 + 8) - 0x3` + `(0x108) = (0x108) - 0x3` + `(0x108) = 0xAB - 0x3` + `(0x108) = 0xA8` ], [`imulq $16,(%rax,%rdx,8)`], solve[`0x118`], solve[`0x110`], note[ + `(%rax,%rdx,8) = (%rax,%rdx,8) * 16` + `(0x100,0x3,8) = (0x100,0x3,8) * 16` + `(0x100 + 0x3 * 8) = (0x100 + 0x3 * 8) * 16` + `(0x100 + 0x18) = (0x100 + 0x18) * 16` + `(0x118) = (0x118) * 16` + `(0x118) = 0x11 * 16` + `(0x118) = 0x110` ], [`incq 16(%rax)`], solve[`0x110`], solve[`0x14`], note[ + `16(%rax) = 16(%rax) + 1` + `16(0x100) = 16(0x100) + 1` + `(0x100 + 16) = (0x100 + 16) + 1` + `(0x110) = (0x110) + 1` + `(0x110) = 0x13 + 1` + `(0x110) = 0x14` ], [`decq %rcx`], solve[`%rcx`], solve[`0x0`], note[ + `%rcx = %rcx - 1` + `%rcx = 0x1 - 1` + `%rcx = 0x0` ], [`subq %rdx,%rax`], solve[`%rax`], solve[`0xFD`], note[ + `%rax = %rax - %rdx` + `%rax = 0x100 - 0x3` + `%rax = 0xFD` ], )