Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.87/82: Рейтинг темы: голосов - 82, средняя оценка - 4.87
В астрале
Эксперт С++
 Аватар для ForEveR
8049 / 4806 / 655
Регистрация: 24.06.2010
Сообщений: 10,562

Эмулятор CHIP-8

10.08.2010, 06:34. Показов 16936. Ответов 90
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Мы с fasked написали простенький эмулятор для платформы CHIP-8.
CHIP-8
Остались вопросы по графике и клавиатуре. Если кто знает как реализовать - помогите, пожалуйста.
Так же приглашаем для участия в доработке этого проекта, а так же следующих проектах всех, кто желает учиться и трудиться.
Кому интересна работа эмулятора все вопросы к fasked)
Полный проект в прикрепленном архиве в сообщении ниже

CHIP8
types.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**************************************************************
* TYPES.H                                                     *
* Emulator CHIP-8                                             *
*                                                             *
* basic typedef and constants                                 *
*                                                             *
* LAST UPD: 09/08/2010                                        *
*                                                             *
* by Lavroff and fasked (c)                                   *
**************************************************************/
 
#ifndef HEADER_TYPES_H
#define HEADER_TYPES_H
 
typedef unsigned char  BYTE;
typedef unsigned short WORD;
 
#define DATA_REG_SIZE   16
#define ADDR_REG_SIZE   4096
#define STACK_SIZE      16
 
#define SCREEN_VER_SIZE 32
#define SCREEN_HOR_SIZE 64 
 
#define VIDEO_MEM_SIZE (SCREEN_HOR_SIZE * SCREEN_VER_SIZE)
 
#endif /* HEADER_TYPES_H */


chip-8.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**************************************************************
* CHIP8.H                                                     *
* Emulator CHIP-8                                             *
*                                                             *
* basic declarations                                          *
*                                                             *
* LAST UPD: 09/08/2010                                        *
*                                                             *
* by Lavroff and fasked (c)                                   *
**************************************************************/
 
#ifndef _HEADER_CHIP8_H_
#define _HEADER_CHIP8_H_
 
#include "types.h"
 
//Declaration of global variables and arrays
static BYTE dataRegisters[DATA_REG_SIZE];
static BYTE addrRegisters[ADDR_REG_SIZE];
 
static WORD stackMemory[STACK_SIZE];
static BYTE videoMemory[VIDEO_MEM_SIZE];
 
static BYTE soundTimer;
static BYTE delayTimer;
 
static WORD iRegister;
 
static BYTE stackCounter;
static WORD offsetMemory;
 
const static BYTE font[80] = { 
   0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
   0x20, 0x60, 0x20, 0x20, 0x70, // 1
   0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
   0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
   0x90, 0x90, 0xF0, 0x10, 0x10, // 4
   0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
   0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
   0xF0, 0x10, 0x20, 0x40, 0x40, // 7
   0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
   0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
   0xF0, 0x90, 0xF0, 0x90, 0x90, // A
   0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
   0xF0, 0x80, 0x80, 0x80, 0xF0, // C
   0xE0, 0x90, 0x90, 0x90, 0xE0, // D
   0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
   0xF0, 0x80, 0xF0, 0x80, 0x80  // F
};
 
//Declaration of macro
#define START_OFFSET 200
 
#define DATA_REG_VX(n)\
   (dataRegisters[(0x0F00 & n) >> 8])
 
#define DATA_REG_VY(n)\
   (dataRegisters[(0x00F0 & n) >> 4])
 
#define DATA_REG_VF\
   (dataRegisters[0xF])
 
#define DATA_REG_V0\
   (dataRegisters[0x0])
 
//Declaration of functions
void InitialiseEmulator();
void EmulatorRun();
 
void LoadOpcodes(WORD * opcodes);
 
WORD GetOpcode();
void ExecuteOpcode (WORD opcode);
 
void ExecuteOpcode0(WORD opcode);
void ExecuteOpcode1(WORD opcode);
void ExecuteOpcode2(WORD opcode);
void ExecuteOpcode3(WORD opcode);
void ExecuteOpcode4(WORD opcode);
void ExecuteOpcode5(WORD opcode);
void ExecuteOpcode6(WORD opcode);
void ExecuteOpcode7(WORD opcode);
void ExecuteOpcode8(WORD opcode);
void ExecuteOpcode9(WORD opcode);
void ExecuteOpcodeA(WORD opcode);
void ExecuteOpcodeB(WORD opcode);
void ExecuteOpcodeC(WORD opcode);
void ExecuteOpcodeD(WORD opcode);
void ExecuteOpcodeE(WORD opcode);
void ExecuteOpcodeF(WORD opcode);
 
void RedrawScreen();
void Delay();
void SoundPlay();
 
#endif /* HEADER_CHIP8_H */


chip8.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
#include <iostream>
#include <windows.h>
#include "chip8.h"
 
