azya52/BrickEmuPy

Another typos in the ROM?

ilyakurdyukov opened this issue · 17 comments

1. This looks wrong because the high nibble of the second byte is not used, so it should be zero:

1C2:    mov R3R2, 0x08                  ;6840

2. There are two ways the code gets to CF5.

First:

C4E:    call 858                        ;F858
C50:    jnc C54                         ;CC54
C52:    jmp CF5                         ;ECF5

Second:

CCA:    mov R1R0, 0x93                  ;5309
...
CD2:    dec R1                          ;13
...
CDC:    jz A, CF5                       ;B4F5

On the first path, calling 858 returns R1R0 = 0x39, on the second path R1R0 = 0x83.

I think there is a typo in the address for jumping from C52.

CF5:    inc R0                          ;10
CF6:    mov A, [R1R0]                   ;04

It could also be a bug in the ROM code.

3. Suddenly inverts the pixel at 254_3 (col=4, row=19):

3F8:    mov A, 0xA                      ;7A
3F9:    call 7EA                        ;F7EA
3FB:    jnc 401                         ;CC01
3FD:    mov R1R0, 0xFE                  ;5E0F
3FF:    mov A, 0x8                      ;78
400:    xor [R1R0], A                   ;1E
401:

There may be a typo in 0xFE.

Thanks! 1, 2 fixed. But I didn't find any errors in block 3.

Thanks!

If you're wondering what I'm doing: I wrote my own disassembler module for IDA, little by little I read the assembly code and comment what the code does. That's how I find these typos.

That's pretty much what I assumed. Really useful work! I hope you will publish the results?

That's pretty much what I assumed. Really useful work! I hope you will publish the results?

If there are results. I first comment and then rewrite it in C. But there's a lot of work, I don’t know if I have the patience to rewrite everything.

It's too hard to decompile manually, so I wrote a decompiler to C code. It works, but I don't know how to fix the timings (because decompiled code don't keep the count of executed instructions).

The decompiler is here. Don't expect much from it. However, it's more convenient than reading the assembly, and you can easily change the game code.

I started manually rewriting the code generated by the decompiler.

The TMRL is used only to obtain random numbers.

I found the table of select screen numbers and the table of Tetris figures:

	static const uint8_t rom_01a[] = {
		/* mode numbers */
#if 1
#define B1(x,n) (#x[7-n]!='.')<<n
#define X(x) B1(x,0)|B1(x,1)|B1(x,2)|B1(x,3)|B1(x,4)|B1(x,5)|B1(x,6)|B1(x,7),
X(.@@@@@..)X(........)X(.@....@.)X(.@...@..)X(...@@@..)X(@@@..@..)X(.@@@@@..)X(@.......)X(.@@.@@..)X(.@@..@..)
X(@.....@.)X(.@....@.)X(@....@@.)X(@..@..@.)X(..@..@..)X(@.@...@.)X(@..@..@.)X(@....@@.)X(@..@..@.)X(@..@..@.)
X(@.....@.)X(@@@@@@@.)X(@...@.@.)X(@..@..@.)X(.@...@..)X(@.@...@.)X(@..@..@.)X(@..@@...)X(@..@..@.)X(@..@..@.)
X(@.....@.)X(......@.)X(@..@..@.)X(@..@..@.)X(@@@@@@@.)X(@.@...@.)X(@..@..@.)X(@.@.....)X(@..@..@.)X(@..@..@.)
X(.@@@@@..)X(........)X(.@@...@.)X(.@@.@@..)X(.....@..)X(@..@@@..)X(.@..@@..)X(@@......)X(.@@.@@..)X(.@@@@@..)
#undef X
#undef B1
#else
		/* 0    1    2    3    4    5    6    7    8    9  */
		0x7c,0x00,0x42,0x44,0x1c,0xe4,0x7c,0x80,0x6c,0x64,
		0x82,0x42,0x86,0x92,0x24,0xa2,0x92,0x86,0x92,0x92,
		0x82,0xfe,0x8a,0x92,0x44,0xa2,0x92,0x98,0x92,0x92,
		0x82,0x02,0x92,0x92,0xfe,0xa2,0x92,0xa0,0x92,0x92,
		0x7c,0x00,0x62,0x6c,0x04,0x9c,0x4c,0xc0,0x6c,0x7c
#endif
	};

	static const uint8_t rom_e80[] = {
#if 1
#define B1(x,n) (#x[3-n]!='.')<<n
#define X(x) B1(x,0)|B1(x,1)|B1(x,2)|B1(x,3)
#define Y(a,b,c,d) (a)<<4|b,(c)<<4|d,
		/* classic figures */
		Y(X(@@@.), /* 0 */
		  X(.@..),
		  X(....), 3)

		Y(X(@@@.), /* 1 */
		  X(@...),
		  X(....), 3)

		Y(X(@@@.), /* 2 */
		  X(..@.),
		  X(....), 3)

		Y(X(.@@.), /* 3 */
		  X(.@@.),
		  X(....), 0)

		Y(X(.@..), /* 4 */
		  X(@@..),
		  X(@...), 1)

		Y(X(@...), /* 5 */
		  X(@@..),
		  X(.@..), 1)

		Y(X(....), /* 6 */
		  X(....),
		  X(@@@@), 1)

		/* weird figures */
		Y(X(@@@.), /* 7 */
		  X(.@..),
		  X(.@..), 3)

		Y(X(.@..), /* 8 */
		  X(@@@.),
		  X(.@..), 0)

		Y(X(@@@.), /* 9 */
		  X(@.@.),
		  X(....), 3)

		Y(X(..@.), /* 10 */
		  X(@@@.),
		  X(@...), 1)

		Y(X(@...), /* 11 */
		  X(@@@.),
		  X(..@.), 1)

		Y(X(....), /* 12 */
		  X(..@.),
		  X(.@@.), 3)

		Y(X(....), /* 13 */
		  X(.@..),
		  X(.@..), 1)
#undef Y
#undef X
#undef B1
#else
		/* classic figures */
		0xe4,0x03, 0xe8,0x03, 0xe2,0x03, 0x06,0x60 ,0x4c,0x81, 0x8c,0x41, 0x00,0xf1,
		/* weird figures */
		0xe4,0x43, 0x4e,0x40, 0xea,0x03, 0x2e,0x81, 0x8e,0x21, 0x02,0x63, 0x04,0x41
#endif
 };
0x90: game_type
0x91: mode_low
0x92: mode_high

Apparently there are only 8 unique types of games:

game_type = (mode - 1) % 8

0..3 is Tetris, for 1 and 3 – every fourth figure will be weird one.

The counter starts at 1, so at the beginning: classic, classic, weird, classic, classic, classic, weird, etc.

Can you check the jump at 0x2EF? This look like nonsense, if this jump goes to 0x2FA, then R3R2 is used as a BCD (binary coded decimal) value to increment some counter, and is also used as an address. They could do that, but it looks more like a ROM reading error.

2EC:    mov R3R2, 0x15                  ;6501
2EE:    mov A, [R3R2]                   ;06   <== used as address
2EF:    ja2 2FA                         ;92FA
2F1:    mov R4, A                       ;28
2F2:    mov A, 0x0                      ;70
2F3:    mov R3, A                       ;26
2F4:    stc                             ;2B
2F5:    rlc A                           ;03
2F6:    dec R4                          ;19
2F7:    jnz R4, 2F4                     ;DAF4
2F9:    mov R2, A                       ;24
2FA:    mov R1R0, 0x4D                  ;5D04
2FC:    jmp 302                         ;E302
2FE:    mov R3R2, 0x01                  ;6100
300:    mov R1R0, 0x4C                  ;5C04
302:    call 370                        ;F370 <= this function adds BCD in R3R2 to memory pointed by R1R0
304:    jnz R4, 30D                     ;DB0D

Looks like m[0x15] is the points for completing horizontal lines in Tetris.

For completing one line it is 100, for two lines it is 300, for three it is 700, for four it is 1500. And they actually reuse the address of the variable as a BCD constant!

So that's why such values were used, it's convenient :)
Just in case, I also checked this block of code - there are no reading errors.

