CPSC 355: Tutorial 13

Interoperability with C and assignment 5

PhD Student

Fall 2017

Outline

We'll be talking about how to approach assignment 5 (part a) today. It's quite long, so I want you to get started on it sooner rather than later.

Assignment 5

  • Use static/global variables
  • Interoperability with C
  • Using command line arguments (we'll talk about this next day)

Skills needed

Assignment 5

#include <stdio.h>
#include <stdlib.h>

#define QUEUESIZE   8
#define MODMASK     0x7
#define FALSE       0
#define TRUE        1

/* Function Prototypes */
void enqueue(int value);
int dequeue();
int queueFull();
int queueEmpty();
void display();

/* Global Variables */
int queue[QUEUESIZE];
int head = -1;
int tail = -1;

Assignment 5


int main()
{
  int operation, value;
    
  do {
    system("clear");
    printf("### Queue Operations ###\n\n");
    printf("Press 1 - Enqueue, 2 - Dequeue, 3 - Display, 4 - Exit\n");
    printf("Your option? ");
    scanf("%d", &operation);

    switch (operation) {
    case 1:
      printf("\nEnter the positive integer value to be enqueued: ");
      scanf("%d", &value);
      enqueue(value);
      break;
    case 2:
      value = dequeue();
      if (value != -1)
	printf("\nDequeued value is %d\n", value);
      break;
    case 3:
      display();
      break;
    case 4:
      printf("\nTerminating program\n");
      exit(0);
    default:
      printf("\nInvalid option! Try again.\n");
      break;
    }
    printf("\nPress the return key to continue . . . ");
    getchar();
    getchar();
  } while (operation != 4);
    
  return 0;
}

Assignment 5

void enqueue(int value)
{
  if (queueFull()) {
    printf("\nQueue overflow! Cannot enqueue into a full queue.\n");
    return;
  }
  
  if (queueEmpty()) {
    head = tail = 0;
  } else {
    tail = ++tail & MODMASK;
  }
  queue[tail] = value;
}

int dequeue()
{
  register int value;
    
  if (queueEmpty()) {
    printf("\nQueue underflow! Cannot dequeue from an empty queue.\n");
    return (-1);
  }
  
  value = queue[head];
  if (head == tail) {
    head = tail = -1;
  } else {
    head = ++head & MODMASK;
  }
  return value;
}

int queueFull()
{
  if (((tail + 1) & MODMASK) == head)
    return TRUE;
  else
    return FALSE;
}

Assignment 5

int queueEmpty()
{
  if (head == -1)
    return TRUE;
  else
    return FALSE;
}


void display()
{
  register int i, j, count;
    
  if (queueEmpty()) {
    printf("\nEmpty queue\n");
    return;
  }

  count = tail - head + 1;
  if (count <= 0)
    count += QUEUESIZE;
  
  printf("\nCurrent queue contents:\n");
  i = head;
  for (j = 0; j < count; j++) {
    printf("  %d", queue[i]);
    if (i == head) {
      printf(" <-- head of queue");
    }
    if (i == tail) {
      printf(" <-- tail of queue");
    }
    printf("\n");
    i = ++i & MODMASK;
  }   
}

Assignment 5

For part a, you need to implement the functions

void enqueue(int value);
int dequeue();
int queueFull();
int queueEmpty();
void display();

In assembly, in a separate file (a5a.s)

This isn't too bad, let's see how to do this via example

Example

#include <stdio.h>
#include <stdlib.h>

int total_count = 0;
int threshold = 128;

int count_element(int element);

int main(int argc, char *argv) {
    for(int i = 0; i < 15; i++) {
        int value = rand() % 255;

        printf("element %d = %d", i, value);
        if(count_element(value)) {
            printf(" > %d", threshold);
        }
        putchar('\n');
    }
    printf("There were %d elements > %d\n", total_count, threshold);
}

int count_element(int element){
    if(element > threshold) {
        total_count += 1;
        return 1;
    }
    return 0;
}

Example

// File: ex2a.c

#include <stdio.h>
#include <stdlib.h>

int total_count = 0;
int threshold = 128;

int count_element(int element);

int main(int argc, char *argv) {
    for(int i = 0; i < 15; i++) {
        int value = rand() % 255;

        printf("element %d = %d", i, value);
        if(count_element(value)) {
            printf(" > %d", threshold);
        }
        putchar('\n');
    }
    printf("There were %d elements > %d\n", total_count, threshold);
}

Example

// File: ex2b.s
// Author: Joshua Horacsel
// Contains the count element function

.global count_element // The .global count_element statement makes 
                      // count_element visible to main
count_element:
        // Setup a local stack frame
        stp     x29, x30, [sp, -16]!
        mov     x29, sp

        // load the threshold into w1
        adrp    x1, threshold
        add     x1, x1, :lo12:threshold
        ldr     w1, [x1]

        // load total_count into w3
        adrp    x2, total_count
        add     x2, x2, :lo12:total_count
        ldr     w3, [x2]        // keep track of x2 (address to total count)

        // Check if w0 (element) > w1(threshold)
        cmp     w0, w1
        mov     w0, wzr         // set return val to 0
        b.le    end_count       // if it's not, end

        add     w3, w3, 1       // add 1 to total count
        str     w3, [x2]        // store it in x2 (address to total count)
        mov     w0, 1           // set return val to 1

end_count:
        ldp     x29, x30, [sp], 16
        ret

Example

gcc -c ex2a.c
as ex2b.s -o ex2b.o
gcc ex2a.o ex2b.o -o ex2 

To compile:

./ex2

To run:

How to approach A5

Assignment 5 is quite large, so here's how I suggest you should approach part a:

  • Step 1: Copy all the source from the handout into the file "a5aMain.c", create a file "a5a.s".
  • Step 2: Test the functionality of the code
  • Step 3: Choose a function to translate into assembly (other than the main function, that needs to remain in C)
  • Step 4: Comment that function out of a5aMain.c
  • Step 5: Implement that function in a5a.s
  • Step 6: Test functionality of your code (make sure you didn't break anything)
  • Step 7: If there's anything left to write in assembly, go to step 3, otherwise you're done.

Let's do this together for queueEmpty()

Another Example

#include <stdio.h>
#include <stdlib.h>

#define NUM_VALS 10
int value_array[NUM_VALS];

void fill_array();

int main(int argc, char *argv) {
    fill_array();
    for(int i = 0; i < NUM_VALS; i++) {
        printf("value_array[%d] = %d\n", i, value_array[i]);
    }

}

void fill_array() {
    register int i;
    for(i = 0; i < NUM_VALS; i++) {
        value_array[i] = rand() % 255;
    }
}

Again, let's selectively translate this into assembly

Another Example

// File: ex3a.c

#include <stdio.h>
#include <stdlib.h>

#define NUM_VALS 10
int value_array[NUM_VALS];

void fill_array();

int main(int argc, char *argv) {
    fill_array();
    for(int i = 0; i < NUM_VALS; i++) {
        printf("value_array[%d] = %d\n", i, value_array[i]);
    }

}

Another Example

// File: ex3b.s

NUM_VALS = 10
  
.global fill_array
fill_array:
        stp     x29, x30, [sp, -32]!
        mov     x29, sp
        str     x19, [x29, 16]  // Save x19 because we need to use it
        str     x20, [x29, 24]  // Save x20

        mov     w19, wzr        // init the counter to 0

        // load the address of value_array into x1
        adrp    x20, value_array
        add     x20, x20, :lo12:value_array

loop_top:
        bl      rand
        and     w0, w0, 0xFF            // w0 = rand() & 0xFF
        str     w0, [x20, w19, SXTW 2]  // store w0 at address x1 + (w19 << 2)
                                        // i.e. value_array[w19] = w0
        add     w19, w19, 1
loop_test:
        cmp w19, NUM_VALS
        b.lt loop_top

        ldr x19, [x29, 16]      // reload x19 for the calling function
        ldr x20, [x29, 24]      // reload x20 for the calling function
        ldp x29, x30, [sp], 32
        ret

Another Example

gcc ex3a.c ex3b.s -o ex3

Compiling (let's be a bit lazier this time):

./ex3

Running:

This should be all you need for part a of assignment 5

Next Day

An elaboration on things we saw today, and part b of assignment 5.

CPSC 355: Tutorial 13

By Joshua Horacsek

CPSC 355: Tutorial 13

  • 2,072