void RedrawScreen()
{
   system("cls");
   for(int i = 0; i < SCREEN_HOR_SIZE; ++i)
   {
      for(int j = 0; j < SCREEN_VER_SIZE; ++j)
      {
         if(videoMemory[i * SCREEN_HOR_SIZE + j])
            printf("*");
         else
            printf(" ");
      }
 
      printf("\n");
   }
}
 
void Delay()
{
   if(delayTimer)
   {
      Sleep(delayTimer*1000);
   }
}
 
void SoundPlay()
{
   if(soundTimer)
   {
      for(int i=0;i<soundTimer;++i)
         std::cout<<'\a'<<std::endl;
   }
}
 
void InitialiseEmulator()
{
   stackCounter = 0;
   soundTimer = 0;
   delayTimer = 0;
 
   offsetMemory = START_OFFSET;
   iRegister = START_OFFSET;
 
   memset(dataRegisters, 0, DATA_REG_SIZE * sizeof(BYTE));
   memset(addrRegisters, 0, ADDR_REG_SIZE * sizeof(BYTE));
   memset(videoMemory, 0, VIDEO_MEM_SIZE * sizeof(BYTE));
   memset(stackMemory, 0, STACK_SIZE * sizeof(WORD));
   memcpy(addrRegisters, font, 80);
}
 
void LoadOpcodes(WORD * opcodes)
{
   memcpy(addrRegisters + offsetMemory, opcodes, ADDR_REG_SIZE * sizeof(BYTE));
}
 
WORD GetOpcode()
{
   return *(WORD*)(addrRegisters + offsetMemory); // TODO исправить на более правильный вариант
}
 
void EmulatorRun()
{
   WORD opcode = 0;
   for(;;)
   {
      opcode = GetOpcode();
//#ifdef _DEBUG
//      printf("%s: %#0.4x | %s: %#0.4x\n", "OFFSET", offsetMemory, "OPCODE", opcode);
//#endif
 
      if(opcode == 0)
         break;
 
      ExecuteOpcode(opcode);
 
      offsetMemory += 2;
 
      SoundPlay();
      Delay();
      //RedrawScreen();
   }
}
 
void ExecuteOpcode0(WORD opcode)
{
   if((opcode & 0x00FF) == 0xE0)
      memset(videoMemory, 0, VIDEO_MEM_SIZE * sizeof(BYTE)); // 0x00E0: Clear the Screen
   
   else if((opcode & 0x00FF) == 0xEE)
   {
      --stackCounter;
      offsetMemory = stackMemory[stackCounter];
   }
 
   else if((opcode & 0xF000) == 0)
   {
      //TODO 0NNN-Opcode
   }
}
 
void ExecuteOpcode1(WORD opcode)
{
   offsetMemory = opcode & 0x0FFF;
}
 
void ExecuteOpcode2(WORD opcode)
{
   if(stackCounter < 16)
   {
      stackMemory[stackCounter] = offsetMemory;
      ++stackCounter;
   }
 
   offsetMemory = opcode & 0x0FFF;
}
 
void ExecuteOpcode3(WORD opcode)
{
   if(DATA_REG_VX(opcode) == (opcode & 0x00FF))
      offsetMemory += 2;
}
 
void ExecuteOpcode4(WORD opcode)
{
   if(DATA_REG_VX(opcode) != (opcode & 0x00FF))
      offsetMemory += 2;
}
 
void ExecuteOpcode5(WORD opcode)
{
   if(DATA_REG_VX(opcode) == DATA_REG_VY(opcode))
      offsetMemory += 2;
}
 
void ExecuteOpcode6(WORD opcode)
{
   DATA_REG_VX(opcode) = 0x00FF & opcode;
}
 
void ExecuteOpcode7(WORD opcode)
{
   DATA_REG_VX(opcode) += 0x00FF & opcode;
}
 
void ExecuteOpcode8(WORD opcode)
{
   if((opcode & 0xF) == 0x0)
      DATA_REG_VX(opcode) = DATA_REG_VY(opcode);
   
   else if((opcode & 0xF) == 0x1)
      DATA_REG_VX(opcode) |= DATA_REG_VY(opcode);
 
   else if((opcode & 0xF) == 0x2)
      DATA_REG_VX(opcode) &= DATA_REG_VY(opcode);
 
   else if((opcode & 0xF) == 0x3)
      DATA_REG_VX(opcode) ^= DATA_REG_VY(opcode);
 
   else if((opcode & 0xF) == 0x4)
   {
      WORD t = DATA_REG_VX(opcode) + DATA_REG_VY(opcode);
      DATA_REG_VX(opcode) = (BYTE)t;
      DATA_REG_VF = (BYTE)(t & 0x100); 
   }
 
   else if((opcode & 0xF) == 0x5)
   {
      WORD t = DATA_REG_VX(opcode) - DATA_REG_VY(opcode);
      DATA_REG_VX(opcode) = (BYTE)t;
      DATA_REG_VF = (BYTE)((t & 0x100) >> 8);
   }
 
   else if((opcode & 0xF) == 0x6)
   {
      DATA_REG_VF = DATA_REG_VX(opcode) & 1;
      DATA_REG_VX(opcode) >>= 1;
   }
 
   else if((opcode & 0xF) == 0x7)
   {
      WORD t= DATA_REG_VY(opcode)- DATA_REG_VX(opcode);
      DATA_REG_VX(opcode) = (BYTE)t;
      DATA_REG_VF = (BYTE)((t & 0x100) >> 8);
   }
 
   else if((opcode & 0xF) == 0xE)
   {
      DATA_REG_VF = DATA_REG_VX(opcode) & 0x80;
      DATA_REG_VX(opcode) <<= 1;
   }
}
 
