/*
 * Example 1: Pseudo-code for CPU address translation hardware.
 */

#define OFFSET_BITS 12 /* a page is 4096 bytes */

int *PTR = ???; /* CPU Page Table Register: points to current PTE array */

translate(int virtaddr) {
  int virtpage = (virtaddr >> OFFSET_BITS);
  int offset = (virtaddr & ((1 << OFFSET_BITS) - 1));
  int physpage = PTR[virtpage]; /* load the PTE */
  int physaddr = (physpage << OFFSET_BITS) + offset;
  return(physaddr);
}

/*
 * Example 2: System call pseudo-code.
 */

/* CPU state */
int PC;  /* program counter */
int PTR; /* Page Table Register */
int kernel_mode; /* privilege flag */
int *SP; /* stack pointer */
#define PUSH(x) ((*--SP) = x)
#define POP() (*(SP++))

/* user code */
user_syscall(arg) {
  PUSH(arg);         /* 1. Save arguments so kernel can find them */
  PUSH(PC+4);        /* 2. Save the return PC (i.e. step 19) */
  kernel_mode = 1;   /* 3. Switch to kernel address space */
  Flush TLB;         /* 4. The address mapping cache isn't valid any more */
  PC = kern_syscall; /* 5. Jump into the kernel */
  int ret = POP();   /* 19. Retrieve the system call result */
  return(ret);
}

/* kernel code */
struct process {
  ... // standard thread stuff.
  int kernel_stack[]; /* process' kernel stack */
  int *saved_SP;
  int saved_PC;
  int saved_PTR;      /* pointer to process' PTE array */
};


kernel_syscall() {
  process *p = current_process;

  p->saved_PC = read_user(SP);          /* 6. Save the program counter */
  p->saved_SP = SP + 1;                 /* 7. Save user stack pointer */
  p->saved_PTR = PTR;                   /* 8. Save address space */
  SP = p->kernel_stack + kstack_size;   /* 9. Switch to kernel stack */

  int arg = read_user(p->saved_SP);     /* 10. Retrieve argument */
  int ret = syscall_code();             /* 11. Execute the system call */
  write_user(p->saved_SP, ret);         /* 12. Replace arg with result */

  SP = p->saved_SP;                     /* 13. Restore user's stack pointer */
  int pc = p->saved_PC;                 /* 14. Grab user program counter */
  PTR = p->saved_PTR;                   /* 15. Set up user address space */
  kernel_mode = 0;                      /* 16. Switch to user address space */
  Flush TLB;                            /* 17. TLB isn't valid any more */
  PC = pc;                              /* 18. Jump to user code */
}

read_user(address) {
  Allow kernel to read user space by emulating translate().
}

/*
 * Example 3: Context switch pseudo-code, in the kernel.
 */

struct process {
  ... // standard thread stuff.
  int *saved_SP;
  int saved_PC;
  int *saved_PTR; /* pointer to process' PTE array */
}

context_switch(process *p) {
  SP = p->saved_SP;                 /* Restore user's stack pointer */
  int pc = p->saved_PC;             /* Grab user program counter */
  PTR = p->saved_PTR;               /* Set up user address space */
  kernel_mode = 0;                  /* Switch to user address space */
  Flush TLB;                        /* TLB isn't valid any more */
  PC = pc;                          /* Jump to user code */
}