I think the output to the PA port is related to sound. For example this place:

7C2:    mov R1R0, 0xC3                  ;530C
7C4:    mov A, 0xF                      ;7F
7C5:    xor A, [R1R0]                   ;1B
7C6:    ja0 7DE                         ;87DE <== skip if sound is off
7C8:    mov R3R2, 0x5D                  ;6D05
7CA:    mov A, 0x8                      ;78
7CB:    mov [R3R2], A                   ;07
7CC:    mov A, 0xB                      ;7B

7CD:    mov R1R0, 0x87                  ;5708
7CF:    and A, [R1R0]                   ;1A
7D0:    out PA, A                       ;30

; wait here
7D1:    inc R0                          ;10
7D2:    jnz R0, 7D1                     ;A7D1
7D4:    inc R1                          ;12
7D5:    jnz R1, 7D1                     ;AFD1

7D7:    mov A, R2                       ;25
7D8:    xor A, 0x2                      ;4302
7DA:    mov R2, A                       ;24
7DB:    dec R4                          ;19
7DC:    jnz R4, 7CD                     ;DFCD
7DE:

There is a delay at address 0x7D1. I think that this is playing sounds that are not in the sound banks.

Are you sure that the PA port is not connected to anything?

There are E23 96in1 versions with a set of LEDs around the screen, maybe they are controlled through this port? There are also “talking” Brick Games, with a separate voice chip.

LED controls can be these:

3E3:    mov R1R0, 0xD3                  ;530D
3E5:    mov A, 0xF                      ;7F
3E6:    xor [R1R0], A                   ;1E
3E7:    mov R1R0, 0xD1                  ;510D
3E9:    xor [R1R0], A                   ;1E

7DE:    mov A, 0xF                      ;7F
7DF:    mov R1R0, 0xD1                  ;510D
7E1:    mov [R1R0], A                   ;05
7E2:    mov R1R0, 0xCF                  ;5F0C
7E4:    mov [R1R0], A                   ;05
7E5:    mov R1R0, 0xD3                  ;530D
7E7:    mov A, 0x0                      ;70
7E8:    mov [R1R0], A                   ;05

They don't seem to be used for any logic.

Please check if the immediate at address 0x272..0x273 is actually 0x2d.

270:    mov R1R0, 0x44                  ;5404
272:    mov R3R2, 0x2D                  ;6D02
274:    call E17                        ;FE17

The call on E17 does memcpy(m + r3r2, m + r1r0, 4), except that the addresses are wrapped around the lower four bits.

So this call does:

ram[0x2D] = ram[0x44]
ram[0x2E] = ram[0x45]
ram[0x2F] = ram[0x46]
ram[0x20] = ram[0x47] <= this is strange

Edit: It looks like this code makes pieces fall in Tetris, changing the 0x2D to 0x2C leads to glitches in the game. The copied value at address 0x20 doesn't seem to be used, so they might be intentionally copying 4 bytes instead of 3, just to save ROM bytes.

About the LEDs - if you can find a video of such a version of this Brick Game, then perhaps I can understand what values ​​control the LEDs.

I've checked all 6 bytes, and there are no errors. Unfortunately, not only video, I can’t even find photos right now. I came across one of these for sale when I was searching for Tetris to decapsulate.

That's pretty much what I assumed. Really useful work! I hope you will publish the results?

I think I've lost interest, so here is the last result.