Форум программистов, компьютерный форум CyberForum.ru

Эмулятор CHIP-8 - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 67, средняя оценка - 4.88
ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
10.08.2010, 06:34     Эмулятор CHIP-8 #1
Мы с 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;
}
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
10.08.2010, 06:34     Эмулятор CHIP-8
Посмотрите здесь:

C++ Эмулятор трёхадресной машины.
C++ Эмулятор Вебки под Seven
C++ Эмулятор клавиатуры
C++ Эмулятор Нормальных Алгорифмов Маркова
Эмулятор мышки C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Tronix
 Аватар для Tronix
157 / 104 / 5
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 16:26     Эмулятор CHIP-8 #61
Цитата Сообщение от Евгений М. Посмотреть сообщение
Tronix, так не интересно. Интереснее свою написать.
Ну не знаю.. Одну и туже работу проделывать... Да и скажу я вам не так то просто ассемблер написать. Я имею ввиду, чтобы с поддержкой макросов, условных конструкций, лексическим анализатором (чтобы понимал выражения типа "LD V0,(23+4) div 2") и так далее. Винтер уже все это сделал, его вы все равно не переплюните.

Я вот, например, решил сразу компилятор Pascal-подобный для CHIP8 писать )) Есть уже первые альфы, выдает рабочий код, но пока без оптимизации, из-за этого бинарный файлы получаются большими (около килобайта средняя программа на паскале). Ну буду допиливать потихоньку, хотя я уже написал на своем компиляторе пару демок для SCHIP8.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Alex_Sabaka
 Аватар для Alex_Sabaka
622 / 39 / 9
Регистрация: 28.07.2010
Сообщений: 895
Завершенные тесты: 3
22.08.2010, 16:51     Эмулятор CHIP-8 #62
Есть еще один вопрос: как понимать эти две инстр.

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

8XYE VX = VX SHL 1 (VX=VX*2), VF = carry
Зачем там регистр Y, если он не используется?
fasked
Эксперт C++
 Аватар для fasked
4925 / 2505 / 180
Регистрация: 07.10.2009
Сообщений: 4,306
Записей в блоге: 1
22.08.2010, 17:52     Эмулятор CHIP-8 #63
Цитата Сообщение от Alex Sabaka Посмотреть сообщение
Есть еще один вопрос: как понимать эти две инстр.
Зачем там регистр Y, если он не используется?
Регистр VY и правда не используется, наверное написан просто потому что семантика 8-го опкода такая.
Код
8XY6 VX = VX SHR 1 (VX=VX/2), VF = carry
Сдвиг регистра VX на 1 бит вправо (это тоже самое, что и деление на 2), регистр VF хранит флаг переноса.
Код
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;
            }
        }
    }
}
Tronix
 Аватар для Tronix
157 / 104 / 5
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 18:29     Эмулятор CHIP-8 #64
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Кстати ошибся я, 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 Кб, 97 просмотров)
Евгений М.
1033 / 974 / 53
Регистрация: 28.02.2010
Сообщений: 2,817
Завершенные тесты: 2
22.08.2010, 18:42     Эмулятор CHIP-8 #65
Цитата Сообщение от Tronix Посмотреть сообщение
делайте синтаксис совместимый
Да... С этим будет небольшие проблемы.
Вот один вариант обозначений: http://michael.toren.net/mirrors/chip8/chip8def.htm
Второй вариант: http://devernay.free.fr/hacks/chip8/chip8.xls
Tronix
 Аватар для Tronix
157 / 104 / 5
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 18:47     Эмулятор CHIP-8 #66
Цитата Сообщение от Евгений М. Посмотреть сообщение
Да... С этим будет небольшие проблемы.
Вот один вариант обозначений: http://michael.toren.net/mirrors/chip8/chip8def.htm
Второй вариант: http://devernay.free.fr/hacks/chip8/chip8.xls
Второй вариант - это как раз синтаксис CHIPPER. Первый вообще странный, ни разу никакого исходника игры на нем не видел. А на CHIPPERе много исходников игрушек есть для CHIP8.
Евгений М.
1033 / 974 / 53
Регистрация: 28.02.2010
Сообщений: 2,817
Завершенные тесты: 2
22.08.2010, 19:45     Эмулятор CHIP-8 #67
Tronix, возник вопрос. Как я понял сама прога на Chip8 имеет имя "SCTEST" (без расширения). А тогда для чего SCTEST.C8?
Tronix
 Аватар для Tronix
157 / 104 / 5
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 20:04     Эмулятор CHIP-8 #68
Цитата Сообщение от Евгений М. Посмотреть сообщение
Tronix, возник вопрос. Как я понял сама прога на Chip8 имеет имя "SCTEST" (без расширения). А тогда для чего SCTEST.C8?
Да, сама выполняемая прога SCTEST (без расширения). Файл SCTEST.C8 - исходный текст программы на ассемблере (обычный текстовый файл).
Евгений М.
1033 / 974 / 53
Регистрация: 28.02.2010
Сообщений: 2,817
Завершенные тесты: 2
22.08.2010, 21:04     Эмулятор CHIP-8 #69
Tronix, возник еще один вопрос.
ERROR 0
Problems with Fx65 instruction. Can't load zeroes from memory to
registers.
Почему нельзя загружать нули из памяти в регистры?
Tronix
 Аватар для Tronix
157 / 104 / 5
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 21:26     Эмулятор CHIP-8 #70
Цитата Сообщение от Евгений М. Посмотреть сообщение
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 команды.
Евгений М.
1033 / 974 / 53
Регистрация: 28.02.2010
Сообщений: 2,817
Завершенные тесты: 2
22.08.2010, 21:54     Эмулятор CHIP-8 #71
Понял. Ну раз так, тогда хочу сказать об одном моменте.
До проверки опкода FX65 регистр V0 имел значение равной нулю. После правильного и неправильного выполнения этого опкода значение V0 не изменится. Или я что упустил?
Tronix
 Аватар для Tronix
157 / 104 / 5
Регистрация: 22.08.2010
Сообщений: 215
22.08.2010, 21:58     Эмулятор CHIP-8 #72
Цитата Сообщение от Евгений М. Посмотреть сообщение
Понял. Ну раз так, тогда хочу сказать об одном моменте.
До проверки опкода FX65 регистр V0 имел значение равной нулю. После правильного и неправильного выполнения этого опкода значение V0 не изменится. Или я что упустил?
Нет, все правильно замечено. Считайте это недочетом в моем тесте. Надо было туда конечно какое-то значение отличное от нуля записать. Пофиксю в версии 1.3 Но на результат вообще-то не влияет особенно, если эмулятор работает нормально - он пройдет этот тест.
fasked
Эксперт C++
 Аватар для fasked
4925 / 2505 / 180
Регистрация: 07.10.2009
Сообщений: 4,306
Записей в блоге: 1
23.08.2010, 08:33     Эмулятор CHIP-8 #73
Если я правильно понимаю, то например по прихождунию опкода 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. После операции вычитания.
Tronix
 Аватар для Tronix
157 / 104 / 5
Регистрация: 22.08.2010
Сообщений: 215
23.08.2010, 10:11     Эмулятор CHIP-8 #74
Цитата Сообщение от 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);
но в сях я "ноль", поэтому не пинайте сильно если не правильно написал. Главное смысл передал.
fasked
Эксперт C++
 Аватар для fasked
4925 / 2505 / 180
Регистрация: 07.10.2009
Сообщений: 4,306
Записей в блоге: 1
23.08.2010, 12:11     Эмулятор CHIP-8 #75
Цитата Сообщение от Tronix Посмотреть сообщение
но в сях я "ноль", поэтому не пинайте сильно если не правильно написал. Главное смысл передал.
Да все правильно. Я почему-то думал, что результат в этой операции должен в VY записываться. Надо было внимательнее читать, когда реализовывали логику. Явно поторопились.

А вот 23 ошибка это уже только для CHIP48?
Tronix
 Аватар для Tronix
157 / 104 / 5
Регистрация: 22.08.2010
Сообщений: 215
23.08.2010, 13:00     Эмулятор CHIP-8 #76
Цитата Сообщение от 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]; Вот и всех делов.
Alex_Sabaka
 Аватар для Alex_Sabaka
622 / 39 / 9
Регистрация: 28.07.2010
Сообщений: 895
Завершенные тесты: 3
23.08.2010, 19:39     Эмулятор CHIP-8 #77
Кое-что уже получается с ассемблером. Но вот проблема при дизассемблировании и повторной компиляции получается вот такая фигня.
До:
Эмулятор CHIP-8
После:
Эмулятор CHIP-8

 Комментарий модератора 
Прикрепляйте вложения в расширенном режиме к сообщению, а не выкладывайте на другие ресурсы
Евгений М.
1033 / 974 / 53
Регистрация: 28.02.2010
Сообщений: 2,817
Завершенные тесты: 2
23.08.2010, 19:41     Эмулятор CHIP-8 #78
Alex Sabaka, компилировал своим компилятором. А дизассемблировал чем?
Alex_Sabaka
 Аватар для Alex_Sabaka
622 / 39 / 9
Регистрация: 28.07.2010
Сообщений: 895
Завершенные тесты: 3
23.08.2010, 22:33     Эмулятор CHIP-8 #79
Своим дизассемблером.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
25.08.2010, 18:22     Эмулятор CHIP-8
Еще ссылки по теме:

C++ Эмулятор. На С, С++
C++ Эмулятор SetTimer
Эмулятор часов настенных (стрелочных) C++

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

Или воспользуйтесь поиском по форуму:
fasked
Эксперт C++
 Аватар для fasked
4925 / 2505 / 180
Регистрация: 07.10.2009
Сообщений: 4,306
Записей в блоге: 1
25.08.2010, 18:22     Эмулятор CHIP-8 #80
С 16 ревизией у меня наконец-то получилось сыграть в крестики-нолики
Миниатюры
Эмулятор CHIP-8  
Yandex
Объявления
25.08.2010, 18:22     Эмулятор CHIP-8
Ответ Создать тему
Опции темы

Текущее время: 13:35. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru