lc4-disassembler / lc4_memory.c
lc4_memory.c
Raw
#include "lc4_memory.h"

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

#define GET_LAST_4(X) ((X) & 0xF)
#define GET_FIRST_4(X) ((X >> 12) & 0xF)

int add_to_list(row_of_memory **head, short unsigned int address,
                short unsigned int contents) {
  row_of_memory *node = search_address(*head, address);

  // if a node with the specified address already exists in the LinkedList, this
  // function MUST update the contents field and take no other action
  if (node) {
    node->contents = contents;
    return 0;
  }

  // allocate space for a new node
  node = (row_of_memory *)malloc(sizeof(row_of_memory));

  // return -1 if malloc fails
  if (!node) {
    fprintf(stderr, "Memory allocation failed\n");
    return -1;
  }

  // set the address and contents fields based on the function arguments
  // and set the label and assembly fields to NULL
  node->address = address;
  node->contents = contents;
  node->label = NULL;
  node->assembly = NULL;
  node->next = NULL;

  // if the head pointer is NULL, this function MUST set the newly created node
  // as the head of the LinkedList.
  if (!*head) {
    *head = node;
  }
  // Otherwise, this function MUST insert the
  // newly created node into the LinkedList based on the address field in
  // ascending order
  else {
    row_of_memory *current = *head;
    row_of_memory *previous = NULL;

    // traverse the list to find the correct position for the new node
    while (current != NULL && current->address < address) {
      previous = current;
      current = current->next;
    }

    // insert at head (new node has smallest address)
    if (!previous) {
      node->next = *head;
      *head = node;
    }
    // insert at middle or tail
    else {
      previous->next = node;
      node->next = current;
    }
  }

  return 0;
}

row_of_memory *search_address(row_of_memory *head, short unsigned int address) {
  if (!head) {
    return NULL;
  }

  row_of_memory *curr = head;
  while (curr) {
    if (curr->address == address) {
      return curr;
    }
    curr = curr->next;
  }

  return NULL;
}

row_of_memory *search_opcode(row_of_memory *head, short unsigned int opcode) {
  if (!head) {
    return NULL;
  }

  row_of_memory *curr = head;
  while (curr) {
    /* traverse linked list until node is found with matching opcode in the most
       significant 4 bits AND "assembly" field of node is NULL */
    if (!curr->assembly && GET_FIRST_4(curr->contents) == GET_LAST_4(opcode)) {
      return curr;
    }
    curr = curr->next;
  }

  return NULL;
}

void print_list(row_of_memory *head) {
  if (!head) {
    return;
  }

  // headers: more space is given to label based on the provided sols
  printf("%-20s%-20s%-20s%-20s\n", "<label>", "<address>", "<contents>",
         "<assembly>");

  row_of_memory *curr = head;
  while (curr) {
    unsigned short opcode = GET_FIRST_4(curr->contents);

    printf("%-20s%-20.04X%-20.04X%-20s\n", (curr->label) ? curr->label : "",
           curr->address, curr->contents,
           (curr->assembly && opcode == 0x0001) ? curr->assembly : "");

    curr = curr->next;
  }

  return;
}

int delete_list(row_of_memory **head) {
  /* delete entire list node by node */
  /* set the list head pointer to NULL upon deletion */
  if (!*head) {
    return 0;
  }

  row_of_memory *curr = *head;
  while (curr) {
    // need to free extra space allocated for assembly and label
    if (curr->assembly) {
      free(curr->assembly);
    }
    if (curr->label) {
      free(curr->label);
    }

    curr = curr->next;
    free(*head);
    *head = curr;
  }

  return 0;
}