I'm trying to know the protocol used for that device. LCD is : 248*60 Binary Size is 512Kb.
The CPU that is used : IC10, UPD70236: V53A is an NEC branch of the Intel 8086 (via their famous V20, V30, etc. series), with various extensions including system peripherals (like DMA), expanded memory (for a total 24-bit memory space), and faster clock and instruction rates.
I'm looking for the functions that are responsible for LCD Write Text, and how are the pins are toggled, specifically, the LCD commands that are sent.
ROM can be find here: https://www.mediafire.com/file/bdepkvyz7dsr9pj/MPC2KXL.BIN/file
Also in the website has everything, including schematic:
http://zine.r-massive.com/akai-mpc-2000xl-archive/
Here is the LCD Socket with the signals:
TL;DR:
Re-engineering leads to these insights, reduced to relevant details for a replacement.
The LCD has a width of 256 pixels and a height of 60 pixels.
There are two driver ICs of the same type. They use the addresses 0x60/0x62 and 0x100/0x102, respectively. The driver ICs receive the address reduced to the single bit A1. The chip select lines nCS1 and nCS2 are active mutually exclusive for each driver IC. The prefix "n" denotes the active-low nature of a line.
nRST | nCS1 | nCS2 | A1 | nIORD | nIOWR | Meaning |
---|---|---|---|---|---|---|
0 | x | x | x | x | x | Asynchronous reset |
1 | 1 | 1 | x | x | x | No access |
1 | 0 | 0 | x | x | x | Irregular, not generated by MCU |
1 | x | x | x | 1 | 1 | No access |
1 | x | x | x | 0 | 0 | Illegal, not generated by MCU |
1 | 0 | 1 | 0 | 0 | 1 | Read status of LCD1 |
1 | 0 | 1 | 1 | 0 | 1 | Read data of LCD1, not used by firmware |
1 | 0 | 1 | 0 | 1 | 0 | Write command of LCD1 |
1 | 0 | 1 | 1 | 1 | 0 | Read command of LCD1 |
1 | 1 | 0 | 0 | 0 | 1 | Read status of LCD2 |
1 | 1 | 0 | 1 | 0 | 1 | Read data of LCD2, not used by firmware |
1 | 1 | 0 | 0 | 1 | 0 | Write command of LCD2 |
1 | 1 | 0 | 1 | 1 | 0 | Read command of LCD2 |
Reading 0x60 and 0x100, respectively, returns status. Bit 7 of the status signals "busy" when set, other bits seem to signal reasons for being busy. A replacement of sufficing speed can simply return 0x00.
Writing 0x60 and 0x100, respectively, sets a command. Reducing the effort to the minimum, only commands 0x23 to 0x20 are relevant for a replacement specific for the use case:
Writing 0x62 and 0x102, respectively, adds one or more parameter bytes after a command:
Details
A web recherche for the LCD revealed no usable results, so I went the hard way to re-engineer the firmware.
This image of the LCD was posted on the OP's thread on a forum, it shows that the LCD has two most probably equal driver ICs. Unfortunately there is no IC marking.
The following excerpts of the schematic (nicely linked by Clifford to a source without paywall) show relevant details:
The schematic details reveal the IO addresses.
The two chip select lines back up the theory that there are two driver ICs. Such configuration is often used in a main-sub relation, where the main driver synchronizes the sub driver. This way LCDs can be driven that are larger than a single driver can control.
The address line A1 suggests that each driver IC has the common organization of a command address and a data address. Later findings support that idea.
The V53 CPU has its reset vector at 0xFFFF:0x0000. The linked binary has a size of 512 kiB = 0x80000, which needs an offset of 0x80000 to provide its end at that location.
Using Ghidra I set up the loaded binary with said offset and added a RAM of same size at offset 0. Then I searched for words of 0x0060, 0x0062, 0x0100, and 0x0102. The findings look quite promising. Note: the following edited excerpts already show edited function definitions.
The following are clearly the base functions to access the LCD.
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near outLcd1Control(uint8_t value)
void <VOID> <RETURN>
uint8_t AL:1 value
9000:ab9b ba 60 00 MOV DX,0x60
9000:ab9e ee OUT DX,value
9000:ab9f c3 RET
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near outLcd1Data(uint8_t value)
void <VOID> <RETURN>
uint8_t AL:1 value
9000:aba0 ba 62 00 MOV DX,0x62
9000:aba3 ee OUT DX,value
9000:aba4 c3 RET
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near waitForLcd1Ready(void)
void <VOID> <RETURN>
uint16_t CX:2 iVar2
9000:aba5 50 PUSH AX
9000:aba6 51 PUSH CX
9000:aba7 b9 ff ff MOV iVar2,0xffff
LAB_9000_abaa
9000:abaa ba 60 00 MOV DX,0x60
9000:abad ec IN AL,DX
9000:abae a8 80 TEST AL,0x80
9000:abb0 74 02 JZ LAB_9000_abb4
9000:abb2 e2 f6 LOOP LAB_9000_abaa
LAB_9000_abb4
9000:abb4 59 POP iVar2
9000:abb5 58 POP AX
9000:abb6 c3 RET
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near outLcd2Control(uint8_t value)
void <VOID> <RETURN>
uint8_t AL:1 value
9000:abb7 ba 00 01 MOV DX,0x100
9000:abba ee OUT DX,value
9000:abbb c3 RET
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near outLcd2Data(uint8_t value)
void <VOID> <RETURN>
uint8_t AL:1 value
9000:abbc ba 02 01 MOV DX,0x102
9000:abbf ee OUT DX,value
9000:abc0 c3 RET
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near waitForLcd2Ready(void)
void <VOID> <RETURN>
uint16_t CX:2 iVar2
9000:abc1 50 PUSH AX
9000:abc2 51 PUSH CX
9000:abc3 b9 ff ff MOV iVar2,0xffff
LAB_9000_abc6
9000:abc6 ba 00 01 MOV DX,0x100
9000:abc9 ec IN AL,DX
9000:abca a8 80 TEST AL,0x80
9000:abcc 74 02 JZ LAB_9000_abd0
9000:abce e2 f6 LOOP LAB_9000_abc6
LAB_9000_abd0
9000:abd0 59 POP iVar2
9000:abd1 58 POP AX
9000:abd2 c3 RET
One important finding is that reading 0x60 and 0x100, respectively, returns status. Bit 7 of the status apparently signals "busy" when set. A counter avoids an endless loop in case of any error.
After disassembling more of the neighborhood, more interesting functions show up.
The following looks like the initializing of the LCD.
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near initializeLcd(void)
void <VOID> <RETURN>
9000:aa74 b0 23 MOV AL,0x23
9000:aa76 e8 22 01 CALL outLcd1Control
9000:aa79 b0 85 MOV AL,0x85
9000:aa7b e8 27 01 CALL waitForLcd1Ready
9000:aa7e e8 1f 01 CALL outLcd1Data
9000:aa81 b0 24 MOV AL,0x24
9000:aa83 e8 15 01 CALL outLcd1Control
9000:aa86 b0 01 MOV AL,0x1
9000:aa88 e8 1a 01 CALL waitForLcd1Ready
9000:aa8b e8 12 01 CALL outLcd1Data
9000:aa8e b0 23 MOV AL,0x23
9000:aa90 e8 24 01 CALL outLcd2Control
9000:aa93 b0 8d MOV AL,0x8d
9000:aa95 e8 29 01 CALL waitForLcd2Ready
9000:aa98 e8 21 01 CALL outLcd2Data
9000:aa9b b0 24 MOV AL,0x24
9000:aa9d e8 17 01 CALL outLcd2Control
9000:aaa0 b0 01 MOV AL,0x1
9000:aaa2 e8 1c 01 CALL waitForLcd2Ready
9000:aaa5 e8 14 01 CALL outLcd2Data
9000:aaa8 e8 28 01 CALL delayXxxUs
9000:aaab b3 00 MOV BL,0x0
LAB_9000_aaad
9000:aaad b0 22 MOV AL,0x22
9000:aaaf e8 e9 00 CALL outLcd1Control
9000:aab2 8a c3 MOV AL,BL
9000:aab4 e8 ee 00 CALL waitForLcd1Ready
9000:aab7 e8 e6 00 CALL outLcd1Data
9000:aaba b0 21 MOV AL,0x21
9000:aabc e8 dc 00 CALL outLcd1Control
9000:aabf b0 00 MOV AL,0x0
9000:aac1 e8 e1 00 CALL waitForLcd1Ready
9000:aac4 e8 d9 00 CALL outLcd1Data
9000:aac7 b0 20 MOV AL,0x20
9000:aac9 e8 cf 00 CALL outLcd1Control
9000:aacc b9 14 00 MOV CX,0x14
LAB_9000_aacf
9000:aacf b0 00 MOV AL,0x0
9000:aad1 e8 d1 00 CALL waitForLcd1Ready
9000:aad4 e8 c9 00 CALL outLcd1Data
9000:aad7 e2 f6 LOOP LAB_9000_aacf
9000:aad9 fe c3 INC BL
9000:aadb 80 fb 41 CMP BL,0x41
9000:aade 75 cd JNZ LAB_9000_aaad
9000:aae0 e8 f0 00 CALL delayXxxUs
9000:aae3 b3 00 MOV BL,0x0
LAB_9000_aae5
9000:aae5 b0 22 MOV AL,0x22
9000:aae7 e8 cd 00 CALL outLcd2Control
9000:aaea 8a c3 MOV AL,BL
9000:aaec e8 d2 00 CALL waitForLcd2Ready
9000:aaef e8 ca 00 CALL outLcd2Data
9000:aaf2 b0 21 MOV AL,0x21
9000:aaf4 e8 c0 00 CALL outLcd2Control
9000:aaf7 b0 00 MOV AL,0x0
9000:aaf9 e8 c5 00 CALL waitForLcd2Ready
9000:aafc e8 bd 00 CALL outLcd2Data
9000:aaff b0 20 MOV AL,0x20
9000:ab01 e8 b3 00 CALL outLcd2Control
9000:ab04 b9 14 00 MOV CX,0x14
LAB_9000_ab07
9000:ab07 b0 00 MOV AL,0x0
9000:ab09 e8 b5 00 CALL waitForLcd2Ready
9000:ab0c e8 ad 00 CALL outLcd2Data
9000:ab0f e2 f6 LOOP LAB_9000_ab07
9000:ab11 fe c3 INC BL
9000:ab13 80 fb 41 CMP BL,0x41
9000:ab16 75 cd JNZ LAB_9000_aae5
9000:ab18 e8 b8 00 CALL delayXxxUs
9000:ab1b b9 00 00 MOV CX,0x0
9000:ab1e b3 64 MOV BL,0x64
LAB_9000_ab20
9000:ab20 51 PUSH CX
9000:ab21 53 PUSH BX
9000:ab22 b0 22 MOV AL,0x22
9000:ab24 e8 90 00 CALL outLcd2Control
9000:ab27 8a c1 MOV AL,CL
9000:ab29 e8 95 00 CALL waitForLcd2Ready
9000:ab2c e8 8d 00 CALL outLcd2Data
9000:ab2f b0 21 MOV AL,0x21
9000:ab31 e8 83 00 CALL outLcd2Control
9000:ab34 8a c3 MOV AL,BL
9000:ab36 2a e4 SUB AH,AH
9000:ab38 b1 08 MOV CL,0x8
9000:ab3a f6 f1 DIV CL
9000:ab3c 8a dc MOV BL,AH
9000:ab3e 2a ff SUB BH,BH
9000:ab40 e8 7e 00 CALL waitForLcd2Ready
9000:ab43 e8 76 00 CALL outLcd2Data
9000:ab46 b0 20 MOV AL,0x20
9000:ab48 e8 6c 00 CALL outLcd2Control
9000:ab4b 8a 87 b7 75 MOV AL,byte ptr [BX + 0x75b7]
9000:ab4f e8 6f 00 CALL waitForLcd2Ready
9000:ab52 e8 67 00 CALL outLcd2Data
9000:ab55 5b POP BX
9000:ab56 59 POP CX
9000:ab57 fe c3 INC BL
9000:ab59 fe c1 INC CL
9000:ab5b 80 f9 3c CMP CL,0x3c
9000:ab5e 75 c0 JNZ LAB_9000_ab20
9000:ab60 b0 23 MOV AL,0x23
9000:ab62 e8 36 00 CALL outLcd1Control
9000:ab65 b0 05 MOV AL,0x5
9000:ab67 e8 3b 00 CALL waitForLcd1Ready
9000:ab6a e8 33 00 CALL outLcd1Data
9000:ab6d b0 24 MOV AL,0x24
9000:ab6f e8 29 00 CALL outLcd1Control
9000:ab72 b0 01 MOV AL,0x1
9000:ab74 e8 2e 00 CALL waitForLcd1Ready
9000:ab77 e8 26 00 CALL outLcd1Data
9000:ab7a b0 23 MOV AL,0x23
9000:ab7c e8 38 00 CALL outLcd2Control
9000:ab7f b0 2d MOV AL,0x2d
9000:ab81 e8 3d 00 CALL waitForLcd2Ready
9000:ab84 e8 35 00 CALL outLcd2Data
9000:ab87 b0 24 MOV AL,0x24
9000:ab89 e8 2b 00 CALL outLcd2Control
9000:ab8c b0 01 MOV AL,0x1
9000:ab8e e8 30 00 CALL waitForLcd2Ready
9000:ab91 e8 28 00 CALL outLcd2Data
9000:ab94 e8 3c 00 CALL delayXxxUs
9000:ab97 e8 92 01 CALL clearBuffer
9000:ab9a c3 RET
As a simplified C function it looks like this.
void initializeLcd(void) {
outLcd1Control(0x23);
waitForLcd1Ready();
outLcd1Data(0x85);
outLcd1Control(0x24);
waitForLcd1Ready();
outLcd1Data(1);
outLcd2Control(0x23);
waitForLcd2Ready();
outLcd2Data(0x8d);
outLcd2Control(0x24);
waitForLcd2Ready();
outLcd2Data(1);
delayXxxUs();
for (uint8_t y = 0; y != 65; y++) {
outLcd1Control(0x22);
waitForLcd1Ready();
outLcd1Data(y);
outLcd1Control(0x21);
waitForLcd1Ready();
outLcd1Data(0);
outLcd1Control(0x20);
for (int i = 0; i != 20; i++) {
waitForLcd1Ready();
outLcd1Data(0);
}
}
delayXxxUs();
for (uint8_t y = 0; y != 65; y++) {
outLcd2Control(0x22);
waitForLcd2Ready();
outLcd2Data(y);
outLcd2Control(0x21);
waitForLcd2Ready();
outLcd2Data(0);
outLcd2Control(0x20);
for (int i = 0; i != 20; i++) {
waitForLcd2Ready();
outLcd2Data(0);
}
}
delayXxxUs();
uint8_t x = 100;
for (uint8_t y = 0; y != 60; y++) {
outLcd2Control(0x22);
waitForLcd2Ready();
outLcd2Data(y);
outLcd2Control(0x21);
waitForLcd2Ready();
outLcd2Data(x / 8);
outLcd2Control(0x20);
waitForLcd2Ready();
outLcd2Data(0x75b7[x % 8]);
x++;
}
outLcd1Control(0x23);
waitForLcd1Ready();
outLcd1Data(0x05);
outLcd1Control(0x24);
waitForLcd1Ready();
outLcd1Data(1);
outLcd2Control(0x23);
waitForLcd2Ready();
outLcd2Data(0x2d);
outLcd2Control(0x24);
waitForLcd2Ready();
outLcd2Data(1);
delayXxxUs();
clearBuffer();
}
This function sets multiple control values of unknown meaning. Some of them can be deduced from their parameters.
For example, command 0x23 has the parameter 0x85 and 0x8d, respectively, for LCD1 and LCD2. We can assume the bit 3 of the parameter selects main/sub. Later the command has the parameter 0x05 and 0x2d, respectively, for LCD1 and LCD2.
There is a function delayxxxUs()
to delay for an interval I did not calculate. It is not relevant for the analysis.
There is a function clearBuffer()
that clears a buffer in RAM. This is also not relevant for the LCD, as this function does not access the drivers.
After more searching, there is this huge part that apparently copies some buffers in different ways to the LCD. The specific differences are not relevant for the LCD. In the end, its drivers receive and store pixel data.
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near copy(void)
void <VOID> <RETURN>
9000:abdc 81 3e a2 CMP word ptr [0x75a2],0x6e22
75 22 6e
9000:abe2 74 0a JZ LAB_9000_abee
9000:abe4 80 3e 0e CMP byte ptr [0x760e],0x0
76 00
9000:abe9 75 03 JNZ LAB_9000_abee
9000:abeb e9 97 00 JMP copyAllMasked
LAB_9000_abee
9000:abee 8b 36 a2 75 MOV SI,word ptr [0x75a2]
9000:abf2 2b db SUB BX,BX
9000:abf4 b9 3c 00 MOV CX,0x3c
LAB_9000_abf7
9000:abf7 e8 08 00 CALL copyUnmasked
9000:abfa fe c3 INC BL
9000:abfc 80 fb 20 CMP BL,0x20
9000:abff 75 f6 JNZ LAB_9000_abf7
9000:ac01 c3 RET
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near copyUnmasked(uint8_t *buffer, uint8_t x, uint16_t height)
void <VOID> <RETURN>
uint8_t * SI:2 buffer
uint8_t BL:1 x
uint16_t CX:2 height
9000:ac02 80 fb 14 CMP x,0x14
9000:ac05 73 3e JNC LAB_9000_ac45
9000:ac07 53 PUSH x
9000:ac08 51 PUSH height
9000:ac09 b0 22 MOV AL,0x22
9000:ac0b e8 8d ff CALL outLcd1Control
9000:ac0e b0 00 MOV AL,0x0
9000:ac10 e8 92 ff CALL waitForLcd1Ready
9000:ac13 e8 8a ff CALL outLcd1Data
9000:ac16 b0 21 MOV AL,0x21
9000:ac18 e8 80 ff CALL outLcd1Control
9000:ac1b 8a c3 MOV AL,x
9000:ac1d e8 85 ff CALL waitForLcd1Ready
9000:ac20 e8 7d ff CALL outLcd1Data
9000:ac23 b0 20 MOV AL,0x20
9000:ac25 e8 73 ff CALL outLcd1Control
9000:ac28 2a ff SUB BH,BH
LAB_9000_ac2a
9000:ac2a 8a 20 MOV AH,byte ptr [x + buffer]
9000:ac2c ba 60 00 MOV DX,0x60
9000:ac2f ec IN AL,DX
9000:ac30 d0 e0 SHL AL,0x1
9000:ac32 74 03 JZ LAB_9000_ac37
9000:ac34 e8 6e ff CALL waitForLcd1Ready
LAB_9000_ac37
9000:ac37 8a c4 MOV AL,AH
9000:ac39 ba 62 00 MOV DX,0x62
9000:ac3c ee OUT DX,AL
9000:ac3d 83 c3 20 ADD x,0x20
9000:ac40 e2 e8 LOOP LAB_9000_ac2a
9000:ac42 59 POP height
9000:ac43 5b POP x
9000:ac44 c3 RET
LAB_9000_ac45
9000:ac45 53 PUSH BX
9000:ac46 51 PUSH CX
9000:ac47 b0 22 MOV AL,0x22
9000:ac49 e8 6b ff CALL outLcd2Control
9000:ac4c b0 00 MOV AL,0x0
9000:ac4e e8 70 ff CALL waitForLcd2Ready
9000:ac51 e8 68 ff CALL outLcd2Data
9000:ac54 b0 21 MOV AL,0x21
9000:ac56 e8 5e ff CALL outLcd2Control
9000:ac59 8a c3 MOV AL,BL
9000:ac5b 2c 14 SUB AL,0x14
9000:ac5d e8 61 ff CALL waitForLcd2Ready
9000:ac60 e8 59 ff CALL outLcd2Data
9000:ac63 b0 20 MOV AL,0x20
9000:ac65 e8 4f ff CALL outLcd2Control
9000:ac68 2a ff SUB BH,BH
LAB_9000_ac6a
9000:ac6a 8a 20 MOV AH,byte ptr [BX + SI]
9000:ac6c ba 00 01 MOV DX,0x100
9000:ac6f ec IN AL,DX
9000:ac70 d0 e0 SHL AL,0x1
9000:ac72 74 03 JZ LAB_9000_ac77
9000:ac74 e8 4a ff CALL waitForLcd2Ready
LAB_9000_ac77
9000:ac77 8a c4 MOV AL,AH
9000:ac79 ba 02 01 MOV DX,0x102
9000:ac7c ee OUT DX,AL
9000:ac7d 83 c3 20 ADD BX,0x20
9000:ac80 e2 e8 LOOP LAB_9000_ac6a
9000:ac82 59 POP CX
9000:ac83 5b POP BX
9000:ac84 c3 RET
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near copyAllMasked(void)
void <VOID> <RETURN>
9000:ac85 2b db SUB BX,BX
9000:ac87 b9 3c 00 MOV CX,0x3c
LAB_9000_ac8a
9000:ac8a e8 08 00 CALL copyMasked
9000:ac8d fe c3 INC BL
9000:ac8f 80 fb 20 CMP BL,0x20
9000:ac92 75 f6 JNZ LAB_9000_ac8a
9000:ac94 c3 RET
**************************************************************
* FUNCTION *
**************************************************************
void __cdecl16near copyMasked(uint8_t x, uint16_t height)
void <VOID> <RETURN>
uint8_t BL:1 x
uint16_t CX:2 height
9000:ac95 80 fb 14 CMP x,0x14
9000:ac98 73 48 JNC LAB_9000_ace2
9000:ac9a 53 PUSH x
9000:ac9b 51 PUSH height
9000:ac9c b0 22 MOV AL,0x22
9000:ac9e e8 fa fe CALL outLcd1Control
9000:aca1 b0 00 MOV AL,0x0
9000:aca3 e8 ff fe CALL waitForLcd1Ready
9000:aca6 e8 f7 fe CALL outLcd1Data
9000:aca9 b0 21 MOV AL,0x21
9000:acab e8 ed fe CALL outLcd1Control
9000:acae 8a c3 MOV AL,x
9000:acb0 e8 f2 fe CALL waitForLcd1Ready
9000:acb3 e8 ea fe CALL outLcd1Data
9000:acb6 b0 20 MOV AL,0x20
9000:acb8 e8 e0 fe CALL outLcd1Control
9000:acbb 2a ff SUB BH,BH
LAB_9000_acbd
9000:acbd 8a a7 a2 57 MOV AH,byte ptr [x + 0x57a2]
9000:acc1 0a a7 22 5f OR AH,byte ptr [x + 0x5f22]
9000:acc5 32 a7 a2 66 XOR AH,byte ptr [x + 0x66a2]
9000:acc9 ba 60 00 MOV DX,0x60
9000:accc ec IN AL,DX
9000:accd d0 e0 SHL AL,0x1
9000:accf 74 03 JZ LAB_9000_acd4
9000:acd1 e8 d1 fe CALL waitForLcd1Ready
LAB_9000_acd4
9000:acd4 8a c4 MOV AL,AH
9000:acd6 ba 62 00 MOV DX,0x62
9000:acd9 ee OUT DX,AL
9000:acda 83 c3 20 ADD x,0x20
9000:acdd e2 de LOOP LAB_9000_acbd
9000:acdf 59 POP height
9000:ace0 5b POP x
9000:ace1 c3 RET
LAB_9000_ace2
9000:ace2 53 PUSH x
9000:ace3 51 PUSH height
9000:ace4 b0 22 MOV AL,0x22
9000:ace6 e8 ce fe CALL outLcd2Control
9000:ace9 b0 00 MOV AL,0x0
9000:aceb e8 d3 fe CALL waitForLcd2Ready
9000:acee e8 cb fe CALL outLcd2Data
9000:acf1 b0 21 MOV AL,0x21
9000:acf3 e8 c1 fe CALL outLcd2Control
9000:acf6 8a c3 MOV AL,x
9000:acf8 2c 14 SUB AL,0x14
9000:acfa e8 c4 fe CALL waitForLcd2Ready
9000:acfd e8 bc fe CALL outLcd2Data
9000:ad00 b0 20 MOV AL,0x20
9000:ad02 e8 b2 fe CALL outLcd2Control
9000:ad05 2a ff SUB BH,BH
LAB_9000_ad07
9000:ad07 8a a7 a2 57 MOV AH,byte ptr [x + 0x57a2]
9000:ad0b 0a a7 22 5f OR AH,byte ptr [x + 0x5f22]
9000:ad0f 32 a7 a2 66 XOR AH,byte ptr [x + 0x66a2]
9000:ad13 ba 00 01 MOV DX,0x100
9000:ad16 ec IN AL,DX
9000:ad17 d0 e0 SHL AL,0x1
9000:ad19 74 03 JZ LAB_9000_ad1e
9000:ad1b e8 a3 fe CALL waitForLcd2Ready
LAB_9000_ad1e
9000:ad1e 8a c4 MOV AL,AH
9000:ad20 ba 02 01 MOV DX,0x102
9000:ad23 ee OUT DX,AL
9000:ad24 83 c3 20 ADD x,0x20
9000:ad27 e2 de LOOP LAB_9000_ad07
9000:ad29 59 POP height
9000:ad2a 5b POP x
9000:ad2b c3 RET
Reduced to a simplified C function that shows just the transfer of a buffer to the LCD:
void copy(void) {
for (uint8_t x = 0; x != 32; x++) {
copyUnmasked(*(uint8_t **)0x75a2, x, 60);
}
}
void copyUnmasked(uint8_t *buffer, uint8_t x, uint16_t height) {
if (x < 20) {
outLcd1Control(0x22);
waitForLcd1Ready();
outLcd1Data(0);
outLcd1Control(0x21);
waitForLcd1Ready();
outLcd1Data(x);
outLcd1Control(0x20);
do {
if ((in(0x60) & 0x7f) != 0) {
waitForLcd1Ready();
}
out(0x62, buffer[x]);
x += 32;
height--;
} while (height != 0);
} else {
outLcd2Control(0x22);
waitForLcd2Ready();
outLcd2Data(0);
outLcd2Control(0x21);
waitForLcd2Ready();
outLcd2Data(x - 20);
outLcd2Control(0x20);
do {
if ((in(0x100) & 0x7f) != 0) {
waitForLcd2Ready();
}
out(0x102, buffer[x]);
x += 32;
height--;
} while (height != 0);
}
}
The buffer is organized as 60 lines of 32 bytes.
There are two stacked loops: