# VOGONS

## Strange Bender range address offset calculation

### First post, by llm

Rank Member
Rank
Member

the game im reversing contains offset calculation code for the bender range

0x03 0x00 0x?4

this is the assembler code (same as in game, my build checks binary equality of my re-assembled version on every build)

... function getting this value

mov al, value
and al, 0Fh
cmp al, 0Ah
jz short no_dec
dec al

no_dec:
mov cl, 4
shl al, cl
mov cl, al
and al, 0Fh
or al, cl

i've converted that to C and check the value for 0-15 and the resulting adress

for( uint8_t value = 0; value < 16; ++value )
{
uint8_t al = value;
if( al != 0x0A )
{
--al;
}
uint8_t cl = 4;
al = al << cl;
cl = al;
al &= 0x0F;
al |= cl;

assert( ( address_byte2 & 0x0F ) == 0x04 );
}

produces these byte2 offsets - and i think that only input value 1-8 is usefull

value: 0x00, address_byte2: 0xF4 -> VALID???
value: 0x09, address_byte2: 0x84 -> VALID???
value: 0x0A, address_byte2: 0xA4 -> VALID???
value: 0x0B, address_byte2: 0xA4 -> VALID???
value: 0x0C, address_byte2: 0xB4 -> VALID???
value: 0x0D, address_byte2: 0xC4 -> VALID???
value: 0x0E, address_byte2: 0xD4 -> VALID???
value: 0x0F, address_byte2: 0xE4 -> VALID???

it seems that the calculation is just:

if(value != 10)
{
--value;
}
address_byte2 = (value << 4u) + 0x04;

but i did not understand what the meaning of the --value if value is != 10 means when every value except 1-8 produces wrong adresses?

the traced midi from the game did not contain wrong offsets - so i think the != 10 check is someway wrong - i have no clue

F0 41 10 16 12 03 00 04 18 61 F7
F0 41 10 16 12 03 00 14 00 69 F7
F0 41 10 16 12 03 00 14 00 69 F7
F0 41 10 16 12 03 00 14 00 69 F7
F0 41 10 16 12 03 00 14 00 69 F7
F0 41 10 16 12 03 00 24 00 59 F7
F0 41 10 16 12 03 00 44 02 37 F7
F0 41 10 16 12 03 00 44 02 37 F7
F0 41 10 16 12 03 00 44 02 37 F7
F0 41 10 16 12 03 00 54 02 27 F7
F0 41 10 16 12 03 00 54 70 39 F7
F0 41 10 16 12 03 00 74 02 07 F7
F0 41 10 16 12 03 00 74 02 07 F7

and it seems that value is the part-nr but the same value is also the midi-channel in a function that is directly called before - is part == midi-channel???

### Reply 1 of 11, by sergm

Rank Oldbie
Rank
Oldbie

I don't think this is wrong. Let us look at the address mapping of the patch temp area:

Whole Part (accessible on unit#)
------------- ----------------------------
03 00 00 Patch Temp Area (part 1)
03 00 10 Patch Temp Area (part 2)
:
03 00 60 Patch Temp Area (part 7)
03 00 70 Patch Temp Area (part 8)
03 01 10 Set up Temp Area (rhythm part)

We can see a gap at address 03 01 00, it corresponds to the part number 9. In fact, LA-only devices feature 8 parts only plus the rhythm part.

Note, the mapping between MIDI channels and actual parts is flexible. By default, these devices are silent when you send MIDI messages to MIDI channels 1, 11-16. But the game may freely reprogram the mapping, so that 1 MIDI channel may be assigned any, 0 or several parts. This is specified by data stored at address of System Area (10 00 00) offsets 0DH-15H.

Is it clearer now? 😀

Last edited by sergm on 2021-07-11, 08:03. Edited 1 time in total.

### Reply 3 of 11, by llm

Rank Member
Rank
Member
sergm wrote on 2021-07-11, 07:43:
I don't think this is wrong. Let us look at the address mapping of the patch temp area: […]

I don't think this is wrong. Let us look at the address mapping of the patch temp area:

Whole Part (accessible on unit#)
------------- ----------------------------
03 00 00 Patch Temp Area (part 1)
03 00 10 Patch Temp Area (part 2)
:
03 00 60 Patch Temp Area (part 7)
03 00 70 Patch Temp Area (part 8)
03 01 10 Set up Temp Area (rhythm part)

We can see a gap at address 03 01 00, it corresponds to the part number 9. In fact, LA-only devices feature 8 parts only plus the rhythm part.

Note, the mapping between MIDI channels and actual parts is flexible. By default, these devices are silent when you send MIDI messages to MIDI channels 1, 11-16. But the game may freely reprogram the mapping, so that 1 MIDI channel may be assigned any, 0 or several parts. This is specified by data stored at address of System Area (10 00 00) offsets 0DH-15H.

Is it clearer now? 😀

but address byte1 is always 0
so its
0x?4 - address byte2, only the high nibble of this address byte is changed, low nibble is always 4

### Reply 5 of 11, by sergm

Rank Oldbie
Rank
Oldbie
llm wrote on 2021-07-11, 09:11:
but address byte1 is always 0 so its 0x03 - base address 0x00 - address byte1 0x?4 - address byte2, only the high nibble of […]

but address byte1 is always 0
so its
0x?4 - address byte2, only the high nibble of this address byte is changed, low nibble is always 4

I still see no problems with that, assuming the parts are mapped to MIDI channels 2-9, and the game does not send bogus messages to unmapped channels. The data sent indicates that the game changes the bender range for parts 1-8, which makes perfect sense.

### Reply 6 of 11, by llm

Rank Member
Rank
Member

so these are the address offset (as 7bit) for part 1-9

[0] part: 1, offset: 0x0000, high7: 0x00, low7: 0x00 [1] part: 2, offset: 0x0010, high7: 0x00, low7: 0x10 [2] part: 3, offset: 0 […]

[0] part: 1, offset: 0x0000, high7: 0x00, low7: 0x00
[1] part: 2, offset: 0x0010, high7: 0x00, low7: 0x10
[2] part: 3, offset: 0x0020, high7: 0x00, low7: 0x20
[3] part: 4, offset: 0x0030, high7: 0x00, low7: 0x30
[4] part: 5, offset: 0x0040, high7: 0x00, low7: 0x40
[5] part: 6, offset: 0x0050, high7: 0x00, low7: 0x50
[6] part: 7, offset: 0x0060, high7: 0x00, low7: 0x60
[7] part: 8, offset: 0x0070, high7: 0x00, low7: 0x70
[8] part: 9, offset: 0x0080, high7: 0x01, low7: 0x00

but part 9 can't be addressed with only the low7 of the address (due to the always hardcoded 0x00 in high7 of the game drv code)

and is the mapping thing something that the game needs to configure activly on the MT32?
because my midi traces do not container any system area settings msg - except volume changes

i can still get no clue out of the if ( value != 10 ){ --value; }
because every value outside of 1-8 will produce an invalid offset address - if there is no of your described mapping is active

could be that the game only sends 1-8 values and the if != 10 always hits - could be old code?

### Reply 7 of 11, by sergm

Rank Oldbie
Rank
Oldbie

I don't know for sure, as I didn't bother looking at the full disasm code. But again, the magic number "10" here would suggest that the code attempts to deal with the rhythm channel specially. As we both noted, part 9 does not exist, but there is a rhythm part that is specially addressed. And in case of the rhythm part, the middle byte cannot be 0, so a bug might be there.

Also, I'm yet to figure the meaning of the "value" that is put to register AL. If this is a zero-based MIDI channel number, then "10" is indeed a bogus constant, because the MIDI channel 10 would be encoded as 09h.

As for the channel mapping, we may think that the default applies. Normally, MIDI channels 2-9 are mapped to parts 1-8, and the rhythm channel 10 is mapped to the rhythm part, obviously.

### Reply 8 of 11, by llm

Rank Member
Rank
Member

i've further analysed the calling place for the functions - there is another function call right before that function which uses the very same value as as midi-channel (i think)

its a "Control change" msg 0xB0

send_midi_msg3(0xB0u + BITS4(value), 7, BITS7(controller_value_)); ... if(value != 10) { --value; } address_byte2 = (value << […]

send_midi_msg3(0xB0u + BITS4(value), 7, BITS7(controller_value_));
...
if(value != 10)
{
--value;
}
address_byte2 = (value << 4u) + 0x04;

and think these msg from the midi traces are the output

B2 07 7F B3 07 4E B3 07 7F B5 07 58 B5 07 7F B6 07 11 B6 07 7F B9 07 6F B9 07 77 B9 07 7F […]

B2 07 7F
B3 07 4E
B3 07 7F
B5 07 58
B5 07 7F
B6 07 11
B6 07 7F
B9 07 6F
B9 07 77
B9 07 7F

### Reply 9 of 11, by llm

Rank Member
Rank
Member

in order trace of the midi-output - some of the mgs are based on the two functions i've talked about

[code]  PlayMsg cmd_buf: B9 07 7F  <-- func0 with 0xB0 Base
PlayMsg cmd_buf: C9 00
PlaySysex: F0 41 10 16 12 03 00 F7 <-- other functions that produces illformed msg
PlayMsg cmd_buf: 84 00 79
PlayMsg cmd_buf: B9 07 77 <-- func0 with 0xB0 Base
PlayMsg cmd_buf: B9 0A 00
PlayMsg cmd_buf: B9 07 6F <-- func0 with 0xB0 Base
PlayMsg cmd_buf: 99 24 7F
PlayMsg cmd_buf: B9 07 7F <-- func0 with 0xB0 Base
PlayMsg cmd_buf: C9 00
PlaySysex: F0 41 10 16 12 03 00 F7 <-- other functions that produces illformed msg
PlayMsg cmd_buf: 84 00 79
PlayMsg cmd_buf: B9 07 77 <-- func0 with 0xB0 Base
PlayMsg cmd_buf: B9 0A 00
PlayMsg cmd_buf: 99 31 7F
PlayMsg cmd_buf: B2 07 7F <-- func0 with 0xB0 Base
PlayMsg cmd_buf: C2 1E
PlaySysex: F0 41 10 16 12 03 00 14 00 69 F7 <-- func1 (with the != 10)
PlayMsg cmd_buf: B2 07 7F <-- func0 with 0xB0 Base
PlayMsg cmd_buf: B2 0A 46
PlayMsg cmd_buf: B2 07 7F <-- func0 with 0xB0 Base
PlayMsg cmd_buf: 92 3D 7F
PlayMsg rt_buf: FF 00 00 00 00 00 00 00
PlayMsg cmd_buf: 92 7B 79
PlayMsg cmd_buf: B5 07 7F <-- func0 with 0xB0 Base
PlayMsg cmd_buf: C5 18
PlaySysex: F0 41 10 16 12 03 00 44 02 37 F7 <-- func1 (with the != 10)
PlayMsg cmd_buf: B5 07 58
PlayMsg cmd_buf: B5 0A 28
PlayMsg cmd_buf: B3 07 7F <-- func0 with 0xB0 Base
PlayMsg cmd_buf: C3 5C
PlaySysex: F0 41 10 16 12 03 00 24 00 59 F7
PlayMsg cmd_buf: B3 07 4E <-- func0 with 0xB0 Base
PlayMsg cmd_buf: B3 0A 22
PlayMsg cmd_buf: B6 07 7F <-- func0 with 0xB0 Base
PlaySysex: F0 41 10 16 12 03 00 54 70 39 F7 <-- func1 (with the != 10)
PlayMsg cmd_buf: B6 07 10 <-- func0 with 0xB0

[/code] Base

### Reply 10 of 11, by sergm

Rank Oldbie
Rank
Oldbie

Yes, the pattern

PlayMsg cmd_buf: B6 07 7F  <-- func0 with 0xB0 Base
PlaySysex: F0 41 10 16 12 03 00 54 70 39 F7 <-- func1 (with the != 10)

looks very much like

--value

is nothing more than an adjustment for the standard MIDI channel mapping in MT-32

MIDI channel 2-9 -> part 1-8

Since we use 0-based channel and part indices, the

B6

refers to MIDI channel 7, which is mapped to MT-32 part 6, that in turn has index 5, so the bender range address is

03 00 54

But in this case you are right, the condition is buggy. It should compare value with 9 - the index of the percussion MIDI channel 10 that needs special handling. Perhaps, there are no messages that would adjust the bender range on channel 10, I think. Otherwise, it'd be very audible 😀