GEVM.Mod 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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(79);
  39. left = CHR(80);
  40. down = CHR(81);
  41. up = CHR(82);
  42. VAR F: Files.File;
  43. r: Files.Rider;
  44. s: G.Bitmap;
  45. font: G.Font;
  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. cx: INTEGER; (* Отступ слева в пикселях *)
  56. cy: INTEGER; (* Отступ сверху в пикселях *)
  57. (* Цвета *)
  58. black: INTEGER;
  59. white: INTEGER;
  60. grey: INTEGER;
  61. green, dkgreen, ltgreen: INTEGER;
  62. red, dkred, ltred: INTEGER;
  63. yellow: INTEGER;
  64. PROCEDURE ResetMem;
  65. VAR i: INTEGER;
  66. BEGIN
  67. FOR i := 0 TO LEN(mem) - 1 DO
  68. mem[i] := 0X;
  69. base10[i] := TRUE
  70. END;
  71. pc := 0X; first := 0; cur := 0;
  72. curPC := FALSE; addr10 := TRUE; off := TRUE
  73. END ResetMem;
  74. PROCEDURE ResetBase;
  75. VAR i: INTEGER;
  76. BEGIN
  77. FOR i := 0 TO LEN(mem) - 1 DO base10[i] := addr10 END
  78. END ResetBase;
  79. PROCEDURE Init(): BOOLEAN;
  80. VAR i: INTEGER;
  81. ok: BOOLEAN;
  82. BEGIN
  83. ok := TRUE;
  84. font := G.LoadFont("data/images/font.bmp", 8, 16);
  85. IF font = NIL THEN
  86. Out.String("Could not load font."); Out.Ln;
  87. ok := FALSE
  88. END;
  89. IF ok THEN
  90. cx := (s.w - (cw + intX) * w - intX) DIV 2;
  91. cy := (s.h - (ch + intY) * h) DIV 2;
  92. black := G.MakeCol( 0, 0, 0);
  93. white := G.MakeCol(255, 255, 255);
  94. grey := G.MakeCol(120, 120, 120);
  95. red := G.MakeCol(230, 0, 0);
  96. dkred := G.MakeCol(100, 0, 0);
  97. ltred := G.MakeCol(255, 90, 90);
  98. green := G.MakeCol( 0, 230, 0);
  99. dkgreen := G.MakeCol( 0, 100, 0);
  100. ltgreen := G.MakeCol( 90, 255, 90);
  101. yellow := G.MakeCol(255, 255, 90);
  102. ResetMem;
  103. ResetBase
  104. END;
  105. RETURN ok
  106. END Init;
  107. PROCEDURE NumToStr(n: INTEGER; b10: BOOLEAN; VAR str: ARRAY OF CHAR);
  108. VAR i, b, x: INTEGER;
  109. L, R: INTEGER;
  110. c: CHAR;
  111. BEGIN
  112. IF b10 THEN b := 10 ELSE b := 16 END;
  113. i := 0;
  114. REPEAT
  115. x := n MOD b;
  116. IF x < 10 THEN str[i] := CHR(ORD('0') + x)
  117. ELSE str[i] := CHR(ORD('A') + x - 10)
  118. END;
  119. n := n DIV b;
  120. INC(i)
  121. UNTIL n = 0;
  122. (* Возможно дописать 0 *)
  123. IF i < 2 THEN str[i] := '0'; INC(i) END;
  124. str[i] := 0X;
  125. (* Перевенуть задом наперёд *)
  126. L := 0; R := i - 1;
  127. WHILE L < R DO c := str[L]; str[L] := str[R]; str[R] := c; INC(L); DEC(R) END
  128. END NumToStr;
  129. (* v - значение в ячейке;
  130. type = 0 или 1, это тип (цвет) кнопки *)
  131. PROCEDURE DrawReg(v: INTEGER; b10: BOOLEAN; x0, y0, x1, y1, type: INTEGER;
  132. shine: BOOLEAN);
  133. VAR str: ARRAY 16 OF CHAR;
  134. c1, c2, c3: INTEGER;
  135. BEGIN
  136. IF type = 0 THEN c1 := green; c2 := dkgreen; c3 := ltgreen
  137. ELSE c1 := red; c2 := dkred; c3 := ltred
  138. END;
  139. G.RectFill(s, x0, y0, x1, y1, c3);
  140. G.HLine(s, x0, y0, x1, c1);
  141. G.VLine(s, x0, y0, y1, c1);
  142. G.HLine(s, x0, y1, x1, c2);
  143. G.VLine(s, x1, y0, y1, c2);
  144. (* Внутри *)
  145. NumToStr(v, b10, str);
  146. G.DrawString(s, font, x0 + (cw - font.charW * 2) DIV 2,
  147. y0 + (ch - font.charH) DIV 2, str, black);
  148. (* Курсор *)
  149. IF shine THEN
  150. G.Rect(s, x0 - 1, y0 - 1, x1 + 1, y1 + 1, yellow);
  151. G.Rect(s, x0 - 3, y0 - 3, x1 + 3, y1 + 3, yellow)
  152. END
  153. END DrawReg;
  154. (* n - индекс ячейки;
  155. type = 0 или 1, это тип (цвет) кнопки *)
  156. PROCEDURE DrawCell(n, x0, y0, x1, y1, type: INTEGER);
  157. VAR str: ARRAY 16 OF CHAR;
  158. x, y, c: INTEGER;
  159. BEGIN
  160. (* Подсветка изменений *)
  161. IF n = ORD(pc) THEN
  162. G.RectFill(s, x0 - 3, y0 - 3, x1 + 3, y1 + font.charH + 3, grey);
  163. G.Rect(s, x0 - 3, y0 - 3, x1 + 3, y1 + font.charH + 3, white);
  164. G.Rect(s, x0 - 4, y0 - 4, x1 + 4, y1 + font.charH + 4, ltred);
  165. G.Rect(s, x0 - 5, y0 - 5, x1 + 5, y1 + font.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 - font.charW * 2) DIV 2; y := y1 + 3;
  175. G.DrawString(s, font, x, y, str, c);
  176. IF mark[n] THEN
  177. G.HLine(s, x - 1, y + font.charH - 2, x + font.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, c: INTEGER;
  199. str: ARRAY 8 OF CHAR;
  200. BEGIN
  201. IF addr10 THEN type := 1; c := ltred ELSE type := 0; c := ltgreen END;
  202. x := cx + (w - 1) * (cw + intX);
  203. y := cy - ch - 20;
  204. str := "Pr. sc."; str[0] := 8FX; str[1] := 0E0X;
  205. str[4] := 0E1X; str[5] := 0E7X;
  206. G.DrawString(s, font, x - 8 * font.charW,
  207. y + (ch - font.charH) DIV 2, str, c);
  208. DrawReg(ORD(pc), addr10, x, y, x + cw - 1, y + ch - 1, type, curPC)
  209. END DrawPC;
  210. PROCEDURE Move(c: CHAR);
  211. BEGIN
  212. IF ~curPC THEN
  213. IF c = up THEN DEC(cur, w)
  214. ELSIF c = left THEN DEC(cur)
  215. ELSIF c = right THEN INC(cur)
  216. ELSE (* c = down *) INC(cur, w)
  217. END;
  218. IF cur < 0 THEN cur := 0
  219. ELSIF cur >= LEN(mem) THEN cur := LEN(mem) - 1
  220. END;
  221. IF cur < first THEN DEC(first, w)
  222. ELSIF cur >= first + w * h THEN INC(first, w)
  223. END
  224. END
  225. END Move;
  226. PROCEDURE Backspace;
  227. VAR b: INTEGER;
  228. BEGIN
  229. IF curPC THEN
  230. IF addr10 THEN b := 10 ELSE b := 16 END;
  231. pc := CHR(ORD(pc) DIV b)
  232. ELSE
  233. IF base10[cur] THEN b := 10 ELSE b := 16 END;
  234. mem[cur] := CHR(ORD(mem[cur]) DIV b)
  235. END
  236. END Backspace;
  237. PROCEDURE Input(x: INTEGER);
  238. VAR v, v0, b: INTEGER;
  239. b10: BOOLEAN;
  240. BEGIN
  241. IF curPC THEN
  242. b10 := addr10;
  243. v0 := ORD(pc)
  244. ELSE
  245. b10 := base10[cur];
  246. v0 := ORD(mem[cur])
  247. END;
  248. IF ~b10 OR (x < 10) THEN
  249. IF b10 THEN b := 10 ELSE b := 16 END;
  250. v := v0 * b + x;
  251. IF v > 255 THEN v := v0 MOD b * b + x END;
  252. IF curPC THEN pc := CHR(v) ELSE mem[cur] := CHR(v) END
  253. END
  254. END Input;
  255. PROCEDURE Mark(n: INTEGER);
  256. BEGIN
  257. mark[n] := TRUE
  258. END Mark;
  259. PROCEDURE ClearMarks;
  260. VAR i: INTEGER;
  261. BEGIN
  262. FOR i := 0 TO LEN(mark) - 1 DO mark[i] := FALSE END
  263. END ClearMarks;
  264. PROCEDURE GoTick;
  265. VAR instr, p0, p1, p2, p3, p4, v1, v2, v3: INTEGER;
  266. BEGIN
  267. ClearMarks;
  268. off := FALSE;
  269. p0 := ORD(pc); instr := ORD(mem[p0]);
  270. p1 := (p0 + 1) MOD LEN(mem); v1 := ORD(mem[p1]);
  271. p2 := (p0 + 2) MOD LEN(mem); v2 := ORD(mem[p2]);
  272. p3 := (p0 + 3) MOD LEN(mem); v3 := ORD(mem[p3]);
  273. p4 := (p0 + 4) MOD LEN(mem);
  274. IF instr = 0E4H THEN
  275. mem[v3] := CHR((ORD(mem[v1]) + ORD(mem[v2])) MOD 256);
  276. Mark(v3);
  277. pc := CHR(p4)
  278. ELSIF instr = 0A1H THEN pc := mem[p1]
  279. ELSIF instr = 0B4H THEN
  280. mem[v1] := CHR((ORD(mem[v1]) - 1) MOD 256);
  281. Mark(v1);
  282. pc := CHR(p2)
  283. ELSIF instr = 0A2H THEN
  284. IF mem[v1] = 0X THEN pc := mem[p2]
  285. ELSE pc := CHR(p3)
  286. END
  287. ELSIF instr = 0 THEN off := TRUE; pc := 0X
  288. END
  289. END GoTick;
  290. PROCEDURE DrawMsg;
  291. VAR z: ARRAY 5 OF CHAR;
  292. BEGIN
  293. IF off THEN
  294. z[0] := 82X; z[1] := 9BX; z[2] := 08AX; z[3] := 08BX; z[4] := 0X;
  295. G.DrawString(s, font, cx, cy - font.charH - intY, z, yellow)
  296. END
  297. END DrawMsg;
  298. PROCEDURE Run;
  299. VAR done: BOOLEAN;
  300. c: CHAR;
  301. BEGIN
  302. done := FALSE;
  303. REPEAT
  304. G.ClearScreen;
  305. DrawMem;
  306. DrawPC;
  307. DrawMsg;
  308. G.Flip;
  309. c := G.ReadKey();
  310. IF (right <= c) & (c <= up) THEN Move(c)
  311. ELSIF c = tab THEN Move(right); mem[cur] := 0X
  312. ELSIF c = backsp THEN Backspace
  313. ELSIF ("0" <= c) & (c <= "9") THEN Input(ORD(c) - ORD("0"))
  314. ELSIF ("a" <= c) & (c <= "f") THEN Input(ORD(c) - ORD("a") + 10)
  315. ELSIF c = "x" THEN base10[cur] := ~base10[cur]
  316. ELSIF c = "y" THEN addr10 := ~addr10
  317. ELSIF c = "p" THEN curPC := ~curPC
  318. ELSIF c = "r" THEN GoTick
  319. ELSIF c = "[" THEN ResetBase
  320. ELSIF c = "]" THEN ResetMem
  321. ELSIF c = esc THEN done := TRUE
  322. END
  323. UNTIL done
  324. END Run;
  325. BEGIN
  326. G.Settings(640, 400, {G.fullscreen, G.sharpPixels, G.spread});
  327. s := G.Init();
  328. IF (s # NIL) & Init() THEN
  329. Run
  330. ELSE Out.String("Init failed."); Out.Ln
  331. END;
  332. G.Close
  333. END GEVM.