GEVM.Mod 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. (* Визуальный эмулятор ГЭВМ - Гипотетической электронно-вычислительной машины.
  2. ГЭВМ состоит из оперативной памяти на 256 байтов и регистра "программный счётчик".
  3. Перемещайтесь по ячейкам памяти с помощью клавиш со стрелками, вводите значения
  4. ячеек памяти при помощи цифр от 0 до 9 и букв от A до F.
  5. Зелёным цветом изображаются шестнадцатеричные числа, красным - десятичные.
  6. Клавиши:
  7. Табуляция - переставить курсор на следующую ячейку памяти и обнулить её.
  8. P - переключить курсор на программный счётчик (и обратно).
  9. R - выполнить инструкцию (процессор прорабатывает один такт).
  10. X - сменить основание системы счисления значения в данной ячейке с 10 на 16 (и обратно).
  11. Y - сменить основание системы счисления адресов ячеек.
  12. [ - привести основание системы счисления значений всех ячеек к основанию адресов.
  13. ] - перезагрузить ГЭВМ.
  14. Escape - закрыть эмулятор.
  15. Инструкции ГЭВМ:
  16. E4 - сложить два числа, адреса которых даны в первых двух параметрах,
  17. и записать результат в ячейку по адресу, данному в третьем параметре.
  18. A1 - безусловный переход на адрес, данный в [единственном] параметре.
  19. B4 - уменьшить значение ячейки, адрес которой дан в первом параметре, на единицу.
  20. A2 - если значение по адресу, данному в первом параметре, равно нулю, перейти на
  21. адрес, данный во втором параметре.
  22. 00 - останов.
  23. 6 апреля 2019 г. Рига. *)
  24. MODULE GEVM;
  25. IMPORT Files, G := Graph, Out;
  26. CONST
  27. w = 8; (* Ячеек ОЗУ в строке *)
  28. h = 5; (* Ячеек ОЗУ в столбце *)
  29. cw = 60; (* Ширина ячейки в пикселях *)
  30. ch = 24; (* Высота ячейки в пикселях *)
  31. intX = 13; (* Интервал между ячейками по горизонтали в пикселях *)
  32. intY = 32; (* Интервал между ячейками по вертикали в пикселях *)
  33. (* Клавиши *)
  34. tab = CHR(9);
  35. backsp = CHR(8);
  36. enter = CHR(13);
  37. esc = CHR(27);
  38. right = CHR(83);
  39. left = CHR(82);
  40. down = CHR(85);
  41. up = CHR(84);
  42. VAR F: Files.File;
  43. r: Files.Rider;
  44. font: G.Font;
  45. charW, charH: INTEGER;
  46. mem: ARRAY 256 OF CHAR; (* ОЗУ *)
  47. base10: ARRAY 256 OF BOOLEAN; (* Основание системы счисления = 10 *)
  48. mark: ARRAY 256 OF BOOLEAN; (* Пометка изменений *)
  49. pc: CHAR; (* Программный счётчик *)
  50. off: BOOLEAN; (* Выключена ли ГЭВМ *)
  51. first: INTEGER; (* Номер первой видимой ячейки ОЗУ *)
  52. cur: INTEGER; (* Номер выделенной ячейки ОЗУ *)
  53. curPC: BOOLEAN; (* Находится ли курсор на программном счётчике *)
  54. addr10: BOOLEAN; (* Показываются ли адреса в десятичном виде *)
  55. sw, sh: INTEGER; (* Размеры экрана в пикселях *)
  56. cx: INTEGER; (* Отступ слева в пикселях *)
  57. cy: INTEGER; (* Отступ сверху в пикселях *)
  58. (* Цвета *)
  59. black, white, grey, green, dkgreen: G.Color;
  60. ltgreen, red, dkred, ltred, yellow: G.Color;
  61. PROCEDURE ResetMem;
  62. VAR i: INTEGER;
  63. BEGIN
  64. FOR i := 0 TO LEN(mem) - 1 DO
  65. mem[i] := 0X;
  66. base10[i] := TRUE
  67. END;
  68. pc := 0X; first := 0; cur := 0;
  69. curPC := FALSE; addr10 := TRUE; off := TRUE
  70. END ResetMem;
  71. PROCEDURE ResetBase;
  72. VAR i: INTEGER;
  73. BEGIN
  74. FOR i := 0 TO LEN(mem) - 1 DO base10[i] := addr10 END
  75. END ResetBase;
  76. PROCEDURE Init(): BOOLEAN;
  77. VAR i: INTEGER;
  78. ok: BOOLEAN;
  79. BEGIN
  80. ok := TRUE;
  81. G.GetScreenSize(sw, sh);
  82. font := G.LoadFont("Data/Fonts/Main");
  83. IF font = NIL THEN
  84. Out.String("Could not load font."); Out.Ln;
  85. ok := FALSE
  86. END;
  87. IF ok THEN
  88. G.GetMonoFontSize(font, charW, charH);
  89. cx := (sw - (cw + intX) * w - intX) DIV 2;
  90. cy := (sh - (ch + intY) * h) DIV 2;
  91. G.MakeCol(black, 0, 0, 0);
  92. G.MakeCol(white , 255, 255, 255);
  93. G.MakeCol(grey , 120, 120, 120);
  94. G.MakeCol(red , 230, 0, 0);
  95. G.MakeCol(dkred , 100, 0, 0);
  96. G.MakeCol(ltred , 255, 90, 90);
  97. G.MakeCol(green , 0, 230, 0);
  98. G.MakeCol(dkgreen, 0, 100, 0);
  99. G.MakeCol(ltgreen, 90, 255, 90);
  100. G.MakeCol(yellow , 255, 255, 90);
  101. ResetMem;
  102. ResetBase
  103. END;
  104. RETURN ok
  105. END Init;
  106. PROCEDURE NumToStr(n: INTEGER; b10: BOOLEAN; VAR str: ARRAY OF CHAR);
  107. VAR i, b, x: INTEGER;
  108. L, R: INTEGER;
  109. c: CHAR;
  110. BEGIN
  111. IF b10 THEN b := 10 ELSE b := 16 END;
  112. i := 0;
  113. REPEAT
  114. x := n MOD b;
  115. IF x < 10 THEN str[i] := CHR(ORD('0') + x)
  116. ELSE str[i] := CHR(ORD('A') + x - 10)
  117. END;
  118. n := n DIV b;
  119. INC(i)
  120. UNTIL n = 0;
  121. (* Возможно дописать 0 *)
  122. IF i < 2 THEN str[i] := '0'; INC(i) END;
  123. str[i] := 0X;
  124. (* Перевенуть задом наперёд *)
  125. L := 0; R := i - 1;
  126. WHILE L < R DO c := str[L]; str[L] := str[R]; str[R] := c; INC(L); DEC(R) END
  127. END NumToStr;
  128. (* v - значение в ячейке;
  129. type = 0 или 1, это тип (цвет) кнопки *)
  130. PROCEDURE DrawReg(v: INTEGER; b10: BOOLEAN; x0, y0, x1, y1, type: INTEGER;
  131. shine: BOOLEAN);
  132. VAR str: ARRAY 16 OF CHAR;
  133. c1, c2, c3: G.Color;
  134. BEGIN
  135. IF type = 0 THEN c1 := green; c2 := dkgreen; c3 := ltgreen
  136. ELSE c1 := red; c2 := dkred; c3 := ltred
  137. END;
  138. G.FillRect(x0, y0, x1, y1, c3);
  139. G.HLine(x0, y0, x1, c1);
  140. G.VLine(x0, y0, y1, c1);
  141. G.HLine(x0, y1, x1, c2);
  142. G.VLine(x1, y0, y1, c2);
  143. (* Внутри *)
  144. NumToStr(v, b10, str);
  145. G.DrawString(str, x0 + (cw - charW * 2) DIV 2,
  146. y0 + (ch - charH) DIV 2, font, black);
  147. (* Курсор *)
  148. IF shine THEN
  149. G.Rect(x0 - 1, y0 - 1, x1 + 1, y1 + 1, yellow);
  150. G.Rect(x0 - 3, y0 - 3, x1 + 3, y1 + 3, yellow)
  151. END
  152. END DrawReg;
  153. (* n - индекс ячейки;
  154. type = 0 или 1, это тип (цвет) кнопки *)
  155. PROCEDURE DrawCell(n, x0, y0, x1, y1, type: INTEGER);
  156. VAR str: ARRAY 16 OF CHAR;
  157. x, y: INTEGER;
  158. c: G.Color;
  159. BEGIN
  160. (* Подсветка изменений *)
  161. IF n = ORD(pc) THEN
  162. G.FillRect(x0 - 3, y0 - 3, x1 + 3, y1 + charH + 3, grey);
  163. G.Rect(x0 - 3, y0 - 3, x1 + 3, y1 + charH + 3, white);
  164. G.Rect(x0 - 4, y0 - 4, x1 + 4, y1 + charH + 4, ltred);
  165. G.Rect(x0 - 5, y0 - 5, x1 + 5, y1 + charH + 5, red)
  166. END;
  167. DrawReg(ORD(mem[n]), base10[n], x0, y0, x1, y1, type, (cur = n) & ~curPC);
  168. (* Подпись внизу *)
  169. NumToStr(n, addr10, str);
  170. IF n = ORD(pc) THEN c := yellow
  171. ELSIF addr10 THEN c := ltred
  172. ELSE c := ltgreen
  173. END;
  174. x := x0 + (cw - charW * 2) DIV 2; y := y1 + 3;
  175. G.DrawString(str, x, y, font, c);
  176. IF mark[n] THEN
  177. G.HLine(x - 1, y + charH - 2, x + charW * 2 - 1, yellow)
  178. END
  179. END DrawCell;
  180. PROCEDURE DrawMem;
  181. VAR i, type: INTEGER;
  182. last: INTEGER;
  183. x, y: INTEGER;
  184. BEGIN
  185. last := first + w * h - 1;
  186. IF last >= LEN(mem) THEN last := LEN(mem) - 1 END;
  187. x := cx; y := cy;
  188. FOR i := first TO last DO
  189. IF base10[i] THEN type := 1 ELSE type := 0 END;
  190. DrawCell(i, x, y, x + cw - 1, y + ch - 1, type);
  191. IF (i + 1) MOD w # 0 THEN INC(x, cw + intX)
  192. ELSE INC(y, ch + intY); x := cx
  193. END
  194. END
  195. END DrawMem;
  196. (* Рисует регистр "программный счётчик" *)
  197. PROCEDURE DrawPC;
  198. VAR x, y, type: INTEGER;
  199. str: ARRAY 8 OF CHAR;
  200. c: G.Color;
  201. BEGIN
  202. IF addr10 THEN type := 1; c := ltred ELSE type := 0; c := ltgreen END;
  203. x := cx + (w - 1) * (cw + intX);
  204. y := cy - ch - 20;
  205. str := "Пр. сч.";
  206. G.DrawString(str, x - 8 * charW, y + (ch - charH) DIV 2, font, c);
  207. DrawReg(ORD(pc), addr10, x, y, x + cw - 1, y + ch - 1, type, curPC)
  208. END DrawPC;
  209. PROCEDURE Move(c: CHAR);
  210. BEGIN
  211. IF ~curPC THEN
  212. IF c = up THEN DEC(cur, w)
  213. ELSIF c = left THEN DEC(cur)
  214. ELSIF c = right THEN INC(cur)
  215. ELSE (* c = down *) INC(cur, w)
  216. END;
  217. IF cur < 0 THEN cur := 0
  218. ELSIF cur >= LEN(mem) THEN cur := LEN(mem) - 1
  219. END;
  220. IF cur < first THEN DEC(first, w)
  221. ELSIF cur >= first + w * h THEN INC(first, w)
  222. END
  223. END
  224. END Move;
  225. PROCEDURE Backspace;
  226. VAR b: INTEGER;
  227. BEGIN
  228. IF curPC THEN
  229. IF addr10 THEN b := 10 ELSE b := 16 END;
  230. pc := CHR(ORD(pc) DIV b)
  231. ELSE
  232. IF base10[cur] THEN b := 10 ELSE b := 16 END;
  233. mem[cur] := CHR(ORD(mem[cur]) DIV b)
  234. END
  235. END Backspace;
  236. PROCEDURE Input(x: INTEGER);
  237. VAR v, v0, b: INTEGER;
  238. b10: BOOLEAN;
  239. BEGIN
  240. IF curPC THEN
  241. b10 := addr10;
  242. v0 := ORD(pc)
  243. ELSE
  244. b10 := base10[cur];
  245. v0 := ORD(mem[cur])
  246. END;
  247. IF ~b10 OR (x < 10) THEN
  248. IF b10 THEN b := 10 ELSE b := 16 END;
  249. v := v0 * b + x;
  250. IF v > 255 THEN v := v0 MOD b * b + x END;
  251. IF curPC THEN pc := CHR(v) ELSE mem[cur] := CHR(v) END
  252. END
  253. END Input;
  254. PROCEDURE Mark(n: INTEGER);
  255. BEGIN
  256. mark[n] := TRUE
  257. END Mark;
  258. PROCEDURE ClearMarks;
  259. VAR i: INTEGER;
  260. BEGIN
  261. FOR i := 0 TO LEN(mark) - 1 DO mark[i] := FALSE END
  262. END ClearMarks;
  263. PROCEDURE GoTick;
  264. VAR instr, p0, p1, p2, p3, p4, v1, v2, v3: INTEGER;
  265. BEGIN
  266. ClearMarks;
  267. off := FALSE;
  268. p0 := ORD(pc); instr := ORD(mem[p0]);
  269. p1 := (p0 + 1) MOD LEN(mem); v1 := ORD(mem[p1]);
  270. p2 := (p0 + 2) MOD LEN(mem); v2 := ORD(mem[p2]);
  271. p3 := (p0 + 3) MOD LEN(mem); v3 := ORD(mem[p3]);
  272. p4 := (p0 + 4) MOD LEN(mem);
  273. IF instr = 0E4H THEN
  274. mem[v3] := CHR((ORD(mem[v1]) + ORD(mem[v2])) MOD 256);
  275. Mark(v3);
  276. pc := CHR(p4)
  277. ELSIF instr = 0A1H THEN pc := mem[p1]
  278. ELSIF instr = 0B4H THEN
  279. mem[v1] := CHR((ORD(mem[v1]) - 1) MOD 256);
  280. Mark(v1);
  281. pc := CHR(p2)
  282. ELSIF instr = 0A2H THEN
  283. IF mem[v1] = 0X THEN pc := mem[p2]
  284. ELSE pc := CHR(p3)
  285. END
  286. ELSIF instr = 0 THEN off := TRUE; pc := 0X
  287. END
  288. END GoTick;
  289. PROCEDURE DrawMsg;
  290. VAR z: ARRAY 5 OF CHAR;
  291. BEGIN
  292. IF off THEN
  293. z := 'ГЭВМ';
  294. G.DrawString(z, cx, cy - charH - intY, font, yellow)
  295. END
  296. END DrawMsg;
  297. PROCEDURE Run;
  298. VAR done: BOOLEAN;
  299. c: CHAR;
  300. BEGIN
  301. done := FALSE;
  302. REPEAT
  303. G.ClearScreen;
  304. DrawMem;
  305. DrawPC;
  306. DrawMsg;
  307. G.Flip;
  308. c := G.ReadKey();
  309. IF (c = up) OR (c = down) OR (c = right) OR (c = left) THEN Move(c)
  310. ELSIF c = tab THEN Move(right); mem[cur] := 0X
  311. ELSIF c = backsp THEN Backspace
  312. ELSIF ("0" <= c) & (c <= "9") THEN Input(ORD(c) - ORD("0"))
  313. ELSIF ("a" <= c) & (c <= "f") THEN Input(ORD(c) - ORD("a") + 10)
  314. ELSIF c = "x" THEN base10[cur] := ~base10[cur]
  315. ELSIF c = "y" THEN addr10 := ~addr10
  316. ELSIF c = "p" THEN curPC := ~curPC
  317. ELSIF c = "r" THEN GoTick
  318. ELSIF c = "[" THEN ResetBase
  319. ELSIF c = "]" THEN ResetMem
  320. ELSIF c = esc THEN done := TRUE
  321. END
  322. UNTIL done
  323. END Run;
  324. BEGIN
  325. G.Settings(640, 400, {});
  326. G.Init;
  327. IF G.Done & Init() THEN Run
  328. ELSE Out.String("Init failed."); Out.Ln
  329. END;
  330. G.Close
  331. END GEVM.