KoMo2  1.0.0
A modern ARM emulator GUI.
kcmd.cpp
1 
11 #include "kcmd.h"
12 #include <ctype.h>
13 #include <fcntl.h>
14 #include <math.h>
15 #include <signal.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/poll.h>
20 #include <sys/signal.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <termios.h>
24 #include <unistd.h>
25 #include <algorithm>
26 #include <iomanip>
27 #include <iostream>
28 #include <regex>
29 #include <string>
30 #include <unordered_map>
31 #include <vector>
32 #include <sstream>
33 #include <thread>
34 #include <mutex>
35 #include <sys/wait.h>
36 #ifdef __APPLE__
37 #include <mach-o/dyld.h>
38 #endif
39 
43 constexpr int SOURCE_BYTE_COUNT = 4;
44 
48 constexpr int SOURCE_FIELD_COUNT = 4;
49 
53 constexpr int SOURCE_TEXT_LENGTH = 100;
54 
58 constexpr int IN_POLL_TIMEOUT = 1000;
59 
63 constexpr int OUT_POLL_TIMEOUT = 100;
64 
68 constexpr int ADDRESS_BUS_WIDTH = 4;
69 
73 constexpr int MAX_NUMBER_OF_BREAKPOINTS = 32;
74 
75 // Communication pipes
81 int emulator_PID;
82 
83 std::thread *t1, *t2;
84 std::mutex mtx;
85 
90  public:
94  unsigned char addressA[ADDRESS_BUS_WIDTH];
95 
101  unsigned char addressB[ADDRESS_BUS_WIDTH] = {0xFF, 0XFF, 0XFF, 0XFF};
102 
108  unsigned char dataA[8] = {0};
109 
115  unsigned char dataB[8] = {0};
116 
122  unsigned int misc = 0xFFFFFFFF;
123 };
124 
128 enum class BoardInstruction : unsigned char {
129  // General commands
130  START = 0xB0,
131  WOT_U_DO = 0x20,
132  STOP = 0x21,
133  CONTINUE = 0x23,
134  RESET = 0x04,
135 
136  // Terminal read/write
137  FR_WRITE = 0x12,
138  FR_READ = 0x13,
139 
140  // Breakpoint read/write
141  BP_WRITE = 0x30,
142  BP_READ = 0x31,
143  BP_SET = 0x32,
144  BP_GET = 0x33,
145 
146  // Register read/write
147  GET_REG = 0x5A,
148  SET_REG = 0x52, // Unused
149 
150  // Memory read/write
151  GET_MEM = 0x4A,
152  SET_MEM = 0x40,
153 };
154 
161 inline unsigned char operator|(BoardInstruction l, unsigned char r) {
162  return static_cast<unsigned char>(l) | r;
163 }
164 
169  public:
174 
179 
183  bool hasData;
184 
188  unsigned int address;
189 
193  int dataSize[4];
194 
198  int dataValue[4];
199 
203  char* text;
204 };
205 
209 class sourceFile {
210  public:
215 
220 };
221 
226 
227 // ! Forward declaring auxiliary load functions
228 
229 // Workers
230 
231 inline void flushSourceFile();
232 inline const bool readSourceFile(const char* const);
233 inline const ClientState getBoardStatus();
234 inline const std::array<unsigned char, 64> readRegistersIntoArray();
235 constexpr const int disassembleSourceFile(SourceFileLine*, unsigned int);
236 constexpr const bool moveSrc(bool firstFlag, SourceFileLine** src);
237 inline const std::string generateMemoryHex(SourceFileLine** src,
238  const uint32_t s_address,
239  int* const increment,
240  const int currentAddressI,
241  unsigned char (*memdata)[52]);
242 
243 // Low level sending
244 
245 inline void sendNBytes(int, int);
246 inline void sendChar(unsigned char);
247 inline void sendCharArray(int, unsigned char*);
248 
249 // Low level receiving
250 
251 inline const int getNBytes(int*, int);
252 inline const int getChar(unsigned char*);
253 inline const int getCharArray(int, unsigned char*);
254 
255 // Breakpoints
256 
257 constexpr const int getNextFreeBreakpoint(const int);
258 inline const bool getBreakpointStatus(unsigned int*, unsigned int*);
259 inline const bool getBreakpointDefinition(unsigned int, BreakpointInfo*);
260 inline void setBreakpointStatus(unsigned int, unsigned int);
261 inline void setBreakpointDefinition(unsigned int, BreakpointInfo*);
262 inline const std::unordered_map<u_int32_t, bool> getAllBreakpoints();
263 
264 // Helpers
265 
266 constexpr void copyStringLiterals(int, unsigned char*, unsigned char*);
267 constexpr const int numericStringSubtraction(const unsigned char* const,
268  const unsigned char* const);
269 constexpr const int numericStringToInt(int, const unsigned char* const);
270 constexpr void numericStringAndIntAddition(unsigned char* const, int);
271 inline const std::string integerArrayToHexString(int,
272  unsigned char* const,
273  const bool = false);
274 constexpr const char getLeastSignificantByte(const int);
275 
283 void Jimulator::compileJimulator(std::string pathToBin,
284  const char* const pathToS,
285  const char* const pathToKMD) {
286  char *file_name, *fnoext, *tmp;
287  size_t len;
288  int pid = -1;
289 
290  close(1);
291  dup2(compilerCommunication[1], 1);
292  close(2);
293  dup2(compilerCommunication[1], 2);
294 
295  file_name = strdup(pathToS);
296  tmp = strrchr(file_name, '/');
297  fnoext = strchr(file_name, '.');
298 
299  *tmp = 0;
300  *fnoext = 0;
301  pid = fork();
302 
303  if(!pid) {
304  int dfd = open("/dev/null", O_RDWR);
305  close(1);
306  dup2(dfd, 1);
307 #ifdef __APPLE__
308  execlp(pathToBin.append("/aasm.sh").c_str(), "aasm.sh", file_name, tmp+1, (char*)0);
309 #else
310  const char *p = pathToBin.append("/aasm").c_str();
311  char *cmd = (char*)malloc(0x500);
312  sprintf(cmd, "%s -lk %s %s", p, pathToKMD, pathToS);
313  system(cmd);
314  exit(0);
315 #endif
316  }
317  wait(NULL);
318 }
319 
326 const bool Jimulator::loadJimulator(const char* const pathToKMD) {
327  flushSourceFile();
328  return readSourceFile(pathToKMD);
329 }
330 
335 void Jimulator::startJimulator(const int steps) {
336  if (checkBoardState() == ClientState::NORMAL ||
337  Jimulator::checkBoardState() == ClientState::BREAKPOINT) {
338  sendChar(static_cast<unsigned char>(BoardInstruction::START));
339  sendNBytes(steps, 4); // Send step count
340  }
341 }
342 
347  if (Jimulator::checkBoardState() == ClientState::NORMAL ||
348  Jimulator::checkBoardState() == ClientState::BREAKPOINT) {
349  sendChar(static_cast<unsigned char>(BoardInstruction::CONTINUE));
350  }
351 }
352 
357  sendChar(static_cast<unsigned char>(BoardInstruction::STOP));
358 }
359 
364  sendChar(static_cast<unsigned char>(BoardInstruction::RESET));
365 }
366 
372 const bool Jimulator::setBreakpoint(const uint32_t addr) {
373  unsigned int wordA = 0, wordB = 0;
374  unsigned char address[ADDRESS_BUS_WIDTH] = {0};
375 
376  // Unpack address to byte array
377  for (int i = 0; i < ADDRESS_BUS_WIDTH; i++) {
378  address[i] = getLeastSignificantByte(addr >> (8 * i));
379  }
380 
381  // Reads the breakpoints
382  if (not getBreakpointStatus(&wordA, &wordB)) {
383  return false;
384  }
385 
386  // Checks to see if a breakpoint exists at this address and turns it off if so
387  for (int i = 0; i < MAX_NUMBER_OF_BREAKPOINTS; i++) {
388  if (((wordA >> i) & 1) != 0) {
389  BreakpointInfo bp;
390 
391  // Gets breakpoint i from the list;
392  // checks if that breakpoint is set for current address;
393  // turns it off if so
394  if (getBreakpointDefinition(i, &bp) &&
395  (numericStringSubtraction(address, bp.addressA) == 0)) {
396  setBreakpointStatus(0, 1 << i);
397  return false;
398  }
399  }
400  }
401 
402  // See if there are any more breakpoints to be set, return if not
403  int temp = (~wordA) & wordB;
404  if (temp == 0) {
405  return false;
406  }
407 
408  // Set the breakpoint
409  BreakpointInfo bp;
410  copyStringLiterals(ADDRESS_BUS_WIDTH, address, bp.addressA);
411 
412  int i = getNextFreeBreakpoint(temp);
413  setBreakpointDefinition(i, &bp);
414  return true;
415 }
416 
422 const ClientState Jimulator::checkBoardState() {
423  const auto board_state = getBoardStatus();
424 
425  // Check and log error states
426  switch (board_state) {
427  case ClientState::RUNNING_SWI:
428  break;
429  case ClientState::RUNNING:
430  break;
431  case ClientState::STEPPING:
432  break;
433  case ClientState::MEMFAULT:
434  break;
435  case ClientState::BUSY:
436  break;
437  case ClientState::FINISHED:
438  break;
439  case ClientState::BREAKPOINT:
440  break;
441  default:
442  return ClientState::NORMAL;
443  break;
444  }
445 
446  return board_state;
447 }
448 
453 const std::array<std::string, 16> Jimulator::getJimulatorRegisterValues() {
454  auto bytes = readRegistersIntoArray();
455 
456  std::array<std::string, 16> ret; // vector of strings
457 
458  // Loop through the array
459  for (long unsigned int i = 0; i < ret.size(); i++) {
460  ret[i] = integerArrayToHexString(4, &bytes[i * 4], true);
461  }
462 
463  return ret;
464 }
465 
471  unsigned char string[256]; // Arbitrary size
472  unsigned char length = 1;
473 
474  std::string output("");
475 
476  while (length > 0) {
477  sendChar(static_cast<unsigned char>(BoardInstruction::FR_READ));
478  sendChar(0); // send the terminal number
479  sendChar(32);
480  getChar(&length); // get length of message
481 
482  // non-zero received from board - not an empty packet
483  if (length != 0) {
484  getCharArray(length, string); // Store the message
485  string[length] = '\0';
486  output.append((char*)string);
487  }
488  }
489 
490  return output;
491 }
492 
499 const bool Jimulator::sendTerminalInputToJimulator(const unsigned int val) {
500  unsigned int key_pressed = val;
501  unsigned char res = 0;
502 
503  // Sending keys to Jimulator
504  if (((key_pressed >= ' ') && (key_pressed <= 0x7F)) ||
505  (key_pressed == '\n') || (key_pressed == '\b') || (key_pressed == '\t') ||
506  (key_pressed == '\a')) {
507  sendChar(static_cast<unsigned char>(
508  BoardInstruction::FR_WRITE)); // begins a write
509  sendChar(0); // tells where to send it
510  sendChar(1); // send length 1
511  sendChar(key_pressed); // send the message - 1 char currently
512  getChar(&res); // Read the result
513  return true;
514  }
515 
516  return false;
517 }
518 
525 std::array<Jimulator::MemoryValues, 13> Jimulator::getJimulatorMemoryValues(
526  const uint32_t s_address) {
527  constexpr int count = 13; // How many values displayed in the memory window
528  constexpr int bytecount = count * ADDRESS_BUS_WIDTH; // bytes to read
529 
530  // Bit level hacking happening here - converting the integer address into
531  // an array of characters.
532  unsigned char* p = (unsigned char*)&s_address;
533  unsigned char currentAddressS[ADDRESS_BUS_WIDTH] = {p[0], p[1], p[2], p[3]};
534  currentAddressS[0] &= -4; // Normalise address down
535 
536  // Reading data into arrays!
537  unsigned char memdata[bytecount];
538  sendChar(static_cast<unsigned char>(BoardInstruction::GET_MEM));
539  sendCharArray(ADDRESS_BUS_WIDTH, currentAddressS);
540  sendNBytes(count, 2);
541  getCharArray(bytecount, memdata);
542 
543  SourceFileLine* src = NULL;
544  bool firstFlag = false;
545 
546  // Moves our src line to the relevant line of the src file
547  if (source.pStart != NULL) {
548  src = source.pStart; // Known to be valid
549  while ((src != NULL) && ((src->address < s_address) || not src->hasData)) {
550  src = src->next;
551  }
552 
553  // We fell off the end; wrap to start
554  if (src == NULL) {
555  src = source.pStart;
556  firstFlag = true;
557 
558  // Find a record with some data
559  while ((src != NULL) && not src->hasData) {
560  src = src->next;
561  }
562  }
563  }
564 
565  // ! Building an array of memory values from here
566  // Data is read into this array
567  std::array<Jimulator::MemoryValues, 13> readValues;
568  const auto bps = getAllBreakpoints();
569 
570  // Iterate over display rows
571  for (long unsigned int i = 0; i < readValues.size(); i++) {
572  int increment = 0;
573  unsigned int currentAddressI =
574  numericStringToInt(ADDRESS_BUS_WIDTH, currentAddressS);
575  readValues[i].address = currentAddressI;
576  readValues[i].hex = std::string("00000000");
577  readValues[i].disassembly = std::string("...");
578 
579  // Generate the hex
580  if (src != NULL && currentAddressI == src->address) {
581  readValues[i].disassembly =
582  std::regex_replace(std::string(src->text), std::regex(";.*$"), "");
583  readValues[i].hex = generateMemoryHex(&src, s_address, &increment,
584  currentAddressI, &memdata);
585 
586  firstFlag = moveSrc(firstFlag, &src);
587  }
588 
589  // Find if a breakpoint is set on this line
590  // if the iterator is not at the end of the map, the breakpoint was found
591  // and can be set
592  auto iter = bps.find(readValues[i].address);
593  if (iter != bps.end()) {
594  readValues[i].breakpoint = true;
595  }
596 
597  // Calculate where the next address is and move the address there
598  increment = disassembleSourceFile(src, currentAddressI);
599  numericStringAndIntAddition(currentAddressS, increment);
600  }
601 
602  return readValues;
603 }
604 
605 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //
606 // !!!!!!!!!! Functions below are not included in the header file !!!!!!!!!! //
607 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //
608 
615 constexpr const int getNextFreeBreakpoint(const int v) {
616  int i = 0;
617  for (; (((v >> i) & 1) == 0); i++)
618  ;
619 
620  return i;
621 }
622 
629 constexpr const char getLeastSignificantByte(const int val) {
630  return val & 0xFF;
631 }
632 
638 constexpr const int rotateRight1Byte(const int val) {
639  return val >> 8;
640 }
641 
647 constexpr const int rotateLeft1Byte(const int val) {
648  return val << 8;
649 }
650 
686 inline const bool getBreakpointStatus(unsigned int* wordA,
687  unsigned int* wordB) {
688  sendChar(static_cast<unsigned char>(BoardInstruction::BP_GET));
689  return not((getNBytes((int*)wordA, 4) != 4) ||
690  (getNBytes((int*)wordB, 4) != 4));
691 }
692 
699 constexpr const bool moveSrc(bool firstFlag, SourceFileLine** src) {
700  do {
701  if ((*src)->next != NULL) {
702  (*src) = (*src)->next;
703  } else {
704  if (not firstFlag) {
705  (*src) = source.pStart;
706  firstFlag = true;
707  } else {
708  (*src) = NULL;
709  }
710  }
711  } while (((*src) != NULL) && not(*src)->hasData);
712 
713  return firstFlag;
714 }
715 
729 inline const std::string generateMemoryHex(SourceFileLine** src,
730  const uint32_t s_address,
731  int* const increment,
732  const int currentAddressI,
733  unsigned char (*memdata)[52]) {
734  std::string hex = "";
735 
736  for (int i = 0; i < SOURCE_FIELD_COUNT; i++) {
737  if ((*src)->dataSize[i] > 0) {
738  char spaces[5] = {0};
739 
740  // Get the data string
741  auto data = integerArrayToHexString(
742  (*src)->dataSize[i],
743  &((*memdata)[currentAddressI - s_address + *increment]));
744 
745  int j = 0;
746  for (; j < (*src)->dataSize[i]; j++) {
747  spaces[j] = ' ';
748  }
749 
750  // Append to the string
751  spaces[j] = '\0';
752  hex += data;
753  hex += spaces;
754  }
755 
756  // Increase the increment
757  *increment = *increment + (*src)->dataSize[i];
758  }
759 
760  return hex;
761 }
762 
773 inline const bool getBreakpointDefinition(unsigned int breakpointlistIndex,
774  BreakpointInfo* bp) {
775  sendChar(static_cast<unsigned char>(BoardInstruction::BP_READ));
776  sendChar(breakpointlistIndex); // send the number of the definition
777 
778  if ((2 != getNBytes((int*)&(bp->misc), 2)) ||
779  (4 != getCharArray(4, bp->addressA)) ||
780  (4 != getCharArray(4, bp->addressB)) ||
781  (8 != getCharArray(8, bp->dataA)) || (8 != getCharArray(8, bp->dataB))) {
782  return false;
783  } else {
784  return true;
785  }
786 }
787 
799 constexpr const int numericStringSubtraction(const unsigned char* const s1,
800  const unsigned char* const s2) {
801  int ret = 0;
802 
803  for (int i = ADDRESS_BUS_WIDTH; i--;) {
804  ret = rotateLeft1Byte(ret) + (int)s1[i] - (int)s2[i];
805  }
806  return ret;
807 }
808 
815 constexpr void copyStringLiterals(int i,
816  unsigned char* source,
817  unsigned char* dest) {
818  while (i--) {
819  dest[i] = source[i];
820  }
821 }
822 
829 inline void setBreakpointStatus(unsigned int wordA, unsigned int wordB) {
830  sendChar(static_cast<unsigned char>(BoardInstruction::BP_SET));
831  sendNBytes(wordA, 4); // send word a
832  sendNBytes(wordB, 4); // send word b
833 }
834 
842 inline void setBreakpointDefinition(unsigned int breakpointIndex,
843  BreakpointInfo* bp) {
844  sendChar(static_cast<unsigned char>(BoardInstruction::BP_WRITE));
845  sendChar(breakpointIndex); // send the list index of the breakpoint
846  sendNBytes(bp->misc, 2);
849  sendCharArray(8, bp->dataA);
850  sendCharArray(8, bp->dataB);
851 }
852 
858 inline void sendCharArray(int length, unsigned char* data) {
859  struct pollfd pollfd;
860  pollfd.fd = writeToJimulator;
861  pollfd.events = POLLOUT;
862 
863  // See if output possible
864  if (not poll(&pollfd, 1, OUT_POLL_TIMEOUT)) {
865  std::cout << "Client system not responding!\n"; // communication problem
866  }
867 
868  // Write char_number bytes
869  if (write(writeToJimulator, data, length) == -1) {
870  std::cout << "Pipe write error!\n";
871  }
872 }
873 
878 inline void sendChar(unsigned char data) {
879  sendCharArray(1, &data);
880 }
881 
887 inline void sendNBytes(int data, int n) {
888  if (n > ADDRESS_BUS_WIDTH) {
889  n = ADDRESS_BUS_WIDTH; // Clip n
890  }
891 
892  unsigned char buffer[n];
893 
894  // Conversion to little endian
895  for (auto& c : buffer) {
896  c = getLeastSignificantByte(data);
897  data = rotateRight1Byte(data);
898  }
899 
900  sendCharArray(n, buffer);
901 }
902 
910 inline const int getCharArray(int length, unsigned char* data) {
911  int reply_count; // Number of chars fetched in latest attempt
912  int reply_total = 0;
913  struct pollfd pollfd;
914 
915  pollfd.fd = readFromJimulator;
916  pollfd.events = POLLIN;
917 
918  // while there is more to get
919  while (length > 0) {
920  // If nothing available
921  if (not poll(&pollfd, 1, IN_POLL_TIMEOUT)) {
922  reply_count = 0; // Will force loop termination
923  }
924 
925  // attempt to read the number of bytes requested and store the number of
926  // bytes received
927  else {
928  reply_count = read(readFromJimulator, data, length);
929  }
930 
931  if (reply_count == 0) {
932  length = -1; // Set to terminate
933  }
934 
935  // Set minimum to 0
936  if (reply_count < 0) {
937  reply_count = 0;
938  }
939 
940  reply_total += reply_count;
941  length -= reply_count; // Update No. bytes that are still required
942  data += reply_count; // Move the data pointer to its new location
943  }
944 
945  return reply_total; // return the number of bytes received
946 }
947 
953 inline const int getChar(unsigned char* data) {
954  return getCharArray(1, data);
955 }
956 
966 inline const int getNBytes(int* data, int n) {
967  if (n > ADDRESS_BUS_WIDTH) {
968  n = ADDRESS_BUS_WIDTH; // Clip, just in case
969  }
970 
971  char unsigned buffer[ADDRESS_BUS_WIDTH];
972  int numberOfReceivedBytes = getCharArray(n, buffer);
973  *data = 0;
974 
975  for (int i = 0; i < numberOfReceivedBytes; i++) {
976  *data = *data | (getLeastSignificantByte(buffer[i]) << (i * 8));
977  }
978 
979  return numberOfReceivedBytes;
980 }
981 
986 inline const ClientState getBoardStatus() {
987  unsigned char clientStatus = 0;
988  int stepsSinceReset;
989  int leftOfWalk;
990 
991  // If the board sends back the wrong the amount of data
992  sendChar(static_cast<unsigned char>(BoardInstruction::WOT_U_DO));
993 
994  if (getChar(&clientStatus) != 1 ||
995  getNBytes(&leftOfWalk, 4) != 4 || // Steps remaining
996  getNBytes(&stepsSinceReset, 4) != 4) { // Steps since reset
997  std::cout << "board not responding\n";
998  return ClientState::BROKEN;
999  }
1000 
1001  // TODO: clientStatus represents what the board is doing and why - can be
1002  // reflected in the view? and the same with stepsSinceReset
1003 
1004  return static_cast<ClientState>(clientStatus);
1005 }
1006 
1013 inline const std::array<unsigned char, 64> readRegistersIntoArray() {
1014  unsigned char data[64];
1015 
1016  sendChar(static_cast<unsigned char>(BoardInstruction::GET_REG));
1017  sendNBytes(0, 4);
1018  sendNBytes(16, 2);
1019  getCharArray(64, data);
1020 
1021  std::array<unsigned char, 64> ret;
1022  std::copy(std::begin(data), std::end(data), std::begin(ret));
1023  return ret;
1024 }
1025 
1036 inline const std::string integerArrayToHexString(int i,
1037  unsigned char* const v,
1038  const bool prepend0x) {
1039  std::stringstream ss;
1040 
1041  if (prepend0x) {
1042  ss << "0x";
1043  }
1044 
1045  while (i--) {
1046  ss << std::setfill('0') << std::setw(2) << std::uppercase << std::hex
1047  << (int)v[i];
1048  }
1049 
1050  return ss.str();
1051 }
1052 
1063 constexpr const int numericStringToInt(int i, const unsigned char* const s) {
1064  int ret = 0;
1065 
1066  while (i--) {
1067  ret = rotateLeft1Byte(ret) + (s[i]);
1068  }
1069 
1070  return ret;
1071 }
1072 
1083 constexpr void numericStringAndIntAddition(unsigned char* const s, int n) {
1084  int temp = 0;
1085 
1086  for (int i = 0; i < ADDRESS_BUS_WIDTH; i++) {
1087  temp = s[i] + getLeastSignificantByte(n);
1088  s[i] = getLeastSignificantByte(temp);
1089  n = rotateRight1Byte(n); // Shift to next byte
1090  if (temp >= 0x100) {
1091  n++; // Propagate carry
1092  }
1093  }
1094 }
1095 
1104 constexpr const int disassembleSourceFile(SourceFileLine* src,
1105  unsigned int addr) {
1106  if (src == NULL || src == nullptr) {
1107  return 4;
1108  }
1109 
1110  unsigned int diff = src->address - addr; // How far to next line start?
1111 
1112  // Do have a source line, but shan't use it
1113  if (diff == 0) {
1114  src = src->next; // Use the one after
1115  if (src != NULL) {
1116  diff = src->address - addr; // if present
1117  } else {
1118  diff = 1000; // Effectively infinity
1119  }
1120  }
1121 
1122  if (diff < 4) {
1123  return diff; // Next source entry
1124  } else {
1125  return 4 - (addr % 4); // To next word alignment
1126  }
1127 }
1128 
1138 inline const std::unordered_map<u_int32_t, bool> getAllBreakpoints() {
1139  std::unordered_map<u_int32_t, bool> breakpointAddresses;
1140  unsigned int wordA, wordB;
1141  bool error = false;
1142 
1143  // If reading the breakpoints was a success, loops through all of the possible
1144  // breakpoints - if they are active, add them to the map.
1145  if (getBreakpointStatus(&wordA, &wordB)) {
1146  for (int i = 0; (i < MAX_NUMBER_OF_BREAKPOINTS) && not error; i++) {
1147  if (((wordA >> i) & 1) != 0) {
1148  BreakpointInfo bp;
1149 
1150  if (getBreakpointDefinition(i, &bp)) {
1151  u_int32_t addr = numericStringToInt(4, bp.addressA);
1152  breakpointAddresses.insert({addr, true});
1153  } else {
1154  error = true; // Read failure causes loop termination
1155  }
1156  }
1157  }
1158  }
1159 
1160  return breakpointAddresses;
1161 }
1162 
1163 // ! COMPILING STUFF BELOW! !
1164 // ! COMPILING STUFF BELOW! !
1165 // ! COMPILING STUFF BELOW! !
1166 
1170 inline void flushSourceFile() {
1171  SourceFileLine* old = source.pStart;
1172  source.pStart = NULL;
1173  source.pEnd = NULL;
1174 
1175  while (old != NULL) {
1176  if (old->text != NULL) {
1177  free(old->text);
1178  }
1179 
1180  SourceFileLine* trash = old;
1181  old = old->next;
1182  free(trash);
1183  }
1184 }
1185 
1191 constexpr const int checkHexCharacter(const char character) {
1192  if ((character >= '0') && (character <= '9')) {
1193  return character - '0';
1194  } else if ((character >= 'A') && (character <= 'F')) {
1195  return character - 'A' + 10;
1196  } else if ((character >= 'a') && (character <= 'f')) {
1197  return character - 'a' + 10;
1198  } else {
1199  return -1;
1200  }
1201 }
1202 
1210 constexpr const int readNumberFromFile(FILE* const f,
1211  char* const c,
1212  unsigned int* const n) {
1213  while ((*c == ' ') || (*c == '\t')) {
1214  *c = getc(f); // Skip spaces
1215  }
1216 
1217  int j = 0, value = 0;
1218  for (int digit = checkHexCharacter(*c); digit >= 0;
1219  digit = checkHexCharacter(*c), j++) {
1220  value = (value << 4) | digit; // Accumulate digit
1221  *c = getc(f); // Get next character
1222  } // Exits at first non-hex character (which is discarded)
1223 
1224  j = (j + 1) / 2; // Round digit count to bytes
1225 
1226  int k = 0;
1227  if (j == 0) {
1228  k = 0;
1229  } else {
1230  *n = value; // Only if n found
1231 
1232  if (j > 4) {
1233  k = 4; // Currently clips at 32-bit
1234  } else {
1235  for (k = 1; k < j; k = k << 1) {
1236  ; // Round j to 2^N
1237  }
1238  }
1239  }
1240 
1241  return k;
1242 }
1243 
1249 constexpr const unsigned int boardTranslateMemsize(const int size) {
1250  switch (size) {
1251  case 1:
1252  return 0;
1253  case 2:
1254  return 1;
1255  case 4:
1256  return 2;
1257  case 8:
1258  return 3;
1259  default:
1260  return 0;
1261  }
1262 }
1263 
1273 inline void boardSetMemory(unsigned char* const address,
1274  unsigned char* const value,
1275  const int size) {
1276  sendChar(BoardInstruction::SET_MEM | boardTranslateMemsize(size));
1277  sendCharArray(ADDRESS_BUS_WIDTH, address); // send address
1278  sendNBytes(1, 2); // send width
1279  sendCharArray(size, value);
1280 }
1281 
1287 inline const bool readSourceFile(const char* const pathToKMD) {
1288  // TODO: this function is a jumbled mess, refactor and remove sections
1289  unsigned int oldAddress, dSize[SOURCE_FIELD_COUNT],
1290  dValue[SOURCE_FIELD_COUNT];
1291  int byteTotal, textLength;
1292  char buffer[SOURCE_TEXT_LENGTH + 1]; // + 1 for terminator
1293  SourceFileLine* currentLine;
1294 
1295  // `system` runs the paramter string as a shell command (i.e. it launches a
1296  // new process) `pidof` checks to see if a process by the name `jimulator` is
1297  // running. If it fails (non-zero) It will print an error and return failure.
1298  /*if (system("pgrep jimulator > /dev/null")) {
1299  std::cout << "Jimulator is not running!\n";
1300  return false;
1301  }*/
1302 
1303  // If file cannot be read, return false
1304  FILE* komodoSource = fopen(pathToKMD, "r");
1305  if (komodoSource == NULL) {
1306  std::cout << "Source could not be opened!\n";
1307  return false;
1308  }
1309 
1310  bool hasOldAddress = false; // Don't know where we start
1311 
1312  // Repeat until end of file
1313  while (not feof(komodoSource)) {
1314  unsigned int address = 0; // Really needed?
1315  bool flag = false; // Haven't found an address yet
1316  char c = getc(komodoSource); // The current character being parsed
1317 
1318  // If the first character is a colon, read a symbol record
1319  if (c == ':') {
1320  hasOldAddress = false; // Don't retain position
1321  }
1322 
1323  // Read a source line record
1324  else {
1325  for (int j = 0; j < SOURCE_FIELD_COUNT; j++) {
1326  dSize[j] = 0;
1327  dValue[j] = 0;
1328  }
1329 
1330  byteTotal = 0;
1331  flag = readNumberFromFile(komodoSource, &c, &address) != 0;
1332 
1333  // Read a new address - and if we got an address, try for data fields
1334  if (flag) {
1335  if (c == ':') {
1336  c = getc(komodoSource); // Skip colon
1337  }
1338 
1339  // Loop on data fields
1340  // repeat several times or until `illegal' character met
1341  for (int j = 0; j < SOURCE_FIELD_COUNT; j++) {
1342  dSize[j] = readNumberFromFile(komodoSource, &c, &dValue[j]);
1343 
1344  if (dSize[j] == 0) {
1345  break; // Quit if nothing found
1346  }
1347 
1348  byteTotal = byteTotal + dSize[j]; // Total input
1349  }
1350 
1351  oldAddress = address + byteTotal; // Predicted -next- address
1352  hasOldAddress = true;
1353  }
1354  // Address field not found Maybe something useable?
1355  else if (hasOldAddress) {
1356  address = oldAddress; // Use predicted address
1357  flag = true; // Note we do have an address
1358  }
1359 
1360  // We have a record with an address
1361  if (flag) {
1362  while ((c != ';') && (c != '\n') && not feof(komodoSource)) {
1363  c = getc(komodoSource);
1364  }
1365 
1366  // Check for field separator
1367  if (c == ';') {
1368  c = getc(komodoSource);
1369  if (c == ' ') {
1370  c = getc(komodoSource); // Skip formatting space
1371  }
1372 
1373  textLength = 0; // Measure (& buffer) source line
1374 
1375  // Everything to end of line (or clip)
1376  while ((c != '\n') && not feof(komodoSource) &&
1377  (textLength < SOURCE_TEXT_LENGTH)) {
1378  buffer[textLength++] = c;
1379  c = getc(komodoSource);
1380  }
1381 
1382  buffer[textLength++] = '\0'; // textLength now length incl. '\0'
1383  currentLine = (SourceFileLine *)malloc(sizeof(SourceFileLine)); // Create new record
1384  currentLine->address = address;
1385 
1386  byteTotal = 0; // Inefficient
1387  for (int j = 0; j < SOURCE_FIELD_COUNT; j++) {
1388  currentLine->dataSize[j] = dSize[j]; // Bytes, not digits
1389  currentLine->dataValue[j] = dValue[j];
1390 
1391  if ((currentLine->dataSize[j] > 0) &&
1392  ((currentLine->dataSize[j] + byteTotal) <= SOURCE_BYTE_COUNT)) {
1393  unsigned char addr[4], data[4];
1394 
1395  for (int i = 0; i < 4; i++) {
1396  addr[i] =
1397  getLeastSignificantByte((address + byteTotal) >> (8 * i));
1398  }
1399  for (int i = 0; i < currentLine->dataSize[j]; i++) {
1400  data[i] = getLeastSignificantByte(currentLine->dataValue[j] >>
1401  (8 * i));
1402  }
1403 
1404  // Ignore Boolean error return value for now
1405  boardSetMemory(addr, data, currentLine->dataSize[j]);
1406  }
1407 
1408  byteTotal = byteTotal + currentLine->dataSize[j];
1409  currentLine->hasData = (byteTotal != 0); // If blank line
1410  }
1411 
1412  // clips source record - essential
1413  if (byteTotal > SOURCE_BYTE_COUNT) {
1414  int m = 0;
1415 
1416  for (int j = 0; j < SOURCE_FIELD_COUNT; j++) {
1417  m = m + currentLine->dataSize[j];
1418  if (m <= SOURCE_BYTE_COUNT) {
1419  dSize[j] = 0;
1420  byteTotal = byteTotal - currentLine->dataSize[j];
1421  } else {
1422  dSize[j] = currentLine->dataSize[j]; // Bytes, not digits
1423  currentLine->dataSize[j] = 0;
1424  }
1425  }
1426 
1427  // error here?
1428  std::cout << "OVERFLOW " << dSize[0] << " " << dSize[1] << " "
1429  << dSize[2] << " " << dSize[3] << " " << byteTotal
1430  << std::endl;
1431  }
1432 
1433  // Copy text to buffer
1434  currentLine->text = (char *)malloc(textLength);
1435  for (int j = 0; j < textLength; j++) {
1436  currentLine->text[j] = buffer[j];
1437  }
1438 
1439  SourceFileLine* temp1 = source.pStart;
1440  SourceFileLine* temp2 = NULL;
1441 
1442  while ((temp1 != NULL) && (address >= temp1->address)) {
1443  temp2 = temp1;
1444  temp1 = temp1->next;
1445  }
1446 
1447  currentLine->next = temp1;
1448  currentLine->prev = temp2;
1449 
1450  if (temp1 != NULL) {
1451  temp1->prev = currentLine;
1452  } else {
1453  source.pEnd = currentLine;
1454  }
1455 
1456  if (temp2 != NULL) {
1457  temp2->next = currentLine;
1458  } else {
1459  source.pStart = currentLine;
1460  }
1461  }
1462  } // Source line
1463  }
1464 
1465  // Get the next relevant character
1466  while ((c != '\n') && not feof(komodoSource)) {
1467  c = getc(komodoSource);
1468  }
1469  }
1470 
1471  fclose(komodoSource);
1472  return true;
1473 }
1474 
1475 char * stokmd(char *file_name) {
1476  char *s = strdup(file_name);
1477  size_t len;
1478  *strrchr(s, '.') = 0;
1479 
1480  len = strlen(file_name);
1481  s = (char *)realloc(s, len + 5);
1482  strcat(s, ".kmd");
1483 
1484  return s;
1485 }
1486 
1487 char * getKcmdPath() {
1488  char *dbuf;
1489  uint32_t size = 0x100;
1490  dbuf = new char[size];
1491 #ifdef __APPLE__
1492  if(_NSGetExecutablePath(dbuf, &size) != 0) {
1493  std::cerr << "Failed to get path for kcmd.\n";
1494  exit(1);
1495  }
1496 #else
1497  if(readlink("/proc/self/exe", dbuf, size-1) < 0) {
1498  perror("readlink");
1499  exit(1);
1500  }
1501  dbuf[size] = 0;
1502 #endif
1503  return dbuf;
1504 }
1505 
1506 void initJimulator(std::string argv0) {
1507  // sets up the pipes to allow communication between Jimulator and
1508  // KoMo2 processes.
1510  std::cout << "A pipe error ocurred." << std::endl;
1511  exit(1);
1512  }
1513 
1516 
1517  // Stores the emulator_PID for later.
1518  emulator_PID = fork();
1519 
1520  // Jimulator process.
1521  if (emulator_PID == 0) {
1522  // Closes Jimulator stdout - Jimulator can write to this pipe using printf
1523  close(1);
1524  dup2(communicationFromJimulator[1], 1);
1525 
1526  // Closes Jimulator stdin - Jimulator can write to this pipe using scanf
1527  close(0);
1528  dup2(communicationToJimulator[0], 0);
1529 
1530  auto jimulatorPath = argv0.append("/jimulator").c_str();
1531  execlp(jimulatorPath, "jimulator", (char*)0);
1532  // should never get here
1533  _exit(1);
1534  }
1535 }
1536 
1537 static void initTerm() {
1538  termios oldt;
1539  tcgetattr(0, &oldt);
1540  termios newt = oldt;
1541  newt.c_lflag &= ~(ECHO|ICANON);
1542  tcsetattr(0, TCSANOW, &newt);
1543  std::cout.setf(std::ios::unitbuf);
1544  std::cin.setf(std::ios::unitbuf);
1545 }
1546 
1547 static void handle_io() {
1548  char c;
1549 
1550  t1 = new std::thread([&]() -> void {
1551  while(true) {
1552  usleep(10000);
1553  mtx.lock();
1555  mtx.unlock();
1556  }
1557  });
1558 
1559  t2 = new std::thread([&]() -> void {
1560  while(true) {
1561  c = getchar();
1562  mtx.lock();
1564  mtx.unlock();
1565  }
1566  });
1567 }
1568 
1569 int main(int argc, char** argv) {
1570  if(argc != 2) {
1571  std::cout << "usage: " << argv[0] << " <asm file>\n";
1572  return 1;
1573  }
1574 
1575  char *kcmd_path = getKcmdPath();
1576  char *kmd_path = stokmd(argv[1]);
1577 
1578  *strrchr(kcmd_path, '/') = 0;
1579  initJimulator(kcmd_path);
1580  initTerm();
1581  Jimulator::compileJimulator(kcmd_path, argv[1], kmd_path);
1582 
1583  Jimulator::loadJimulator(kmd_path);
1584  Jimulator::startJimulator(1000000);
1585  handle_io();
1586 
1587  free(kmd_path);
1588  free(kcmd_path);
1589  wait(NULL);
1590  kill(emulator_PID, SIGTERM);
1591 }
1592 
const std::string integerArrayToHexString(int, unsigned char *const, const bool=false)
Converts an array of integers into a formatted hexadecimal string.
unsigned int address
The address of the source line.
Definition: kcmd.cpp:188
Describes an entire file of a .kmd sourceFile.
Definition: kcmd.cpp:209
void sendCharArray(int, unsigned char *)
Sends an array of characters to Jimulator.
unsigned char operator|(BoardInstruction l, unsigned char r)
Performing an or between a BoardInstruction and an unsigned char.
constexpr void copyStringLiterals(int, unsigned char *, unsigned char *)
Copy one string literal into another string literal.
constexpr int IN_POLL_TIMEOUT
The maximum amount of time to wait after sending input to the pipes.
bool hasData
A flag that indicates that the line stores internal data or not.
Definition: kcmd.cpp:183
char * text
Text, as read from the source file.
Definition: kcmd.cpp:203
SourceFileLine * pEnd
The last line in a source file.
Definition: kcmd.cpp:219
void resetJimulator()
Reset the emulators running.
Definition: kcmd.cpp:363
const int getCharArray(int, unsigned char *)
reads an array of characters from Jimulator.
unsigned char addressB[ADDRESS_BUS_WIDTH]
A secondary address for a breakpoint.
Definition: kcmd.cpp:101
const std::string generateMemoryHex(SourceFileLine **src, const uint32_t s_address, int *const increment, const int currentAddressI, unsigned char(*memdata)[52])
Get the hex values from the memory data.
const bool getBreakpointStatus(unsigned int *, unsigned int *)
Gets the status of breakpoints from within Jimulator.
unsigned char dataA[8]
Data associated with the breakpoint.
Definition: kcmd.cpp:108
constexpr const unsigned int boardTranslateMemsize(const int size)
Converts a provided memory size into a Jimulator legal memory size.
void compileJimulator(std::string pathToBin, const char *const pathToS, const char *const pathToKMD)
Runs pathToS through the associated compiler binary, and outputs a .kmd file at pathToKMD.
Definition: kcmd.cpp:283
constexpr int SOURCE_FIELD_COUNT
The number of fields that can be used in a source file.
unsigned int misc
Miscellaneous information associated with the breakpoint.
Definition: kcmd.cpp:122
unsigned char addressA[ADDRESS_BUS_WIDTH]
The address of the breakpoint.
Definition: kcmd.cpp:94
const bool setBreakpoint(const uint32_t address)
Sets a breakpoint.
Definition: kcmd.cpp:372
int writeToJimulator
Stores the file descriptor used for writing to Jimulator.
Definition: kcmd.cpp:79
constexpr int SOURCE_BYTE_COUNT
The maximum number of bytes that can be read from a source file.
constexpr int OUT_POLL_TIMEOUT
The maximum amount of time to wait waiting for output from the pipes.
Describes a single line of a .kmd file.
Definition: kcmd.cpp:168
void setBreakpointStatus(unsigned int, unsigned int)
Overwrites some breakpoint information into Jimulator.
constexpr const int rotateRight1Byte(const int val)
Rotates an integers bits to the right by 1 byte.
void initJimulator(const std::string argv0)
Initialises Jimulator in a separate child process.
Definition: main.cpp:152
unsigned char dataB[8]
Data associated with the secondary breakpoint.
Definition: kcmd.cpp:115
constexpr const char getLeastSignificantByte(const int)
Get the least significant byte out of an integer - if little endian, get the most signficant byte...
constexpr int SOURCE_TEXT_LENGTH
The maximum length of a line in a source file.
constexpr int ADDRESS_BUS_WIDTH
The width of Jimulators internal address bus.
void sendChar(unsigned char)
Sends a singular character to Jimulator.
constexpr const int disassembleSourceFile(SourceFileLine *, unsigned int)
Calculates the difference between the current address to display and the next address that needs to b...
void pauseJimulator()
Pauses the emulator running.
Definition: kcmd.cpp:356
SourceFileLine * pStart
The first line in a source file.
Definition: kcmd.cpp:214
const int getNBytes(int *, int)
Reads n bytes of data from Jimulator.
const std::unordered_map< u_int32_t, bool > getAllBreakpoints()
Reads all of the breakpoints from Jimulator into a map that can be indexed by address. The address is the key, with a boolean as the value to form a pair. However the boolean is redundant - since the address is the key, if you lookup an address in the map and it is present, you know that a breakpoint was found there.
constexpr const int checkHexCharacter(const char character)
Check if the passed character represents a legal hex digit.
int dataValue[4]
Stores the data values.
Definition: kcmd.cpp:198
int emulator_PID
This variable will store the PID of the Jimulator process.
Definition: main.cpp:53
constexpr const int numericStringSubtraction(const unsigned char *const, const unsigned char *const)
Takes two string literals of equal length which represent string forms of integers - for example...
constexpr const bool moveSrc(bool firstFlag, SourceFileLine **src)
Steps any given source line to the next valid source line.
sourceFile source
The source file that is currently loaded into Jimulator.
void startJimulator(const int steps)
Commences running the emulator.
Definition: kcmd.cpp:335
constexpr const int rotateLeft1Byte(const int val)
Rotates an integers bits to the left by 1 byte.
const bool sendTerminalInputToJimulator(const unsigned int val)
Sends terminal information to Jimulator.
Definition: kcmd.cpp:499
void setBreakpointDefinition(unsigned int, BreakpointInfo *)
Writes a new breakpoint into the breakpoint list.
constexpr int MAX_NUMBER_OF_BREAKPOINTS
The maximum number of breakpoints within the application.
const std::array< unsigned char, 64 > readRegistersIntoArray()
Gets serialized bit data from the board that represents 16 register values - 15 general purpose regis...
const bool loadJimulator(const char *const pathToKMD)
Clears the existing source object and loads the file at pathToKMD into Jimulator. ...
Definition: kcmd.cpp:326
const int getChar(unsigned char *)
Reads a singular character from Jimulator.
int readFromJimulator
Stores the file descriptor used for reading from Jimulator.
Definition: kcmd.cpp:80
void flushSourceFile()
removes all of the old references to the previous file.
constexpr const int getNextFreeBreakpoint(const int)
Gets the index of the next free breakpoint from Jimulators internal breakpoint list.
int communicationToJimulator[2]
The pipe which will be used by KoMo2 to write to Jimulator (i.e. Jimulator will read from it...
Definition: kcmd.cpp:77
int communicationFromJimulator[2]
The pipe which will be used by KoMo2 to read from Jimulator (i.e. Jimulator will write to it...
Definition: kcmd.cpp:76
SourceFileLine * next
The next line of the overall source file.
Definition: kcmd.cpp:178
SourceFileLine * prev
The previous line of the overall source file.
Definition: kcmd.cpp:173
const ClientState getBoardStatus()
Gets a code that indicates the internal state of Jimulator.
void continueJimulator()
Continues running Jimulator.
Definition: kcmd.cpp:346
constexpr void numericStringAndIntAddition(unsigned char *const, int)
Takes a string representation of an integer - for example, the string {&#39;5&#39; &#39;7&#39; &#39;2&#39;} representing the ...
const bool readSourceFile(const char *const)
Reads the source of the file pointer to by pathToKMD.
std::array< Jimulator::MemoryValues, 13 > getJimulatorMemoryValues(const uint32_t s_address_int)
Get the memory values from Jimulator, starting to s_address.
Definition: kcmd.cpp:525
constexpr const int readNumberFromFile(FILE *const f, char *const c, unsigned int *const n)
Reads a n from a string of text in the .kmd file.
int compilerCommunication[2]
The pipe that handles communication between the compiler process and KoMo2.
Definition: kcmd.cpp:78
const bool getBreakpointDefinition(unsigned int, BreakpointInfo *)
Reads a breakpoint definition for use by the breakpoints callbacks.
void boardSetMemory(unsigned char *const address, unsigned char *const value, const int size)
Sets a memory value of a given address to a new value. This code is LEGACY. It used to run with a che...
int main(int argc, char *argv[])
The program entry point.
Definition: main.cpp:94
Contains the information read from Jimulator about a given breakpoint.
Definition: kcmd.cpp:89
const ClientState checkBoardState()
Check the state of the board - logs what it is doing.
Definition: kcmd.cpp:422
constexpr const int numericStringToInt(int, const unsigned char *const)
Takes a string literals which represent a string form of an integer - for example, the number 5 represented as the string {&#39;5&#39;}, or the number 327 represented as the string {&#39;3&#39;, &#39;2&#39;, &#39;7&#39;} - and returns the value represented as an int. For example: Input string {&#39;8&#39;, &#39;1&#39;, &#39;8&#39;} will result in the output 818.
const std::string getJimulatorTerminalMessages()
Reads for messages from Jimulator, to display in the terminal output.
Definition: kcmd.cpp:470
void sendNBytes(int, int)
Writes n bytes of data to Jimulator.
const std::array< std::string, 16 > getJimulatorRegisterValues()
Queries a register in Jimulator to get it&#39;s current value.
Definition: kcmd.cpp:453
int dataSize[4]
Stores the size of the data fields.
Definition: kcmd.cpp:193