void ExecuteOpcode9(WORD opcode)
{
   if(DATA_REG_VX(opcode) != DATA_REG_VY(opcode))
      offsetMemory+=2;  
}
 
void ExecuteOpcodeA(WORD opcode)
{
   iRegister = opcode & 0x0FFF;
}
 
void ExecuteOpcodeB(WORD opcode)
{
   offsetMemory = (opcode & 0x0FFF) + dataRegisters[0];
}
 
void ExecuteOpcodeC(WORD opcode)
{
   DATA_REG_VX(opcode) = (BYTE)(rand()) & (opcode & 0x00FF);
}
 
void ExecuteOpcodeD(WORD opcode)
{
   // TODO D-opcode
   WORD x = DATA_REG_VX(opcode);
   WORD y = DATA_REG_VY(opcode);
   WORD h = opcode & 0x000F;
 
   for(int i = 0; i < h; ++i)
   {
      WORD data = addrRegisters[i + iRegister];
      
      for(int j = 0; j < 8; ++j)
      {
         if((data & (0x80 >> i)) != 0)
         {
            if(videoMemory[j + x + (( y + i ) * SCREEN_HOR_SIZE)] == 1)
               DATA_REG_VF = 1;
            else
               DATA_REG_VF=0;
 
            videoMemory[j + x + (( y + i ) * SCREEN_HOR_SIZE)] ^= 1;
         }
      }
   }
}
 
void ExecuteOpcodeE(WORD opcode)
{
  if((opcode & 0x00FF) == 0x9E)
  {
     // TODO EX9E-Opcode. Keyboard
  }
  else if((opcode & 0x00FF) == 0xA1) 
  {
     // TODO EXA1-Opcode. Keyboard
  }
}
 
void ExecuteOpcodeF(WORD opcode)
{
  if((opcode & 0x000F) == 0x7)
     DATA_REG_VX(opcode) = delayTimer;
 
  else if((opcode & 0x000F) == 0xA)
  {
     //TODO FX0A opcode
  }
  
  else if((opcode & 0x00FF) == 0x15)
     delayTimer = DATA_REG_VX(opcode);
  
  else if((opcode & 0x00FF) == 0x18)
     soundTimer = DATA_REG_VX(opcode);
  
  else if((opcode & 0x00FF) == 0x1E)
     iRegister += DATA_REG_VX(opcode);
  
  else if((opcode & 0x00FF) == 0x29)
  {
     iRegister=DATA_REG_VX(opcode);
  }
  
  else if((opcode & 0x00FF) == 0x33)
  {
     addrRegisters[iRegister] = DATA_REG_VX(opcode) / 100;
     addrRegisters[iRegister + 1] = (DATA_REG_VX(opcode) % 100) / 10;
     addrRegisters[iRegister + 2] = DATA_REG_VX(opcode) % 10;
  }
  
  else if((opcode & 0x00FF) == 0x55)
  {
     for(int i = 0; i < ((opcode & 0x0F00) >> 8); ++i)
        addrRegisters[i + iRegister] = dataRegisters[i];  
  }
  
  else if((opcode & 0x00FF) == 0x65)
  {
     for(int i = 0; i < ((opcode & 0x0F00) >> 8); ++i)
        dataRegisters[i] = addrRegisters[i + iRegister];
  }
} 
 
void ExecuteOpcode(WORD opcode)
{
  WORD opcodeGroup = opcode >> 12; // выделить старшие 4 бита
 
  switch(opcodeGroup)
  {
  case 0x0:
     ExecuteOpcode0(opcode);
     break;
  case 0x1:
     ExecuteOpcode1(opcode);
     break;
  case 0x2:
     ExecuteOpcode2(opcode);
     break;
  case 0x3:
     ExecuteOpcode3(opcode);
     break;
  case 0x4:
     ExecuteOpcode4(opcode);
     break;
  case 0x5:
     ExecuteOpcode5(opcode);
     break;
  case 0x6:
     ExecuteOpcode6(opcode);
     break;
  case 0x7:
     ExecuteOpcode7(opcode);
     break;
  case 0x8:
     ExecuteOpcode8(opcode);
     break;
  case 0x9:
     ExecuteOpcode9(opcode);
     break;
  case 0xA:
     ExecuteOpcodeA(opcode);
     break;
  case 0xB:
     ExecuteOpcodeB(opcode);
     break;
  case 0xC:
     ExecuteOpcodeC(opcode);
     break;
  case 0xD:
     ExecuteOpcodeD(opcode);
     break;
  case 0xE:
     ExecuteOpcodeE(opcode);
     break;
  case 0xF:
     ExecuteOpcodeF(opcode);
     break;
  }
}


