text: #include <curses.h>
  #include <string.h>
  #include "debug.h"
  #include "data.h"
  #include "execution.h"
  #include "conversion.h"
  
  #define HEADER_Y 1
  #define WINDOW_X 2
  #define WINDOW_Y HEADER_Y + 2
  #define GAP 2
  #define INSTRUCTION_X WINDOW_X
  #define INSTRUCTION_WIDTH 52
  #define REGISTER_X INSTRUCTION_X + INSTRUCTION_WIDTH + GAP
  #define REGISTER_WIDTH 15
  #define REGISTER_LENGTH 16
  #define MEMORY_X REGISTER_X + REGISTER_WIDTH + GAP
  #define MEMORY_WIDTH 28
  #define PSR_X MEMORY_X + MEMORY_WIDTH + GAP
  #define PSR_WIDTH 23
  #define PSR_LENGTH 1
  #define LABEL_WIDTH 16
  #define SIGN_Y 6
  #define SIGN_X 123
  #define SIGN_WIDTH 3
  #define SIGN_LENGTH 3
  #define CONTROLLS_HEADER_Y SIGN_Y + SIGN_LENGTH + 1
  #define CONTROLLS_Y CONTROLLS_HEADER_Y + 2
  #define CONTROLLS_X PSR_X
  #define CONTROLLS_WIDTH PSR_WIDTH
  #define CONTROLLS_LENGTH 7
  #define EXIT_MESSAGE_Y 22
  #define KEY_SPACE 32
  #define KEY_ESCAPE 27
  #define KEY_TAB 9
  
  int debug(memory_array *tab_m, instruction_array *tab_i, reg registers[]) {		//sets up window for debug mode, waits for input from user and calls appropriate functions
  	WINDOW** windows = NULL, ** current_window = NULL;
  	char PSR[64];
  	const char* headers[] = { "INSTRUCTIONS", "REGISTERS", "MEMORY", "PSR", "LAST OPERATION SIGN"};
  	int i = 0, instruction_offset = 0, memory_offset = 0, code = 0, key, last = 0;
  
  	if (tab_i->size == 0) {
  		printf("NO INSTRUCTIONS TO DEBUG");
  		return 0;
  	}
  
  	initscr();
  	noecho();
  	keypad(stdscr, 1);
  	curs_set(0);
  	resize_term(30, 128);
  
  	for (int i = 0; i < 64; i++) PSR[i] = '0';
  	windows = initWindow(tab_m, tab_i, registers, &PSR, headers, tab_m->size);
  	hilighting_on(windows, tab_m, tab_i, registers, PSR, i, instruction_offset, memory_offset);
  	refresh_all(windows, tab_m, tab_i, instruction_offset, memory_offset);
  	initControlls();
  	if (tab_i->size <= REGISTER_LENGTH && tab_m->size > REGISTER_LENGTH)
  		current_window = windows[2];
  	else
  		current_window = windows[0];
  
  	while ((key = getch()) != KEY_ESCAPE) {
  		switch (key) {
  		case KEY_LEFT:
  			if (tab_i->size > REGISTER_LENGTH && tab_m->size > REGISTER_LENGTH) {
  				current_window = windows[0];
  				highlight_header(strlen(headers[0]), strlen(headers[2]), 'i');
  			}
  			break;
  		case KEY_RIGHT:
  			if (tab_i->size > REGISTER_LENGTH && tab_m->size > REGISTER_LENGTH) {
  				current_window = windows[2];
  				highlight_header(strlen(headers[0]), strlen(headers[2]), 'm');
  			}
  			break;
  		case KEY_UP:
  			if (tab_i->size > REGISTER_LENGTH || tab_m->size > REGISTER_LENGTH) {
  				if (current_window == windows[0] && instruction_offset > 0)
  					prefresh(current_window, --instruction_offset, 0, WINDOW_Y, INSTRUCTION_X, WINDOW_Y + min(tab_i->size, REGISTER_LENGTH) - 1, INSTRUCTION_X + INSTRUCTION_WIDTH - 1);
  				if (current_window == windows[2] && memory_offset > 0)
  					prefresh(current_window, --memory_offset, 0, WINDOW_Y, MEMORY_X, WINDOW_Y + min(tab_m->size, REGISTER_LENGTH) - 1, MEMORY_X + MEMORY_WIDTH - 1);
  			}
  			break;
  		case KEY_DOWN:
  			if (tab_i->size > REGISTER_LENGTH || tab_m->size > REGISTER_LENGTH) {
  				if (current_window == windows[0] && instruction_offset + min(tab_i->size, REGISTER_LENGTH) < tab_i->size)
  					prefresh(current_window, ++instruction_offset, 0, WINDOW_Y, INSTRUCTION_X, WINDOW_Y + min(tab_i->size, REGISTER_LENGTH) - 1, INSTRUCTION_X + INSTRUCTION_WIDTH - 1);
  				if (current_window == windows[2] && memory_offset + min(tab_m->size, REGISTER_LENGTH) < tab_m->size)
  					prefresh(current_window, ++memory_offset, 0, WINDOW_Y, MEMORY_X, WINDOW_Y + min(tab_m->size, REGISTER_LENGTH) - 1, MEMORY_X + MEMORY_WIDTH - 1);
  			}
  			break;
  		case KEY_SPACE:
  			if (i < tab_i->size) {
  				last = i;
  				if (!(code = step(&tab_m, &tab_i, registers, &PSR, &i))) {
  					update(windows, tab_i, tab_m, registers, PSR, last, instruction_offset, memory_offset);
  					if (i < tab_i->size) {
  						hilighting_on(windows, tab_m, tab_i, registers, PSR, i, instruction_offset, memory_offset);
  						autoscroll_instruction(i, &instruction_offset, tab_i->size, windows[0]);
  						autoscroll_memory(i, &memory_offset, tab_m->size, tab_i, registers, windows[2]);
  					}
  					else show_exit_message(code);
  					refresh_all(windows, tab_m, tab_i, instruction_offset, memory_offset);
  				}
  				else show_exit_message(code);
  			}
  			else show_exit_message(code);
  			break;
  		case KEY_TAB:
  			while (i < tab_i->size) {
  				last = i;
  				if (!(code = step(&tab_m, &tab_i, registers, &PSR, &i))) {
  					update(windows, tab_i, tab_m, registers, PSR, last, instruction_offset, memory_offset);
  				}
  				else break;
  			}
  			show_exit_message(code);
  			refresh_all(windows, tab_m, tab_i, instruction_offset, memory_offset);
  			break;
  		default:
  			break;
  		}
  	}
  
  	endwin();
  	return code;
  }
  
  
  WINDOW** initWindow(memory_array *tab_m, instruction_array *tab_i, reg registers[], char(*PSR)[], const char* headers[]) {	//initializes debug mode window
  	WINDOW** windows = (WINDOW**)malloc(5 * sizeof(WINDOW*));
  	char a[17], b[5];
  
  	windows[0] = newpad(tab_i->size, INSTRUCTION_WIDTH);
  	windows[1] = newwin(REGISTER_LENGTH, REGISTER_WIDTH, WINDOW_Y, REGISTER_X);
  	windows[2] = newpad(tab_m->size, MEMORY_WIDTH);
  	windows[3] = newwin(PSR_LENGTH, PSR_WIDTH, WINDOW_Y, PSR_X);
  	windows[4] = newwin(SIGN_LENGTH, SIGN_WIDTH, SIGN_Y, SIGN_X);
  
  	if (tab_i->size > REGISTER_LENGTH && tab_m->size > REGISTER_LENGTH) attron(A_STANDOUT);
  	mvprintw(HEADER_Y, (INSTRUCTION_WIDTH - strlen(headers[0])) / 2 + INSTRUCTION_X, headers[0]);
  	if (tab_i->size > REGISTER_LENGTH && tab_m->size > REGISTER_LENGTH) attroff(A_STANDOUT);
  	mvprintw(HEADER_Y, (REGISTER_WIDTH - strlen(headers[1])) / 2 + REGISTER_X, headers[1]);
  	mvprintw(HEADER_Y, (MEMORY_WIDTH - strlen(headers[2])) / 2 + MEMORY_X, headers[2]);
  	mvprintw(HEADER_Y, (PSR_WIDTH - strlen(headers[3])) / 2 + PSR_X, headers[3]);
  	mvprintw(SIGN_Y + SIGN_LENGTH / 2, PSR_X, headers[4]);
  
  	box_border(min(tab_i->size, REGISTER_LENGTH), INSTRUCTION_WIDTH, WINDOW_Y, INSTRUCTION_X);
  	box_border(REGISTER_LENGTH, REGISTER_WIDTH, WINDOW_Y, REGISTER_X);
  	box_border(min(tab_m->size, REGISTER_LENGTH), MEMORY_WIDTH, WINDOW_Y, MEMORY_X);
  	box_border(PSR_LENGTH, PSR_WIDTH, WINDOW_Y, PSR_X);
  	box_border(SIGN_LENGTH, SIGN_WIDTH, SIGN_Y, SIGN_X);
  
  	for (int i = 0; i < tab_i->size; i++) {
  		instruction_node tmp = tab_i->tab[i];
  		if (tmp.command[1] == 'R')
  			mvwprintw(windows[0], i, 0, "%-16s %-2s %-2d,%-16d  %-2s %c%c", tmp.label, tmp.command, tmp.arg1, tmp.arg2, encode(tmp.command), dec(tmp.arg1), dec(tmp.arg2));
  		else if (tmp.arg_label[0] == '\0') {
  			snprintf(a, 17, "%d(%d)", tmp.offset, tmp.arg2);
  			snprintf(b, 5, "%04X", tmp.offset);
  			mvwprintw(windows[0], i, 0, "%-16s %-2s %-2d,%-16s  %-2s %c%c %-5s", tmp.label, tmp.command, tmp.arg1, a, encode(tmp.command), dec(tmp.arg1), dec(tmp.arg2), add_gap(b));
  		}
  		else if (tmp.command[0] == 'J') {
  			snprintf(b, 5, "%04X", tmp.offset);
  			mvwprintw(windows[0], i, 0, "%-16s %-2s %-19s  %-2s %c%c %-5s", tmp.label, tmp.command, tmp.arg_label, encode(tmp.command), dec(tmp.arg1), dec(tmp.arg2), add_gap(b));
  		}
  		else {
  			snprintf(b, 5, "%04X", tmp.offset);
  			mvwprintw(windows[0], i, 0, "%-16s %-2s %-2d,%-16s  %-2s %c%c %-5s", tmp.label, tmp.command, tmp.arg1, tmp.arg_label, encode(tmp.command), dec(tmp.arg1), dec(tmp.arg2), add_gap(b));
  		}
  	}
  
  	for (int i = 0; i < REGISTER_LENGTH; i++) {
  		snprintf(a, 9, "%08X", registers[i].value);
  		mvwprintw(windows[1], i, 0, "%-2d: %-11s", i, add_gap(a));
  	}
  	
  
  	for (int i = 0; i < tab_m->size; i++) {
  		memory_node tmp = tab_m->tab[i];
  		char a[9];
  
  		if (tmp.is_constant == '0') {
  			mvwprintw(windows[2], i, 0, "%-16s ~~ ~~ ~~ ~~", tmp.label);
  		}
  		else {
  			snprintf(a, 9, "%08X", tmp.value);
  			mvwprintw(windows[2], i, 0, "%-16s %-11s", tmp.label, add_gap(a));
  		}
  	}
  
  	snprintf(a, 17, "%016X", tab_m->size * 4);
  	mvwprintw(windows[3], 0, 0, "%s", add_gap(a));
  
  	mvwprintw(windows[4], SIGN_LENGTH / 2, SIGN_WIDTH / 2, "%c", (*PSR)[4]);
  
  	return windows;
  }
  
  void box_border(int length, int width, int y, int x) {		//draws border around given window coordinates
  	mvaddch(y - 1, x - 1, ACS_ULCORNER);
  	mvaddch(y - 1, x + width, ACS_URCORNER);
  	mvaddch(y + length, x - 1, ACS_LLCORNER);
  	mvaddch(y + length, x + width, ACS_LRCORNER);
  	for (int i = 0; i < length; i++) {
  		mvaddch(y + i, x - 1, ACS_VLINE);
  		mvaddch(y + i, x + width, ACS_VLINE);
  	}
  	for (int i = 0; i < width; i++) {
  		mvaddch(y - 1, x + i, ACS_HLINE);
  		mvaddch(y + length, x + i, ACS_HLINE);
  	}
  }
  
  void highlight_header(int instruction_header_length, int memory_header_length, char which) {		//hilights memory or instruction header and unhilights the other one
  	if (which == 'i') {
  		mvchgat(HEADER_Y, (INSTRUCTION_WIDTH - instruction_header_length) / 2 + INSTRUCTION_X, instruction_header_length, A_STANDOUT, 0, NULL);
  		mvchgat(HEADER_Y, (MEMORY_WIDTH - memory_header_length) / 2 + MEMORY_X, memory_header_length, A_NORMAL, 0, NULL);
  	}
  	else {
  		mvchgat(HEADER_Y, (INSTRUCTION_WIDTH - instruction_header_length) / 2 + INSTRUCTION_X, instruction_header_length, A_NORMAL, 0, NULL);
  		mvchgat(HEADER_Y, (MEMORY_WIDTH - memory_header_length) / 2 + MEMORY_X, memory_header_length, A_STANDOUT, 0, NULL);
  	}
  	refresh();
  }
  
  void update(WINDOW** windows, instruction_array *tab_i, memory_array* tab_m, reg registers[], char PSR[], int line, int instruction_offset, int memory_offset) {		//updates all windows with new data and unhilights previous instruction data
  	char a[17];
  
  	mvwchgat(windows[0], line, 0, INSTRUCTION_WIDTH, A_NORMAL, 0, NULL);
  
  	wattron(windows[1], A_NORMAL);
  	if (tab_i->tab[line].command[0] != 'J') {
  		snprintf(a, 9, "%08X", registers[tab_i->tab[line].arg1].value);
  		mvwprintw(windows[1], tab_i->tab[line].arg1, 0, "%-2d: %-11s", tab_i->tab[line].arg1, add_gap(a));
  	}
  	if (tab_i->tab[line].command[1] == 'R') {
  		snprintf(a, 9, "%08X", registers[tab_i->tab[line].arg2].value);
  		mvwprintw(windows[1], tab_i->tab[line].arg2, 0, "%-2d: %-11s", tab_i->tab[line].arg2, add_gap(a));
  	}
  	wattroff(windows[1], A_NORMAL);
  
  	wattron(windows[2], A_NORMAL);
  	if (tab_i->tab[line].command[0] != 'J' && tab_i->tab[line].command[1] != 'R') {
  		mvwchgat(windows[2], (registers[tab_i->tab[line].arg2].value + tab_i->tab[line].offset) / 4, 0, LABEL_WIDTH + 1, A_NORMAL, 0, NULL);
  		if (tab_m->tab[(registers[tab_i->tab[line].arg2].value + tab_i->tab[line].offset) / 4].is_constant == '1') {
  			snprintf(a, 9, "%08X", tab_m->tab[(registers[tab_i->tab[line].arg2].value + tab_i->tab[line].offset) / 4].value);
  			mvwprintw(windows[2], (registers[tab_i->tab[line].arg2].value + tab_i->tab[line].offset) / 4, LABEL_WIDTH + 1, "%s", add_gap(a));
  		}
  		else {
  			mvwprintw(windows[2], (registers[tab_i->tab[line].arg2].value + tab_i->tab[line].offset) / 4, LABEL_WIDTH + 1, "~~ ~~ ~~ ~~");
  		}
  	}
  	wattroff(windows[2], A_NORMAL);
  
  	strncpy_s(a, 17, &PSR[48], 16);
  	mvwprintw(windows[3], 0, 0, "%s", add_gap(a));
  
  	mvwprintw(windows[4], SIGN_LENGTH / 2, SIGN_WIDTH / 2, "%c", PSR[4]);
  }
  
  void hilighting_on(WINDOW** windows, memory_array* tab_m, instruction_array* tab_i, reg registers[], char PSR[], int line, int instruction_offset, int memory_offset) {		//hilights new instruction data
  	mvwchgat(windows[0], line, 0, INSTRUCTION_WIDTH, A_STANDOUT, 0, NULL);
  	if (tab_i->tab[line].command[0] != 'J')
  		mvwchgat(windows[1], tab_i->tab[line].arg1, 0, REGISTER_WIDTH, A_STANDOUT, 0, NULL);
  	if (tab_i->tab[line].command[1] == 'R')
  		mvwchgat(windows[1], tab_i->tab[line].arg2, 0, REGISTER_WIDTH, A_STANDOUT, 0, NULL);
  	if (tab_i->tab[line].command[0] != 'J' && tab_i->tab[line].command[1] != 'R')
  		mvwchgat(windows[2], (registers[tab_i->tab[line].arg2].value + tab_i->tab[line].offset) / 4, 0, MEMORY_WIDTH, A_STANDOUT, 0, NULL);
  }
  
  void initControlls() {		//initializes window showing information about controlls in debug mode
  	WINDOW* window = newwin(CONTROLLS_LENGTH, CONTROLLS_WIDTH, CONTROLLS_Y, CONTROLLS_X);
  	const char* messages[] = { "SPACE - next instruct", "UP - scroll up", "DOWN - scroll down", "LEFT - select instruct", "RIGHT - select memory", "ESC - exit", "TAB - skip all"};
  	const char* header = "CONTROLLS";
  	mvprintw(CONTROLLS_HEADER_Y, (CONTROLLS_WIDTH - strlen(header)) / 2 + CONTROLLS_X, header);
  	box_border(CONTROLLS_LENGTH, CONTROLLS_WIDTH, CONTROLLS_Y, CONTROLLS_X);
  	for (int i = 0; i < CONTROLLS_LENGTH; i++)
  		mvwprintw(window, i, 0, messages[i]);
  	refresh();
  	wrefresh(window);
  }
  
  void autoscroll_instruction(int i, int *instruction_offset, int instruction_length, WINDOW* window) {		//scrolls instruction window if hilighted instruction is off-screen
  	while (i < *instruction_offset && *instruction_offset > 0) (*instruction_offset)--;
  	while (i >= *instruction_offset + min(instruction_length, REGISTER_LENGTH) && *instruction_offset < instruction_length) (*instruction_offset)++;
  	prefresh(window, *instruction_offset, 0, WINDOW_Y, INSTRUCTION_X, WINDOW_Y + min(instruction_length, REGISTER_LENGTH) - 1, INSTRUCTION_X + INSTRUCTION_WIDTH - 1);
  }
  
  void autoscroll_memory(int i, int *memory_offset, int memory_length, instruction_array *tab_i, reg registers[], WINDOW* window) {		//scrolls memory window if hilighted variable is off-screen
  	if (tab_i->tab[i].command[0] != 'J' && tab_i->tab[i].command[1] != 'R') {
  		while ((registers[tab_i->tab[i].arg2].value + tab_i->tab[i].offset) /4 < *memory_offset && *memory_offset > 0) (*memory_offset)--;
  		while ((registers[tab_i->tab[i].arg2].value + tab_i->tab[i].offset) / 4 >= *memory_offset + min(memory_length, REGISTER_LENGTH) && *memory_offset < memory_length) (*memory_offset)++;
  		prefresh(window, *memory_offset, 0, WINDOW_Y, MEMORY_X, WINDOW_Y + min(memory_length, REGISTER_LENGTH) - 1, MEMORY_X + MEMORY_WIDTH - 1);
  	}
  }
  
  void show_exit_message(int code) {		//prints exit message to window
  	if (code)
  		mvprintw(EXIT_MESSAGE_Y, WINDOW_X, "%s (press ESCAPE to exit)", code_to_msg(code));
  	else
  		mvprintw(EXIT_MESSAGE_Y, WINDOW_X, "PROGRAM ENDED SUCCESSFULLY (press ESCAPE to exit)");
  }
  
  void refresh_all(WINDOW** windows, memory_array* tab_m, instruction_array* tab_i, int instruction_offset, int memory_offset) {		//refreshes main window and all subwindows exept controlls window
  	refresh();
  	prefresh(windows[0], instruction_offset, 0, WINDOW_Y, INSTRUCTION_X, WINDOW_Y + min(tab_i->size, REGISTER_LENGTH) - 1, INSTRUCTION_X + INSTRUCTION_WIDTH - 1);
  	wrefresh(windows[1]);
  	prefresh(windows[2], memory_offset, 0, WINDOW_Y, MEMORY_X, WINDOW_Y + min(tab_m->size, REGISTER_LENGTH) - 1, MEMORY_X + MEMORY_WIDTH - 1);
  	wrefresh(windows[3]);
  	wrefresh(windows[4]);
  }