├── .gitignore ├── ComputerEmulator ├── ComputerEmulator.csproj ├── ComputerEmulator.sln ├── MyByte.cs ├── Program.cs ├── V1 │ ├── Cpu.cs │ ├── Ram.cs │ └── SpaceFight.cs └── V2 │ ├── Cpu.cs │ ├── GameOfLife.cs │ ├── Ram.cs │ └── SpaceFight.cs ├── README.md ├── computer-v1 ├── README.md ├── asm │ ├── fibonacci-sequence.asm │ ├── font-test.asm │ ├── hello-world.asm │ ├── prime-numbers.asm │ ├── space-fight.asm │ └── typewriter.asm ├── img │ ├── cpu.jpg │ ├── digits.jpg │ ├── display.jpg │ ├── fibonacci-sequence.jpg │ ├── font-test.jpg │ ├── prime-numbers.jpg │ ├── ram.jpg │ ├── space-fight.jpg │ ├── summary.jpg │ └── terminal.jpg ├── programming.md └── specification.md ├── computer-v2 ├── README.md ├── asm │ ├── demo.asm │ ├── fibonacci-sequence.asm │ ├── font-test.asm │ ├── game-of-life.asm │ ├── prime-numbers.asm │ ├── space-fight.asm │ └── typewriter.asm ├── img │ ├── cpu.jpg │ ├── digits.jpg │ ├── display.jpg │ ├── fibonacci-sequence.jpg │ ├── font-test.jpg │ ├── game-of-life.jpg │ ├── keyboard.jpg │ ├── prime-numbers.jpg │ ├── ram.jpg │ ├── space-fight.jpg │ ├── summary.jpg │ ├── terminal.jpg │ └── typewriter.jpg ├── programming.md └── specification.md └── img ├── adder.jpg ├── ant.jpg ├── bcd-converter.jpg ├── cpu.jpg ├── digits.jpg ├── gates.jpg ├── life.jpg ├── matrix-compact.jpg ├── matrix-rgb.jpg ├── matrix.jpg ├── multiplier.jpg ├── ram-256-v1.jpg ├── ram-256-v2.jpg ├── ram-32k.jpg ├── ram-64.jpg ├── rom-compact.jpg ├── subtractor.jpg ├── transmitters.jpg └── youtube.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | bin/ 3 | obj/ 4 | *.user 5 | -------------------------------------------------------------------------------- /ComputerEmulator/ComputerEmulator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0-windows 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ComputerEmulator/ComputerEmulator.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34330.188 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ComputerEmulator", "ComputerEmulator.csproj", "{40DC1A38-B5D3-40A6-B051-F86C2525393C}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {40DC1A38-B5D3-40A6-B051-F86C2525393C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {40DC1A38-B5D3-40A6-B051-F86C2525393C}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {40DC1A38-B5D3-40A6-B051-F86C2525393C}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {40DC1A38-B5D3-40A6-B051-F86C2525393C}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {3992EC4A-17C4-4758-A3E6-D30704482AF4} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /ComputerEmulator/MyByte.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerEmulator; 2 | 3 | using System.Text.RegularExpressions; 4 | 5 | internal readonly partial struct MyByte 6 | { 7 | public readonly byte Value; 8 | 9 | public MyByte(int @byte) 10 | { 11 | Value = unchecked((byte)@byte); 12 | } 13 | 14 | public MyByte(string hex) 15 | { 16 | if (!GetPattern().IsMatch(hex)) 17 | throw new InvalidOperationException(); 18 | 19 | Value = (byte)(_toNumberMap[hex[0]] * 16 + _toNumberMap[hex[1]]); 20 | } 21 | 22 | [GeneratedRegex(@"^[0-9A-F]{2}$")] 23 | private static partial Regex GetPattern(); 24 | 25 | public string Hex 26 | { 27 | get { 28 | var major = _toCharMap[Value / 16]; 29 | var minor = _toCharMap[Value % 16]; 30 | return $"{major}{minor}"; 31 | } 32 | } 33 | 34 | public bool IsSigned => Value >= 128; 35 | 36 | public override string ToString() 37 | { 38 | return $"{Hex} {Value}"; 39 | } 40 | 41 | private static readonly Dictionary _toCharMap = new() 42 | { 43 | { 0, '0'}, 44 | { 1, '1'}, 45 | { 2, '2'}, 46 | { 3, '3'}, 47 | { 4, '4'}, 48 | { 5, '5'}, 49 | { 6, '6'}, 50 | { 7, '7'}, 51 | { 8, '8'}, 52 | { 9, '9'}, 53 | { 10, 'A'}, 54 | { 11, 'B'}, 55 | { 12, 'C'}, 56 | { 13, 'D'}, 57 | { 14, 'E'}, 58 | { 15, 'F'}, 59 | }; 60 | 61 | private static readonly Dictionary _toNumberMap = new() 62 | { 63 | { '0', 0}, 64 | { '1', 1}, 65 | { '2', 2}, 66 | { '3', 3}, 67 | { '4', 4}, 68 | { '5', 5}, 69 | { '6', 6}, 70 | { '7', 7}, 71 | { '8', 8}, 72 | { '9', 9}, 73 | { 'A', 10}, 74 | { 'B', 11}, 75 | { 'C', 12}, 76 | { 'D', 13}, 77 | { 'E', 14}, 78 | { 'F', 15}, 79 | }; 80 | 81 | public static implicit operator MyByte(int value) => new(value); 82 | public static implicit operator byte(MyByte value) => value.Value; 83 | } 84 | -------------------------------------------------------------------------------- /ComputerEmulator/Program.cs: -------------------------------------------------------------------------------- 1 | Console.Extras.WindowMaximize(); 2 | 3 | // V1 - Space Fight! 4 | //{ 5 | // var ram = new ComputerEmulator.V1.Ram(); 6 | // Console.Pin(ram.Display); 7 | // ram.Load(ComputerEmulator.V1.SpaceFight.Bytes()); 8 | // var cpu = new ComputerEmulator.V1.Cpu(ram); 9 | // cpu.Run(); 10 | //} 11 | 12 | // V2 - Space Fight! 13 | //{ 14 | // var ram = new ComputerEmulator.V2.Ram(); 15 | // Console.Pin(ram.Display); 16 | // ram.Load(ComputerEmulator.V2.SpaceFight.Bytes()); 17 | // var cpu = new ComputerEmulator.V2.Cpu(ram); 18 | // cpu.Run(); 19 | //} 20 | 21 | // V2 - Game of Life 22 | { 23 | var ram = new ComputerEmulator.V2.Ram(); 24 | Console.Pin(ram.Display); 25 | ram.Load(ComputerEmulator.V2.GameOfLife.Bytes()); 26 | var cpu = new ComputerEmulator.V2.Cpu(ram); 27 | cpu.Run(); 28 | } 29 | -------------------------------------------------------------------------------- /ComputerEmulator/V1/Ram.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerEmulator.V1; 2 | 3 | using ComputerEmulator; 4 | 5 | internal class Ram 6 | { 7 | private const int Size = 256; 8 | private readonly IList _main = new MyByte[Size]; 9 | private readonly IList _screen = new MyByte[32]; 10 | private MyByte _number = default; 11 | private bool _numberSetted = false; 12 | private static readonly MyByte _outAddr = new("3F"); 13 | private static readonly MyByte _outNumber = new("10"); 14 | private static readonly MyByte _outScreen = new("80"); 15 | private static readonly MyByte _screenMinAddr = new("40"); 16 | private static readonly MyByte _screenMaxAddr = new("5F"); 17 | 18 | public MyByte Read(string addr) => Read(new MyByte(addr)); 19 | 20 | public MyByte Read(MyByte addr) => _main[addr]; 21 | 22 | public void Write(MyByte addr, MyByte value) 23 | { 24 | _main[addr] = value; 25 | 26 | if (_main[_outAddr] == _outNumber && addr == _screenMinAddr) 27 | { 28 | _number = value; 29 | _numberSetted = true; 30 | Console.UpdatePin(); 31 | } 32 | 33 | if (_main[_outAddr] == _outScreen && addr >= _screenMinAddr && addr <= _screenMaxAddr) 34 | { 35 | _screen[addr - 64] = value; 36 | Console.UpdatePin(); 37 | } 38 | } 39 | 40 | public void Load(IReadOnlyList bytes) 41 | { 42 | if (bytes.Count > Size) 43 | throw new InvalidOperationException(); 44 | 45 | for (var i = 0; i < bytes.Count; i++) 46 | Write(i, bytes[i]); 47 | } 48 | 49 | public IReadOnlyList Display() 50 | { 51 | var items = new List(); 52 | 53 | for (var i = _screenMinAddr; i < _screenMaxAddr; i += 2) 54 | { 55 | var row = "rw`"; 56 | row += Pixels(_screen[i - 64]); 57 | row += Pixels(_screen[i - 63]); 58 | items.Add(row); 59 | 60 | if (i < _screenMaxAddr - 2) 61 | items.Add("\n"); 62 | } 63 | 64 | items.Add(" BCD: "); 65 | 66 | if (_numberSetted) 67 | items.Add($"G`{_number.Value}"); 68 | 69 | return items; 70 | } 71 | 72 | private static string Pixels(MyByte value) 73 | { 74 | var hex = value.Hex; 75 | return $"{_pixelMap[hex[0]]}{_pixelMap[hex[1]]}"; 76 | } 77 | 78 | private static readonly Dictionary _pixelMap = new() 79 | { 80 | { '0', " "}, 81 | { '1', " ▓▓"}, 82 | { '2', " ▓▓ "}, 83 | { '3', " ▓▓▓▓"}, 84 | { '4', " ▓▓ "}, 85 | { '5', " ▓▓ ▓▓"}, 86 | { '6', " ▓▓▓▓ "}, 87 | { '7', " ▓▓▓▓▓▓"}, 88 | { '8', "▓▓ "}, 89 | { '9', "▓▓ ▓▓"}, 90 | { 'A', "▓▓ ▓▓ "}, 91 | { 'B', "▓▓ ▓▓▓▓"}, 92 | { 'C', "▓▓▓▓ "}, 93 | { 'D', "▓▓▓▓ ▓▓"}, 94 | { 'E', "▓▓▓▓▓▓ "}, 95 | { 'F', "▓▓▓▓▓▓▓▓"}, 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /ComputerEmulator/V1/SpaceFight.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerEmulator.V1; 2 | 3 | internal static class SpaceFight 4 | { 5 | public static IReadOnlyList Bytes() 6 | { 7 | return _bytes.Select(x => new MyByte(x)).ToList(); 8 | } 9 | 10 | // WIN_LEFT equ 30 11 | // STEP_CNT equ 14 12 | // KEY_LEFT equ 0x11 13 | // KEY_RIGHT equ 0x13 14 | // KEY_FIRE equ 0x20 15 | // OUT_BCD equ 0x10 16 | // OUT_DISPLAY equ 0x80 17 | 18 | private static readonly IReadOnlyList _bytes = [ 19 | 20 | // 00 21 | "8A", // 00 clear: ldi c, 0x5C 22 | "5C", // 01 23 | "8B", // 02 ldi d, 0x58 24 | "58", // 03 25 | "4E", // 04 clear_loop: dec c 26 | "B9", // 05 st b, c 27 | "02", // 06 mov a, c 28 | "1B", // 07 xor a, d 29 | "E4", // 08 jnz clear_loop 30 | "04", // 09 31 | "8A", // 0A random: ldi c, display 32 | "40", // 0B 33 | "A8", // 0C random_loop: rnd a 34 | "A9", // 0D rnd b 35 | "09", // 0E and a, b 36 | "50", // 0F shl a 37 | "4F", // 10 dec d 38 | "BC", // 11 st a, d 39 | "A8", // 12 rnd a 40 | "A9", // 13 rnd b 41 | "09", // 14 and a, b 42 | "54", // 15 shr a 43 | "4F", // 16 dec d 44 | "BC", // 17 st a, d 45 | "03", // 18 mov a, d 46 | "1A", // 19 xor a, c 47 | "E4", // 1A jnz random_loop 48 | "0C", // 1B 49 | "80", // 1C score: ld a, win_left 50 | "3C", // 1D 51 | "8B", // 1E ldi d, display 52 | "40", // 1F 53 | 54 | // 20 55 | "9D", // 20 ld b, d 56 | "8A", // 21 ldi c, OUT_BCD 57 | "10", // 22 58 | "A2", // 23 st c, out 59 | "3F", // 24 60 | "BC", // 25 st a, d 61 | "8A", // 26 ldi c, OUT_DISPLAY 62 | "80", // 27 63 | "A2", // 28 st c, out 64 | "3F", // 29 65 | "BD", // 2A st b, d 66 | "20", // 1B add a, 0 67 | "E0", // 2C jz win 68 | "D5", // 2D 69 | "89", // 2E step: ldi b, step2 70 | "30", // 2F 71 | "80", // 30 step2: ld a, step_left 72 | "3B", // 31 73 | "4C", // 32 dec a 74 | "E2", // 33 js level 75 | "B2", // 34 76 | "A0", // 35 st a, step_left 77 | "3B", // 36 78 | "E8", // 37 jmp keys 79 | "60", // 38 80 | "00", // 39 --- 81 | "0E", // 3A step_cnt: STEP_CNT 82 | "0E", // 3B step_left: STEP_CNT 83 | "1E", // 3C win_left: WIN_LEFT 84 | "00", // 3D bank: 0 85 | "00", // 3E in: 0 86 | "80", // 3F out: OUT_DISPLAY 87 | 88 | // 40 (display) 89 | "28", // 40 00101000 00000000 90 | "00", // 41 91 | "20", // 42 00100000 00101000 92 | "28", // 43 93 | "00", // 44 00000000 00001000 94 | "08", // 45 95 | "09", // 46 00001001 00000000 96 | "00", // 47 97 | "21", // 48 00100001 00100000 98 | "20", // 49 99 | "23", // 4A 00100011 10001000 100 | "88", // 4B 101 | "03", // 4C 00000011 10001000 102 | "88", // 4D 103 | "0B", // 4E 00001011 10100000 104 | "A0", // 4F 105 | "0A", // 50 00001010 10100000 106 | "A0", // 51 107 | "2E", // 52 00101110 11101000 108 | "E8", // 53 109 | "2F", // 54 00101111 11101000 110 | "E8", // 55 111 | "3B", // 56 00111011 10111000 112 | "B8", // 57 113 | "35", // 58 00110101 01011000 114 | "58", // 59 115 | "20", // 5A 00100000 00001000 116 | "08", // 5B 117 | "01", // 5C 00000001 00000000 118 | "00", // 5D 119 | "03", // 5E 00000011 10000000 120 | "80", // 5F 121 | 122 | // 60 123 | "00", // 60 keys: mov a, 0 124 | "82", // 61 ld c, in 125 | "3E", // 62 126 | "A0", // 63 st a, in 127 | "3E", // 64 128 | "88", // 65 ldi a, KEY_FIRE 129 | "20", // 66 130 | "1A", // 67 xor a, c 131 | "E0", // 68 jz fire 132 | "93", // 69 133 | "88", // 6A ldi a, KEY_RIGHT 134 | "13", // 6B 135 | "1A", // 6C xor a, c 136 | "E0", // 6D jz right 137 | "83", // 6E 138 | "88", // 6F ldi a, KEY_LEFT 139 | "11", // 70 140 | "1A", // 71 xor a, c 141 | "D4", // 72 jnz b ; step2 142 | "80", // 73 left: ld a, 0x5E 143 | "5E", // 74 144 | "50", // 75 shl a 145 | "C5", // 76 jc b ; step2 146 | "8B", // 77 ldi d, 0x5F 147 | "5F", // 78 148 | "8A", // 79 ldi c, 4 149 | "04", // 7A 150 | "9C", // 7B left_loop: ld a, d 151 | "60", // 7C rcl a 152 | "BC", // 7D st a, d 153 | "4F", // 7E dec d 154 | "4E", // 7F dec c 155 | 156 | // 80 157 | "E4", // 80 jnz left_loop 158 | "7B", // 81 159 | "F4", // 82 jmp b ; step2 160 | "80", // 83 right: ld a, 0x5F 161 | "5F", // 84 162 | "54", // 85 shr a 163 | "C5", // 86 jc b ; step2 164 | "8B", // 87 ldi d, 0x5C 165 | "5C", // 88 166 | "8A", // 89 ldi c, 4 167 | "04", // 8A 168 | "9C", // 8B right_loop: ld a, d 169 | "64", // 8C rcr a 170 | "BC", // 8D st a, d 171 | "4B", // 8E inc d 172 | "4E", // 8F dec c 173 | "E4", // 90 jnz right_loop 174 | "8B", // 91 175 | "F4", // 92 jmp b ; step2 176 | "8B", // 93 fire: ldi d, 0x5C 177 | "5C", // 94 178 | "9C", // 95 ld a, d 179 | "20", // 96 add a, 0 180 | "E4", // 97 jnz fire_shot 181 | "9A", // 98 182 | "4B", // 99 inc d 183 | "9D", // 9A fire_shot: ld b, d 184 | "8A", // 9B ldi c, 14 185 | "0E", // 9C 186 | "4F", // 9D fire_loop: dec d 187 | "4F", // 9E dec d 188 | "9C", // 9F ld a, d 189 | 190 | // A0 191 | "09", // A0 and a, b 192 | "E4", // A1 jnz fire_hit 193 | "A8", // A2 194 | "4E", // A3 dec c 195 | "E4", // A4 jnz fire_loop 196 | "9D", // A5 197 | "E8", // A6 jmp step 198 | "2E", // A7 199 | "9C", // A8 fire_hit: ld a, d 200 | "19", // A9 xor a, b 201 | "BC", // AA st a, d 202 | "80", // AB ld a, win_left 203 | "3C", // AC 204 | "4C", // AD dec a 205 | "A0", // AE st a, win_left 206 | "3C", // AF 207 | "E8", // B0 jmp score 208 | "1C", // B1 209 | "80", // B2 level: ld a, step_cnt 210 | "3A", // B3 211 | "4C", // B4 dec a 212 | "4C", // B5 dec a 213 | "A0", // B6 st a, step_cnt 214 | "3A", // B7 215 | "A0", // B8 st a, step_left 216 | "3B", // B9 217 | "8A", // BA ldi c, 0x5A 218 | "5A", // BB 219 | "98", // BC ld a, c 220 | "81", // BD ld b, 0x5B 221 | "5B", // BE 222 | "11", // BF or a, b 223 | 224 | // C0 225 | "E0", // C0 jz scroll 226 | "C8", // C1 227 | "4A", // C2 inc c 228 | "4A", // C3 inc c 229 | "88", // C4 ldi a, 0xEC ; "hlt" 230 | "EC", // C5 231 | "A0", // C6 st a, score 232 | "1C", // C7 233 | "8B", // C8 scroll: ldi d, 0x42 234 | "42", // C9 235 | "4E", // CA scroll_loop: dec c 236 | "02", // CB mov a, c 237 | "91", // CC ld b, a 238 | "48", // CD inc a 239 | "48", // CE inc a 240 | "B1", // CF st b, a 241 | "1B", // D0 xor a, d 242 | "E4", // D1 jnz scroll_loop 243 | "CA", // D2 244 | "E8", // D3 jmp random 245 | "0A", // D4 246 | "8A", // D5 win: ldi c, prize 247 | "E0", // D6 248 | "98", // D7 win_loop: ld a, c 249 | "BC", // D8 st a, d 250 | "4B", // D9 inc d 251 | "4A", // DA inc c 252 | "E4", // DB jnz win_loop 253 | "D7", // DC 254 | "EC", // DD hlt 255 | "00", // DE --- 256 | "00", // DF --- 257 | 258 | // E0 (prize) 259 | "00", // E0 00000000 00000000 260 | "00", // E1 261 | "00", // E2 00000000 00000000 262 | "00", // E3 263 | "03", // E4 00000011 11000000 264 | "C0", // E5 265 | "04", // E6 00000100 00100000 266 | "20", // E7 267 | "09", // E8 00001001 00010000 268 | "10", // E9 269 | "10", // EA 00010000 00101000 270 | "28", // EB 271 | "14", // EC 00010100 00001000 272 | "08", // ED 273 | "20", // EE 00100000 10000100 274 | "84", // EF 275 | "20", // F0 00100000 00000100 276 | "04", // F1 277 | "40", // F2 01000000 00001010 278 | "0A", // F3 279 | "53", // F4 01010011 11000010 280 | "C2", // F5 281 | "47", // F6 01000111 11100010 282 | "E2", // F7 283 | "47", // F8 01000111 11100010 284 | "E2", // F9 285 | "37", // FA 00110111 11101100 286 | "EC", // FB 287 | "0F", // FC 00001111 11110000 288 | "F0", // FD 289 | "00", // FE 00000000 00000000 290 | "00", // FF 291 | ]; 292 | } 293 | -------------------------------------------------------------------------------- /ComputerEmulator/V2/GameOfLife.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerEmulator.V2; 2 | 3 | internal static class GameOfLife 4 | { 5 | public static IReadOnlyList Bytes() 6 | { 7 | return _bytes.Select(x => new MyByte(x)).ToList(); 8 | } 9 | 10 | // BANK_INNER equ 1 11 | // BANK_MAIN equ 2 12 | // BANK_OUTER equ 3 13 | // TERMINAL equ 0x3C 14 | 15 | private static readonly IReadOnlyList _bytes = [ 16 | 17 | // 0000: COMMON (0) 18 | "03", // 00 jmp title_show 19 | "19", // 01 20 | "FC", // 02 title db "ь", 21 | "ED", // 03 "н", 22 | "E7", // 04 "з", 23 | "E8", // 05 "и", 24 | "C6", // 06 "Ж", 25 | "20", // 07 " ", 26 | "E0", // 08 "а", 27 | "F0", // 09 "р", 28 | "E3", // 0A "г", 29 | "C8", // 0B "И", 30 | "20", // 0C " ", 31 | "65", // 0D "e", 32 | "66", // 0E "f", 33 | "69", // 0F "i", 34 | "4C", // 10 "L", 35 | "20", // 11 " ", 36 | "66", // 12 "f", 37 | "6F", // 13 "o", 38 | "20", // 14 " ", 39 | "65", // 15 "e", 40 | "6D", // 16 "m", 41 | "61", // 17 "a", 42 | "47", // 18 "G" 43 | "55", // 19 title_show: ldi b, " " 44 | "20", // 1A 45 | "35", // 1B st b, title -1 46 | "01", // 1C 47 | "54", // 1D ldi a, TERMINAL 48 | "3C", // 1E 49 | "56", // 1F ldi c, title_show -1 50 | 51 | // 0020 52 | "18", // 20 53 | "57", // 21 ldi d, title_show_loop 54 | "23", // 22 55 | "49", // 23 title_show_loop: ld b, c 56 | "31", // 24 st b, a 57 | "7A", // 25 dec c 58 | "2C", // 26 jnz d 59 | "54", // 27 ldi a, 0b00000110 ; b/w display + bcd 60 | "06", // 28 61 | "30", // 29 st a, in_out 62 | "3E", // 2A 63 | "54", // 2B ldi a, display ; b = 32 64 | "40", // 2C 65 | "56", // 2D ldi c, BANK_MAIN 66 | "02", // 2E 67 | "57", // 2F ldi d, main_rnd 68 | "F6", // 30 69 | "3A", // 31 set_bank: st c, bank 70 | "3F", // 32 71 | "07", // 33 jmp d 72 | "00", // 34 --- 73 | "00", // 35 upper_area db 0x00 74 | "10", // 36 current_area db 0x10 75 | "20", // 37 lower_area db 0x20 76 | "00", // 38 upper_ptr db 0x00 77 | "20", // 39 lower_ptr db 0x20 78 | "FF", // 3A step_count db 0xFF ; bcd 79 | "00", // 3B --- ; bcd (reserved) 80 | "00", // 3C incrementor db 0 ; terminal 81 | "40", // 3D display_ptr db display ; terminal (reserved) 82 | "0D", // 3E in_out db 0b00001101 ; color display + terminal 83 | "00", // 3F bank db 0 ; bank 84 | 85 | // 0040 (display) 86 | "60", // 40 01100000 00000000 87 | "00", // 41 88 | "89", // 42 10001001 10100010 89 | "A2", // 43 90 | "A5", // 44 10100101 01010101 91 | "55", // 45 92 | "AD", // 46 10101101 01010110 93 | "56", // 47 94 | "6D", // 48 01101101 01010011 95 | "53", // 49 96 | "00", // 4A 00000000 00000000 97 | "00", // 4B 98 | "0A", // 4C 00001010 00101000 99 | "28", // 4D 100 | "11", // 4E 00010001 01000000 101 | "40", // 4F 102 | "22", // 50 00100010 00101000 103 | "28", // 51 104 | "14", // 52 00010100 01000000 105 | "40", // 53 106 | "00", // 54 00000000 00000000 107 | "00", // 55 108 | "8E", // 56 10001110 11101110 109 | "EE", // 57 110 | "84", // 58 10000100 10001000 111 | "88", // 59 112 | "84", // 5A 10000100 11001100 113 | "CC", // 5B 114 | "84", // 5C 10000100 10001000 115 | "88", // 5D 116 | "EE", // 5E 11101110 10001110 117 | "8E", // 5F 118 | 119 | // 0060 (frame_cache) 120 | "60", // 60 01100000 00000000 121 | "00", // 61 122 | "89", // 62 10001001 10100010 123 | "A2", // 63 124 | "A5", // 64 10100101 01010101 125 | "55", // 65 126 | "AD", // 66 10101101 01010110 127 | "56", // 67 128 | "6D", // 68 01101101 01010011 129 | "53", // 69 130 | "00", // 6A 00000000 00000000 131 | "00", // 6B 132 | "14", // 6C 00010100 01010100 133 | "54", // 6D 134 | "22", // 6E 00100010 00100000 135 | "20", // 6F 136 | "11", // 70 00010001 01010000 137 | "50", // 71 138 | "0A", // 72 00001010 00100000 139 | "20", // 73 140 | "00", // 74 00000000 00000000 141 | "00", // 75 142 | "8E", // 76 10001110 11101110 143 | "EE", // 77 144 | "84", // 78 10000100 10001000 145 | "88", // 79 146 | "84", // 7A 10000100 11001100 147 | "CC", // 7B 148 | "84", // 7C 10000100 10001000 149 | "88", // 7D 150 | "EE", // 7E 11101110 10001110 151 | "8E", // 7F 152 | 153 | // 0080: BANK_INNER (1) 154 | "55", // 80 inner_begin: ldi b, inner_center 155 | "B7", // 81 156 | "35", // 82 st b, inner_edge_jmp +1 157 | "A9", // 83 158 | "55", // 84 ldi b, inner_right 159 | "AA", // 85 160 | "35", // 86 st b, inner_center_jmp +1 161 | "C1", // 87 162 | "51", // 88 ld b, display_ptr 163 | "3D", // 89 164 | "44", // 8A ld a, b 165 | "E4", // 8B shr a 166 | "52", // 8C ld c, current_area 167 | "36", // 8D 168 | "6A", // 8E inc c 169 | "C0", // 8F inner_edge: rcl a 170 | "0D", // 90 jns inner_edge_end 171 | "A7", // 91 172 | "53", // 92 ld d, upper_ptr 173 | "38", // 93 174 | "4D", // 94 ld b, d 175 | "65", // 95 inc b 176 | "3D", // 96 st b, d 177 | "6F", // 97 inc d 178 | "4D", // 98 ld b, d 179 | "65", // 99 inc b 180 | "3D", // 9A st b, d 181 | "49", // 9B ld b, c 182 | "65", // 9C inc b 183 | "39", // 9D st b, c 184 | "53", // 9E ld d, lower_ptr 185 | "39", // 9F 186 | 187 | // 00A0 188 | "4D", // A0 ld b, d 189 | "65", // A1 inc b 190 | "3D", // A2 st b, d 191 | "6F", // A3 inc d 192 | "4D", // A4 ld b, d 193 | "65", // A5 inc b 194 | "3D", // A6 st b, d 195 | "7A", // A7 inner_edge_end: dec c 196 | "03", // A8 inner_edge_jmp: jmp inner_center 197 | "B7", // A9 198 | "55", // AA inner_right: ldi b, inner_edge 199 | "8F", // AB 200 | "35", // AC st b, inner_center_jmp +1 201 | "C1", // AD 202 | "55", // AE ldi b, inner_end 203 | "F6", // AF 204 | "35", // B0 st b, inner_edge_jmp +1 205 | "A9", // B1 206 | "51", // B2 ld b, display_ptr 207 | "3D", // B3 208 | "65", // B4 inc b 209 | "44", // B5 ld a, b 210 | "E4", // B6 shr a 211 | "55", // B7 inner_center: ldi b, 8 212 | "08", // B8 213 | "35", // B9 st b, incrementor 214 | "3C", // BA 215 | "51", // BB inner_center_loop: ld b, incrementor 216 | "3C", // BC 217 | "75", // BD dec b 218 | "35", // BE st b, incrementor 219 | "3C", // BF 220 | 221 | // 00C0 222 | "08", // C0 inner_center_jmp: jz inner_right 223 | "AA", // C1 224 | "C0", // C2 rcl a 225 | "0D", // C3 jns inner_center_end 226 | "E9", // C4 227 | "53", // C5 ld d, upper_ptr 228 | "38", // C6 229 | "4D", // C7 ld b, d 230 | "65", // C8 inc b 231 | "3D", // C9 st b, d 232 | "6F", // CA inc d 233 | "4D", // CB ld b, d 234 | "65", // CC inc b 235 | "3D", // CD st b, d 236 | "6F", // CE inc d 237 | "4D", // CF ld b, d 238 | "65", // D0 inc b 239 | "3D", // D1 st b, d 240 | "49", // D2 ld b, c 241 | "65", // D3 inc b 242 | "39", // D4 st b, c 243 | "6A", // D5 inc c 244 | "6A", // D6 inc c 245 | "49", // D7 ld b, c 246 | "65", // D8 inc b 247 | "39", // D9 st b, c 248 | "7A", // DA dec c 249 | "7A", // DB dec c 250 | "53", // DC ld d, lower_ptr 251 | "39", // DD 252 | "4D", // DE ld b, d 253 | "65", // DF inc b 254 | 255 | // 00E0 256 | "3D", // E0 st b, d 257 | "6F", // E1 inc d 258 | "4D", // E2 ld b, d 259 | "65", // E3 inc b 260 | "3D", // E4 st b, d 261 | "6F", // E5 inc d 262 | "4D", // E6 ld b, d 263 | "65", // E7 inc b 264 | "3D", // E8 st b, d 265 | "53", // E9 inner_center_end: ld d, upper_ptr 266 | "38", // EA 267 | "6F", // EB inc d 268 | "3F", // EC st d, upper_ptr 269 | "38", // ED 270 | "6A", // EE inc c 271 | "53", // EF ld d, lower_ptr 272 | "39", // F0 273 | "6F", // F1 inc d 274 | "3F", // F2 st d, lower_ptr 275 | "39", // F3 276 | "03", // F4 jmp inner_center_loop 277 | "BB", // F5 278 | "56", // F6 inner_end: ldi c, BANK_MAIN 279 | "02", // F7 280 | "57", // F8 ldi d, main_calc 281 | "AC", // F9 282 | "03", // FA jmp set_bank 283 | "31", // FB 284 | "00", // FC --- 285 | "00", // FD --- 286 | "00", // FE --- 287 | "00", // FF --- 288 | 289 | // 0100: BANK_MAIN (2) 290 | "51", // 80 main_row: ld b, upper_area 291 | "35", // 81 292 | "52", // 82 ld c, current_area 293 | "36", // 83 294 | "53", // 84 ld d, lower_area 295 | "37", // 85 296 | "3A", // 86 st c, upper_area 297 | "35", // 87 298 | "3F", // 88 st d, current_area 299 | "36", // 89 300 | "35", // 8A st b, lower_area 301 | "37", // 8B 302 | "3A", // 8C st c, upper_ptr 303 | "38", // 8D 304 | "35", // 8E st b, lower_ptr 305 | "39", // 8F 306 | "53", // 90 ld d, display_ptr 307 | "3D", // 91 308 | "54", // 92 ldi a, 0x5E 309 | "5E", // 93 310 | "DC", // 94 xor a, d 311 | "08", // 95 jz main_row_end 312 | "A6", // 96 313 | "54", // 97 ldi a, 0xFE ; b = lower_area 314 | "FE", // 98 315 | "56", // 99 ldi c, 0x10 316 | "10", // 9A 317 | "57", // 9B ldi d, main_row_loop 318 | "9D", // 9C 319 | "34", // 9D main_row_loop: st a, b 320 | "65", // 9E inc b 321 | "7A", // 9F dec c 322 | 323 | // 0120 324 | "2C", // A0 jnz d 325 | "6A", // A1 inc c ; = BANK_INNER 326 | "57", // A2 ldi d, inner_begin 327 | "80", // A3 328 | "03", // A4 jmp set_bank 329 | "31", // A5 330 | "56", // A6 main_row_end: ldi c, BANK_OUTER 331 | "03", // A7 332 | "57", // A8 ldi d, outer_down 333 | "93", // A9 334 | "03", // AA jmp set_bank 335 | "31", // AB 336 | "52", // AC main_calc: ld c, display_ptr 337 | "3D", // AD 338 | "53", // AE ld d, upper_area 339 | "35", // AF 340 | "7A", // B0 main_calc_iter: dec c 341 | "7A", // B1 dec c 342 | "48", // B2 ld a, c 343 | "56", // B3 ldi c, 0b10000000 344 | "80", // B4 345 | "4D", // B5 main_calc_loop: ld b, d 346 | "B5", // B6 test b 347 | "08", // B7 jz main_calc_next 348 | "BE", // B8 349 | "C8", // B9 or a, c 350 | "75", // BA dec b 351 | "08", // BB jz main_calc_next 352 | "BE", // BC 353 | "D8", // BD xor a, c 354 | "6F", // BE main_calc_next: inc d 355 | "E6", // BF shr c 356 | 357 | // 0140 358 | "0C", // C0 jnz main_calc_loop 359 | "B5", // C1 360 | "52", // C2 ld c, display_ptr 361 | "3D", // C3 362 | "55", // C4 ldi b, 30 363 | "1E", // C5 364 | "69", // C6 add b, c 365 | "34", // C7 st a, b 366 | "6A", // C8 inc c 367 | "3A", // C9 st c, display_ptr 368 | "3D", // CA 369 | "E5", // CB shr b 370 | "0E", // CC jnc main_calc_iter 371 | "B0", // CD 372 | "03", // CE main_calc_jmp: jmp main_row 373 | "80", // CF 374 | "55", // D0 main_final: ldi b, main_final2 375 | "D6", // D1 376 | "35", // D2 st b, main_calc_jmp +1 377 | "CF", // D3 378 | "03", // D4 jmp main_calc 379 | "AC", // D5 380 | "55", // D6 main_final2: ldi b, main_final3 381 | "DC", // D7 382 | "35", // D8 st b, main_calc_jmp +1 383 | "CF", // D9 384 | "03", // DA jmp main_calc_iter 385 | "B0", // DB 386 | "55", // DC main_final3: ldi b, main_row 387 | "80", // DD 388 | "35", // DE st b, main_calc_jmp +1 389 | "CF", // DF 390 | 391 | // 0160 392 | "55", // E0 main_frame: ldi b, display 393 | "40", // E1 394 | "56", // E2 ldi c, frame_cache 395 | "60", // E3 396 | "57", // E4 ldi d, main_frame_loop 397 | "E6", // E5 398 | "48", // E6 main_frame_loop: ld a, c 399 | "34", // E7 st a, b 400 | "65", // E8 inc b 401 | "6A", // E9 inc c 402 | "2D", // EA jns d 403 | "50", // EB main_count: ld a, step_count 404 | "3A", // EC 405 | "60", // ED inc a 406 | "30", // EE st a, step_count 407 | "3A", // EF 408 | "56", // F0 ldi c, BANK_OUTER 409 | "03", // F1 410 | "57", // F2 ldi d, outer_up 411 | "80", // F3 412 | "03", // F4 jmp set_bank 413 | "31", // F5 414 | "EE", // F6 main_rnd_loop: rnd c ; main_rnd: 415 | "EF", // F7 rnd d 416 | "BE", // F8 and c, d 417 | "32", // F9 st c, a 418 | "60", // FA inc a 419 | "75", // FB dec b 420 | "0C", // FC jnz main_rnd_loop 421 | "F6", // FD 422 | "03", // FE jmp main_count 423 | "EB", // FF 424 | 425 | // 0180: BANK_OUTER (3) 426 | "56", // 80 outer_up: ldi c, display 427 | "40", // 81 428 | "3A", // 82 st c, display_ptr 429 | "3D", // 83 430 | "E6", // 84 shr c ; = 0x20 431 | "3A", // 85 st c, lower_area 432 | "37", // 86 433 | "54", // 87 ldi a, 0xFE 434 | "FE", // 88 435 | "51", // 89 ld b, current_area ; c = 32 436 | "36", // 8A 437 | "57", // 8B ldi d, outer_up_loop 438 | "8D", // 8C 439 | "34", // 8D outer_up_loop: st a, b 440 | "65", // 8E inc b 441 | "7A", // 8F dec c 442 | "2C", // 90 jnz d 443 | "03", // 91 jmp outer_begin 444 | "96", // 92 445 | "AA", // 93 outer_down: clr c ; = upper_area 446 | "3A", // 94 st c, lower_area 447 | "37", // 95 448 | "55", // 96 outer_begin: ldi b, outer_center 449 | "C4", // 97 450 | "35", // 98 st b, outer_edge_jmp +1 451 | "B6", // 99 452 | "55", // 9A ldi b, outer_right 453 | "B7", // 9B 454 | "35", // 9C st b, outer_center_jmp +1 455 | "CE", // 9D 456 | "51", // 9E ld b, display_ptr 457 | "3D", // 9F 458 | 459 | // 01A0 460 | "44", // A0 ld a, b 461 | "E4", // A1 shr a 462 | "56", // A2 ldi c, 0x11 ; = current_area +1 463 | "11", // A3 464 | "53", // A4 ld d, lower_area 465 | "37", // A5 466 | "C0", // A6 outer_edge: rcl a 467 | "0D", // A7 jns outer_edge_end 468 | "B4", // A8 469 | "49", // A9 ld b, c 470 | "65", // AA inc b 471 | "39", // AB st b, c 472 | "4D", // AC ld b, d 473 | "65", // AD inc b 474 | "3D", // AE st b, d 475 | "6F", // AF inc d 476 | "4D", // B0 ld b, d 477 | "65", // B1 inc b 478 | "3D", // B2 st b, d 479 | "7F", // B3 dec d 480 | "7A", // B4 outer_edge_end: dec c 481 | "03", // B5 outer_edge_jmp: jmp outer_center 482 | "C4", // B6 483 | "55", // B7 outer_right: ldi b, outer_edge 484 | "A6", // B8 485 | "35", // B9 st b, outer_center_jmp +1 486 | "CE", // BA 487 | "55", // BB ldi b, outer_end 488 | "ED", // BC 489 | "35", // BD st b, outer_edge_jmp +1 490 | "B6", // BE 491 | "51", // BF ld b, display_ptr 492 | 493 | // 01C0 494 | "3D", // C0 495 | "65", // C1 inc b 496 | "44", // C2 ld a, b 497 | "E4", // C3 shr a 498 | "55", // C4 outer_center: ldi b, 8 499 | "08", // C5 500 | "35", // C6 st b, incrementor 501 | "3C", // C7 502 | "51", // C8 outer_center_loop: ld b, incrementor 503 | "3C", // C9 504 | "75", // CA dec b 505 | "35", // CB st b, incrementor 506 | "3C", // CC 507 | "08", // CD outer_center_jmp: jz outer_right 508 | "B7", // CE 509 | "C0", // CF rcl a 510 | "0D", // D0 jns outer_center_else 511 | "E9", // D1 512 | "49", // D2 ld b, c 513 | "65", // D3 inc b 514 | "39", // D4 st b, c 515 | "6A", // D5 inc c 516 | "6A", // D6 inc c 517 | "49", // D7 ld b, c 518 | "65", // D8 inc b 519 | "39", // D9 st b, c 520 | "7A", // DA dec c 521 | "4D", // DB ld b, d 522 | "65", // DC inc b 523 | "3D", // DD st b, d 524 | "6F", // DE inc d 525 | "4D", // DF ld b, d 526 | 527 | // 01E0 528 | "65", // E0 inc b 529 | "3D", // E1 st b, d 530 | "6F", // E2 inc d 531 | "4D", // E3 ld b, d 532 | "65", // E4 inc b 533 | "3D", // E5 st b, d 534 | "7F", // E6 dec d 535 | "03", // E7 jmp outer_center_loop 536 | "C8", // E8 537 | "6A", // E9 outer_center_else: inc c 538 | "6F", // EA inc d 539 | "03", // EB jmp outer_center_loop 540 | "C8", // EC 541 | "56", // ED outer_end: ldi c, BANK_MAIN 542 | "02", // EE 543 | "50", // EF ld a, lower_area 544 | "37", // F0 545 | "B0", // F1 test a 546 | "08", // F2 jz outer_end_down 547 | "FC", // F3 548 | "54", // F4 outer_end_up: ldi a, 0x42 549 | "42", // F5 550 | "30", // F6 st a, display_ptr 551 | "3D", // F7 552 | "57", // F8 ldi d, main_row 553 | "80", // F9 554 | "03", // FA jmp set_bank 555 | "31", // FB 556 | "57", // FC outer_end_down: ldi d, main_final 557 | "D0", // FD 558 | "03", // FE jmp set_bank 559 | "31", // FF 560 | ]; 561 | } 562 | -------------------------------------------------------------------------------- /ComputerEmulator/V2/Ram.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerEmulator.V2; 2 | 3 | using ComputerEmulator; 4 | 5 | internal class Ram 6 | { 7 | private const int Size = 1024; 8 | private readonly IList _main = new MyByte[Size]; 9 | private readonly IList _screen = new MyByte[32]; 10 | private MyByte _bcd = default; 11 | private bool _bcdSetted = false; 12 | private MyByte _in = new(0); 13 | private static readonly MyByte _bcdAddr = new("3A"); 14 | private static readonly MyByte _ioAddr = new("3E"); 15 | private static readonly MyByte _bankAddr = new("3F"); 16 | private static readonly MyByte _screenMinAddr = new("40"); 17 | private static readonly MyByte _screenMaxAddr = new("5F"); 18 | private int _bankShift = 0; 19 | 20 | private const byte Mode_Terminal = 1; 21 | private const byte Mode_Bcd = 2; 22 | private const byte Mode_Screen = 4; 23 | private const byte Mode_ScreenColor = 8; 24 | 25 | public MyByte Read(MyByte rawAddr) 26 | { 27 | if (rawAddr == _ioAddr) 28 | { 29 | var value = _in; 30 | _in = 0; 31 | return value; 32 | } 33 | 34 | return Get(rawAddr); 35 | } 36 | 37 | public MyByte Get(string rawAddr) => Get(new MyByte(rawAddr)); 38 | 39 | public MyByte Get(MyByte rawAddr) => _main[GetFullAddr(rawAddr)]; 40 | 41 | public void Write(MyByte rawAddr, MyByte value) 42 | { 43 | var addr = GetFullAddr(rawAddr); 44 | WriteInternal(addr, value); 45 | } 46 | 47 | private void WriteInternal(int addr, MyByte value) 48 | { 49 | _main[addr] = value; 50 | 51 | if ((_main[_ioAddr] & Mode_Bcd) != 0 && addr == _bcdAddr) 52 | { 53 | _bcd = value; 54 | _bcdSetted = true; 55 | Console.UpdatePin(); 56 | } 57 | 58 | if ((_main[_ioAddr] & Mode_Screen) != 0 && addr >= _screenMinAddr && addr <= _screenMaxAddr) 59 | { 60 | _screen[addr - 64] = value; 61 | Console.UpdatePin(); 62 | } 63 | 64 | if (addr == _bankAddr) 65 | { 66 | var maskedValue = Math.Max(1, value & 0b00000111); 67 | _bankShift = (maskedValue - 1) * 128; 68 | } 69 | } 70 | 71 | public void SetIn(MyByte value) => _in = value; 72 | 73 | private int GetFullAddr(MyByte rawAddr) => rawAddr < 128 ? rawAddr : rawAddr + _bankShift; 74 | 75 | public void Load(IReadOnlyList bytes) 76 | { 77 | if (bytes.Count > Size) 78 | throw new InvalidOperationException(); 79 | 80 | for (var i = 0; i < bytes.Count; i++) 81 | if (i != _bankAddr) 82 | WriteInternal(i, bytes[i]); 83 | } 84 | 85 | public IReadOnlyList Display() 86 | { 87 | var items = new List(); 88 | 89 | for (var i = _screenMinAddr; i < _screenMaxAddr; i += 2) 90 | { 91 | var row = "rw`"; 92 | row += Pixels(_screen[i - 64]); 93 | row += Pixels(_screen[i - 63]); 94 | items.Add(row); 95 | 96 | items.Add(" "); 97 | items.Add("rw`" + Pixels(_main[i + 32]) + Pixels(_main[i + 33])); 98 | 99 | if (i < _screenMaxAddr - 2) 100 | items.Add("\n"); 101 | } 102 | 103 | items.Add(" BCD: "); 104 | 105 | if (_bcdSetted) 106 | items.Add($"G`{_bcd.Value}"); 107 | 108 | return items; 109 | } 110 | 111 | private static string Pixels(MyByte value) 112 | { 113 | var hex = value.Hex; 114 | return $"{_pixelMap[hex[0]]}{_pixelMap[hex[1]]}"; 115 | } 116 | 117 | private static readonly Dictionary _pixelMap = new() 118 | { 119 | { '0', " "}, 120 | { '1', " ▓▓"}, 121 | { '2', " ▓▓ "}, 122 | { '3', " ▓▓▓▓"}, 123 | { '4', " ▓▓ "}, 124 | { '5', " ▓▓ ▓▓"}, 125 | { '6', " ▓▓▓▓ "}, 126 | { '7', " ▓▓▓▓▓▓"}, 127 | { '8', "▓▓ "}, 128 | { '9', "▓▓ ▓▓"}, 129 | { 'A', "▓▓ ▓▓ "}, 130 | { 'B', "▓▓ ▓▓▓▓"}, 131 | { 'C', "▓▓▓▓ "}, 132 | { 'D', "▓▓▓▓ ▓▓"}, 133 | { 'E', "▓▓▓▓▓▓ "}, 134 | { 'F', "▓▓▓▓▓▓▓▓"}, 135 | }; 136 | } 137 | -------------------------------------------------------------------------------- /ComputerEmulator/V2/SpaceFight.cs: -------------------------------------------------------------------------------- 1 | namespace ComputerEmulator.V2; 2 | 3 | internal static class SpaceFight 4 | { 5 | public static IReadOnlyList Bytes() 6 | { 7 | return _bytes.Select(x => new MyByte(x)).ToList(); 8 | } 9 | 10 | // WIN_LEFT equ 30 11 | // STEP_CNT equ 14 12 | // KEY_LEFT equ 0x11 13 | // KEY_RIGHT equ 0x13 14 | // KEY_FIRE equ 0x20 15 | 16 | private static readonly IReadOnlyList _bytes = [ 17 | 18 | // 00 19 | "55", // 00 clear: ldi b, clear_loop 20 | "06", // 01 21 | "56", // 02 ldi c, 4 22 | "04", // 03 23 | "57", // 04 ldi d, 0x5B 24 | "5B", // 05 25 | "3C", // 06 clear_loop: st a, d 26 | "7F", // 07 dec d 27 | "7A", // 08 dec c 28 | "24", // 09 jnz b 29 | "56", // 0A ldi c, 12 30 | "0C", // 0B 31 | "EC", // 0C random: rnd a 32 | "ED", // 0D rnd b 33 | "B4", // 0E and a, b 34 | "E0", // 0F shl a 35 | "3C", // 10 st a, d 36 | "7F", // 11 dec d 37 | "EC", // 12 rnd a 38 | "ED", // 13 rnd b 39 | "B4", // 14 and a, b 40 | "E4", // 15 shr a 41 | "3C", // 16 st a, d 42 | "7F", // 17 dec d 43 | "7A", // 18 dec c 44 | "0C", // 19 jnz random 45 | "0C", // 1A 46 | "03", // 1B random_jmp: jmp score 47 | "1D", // 1C 48 | "50", // 1D score: ld a, win_left 49 | "3A", // 1E 50 | "30", // 1F st a, win_left 51 | 52 | // 20 53 | "3A", // 20 54 | "54", // 21 ldi a, step 55 | "25", // 22 56 | "30", // 23 st a, random_jmp +1 57 | "1C", // 24 58 | "55", // 25 step: ldi b, KEY_LEFT 59 | "11", // 26 60 | "56", // 27 ldi c, KEY_RIGHT 61 | "13", // 28 62 | "57", // 29 ldi d, KEY_FIRE 63 | "20", // 2A 64 | "50", // 2B step_loop: ld a, step_left 65 | "3D", // 2C 66 | "70", // 2D dec a 67 | "09", // 2E js level 68 | "B6", // 2F 69 | "30", // 30 st a, step_left 70 | "3D", // 31 71 | "50", // 32 ld a, in_out 72 | "3E", // 33 73 | "D3", // 34 xor d, a 74 | "08", // 35 jz fire 75 | "8B", // 36 76 | "D2", // 37 xor c, a 77 | "03", // 38 jmp step_end 78 | "60", // 39 79 | "1E", // 3A win_left db WIN_LEFT ; bcd 80 | "00", // 3B --- ; bcd (reserved) 81 | "0E", // 3C step_cnt db STEP_CNT 82 | "0E", // 3D step_left db STEP_CNT 83 | "06", // 3E in_out db 0b00000110 ; b/w display + bcd 84 | "00", // 3F bank db 0 85 | 86 | // 40 (display) 87 | "28", // 40 00101000 00000000 88 | "00", // 41 89 | "20", // 42 00100000 00101000 90 | "28", // 43 91 | "00", // 44 00000000 00001000 92 | "08", // 45 93 | "09", // 46 00001001 00000000 94 | "00", // 47 95 | "21", // 48 00100001 00100000 96 | "20", // 49 97 | "23", // 4A 00100011 10001000 98 | "88", // 4B 99 | "03", // 4C 00000011 10001000 100 | "88", // 4D 101 | "0B", // 4E 00001011 10100000 102 | "A0", // 4F 103 | "0A", // 50 00001010 10100000 104 | "A0", // 51 105 | "2E", // 52 00101110 11101000 106 | "E8", // 53 107 | "2F", // 54 00101111 11101000 108 | "E8", // 55 109 | "3B", // 56 00111011 10111000 110 | "B8", // 57 111 | "35", // 58 00110101 01011000 112 | "58", // 59 113 | "20", // 5A 00100000 00001000 114 | "08", // 5B 115 | "01", // 5C 00000001 00000000 116 | "00", // 5D 117 | "03", // 5E 00000011 10000000 118 | "80", // 5F 119 | 120 | // 60 121 | "08", // 60 step_end: jz right 122 | "78", // 61 123 | "D1", // 62 xor b, a 124 | "0C", // 63 jnz step_loop 125 | "2B", // 64 126 | "51", // 65 left: ld b, 0x5E 127 | "5E", // 66 128 | "E1", // 67 shl b 129 | "0A", // 68 jc step 130 | "25", // 69 131 | "55", // 6A ldi b, 0x5F 132 | "5F", // 6B 133 | "56", // 6C ldi c, 4 134 | "04", // 6D 135 | "57", // 6E ldi d, left_loop 136 | "70", // 6F 137 | "44", // 70 left_loop: ld a, b 138 | "C0", // 71 rcl a 139 | "34", // 72 st a, b 140 | "75", // 73 dec b 141 | "7A", // 74 dec c 142 | "2C", // 75 jnz d 143 | "03", // 76 jmp step 144 | "25", // 77 145 | "51", // 78 right: ld b, 0x5F 146 | "5F", // 79 147 | "E5", // 7A shr b 148 | "0A", // 7B jc step 149 | "25", // 7C 150 | "55", // 7D ldi b, 0x5C 151 | "5C", // 7E 152 | "56", // 7F ldi c, 4 153 | 154 | // 80 155 | "04", // 80 156 | "57", // 81 ldi d, right_loop 157 | "83", // 82 158 | "44", // 83 right_loop: ld a, b 159 | "D0", // 84 rcr a 160 | "34", // 85 st a, b 161 | "65", // 86 inc b 162 | "7A", // 87 dec c 163 | "2C", // 88 jnz d 164 | "03", // 89 jmp step 165 | "25", // 8A 166 | "57", // 8B fire: ldi d, 0x5C 167 | "5C", // 8C 168 | "4C", // 8D ld a, d 169 | "B0", // 8E test a 170 | "0C", // 8F jnz fire2 171 | "92", // 90 172 | "6F", // 91 inc d 173 | "4D", // 92 fire2: ld b, d 174 | "56", // 93 ldi c, 14 175 | "0E", // 94 176 | "7F", // 95 fire_loop: dec d 177 | "7F", // 96 dec d 178 | "4C", // 97 ld a, d 179 | "B4", // 98 and a, b 180 | "0C", // 99 jnz fire_hit 181 | "A0", // 9A 182 | "7A", // 9B dec c 183 | "0C", // 9C jnz fire_loop 184 | "95", // 9D 185 | "03", // 9E jmp step 186 | "25", // 9F 187 | 188 | // A0 189 | "4C", // A0 fire_hit: ld a, d 190 | "D4", // A1 xor a, b 191 | "3C", // A2 st a, d 192 | "50", // A3 ld a, win_left 193 | "3A", // A4 194 | "70", // A5 dec a 195 | "30", // A6 st a, win_left 196 | "3A", // A7 197 | "0C", // A8 jnz step 198 | "25", // A9 199 | "55", // AA win: ldi b, 0x40 200 | "40", // AB 201 | "56", // AC ldi c, 0xE0 202 | "E0", // AD 203 | "57", // AE ldi d, win_loop 204 | "B0", // AF 205 | "48", // B0 win_loop: ld a, c 206 | "34", // B1 st a, b 207 | "65", // B2 inc b 208 | "6A", // B3 inc c 209 | "2C", // B4 jnz d 210 | "01", // B5 hlt 211 | "50", // B6 level: ld a, step_cnt 212 | "3C", // B7 213 | "70", // B8 dec a 214 | "70", // B9 dec a 215 | "30", // BA st a, step_cnt 216 | "3C", // BB 217 | "30", // BC st a, step_left 218 | "3D", // BD 219 | "50", // BE ld a, 0x5A 220 | "5A", // BF 221 | 222 | // C0 223 | "51", // C0 ld b, 0x5B 224 | "5B", // C1 225 | "56", // C2 ldi c, 26 226 | "1A", // C3 227 | "57", // C4 ldi d, 0x59 228 | "59", // C5 229 | "C4", // C6 or a, b 230 | "08", // C7 jz scroll 231 | "D1", // C8 232 | "6A", // C9 level_lose: inc c 233 | "6A", // CA inc c 234 | "6F", // CB inc d 235 | "6F", // CC inc d 236 | "54", // CD ldi a, 0x01 ; hlt 237 | "01", // CE 238 | "30", // CF st a, random_jmp 239 | "1B", // D0 240 | "AD", // D1 scroll: mov b, d 241 | "44", // D2 ld a, b 242 | "65", // D3 inc b 243 | "65", // D4 inc b 244 | "34", // D5 st a, b 245 | "7F", // D6 dec d 246 | "7A", // D7 dec c 247 | "0C", // D8 jnz scroll 248 | "D1", // D9 249 | "6A", // DA inc c 250 | "6F", // DB inc d 251 | "6F", // DC inc d 252 | "03", // DD jmp random 253 | "0C", // DE 254 | "00", // DF --- 255 | 256 | // E0 (prize) 257 | "00", // E0 00000000 00000000 258 | "00", // E1 259 | "0D", // E2 00001101 10000000 260 | "80", // E3 261 | "32", // E4 00110010 01000000 262 | "40", // E5 263 | "40", // E6 01000000 00100000 264 | "20", // E7 265 | "4B", // E8 01001011 11100000 266 | "E0", // E9 267 | "54", // EA 01010100 00111100 268 | "3C", // EB 269 | "30", // EC 00110000 00100010 270 | "22", // ED 271 | "2A", // EE 00101010 10111010 272 | "BA", // EF 273 | "2A", // F0 00101010 10101010 274 | "AA", // F1 275 | "2A", // F2 00101010 10101010 276 | "AA", // F3 277 | "2A", // F4 00101010 10111010 278 | "BA", // F5 279 | "2A", // F6 00101010 10100010 280 | "A2", // F7 281 | "20", // F8 00100000 00111100 282 | "3C", // F9 283 | "20", // FA 00100000 00100000 284 | "20", // FB 285 | "1F", // FC 00011111 11000000 286 | "C0", // FD 287 | "00", // FE 00000000 00000000 288 | "00", // FF 289 | ]; 290 | } 291 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Стрелочки 2 | Коллекция карт, созданных в игре [«Стрелочки»](https://logic-arrows.io/) от 3 | [Onigiri](https://github.com/ArtemOnigiri). 4 |
5 | 6 | Об игре от автора:
7 | [![Видео об игре](img/youtube.jpg)](https://www.youtube.com/watch?v=q_ve9SsuyvU) 8 |


9 | 10 | ## Карты 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 34 | 38 | 39 | 40 | 45 | 50 | 51 | 52 | 56 | 61 | 62 | 63 | 68 | 72 | 73 | 74 | 79 | 84 | 85 | 86 | 91 | 96 | 97 | 98 | 103 | 107 | 108 | 109 | 114 | 119 | 120 | 121 | 126 | 130 | 131 | 132 |
16 |

Компьютер из стрелочек 17 | Gen. 2

18 | Компьютер из стрелочек (Gen. 2) 20 |
22 |

Компьютер из стрелочек 23 | Gen. 1

24 | Компьютер из стрелочек (Gen. 1) 26 |
31 |

Игра «Жизнь»

32 | Игра «Жизнь» 33 |
35 |

Матрица

36 | Матрица 37 |
41 |

Матрица цветная

42 | Матрица цветная 44 |
46 |

Матрицы плотные

47 | Матрицы плотные 49 |
53 |

RAM 32K

54 | RAM 32K 55 |
57 |

RAM 256 v2

58 | RAM 256 v2 60 |
64 |

RAM 256 v1

65 | RAM 256 v1 67 |
69 |

RAM 64

70 | RAM 64 71 |
75 |

ROM компактная

76 | ROM компактная 78 |
80 |

Конвертер BCD

81 | Конвертер BCD 83 |
87 |

Сегментные дисплеи

88 | Сегментные дисплеи 90 |
92 |

Прототип процессора

93 | Прототип процессора 95 |
99 |

Передатчики

100 | Передатчики 102 |
104 |

Вентили

105 | Вентили 106 |
110 |

Умножитель

111 | Умножитель 113 |
115 |

Сумматор

116 | Сумматор 118 |
122 |

Вычитатель

123 | Вычитатель 125 |
127 |

Муравей

128 | Муравей 129 |
133 | -------------------------------------------------------------------------------- /computer-v1/README.md: -------------------------------------------------------------------------------- 1 | # Компьютер из стрелочек *Gen. 1* 2 | 3 | > [!TIP] 4 | > См. новый [Компьютер из стрелочек ***Gen. 2***](../computer-v2/README.md). 5 | 6 |
7 | 8 | 9 | 10 | 11 | 19 | 23 | 24 | 25 |
12 | Полноценный компьютер, целиком сделанный из стрелочек. Позволяет создавать и запускать 13 | различные программы и игры.

14 | Карта с компьютером

15 | Устройство и характеристики

16 | Программирование

17 | Готовые программы 18 |
20 | Компьютер из стрелочек (Gen. 1) 22 |
26 |
27 | 28 | 29 | ## Демонстрация работы 30 | Зайдите на [карту с компьютером](https://logic-arrows.io/map-lVeJ9jtX). В нижнем ползунке установите 31 | максимальную скорость. Нажмите на кнопку `Hello world` и дождитесь загрузки программы в память 32 | компьютера. Далее нажмите на кнопку `RUN` и наблюдайте, как программа выведет на дисплей котика и 33 | надпись «Hello world». По окончании загорится лампочка `DONE`. Чтобы запустить на компьютере вашу 34 | собственную программу, см. [Программирование](programming.md). 35 |


36 | 37 | 38 | ## Готовые программы 39 | 40 | 41 | 42 | 52 | 57 | 58 | 59 | 66 | 72 | 73 | 74 | 79 | 85 | 86 | 87 |
43 |

Игра «Space Fight!»

44 | Игра «Space Fight!»
46 | Внизу дисплея расположен корабль, а остальная область заполнена врагами. Нужно сбить 30 47 | врагов за ограниченное время. Враги приближаются с нарастающей скоростью, и, если враг 48 | достигнет корабля, игра проиграна. В случае победы на дисплее появится приз.

49 | Игра занимает весь доступный объём памяти 256 байт и выложена на 50 | отдельной карте. 51 |
53 |

Hello World

54 | Hello World
55 | Выводит на дисплей котика и надпись «Hello world». 56 |
60 |

Prime Numbers

61 | Prime Numbers
63 | Находит первые 16 простых чисел и выводит их на дисплей в двоичном формате. Выполнение 64 | занимает 3691 операцию. 65 |
67 |

Fibonacci Sequence

68 | Fibonacci Sequence
70 | Находит 10 чисел Фибоначчи и выводит их на дисплей в двоичном формате. 71 |
75 |

Typewriter

76 | Typewriter
77 | Выводит в терминал текст, набираемый на клавиатуре. 78 |
80 |

Font Test

81 | Font Test
82 | Выводит в терминал все возможные символы (кодировка 83 | cp1251). 84 |
88 | -------------------------------------------------------------------------------- /computer-v1/asm/fibonacci-sequence.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Fibonacci Sequence" program for a computer made of logic arrows ## 3 | ## Исходный код программы "Fibonacci Sequence" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v1 ## 5 | ## (с) 2023 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | ldi a, 0x80 ; Код для подключения дисплея 10 | st a, 0x3F ; Подключаем вывод 11 | inc b ; Подготавливаем число 1 12 | inc c ; Подготавливаем число 1 13 | ldi d, 0x3F ; Указатель для вывода на дисплей 14 | 15 | loop: inc d ; Смещаем указатель на дисплее на ряд ниже 16 | inc d 17 | mov a, b ; Берём первое число 18 | add a, c ; Складываем последние два числа 19 | mov b, a ; Сохраняем сумму в первое число 20 | st a, d ; Выводим сумму на дисплей 21 | inc d ; Смещаем указатель на дисплее на ряд ниже 22 | inc d 23 | mov a, c ; Берём второе число 24 | add a, b ; Складываем последние два числа 25 | mov c, a ; Сохраняем сумму во второе число 26 | st a, d ; Выводим сумму на дисплей 27 | ldi a, 0x53 ; Адрес на дисплее для последнего числа 28 | xor a, d 29 | jnz loop ; Если мы ещё не достигли этого адреса, повторяем итерацию 30 | 31 | hlt ; Завершаем выполнение программы 32 | -------------------------------------------------------------------------------- /computer-v1/asm/font-test.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Font Test" program for a computer made of logic arrows ## 3 | ## Исходный код программы "Font Test" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v1 ## 5 | ## (с) 2025 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | ldi c, 0x40 ; Код для подключения терминала и одновременно адрес для вывода на него 10 | st c, 0x3F ; Подключаем вывод 11 | ldi d, loop ; Запоминаем адрес начала итерации для ускорения цикла 12 | 13 | repeat: ldi a, 0x20 ; Берём код первого символа (пробел) 14 | 15 | loop: st a, c ; Выводим символ в терминал 16 | inc a ; Берём код следующего символа 17 | jnz d ; Повторяем итерацию, пока код символа после 0xFF не станет 0x00 18 | 19 | jmp repeat ; Бесконечно повторяем программу 20 | -------------------------------------------------------------------------------- /computer-v1/asm/hello-world.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Hello World" program for a computer made of logic arrows ## 3 | ## Исходный код программы "Hello World" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v1 ## 5 | ## (с) 2023 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | ldi a, 0x80 ; Код для подключения дисплея 10 | st a, 0x3F ; Подключаем вывод 11 | ldi b, image_size ; Счётчик, по которому программа завершится 12 | ldi c, image ; Указатель на начало картинки 13 | ldi d, 0x40 ; Указатель на начало области дисплея 14 | 15 | ; Цикл копирования картинки на дисплей 16 | loop: ld a, c ; Читаем один байт картинки 17 | st a, d ; Выводим байт картинки на дисплей 18 | inc c ; Смещаем указатель на картинку 19 | inc d ; Смещаем указатель на дисплей 20 | dec b ; Уменьшаем счётчик 21 | jnz loop ; Если счётчик не равен нулю, повторяем итерацию 22 | 23 | hlt ; Завершаем выполнение программы 24 | 25 | void db 0, 0, 0, 0, 0, 0 ; Выравнивание памяти (необязательно) 26 | 27 | ; Картинка для вывода на дисплей 28 | image db 0b00000010, 0b01000000, ; ██ ██ ; 29 | 0b00110101, 0b10101100, ; ████ ██ ████ ██ ████ ; 30 | 0b00000100, 0b00100000, ; ██ ██ ; 31 | 0b00110011, 0b11001100, ; ████ ████████ ████ ; 32 | 0b00000000, 0b00000000, ; ; 33 | 0b10100000, 0b10100000, ; ██ ██ ██ ██ ; 34 | 0b10100100, 0b10100100, ; ██ ██ ██ ██ ██ ██ ; 35 | 0b11101010, 0b10101010, ; ██████ ██ ██ ██ ██ ██ ██ ; 36 | 0b10101100, 0b10101010, ; ██ ██ ████ ██ ██ ██ ██ ; 37 | 0b10100110, 0b10100100, ; ██ ██ ████ ██ ██ ██ ; 38 | 0b00000000, 0b00000000, ; ; 39 | 0b00000000, 0b00010001, ; ██ ██ ; 40 | 0b10100100, 0b11010001, ; ██ ██ ██ ████ ██ ██ ; 41 | 0b11101010, 0b10010011, ; ██████ ██ ██ ██ ██ ████ ; 42 | 0b11101010, 0b10010101, ; ██████ ██ ██ ██ ██ ██ ██ ; 43 | 0b01000100, 0b10010011 ; ██ ██ ██ ██ ████ ; 44 | 45 | image_size equ $ - image ; Размер картинки 46 | -------------------------------------------------------------------------------- /computer-v1/asm/prime-numbers.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Prime Numbers" program for a computer made of logic arrows ## 3 | ## Исходный код программы "Prime Numbers" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v1 ## 5 | ## (с) 2023 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | ldi a, 0x80 ; Код для подключения дисплея 10 | st a, 0x3F ; Подключаем вывод 11 | ldi b, 0x02 ; Первое простое число 2 12 | ldi d, 0x41 ; Указатель на дисплее для первого числа 13 | st b, d ; Выводим число на дисплей 14 | inc b ; Второе простое число 3 15 | inc d ; Дважды смещаем указатель на дисплее, чтобы перейти на следующий ряд 16 | inc d 17 | st b, d ; Выводим число на дисплей 18 | 19 | ; Цикл подбора кандидатов на простые числа 20 | next: inc b ; Дважды увеличиваем число-кандидат - идём только по нечётным числам 21 | inc b 22 | ldi d, 0x41 ; Устанавливаем указатель на первое простое число на дисплее 23 | 24 | ; Цикл подбора множителей среди уже найденных простых чисел 25 | factor: inc d ; Смещаем указатель на следующее простое число на дисплее 26 | inc d 27 | ld c, d ; Берём простое число с дисплея в качестве множителя 28 | mov a, b ; Копируем число-кандидат 29 | shr a ; Делим его на 2 30 | sub a, c ; Вычитаем из него текущий множитель 31 | js prime ; Если множитель больше половины числа-кандидата, то результат будет 32 | ; меньше нуля, и значит дальше подбирать множители нет смысла - мы 33 | ; уже нашли новое простое число 34 | mov a, b ; Копируем число-кандидат 35 | 36 | ; Цикл вычитания множителя из числа-кандидата 37 | loop: sub a, c ; Вычитание 38 | jz next ; Если результат равен нулю, значит число не простое, переходим к 39 | ; следующему кандидату 40 | jns loop ; Если результат больше нуля, продолжаем вычитать 41 | 42 | jmp factor ; Если результат меньше нуля, переходим к следующему множителю 43 | 44 | ; Работаем с найденным простым числом 45 | prime: ld d, last ; Читаем указатель на последнее найденное простое число на дисплее 46 | inc d ; Дважды смещаем указатель 47 | inc d 48 | st d, last ; Сохраняем указатель 49 | st b, d ; Выводим новое простое число на дисплей 50 | ldi a, 0x5F ; Берём указатель на последнее возможное место в нижнем ряду дисплея 51 | xor a, d ; Сравниваем два указателя 52 | jnz next ; Если они не равны, переходим к рассмотрению следующего 53 | ; числа-кандидата 54 | hlt ; Завершаем выполнение программы 55 | 56 | last db 0x43 ; Указатель на последнее найденное простое число на дисплее 57 | -------------------------------------------------------------------------------- /computer-v1/asm/space-fight.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Space Fight!" game for a computer made of logic arrows ## 3 | ## Исходный код игры "Space Fight!" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v1 ## 5 | ## (с) 2024 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | 10 | ; Константы 11 | WIN_LEFT equ 30 ; Количество врагов, которое необходимо сбить для победы 12 | STEP_CNT equ 14 ; Количество доступных ходов на первом уровне 13 | KEY_LEFT equ 0x11 ; Код клавиши "Влево" 14 | KEY_RIGHT equ 0x13 ; Код клавиши "Вправо" 15 | KEY_FIRE equ 0x20 ; Код клавиши "Пробел" (огонь) 16 | OUT_BCD equ 0x10 ; Код для подключения цифрового индикатора 17 | OUT_DISPLAY equ 0x80 ; Код для подключения дисплея 18 | 19 | 20 | 21 | ; Очищаем нижнюю часть дисплея 22 | clear: ldi c, 0x5C 23 | ldi d, 0x58 ; Верхняя граница очистки дисплея, а также для блока 24 | ; "random" нижняя граница заполнения врагами 25 | clear_loop: dec c 26 | st b, c 27 | mov a, c 28 | xor a, d 29 | jnz clear_loop 30 | 31 | ; Заполняем дисплей случайно расположенными врагами. 32 | ; Регистр D уже содержит указатель на дисплее на нижнюю границу заполнения. 33 | random: ldi c, display ; Адрес начала области дисплея как верхняя граница 34 | ; заполнения врагами 35 | 36 | random_loop: rnd a ; Работаем с байтом в правой части дисплея 37 | rnd b 38 | and a, b ; Совмещаем два случайных байта, чтобы для каждого бита 39 | ; получилась вероятность 25% 40 | shl a ; Отодвигаем биты от края дисплея, где невозможно произвести 41 | ; выстрел 42 | dec d 43 | st a, d 44 | rnd a ; Повторяем то же самое для левой части дисплея 45 | rnd b 46 | and a, b 47 | shr a 48 | dec d 49 | st a, d 50 | mov a, d 51 | xor a, c 52 | jnz random_loop 53 | 54 | ; Выводим счётчик врагов на цифровой индикатор 55 | score: ld a, win_left ; Читаем счётчик врагов. Если игра проиграна, эта инструкция 56 | ; будет перезаписана на "hlt". 57 | ldi d, display ; Адрес начала области дисплея, в т.ч. для блока "win" 58 | ld b, d ; Запоминаем первый байт изображения на дисплее 59 | ldi c, OUT_BCD 60 | st c, out ; Переключаем вывод на цифровой индикатор 61 | st a, d ; Выводим значение счётчика врагов 62 | ldi c, OUT_DISPLAY 63 | st c, out ; Переключаем вывод на дисплей 64 | st b, d ; Восстанавливаем в памяти первый байт изображения 65 | add a, 0 ; Если счётчик врагов равен нулю, то игра выиграна 66 | jz win 67 | 68 | ; Уменьшаем счётчик ходов 69 | step: ldi b, step2 ; Запоминаем адрес для перехода к следующему шагу для блоков 70 | ; "keys", "left", "right" 71 | step2: ld a, step_left 72 | dec a 73 | js level ; Если ходов не осталось, переходим на следующий уровень 74 | st a, step_left 75 | jmp keys ; Переходим через зарезервированную область памяти 76 | 77 | void1 db 0 78 | 79 | ; Переменные и порты 80 | 81 | step_cnt db STEP_CNT ; Количество доступных ходов на текущем уровне 82 | step_left db STEP_CNT ; Счётчик оставшихся ходов на текущем уровне 83 | win_left db WIN_LEFT ; Счётчик врагов, оставшихся до победы 84 | bank db 0 ; Порт банка памяти (не используется) 85 | in db 0 ; Порт ввода 86 | out db OUT_DISPLAY ; Порт вывода, подключаем дисплей 87 | 88 | ; Область дисплея 0x40...0x5F содержит заставку, отображаемую во время загрузки программы 89 | display db 0b00101000, 0b00000000, ; ██ ██ ; 90 | 0b00100000, 0b00101000, ; ██ ██ ██ ; 91 | 0b00000000, 0b00001000, ; ██ ; 92 | 0b00001001, 0b00000000, ; ██ ██ ; 93 | 0b00100001, 0b00100000, ; ██ ██ ██ ; 94 | 0b00100011, 0b10001000, ; ██ ██████ ██ ; 95 | 0b00000011, 0b10001000, ; ██████ ██ ; 96 | 0b00001011, 0b10100000, ; ██ ██████ ██ ; 97 | 0b00001010, 0b10100000, ; ██ ██ ██ ██ ; 98 | 0b00101110, 0b11101000, ; ██ ██████ ██████ ██ ; 99 | 0b00101111, 0b11101000, ; ██ ██████████████ ██ ; 100 | 0b00111011, 0b10111000, ; ██████ ██████ ██████ ; 101 | 0b00110101, 0b01011000, ; ████ ██ ██ ██ ████ ; 102 | 0b00100000, 0b00001000, ; ██ ██ ; 103 | 0b00000001, 0b00000000, ; ██ ; 104 | 0b00000011, 0b10000000 ; ██████ ; 105 | 106 | ; Опрашиваем клавиатуру на нажатие одной из клавиш управления кораблём. 107 | ; Регистр B уже содержит адрес для возврата к step2. 108 | keys: mov a, 0 109 | ld c, in ; Читаем код нажатой клавиши 110 | st a, in ; Обнуляем значение в порту, чтобы определять повторные 111 | ; нажатия 112 | ldi a, KEY_FIRE 113 | xor a, c 114 | jz fire 115 | ldi a, KEY_RIGHT 116 | xor a, c 117 | jz right 118 | ldi a, KEY_LEFT 119 | xor a, c 120 | jnz b ; Переходим к следующему ходу 121 | 122 | ; Сдвигаем корабль влево. 123 | ; Регистр B уже содержит адрес для возврата к step2. 124 | left: ld a, 0x5E 125 | shl a 126 | jc b ; Если мы у левого края, переходим к следующему ходу 127 | ldi d, 0x5F 128 | ldi c, 4 129 | 130 | left_loop: ld a, d 131 | rcl a 132 | st a, d 133 | dec d 134 | dec c 135 | jnz left_loop 136 | 137 | jmp b ; Переходим к следующему ходу 138 | 139 | ; Сдвигаем корабль вправо. 140 | ; Регистр B уже содержит адрес для возврата к step2. 141 | right: ld a, 0x5F 142 | shr a 143 | jc b ; Если мы у правого края, переходим к следующему ходу 144 | ldi d, 0x5C 145 | ldi c, 4 146 | 147 | right_loop: ld a, d 148 | rcr a 149 | st a, d 150 | inc d 151 | dec c 152 | jnz right_loop 153 | 154 | jmp b ; Переходим к следующему ходу 155 | 156 | ; Производим выстрел 157 | fire: ldi d, 0x5C ; Определяем стартовое положение снаряда 158 | ld a, d 159 | add a, 0 160 | jnz fire_shot 161 | inc d 162 | 163 | fire_shot: ld b, d 164 | ldi c, 14 165 | 166 | ; Невидимый полёт снаряда 167 | fire_loop: dec d 168 | dec d 169 | ld a, d 170 | and a, b ; Если попали, выходим из цикла 171 | jnz fire_hit 172 | dec c 173 | jnz fire_loop 174 | 175 | jmp step ; Промах, переходим к следующему ходу 176 | 177 | ; Попадание. Убираем врага с дисплея, уменьшаем счётчик врагов и переходим к следующему ходу. 178 | fire_hit: ld a, d 179 | xor a, b 180 | st a, d 181 | ld a, win_left 182 | dec a 183 | st a, win_left 184 | jmp score 185 | 186 | ; Переходим на следующий уровень. 187 | ; Здесь враги станут ближе к нам, а число доступных ходов уменьшится. 188 | level: ld a, step_cnt ; Уменьшаем число доступных ходов на два 189 | dec a 190 | dec a 191 | st a, step_cnt 192 | st a, step_left 193 | ldi c, 0x5A ; Нижняя граница сдвига карты для блока "scroll" 194 | ld a, c 195 | ld b, 0x5B 196 | or a, b 197 | jz scroll ; Если в третьем снизу ряду нет врагов, переходим к сдвигу 198 | ; карты 199 | inc c ; Игра проиграна, смещаем нижнюю границу сдвига карты на 200 | ; один ряд ниже 201 | inc c 202 | ldi a, 0xEC ; В блоке "score" первую инструкцию перезаписываем на "hlt" 203 | st a, score 204 | 205 | ; Сдвигаем всех врагов ближе к нам. 206 | ; Регистр C уже содержит указатель на нижнюю границу для сдвига карты. 207 | scroll: ldi d, 0x42 ; Верхняя граница сдвига карты, а также для блока "random" 208 | ; нижняя граница заполнения врагами 209 | scroll_loop: dec c 210 | mov a, c 211 | ld b, a 212 | inc a 213 | inc a 214 | st b, a 215 | xor a, d 216 | jnz scroll_loop 217 | 218 | jmp random ; Переходим к генерации врагов в верхнем ряду, а затем к 219 | ; следующему шагу 220 | 221 | ; Мы выиграли! Выводим на дисплей заставку-приз и завершаем выполнение программы. 222 | ; Регистр D уже содержит указатель на начало области дисплея. 223 | win: ldi c, prize 224 | 225 | win_loop: ld a, c 226 | st a, d 227 | inc d 228 | inc c ; После 0xFF значение перейдёт в 0x00, что означает конец 229 | ; вывода 230 | jnz win_loop 231 | 232 | hlt ; Остановка программы 233 | 234 | void2 db 0, 0 235 | 236 | ; Область 0xE0...0xFF содержит заставку-приз для победителя 237 | prize db 0b00000000, 0b00000000, 238 | 0b00000000, 0b00000000, 239 | 0b00000011, 0b11000000, 240 | 0b00000100, 0b00100000, 241 | 0b00001001, 0b00010000, 242 | 0b00010000, 0b00101000, 243 | 0b00010100, 0b00001000, 244 | 0b00100000, 0b10000100, 245 | 0b00100000, 0b00000100, 246 | 0b01000000, 0b00001010, 247 | 0b01010011, 0b11000010, 248 | 0b01000111, 0b11100010, 249 | 0b01000111, 0b11100010, 250 | 0b00110111, 0b11101100, 251 | 0b00001111, 0b11110000, 252 | 0b00000000, 0b00000000 253 | -------------------------------------------------------------------------------- /computer-v1/asm/typewriter.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Typewriter" program for a computer made of logic arrows ## 3 | ## Исходный код программы "Typewriter" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v1 ## 5 | ## (с) 2024 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | ldi b, 0x3E ; Адрес порта ввода 10 | ldi c, 0x40 ; Код для подключения терминала и одновременно адрес для вывода на него 11 | st c, 0x3F ; Подключаем вывод 12 | ldi d, loop ; Запоминаем адрес начала итерации для ускорения цикла 13 | 14 | loop: ld a, b ; Читаем код нажатой клавиши 15 | st a, c ; Выводим символ в терминал 16 | st d, b ; Пишем в порт ввода непечатный символ, тем самым сбрасывая порт. Это даёт 17 | ; возможность определить повторное нажатие. 18 | jmp d ; Повторяем итерацию 19 | -------------------------------------------------------------------------------- /computer-v1/img/cpu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v1/img/cpu.jpg -------------------------------------------------------------------------------- /computer-v1/img/digits.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v1/img/digits.jpg -------------------------------------------------------------------------------- /computer-v1/img/display.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v1/img/display.jpg -------------------------------------------------------------------------------- /computer-v1/img/fibonacci-sequence.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v1/img/fibonacci-sequence.jpg -------------------------------------------------------------------------------- /computer-v1/img/font-test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v1/img/font-test.jpg -------------------------------------------------------------------------------- /computer-v1/img/prime-numbers.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v1/img/prime-numbers.jpg -------------------------------------------------------------------------------- /computer-v1/img/ram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v1/img/ram.jpg -------------------------------------------------------------------------------- /computer-v1/img/space-fight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v1/img/space-fight.jpg -------------------------------------------------------------------------------- /computer-v1/img/summary.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v1/img/summary.jpg -------------------------------------------------------------------------------- /computer-v1/img/terminal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v1/img/terminal.jpg -------------------------------------------------------------------------------- /computer-v1/programming.md: -------------------------------------------------------------------------------- 1 | # Программирование 2 | Чтобы запустить на компьютере вашу собственную программу, выполните следующие шаги: 3 | - Изучите язык ассемблера на этой странице. 4 | - Откройте [онлайн-компилятор](https://gulgdev.github.io/chubrik-compiler/) и в его левой части 5 | напишите программу. 6 | - Скопируйте из правой части скомпилированный код. 7 | - Зайдите на [карту с компьютером](https://logic-arrows.io/map-lVeJ9jtX) и нажмите `Ctrl+V`, чтобы 8 | вставить дискету с программой. 9 | - Подсоедините провод от дискеты к общему проводу от других дискет. 10 | - Нажмите на кнопку у дискеты и дождитесь загрузки программы в память компьютера. 11 | - Нажмите на кнопку `RUN` у компьютера и наблюдайте за ходом выполнения вашей программы. 12 |


13 | 14 | 15 | ## Общие принципы 16 | Программа состоит из последовательности инструкций и набора данных. Каждая инструкция занимает в 17 | памяти 1 байт. Некоторые инструкции требуют дополнительный операнд, который также занимает 1 байт и 18 | располагается в памяти сразу вслед за инструкцией. Процессор выполняет инструкции друг за другом, 19 | начиная с адреса `00`. Согласно программе, процессор также может совершать переходы к различным 20 | участкам кода, тем самым создавая циклы, подпрограммы и пр. 21 | 22 | Выполняя программу, процессор взаимодействует с регистрами `A` `B` `C` `D` и флагами `Z` `S` `C` 23 | `O`. Каждый регистр хранит 1 байт и имеет универсальное назначение. Флаги выражают собой результат 24 | выполнения вычислительных операций и могут использоваться как условия для переходов: 25 | - **Флаг Z (ноль)** показывает, что результат равен нулю. 26 | - **Флаг S (знак)** показывает, что у результата активен старший бит. В знаковой арифметике это 27 | означает отрицательное число. 28 | - **Флаг C (перенос)** показывает, что во время операции один из битов вышел за пределы байта. 29 | - **Флаг O (переполнение)** показывает, что два операнда имели одинаковый старший бит, но в 30 | результате операции он изменился. В знаковой арифметике это означает выход за допустимый диапазон. 31 |


32 | 33 | 34 | ## Синтаксис 35 | Для наилучшего понимания ассемблера и его синтаксиса ознакомьтесь с [примерами программ](asm), 36 | созданных автором компьютера. Здесь рассмотрим основные моменты: 37 | ```asm 38 | my_const equ 196 ; Объявляем константу my_const со значением 196 при помощи ключевого 39 | ; слова "equ". В качестве значений в любом месте кода можно исполь- 40 | ; зовать числа от 0 до 255 в десятичной (196), шестнадцатеричной 41 | ; (0xC4), восьмеричной (0304) или двоичной (0b11000100) форме. 42 | 43 | ldi a, my_const ; При сборке кода "my_const" заменяется на конечное значение, 44 | ; поэтому фактически программа выполнит "ldi a, 196" 45 | 46 | my_label: ; Объявляем метку, она обозначает текущий адрес в коде 47 | inc a 48 | jnz my_label ; Программа совершит переход к метке my_label, т.е. возникнет цикл 49 | 50 | my_data db 1, 2, 3, 0x04, 0x05 ; Объявляем область данных при помощи ключевого слова "db". Метка 51 | ; my_data обозначает адрес начала этой области. В качестве данных 52 | ; через запятую можно указывать числа, константы и метки. 53 | 54 | my_data_size equ $ - my_data ; "$" обозначает текущий адрес в коде. Вычитая адрес my_data, 55 | ; получаем размер области данных, что и присваиваем константе 56 | ; my_data_size. 57 | ``` 58 |

59 | 60 | 61 | ## Инструкции 62 | 63 | ### Управляющие инструкции 64 | Инструкции из этого набора отвечают за работу с памятью и переходы. В таблице ниже используются 65 | условные ***X*** и ***Y*** для обозначения любых регистров, а также ***F*** для обозначения любого 66 | флага. 67 | 68 | Инструкция | Описание 69 | ---|--- 70 | ld ***X*** | Загружает в ***X*** значение из памяти, используя операнд как адрес 71 | ld ***X***, ***Y*** | Загружает в ***X*** значение из памяти, используя ***Y*** как адрес 72 | ldi ***X*** | Загружает операнд непосредственно в ***X*** 73 | st ***X*** | Сохраняет значение ***X*** в память по адресу из операнда 74 | st ***X***, ***Y*** | Сохраняет значение ***X*** в память по адресу из ***Y*** 75 | jmp | Безусловный переход по адресу из операнда 76 | jmp ***X*** | Безусловный переход по адресу из ***X*** 77 | j***F*** | Переход по адресу из операнда, если ***F*** = 1 78 | jn***F*** | Переход по адресу из операнда, если ***F*** = 0 79 | j***F*** ***X*** | Переход по адресу из ***X***, если ***F*** = 1 80 | jn***F*** ***X*** | Переход по адресу из ***X***, если ***F*** = 0 81 | rnd ***X*** | Генерирует случайное значение в регистре ***X*** 82 | hlt | Останавливает выполнение программы 83 | 84 |
85 | 86 | ### Вычислительные инструкции 87 | Инструкции из этого набора производят вычисления на основе регистров и флагов. В таблице ниже 88 | используются условные ***X*** и ***Y*** для обозначения любых регистров. 89 | 90 | Инструкция | Описание | Воздействие
на флаги 91 | ---|---|--- 92 | mov ***X***, 0 | Обнуляет ***X*** | – 93 | mov ***X***, ***Y*** | Копирует из ***Y*** в ***X*** | – 94 | and ***X***, ***Y*** | Побитовое И между ***X*** и ***Y***, результат записывает в ***X*** | Z, S 95 | or ***X***, ***Y*** | Побитовое ИЛИ между ***X*** и ***Y***, результат записывает в ***X*** | Z, S 96 | xor ***X***, ***Y*** | Исключающее ИЛИ между ***X*** и ***Y***, результат записывает в ***X*** | Z, S 97 | add ***X***, ***Y*** | Складывает ***X*** и ***Y***, результат записывает в ***X*** | Z, S, C, O 98 | adc ***X***, ***Y*** | Складывает ***X***, ***Y*** и флаг `C`, результат записывает в ***X*** | Z, S, C, O 99 | sub ***X***, ***Y*** | Из ***X*** вычитает ***Y***, результат записывает в ***X*** | Z, S, C, O 100 | sbb ***X***, ***Y*** | Из ***X*** вычитает ***Y*** и флаг `C`, результат записывает в ***X*** | Z, S, C, O 101 | and ***X***, 0
or ***X***, 0
xor ***X***, 0
add ***X***, 0
adc ***X***, 0
sub ***X***, 0
sbb ***X***, 0 | Не производит вычислений, а лишь обновляет флаги по значению ***X*** | Z, S 102 | inc ***X*** | Прибавляет 1 | Z, S 103 | dec ***X*** | Вычитает 1 | Z, S 104 | not ***X*** | Заменяет каждый бит на противоположный | Z, S 105 | neg ***X*** | Меняет знак (значение трактует как число со знаком) | Z, S 106 | exp ***X*** | Делает все биты равными флагу `С` | Z, S 107 | shl ***X*** | Сдвигает все биты на один влево, правый бит обнуляет | Z, S, C 108 | shr ***X*** | Сдвигает все биты на один вправо, левый бит обнуляет | Z, S, C 109 | sar ***X*** | Сдвигает все биты на один вправо, левый бит не меняет | Z, S, C 110 | rcl ***X*** | Сдвигает все биты на один влево, правый бит берёт из флага `С` | Z, S, C 111 | rcr ***X*** | Сдвигает все биты на один вправо, левый бит берёт из флага `С` | Z, S, C 112 | 113 |
114 | 115 | ### Таблица инструкций 116 | Каждая инструкция имеет свой код (opcode). К примеру, `adc a, d` имеет код `2B` (ряд, 117 | затем колонка). 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 |
0123456789ABCDEF
0mov a, 0mov a, bmov a, cmov a, dmov a, 0mov b, amov c, amov d, aand a, 0and a, band a, cand a, dand a, 0and b, aand c, aand d, a
1or a, 0or a, bor a, cor a, dor a, 0or b, aor c, aor d, axor a, 0xor a, bxor a, cxor a, dxor a, 0xor b, axor c, axor d, a
2add a, 0add a, badd a, cadd a, dadd a, 0add b, aadd c, aadd d, aadc a, 0adc a, badc a, cadc a, dadc a, 0adc b, aadc c, aadc d, a
3sub a, 0sub a, bsub a, csub a, dsub a, 0sub b, asub c, asub d, asbb a, 0sbb a, bsbb a, csbb a, dsbb a, 0sbb b, asbb c, asbb d, a
4not anot bnot cnot dneg aneg bneg cneg dinc ainc binc cinc
d
dec adec bdec cdec d
5shl ashl bshl cshl dshr ashr bshr cshr dsar asar bsar csar dexp aexp bexp cexp d
6rcl arcl brcl crcl drcr arcr brcr crcr dreserved
7reserved
8ld ald bld cld dld ald bld cld dldi aldi bldi cldi dldi aldi bldi cldi d
9ld a, ald b, ald c, ald d, ald a, bld b, bld c, bld d, bld a, cld b, cld c, cld d, cld a, dld b, dld c, dld d, d
Ast ast bst cst dst ast bst cst drnd arnd brnd crnd drnd arnd brnd crnd d
Bst a, ast b, ast c, ast d, ast a, bst b, bst c, bst d, bst a, cst b, cst c, cst d, cst a, dst b, dst c, dst d, d
Cjz ajc ajs ajo ajz bjc bjs bjo bjz cjc cjs cjo cjz djc djs djo d
Djnz ajnc ajns ajno ajnz bjnc bjns bjno bjnz cjnc cjns cjno cjnz djnc djns djno d
Ejzjcjsjojnzjncjnsjnojmphlt
Fjmp ajmp bjmp cjmp d
239 | -------------------------------------------------------------------------------- /computer-v1/specification.md: -------------------------------------------------------------------------------- 1 | # Устройство и характеристики 2 | Компьютер состоит из процессора, оперативной памяти, устройств ввода/вывода и набора программ. 3 | Основные характеристики: 4 | - 8-битная архитектура, процессор с 4 регистрами и флагами. 5 | - Оперативная память 256 байт с интегрированной видеопамятью и портами. 6 | - Ввод/вывод: клавиатура, монохромный дисплей, терминал и цифровой индикатор. 7 | - Собственный язык ассемблера (см. [Программирование](programming.md)). 8 | - Загрузка программ со специальных дискет. 9 |


10 | 11 | 12 | ## Процессор 13 | Процессор состоит из двух частей: управляющей и вычислительной (ALU). Управляющая часть состоит из 14 | указателя инструкции `IP`, регистра инструкции `IR`, блока управления и 4 свободных регистров `A` 15 | `B` `C` `D`. Вычислительная часть состоит из регистра инструкции `IR`, 4 флагов `Z` `C` `S` `O`, 16 | блока управления, многофункционального сумматора и ряда других мелких механизмов. 17 | 18 | Процессор читает инструкцию из RAM по адресу, лежащему в `IP`. Инструкция попадает в `IR`, инициируя 19 | выполнение той или иной операции. Во время выполнения операции происходит взаимодействие с 20 | регистрами и флагами. После этого `IP` инкрементируется и процесс повторяется. Подробнее см. 21 | [Программирование](programming.md). 22 | 23 | Процессор 24 |

25 | 26 | 27 | ## Оперативная память 28 | Памятью компьютера является RAM объёмом 256 байт. Единицей хранимой информации является 1 байт, 29 | адрес доступа к памяти также представляет собой 1 байт. 30 | 31 | Адрес `3E` подключён к системе ввода. По этому адресу всегда можно прочитать, например, о нажатой 32 | клавише на клавиатуре. 33 | 34 | Адрес `3F` подключён к управлению выводом. К примеру, если записать по этому адресу байт `80`, то 35 | вывод переключится на дисплей. 36 | 37 | Адреса `40...7F` являются совмещённой видеопамятью. Отправка данных по этим адресам может 38 | воздействовать на текущее средство вывода, в зависимости от его типа. 39 | 40 | Оперативная память 41 |

42 | 43 | 44 | ## Клавиатура 45 | После нажатия на любую клавишу, её код может быть прочитан программой из порта `3E`. Для 46 | определения повторных нажатий программе следует самостоятельно сбрасывать значение в порту. Коды 47 | символов соответствуют кодировке [`cp1251`](https://ru.wikipedia.org/wiki/Windows-1251). Клавишам 48 | `←` `↑` `→` `↓` `Enter` соответствуют коды `11` `12` `13` `14` `0A`. 49 |


50 | 51 | 52 | ## Дисплей 53 | Для переключения вывода на дисплей необходимо в порт `3F` записать байт `80`. Ниже показано 54 | соответствие адресов различным участкам дисплея. Отправка данных по этим адресам приводит к 55 | появлению на дисплее соответствующих пикселей. 56 | 57 | Дисплей 58 |

59 | 60 | 61 | ## Терминал 62 | Для переключения вывода на терминал необходимо в порт `3F` записать байт `40`. Далее, каждый байт, 63 | отправленный по адресу `40`, выводится в терминал как символ в кодировке 64 | [`cp1251`](https://ru.wikipedia.org/wiki/Windows-1251), сдвигая все предыдущие символы влево и далее 65 | на строчку выше. 66 | 67 | Терминал 68 |

69 | 70 | 71 | ## Цифровой индикатор 72 | Состоит из 3 десятичных цифр и отображает числа в диапазоне `0...255`. Для переключения вывода на 73 | цифровой индикатор необходимо в порт `3F` записать байт `10`. Далее, каждый байт, отправленный по 74 | адресу `40`, преобразуется в десятичный формат и выводится на цифровой индикатор. 75 | 76 | Цифровой индикатор 77 | -------------------------------------------------------------------------------- /computer-v2/README.md: -------------------------------------------------------------------------------- 1 | # Компьютер из стрелочек *Gen. 2* 2 |
3 | 4 | 5 | 6 | 7 | 15 | 19 | 20 | 21 |
8 | Полноценный компьютер, целиком сделанный из стрелочек. Позволяет создавать и запускать 9 | различные программы и игры.

10 | Карта с компьютером

11 | Устройство и характеристики

12 | Программирование

13 | Готовые программы 14 |
16 | Компьютер из стрелочек (Gen. 2) 18 |
22 |
23 | 24 | 25 | ## Демонстрация работы 26 | Зайдите на [карту с компьютером](https://logic-arrows.io/map-computer). В нижнем ползунке установите 27 | максимальную скорость. Нажмите на кнопку `Demo` и дождитесь загрузки программы в память компьютера. 28 | Во время загрузки на дисплей будет выведена цветная бабочка. Далее нажмите на кнопку `RUN` и 29 | наблюдайте, как программа в терминале напишет «Привет, Онигири!», нарисует изображение онигири и 30 | трижды позвонит в колокольчик. По окончании загорится лампочка `DONE`. Чтобы запустить на компьютере 31 | вашу собственную программу, см. [Программирование](programming.md). 32 |


33 | 34 | 35 | ## Готовые программы 36 | 37 | 38 | 39 | 47 | 56 | 57 | 58 | 64 | 71 | 72 | 73 | 80 | 85 | 86 | 87 | 93 | 94 | 95 |
40 |

Игра «Жизнь»

41 | Игра «Жизнь»
42 | В терминал выводится название игры, дисплей заполняется случайным набором пикселей, а на 43 | цифровом индикаторе отображается счётчик кадров. Затем запускается бесконечный цикл 44 | вычислений, на обработку одного кадра уходит около часа.

45 | Программа занимает 512 байт. 46 |
48 |

Игра «Space Fight!»

49 | Игра «Space Fight!»
51 | Внизу дисплея расположен корабль, а остальная область заполнена врагами. Нужно сбить 30 52 | врагов за ограниченное время. Враги приближаются с нарастающей скоростью, и, если враг 53 | достигнет корабля, игра проиграна. В случае победы на дисплее появится приз.

54 | Программа занимает 256 байт. 55 |
59 |

Demo

60 | Demo
61 | Во время загрузки выводит на дисплей цветную бабочку. При запуске пишет в терминал 62 | «Привет, Онигири!», рисует изображение онигири и звонит в колокольчик. 63 |
65 |

Prime Numbers

66 | Prime Numbers
68 | Находит первые 16 простых чисел и выводит их на цифровой индикатор, а также на дисплей в 69 | двоичном формате. Выполнение занимает 3091 операцию. 70 |
74 |

Fibonacci Sequence

75 | Fibonacci Sequence
77 | Находит 12 чисел Фибоначчи. Выводит их на цифровой индикатор, а также на дисплей в двоичном 78 | формате. 79 |
81 |

Typewriter

82 | Typewriter
83 | Выводит в терминал текст, набираемый на клавиатуре. 84 |
88 |

Font Test

89 | Font Test
90 | Выводит в терминал все возможные символы (кодировка 91 | cp1251). 92 |
96 | -------------------------------------------------------------------------------- /computer-v2/asm/demo.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Demo" program for a computer made of logic arrows ## 3 | ## Исходный код программы "Demo" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v2 ## 5 | ## (с) 2025 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | TERMINAL equ 0x3C ; Константа с адресом для вывода текста в терминал 10 | JS_C equ 0x19 ; Константа с кодом инструкции "js c" 11 | 12 | jmp init ; Переходим через область данных 13 | 14 | ; Верхняя часть изображения для вывода в терминал 15 | img1 db 0b00000000, ; ; 16 | 0b00000000, ; ; 17 | 0b10000000, ; ██ ; 18 | 0b01100000, ; ████ ; 19 | 0b00010000, ; ██ ; 20 | 0b00101000, ; ██ ██ ; 21 | 0b00000100, ; ██ ; 22 | 0b10000100, ; ██ ██ ; 23 | 0b00010100, ; ██ ██ ; 24 | 0b00000100, ; ██ ; 25 | 0b01001000, ; ██ ██ ; 26 | 0b00010000, ; ██ ; 27 | 0b01100000, ; ████ ; 28 | 0b10000000, ; ██ ; 29 | 0b00000000, ; ; 30 | 0b00000000, ; ; 31 | 0b00000000 ; ; 32 | 33 | img1_last equ $ -1 34 | 35 | ; Подготавливаем вывод нижней половинки изображения 36 | img2_show: ldi a, JS_C 37 | st a, img_cond ; Заменяем условие для выхода из цикла "img_loop" 38 | ldi b, img2_last ; Указатель на последний байт изображения. В течение цикла 39 | ; указатель смещается к первому байту и одновременно 40 | ; работает как счётчик, по которому цикл завершается. 41 | jmp img_show 42 | 43 | ; Подготавливаем вывод верхней половинки изображения 44 | img1_show: ldi b, img1_last ; Указатель на последний байт изображения. В течение цикла 45 | ; указатель смещается к первому байту и одновременно 46 | ; работает как счётчик, по которому цикл завершается. 47 | 48 | img_show: ldi c, img_loop ; Запоминаем адрес начала итерации для ускорения цикла 49 | inc d ; Меняем адрес вывода в терминал для графического режима 50 | 51 | ; Выводим часть изображения в терминал при помощи цикла 52 | img_loop: ld a, b 53 | st a, d 54 | dec b 55 | img_cond: jnz c ; Заменяемое условие для выхода из цикла 56 | 57 | img_jmp: jmp text2_show ; Заменяемый адрес для перехода 58 | 59 | ; Выводим часть текста в терминал при помощи цикла 60 | text_loop: ld a, b 61 | st a, d 62 | inc b 63 | dec c 64 | jnz text_loop 65 | 66 | text_jmp: jmp img1_show ; Заменяемый адрес для перехода 67 | 68 | ; Выводим в терминал оставшийся текст 69 | end: ldi a, "\n" 70 | st a, d 71 | ldi a, ">" 72 | st a, d 73 | 74 | ; Звоним в колокольчик 75 | ldi a, "\a" ; Код команды "звонок" у терминала 76 | st a, d ; Активируем звонок, у терминала загорается колокольчик 77 | st a, d ; Снова активируем звонок, но в этот раз колокольчик гаснет. 78 | ; Терминал устроен так, что колокольчик гаснет при любом 79 | ; дальнейшем вводе, в т.ч. повторном "\a". 80 | st a, d 81 | st a, d 82 | st a, d ; Зажигаем колокольчик в третий раз и оставляем 83 | 84 | ; Завершаем выполнение программы 85 | hlt 86 | 87 | void db 0, 0 ; Текущие адреса подключены к выводу в терминал 88 | 89 | out db 0b00110001 ; Порт вывода, подключаем цветной дисплей и терминал 90 | bank db 0 ; Порт банка памяти (не используется) 91 | 92 | ; Область 0x40...0x5F содержит красный компонент заставки, отображаемой на дисплее во время загрузки 93 | ; программы 94 | display_r db 0b00000000, 0b00000000, ; ; 95 | 0b00000000, 0b00000000, ; ; 96 | 0b00000000, 0b00000000, ; ; 97 | 0b00000010, 0b10000000, ; ██ ██ ; 98 | 0b00111011, 0b10111000, ; ██████ ██████ ██████ ; 99 | 0b00100101, 0b01111000, ; ██ ██ ██ ████████ ; 100 | 0b00100011, 0b11111000, ; ██ ██████████████ ; 101 | 0b00100001, 0b11111000, ; ██ ████████████ ; 102 | 0b00010001, 0b11110000, ; ██ ██████████ ; 103 | 0b00001101, 0b11100000, ; ████ ████████ ; 104 | 0b00010001, 0b11110000, ; ██ ██████████ ; 105 | 0b00010011, 0b11110000, ; ██ ████████████ ; 106 | 0b00001101, 0b01100000, ; ████ ██ ████ ; 107 | 0b00000000, 0b00000000, ; ; 108 | 0b00000000, 0b00000000, ; ; 109 | 0b00000000, 0b00000000 ; ; 110 | 111 | ; Область 0x60...0x7F содержит синий компонент заставки, отображаемой на дисплее во время загрузки 112 | ; программы 113 | display_b db 0b00000000, 0b00000000, ; ; 114 | 0b00000000, 0b00000000, ; ; 115 | 0b00000000, 0b00000000, ; ; 116 | 0b00000010, 0b10000000, ; ██ ██ ; 117 | 0b00111011, 0b10111000, ; ██████ ██████ ██████ ; 118 | 0b00111101, 0b01001000, ; ████████ ██ ██ ██ ; 119 | 0b00111111, 0b10001000, ; ██████████████ ██ ; 120 | 0b00111111, 0b00001000, ; ████████████ ██ ; 121 | 0b00011111, 0b00010000, ; ██████████ ██ ; 122 | 0b00001111, 0b01100000, ; ████████ ████ ; 123 | 0b00011111, 0b00010000, ; ██████████ ██ ; 124 | 0b00011111, 0b10010000, ; ████████████ ██ ; 125 | 0b00001101, 0b01100000, ; ████ ██ ████ ; 126 | 0b00000000, 0b00000000, ; ; 127 | 0b00000000, 0b00000000, ; ; 128 | 0b00000000, 0b00000000 ; ; 129 | 130 | ; Нижняя часть изображения для вывода в терминал 131 | img2 db 0b00000000, ; ; 132 | 0b00000000, ; ; 133 | 0b00011110, ; ████████ ; 134 | 0b00100001, ; ██ ██ ; 135 | 0b00100010, ; ██ ██ ; 136 | 0b01000000, ; ██ ; 137 | 0b01111000, ; ████████ ; 138 | 0b01111100, ; ██████████ ; 139 | 0b01111100, ; ██████████ ; 140 | 0b01111100, ; ██████████ ; 141 | 0b01111100, ; ██████████ ; 142 | 0b01111000, ; ████████ ; 143 | 0b01000000, ; ██ ; 144 | 0b00100100, ; ██ ██ ; 145 | 0b00100001, ; ██ ██ ; 146 | 0b00011110, ; ████████ ; 147 | 0b00000000, ; ; 148 | 0b00000000 ; ; 149 | 150 | img2_last equ $ -1 151 | 152 | ; Инициализация программы 153 | init: inc b 154 | st a, b ; Очищаем адрес 0x01, тем самым дополняем изображение 155 | ldi d, TERMINAL 156 | 157 | ; Подготавливаем вывод первой строки текста 158 | text1_show: ldi b, text1 159 | ldi c, text1_size 160 | 161 | jmp text_loop ; Переходим к выводу первой строки 162 | 163 | ; Подготавливаем вывод второй строки текста 164 | text2_show: ldi a, img2_show 165 | st a, text_jmp +1 ; Заменяем адрес для перехода после вывода текста 166 | ldi a, pre_end 167 | st a, img_jmp +1 ; Заменяем адрес для перехода после вывода изображения 168 | ldi b, text2 169 | ldi c, text2_size 170 | dec d ; Меняем адрес вывода в терминал для текстового режима 171 | jmp text_loop 172 | 173 | pre_end: dec d ; Меняем адрес вывода в терминал для текстового режима 174 | jmp end ; Переходим к выводу оставшегося текста 175 | 176 | ; Первая строка текста для вывода в терминал 177 | text1 db "Привет, " 178 | text1_size equ $ - text1 179 | 180 | ; Вторая строка текста для вывода в терминал 181 | text2 db "Онигири! " 182 | text2_size equ $ - text2 183 | -------------------------------------------------------------------------------- /computer-v2/asm/fibonacci-sequence.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Fibonacci Sequence" program for a computer made of logic arrows ## 3 | ## Исходный код программы "Fibonacci Sequence" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v2 ## 5 | ## (с) 2024 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | ldi c, 0b00010100 ; Код для подключения дисплея и цифрового индикатора 10 | st c, 0x3E ; Подключаем вывод 11 | inc b ; Подготавливаем число 1 12 | ldi c, 0x3A ; Адрес для вывода на цифровой индикатор 13 | ldi d, 0x41 ; Указатель для вывода на дисплей 14 | 15 | loop: add a, b ; Складываем последние два числа, сохраняя сумму в первое из них 16 | jc end ; Если сумма превысила 255, выходим из цикла 17 | st a, c ; Выводим сумму на цифровой индикатор 18 | st a, d ; Выводим сумму на дисплей 19 | inc d ; Смещаем указатель на дисплее на ряд ниже 20 | inc d 21 | add b, a ; Складываем последние два числа, сохраняя сумму во второе из них 22 | st b, c ; Выводим сумму на цифровой индикатор 23 | st b, d ; Выводим сумму на дисплей 24 | inc d ; Смещаем указатель на дисплее на ряд ниже 25 | inc d 26 | jmp loop ; Повторяем итерацию 27 | 28 | end: hlt ; Завершаем выполнение программы 29 | -------------------------------------------------------------------------------- /computer-v2/asm/font-test.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Font Test" program for a computer made of logic arrows ## 3 | ## Исходный код программы "Font Test" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v2 ## 5 | ## (с) 2025 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | inc a ; 0b00000001, код для подключения терминала 10 | st a, 0x3E ; Подключаем вывод 11 | ldi c, 0x3C ; Адрес для вывода в терминал 12 | ldi d, loop ; Запоминаем адрес начала итерации для ускорения цикла 13 | 14 | repeat: ldi a, " " ; Берём первый символ 15 | 16 | loop: st a, c ; Выводим символ в терминал 17 | inc a ; Берём следующий символ 18 | jnz d ; Повторяем итерацию, пока код символа после 0xFF не станет 0x00 19 | 20 | jmp repeat ; Бесконечно повторяем программу 21 | -------------------------------------------------------------------------------- /computer-v2/asm/game-of-life.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Game of Life" for a computer made of logic arrows ## 3 | ## Исходный код игры "Жизнь" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v2 ## 5 | ## (с) 2024 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | 10 | ; Константы 11 | BANK_INNER equ 1 ; Номер банка 1 12 | BANK_MAIN equ 2 ; Номер банка 2 13 | BANK_OUTER equ 3 ; Номер банка 3 14 | TERMINAL equ 0x3C ; Адрес для вывода в терминал 15 | 16 | 17 | 18 | ;=================================================================================================== 19 | ; Общая область памяти - адресное пространство 0x00...0x7F. 20 | ; Содержит: код вывода названия игры в терминал, переключатель банков, порты, переменные, память 21 | ; дисплея, кэш для следующего кадра. После вывода названия игры в терминал, вся область памяти 22 | ; 0x00...0x2F перетирается и далее используется для хранения счётчиков соседей у клеток. Счётчики 23 | ; разбиты на три группы по 16, где каждая группа отвечает за свой ряд на дисплее. 24 | ;=================================================================================================== 25 | 26 | ; Старт игры 27 | jmp title_show ; Переходим через строку 28 | title db "ьнзиЖ аргИ efiL fo emaG" ; Строка "Game of Life Игра Жизнь" задом-наперёд 29 | 30 | ; Готовим вывод названия игры в терминал 31 | title_show: ldi b, " " 32 | st b, title -1 ; Дополняем пробелом строку с названием игры 33 | ldi a, TERMINAL 34 | ldi c, title_show -1 ; Указатель на последний символ строки. В течение 35 | ; цикла указатель смещается к началу строки и 36 | ; одновременно работает как счётчик, по которому 37 | ; цикл завершается. 38 | ldi d, title_show_loop 39 | 40 | ; Цикл вывода в терминал 41 | title_show_loop: ld b, c 42 | st b, a 43 | dec c 44 | jnz d 45 | 46 | ; Переключаем вывод на монохромный дисплей и цифровой индикатор 47 | ldi a, 0b00010100 48 | st a, out 49 | 50 | ; Готовим регистры C и D для перехода между банками, а регистры A и B для блока "main_rnd" 51 | ldi a, display 52 | ; ; Регистр B уже содержит " ", т.е. число 32 53 | ldi c, BANK_MAIN 54 | ldi d, main_rnd 55 | 56 | ; Универсальный код для перехода между банками. 57 | ; Регистр C уже содержит номер банка. 58 | ; Регистр D уже содержит адрес для перехода. 59 | set_bank: st c, bank 60 | jmp d 61 | 62 | void1 db 0 63 | 64 | ; Переменные и порты 65 | 66 | upper_area db 0x00 ; Адрес начала области счётчиков для ряда выше текущего 67 | current_area db 0x10 ; Адрес начала области счётчиков для текущего ряда 68 | lower_area db 0x20 ; Адрес начала области счётчиков для ряда ниже текущего 69 | upper_ptr db 0x00 ; Указатель в области счётчиков для ряда выше текущего 70 | lower_ptr db 0x20 ; Указатель в области счётчиков для ряда ниже текущего 71 | 72 | step_count db 0xFF ; Текущий адрес подключён к выводу на цифровой индикатор 73 | ; и используется как счётчик кадров. Изначально 74 | ; содержит 0xFF, чтобы при первой инкрементации 75 | ; получилось число 0. 76 | void2 db 0 77 | 78 | incrementor db 0 ; Текущий адрес подключён к выводу в терминал. После 79 | ; отключения терминала используется как счётчик в 80 | ; некоторых циклах. 81 | display_ptr db display ; Указатель на дисплее 82 | out db 0b00110001 ; Порт вывода, подключаем цветной дисплей и терминал 83 | bank db 0 ; Порт банка памяти 84 | 85 | ; Область 0x40...0x5F содержит красный компонент заставки, отображаемой на дисплее во время загрузки 86 | ; программы. Затем эта область используется для отображения на дисплее текущего кадра. 87 | display db 0b01100000, 0b00000000, ; ████ ; 88 | 0b10001001, 0b10100010, ; ██ ██ ████ ██ ██ ; 89 | 0b10100101, 0b01010101, ; ██ ██ ██ ██ ██ ██ ██ ██ ; 90 | 0b10101101, 0b01010110, ; ██ ██ ████ ██ ██ ██ ████ ; 91 | 0b01101101, 0b01010011, ; ████ ████ ██ ██ ██ ████ ; 92 | 0b00000000, 0b00000000, ; ; 93 | 0b00001010, 0b00101000, ; ██ ██ ██ ██ ; 94 | 0b00010001, 0b01000000, ; ██ ██ ██ ; 95 | 0b00100010, 0b00101000, ; ██ ██ ██ ██ ; 96 | 0b00010100, 0b01000000, ; ██ ██ ██ ; 97 | 0b00000000, 0b00000000, ; ; 98 | 0b10001110, 0b11101110, ; ██ ██████ ██████ ██████ ; 99 | 0b10000100, 0b10001000, ; ██ ██ ██ ██ ; 100 | 0b10000100, 0b11001100, ; ██ ██ ████ ████ ; 101 | 0b10000100, 0b10001000, ; ██ ██ ██ ██ ; 102 | 0b11101110, 0b10001110 ; ██████ ██████ ██ ██████ ; 103 | 104 | ; Область 0x60...0x7F содержит синий компонент заставки, отображаемой на дисплее во время загрузки 105 | ; программы. Затем эта область используется для кэша следующего кадра. 106 | frame_cache db 0b01100000, 0b00000000, ; ████ ; 107 | 0b10001001, 0b10100010, ; ██ ██ ████ ██ ██ ; 108 | 0b10100101, 0b01010101, ; ██ ██ ██ ██ ██ ██ ██ ██ ; 109 | 0b10101101, 0b01010110, ; ██ ██ ████ ██ ██ ██ ████ ; 110 | 0b01101101, 0b01010011, ; ████ ████ ██ ██ ██ ████ ; 111 | 0b00000000, 0b00000000, ; ; 112 | 0b00010100, 0b01010100, ; ██ ██ ██ ██ ██ ; 113 | 0b00100010, 0b00100000, ; ██ ██ ██ ; 114 | 0b00010001, 0b01010000, ; ██ ██ ██ ██ ; 115 | 0b00001010, 0b00100000, ; ██ ██ ██ ; 116 | 0b00000000, 0b00000000, ; ; 117 | 0b10001110, 0b11101110, ; ██ ██████ ██████ ██████ ; 118 | 0b10000100, 0b10001000, ; ██ ██ ██ ██ ; 119 | 0b10000100, 0b11001100, ; ██ ██ ████ ████ ; 120 | 0b10000100, 0b10001000, ; ██ ██ ██ ██ ; 121 | 0b11101110, 0b10001110 ; ██████ ██████ ██ ██████ ; 122 | 123 | 124 | 125 | ;=================================================================================================== 126 | ; BANK_INNER - Банк памяти № 1 - адресное пространство 0x80...0xFF. 127 | ; Содержит логику обработки всех рядов на дисплее, кроме верхнего и нижнего. 128 | ;=================================================================================================== 129 | 130 | ; Начинаем обработку ряда на дисплее 131 | inner_begin: ldi b, inner_center 132 | st b, inner_edge_jmp +1 ; Заменяем адрес для перехода после обработки левого 133 | ; крайнего пикселя 134 | ldi b, inner_right 135 | st b, inner_center_jmp +1 ; Заменяем адрес для перехода после обработки левой 136 | ; половины ряда на дисплее 137 | ld b, display_ptr 138 | ld a, b ; Загружаем левую половину ряда на дисплее 139 | shr a ; Сдвиг помогает унифицировать дальнейшую обработку 140 | 141 | ld c, current_area 142 | inc c ; Указатель на счётчик для соседа справа 143 | 144 | ; Обрабатываем крайний пиксель на дисплее. 145 | ; Регистр A уже содержит крайний пиксель в 6-м бите. 146 | ; Регистр C уже содержит указатель на счётчик для единственного бокового соседа. 147 | inner_edge: rcl a ; Сдвигаем крайний пиксель в 7-й бит 148 | jns inner_edge_end ; Если пиксель пустой, пропускаем его обработку 149 | 150 | ; Инкрементируем счётчики для двух соседей в ряду выше 151 | ld d, upper_ptr 152 | ld b, d 153 | inc b 154 | st b, d 155 | inc d 156 | ld b, d 157 | inc b 158 | st b, d 159 | 160 | ; Инкрементируем счётчик для единственного бокового соседа 161 | ld b, c 162 | inc b 163 | st b, c 164 | 165 | ; Инкрементируем счётчики для двух соседей в ряду ниже 166 | ld d, lower_ptr 167 | ld b, d 168 | inc b 169 | st b, d 170 | inc d 171 | ld b, d 172 | inc b 173 | st b, d 174 | 175 | inner_edge_end: dec c 176 | inner_edge_jmp: jmp inner_center ; Заменяемый адрес для перехода 177 | 178 | ; Переходим с обработки левой половины дисплея на правую. 179 | ; Регистр C уже содержит указатель на счётчик для левого из двух боковых соседей. 180 | inner_right: ldi b, inner_edge 181 | st b, inner_center_jmp +1 ; Заменяем адрес для перехода после обработки 182 | ; правой половины ряда на дисплее 183 | ldi b, inner_end 184 | st b, inner_edge_jmp +1 ; Заменяем адрес для перехода после обработки 185 | ; правого крайнего пикселя 186 | ld b, display_ptr 187 | inc b 188 | ld a, b ; Загружаем правую половину ряда на дисплее 189 | shr a ; Сдвиг помогает унифицировать дальнейшую обработку 190 | 191 | ; Обрабатываем 7 пикселей, кроме крайнего, на одной половине дисплея. 192 | ; Регистр A уже содержит 7 пикселей в битах с 6 по 0-й. 193 | ; Регистр C уже содержит указатель на счётчик для левого из двух боковых соседей. 194 | inner_center: ldi b, 8 195 | st b, incrementor 196 | 197 | ; Цикл обработки каждого из 7 пикселей 198 | inner_center_loop: ld b, incrementor 199 | dec b 200 | st b, incrementor 201 | inner_center_jmp: jz inner_right ; Заменяемый адрес для перехода 202 | 203 | rcl a ; Сдвигаем очередной пиксель в 7-й бит 204 | jns inner_center_end ; Если пиксель пустой, пропускаем его обработку 205 | 206 | ; Инкрементируем счётчики для трёх соседей в ряду выше 207 | ld d, upper_ptr 208 | ld b, d 209 | inc b 210 | st b, d 211 | inc d 212 | ld b, d 213 | inc b 214 | st b, d 215 | inc d 216 | ld b, d 217 | inc b 218 | st b, d 219 | 220 | ; Инкрементируем счётчики для двух боковых соседей 221 | ld b, c 222 | inc b 223 | st b, c 224 | inc c 225 | inc c 226 | ld b, c 227 | inc b 228 | st b, c 229 | dec c 230 | dec c 231 | 232 | ; Инкрементируем счётчики для трёх соседей в ряду ниже 233 | ld d, lower_ptr 234 | ld b, d 235 | inc b 236 | st b, d 237 | inc d 238 | ld b, d 239 | inc b 240 | st b, d 241 | inc d 242 | ld b, d 243 | inc b 244 | st b, d 245 | 246 | ; Готовим к обработке следующий пиксель 247 | inner_center_end: ld d, upper_ptr 248 | inc d 249 | st d, upper_ptr 250 | inc c 251 | ld d, lower_ptr 252 | inc d 253 | st d, lower_ptr 254 | jmp inner_center_loop 255 | 256 | ; Обработка ряда на дисплее завершена. Переходим к калькуляции ряда выше текущего. 257 | inner_end: ldi c, BANK_MAIN 258 | ldi d, main_calc 259 | jmp set_bank 260 | 261 | void3 db 0, 0, 0, 0 262 | 263 | 264 | 265 | ;=================================================================================================== 266 | ; BANK_MAIN - Банк памяти № 2 - адресное пространство 0x80...0xFF. 267 | ; Содержит: генерацию стартового кадра со случайным рисунком, переключение логики между рядами на 268 | ; дисплее, калькуляцию и вывод на дисплей следующего кадра, инкрементацию цифрового индикатора и 269 | ; переход к следующей итерации. 270 | ;=================================================================================================== 271 | 272 | ; Переключаемся на следующий ряд на дисплее. Для этого меняем местами адреса областей счётчиков, 273 | ; записанные в переменных: текущий ряд становится для нас рядом выше, ряд ниже становится текущим 274 | ; рядом, а ряд выше становится рядом ниже и требует сброса своих счётчиков. Когда мы достигнем 275 | ; последнего ряда, все переменные автоматически получат свои изначальные значения. 276 | main_row: ld b, upper_area 277 | ld c, current_area 278 | ld d, lower_area 279 | st c, upper_area 280 | st d, current_area 281 | st b, lower_area 282 | 283 | st c, upper_ptr 284 | st b, lower_ptr 285 | 286 | ; Если display_ptr имеет значение 0x5E, значит мы достигли нижнего ряда на дисплее, и для его 287 | ; обработки нужно перейти к другому коду 288 | ld d, display_ptr 289 | ldi a, 0x5E 290 | xor a, d 291 | jz main_row_end 292 | 293 | ; Сбрасываем счётчики для ряда на дисплее ниже текущего. Вместо нулей используем 0xFE, что 294 | ; оптимизирует последующую калькуляцию. 295 | ldi a, 0xFE 296 | ; ; Регистр B уже содержит lower_area 297 | ldi c, 16 298 | ldi d, main_row_loop 299 | 300 | ; Цикл сброса счётчиков 301 | main_row_loop: st a, b 302 | inc b 303 | dec c 304 | jnz d 305 | 306 | ; Переходим к обработке ряда на дисплее (кроме нижнего) 307 | inc c ; Значение 1 соответствует BANK_INNER 308 | ldi d, inner_begin 309 | jmp set_bank 310 | 311 | ; Переходим к обработке нижнего ряда на дисплее 312 | main_row_end: ldi c, BANK_OUTER 313 | ldi d, outer_down 314 | jmp set_bank 315 | 316 | ; Калькулируем один ряд следующего кадра, для которого все счётчики уже заполнены 317 | main_calc: ld c, display_ptr 318 | ld d, upper_area 319 | 320 | ; Калькулируем половину ряда на дисплее. Для второй половины выполняем этот код повторно. 321 | ; Регистр C уже содержит указатель на дисплее. 322 | ; Регистр D уже содержит указатель в области счётчиков. 323 | main_calc_iter: dec c ; Дважды декрементируем указатель на дисплее, чтобы 324 | dec c ; перейти на ряд выше 325 | ld a, c ; Загружаем одну из половин ряда на дисплее 326 | ldi c, 0b10000000 ; Создаём маску для левого пикселя 327 | 328 | ; Цикл калькуляции каждого из 8 пикселей. 329 | main_calc_loop: ld b, d ; Загружаем значение счётчика 330 | test b 331 | jz main_calc_next ; Если счётчик показывает ноль, значит число соседей 332 | ; равно 2, и пиксель не меняем 333 | or a, c ; Закрашиваем пиксель 334 | dec b ; Уменьшаем значение счётчика на 1 335 | jz main_calc_next ; Теперь, если счётчик показывает ноль, значит число 336 | ; соседей 3, и пиксель больше не меняем 337 | xor a, c ; Число соседей не 2 и не 3, значит очищаем пиксель 338 | 339 | ; Готовим следующий пиксель 340 | main_calc_next: inc d ; Сдвигаем указатель на следующий счётчик 341 | shr c ; Сдвигаем вправо маску для пикселя 342 | jnz main_calc_loop ; Повторяем итерацию 343 | 344 | ; Применяем результат калькуляции 345 | ld c, display_ptr 346 | ldi b, 30 ; Величина сдвига для адреса сохранения 347 | add b, c 348 | st a, b ; Сохраняем результат в кэш следующего кадра 349 | inc c 350 | st c, display_ptr ; Сдвигаем указатель на дисплее 351 | shr b 352 | jnc main_calc_iter ; Если адрес сохранения чётный, повторяем 353 | ; калькуляцию 354 | 355 | main_calc_jmp: jmp main_row ; Заменяемый адрес для перехода 356 | 357 | ; Обработка всего дисплея завершена. Осталось провести калькуляцию для двух нижних рядов. 358 | main_final: ldi b, main_final2 359 | st b, main_calc_jmp +1 ; Заменяем адрес для перехода после калькуляции 360 | ; предпоследнего ряда 361 | jmp main_calc 362 | 363 | ; Осталось провести калькуляцию одного нижнего ряда на дисплее. 364 | ; Регистр C уже содержит указатель на дисплее. 365 | ; Регистр D уже содержит адрес начала области счётчиков для нижнего ряда на дисплее. 366 | main_final2: ldi b, main_final3 367 | st b, main_calc_jmp +1 ; Заменяем адрес для перехода после калькуляции 368 | ; нижнего ряда 369 | jmp main_calc_iter 370 | 371 | main_final3: ldi b, main_row 372 | st b, main_calc_jmp +1 ; Восстанавливаем адрес для перехода после 373 | ; калькуляции 374 | 375 | ; Выводим следующий кадр из кэша на дисплей 376 | main_frame: ldi b, display 377 | ldi c, frame_cache 378 | ldi d, main_frame_loop 379 | 380 | main_frame_loop: ld a, c 381 | st a, b 382 | inc b 383 | inc c 384 | jns d 385 | 386 | ; Обновляем счётчик кадров на цифровом индикаторе. 387 | ; На этом вся работа над кадром завершена, и мы переходим к работе над следующим кадром. 388 | main_count: ld a, step_count 389 | inc a 390 | st a, step_count 391 | 392 | ldi c, BANK_OUTER 393 | ldi d, outer_up 394 | jmp set_bank 395 | 396 | ; Генерируем и выводим на дисплей стартовый кадр со случайным рисунком. 397 | ; Регистр A уже содержит display. 398 | ; Регистр B уже содержит число 32. 399 | main_rnd: 400 | main_rnd_loop: rnd c 401 | rnd d 402 | and c, d 403 | st c, a 404 | inc a 405 | dec b 406 | jnz main_rnd_loop 407 | 408 | jmp main_count 409 | 410 | 411 | 412 | ;=================================================================================================== 413 | ; BANK_OUTER - Банк памяти № 3 - адресное пространство 0x80...0xFF. 414 | ; Содержит логику обработки верхнего и нижнего рядов на дисплее. 415 | ;=================================================================================================== 416 | 417 | ; Готовим к обработке верхний ряд на дисплее 418 | outer_up: ldi c, display 419 | st c, display_ptr ; Устанавливаем указатель на начало области дисплея 420 | 421 | shr c ; Значение становится 0x20 422 | st c, lower_area ; Восстанавливаем значение переменной lower_area 423 | ; после обработки предыдущего кадра 424 | 425 | ; Сбрасываем счётчики для текущего ряда на дисплее и для ряда ниже. Вместо нулей используем 0xFE, 426 | ; что оптимизирует последующую калькуляцию. 427 | ldi a, 0xFE 428 | ld b, current_area 429 | ; ; Регистр C уже содержит число 32 430 | ldi d, outer_up_loop 431 | 432 | ; Цикл сброса счётчиков 433 | outer_up_loop: st a, b 434 | inc b 435 | dec c 436 | jnz d 437 | 438 | jmp outer_begin 439 | 440 | ; Готовим к обработке нижний ряд на дисплее. 441 | ; Тут мы в переменную lower_area временно записываем адрес, отвечающий за ряд выше текущего. Это 442 | ; позволяет использовать единый код программы как для верхнего ряда на дисплее, так и для нижнего. 443 | outer_down: clr c ; Значение 0x00 соответствует upper_area 444 | st c, lower_area 445 | 446 | ; Начало обработки ряда на дисплее 447 | outer_begin: ldi b, outer_center 448 | st b, outer_edge_jmp +1 ; Заменяем адрес для перехода после обработки левого 449 | ; углового пикселя 450 | ldi b, outer_right 451 | st b, outer_center_jmp +1 ; Заменяем адрес для перехода после обработки левой 452 | ; половины ряда на дисплее 453 | ld b, display_ptr 454 | ld a, b ; Загружаем левую половину ряда на дисплее 455 | shr a ; Сдвиг помогает унифицировать дальнейшую обработку 456 | 457 | ldi c, 0x11 ; current_area +1, т.е. указатель на счётчик для 458 | ; соседа справа 459 | ld d, lower_area ; Указатель на счётчик для левого из двух соседей в 460 | ; другом ряду 461 | 462 | ; Обрабатываем угловой пиксель на дисплее. 463 | ; Регистр A уже содержит угловой пиксель в 6-м бите. 464 | ; Регистр C уже содержит указатель на счётчик для единственного бокового соседа. 465 | ; Регистр D уже содержит указатель на счётчик для левого из двух соседей в другом ряду. 466 | outer_edge: rcl a ; Сдвигаем угловой пиксель в 7-й бит 467 | jns outer_edge_end ; Если пиксель пустой, пропускаем его обработку 468 | 469 | ; Инкрементируем счётчик для единственного бокового соседа 470 | ld b, c 471 | inc b 472 | st b, c 473 | 474 | ; Инкрементируем счётчики для двух соседей в другом ряду 475 | ld b, d 476 | inc b 477 | st b, d 478 | inc d 479 | ld b, d 480 | inc b 481 | st b, d 482 | dec d 483 | 484 | outer_edge_end: dec c 485 | outer_edge_jmp: jmp outer_center ; Заменяемый адрес для перехода 486 | 487 | ; Переходим с обработки левой половины дисплея на правую. 488 | ; Регистр C уже содержит указатель на счётчик для левого из двух боковых соседей. 489 | ; Регистр D уже содержит указатель на счётчик для левого из трёх соседей в другом ряду. 490 | outer_right: ldi b, outer_edge 491 | st b, outer_center_jmp +1 ; Заменяем адрес для перехода после обработки 492 | ; правой половины ряда на дисплее 493 | ldi b, outer_end 494 | st b, outer_edge_jmp +1 ; Заменяем адрес для перехода после обработки 495 | ; правого углового пикселя 496 | ld b, display_ptr 497 | inc b 498 | ld a, b ; Загружаем правую половину ряда на дисплее 499 | shr a ; Сдвиг помогает унифицировать дальнейшую обработку 500 | 501 | ; Обрабатываем 7 пикселей, кроме углового, на одной половине дисплея. 502 | ; Регистр A уже содержит 7 пикселей в битах с 6 по 0-й. 503 | ; Регистр C уже содержит указатель на счётчик для левого из двух боковых соседей. 504 | ; Регистр D уже содержит указатель на счётчик для левого из трёх соседей в другом ряду. 505 | outer_center: ldi b, 8 506 | st b, incrementor 507 | 508 | ; Цикл обработки каждого из 7 пикселей 509 | outer_center_loop: ld b, incrementor 510 | dec b 511 | st b, incrementor 512 | outer_center_jmp: jz outer_right ; Заменяемый адрес для перехода 513 | 514 | rcl a ; Сдвигаем очередной пиксель в 7-й бит 515 | jns outer_center_else ; Если пиксель пустой, пропускаем его обработку 516 | 517 | ; Инкрементируем счётчики для двух боковых соседей 518 | ld b, c 519 | inc b 520 | st b, c 521 | inc c 522 | inc c 523 | ld b, c 524 | inc b 525 | st b, c 526 | dec c 527 | 528 | ; Инкрементируем счётчики для трёх соседей в другом ряду 529 | ld b, d 530 | inc b 531 | st b, d 532 | inc d 533 | ld b, d 534 | inc b 535 | st b, d 536 | inc d 537 | ld b, d 538 | inc b 539 | st b, d 540 | dec d 541 | 542 | jmp outer_center_loop 543 | 544 | ; Готовим к обработке следующий пиксель 545 | outer_center_else: inc c 546 | inc d 547 | jmp outer_center_loop 548 | 549 | ; Обработка ряда на дисплее завершена. Чтобы определить, верхний это был ряд или нижний, смотрим на 550 | ; переменную lower_area. Для нижнего ряда мы подменяли её значение на 0x00. 551 | outer_end: ldi c, BANK_MAIN 552 | 553 | ld a, lower_area 554 | test a 555 | jz outer_end_down 556 | 557 | ; Обработка верхнего ряда на дисплее завершена. Переходим к обработке следующих рядов. 558 | outer_end_up: ldi a, 0x42 559 | st a, display_ptr 560 | ldi d, main_row 561 | jmp set_bank 562 | 563 | ; Обработка нижнего ряда на дисплее завершена. Переходим к калькуляции оставшихся двух нижних рядов 564 | ; и выводу следующего кадра на дисплей. 565 | outer_end_down: ldi d, main_final 566 | jmp set_bank 567 | -------------------------------------------------------------------------------- /computer-v2/asm/prime-numbers.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Prime Numbers" program for a computer made of logic arrows ## 3 | ## Исходный код программы "Prime Numbers" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v2 ## 5 | ## (с) 2024 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | ldi a, 0b00010100 ; Код для подключения монохромного дисплея и цифрового индикатора 10 | st a, 0x3E ; Подключаем вывод 11 | ldi b, 0x02 ; Первое простое число 2 12 | ldi d, 0x41 ; Указатель на дисплее для первого числа 13 | st b, 0x3A ; Выводим число на цифровой индикатор 14 | st b, d ; Выводим число на дисплей 15 | inc b ; Второе простое число 3 16 | inc d ; Дважды смещаем указатель на дисплее, чтобы перейти на следующий ряд 17 | inc d 18 | st b, 0x3A ; Выводим число на цифровой индикатор 19 | st b, d ; Выводим число на дисплей 20 | 21 | ; Цикл подбора кандидатов на простые числа 22 | next: inc b ; Дважды увеличиваем число-кандидат - идём только по нечётным числам 23 | inc b 24 | ldi d, 0x41 ; Устанавливаем указатель на первое простое число на дисплее 25 | 26 | ; Цикл подбора множителей среди уже найденных простых чисел 27 | factor: inc d ; Смещаем указатель на следующее простое число на дисплее 28 | inc d 29 | ld c, d ; Берём простое число с дисплея в качестве множителя 30 | mov a, b ; Копируем число-кандидат 31 | shr a ; Делим его на 2 32 | sub a, c ; Вычитаем из него текущий множитель 33 | js prime ; Если множитель больше половины числа-кандидата, то результат будет 34 | ; меньше нуля, и значит дальше подбирать множители нет смысла - мы 35 | ; уже нашли новое простое число 36 | mov a, b ; Копируем число-кандидат 37 | 38 | ; Цикл вычитания множителя из числа-кандидата 39 | loop: sub a, c ; Вычитание 40 | jz next ; Если результат равен нулю, значит число не простое, переходим к 41 | ; следующему кандидату 42 | jns loop ; Если результат больше нуля, продолжаем вычитать 43 | 44 | jmp factor ; Если результат меньше нуля, переходим к следующему множителю 45 | 46 | ; Работаем с найденным простым числом 47 | prime: ld d, last ; Читаем указатель на последнее найденное простое число на дисплее 48 | inc d ; Дважды смещаем указатель 49 | inc d 50 | st d, last ; Сохраняем указатель 51 | st b, 0x3A ; Выводим новое простое число на цифровой индикатор 52 | st b, d ; Выводим новое простое число на дисплей 53 | ldi a, 0x5F ; Берём указатель на последнее возможное место в нижнем ряду дисплея 54 | xor a, d ; Сравниваем два указателя 55 | jnz next ; Если они не равны, переходим к рассмотрению следующего 56 | ; числа-кандидата 57 | hlt ; Завершаем выполнение программы 58 | 59 | last db 0x43 ; Указатель на последнее найденное простое число на дисплее 60 | -------------------------------------------------------------------------------- /computer-v2/asm/space-fight.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Space Fight!" game for a computer made of logic arrows ## 3 | ## Исходный код игры "Space Fight!" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v2 ## 5 | ## (с) 2024 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | 10 | ; Константы 11 | WIN_LEFT equ 30 ; Количество врагов, которое необходимо сбить для победы 12 | STEP_CNT equ 14 ; Количество доступных ходов на первом уровне 13 | KEY_LEFT equ 0x11 ; Код клавиши "Влево" 14 | KEY_RIGHT equ 0x13 ; Код клавиши "Вправо" 15 | KEY_FIRE equ 0x20 ; Код клавиши "Пробел" (огонь) 16 | 17 | 18 | 19 | ; Очищаем нижнюю часть дисплея 20 | clear: ldi b, clear_loop 21 | ldi c, 4 22 | ldi d, 0x5B 23 | 24 | clear_loop: st a, d 25 | dec d 26 | dec c 27 | jnz b 28 | 29 | ldi c, 12 30 | 31 | ; Заполняем дисплей случайно расположенными врагами. 32 | ; Регистр C уже содержит счётчик для выхода из цикла. 33 | ; Регистр D уже содержит указатель на дисплее на нижнюю границу заполнения. 34 | random: rnd a ; Работаем с правой частью дисплея 35 | rnd b 36 | and a, b ; Совмещаем два случайных байта, чтобы для каждого бита 37 | ; получилась вероятность 25% 38 | shl a ; Отодвигаем биты от края дисплея, где невозможно произвести 39 | ; выстрел 40 | st a, d 41 | dec d 42 | rnd a ; Повторяем то же самое для левой части дисплея 43 | rnd b 44 | and a, b 45 | shr a 46 | st a, d 47 | dec d 48 | dec c 49 | jnz random 50 | 51 | random_jmp: jmp score 52 | 53 | ; Выводим стартовый счёт на цифровой индикатор 54 | score: ld a, win_left 55 | st a, win_left 56 | ldi a, step 57 | st a, random_jmp +1 ; Блок "score" нужен лишь однократно, поэтому делаем так, 58 | ; чтобы в дальнейшем переходить через него 59 | 60 | ; Опрашиваем клавиатуру на нажатие одной из клавиш управления кораблём 61 | step: ldi b, KEY_LEFT 62 | ldi c, KEY_RIGHT 63 | ldi d, KEY_FIRE 64 | 65 | step_loop: ld a, step_left ; Уменьшаем счётчик ходов 66 | dec a 67 | js level ; Если ходов не осталось, переходим на следующий уровень 68 | st a, step_left 69 | ld a, in_out 70 | xor d, a 71 | jz fire 72 | xor c, a 73 | jmp step_end ; Переходим через зарезервированную область памяти 74 | 75 | ; Переменные и порты 76 | 77 | win_left db WIN_LEFT ; Текущий адрес подключён к выводу на цифровой индикатор и 78 | ; используется как счётчик врагов, оставшихся до победы 79 | void1 db 0 80 | 81 | step_cnt db STEP_CNT ; Количество доступных ходов на текущем уровне 82 | step_left db STEP_CNT ; Счётчик оставшихся ходов на текущем уровне 83 | in_out db 0b00010100 ; Порт ввода/вывода, подключаем монохромный дисплей и 84 | ; цифровой индикатор 85 | bank db 0 ; Порт банка памяти (не используется) 86 | 87 | ; Область дисплея 0x40...0x5F содержит заставку, отображаемую во время загрузки программы 88 | display db 0b00101000, 0b00000000, ; ██ ██ ; 89 | 0b00100000, 0b00101000, ; ██ ██ ██ ; 90 | 0b00000000, 0b00001000, ; ██ ; 91 | 0b00001001, 0b00000000, ; ██ ██ ; 92 | 0b00100001, 0b00100000, ; ██ ██ ██ ; 93 | 0b00100011, 0b10001000, ; ██ ██████ ██ ; 94 | 0b00000011, 0b10001000, ; ██████ ██ ; 95 | 0b00001011, 0b10100000, ; ██ ██████ ██ ; 96 | 0b00001010, 0b10100000, ; ██ ██ ██ ██ ; 97 | 0b00101110, 0b11101000, ; ██ ██████ ██████ ██ ; 98 | 0b00101111, 0b11101000, ; ██ ██████████████ ██ ; 99 | 0b00111011, 0b10111000, ; ██████ ██████ ██████ ; 100 | 0b00110101, 0b01011000, ; ████ ██ ██ ██ ████ ; 101 | 0b00100000, 0b00001000, ; ██ ██ ; 102 | 0b00000001, 0b00000000, ; ██ ; 103 | 0b00000011, 0b10000000 ; ██████ ; 104 | 105 | ; Продолжаем обработку клавиатуры 106 | step_end: jz right 107 | xor b, a 108 | jnz step_loop 109 | 110 | ; Сдвигаем корабль влево 111 | left: ld b, 0x5E 112 | shl b 113 | jc step ; Если мы у левого края, переходим к следующему ходу 114 | ldi b, 0x5F 115 | ldi c, 4 116 | ldi d, left_loop 117 | 118 | left_loop: ld a, b 119 | rcl a 120 | st a, b 121 | dec b 122 | dec c 123 | jnz d 124 | 125 | jmp step 126 | 127 | ; Сдвигаем корабль вправо 128 | right: ld b, 0x5F 129 | shr b 130 | jc step ; Если мы у правого края, переходим к следующему ходу 131 | ldi b, 0x5C 132 | ldi c, 4 133 | ldi d, right_loop 134 | 135 | right_loop: ld a, b 136 | rcr a 137 | st a, b 138 | inc b 139 | dec c 140 | jnz d 141 | 142 | jmp step 143 | 144 | ; Производим выстрел 145 | fire: ldi d, 0x5C ; Определяем стартовое положение снаряда 146 | ld a, d 147 | test a 148 | jnz fire2 149 | inc d 150 | 151 | fire2: ld b, d 152 | ldi c, 14 153 | 154 | ; Невидимый полёт снаряда 155 | fire_loop: dec d 156 | dec d 157 | ld a, d 158 | and a, b 159 | jnz fire_hit ; Если попали, выходим из цикла 160 | dec c 161 | jnz fire_loop 162 | 163 | jmp step ; Промах, переходим к следующему ходу 164 | 165 | ; Попадание. Убираем врага с дисплея, уменьшаем счётчик врагов и переходим к следующему ходу. 166 | fire_hit: ld a, d 167 | xor a, b 168 | st a, d 169 | ld a, win_left 170 | dec a 171 | st a, win_left ; Выводим счётчик врагов на цифровой индикатор 172 | jnz step ; Если счётчик врагов не равен нулю, переходим к следующему 173 | ; ходу 174 | 175 | ; Мы выиграли! Выводим на дисплей заставку-приз и завершаем выполнение программы. 176 | win: ldi b, display 177 | ldi c, prize 178 | ldi d, win_loop 179 | 180 | win_loop: ld a, c 181 | st a, b 182 | inc b 183 | inc c 184 | jnz d 185 | 186 | hlt 187 | 188 | ; Переходим на следующий уровень. 189 | ; Здесь враги станут ближе к нам, а число доступных ходов уменьшится. 190 | level: ld a, step_cnt ; Уменьшаем число доступных ходов на два 191 | dec a 192 | dec a 193 | st a, step_cnt 194 | st a, step_left 195 | ld a, 0x5A ; Определяем, есть ли хоть один враг в нижнем ряду 196 | ld b, 0x5B 197 | ldi c, 26 198 | ldi d, 0x59 199 | or a, b 200 | jz scroll ; Если в нижнем ряду нет врагов, переходим к сдвигу всех 201 | ; врагов ближе к нам 202 | 203 | ; Игра проиграна. Осталось сдвинуть всех врагов ещё ближе и завершить выполнение программы. 204 | level_lose: inc c 205 | inc c 206 | inc d 207 | inc d 208 | ldi a, 0x01 ; Код инструкции "hlt". Записываем его в то место, где 209 | ; закончится заполнение верхнего ряда. 210 | st a, random_jmp 211 | 212 | ; Сдвигаем всех врагов ближе к нам. 213 | ; Регистр C уже содержит счётчик для выхода из цикла. 214 | ; Регистр D уже содержит указатель на нижнюю границу для сдвига карты. 215 | scroll: mov b, d 216 | ld a, b 217 | inc b 218 | inc b 219 | st a, b 220 | dec d 221 | dec c 222 | jnz scroll 223 | 224 | ; После сдвига остаётся заполнить верхний ряд дисплея новыми врагами 225 | inc c 226 | inc d 227 | inc d 228 | jmp random 229 | 230 | void2 db 0 231 | 232 | ; Область 0xE0...0xFF содержит заставку-приз для победителя 233 | prize db 0b00000000, 0b00000000, 234 | 0b00001101, 0b10000000, 235 | 0b00110010, 0b01000000, 236 | 0b01000000, 0b00100000, 237 | 0b01001011, 0b11100000, 238 | 0b01010100, 0b00111100, 239 | 0b00110000, 0b00100010, 240 | 0b00101010, 0b10111010, 241 | 0b00101010, 0b10101010, 242 | 0b00101010, 0b10101010, 243 | 0b00101010, 0b10111010, 244 | 0b00101010, 0b10100010, 245 | 0b00100000, 0b00111100, 246 | 0b00100000, 0b00100000, 247 | 0b00011111, 0b11000000, 248 | 0b00000000, 0b00000000 249 | -------------------------------------------------------------------------------- /computer-v2/asm/typewriter.asm: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | ## Source code for the "Typewriter" program for a computer made of logic arrows ## 3 | ## Исходный код программы "Typewriter" для компьютера из логических стрелочек ## 4 | ## https://github.com/chubrik/LogicArrows/tree/main/computer-v2 ## 5 | ## (с) 2024 Arkadi Chubrik (arkadi@chubrik.org) ## 6 | #################################################################################################### 7 | 8 | 9 | inc a ; 0b00000001, код для подключения терминала 10 | ldi b, 0x3E ; Порт ввода/вывода 11 | st a, b ; Подключаем вывод 12 | ldi c, 0x3C ; Адрес для вывода в терминал 13 | ldi d, loop ; Запоминаем адрес начала итерации для ускорения цикла 14 | 15 | loop: ld a, b ; Читаем код нажатой клавиши, порт ввода при этом обнуляется 16 | st a, c ; Выводим символ в терминал 17 | jmp d ; Повторяем итерацию 18 | -------------------------------------------------------------------------------- /computer-v2/img/cpu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/cpu.jpg -------------------------------------------------------------------------------- /computer-v2/img/digits.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/digits.jpg -------------------------------------------------------------------------------- /computer-v2/img/display.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/display.jpg -------------------------------------------------------------------------------- /computer-v2/img/fibonacci-sequence.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/fibonacci-sequence.jpg -------------------------------------------------------------------------------- /computer-v2/img/font-test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/font-test.jpg -------------------------------------------------------------------------------- /computer-v2/img/game-of-life.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/game-of-life.jpg -------------------------------------------------------------------------------- /computer-v2/img/keyboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/keyboard.jpg -------------------------------------------------------------------------------- /computer-v2/img/prime-numbers.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/prime-numbers.jpg -------------------------------------------------------------------------------- /computer-v2/img/ram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/ram.jpg -------------------------------------------------------------------------------- /computer-v2/img/space-fight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/space-fight.jpg -------------------------------------------------------------------------------- /computer-v2/img/summary.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/summary.jpg -------------------------------------------------------------------------------- /computer-v2/img/terminal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/terminal.jpg -------------------------------------------------------------------------------- /computer-v2/img/typewriter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/computer-v2/img/typewriter.jpg -------------------------------------------------------------------------------- /computer-v2/programming.md: -------------------------------------------------------------------------------- 1 | # Программирование 2 | Чтобы запустить на компьютере вашу собственную программу, выполните следующие шаги: 3 | - Изучите язык ассемблера на этой странице. 4 | - Откройте [онлайн-компилятор](https://chubrik.github.io/arrows-compiler/) и в его левой части 5 | напишите программу. 6 | - Скопируйте из правой части скомпилированный код. 7 | - Зайдите на [карту с компьютером](https://logic-arrows.io/map-computer) и нажмите `Ctrl+V`, чтобы 8 | вставить дискету с программой. 9 | - Подсоедините провод от дискеты к общему проводу от других дискет. 10 | - Нажмите на кнопку у дискеты и дождитесь загрузки программы в память компьютера. 11 | - Нажмите на кнопку `RUN` у компьютера и наблюдайте за ходом выполнения вашей программы. 12 |


13 | 14 | 15 | ## Общие принципы 16 | Программа состоит из последовательности инструкций и набора данных. Каждая инструкция занимает в 17 | памяти 1 байт. Некоторые инструкции требуют дополнительный операнд, который также занимает 1 байт и 18 | располагается в памяти сразу вслед за инструкцией. Процессор выполняет инструкции друг за другом, 19 | начиная с адреса `00`. Согласно программе, процессор также может совершать переходы к различным 20 | участкам кода, тем самым создавая циклы, подпрограммы и пр. 21 | 22 | Выполняя программу, процессор взаимодействует с регистрами `A` `B` `C` `D` и флагами `Z` `S` `C` 23 | `O`. Каждый регистр хранит 1 байт и имеет универсальное назначение. Флаги выражают собой результат 24 | выполнения вычислительных операций и могут использоваться как условия для переходов: 25 | - **Флаг Z (ноль)** показывает, что результат равен нулю. 26 | - **Флаг S (знак)** показывает, что у результата активен старший бит. В знаковой арифметике это 27 | означает отрицательное число. 28 | - **Флаг C (перенос)** показывает, что во время операции один из битов вышел за пределы байта. 29 | - **Флаг O (переполнение)** показывает, что два операнда имели одинаковый старший бит, но в 30 | результате операции он изменился. В знаковой арифметике это означает выход за допустимый диапазон. 31 |


32 | 33 | 34 | ## Синтаксис 35 | Для наилучшего понимания ассемблера и его синтаксиса ознакомьтесь с [примерами программ](asm), 36 | созданных автором компьютера. Здесь рассмотрим основные моменты: 37 | ```asm 38 | my_const equ 196 ; Объявляем константу my_const со значением 196 при помощи ключевого 39 | ; слова "equ". В качестве значений в любом месте кода можно исполь- 40 | ; зовать числа от 0 до 255 в десятичной (196), шестнадцатеричной 41 | ; (0xC4), восьмеричной (0304) или двоичной (0b11000100) форме. Также 42 | ; можно использовать символ в кавычках ("Д"), что аналогично числу 43 | ; в кодировке cp1251. 44 | 45 | ldi a, my_const ; При сборке кода "my_const" заменяется на конечное значение, 46 | ; поэтому фактически программа выполнит "ldi a, 196" 47 | 48 | my_label: ; Объявляем метку, она обозначает текущий адрес в коде 49 | inc a 50 | jnz my_label ; Программа совершит переход к метке my_label, т.е. возникнет цикл 51 | 52 | my_data db 1, 2, 3, 0x4, "абв" ; Объявляем область данных при помощи ключевого слова "db". Метка 53 | ; my_data обозначает адрес начала этой области. В качестве данных 54 | ; через запятую можно указывать числа, константы, метки и строки. 55 | 56 | my_data_size equ $ - my_data ; "$" обозначает текущий адрес в коде. Вычитая адрес my_data, 57 | ; получаем размер области данных, что и присваиваем константе 58 | ; my_data_size. 59 | ``` 60 |

61 | 62 | 63 | ## Инструкции 64 | 65 | ### Управляющие инструкции 66 | Инструкции из этого набора отвечают за работу с памятью и переходы. В таблице ниже используются 67 | условные ***X*** и ***Y*** для обозначения любых регистров, а также ***F*** для обозначения любого 68 | флага. 69 | 70 | Инструкция | Описание 71 | ---|--- 72 | nop | Ничего не делает, переходит к следующей инструкции 73 | ld ***X*** | Загружает в ***X*** значение из памяти, используя операнд как адрес 74 | ld ***X***, ***Y*** | Загружает в ***X*** значение из памяти, используя ***Y*** как адрес 75 | ldi ***X*** | Загружает операнд непосредственно в ***X*** 76 | st ***X*** | Сохраняет значение ***X*** в память по адресу из операнда 77 | st ***X***, ***Y*** | Сохраняет значение ***X*** в память по адресу из ***Y*** 78 | jmp | Безусловный переход по адресу из операнда 79 | jmp ***X*** | Безусловный переход по адресу из ***X*** 80 | j***F*** | Переход по адресу из операнда, если ***F*** = 1 81 | jn***F*** | Переход по адресу из операнда, если ***F*** = 0 82 | j***F*** ***X*** | Переход по адресу из ***X***, если ***F*** = 1 83 | jn***F*** ***X*** | Переход по адресу из ***X***, если ***F*** = 0 84 | hlt | Останавливает выполнение программы 85 | 86 |
87 | 88 | 89 | ### Вычислительные инструкции 90 | Инструкции из этого набора производят вычисления на основе регистров и флагов. В таблице ниже 91 | используются условные ***X*** и ***Y*** для обозначения любых регистров. 92 | 93 | Инструкция | Описание | Воздействие
на флаги 94 | ---|---|--- 95 | clr ***X*** | Обнуляет ***X*** | – 96 | mov ***X***, ***Y*** | Копирует из ***Y*** в ***X*** | – 97 | and ***X***, ***Y*** | Побитовое И между ***X*** и ***Y***, результат записывает в ***X*** | Z, S 98 | or ***X***, ***Y*** | Побитовое ИЛИ между ***X*** и ***Y***, результат записывает в ***X*** | Z, S 99 | xor ***X***, ***Y*** | Исключающее ИЛИ между ***X*** и ***Y***, результат записывает в ***X*** | Z, S 100 | add ***X***, ***Y*** | Складывает ***X*** и ***Y***, результат записывает в ***X*** | Z, S, C, O 101 | adc ***X***, ***Y*** | Складывает ***X***, ***Y*** и флаг `C`, результат записывает в ***X*** | Z, S, C, O 102 | sub ***X***, ***Y*** | Из ***X*** вычитает ***Y***, результат записывает в ***X*** | Z, S, C, O 103 | sbb ***X***, ***Y*** | Из ***X*** вычитает ***Y*** и флаг `C`, результат записывает в ***X*** | Z, S, C, O 104 | test ***X*** | Обновляет флаги по значению ***X*** | Z, S 105 | inc ***X*** | Прибавляет 1 | Z, S 106 | dec ***X*** | Вычитает 1 | Z, S 107 | not ***X*** | Заменяет каждый бит на противоположный | Z, S 108 | neg ***X*** | Меняет знак (значение трактует как число со знаком) | Z, S 109 | rnd ***X*** | Генерирует случайное значение | Z, S 110 | shl ***X*** | Сдвигает все биты на один влево, правый бит обнуляет | Z, S, C 111 | shr ***X*** | Сдвигает все биты на один вправо, левый бит обнуляет | Z, S, C 112 | sar ***X*** | Сдвигает все биты на один вправо, левый бит не меняет | Z, S, C 113 | rcl ***X*** | Сдвигает все биты на один влево, правый бит берёт из флага `С` | Z, S, C 114 | rcr ***X*** | Сдвигает все биты на один вправо, левый бит берёт из флага `С` | Z, S, C 115 | 116 |
117 | 118 | 119 | ### Таблица инструкций 120 | Каждая инструкция имеет свой код (opcode). К примеру, `st a, d` имеет код `3C` (ряд, 121 | затем колонка). 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 |
0123456789ABCDEF
0nophltjmpjmp ajmp bjmp cjmp djzjsjcjojnzjnsjncjno
1jz ajs ajc ajo ajz bjs bjc bjo bjz cjs cjc cjo cjz djs djc djo d
2jnz ajns ajnc ajno ajnz bjns bjnc bjno bjnz cjns cjnc cjno cjnz djns djnc djno d
3st ast b, ast c, ast d, ast a, bst bst c, bst d, bst a, cst b, cst cst d, cst a, dst b, dst c, dst d
4ld a, ald b, ald c, ald d, ald a, bld b, bld c, bld d, bld a, cld b, cld c, cld d, cld a, dld b, dld c, dld d, d
5ld ald bld cld dldi aldi bldi cldi dreserved
6inc aadd b, aadd c, aadd d, aadd a, binc
b
add c, badd d, badd a, cadd b, cinc
c
add d, cadd a, dadd b, dadd c, dinc
d
7dec asub b, asub c, asub d, asub a, bdec bsub c, bsub d, bsub a, csub b, cdec csub d, csub a, dsub b, dsub c, ddec d
8not aadc b, aadc c, aadc d, aadc a, bnot badc c, badc d, badc a, cadc b, cnot cadc d, cadc a, dadc b, dadc c, dnot d
9neg asbb b, asbb c, asbb d, asbb a, bneg bsbb c, bsbb d, bsbb a, csbb b, cneg csbb d, csbb a, dsbb b, dsbb c, dneg d
Aclr amov b, amov c, amov d, amov a, bclr bmov c, bmov d, bmov a, cmov b, cclr cmov d, cmov a, dmov b, dmov c, dclr d
Btest aand b, aand c, aand d, aand a, btest band c, band d, band a, cand b, ctest cand d, cand a, dand b, dand c, dtest d
Crcl aor b, aor c, aor d, aor a, brcl bor c, bor d, bor a, cor b, crcl cor d, cor a, dor b, dor c, drcl d
Drcr axor b, axor c, axor d, axor a, brcr bxor c, bxor d, bxor a, cxor b, crcr cxor d, cxor a, dxor b, dxor c, drcr d
Eshl ashl bshl cshl dshr ashr bshr cshr dsar asar bsar csar drnd arnd brnd crnd d
Freserved
243 | -------------------------------------------------------------------------------- /computer-v2/specification.md: -------------------------------------------------------------------------------- 1 | # Устройство и характеристики 2 | Компьютер состоит из процессора, оперативной памяти, устройств ввода/вывода и набора программ. 3 | Основные характеристики: 4 | - 8-битная архитектура, процессор с 4 регистрами и флагами. 5 | - Оперативная память до 32 КБ с интегрированной видеопамятью и портами. 6 | - Ввод/вывод: клавиатура, цветной дисплей, терминал и цифровой индикатор. 7 | - Собственный язык ассемблера (см. [Программирование](programming.md)). 8 | - Загрузка программ со специальных дискет. 9 |


10 | 11 | 12 | ## Процессор 13 | Процессор состоит из указателя инструкции `IP`, регистра инструкции `IR`, интерпретатора, 4 14 | свободных регистров `A` `B` `C` `D`, флагов `Z` `S` `C` `O`, многофункционального сумматора, 15 | механизма битового сдвига и ряда других мелких механизмов. 16 | 17 | Процессор читает инструкцию из RAM по адресу, лежащему в `IP`. Инструкция попадает в `IR`, инициируя 18 | выполнение той или иной операции. Во время выполнения операции происходит взаимодействие с 19 | регистрами и флагами. После этого `IP` инкрементируется и процесс повторяется. Подробнее см. 20 | [Программирование](programming.md). 21 | 22 | Процессор 23 |

24 | 25 | 26 | ## Оперативная память 27 | Памятью компьютера является RAM объёмом 1 КБ с возможностью расширения до 32 КБ. Единицей хранимой 28 | информации является 1 байт, адрес доступа к памяти также представляет собой 1 байт. 29 | 30 | Т. к. 8-битный адрес может обеспечить доступ лишь по 256 адресам RAM, существует система 31 | переключения банков через порт `3F`. Если в этот порт записать число, диапазон адресов `80...FF` 32 | переключится на банк соответствующего номера. Число `0` как исключение ссылается на банк № 1. Номер 33 | банка не может быть предзаписан на дискете, а управляется только во время выполнения программы. 34 | 35 | Оперативная память 36 |

37 | 38 | 39 | ## Клавиатура 40 | Полноформатная клавиатура, приближенная к реальному ПК, с крупными кнопками и подробными подписями. 41 | Имеет латинскую и кириллическую раскладки, однократный и постоянный верхний регистр. Текущий режим 42 | клавиатуры отображается непосредственно на кнопках переключения раскладки и регистра. 43 | 44 | После нажатия на любую клавишу, её код может быть прочитан программой из порта `3E`. После чтения 45 | порт автоматически обнуляется для возможности определения повторных нажатий. Коды символов 46 | соответствуют кодировке [`cp1251`](https://ru.wikipedia.org/wiki/Windows-1251). Клавишам `←` `↑` `→` 47 | `↓` `Enter` соответствуют коды `11` `12` `13` `14` `0A`. 48 | 49 | Клавиатура 50 |

51 | 52 | 53 | ## Дисплей 54 | Для подключения вывода на дисплей необходимо в порт `3E` записать значение с битами 5 и 4 равными 55 | `01` для монохромного режима или `11` для цветного. В монохромном режиме дисплей использует диапазон 56 | памяти `40...5F`, в цветном `40...7F`. Ниже показано соответствие адресов различным участкам 57 | дисплея. Отправка данных по этим адресам приводит к появлению на дисплее соответствующих пикселей. 58 | 59 | Дисплей 60 |

61 | 62 | 63 | ## Терминал 64 | Размер терминала 12×4 символов с возможностью расширения до любых размеров. Имеет подвижный курсор в 65 | нижней строке, где можно выводить различные символы и графику. Остальные строки являются историей и 66 | постепенно сдвигаются вверх. Поддерживает команды `\a` `\b` `\t` `\n` `\v` `\f` `\r` `delete` и 67 | перемещение курсора стрелками. 68 | 69 | Для подключения вывода в терминал необходимо в порт `3E` записать значение с установленным битом 0. 70 | Далее, каждый байт, отправленный по адресу `3C`, выводится в терминал как символ в кодировке 71 | [`cp1251`](https://ru.wikipedia.org/wiki/Windows-1251). Шесть байт, последовательно отправленных по 72 | адресу `3D`, выводятся в терминал как один графический символ. 73 | 74 | Терминал 75 |

76 | 77 | 78 | ## Цифровой индикатор 79 | Состоит из 5 десятичных цифр и имеет два режима работы: беззнаковый с диапазоном `0...65535` и 80 | знаковый с диапазоном `−32768...+32767`. Для подключения вывода на цифровой индикатор необходимо в 81 | порт `3E` записать значение с битами 3 и 2 равными `01` для беззнакового режима или `11` для 82 | знакового. Далее, каждый байт, отправленный по адресу `3A`, преобразуется в десятичный формат и 83 | выводится на цифровой индикатор. Байт, отправленный по адресу `3B`, преобразуется в десятичный 84 | формат с умножением на 256. 85 | 86 | Цифровой индикатор 87 | -------------------------------------------------------------------------------- /img/adder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/adder.jpg -------------------------------------------------------------------------------- /img/ant.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/ant.jpg -------------------------------------------------------------------------------- /img/bcd-converter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/bcd-converter.jpg -------------------------------------------------------------------------------- /img/cpu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/cpu.jpg -------------------------------------------------------------------------------- /img/digits.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/digits.jpg -------------------------------------------------------------------------------- /img/gates.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/gates.jpg -------------------------------------------------------------------------------- /img/life.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/life.jpg -------------------------------------------------------------------------------- /img/matrix-compact.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/matrix-compact.jpg -------------------------------------------------------------------------------- /img/matrix-rgb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/matrix-rgb.jpg -------------------------------------------------------------------------------- /img/matrix.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/matrix.jpg -------------------------------------------------------------------------------- /img/multiplier.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/multiplier.jpg -------------------------------------------------------------------------------- /img/ram-256-v1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/ram-256-v1.jpg -------------------------------------------------------------------------------- /img/ram-256-v2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/ram-256-v2.jpg -------------------------------------------------------------------------------- /img/ram-32k.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/ram-32k.jpg -------------------------------------------------------------------------------- /img/ram-64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/ram-64.jpg -------------------------------------------------------------------------------- /img/rom-compact.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/rom-compact.jpg -------------------------------------------------------------------------------- /img/subtractor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/subtractor.jpg -------------------------------------------------------------------------------- /img/transmitters.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/transmitters.jpg -------------------------------------------------------------------------------- /img/youtube.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chubrik/LogicArrows/b5d271581e7eddbcd81870857c5f4b5dd9e0634c/img/youtube.jpg --------------------------------------------------------------------------------