main.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "chip8.h"
 
int main()
{
   // массив опкодов
   // в дальнейшем должны читаться из файлов 
   // специально откомпилированнынх под CHIP-8
   WORD opcodes[ADDR_REG_SIZE] = { 0x6000, 0x6100, 0xA222, 0xC201, 
                                   0x3201, 0xA2E1, 0xD014, 0x7004, 
                                   0x3040, 0x1204, 0x6000, 0x7104, 
                                   0x3120, 0x1204, 0x121C, 0x8040, 
                                   0x2010, 0x2040, 0x8010, 0x0000
   };  
 
   // Инициализация ЦПУ эмулятора
   // очистка памяти, сброс счетчиков и таймеров
   InitialiseEmulator();
 
   // Загрузка опкодов в память эмулятора
   LoadOpcodes(opcodes);
 
   // Запуск эмулятора
   EmulatorRun();
 
   return 0;
}
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
10.08.2010, 06:34
Ответы с готовыми решениями:

Эмулятор. На С, С++
Здравствуйте уважаемые читатели форума! У меня такой вопрос: (СРАЗУ ПРЕДУПРЕЖДАЮ Я НЕ ОЧЕНЬ ХОРОШО РАЗБИРАЮСЬ ПОКА В ЭТОМ ТАК ЧТО ОЧЕНЬ...

Эмулятор телефона. С++
Эмулятор работы телефона. Телефон живёт своей жизнью может садиться батарея, а когда она сядет - батарея может зарядиться до 100%...

Эмулятор мышки
Здравствуйте все. Хотелось бы узнать, как можно на компьютере1 управлять курсором с помощью компьютера2 через USB-вход на компьютере1, так,...

90
 Аватар для Tronix
158 / 105 / 6
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 16:26
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Евгений М. Посмотреть сообщение
Tronix, так не интересно. Интереснее свою написать.
Ну не знаю.. Одну и туже работу проделывать... Да и скажу я вам не так то просто ассемблер написать. Я имею ввиду, чтобы с поддержкой макросов, условных конструкций, лексическим анализатором (чтобы понимал выражения типа "LD V0,(23+4) div 2") и так далее. Винтер уже все это сделал, его вы все равно не переплюните.

Я вот, например, решил сразу компилятор Pascal-подобный для CHIP8 писать )) Есть уже первые альфы, выдает рабочий код, но пока без оптимизации, из-за этого бинарный файлы получаются большими (около килобайта средняя программа на паскале). Ну буду допиливать потихоньку, хотя я уже написал на своем компиляторе пару демок для SCHIP8.
0
 Аватар для Alex_Sabaka
638 / 499 / 77
Регистрация: 28.07.2010
Сообщений: 895
22.08.2010, 16:51
Есть еще один вопрос: как понимать эти две инстр.

8XY6 VX = VX SHR 1 (VX=VX/2), VF = carry

8XYE VX = VX SHL 1 (VX=VX*2), VF = carry
Зачем там регистр Y, если он не используется?
0
Эксперт С++
 Аватар для fasked
5045 / 2624 / 241
Регистрация: 07.10.2009
Сообщений: 4,310
Записей в блоге: 5
22.08.2010, 17:52
Цитата Сообщение от Alex Sabaka Посмотреть сообщение
Есть еще один вопрос: как понимать эти две инстр.
Зачем там регистр Y, если он не используется?
Регистр VY и правда не используется, наверное написан просто потому что семантика 8-го опкода такая.
Code
1
8XY6 VX = VX SHR 1 (VX=VX/2), VF = carry
Сдвиг регистра VX на 1 бит вправо (это тоже самое, что и деление на 2), регистр VF хранит флаг переноса.
Code
1
8XYE VX = VX SHL 1 (VX=VX*2), VF = carry
Сдвиг регистра VX на 1 бит влево (тоже самое, что и умножение на 2), регистр VF - флаг переноса.
Alex Sabaka, что же Вы вообще мучаетесь. Просто посмотрите наши исходники. Может и ошибки найдете
чем вам CHIPPER от Девида Винтера не угодил? Вполне годная тулза, с исходником.
Tronix, Это и правда неинтересно. Проект начался для изучения построения подобных вещей. Смысл же нам пользоваться чужими разработками? Если хочешь понять, как это работает - просто попробуй сделать этом сам

