Crafting a ‘demo’ program A ‘walk-through’ of the program development cycle for an example in assembly language Our purpose • We want to illustrate the steps that a Linux program needs to take when modifying the normal ‘canonical mode’ terminal behavior • We want to write it in assembly language • Our Project #2 involves something similar • Here we want to ‘Keep It Simple’ (KISS) • But yet we want to show the essentials • We might see new Pentium instructions Just a tiny change • • • • • • • Users can normally ‘cancel’ a program They can do it by typing <CONTROL>-C It’s important for stopping “infinite loops” The system sends a ‘termination’ signal This avoids the need for a system ‘reboot’ But we can ‘reprogram’ this tty capability We just turn off a bit in the ‘c_lflag’ field Our ‘nocbreak.s’ demo • • • • • • • Step 1: get the terminal’s initial settings Step 2: save a copy of these settings Step 3: modify the ISIG bit in ‘c_lflag’ field Step 4: install the ‘modified’ tty settings Step 5: let user do some keyboard input Step 6: reinstall original terminal settings Step 7: Quit (i.e., return control to Linux) Step 1: Get ‘tty’ settings • • • • We can use the ‘tcgetattr()’ function It’s part of the system’s runtime library Use ‘man’ command to see how it’s called Here’s its function prototype: int tcgetattr( int fileno, struct termios &tty ); • We can call it using assembly language: – Push the arguments (in right-to-left order) – Call the function: call tcgetattr – Discard the arguments from the stack Here’s the code .section ttywrk: .space .section pushl pushl call addl .data 60 # for ‘termios’ object .text $ttywrk $0 tcgetattr $8, %esp # push the address # push device-ID # call runtime library # discard arguments Step 2: copy the object • • • • • • • We can setup a loop to perfortm copying Loop can copy structure one byte at a time Total number of bytes is loop-count (60) Put source-address into a cpu register Put dest’n-address into a cpu register Advance addresses as each byte is copied Use ‘loop’ opcode to decrement-and-jump Here’s the data .section .data ttysav: .space 60 ttywrk: .space 60 # original structure # our working copy And here’s the code .section .text movl $ttywrk, %esi movl $ttysav, %edi movl $60, %ecx nxmv: movb (%esi), %al movb %al, (%edi) incl %esi incl %edi loop nxmv # setup source addr # setup dest’n addr # setup loop-count # label the loop-body # copy src byte to AL # copy AL to dest’n # advance src-addr # advance dst-addr # finish coping bytes Step 3: modify the flag-bit • • • • • • • We know where the ‘c_lflag’ field is It’s starts 12 bytes into ‘termios’ structure We got this info from our ‘ttyinfo.cpp’ demo Similarly we can find that ISIG bit is bit #1 We want to “reset” this bit (i.e.,clear it to 0) We could use a bitwise AND operation But Pentium offers us another way (BTR) Here’s the code .equ ISIG, 0 .section ttywrk: .space .data 60 # symbolic constant # for termios object .section .text movl $12, (%edx) # offset for ‘c_lflag’ btr #ISIG, ttywrk(%edx) # resets bit #1 Brief digression • Other Pentium bit-manipulations: BTS (bit-set) BTR (bit-reset) BTC (bit-complement) BT (bit-test) • These operations all have this “side effect”: – the previous bit-value gets transferred to the CF-bit (Carry Flag) within the Pentium’s EFLAGS register • Why? So you can use JC (or JNC) afterward Step 4: Install new behavior • We can use the ‘tcsetattr()’ function • Use ‘man tcsetattr’ to see how its called • Requires three function arguments: – Device’s ID-number (i.e., 0 for keyboard) – A flag-value, to specify buffer-flushing – The address of the new ‘termios’ object • As usual, these arguments have to be pushed in reverse (i.e., right-to-left) order Here’s the function-call .section .text pushl $ttywrk # address of the object pushl $TCSAFLUSH # flag-value pushl $0 # keyboard’s device-ID call tcsetattr # call to runtime library addl $12, %esp # rebalance stack # NOTE: Similar code is used later in step 6 Step 5: Try new tty behavior • • • • • • • We want to let the user type some input In particular, we want to test <CTRL>-C We’ve changed the normal tty handling Prove <CTRL>-C won’t stop the program Find out what the new response will be We need program to ‘read’ from keyboard Can use ‘read()’ from the runtime library How ‘read()’ works • Function’s prototype shows 3 arguments: – Device ID-number (e.g., 0 for the keyboard) – Address for an input-buffer (we create buffer) – Maximum number of bytes that will be read • In canonical mode, the ‘read()’ call won’t return until either the user hits <ENTER> or the maximum number of bytes have been transferred into the input-buffer So here’s the ‘read()’ call .section .data inchar: .space 1 .section pushl pushl pushl call addl .text $1 $inchar $0 read $12, %esp # room for 1 byte # maximum bytes # buffer’s address # keyboard’ ID # call to C library # discard arguments Testing for <EACAPE>-code • • • • • • We needed a way to stop the program Can’t quit by using <CONTROL>-C now Our solution: quit by hitting <ESCAPE> So program needs to test for its ascii-code ASCII-code for ESCAPE-key equals 0x1B Our loop includes a compare-and-branch Testing for the ‘exit’ condition .section inchar: .space .data 1 .section .text again: … cmpb $0x1B, inchar jne again # buffer for user input # user typed ESC? # no, reenter loop # otherwise, fall through to next instruction A ‘tweak’ for esthetics • When we tested our ‘nocbreak’ demo, we did not like the screen’s appearance • Our program’s final output was ‘garbled’ by the subsequent command-shell prompt • We wanted to make the output prettier • So we added a additional code-fragment • A ‘newline’ control-code gets printed after each keypress by the user (using ‘write()’) In-class exercises • Programmers can choose among several ways of accomplishing a particular task • Example: there’s more than one way to copy a 60-byte data-structure from one place in memory to another • We don’t have to do it one-byte-at-a-time • We don’t have to use both %esi and %edi • Try doing the copying in some other ways Using a common array-index • Here’s an idea for a different copying scheme .section .text xorl %esi, %esi # array-index movl $30, %ecx # word-count nxwm: movw ttywrk(%esi), %ax # fetch word movw %ax, ttysav(%esi) # store word addl $2, %esi # next word index loop nxwm Using a ‘scaled’ array-index # we use a ‘scaled index’ to do array-addressing .section .text xorl %esi, %esi # clear to zero movl $30, %ecx # loop-count nxwd: movw ttywrk( , %esi, 2), %ax movw %ax, ttysav( , %esi, 2) incl %esi # increment index loop nxwd: Exercise • Try to devise the ‘most efficient’ method you can think of for copying the 60-bytes • But what does ‘most efficient’ mean? – Using the fewest assembly statements? – Using the fewest cpu regisers? – Executing the fewest loop-iterations? • Will your “solution” be the same no matter what you think “most efficient” means?