Crafting a ‘demo’ program A ‘walk-through’ of the program in assembly language

advertisement
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?
Download