Евгений М., поправил оба опкода. Все цифры кстати отрисовываются, а не только "1".
D-опкод в итоге сделал таким. Все таки, как я понял часть спрайта может находиться с одной стороны экрана, а часть с другой. Если придется координата 63 - то вылет за границы массива. В общем вот - UFO больше не вылетает, а тарелка добравшись до края экрана появляется с другой стороны
Смотрим 13 ревизию.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void ExecuteOpcodeD(WORD opc)
{
    BYTE x = REG_VX(opc);
    BYTE y = REG_VY(opc);
 
    BYTE w = 8;
    BYTE h = opc & 0xF;
 
    for(int i = 0; i < h; ++i)
    {
        assert((iRegister + i) < DATA_MEM_SIZE);
        BYTE data = dataMemory[iRegister + i];
        for(int j = 0; j < w; ++j)
        {
            if((data & (0x80 >> j)) != 0)
            {
                int x0 = (j + x) % SCREEN_HOR_RES;
                int y0 = (i + y) % SCREEN_VER_RES;
                int offset = y0 + (x0 * SCREEN_VER_RES);
 
                assert(offset < VIDEO_MEM_SIZE);
 
                if(videoMemory[offset] == 1)
                    REG_VF = 1;
                else
                    REG_VF = 0;
 
                videoMemory[offset] ^= 1;
            }
        }
    }
}
1
 Аватар для Tronix
158 / 105 / 6
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 18:29
Лучший ответ Сообщение было отмечено как решение

Решение

Кстати ошибся я, CHIPPER то не Девида Винтера, а Christian Egeberg . Ну не суть конечно, пишите свой ассемблер если хочется. Единственное пожелание или совет, делайте синтаксис совместимый. А то будет - там команда пересылки "LD", у вас какая-нибудь "MOV", еще у кого-то вообще "LET"... Хотя хозяин барин конечно.

Я когда отлаживал свой эмулятор, написал небольшую программу-ТЕСТ. Всего 26 различных ошибок, включая следующие основные вещи:
  • - правильную инициализацию
  • - работу FX65 инструкции
  • - системный шрифт 8x5
  • - работу инструкции BCD
  • - правильность математических операций ADD,SUB,SHL,SHR,XOR (состояние VF и собственно сам результат с переполнением и без)
  • - HP48 флаги (это только для SCHIP)
  • - FX1E инструкция (учитывается ли переполнение)
Полный исходник на ассемблере (CHIPPER) прилагается.
Если все тесты пройдены - выводится сообщение OK в левом верхнем углу. Если какой-то тест не пройден - выводится надпись ERROR с номером ошибки. В архиве прилагается описание номеров ошибок (правда маленько не полное, но если что - всегда можно посмотреть в исходник). Мне лично здорово помогло отладить мой эмуль.
Миниатюры
Эмулятор CHIP-8   Эмулятор CHIP-8  
Вложения
Тип файла: zip sctest_12.zip (3.8 Кб, 106 просмотров)
3
1080 / 1007 / 107
Регистрация: 28.02.2010
Сообщений: 2,889
22.08.2010, 18:42
Цитата Сообщение от Tronix Посмотреть сообщение
делайте синтаксис совместимый
Да... С этим будет небольшие проблемы.
Вот один вариант обозначений: http://michael.toren.net/mirro... ip8def.htm
Второй вариант: http://devernay.free.fr/hacks/chip8/chip8.xls
0
 Аватар для Tronix
158 / 105 / 6
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 18:47
Цитата Сообщение от Евгений М. Посмотреть сообщение
Да... С этим будет небольшие проблемы.
Вот один вариант обозначений: http://michael.toren.net/mirro... ip8def.htm
Второй вариант: http://devernay.free.fr/hacks/chip8/chip8.xls
Второй вариант - это как раз синтаксис CHIPPER. Первый вообще странный, ни разу никакого исходника игры на нем не видел. А на CHIPPERе много исходников игрушек есть для CHIP8.
0
1080 / 1007 / 107
Регистрация: 28.02.2010
Сообщений: 2,889
22.08.2010, 19:45
Tronix, возник вопрос. Как я понял сама прога на Chip8 имеет имя "SCTEST" (без расширения). А тогда для чего SCTEST.C8?
0
 Аватар для Tronix
158 / 105 / 6
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 20:04
Цитата Сообщение от Евгений М. Посмотреть сообщение
Tronix, возник вопрос. Как я понял сама прога на Chip8 имеет имя "SCTEST" (без расширения). А тогда для чего SCTEST.C8?
Да, сама выполняемая прога SCTEST (без расширения). Файл SCTEST.C8 - исходный текст программы на ассемблере (обычный текстовый файл).
1
1080 / 1007 / 107
Регистрация: 28.02.2010
Сообщений: 2,889
22.08.2010, 21:04
Tronix, возник еще один вопрос.
ERROR 0
Problems with Fx65 instruction. Can't load zeroes from memory to
registers.
Почему нельзя загружать нули из памяти в регистры?
0
 Аватар для Tronix
158 / 105 / 6
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 21:26
Цитата Сообщение от Евгений М. Посмотреть сообщение
Tronix, возник еще один вопрос.

Почему нельзя загружать нули из памяти в регистры?
Тут имеется в виду не "нельзя", а "не может". То есть что происходит: в моем тесте по определенному смещению есть массив из 16 нулей.
Assembler
1
2
3
4
5
6
7
8
9
NullReg:
    dw #0000
    dw #0000
    dw #0000
    dw #0000
    dw #0000
    dw #0000
    dw #0000
    dw #0000
Сперва я заполняю все регистры вручную цифрами от 0 до 0xF :
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    ld v0,0     ; fill all registers by hands
    ld v1,1     ; but we don't know, how work FX65 instruction
    ld v2,2
    ld v3,3
    ld v4,4
    ld v5,5
    ld v6,6
    ld v7,7
    ld v8,8
    ld v9,9
    ld va,#a
    ld vb,#b
    ld vc,#c
    ld vd,#d
    ld ve,#e
    ld vf,#f
Затем я устанавливаю регистр I на смещение, содержащее массив из нулей:
Assembler
1
    ld I, nullreg   ; fill all reg with null
И после этого выполняю команду FF65, которая должна во все регистры (от V0 до VF) загрузить содержимое из памяти по смещению в регистре I, то есть должны все регистры заполнится нулями:
Assembler
1
    ld vf, [I]
Далее я сравниваю каждый регистр с 0 (нулем). Если какой-то из регистров не обнулился, то уходим на ошибку (печатаем ERROR 0)
Assembler
1
2
3
4
5
6
7
8
9
10
11
    se vf,0     
    jp error1
    se ve,0     
    jp error1
    se vd,0 
    jp error1
           ; skip some code
    se v1,0
    jp error1
    se v0,0
    jp error1
Таким образом что-то в вашем эмуле не так с Fx65 инструкцией. Я конечно в сях не силен ни разу, но вот помоему тут у вас бага:

C++
1
2
3
4
5
6
7
8
    else if((opc & 0xFF) == 0x65)
    {
        for(int i = 0; i < ((opc & 0xF00) >> 8); ++i)
        {
            assert((iRegister + i) < DATA_MEM_SIZE);
            dataRegisters[i] = dataMemory[iRegister + i];
        }
    }
А конкретнее в условии цикла. Мне кажется надо писать так: i <= ((opc & 0xF00) >> 8) , (меньше или равно) а то последний регистр (VF) не обнуляется у вас. Ну и так же исправить условие цикла в обработке Fx55 команды.
1
1080 / 1007 / 107
Регистрация: 28.02.2010
Сообщений: 2,889
22.08.2010, 21:54
Понял. Ну раз так, тогда хочу сказать об одном моменте.
До проверки опкода FX65 регистр V0 имел значение равной нулю. После правильного и неправильного выполнения этого опкода значение V0 не изменится. Или я что упустил?
1
 Аватар для Tronix
158 / 105 / 6
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 21:58
Цитата Сообщение от Евгений М. Посмотреть сообщение
Понял. Ну раз так, тогда хочу сказать об одном моменте.
До проверки опкода FX65 регистр V0 имел значение равной нулю. После правильного и неправильного выполнения этого опкода значение V0 не изменится. Или я что упустил?
Нет, все правильно замечено. Считайте это недочетом в моем тесте. Надо было туда конечно какое-то значение отличное от нуля записать. Пофиксю в версии 1.3 Но на результат вообще-то не влияет особенно, если эмулятор работает нормально - он пройдет этот тест.
0
Эксперт С++
 Аватар для fasked
5045 / 2624 / 241
Регистрация: 07.10.2009
Сообщений: 4,310
Записей в блоге: 5
23.08.2010, 08:33
Если я правильно понимаю, то например по прихождунию опкода F365 регистры V0, V1, V2, V3 должны заполниться значениями из памяти по смещению I. А это значит, что ваше замечание было верным

Еще вопрос по поводу вашего теста. Код ошибки 6. В описании написано:
ERROR 6
After substraction 1-1 register VF must be 1, but he still 0.
То есть при операции 1 - 1 флаг переноса должен быть равен 1. Почему? Откуда возникнет флаг "отрицательности"?

Добавлено через 29 минут
Кажется я очень сглупил
Вопрос отпал. Но появился следующий:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
ld v1, 1
 
...
 
ld v0,1
ld ve,10       ; set error = 10
ld vf,0        ; set carry to 0
subn v0,v1  ; substract v1-v0. VF must be 1. Result must be 0.
se vf,1
jp error
ld ve,11       ; set error = 11
se v0,0
jp error
Ошибка под номером 11. Куда же должен записываться результат, если не в регистр V0. После операции вычитания.
0
 Аватар для Tronix
158 / 105 / 6
Регистрация: 22.08.2010
Сообщений: 215
23.08.2010, 10:11
Цитата Сообщение от fasked Посмотреть сообщение
Если я правильно понимаю, то например по прихождунию опкода F365 регистры V0, V1, V2, V3 должны заполниться значениями из памяти по смещению I. А это значит, что ваше замечание было верным
Правильно понимаете.
Еще вопрос по поводу вашего теста. Код ошибки 6. В описании написано:
ERROR 6
After substraction 1-1 register VF must be 1, but he still 0.
То есть при операции 1 - 1 флаг переноса должен быть равен 1. Почему? Откуда возникнет флаг "отрицательности"?

Добавлено через 29 минут
Кажется я очень сглупил
Вопрос отпал.
Да с этим я сам сначала тупил. Вот такая путаница - в случае сложение если происходит переполнение флаг VF устанавливается в 1, а вот при переполнение в результате отрицания флаг VF наоборот устанавливается в ноль. Если переполнения не произошло при отрицании - то VF = 1.
Но появился следующий:
Ошибка под номером 11. Куда же должен записываться результат, если не в регистр V0. После операции вычитания.
Дык все правильно, в V0 и записывается результат. Попробую прокомментировать:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ld v1, 1    ;в V1 заносим единицу
 
...
 
ld v0,1     ;в V0 тоже единица
ld ve,10       ;в VE - код ошибки 10, на случай если прыгнем по джампу на печать ошибки (процедура error)
ld vf,0        ;На всякий случай установим VF заведомо неправильный, на случай если эмулятор вообще
              ;не обрабатывает VF после отрицания.
subn v0,v1  ;вычитаем из v1 регистр v0. Тоесть из 1 вычитаем 1. Переполнения не произошло, поэтому
              ;как выше уже говорилось регистр VF должен принять значение 1. Результат должен по-
              ;меститься в V0 и должен быть равным нулю (1-1=0).
se vf,1     ;пропустить следующую инструкцию, если VF=1
jp error     ; Если VF не установился в 1, уходим на ERROR 10
ld ve,11       ;Установим код ошибки равный 11
se v0,0    ; Пропустить следующую инструкцию, если V0 равно нулю
jp error    ; V0 не равно нулю, поэтому печатаем ERROR 11
Таким образом, если появляется ошибка ERROR 11, то после выполнения операции вычитания из единицы единицу (1-1), результат в V0 получился не равным 0 (нулю).

Позволю напомнить, что в CHIP8 у нас есть две операции вычитания:
Assembler
1
SUB   VX, VY  ; код 8XY5
и
Assembler
1
SUBN   VX, VY ; код 8XY7
Первая вычитает из VX регистр VY и результат записывает в VX. (VX = VX - VY).
Вторая вычитает из VY регистр VX и результат записывает в VX. (VX = VY - VX).

В 11 тесте мы имеем дело со второй операцией вычитания. Возможно в этом проблема (тоесть вы в коде смотрите просто не на тот опкод).

Добавлено через 16 минут
То есть в этом коде:
C
1
2
3
4
5
6
7
8
9
else if((opc & 0xF) == 7)
    {
        if(REG_VY(opc) >= REG_VX(opc))
            REG_VF = 1;
        else
            REG_VF = 0;
 
        REG_VY(opc) -= REG_VX(opc);
    }
Я бы что-нибуть сделал со строчкой REG_VY(opc) -= REG_VX(opc); Должно быть как-то так по идее:
C
1
REG_VX(opc) = REG_VY(opc) - REG_VX(opc);
но в сях я "ноль", поэтому не пинайте сильно если не правильно написал. Главное смысл передал.
2
Эксперт С++
 Аватар для fasked
5045 / 2624 / 241
Регистрация: 07.10.2009
Сообщений: 4,310
Записей в блоге: 5
23.08.2010, 12:11
Цитата Сообщение от Tronix Посмотреть сообщение
но в сях я "ноль", поэтому не пинайте сильно если не правильно написал. Главное смысл передал.
Да все правильно. Я почему-то думал, что результат в этой операции должен в VY записываться. Надо было внимательнее читать, когда реализовывали логику. Явно поторопились.

А вот 23 ошибка это уже только для CHIP48?
0
 Аватар для Tronix
158 / 105 / 6
Регистрация: 22.08.2010
Сообщений: 215
23.08.2010, 13:00
Цитата Сообщение от fasked Посмотреть сообщение
А вот 23 ошибка это уже только для CHIP48?
Да, это уже для SCHIP. Реализуется кстати вообще проще простого, по аналогии с FX55/FX66 командами:
FX75 Сохранить V0...VX в HP48 флагах
FX85 Прочитать V0...VX из HP48 флагов
Просто заводится дополнительный массив размеров в 16 байт, и сохраняем регистры туда, а не в память по смещению I. Также и читаем оттуда. Примерно попробую:

В globals.h пишете что-то типа:
C++
1
2
#define HP48_SIZE      16
extern BYTE HP48flags[HP48_SIZE];
Ну а в cpu.cpp наверное так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void InitCpu()
{
    // bla-bla-bla
    memset(HP48flags, 0, HP48_SIZE);
    // bla-bla-bla
}
 
/// blablabla
 
    else if((opc & 0xFF) == 0x75)
    {
        for(int i = 0; i <= ((opc & 0xF00) >> 8); ++i)
        {
            HP48flags[i] = dataRegisters[i];
        }
    }
Также соответственно и для FX85 команды, только заменяем строку на dataRegisters[i] = HP48flags[i]; Вот и всех делов.
1
 Аватар для Alex_Sabaka
638 / 499 / 77
Регистрация: 28.07.2010
Сообщений: 895
23.08.2010, 19:39
Кое-что уже получается с ассемблером. Но вот проблема при дизассемблировании и повторной компиляции получается вот такая фигня.
До:

После:


 Комментарий модератора 
Прикрепляйте вложения в расширенном режиме к сообщению, а не выкладывайте на другие ресурсы
0
1080 / 1007 / 107
Регистрация: 28.02.2010
Сообщений: 2,889
23.08.2010, 19:41
Alex Sabaka, компилировал своим компилятором. А дизассемблировал чем?
0
 Аватар для Alex_Sabaka
638 / 499 / 77
Регистрация: 28.07.2010
Сообщений: 895
23.08.2010, 22:33
Своим дизассемблером.
0
Эксперт С++
 Аватар для fasked
5045 / 2624 / 241
Регистрация: 07.10.2009
Сообщений: 4,310
Записей в блоге: 5
25.08.2010, 18:22
С 16 ревизией у меня наконец-то получилось сыграть в крестики-нолики
Миниатюры
Эмулятор CHIP-8  
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
25.08.2010, 18:22

Эмулятор SetTimer
Ребята, подскажите, как написать функцию на подобие SetTimer. Дело в том, что используя std::thread, не дожидаясь завершение потока,...

Эмулятор клавиатуры
Привет. Нужно сделать програму которая при нажатии глобальных клавиш писала какойта текст. Как будто пользователь введёт его с клавиатуры....

Эмулятор трёхадресной машины.
В общем, имеется задача &quot;Эмулятор трёхадресной машины&quot;. Каждая инструкция имеет следующий формат: код инструкции (1 байт), операнд 1...

Эмулятор Вебки под Seven
Всем привет. Я проект пишу на С++, свой первый, более мене серьезный. Мне нужен эмулятор Веб камеры, но только что бы на Sevene работал......

Реализовать эмулятор командной строки
Реализовать эмулятор командной строки. Программа должна выполнять следующие функции: Создание файла Удаление файла Копирование файла ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
80
Ответ Создать тему
Новые блоги и статьи
Море в объективе
kumehtar 06.07.2026
Хотел написать много проникновенных слов, но передумал. Оно само - говорит. Кто слышит тот слышит.
Оказывается, Unreal Engine позволяет качество на порядки выше, чем было в Lineedge
Etyuhibosecyu 05.07.2026
Жаль, конечно, что я не узнал об этом, пока Lineedge существовала, а то бы Noname2331 написал, что волки превращаются в пиксельную кашу, а я бы его попросил скачать какую-нибудь бриллиантовую или Pro. . .
Doom для терминала без стрельбы и монстров. 3D Raycasting на ascii.
dcc0 05.07.2026
Попросил нейронную сеть deepai. org написать рейкастинг 3D с библиотекой ncurses для Linux. Чтобы можно было ходить на стрелочки. Чтобы стены были отрисованы символами. Справилась. Первый вариант. . .
Установка статуса документа по условию
Maks 05.07.2026
Алгоритм из решения ниже реализован на нетиповом документе "НарядПутевка" разработанного в КА2. Задача: в табличной части "Материалы" документа при записи автоматически устанавливать статус. . .
Сезонность и суточность закисления почв
anaschu 04.07.2026
200 часов это все равно моловато. Есть ситуации, но нестандартные, когда смена происходит за 5 лет. Но обычно это 50 лет и более. Наверное, закисление почвы происходит сезонно в средней. . .
В чем ценность человеческого опыта в глобальном смысле?
kumehtar 03.07.2026
Возможно, ценность человека не в том, что он однажды достигает мудрости, а в том, что он становится носителем карты пути. Он знает не только истину, но и последовательность внутренних изменений,. . .
интеграция AnyLogic с самописным REST API и переход на Odoo
anaschu 03.07.2026
Успешная интеграция AnyLogic с самописным REST API и переход на промышленную Odoo WMS Сегодня проделал огромный путь от простой симуляции физических процессов до построения полноценной. . .
Поиск всех путей на ориентированном графе. Linux
dcc0 02.07.2026
Переработка старого кода из моей статьи. Через несколько переработок от PHP кода к C89 (надеюсь, 89). Но довольно запутанно получилось. Код для Linux. Но если убрать time и то, что с ним. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru