Home Page
Posted By: Golden Child lx800 self test - 01/09/22 03:30 AM
I felt inspired to hack on the lx800 and was able to get it to do the self test. It's not perfect, there's still some stray junk, and also some strange vertical blind effects but it's fun to see it do something.

In many ways, it's very similar to the ap2000/lx810l, they're 9 pin printers that do double passes to get NLQ text. It's useful to take some code from the ap2000 and merge it into the lx800.

The lx800 uses a different gate array and things are wired up quite differently. (More gatorade...)

I got the buttons to work, but oddly, if you do a form feed it hangs once it kicks down a page.

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 01/09/22 09:33 PM
Most everything seems to sorta work now, but for some reason, once it goes past a single page it tends to freeze up/act strangely.

I hacked in the centronics and it does seem to work okay, except that I can't seem to shut off auto line feed for some reason.

(printing apple 2 hi-res screen)

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 01/10/22 03:43 AM
I finally figured out why it would hang after kicking out a page: It was in "Cut sheet feeder mode". I was able to take it out of this mode once I fixed the dip switches.

Dipswitch #2 is read on the upd7810's analog inputs, but for some reason they weren't working properly. Finally I realized that the existing driver specified that the anX_r functions were READ_LINE_MEMBERS which return a one bit value, not an 8 bit value. Once I realized this and converted it to a regular uint8_t, it worked fine.


// it was declared as a DECLARE_READ_LINE_MEMBER not a uint8_t
/* DECLARE_READ_LINE_MEMBER(an0_r);
DECLARE_READ_LINE_MEMBER(an1_r);
DECLARE_READ_LINE_MEMBER(an2_r);
DECLARE_READ_LINE_MEMBER(an3_r);
DECLARE_READ_LINE_MEMBER(an4_r);
DECLARE_READ_LINE_MEMBER(an5_r);
DECLARE_READ_LINE_MEMBER(an6_r);
DECLARE_READ_LINE_MEMBER(an7_r);
*/

uint8_t an0_r();
uint8_t an1_r();
uint8_t an2_r();
uint8_t an3_r();
uint8_t an4_r();
uint8_t an5_r();
uint8_t an6_r();
uint8_t an7_r();


So now it will happily print more than one page!

Unfortunately, that doesn't change the stray junk, still have to figure that out.


Interestingly, if you let the self test continue, it goes to a Cyrillic character set.

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 01/10/22 12:56 PM
It's still only printing in every other column, but print shop doesn't look too bad. Using the grappler plus card but the original grappler setting in print shop.

My plan is to calculate the printhead velocity by the stepper movement so I can hit the in between spots.

[Linked Image from i.imgur.com]


======================================================


"standard" drive table in steppers.cpp

Note how there's 3 consecutive vertical ones in each column.

2 0010
6 0110
4 0100
5 0101
1 0001
9 1001
8 1000
a 1010


============================

lx800 pf stepper: 2 6 4 5 1 9 8 a (matches the standard drive table)

However, it goes in the reverse direction so we do a bitswap, just reversing the bits.

void epson_lx800_device::pf_stepper(uint8_t data)
{
m_bitmap_printer->update_pf_stepper(bitswap<4>(data, 0, 1, 2, 3));
}


===============================

lx800 cr stepper: d 9 b a e 6 7 5

To get it to match the standard table, it has to be inverted. There are sequences of 3 vertical zeroes where we would expect to see 3 vertical ones.

Inverting, we just get the standard table:

d 1101 -> 2 0010
9 1001 -> 6 0110
b 1011 -> 4 0100
a 1010 -> 5 0101
e 1110 -> 1 0001
6 0110 -> 9 1001
7 0111 -> 8 1000
5 0101 -> a 1010

void epson_lx800_device::cr_stepper(uint8_t data)
{
m_bitmap_printer->update_cr_stepper(data ^ 0xf);
}
Posted By: Golden Child Re: lx800 self test - 01/10/22 10:02 PM
Ok, I think I've got a reason why there's junk on the printout.

There's a 24 bit shift register at C000,C001,C002. But for some reason, C002 isn't getting set.

According to my understanding of the upd7810, the SKIP flag is supposed to skip the next instruction. However, it seems to be skipping two instructions.

So I've found a repeatable test, I boot the apple2e with grapplerplus and lx800. Then while it boots I type CTRL+F12 to reset the apple. Then type "PR#1:?CHR$(118):PR#0" and hit enter which prints a lowercase v.

I've set a watchpoint at "C000,3,w" to watch the shift register.

So it writes C000 and C001 in one step with the STEAX. But then things go awry when it hits 1FEF. It seems to skip the STAX(DE+). Note that DE doesn't get incremented and C002 doesn't get written.

[Linked Image from i.imgur.com]


[Linked Image from i.imgur.com]




edit: Ok I think I know what's happening:

This screenshot shows the opcodes:

[Linked Image from i.imgur.com]


If I interpret this table correctly:

LDAX (HL+) is 2d
STAX (DE+) is 3c


I think that LDAX(HL+) 2d is only a 1 byte opcode but the table says its a 2 byte opcode
and STAX (DE+) 3c

Code
        {&upd7810_device::LXI_D_w,       3,10,10,L0|L1}, /* 24: 0010 0100 llll llll hhhh hhhh            */
        {&upd7810_device::GTIW_wa_xx,    3,19,19,L0|L1}, /* 25: 0010 0101 oooo oooo xxxx xxxx            */
        {&upd7810_device::ADINC_A_xx,    2, 7, 7,L0|L1}, /* 26: 0010 0110 xxxx xxxx                      */
        {&upd7810_device::GTI_A_xx,      2, 7, 7,L0|L1}, /* 27: 0010 0111 xxxx xxxx                      */
        {&upd7810_device::illegal,       1, 4, 4,L0|L1}, /* 28:                                          */
        {&upd7810_device::LDAX_B,        2, 7, 7,L0|L1}, /* 29: 0010 1001 dddd dddd                      */
        {&upd7810_device::LDAX_D,        2, 7, 7,L0|L1}, /* 2a: 0010 1010 dddd dddd                      */
        {&upd7810_device::LDAX_H,        2, 7, 7,L0|L1}, /* 2b: 0010 1011 dddd dddd                      */
        {&upd7810_device::LDAX_Dp,       2, 7, 7,L0|L1}, /* 2c: 0010 1100 dddd dddd                      */
        {&upd7810_device::LDAX_Hp,       2, 7, 7,L0|L1}, /* 2d: 0010 1101 dddd dddd                      */
        {&upd7810_device::LDAX_Dm,       2, 7, 7,L0|L1}, /* 2e: 0010 1110 dddd dddd                      */
        {&upd7810_device::LDAX_Hm,       2, 7, 7,L0|L1}, /* 2f: 0010 1111 dddd dddd                      */

and I think STAX(DE+) 3c should be a single byte opcode as well?

Code
        {&upd7810_device::DCRW_wa,       2,16,16,L0|L1}, /* 30: 0011 0000 oooo oooo                      */
        {&upd7810_device::BLOCK,         1,13,13,L0|L1}, /* 31: 0011 0001                                */  /* 7810 */
        {&upd7810_device::INX_HL,        1, 7, 7,L0|L1}, /* 32: 0011 0010                                */
        {&upd7810_device::DCX_HL,        1, 7, 7,L0|L1}, /* 33: 0011 0011                                */
        {&upd7810_device::LXI_H_w,       3,10,10,   L1}, /* 34: 0011 0100 llll llll hhhh hhhh            */
        {&upd7810_device::LTIW_wa_xx,    3,19,19,L0|L1}, /* 35: 0011 0101 oooo oooo xxxx xxxx            */
        {&upd7810_device::SUINB_A_xx,    2, 7, 7,L0|L1}, /* 36: 0011 0110 xxxx xxxx                      */
        {&upd7810_device::LTI_A_xx,      2, 7, 7,L0|L1}, /* 37: 0011 0111 xxxx xxxx                      */
        {&upd7810_device::illegal,       1, 4, 4,L0|L1}, /* 38:                                          */
        {&upd7810_device::STAX_B,        2, 7, 7,L0|L1}, /* 39: 0011 1001 dddd dddd                      */
        {&upd7810_device::STAX_D,        2, 7, 7,L0|L1}, /* 3a: 0011 1010 dddd dddd                      */
        {&upd7810_device::STAX_H,        2, 7, 7,L0|L1}, /* 3b: 0011 1011 dddd dddd                      */
        {&upd7810_device::STAX_Dp,       2, 7, 7,L0|L1}, /* 3c: 0011 1100 dddd dddd                      */
        {&upd7810_device::STAX_Hp,       2, 7, 7,L0|L1}, /* 3d: 0011 1101 dddd dddd                      */
        {&upd7810_device::STAX_Dm,       2, 7, 7,L0|L1}, /* 3e: 0011 1110 dddd dddd                      */
        {&upd7810_device::STAX_Hm,       2, 7, 7,L0|L1}, /* 3f: 0011 1111 dddd dddd                      */
Posted By: Golden Child Re: lx800 self test - 01/11/22 01:42 AM
After fixing the table entries, the text doesn't have stray junk now:

[Linked Image from i.imgur.com]
Posted By: Lord Nightmare Re: lx800 self test - 01/11/22 09:58 PM
I've confirmed from the UPD78C10 manual that this is a legit bug in MAME's upd7810 core, the number of bytes for LDAX opcodes 29-2f and STAX opcodes 39-3f are wrong.
This might explain random bugs in other devices using the upd78(c)10 cpu core in MAME.
Posted By: Golden Child Re: lx800 self test - 01/12/22 05:13 PM
Since I'm on a roll, I decided to take a stab at the ex800, I've got it doing the self test:

[Linked Image from i.imgur.com]


Ohhh, careful reading of the EX-800 manual it says that it's a 9 pin printer. I thought it was a 24 pin printer. When I read the manual previously, I just saw the number "24".

If I can get this one working, it's supposed to have color support like the JX-80, which would be nice.


The EX printers combine all the well-known virtues of previous
Epson 9 pin printers with many features normally exclusive to costly
24-pin printers.

The Color Option Kit makes your EX a fully-fledged 7-color printer,
capable of using the many programs designed for the Epson JX-80
color printer. Even with the color unit, however, you can still use
inexpensive black ribbons when you don’t need color.


I think this ebay listing price is off by a few orders of magnitude. (and not even free shipping!)

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 01/17/22 03:27 PM
Trying a little experiment of modeling the stepper motor as "spring physics" where you try to converge on the current position of the stepper.

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 01/23/22 02:41 AM
Was able to get this one to self test a little bit:

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 01/23/22 12:46 PM
This one's a little tricky since it has a Print Timing Sensor (PTS) which is a slotted disk light sensor that will send pulses to INT1 of the upd7810.

It would move the carriage a little bit then start beeping until I made it send some pulses to the INT1 depending on carriage movement.

That works a little bit, but the self test will stop after 3 lines. I think it's erroring out because the pulses don't match the timing expected:


[Linked Image from i.imgur.com]

More experiments to see if that fixes it.
Posted By: Golden Child Re: lx800 self test - 01/24/22 06:34 AM
I've been working on a Star dot matrix printer that uses a upd7800 which is a upd7801 that's romless. So far I've got it to move the cr at startup and respond to the online button, but it doesn't seem to respond to the LF or FF buttons.

The 7801 core doesn't have SIO implemented so it hangs up on a SKIT FS loop.
Posted By: rfka01 Re: lx800 self test - 01/24/22 07:29 AM
@GC I have dumps and pictures from a Star NL-10, HP Deskjet 500, Nec Pinwriter P2+ and P6+, Commodore MPS 1270A and CTI CPA-80 printer. If you're interested, I can send them your way.
Posted By: Golden Child Re: lx800 self test - 01/24/22 05:26 PM
Yes, please. I'd be very interested. PM sent.
Posted By: Artiom Re: lx800 self test - 01/24/22 06:51 PM
@rfka01 @GC Pretty good collection. I'm also interested. Can you share with me too? Dumps I can offer are from Epson FX-85, IBM 3852-2 Color JetPrinter, Hyundai HDP-920, Apple ImageWriter II and some more models (I've listed here dot-matrix printers).
Posted By: rfka01 Re: lx800 self test - 01/24/22 06:59 PM
You can get the files on https://mega.nz/folder/vAJyERCb#fP5enY0Ra5nhO7E_wZILOQ

I'll add the necessary information and upload what's missing to the FTP as well as usual.

@Lord Nightmare dumped the ROMs from the DEC LA-50 a while ago, maybe he can chime in as well.

@Artiom your dumps are most welcome!
Posted By: Artiom Re: lx800 self test - 01/24/22 07:07 PM
@rfka01 Thank you very much. PM is sent and contains what I promised.
Posted By: Golden Child Re: lx800 self test - 01/24/22 09:14 PM
I think the Dec LA-50 is very similar to the C-itoh 8510 / NEC 8023, but I think it has bigger roms and different functionality.
(IIRC it's made by TEC (Tokyo Electric Co), which is now Toshiba TEC)



Oh, imagewriter II roms you say? Yes, please!!
Posted By: Artiom Re: lx800 self test - 01/25/22 06:38 AM
I'm not sure if I need to say that, but PM sent.
Posted By: Golden Child Re: lx800 self test - 01/25/22 10:47 AM
Hi Artiom, thanks for the PM!

I've been searching for the Imagewriter II for a long time, but unfortunately it's not a rom dump but a capture of data sent to the printer.

Hexdumping it shows a bunch of ESC codes.

00000000 1b 63 1b 48 31 35 38 34 1b 66 1b 54 33 36 0a 1b |.c.H1584.f.T36..|
00000010 3c 1b 46 30 30 30 30 1b 4b 31 1b 50 1b 56 30 30 |<.F0000.K1.P.V00|
00000020 38 30 00 1b 47 30 30 31 32 03 03 03 03 0f 0f 0f |80..G0012.......|


I wonder what it actually prints when sent to a printer?


But there's all kinds of other things to study, this should keep me busy for awhile.

Lots of other stuff I've been looking for...

Many thanks!
Posted By: Artiom Re: lx800 self test - 01/25/22 03:24 PM
Correct files of dump are sent. Once again, sorry for mistake.
Posted By: Golden Child Re: lx800 self test - 01/25/22 06:32 PM
No worries!

Thanks for the updated dump, Artiom.

I'm trying to make sense of it. I always thought that the CPU on the Imagewriter II was a upd7810, looking at the computerfacts schematic, since it seems to line up pretty closely to the pins of a upd7810. They label it as an "EC-A056" and it has a 64 pin QIP.

I run unidasm on it


./unidasm -arch upd7810 ImageWriter2_new.bin | less

and everything looks mostly sane, except for the illegal instructions.


Code

0000: ba           DI      
0001: ba           DI      
0002: 68 ff        MVI     V,$FF
0004: 48 ad        illegal 
0006: 4e 24        JRE     $002C
0008: ba           DI      
0009: b0           PUSH    VA
000a: b1           PUSH    BC
000b: b2           PUSH    DE
000c: b3           PUSH    HL
000d: 54 f2 25     JMP     $25F2
0010: ba           DI      
0011: b0           PUSH    VA
0012: b1           PUSH    BC
0013: b2           PUSH    DE
0014: b3           PUSH    HL
0015: 54 30 1b     JMP     $1B30
0018: ba           DI      
0019: b0           PUSH    VA
001a: b1           PUSH    BC
001b: b2           PUSH    DE
001c: b3           PUSH    HL
001d: 54 0a 1b     JMP     $1B0A
0020: ff           JR      $0020
0021: ff           JR      $0021
0022: ff           JR      $0022
0023: ff           JR      $0023
0024: ff           JR      $0024
0025: ff           JR      $0025
0026: ff           JR      $0026
0027: ff           JR      $0027
0028: ba           DI      
0029: 54 02 21     JMP     $2102
002c: 68 ff        MVI     V,$FF
002e: 69 00        MVI     A,$00
0030: 70 79 72 fe  MOV     ($FE72),A
0034: 34 00 fe     LXI     HL,$FE00
0037: 3d           STAX    (HL+)
0038: 32           INX     HL
0039: 3d           STAX    (HL+)
003a: 32           INX     HL
003b: 3b           STAX    (HL)
003c: 70 79 06 fe  MOV     ($FE06),A
0040: 4d d2        MOV     MA,A
0042: 69 20        MVI     A,$20
0044: 4d d4        MOV     MC,A
0046: 69 0f        MVI     A,$0F
0048: 4d d1        MOV     MCC,A
004a: 69 00        MVI     A,$00
004c: 4d d3        MOV     MB,A
004e: 64 02 c0     MVI     PC,$C0
0051: 69 08        MVI     A,$08
0053: 4d e5        illegal 
0055: 64 00 0c     MVI     PA,$0C
0058: 5f b2        BIT     7,VV:B2


Looking at the opcodes it says are illegal

Code
./unidasm -arch upd7810 ImageWriter2_new.bin | grep 'illegal'
0004: 48 ad        illegal 
0053: 4d e5        illegal 
00a6: 73           illegal 
01fd: 4c ce        illegal 
0202: 4c ce        illegal 
0273: 64 ce        illegal 
0632: 06           illegal 
06ea: 4c ce        illegal 
06ef: 4c ce        illegal 
06fa: 48 aa        illegal 
0750: 64 ce        illegal 
0ad6: 4c ce        illegal 
0adb: 4c ce        illegal 
0b63: 4c ce        illegal 
0b69: 48 aa        illegal 
0b6f: 4c ce        illegal 
0b74: 4c ce        illegal 
0b83: 4c ce        illegal 
0b8b: 48 aa        illegal 
0b91: 4c ce        illegal 
0b9b: 48 aa        illegal 
0f8a: 64 ca        illegal 
13de: 48 aa        illegal 
144d: 70 a8        illegal 
17ec: 06           illegal 
17ee: 06           illegal 
1c19: 06           illegal 
20f8: 06           illegal 
2102: 48 ad        illegal 
2133: 48 ad        illegal 
26d2: 4c 4f        illegal 
26fc: 28           illegal 
2706: 28           illegal 
271b: 4d 3d        illegal 
272d: 4c 4f        illegal 
28c6: 06           illegal 
28d6: 28           illegal 
2c43: 4d 2d        illegal 
2c5d: 06           illegal 
2ffd: 4c b8        illegal 
3dd9: 28           illegal 
3f71: 06           illegal 
3f96: 60 5f        illegal 


Found some things that look like stepper motor code:

Code
201d: 34 33 20     LXI     HL,$2033
2020: ac           LDAX    (HL+A)
2021: 1f           MOV     L,A
2022: 4c c0        MOV     A,PA
2024: 07 f0        ANI     A,$F0
2026: 60 9f        ORA     A,L
2028: 4d c0        MOV     PA,A
202a: 70 79 01 fe  MOV     ($FE01),A
202e: 70 79 73 fe  MOV     ($FE73),A
2032: b8           RET     
// stepper motor pattern here
2033: 0c           MOV     A,D
2034: 0d           MOV     A,E
2035: 09           MOV     A,EAL
2036: 0b           MOV     A,C
2037: 03           DCX     SP
2038: 07 06        ANI     A,$06
203a: 0e           MOV     A,H


206d: 34 7f 20     LXI     HL,$207F
2070: ac           LDAX    (HL+A)
2071: 1f           MOV     L,A
2072: 4c c1        MOV     A,PB
2074: 07 cf        ANI     A,$CF
2076: 60 9f        ORA     A,L
2078: 4d c1        MOV     PB,A
207a: 70 79 05 fe  MOV     ($FE05),A
207e: b8           RET     
// stepper motor pattern here
207f: 10           EXA     
2080: 30 20        DCRW    VV:20
2082: 00           NOP     


20e5: 34 f7 20     LXI     HL,$20F7
20e8: ac           LDAX    (HL+A)
20e9: 1f           MOV     L,A
20ea: 4c c1        MOV     A,PB
20ec: 07 f0        ANI     A,$F0
20ee: 60 9f        ORA     A,L
20f0: 4d c1        MOV     PB,A
20f2: 70 79 03 fe  MOV     ($FE03),A
20f6: b8           RET     
// stepper motor pattern here
20f7: 03           DCX     SP
20f8: 06           illegal 
20f9: 0c           MOV     A,D
20fa: 09           MOV     A,EAL


There's a lot of accesses to the $FExx range, I wonder if that goes to the gate array chip
Code
1b30: b4           PUSH    EA
1b31: 70 69 61 fe  MOV     A,($FE61)
1b35: 57 01        OFFI    A,$01
1b37: 70 79 6c fe  MOV     ($FE6C),A
1b3b: 57 02        OFFI    A,$02
1b3d: 70 79 6d fe  MOV     ($FE6D),A
1b41: 57 04        OFFI    A,$04
1b43: 70 79 6e fe  MOV     ($FE6E),A
1b47: 57 08        OFFI    A,$08
1b49: 70 79 6f fe  MOV     ($FE6F),A
1b4d: 70 79 07 aa  MOV     ($AA07),A
Posted By: Golden Child Re: lx800 self test - 01/25/22 11:06 PM
I wonder if it could be a upd7807?

Code
 ./unidasm -arch upd7807 ImageWriter2_new.bin | grep 'illegal'
005a: 5a e1        MOV     illegal,7,CY
00a0: 5d 27        SK      illegal,1
00a6: 73           illegal 
011e: 5d 61        SK      illegal,3
0125: 5d e1        SK      illegal,7
012b: 5f 41        MOV     CY,illegal,2
012d: 5a 66        MOV     illegal,3,CY
013e: 58 c6        SETB    illegal,6
0144: 58 a6        SETB    illegal,5
0146: 5d 66        SK      illegal,3
0164: 58 63        SETB    illegal,3
0173: 58 09        SETB    illegal,0
0179: 58 29        SETB    illegal,1
0184: 58 69        SETB    illegal,3
018d: 5b a6        CLR     illegal,5
018f: 58 c6        SETB    illegal,6
01a5: 5d c6        SK      illegal,6
01e0: 5f c6        MOV     CY,illegal,6
01e2: 5c 69        OR      CY,illegal,3
022d: 58 e3        SETB    illegal,7
0232: 50 e3        SKN     illegal,7
0271: 58 85        SETB    illegal,4
0276: 58 a5        SETB    illegal,5
0279: 5d 63        SK      illegal,3
027c: 58 03        SETB    illegal,0
027e: 58 43        SETB    illegal,2
0294: 5b 25        CLR     illegal,1
0296: 5b 05        CLR     illegal,0
0298: 5b 06        CLR     illegal,0

[Linked Image from i.imgur.com]
[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 01/26/22 05:00 AM
If I look at some of the code and interpret it as 7807 code, I think it would make sense

Code
002c: 68 ff        MVI     V,$FF
002e: 69 00        MVI     A,$00
0030: 70 79 72 fe  MOV     ($FE72),A
0034: 34 00 fe     LXI     HL,$FE00
0037: 3d           STAX    (HL+)
0038: 32           INX     HL
0039: 3d           STAX    (HL+)
003a: 32           INX     HL
003b: 3b           STAX    (HL)
003c: 70 79 06 fe  MOV     ($FE06),A
0040: 4d d2        MOV     MA,A
0042: 69 20        MVI     A,$20
0044: 4d d4        MOV     MC,A
0046: 69 0f        MVI     A,$0F
0048: 4d d1        MOV     MCC,A
004a: 69 00        MVI     A,$00
004c: 4d d3        MOV     MB,A
004e: 64 02 c0     MVI     PC,$C0
0051: 69 08        MVI     A,$08
0053: 4d e5        MOV     MT,A
0055: 64 00 0c     MVI     PA,$0C
0058: 5f b2        MOV     CY,PC,5      <<< get PC bit 5 and put into CY
005a: 5a e1        MOV     illegal,7,CY     <<< says that this is illegal but it would put CY into VV:01 bit 7
005c: 6b 07        MVI     C,$07

Looking at how one of the opcodes gets handled:

Code
/* 5d: 0101 1111 bbbb bbbb (7807 only) */
void upd7810_device::SK_bit()
{
        uint8_t imm;
        int val;

        RDOPARG( imm );

        switch( imm & 0x1f )
        {
                case 0x10:  /* PA */
                        val = RP( UPD7810_PORTA );
                        break;
                case 0x11:  /* PB */
                        val = RP( UPD7810_PORTB );
                        break;
                case 0x12:  /* PC */
                        val = RP( UPD7810_PORTC );
                        break;
                case 0x13:  /* PD */
                        val = RP( UPD7810_PORTD );
                        break;
                case 0x15:  /* PF */
                        val = RP( UPD7810_PORTF );
                        break;
                case 0x16:  /* MKH */
                        val = MKH;
                        break;
                case 0x17:  /* MKL */
                        val = MKL;
                        break;
                case 0x19:  /* SMH */
                        val = SMH;
                        break;
                case 0x1b:  /* EOM */
                        val = EOM;
                        break;
                case 0x1d:  /* TMM */
                        val = TMM;
                        break;
                case 0x1e:  /* PT */
                        val = RP( UPD7807_PORTT );
                        break;
                default:
                        logerror("uPD7810 '%s': illegal opcode %02x %02x at PC:%04x\n", tag(), OP, imm, PC);
                        val = 0;
                        break;
        }
        if (val & (1 << (imm >> 5)))
                PSW |= SK;
}


so if I understand this correctly, the upper 3 bits are the bit number and the lower 5 specify the location, where locations < 0x10 are first bytes in the working area addressed by VV and locations >= 0x10 are the special registers.

It appears that the anything less than 0x10 isn't currently handled by the code.


So 5a e1 above would be interpreted as e1 = 1110 0001 upper 3 bits = 111 lower = 00001

so it would refer to bit 7 of VV:01


and many of those aren't currently implemented yet.

Code
       {&upd7810_device::SKN_bit,       2,13,13,L0|L1}, /* 50: 0101 0000 bbbb bbbb                      */  /* 7807 */
        {&upd7810_device::DCR_A,         1, 4, 4,L0|L1}, /* 51: 0101 0001                                */
        {&upd7810_device::DCR_B,         1, 4, 4,L0|L1}, /* 52: 0101 0010                                */
        {&upd7810_device::DCR_C,         1, 4, 4,L0|L1}, /* 53: 0101 0011                                */
        {&upd7810_device::JMP_w,         3,10,10,L0|L1}, /* 54: 0101 0100 llll llll hhhh hhhh            */
        {&upd7810_device::OFFIW_wa_xx,   3,19,19,L0|L1}, /* 55: 0101 0101 oooo oooo xxxx xxxx            */
        {&upd7810_device::ACI_A_xx,      2, 7, 7,L0|L1}, /* 56: 0101 0110 xxxx xxxx                      */
        {&upd7810_device::OFFI_A_xx,     2, 7, 7,L0|L1}, /* 57: 0101 0111 xxxx xxxx                      */
        {&upd7810_device::SETB,          2,13,13,L0|L1}, /* 58: 0101 1000 bbbb bbbb                      */  /* 7807 */
        {&upd7810_device::illegal,       2, 8, 8,L0|L1}, /* 59: 0101 1001 bbbb bbbb                      */  /* 7807 */
        {&upd7810_device::illegal,       2, 8, 8,L0|L1}, /* 5a: 0101 1010 bbbb bbbb                      */  /* 7807 */
        {&upd7810_device::CLR,           2,13,13,L0|L1}, /* 5b: 0101 1011 bbbb bbbb                      */  /* 7807 */
        {&upd7810_device::illegal,       2, 8, 8,L0|L1}, /* 5c: 0101 1100 bbbb bbbb                      */  /* 7807 */
        {&upd7810_device::SK_bit,        2,10,10,L0|L1}, /* 5d: 0101 1101 bbbb bbbb                      */  /* 7807 */
        {&upd7810_device::illegal,       2, 8, 8,L0|L1}, /* 5e: 0101 1110 bbbb bbbb                      */  /* 7807 */
        {&upd7810_device::illegal,       2, 8, 8,L0|L1}, /* 5f: 0101 1111 bbbb bbbb                      */  /* 7807 */
Posted By: Lord Nightmare Re: lx800 self test - 01/26/22 04:31 PM
The uPD7807 core in MAME at least currently has the same bug with those opcodes mentioned earlier that the uPD7810 did, so be warned. The 'base, non-prefixed' opcode table for uPD7807 was (mostly) a copy-paste of the uPD7810 one so it inherited the same bug at some point.
Posted By: Golden Child Re: lx800 self test - 01/28/22 09:19 PM
Man, so many interesting things to look at. I keep falling asleep at my computer with my head down like I've been sleeping on a plane.


I came across a commodore mcs820 which is the same as an okimate20. The 8051 code looks reasonably sane, it has a couple of chips that do the heavy lifting, lots of reads and writes around $8000-$8010 or so. Making a driver file for it and running some code, it looks to be flashing an error led possibly.


I found a facit 4513 printer, so another neat thing to study. Looks like it uses an intel 8085.


Searching on the digital LA50, there's supposed to be a technical manual listed at manx:

LA50 Printer Technical Manual
Company: Digital Equipment Corporation
Part: EK-0LA50-TM-001
Date: 1983-03
Keywords:

Copies
Address: http://dundas-mac.caltech.edu/%7Edundas/retro/Printers/ek-ola50-tm-001.pdf
Site: John's Retro Computing Notes
Format: PDF
Size: 2835368 bytes (2.7 MiB)
MD5: cc67d0474977405a4a79f163b1224df5

but sadly, I think that the site has been gone for a long while, and wayback doesn't have it.
Posted By: Walker Re: lx800 self test - 01/29/22 05:02 PM
Is this one related to the missing file? At least it looks like there are cross references.

http://www.bitsavers.org/pdf/dec/dsa/hsc/EK-HSCMA-SV-002_HSC_Service_Manual_Dec89.pdf

or

https://www.manualslib.com/manual/954937/Digital-Equipment-La50.html
Posted By: Golden Child Re: lx800 self test - 01/29/22 06:08 PM
Thanks for the links, Walker.

The HSC manual is for the DEC Hierarchical Storage Controllers and just mentions the LA50.

The other one is a user's manual which does give some helpful information like DIP switch settings.



Looking at this DEC stuff reminds me of the days of VMS and its file system that kept 3 different versions of a file. I think I used that capability a few times 8-)

An interesting tidbit from the dip switch settings, the LA50 will support a baud rate of 110. Why is 110 interesting? It's the rate of the ASR33 teletype, which is something I've been reading up on. A teletype terminal that is purely electromechanical and prints at 10 characters per second at 110 baud.

1 start bit + 7 data bits + 1 parity bit + 2 stop bits = 11 bits per character for 10 characters per second.
Posted By: Al Kossow Re: lx800 self test - 01/29/22 08:45 PM
I have the Dundas LA50 manuals. ULed to bitsavers.
I'll also see about scanning the schematic
..no go. partial document with no schematic
Posted By: Golden Child Re: lx800 self test - 01/29/22 09:27 PM
Cool! Thanks, Al!

Lots of good details in the Tech Manual.
Posted By: Golden Child Re: lx800 self test - 02/04/22 03:28 PM
Was able to get the self test for the Star Gemini kinda working. It prints one line, then the carriage keeps moving to the left for some reason, ignoring the home sensor...

It uses the upd7800 and had to hack the SIO instruction for now, just had it set the INTFST flag in the IRR, since it was hanging up on SIO, then a SKIT FS loop.

void upd7810_device::SIO()
{
logerror("unimplemented instruction: SIO setting IRR\n");
IRR |= INTFST;
}

[Linked Image from i.imgur.com]


Had a heck of a time with the ioports, primarily because I did some copypasting from different files and didn't realize that these unused bits would cause a problem.

Code
	PORT_START("ONLINE")
	PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ON LINE")   PORT_CODE(KEYCODE_0_PAD) 

	PORT_START("FORMFEED")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FORM FEED") PORT_CODE(KEYCODE_7_PAD)

	PORT_START("LINEFEED")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LINE FEED") PORT_CODE(KEYCODE_9_PAD)

The line PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED) would cause a read to return 0xFE or 0xFF, not just 0x0 or 0x1.

So this code would keep returning 0xFF or 0xFE and the line feed and form feed keys wouldn't respond.

Code
	if (offset==0)
	{
		retval = (ioport("ONLINE")->read()  << 0) |
				(ioport("LINEFEED")->read() << 1) |
				(ioport("FORMFEED")->read() << 2) |
				((ioport("DIPSW2")->read() & 0x1) << 3) |
				((ioport("DIPSW1")->read() & 0xf) << 4);
		retval = retval ^ 0xf8;  // invert dip switches for active low
	}

Removing that line made it work as expected.
Posted By: Golden Child Re: lx800 self test - 02/05/22 05:56 PM
Got the color ribbon working on the ex800 self test:

There's a stepper motor for the color ribbon positioner that moves the ribbon up and down along with a switch to tell you when you're at the home position.

The stepper position goes from 0 to 70 to 142 to 216 so just divide by 70 to get an index for the ribbon color.


[Linked Image from i.imgur.com]

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 02/07/22 04:08 AM
I got a dump of the 8042 "associate" processor used in Epson FX-80. It handles reading the dip switches and also handles the carriage control.

It looks fairly sane, there may be a couple of bad bytes. Took me awhile to figure out that although based on the mcs48, it uses ./unidasm -arch upi41
Posted By: Golden Child Re: lx800 self test - 02/10/22 12:04 AM
Making a little bit of progress on the FX-80, after hooking up the 8042 I can get the initial carriage movement to move to the left and go into the online state, then I can take it online/offline and feed a line or page.

There's still some things to figure out, it won't self test or print yet, but I'm happy to see the 8042 processing the commands from the 7810.

I'm trying to understand the 8042 disassembly but it really does a lot of gymnastics, so it's a bit hard to follow.

One of the things that the upd7810 driver doesn't currently implement is setting the INTAN7 flag which is checked by SKIT AN7. AN7 is hooked up to the online switch so you can't switch it on or offline.

I added a set_an7() function to upd7810.h to set this flag so I could get the SKIT AN7 to function.

According to the datasheet, "each of these flags is set whenever a falling edge is sensed on the corresponding input line if that line is used as an edge-sensing input."

void set_an7() { m_itf |= INTAN7; }


INPUT_CHANGED_MEMBER(epson_fx80_device::online_switch)
{
if (oldval & !newval)
{
m_maincpu->set_an7();
}
}




Here's a little bit of 8042 code, this part handles the cr steppers:

so port 2 bits 0 and 1 are the stepper out, and they go in a pattern like 00, 01, 11, 10, then cycle.

so it shifts bit 0 left to bit position 1 and inverts the old bit 1 and puts that in bit 0.

00 becomes 01, 01 becomes 11, 11 becomes 10, and 10 becomes 00.


Likewise, the opposite direction pattern is 00, 10, 11, 01, 00

so bit 1 is shifted into bit 0 and bit 1 is bit 0 inverted:

00 becomes 10, 10 becomes 11, 11 becomes 01, and 01 becomes 00


Code
71b: b9 59  mov  r1,#$59       get status
71d: f1     mov  a,@r1
71e: 92 2e  jb4  $72E            jump if bit 4 set  (so is bit 4 of status indicate direction?)
720: b9 5e  mov  r1,#$5E  5E       <<<<<<<<<<<< 720 move carriage 1 step
722: f1     mov  a,@r1
723: 77     rr   a
724: f2 2a  jb7  $72A
726: 43 02  orl  a,#$02   0000 0010   
728: e4 36  jmp  $736
72a: 53 fd  anl  a,#$FD   1111 1101
72c: e4 36  jmp  $736
72e: b9 5e  mov  r1,#$5E               <<<<<<< 72E move carriage in opposite direction 1 step
730: f1     mov  a,@r1
731: 97     clr  c
732: 32 35  jb1  $735
734: a7     cpl  c
735: f7     rlc  a
736: 53 03  anl  a,#$03        0000 0011
738: b9 5e  mov  r1,#$5E
73a: 21     xch  a,@r1
73b: 53 fc  anl  a,#$FC        1111 1100
73d: 41     orl  a,@r1
73e: 3a     outl p2,a               << update p2 STEPPERS
73f: a1     mov  @r1,a
Posted By: Just Desserts Re: lx800 self test - 02/10/22 01:49 PM
That's completely the wrong way of hooking up AN7. You have the logic that should be in the CPU core, outside of it. You should just be using a devcb_write_line in the CPU core for it.
Posted By: Golden Child Re: lx800 self test - 02/10/22 04:37 PM
Yes, agreed. The logic should be in the cpu core. (That was a hack to see if it would work).


Howabout a WRITE_LINE_MEMBER so the change to the INTAN7 flag can be initiated from outside the cpu core and have the logic inside the WRITE_LINE_MEMBER to track if it's a falling transition?



DECLARE_WRITE_LINE_MEMBER( an7_w )
{
if (!state & m_an7_state) m_itf |= INTAN7; // falling
m_an7_state = state;

}


INPUT_CHANGED_MEMBER(epson_fx80_device::online_switch)
{
m_maincpu->set_an7(newval);
}


The devcb call backs have the cpu core initiating the call back, they'd have to poll it.
Posted By: Golden Child Re: lx800 self test - 02/12/22 11:22 PM
Just for fun, I was looking at the minigraf at src/devices/bus/iq151/minigraf.cpp which is a driver for the

IQ151 Aritma Minigraf 0507 module emulation

and it uses 3 bits for the X stepper and 3 bits for the Y stepper.

So how does 3 bits map into the 4 lines of a stepper motor?

There's a manual in czech
Quote
jednim krokem se navodi.
musi se dodrzet spojitost posloupnosti jednotlivych kroku dle tabulky 2. pri navozeni rostouci posloupnosti kroku se provadi pohyb v kladnem smeru souradnych os pisatka zleva doprava u sour.
y pohyb papiru zezadu dopredu. pri navozeni klesajici posloupnosti kroku se provadi pohyb opacnym smerem.
which google translated as:
Quote
one step.
the continuity of the sequence of individual steps according to Table 2 must be observed.
y move the paper from back to front. when inducing a decreasing sequence of steps, a movement is performed in the opposite direction.

The manual also had a schematic which gives the circuit:

[Linked Image from i.imgur.com]

So steppers.cpp gives the standard drive table which is an 8 value cycle:

//Standard drive table is 2,6,4,5,1,9,8,a


The schematic lists T105 (which must be a 74LS05 NOT inverter gate) and T103 (which should be a 74LS03 NAND gate).
Each input to the 74LS03 is given as inverted, so let's treat it as a OR gate with inverted inputs (not a OR not b).

so let's write some lua code to see if we can make something that looks like our "standard" drive table:


We'll make a function to do a "logical not" called lnot and a function "t103" for the T103 gate:

function lnot(a) if a==0 then return 1 else return 0 end end
function t103(a,b) return lnot(a) | lnot(b) end

Let's try the topmost set of gates:


Code
for x = 0,7 do  x1 = x & 1 x2 = (x & 2) >> 1 x4 = (x&4) >> 2 
print(x4,x2,x1,"", t103(lnot(x1),lnot(x2)) & (x4),"") end
0	0	0		0	
0	0	1		0	
0	1	0		0	
0	1	1		0	
1	0	0		0	
1	0	1		1	
1	1	0		1	
1	1	1		1	

then the second

Code
for x = 0,7 do  x1 = x & 1 x2 = (x & 2) >> 1 x4 = (x&4) >> 2 
print(x4,x2,x1,"", t103(lnot(x1),lnot(x2)) & lnot(x4)) end
0	0	0		0	
0	0	1		1	
0	1	0		1	
0	1	1		1	
1	0	0		0	
1	0	1		0	
1	1	0		0	
1	1	1		0	

so let's do all the gates and put them together:
(It's a little easier to see if on multiple lines)

Code
function lnot(a) if a==0 then return 1 else return 0 end end
function t103(a,b) return lnot(a) | lnot(b) end

for x = 0,7 do  
x1 = x & 1 
x2 = (x & 2) >> 1 
x4 = (x & 4) >> 2   
a = t103(lnot(x1),lnot(x2)) & (x4) 
b = t103(lnot(x1),lnot(x2)) & lnot(x4)  
c = t103(lnot(x2),x4) & t103(lnot(x1),x2) & t103(x2,lnot(x4))
d = t103(x4,x2) & t103(lnot(x2),lnot(x4)) & t103(x2,lnot(x1)) 
print(x4,x2,x1,"",a,b,c,d,"",string.format("%x",a<<3 | b<<2 | c<<1 | d)) 
end

and all as one line:

Code
> for x = 0,7 do  x1 = x & 1 x2 = (x & 2) >> 1 x4 = (x&4) >> 2    a = t103(lnot(x1),lnot(x2)) & (x4) b=t103(lnot(x1),lnot(x2)) & lnot(x4)  c = t103(lnot(x2),x4) & t103(lnot(x1),x2) & t103(x2,lnot(x4)) d = t103(x4,x2) & t103(lnot(x2),lnot(x4)) & t103(x2,lnot(x1)) print(x4,x2,x1,"",a,b,c,d,"",string.format("%x",a<<3 | b<<2 | c<<1 | d)) end
0	0	0		0	0	1	0		2
0	0	1		0	1	1	0		6
0	1	0		0	1	0	0		4
0	1	1		0	1	0	1		5
1	0	0		0	0	0	1		1
1	0	1		1	0	0	1		9
1	1	0		1	0	0	0		8
1	1	1		1	0	1	0		a
> 
which matches the standard drive table.
Posted By: Golden Child Re: lx800 self test - 02/13/22 11:05 AM
[Linked Image from i.imgur.com]

I wanted to draw some text in a bitmap and had an idea to try the 1520 plotter font.

Reading some code from font2svg.rb at Project-64's webpage shows how to process the bytes. It's a very clever encoding:

bit 7 = indicates last byte of character
bit 6..4 = x coord
bit 3..1 = y coord (from the bottom)
bit 0 = draw line

A little bit of lua code to make an array:

Code
p = io.open("1520_325340-03.bin","rb") a = p:read("*a") print("SIZE="..#a)

-- generate character array

function iif(a,b,c)if a then return b else return c end end 

print("static constexpr u8 vector_font[96][20] =") print("{") pos = 1; for char = 32,127 do io.write ("    {") c = 0 while c&0x80==0 do pos=pos+1; c = a:byte(pos) ; x = (c & 0x70) >> 4; y = (c&0xe)>>1; done = (c&0x80)>>7; line=(c&0x1); io.write(c) if done==0 then io.write(",") end end print("}"..iif(char~=127,",","").." // char "..char.." \""..string.char(char).."\"") end print("};")

and a couple of functions to do the character drawing:


Code
void drawline(bitmap_rgb32 &bitmap, int x0, int y0, int x1, int y1, u32 pixelval)
//routine copied from void hp9845ct_base_state::draw_line
{
    int dx, dy, sx, sy, x, y, err, e2;

    // draw line, vector generator uses Bresenham's algorithm
    x = x0;
    y = y0;
    dx = abs((int) (x1 - x));
    sx = x < x1 ? 1 : -1;
    dy = abs((int) (y1 - y));
    sy = y < y1 ? 1 : -1;
    err = (dx > dy ? dx : -dy) / 2;

    for(;;)
    {
        if (!((x<0) || (x >= bitmap.width()) || (y<0) || (y >= bitmap.height()))) 
                bitmap.pix(y,x) = pixelval;

        if (x == x1 && y == y1) break;

        e2 = err;
        if (e2 > -dx) 
        {
                err -= dy;
                x += sx;
        }
        if (e2 < dy)
        {
                err += dx;
                y += sy;
       	}
	}
}

void draw_vector_char(bitmap_rgb32 &bitmap, u8 c, int x0, int y0, int xsize, int ysize, u32 pixelval)
{

static constexpr u8 vector_font[96][20] =
{
    {128}, // char 32 " "
    {34,37,53,51,35,38,47,63,55,167}, // char 33 "!"
    {30,29,62,189}, // char 34 """
    {20,29,60,53,70,7,10,203}, // char 35 "#"
    {20,53,71,57,25,11,29,61,46,163}, // char 36 "$"
    {4,77,28,13,11,27,29,54,71,69,53,183}, // char 37 "%"
    {66,11,13,31,45,43,7,5,19,35,199}, // char 38 "&"
    {42,175}, // char 39 "'"
    {50,35,21,29,47,191}, // char 40 "("
    {34,51,69,77,63,175}, // char 41 ")"
    {4,77,12,69,44,165}, // char 42 "*"
    {36,45,8,201}, // char 43 "+"
    {32,51,53,37,35,179}, // char 44 ","
    {8,201}, // char 45 "-"
    {34,37,53,51,163}, // char 46 "."
    {2,221}, // char 47 "/"  221 = 0xdd = (5,6)
    {4,77,63,31,13,5,19,51,69,205}, // char 48 "0"
    {28,47,35,18,179}, // char 49 "1"
    {12,31,63,77,75,3,195}, // char 50 "2"
    {12,31,63,77,75,57,41,56,71,69,51,19,133}, // char 51 "3"
    {50,63,9,7,199}, // char 52 "4"
    {4,19,51,69,73,59,11,15,207}, // char 53 "5"
    {8,27,59,73,69,51,19,5,13,31,63,205}, // char 54 "6"
    {2,75,79,143}, // char 55 "7"
    {24,11,13,31,63,77,75,57,71,69,51,19,5,7,25,185}, // char 56 "8"
    {4,19,51,69,77,63,31,13,11,25,57,203}, // char 57 "9"
    {20,23,39,37,21,26,29,45,43,155}, // char 58 ":"
    {18,37,39,23,21,37,42,45,29,27,171}, // char 59 ";"
    {66,25,207}, // char 60 "<"
    {22,71,26,203}, // char 61 "="
    {18,73,159}, // char 62 ">"
    {12,31,63,77,75,57,41,39,36,163}, // char 63 "?"
    {54,59,27,21,53,71,75,61,29,11,5,19,195}, // char 64 "@"
    {2,11,47,75,67,8,201}, // char 65 "A"
    {2,15,63,77,75,57,8,57,71,69,51,131}, // char 66 "B"
    {68,51,19,5,13,31,63,205}, // char 67 "C"
    {2,15,63,77,69,51,131}, // char 68 "D"
    {66,3,15,79,56,137}, // char 69 "E"
    {2,15,79,8,185}, // char 70 "F"
    {76,63,31,13,5,19,67,73,169}, // char 71 "G"
    {2,15,78,67,8,201}, // char 72 "H"
    {18,51,34,47,30,191}, // char 73 "I"
    {4,19,35,53,191}, // char 74 "J"
    {2,15,78,7,24,195}, // char 75 "K"
    {14,3,195}, // char 76 "L"
    {2,15,43,41,43,79,195}, // char 77 "M"
    {2,15,12,69,78,195}, // char 78 "N"
    {4,13,31,63,77,69,51,19,133}, // char 79 "O"
    {2,15,63,77,75,57,137}, // char 80 "P"
    {38,67,50,19,5,13,31,63,77,69,179}, // char 81 "Q"
    {2,15,63,77,75,57,9,24,195}, // char 82 "R"
    {4,19,51,69,71,57,25,11,13,31,63,205}, // char 83 "S"
    {34,47,14,207}, // char 84 "T"
    {14,5,19,51,69,207}, // char 85 "U"
    {14,7,35,71,207}, // char 86 "V"
    {14,3,39,40,39,67,207}, // char 87 "W"
    {2,5,77,79,14,13,69,195}, // char 88 "X"
    {34,41,77,79,14,13,169}, // char 89 "Y"
    {14,79,77,5,3,195}, // char 90 "Z"
    {34,3,15,175}, // char 91 "["
    {0x0c,0xd3}, // char 92 "\"
//    {92,77,59,53,35,19,5,23,51,83,40,201}, // char 92 "\"
    {18,51,63,159}, // char 93 "]"
    {0xa,0x2f,0xcb}, // char 94 "^"
//    {34,45,8,45,201}, // char 94 "^"
    {0x02,0xc3}, // char 95 "_"
//    {36,9,45,8,217}, // char 95 "_"
    {0x1e,0xbb}, // char 96 "`"
//    {8,249}, // char 96 "`"
    {52,35,19,5,7,25,41,55,10,43,57,53,195}, // char 97 "a"
    {14,3,35,53,57,43,139}, // char 98 "b"
    {52,35,19,5,9,27,43,185}, // char 99 "c"
    {58,27,9,5,19,51,191}, // char 100 "d"
    {6,55,57,43,27,9,5,19,179}, // char 101 "e"
    {0x22,0x2d,0x3f,0x4f,0x08,0xc9}, // char 102 "f"
//    {34,47,63,24,185}, // char 102 "f"
    {2,17,33,51,57,43,27,9,7,21,37,183}, // char 103 "g"
    {2,15,10,43,57,179}, // char 104 "h"
    {34,41,42,173}, // char 105 "i"
    {2,17,33,51,57,58,189}, // char 106 "j"
    {50,23,14,3,4,187}, // char 107 "k"
    {30,19,163}, // char 108 "l"
    {2,11,8,27,41,35,40,59,73,195}, // char 109 "m"
    {2,11,9,27,43,57,179}, // char 110 "n"
    {4,9,27,43,57,53,35,19,133}, // char 111 "o"
    {4,37,55,57,43,11,129}, // char 112 "p"
    {64,49,59,27,9,7,21,181}, // char 113 "q"
    {10,25,19,24,43,187}, // char 114 "r"
    {4,19,35,53,39,23,9,27,43,185}, // char 115 "s"
    {10,43,30,19,163}, // char 116 "t"
    {10,0x05,0x13,51,187}, // char 117 "u"
//    {10,3,51,187}, // char 117 "u"
    {10,7,35,71,203}, // char 118 "v"
    {10,5,19,37,39,37,51,69,203}, // char 119 "w"
    {2,75,10,195}, // char 120 "x"
    {10,9,37,74,73,129}, // char 121 "y"
    {10,75,3,195}, // char 122 "z"
    {0x3e,0x2d,0x2b,0x19,0x27,0x25,0xb3}, // char 123 "{"
//    {48,191}, // char 123 "{"
    {0x22,0xaf}, // char 124 "|"
//    {112,129}, // char 124 "|"  0x70, 0x81
    {0x1e,0x2d,0x2b,0x39,0x27,0x25,0x93}, // char 125 "}"
//    {2,57,99,131}, // char 125 "}"
    {0x0a,0x1d,0x2d,0x2b,0x3b,0xcd}, // char 126 "~"
//    {6,25,57,51,18,25,57,203}, // char 126 "~"
    {2,15,95,83,131} // char 127 ""
};


	if (c >= 32 && c <= 127) 
	{
		u8 i = 0;
		u8 val = 0;
		u8 xold = 0;
		u8 yold = 0;
		c = c - 32;
		while (val < 128 && i <= 20)
		{
			val = vector_font[c][i];
			u8 xc = (val & 0x70) >> 4;
			u8 yc = (val & 0x0e) >> 1;
			u8 lineto = (val & 0x1);
			if (lineto) drawline(bitmap, xc * xsize + x0, (7-yc) * ysize + y0, xold * xsize + x0, (7-yold)*ysize + y0, pixelval);
			xold = xc;
			yold = yc;
			i ++;
		}
	}
} // draw_vector_char





And drawing text is pretty simple:

Code
const char *teststr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnop";

	for (int i=0; i<strlen(teststr); i++)
	  draw_vector_char(bitmap, teststr[i], 20 + i*6*3, 5, 3, 2, 0);

	char buffer[128];
	snprintf(buffer, 128, "%5.2f", 3.25);

	for (int i=0; i<strlen(buffer); i++)
	  draw_vector_char(bitmap, buffer[i], 20 + i*6*3, 150, 3, 2, 0);

	snprintf(buffer, 20, "xpos=%d  ypos=%d", m_xpos, m_ypos);

	for (int i=0; i<strlen(buffer); i++)
	  draw_vector_char(bitmap, buffer[i], 20 + i*6*2, 170, 2, 2, 0);

The font isn't exactly ascii, but it shouldn't be hard to fix that.

(edit) It's not hard, just a bit of mental binary math to do, fixed the ascii characters.

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 02/14/22 10:16 PM
Been quite puzzled why the fx80 isn't printing properly on the self test, I think it's because it uses the CI input on PC5 to generate INTFEIN interrupts on the upd7810. Also it uses the CI input to reset the ECNT, and currently, neither one is supported by the upd7810 driver.

The fx80 has the PTS print timing sensor hooked up to the CI input of the upd7810 as well as the T1 input of the 8042.

After a little bit of hacking, the self test looks somewhat recognizable.


[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 02/16/22 03:57 AM
Managed to hook up the centronics interface and try out print shop. The horizontal positioning is inaccurate but it's recognizable.

Regular text seems a little bit screwy still.

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 02/16/22 06:27 PM
So weird, fired it up today and for some reason, the alignment seems more consistent. I'm not sure I changed anything...

edit: I think it was an uninitialized variable giving non deterministic behavior.


I think it's actually an FX-80+.

[Linked Image from i.imgur.com]
[Linked Image from i.imgur.com]

Also was perusing the upd7801 tables and noticed that the skipping for CALB should be 1 byte since the argument is implied (in the register BC).

Code
const struct upd7810_device::opcode_s upd7810_device::s_opXX_7801[256] =
{
       /* 0x60 - 0x7F */  // should CALB be 1 instead of 2?
        {&upd7810_device::PRE_60,        1, 0, 0,L0|L1}, {&upd7810_device::DAA,           1, 4, 4,L0|L1},
        {&upd7810_device::RETI,          1,15,15,L0|L1}, {&upd7810_device::CALB,          1,13,13,L0|L1},
        {&upd7810_device::PRE_64,        1, 0, 0,L0|L1}, {&upd7810_device::NEIW_wa_xx,    3,13,13,L0|L1},
        {&upd7810_device::SUI_A_xx,      2, 7, 7,L0|L1}, {&upd7810_device::NEI_A_xx,      2, 7, 7,L0|L1},
        {&upd7810_device::MVI_V_xx,      2, 7, 7,L0|L1}, {&upd7810_device::MVI_A_xx,      2, 7, 7,L0   },
        {&upd7810_device::MVI_B_xx,      2, 7, 7,L0|L1}, {&upd7810_device::MVI_C_xx,      2, 7, 7,L0|L1},
        {&upd7810_device::MVI_D_xx,      2, 7, 7,L0|L1}, {&upd7810_device::MVI_E_xx,      2, 7, 7,L0|L1},
        {&upd7810_device::MVI_H_xx,      2, 7, 7,L0|L1}, {&upd7810_device::MVI_L_xx,      2, 7, 7,   L1},
Posted By: Golden Child Re: lx800 self test - 02/19/22 01:23 AM
I was looking at the Amstrad lq3500di and it apparently uses a upd78310, which is interesting.

Mame has a upd78310 driver but it's just a disassembler right now. The disassembly looks sane. What's cool is that the lq5000di manual shows a lot of details that the lq3500 manual doesn't. They use the same i/o chips. In the lq5000di manual, addresses for the io chip are given, 7FE0 to 7FE7 which handles 8 x 4-bit io ports, and the printhead chip which is at 7FF4, 7FF5, 7FF7 AND 7FF6. for 24 pins (2 pairs of 8/4 bits) .

They're not hooked up identically, but the pin addresses for the ports should be the same which makes it so much easier to figure out.
Posted By: Golden Child Re: lx800 self test - 02/20/22 06:07 PM
The epson fx-80 sometimes used a SUMI board which connected via a ribbon cable to the socket for the 8042ah.

Onboard was an 8039 and a 2716 rom, which basically emulated the 8042ah.

There's flipflops to handle the flags OBF IBF and F0 and F1, along with an LS374 for the IN BUF and an LS374 for the OUT BUF.

I wonder if they couldn't get enough 8042AH chips and came up with the board as a workaround. So instead of a single chip, there's a board with 14 chips on it.
Posted By: Golden Child Re: lx800 self test - 02/21/22 02:29 PM
So the 8039 on the SUMI board will access addresses with movx to do certain functions corresponding to the 8042:

A 74LS138 decodes the function based on A2, A1, and A0.

0 = read input buffer (8042: IN A,DBB)
1 = write ST3-ST0 bits into LS374 (8042: MOV STS,A)
2 = write output buffer (8042: OUT DBB, A)
3 = read status bits F1 F0 IBF OBF
4 = set F0
5 = clear F0
6 = set F1
7 = clear F1

and a couple of examples:

Code
0:20b: b9 03  mov  r1,#$03
0:20d: 81     movx a,@r1            <<<  read memory 3, get status
0:20e: 12 0b  jb0  $20B              <<< busy wait on OBF flag

some routines that do various functions:
Code
0:3be: d5     sel  rb1
0:3bf: b8 00  mov  r0,#$00        0 = read input buffer
0:3c1: 80     movx a,@r0
0:3c2: 93     retr
0:3c3: d5     sel  rb1
0:3c4: b8 02  mov  r0,#$02        2 = write output buffer
0:3c6: 90     movx @r0,a
0:3c7: 93     retr
0:3c8: d5     sel  rb1
0:3c9: b8 07  mov  r0,#$07             7 = resets f1
0:3cb: 90     movx @r0,a
0:3cc: 93     retr
0:3cd: d5     sel  rb1
0:3ce: b8 06  mov  r0,#$06             6 = sets f1
0:3d0: 90     movx @r0,a
0:3d1: 93     retr

And a routine that sets/clears the status bits:

Code
0:403: 23 20  mov  a,#$20         set status bit  20
0:405: 84 15  jmp  $415
0:407: 23 df  mov  a,#$DF       reset status bit 20
0:409: 84 0d  jmp  $40D
0:40b: 23 ef  mov  a,#$EF       reset status bit 10
0:40d: b9 59  mov  r1,#$59
0:40f: 51     anl  a,@r1
0:410: a1     mov  @r1,a
0:411: 91     movx @r1,a
0:412: 83     ret
0:413: 23 10  mov  a,#$10             set status bit 10
0:415: b9 59  mov  r1,#$59
0:417: 41     orl  a,@r1
0:418: a1     mov  @r1,a
0:419: 91     movx @r1,a          <<< lower bits of 59 is an address of 1  #$59 = 0101 1001   lower 3 bits = 001
0:41a: 83     ret
Posted By: Golden Child Re: lx800 self test - 02/22/22 08:50 AM
I was able to find a table which gives all of the combinations of different chips for FX RX and MX printers:

Note that 78010BD is actually a upd7811 which has 4k of rom onboard and J1 is installed to pull MODE0 to ground. That's why location 4A is empty for the FX-80 and FX-100 that use it. The FX-80+ has a RAM installed in 4A and also has a different fuse rom (which handles the memory mapping).

(there is a typo, the FX-100 78010BD should use the M02011GA as the fuse I think)

This also explains why some FX-80 dumps seem incomplete, they don't have that 4k of internal rom.

[Linked Image from i.imgur.com]

and also a mention that SUMI boards (and any slave chip) can be replaced directly with C42040KB

[Linked Image from i.imgur.com]


The fuse rom TBP18S030 is just a 32 byte x 8 rom, it takes the upper 5 bits of the address (32 possibilities) and activates a single chip select (out of 8 possible chip selects) to route memory accesses in the memory map. That gives a 2k resolution out of the 64kb memory map.

Different printer versions can have the same basic design, but have different ram/rom combination installed and change the fuse rom for the corresponding memory map.
Posted By: Golden Child Re: lx800 self test - 02/22/22 12:21 PM
The TBP18S030 isn't supported by the TL866, so I have an arduino mega 2560 and a breadboard, let's see if we can dump the contents with the help of the datasheet:


[Linked Image from i.imgur.com]

so hookup is:
28 -> not G
30-34 -> A0..A4
40-47 -> Q0..Q7
and hooking up the GND and 5V to the arduino 5V

I chose those pins as I had some other stuff hooked up (an LCD) that I didn't want to disconnect, and the mega's got lots of pins.

Code
void setup() {
  Serial.begin(9600);

	pinMode(28,OUTPUT);
	pinMode(30,OUTPUT);
	pinMode(31,OUTPUT);
	pinMode(32,OUTPUT);	
	pinMode(33,OUTPUT);	
	pinMode(34,OUTPUT);	
}

char buffer[32];

void loop() {

for (int i=0;i<32;i++) 
{
  digitalWrite(28,LOW);
  for (int j=0;j<5;j++) digitalWrite(30+j,(i & (1<<j)));
  delay(10);
  int outval = 0;
  for (int j=0;j<8;j++) outval |= (digitalRead(40+j) ? (1<<j) : 0);
 
  dumparray[i]=outval;

  snprintf(buffer, sizeof(buffer), "%x: %x\n", i,outval);
  Serial.write(buffer);
}


for (int i=0;i<32;i++) 
{

  snprintf(buffer, sizeof(buffer), "%x ", dumparray[i]);
  Serial.write(buffer);
}
Serial.println();


delay(10000);
}
}



and it looks like what you'd expect, a single bit low for chip select, FF for no chip select. It's nice to provide confirmation of the address map for a given chip.

Code

marked m20214ga  (I think this is actually supposed to be m02014ga, two zeros instead of 2 twos)

0: fe
1: fe
2: fe
3: fe
4: fe
5: fe
6: fe
7: fe
8: bf
9: bf
a: bf
b: bf
c: bf
d: bf
e: bf
f: bf
10: fd
11: f7
12: fb
13: ff
14: ff
15: ff
16: ff
17: ff
18: ff
19: 7f
1a: ef
1b: df
1c: ff
1d: ff
1e: ff
1f: ff
fe fe fe fe fe fe fe fe bf bf bf bf bf bf bf bf fd f7 fb ff ff ff ff ff ff 7f ef df ff ff ff ff 

So for example, rom address 0 in the TBP18S030 corresponds to memory access to 0x0 to 0x7ff, and CS0 gets activated for the ROM chip at 5A:

[Linked Image from i.imgur.com]


And also compare with a M02011GA, I believe that this one has 4K of rom on the upd7811, so you see that the first two entries are FF since it's going to the internal cpu rom instead of the rom chip.

dump of M02011GA

Code
0: ff
1: ff
2: fe
3: fe
4: fe
5: fe
6: fe
7: fe
8: fe
9: fe
a: fd
b: fd
c: fd
d: fd
e: fd
f: fd
10: fd
11: fd
12: fb
13: f7
14: fd
15: ff
16: ff
17: ff
18: ff
19: ff
1a: ef
1b: df
1c: bf
1d: 7f
1e: ff
1f: ff
ff ff fe fe fe fe fe fe fe fe fd fd fd fd fd fd fd fd fb f7 fd ff ff ff ff ff ef df bf 7f ff ff 
Posted By: Al Kossow Re: lx800 self test - 02/22/22 01:27 PM
The TBP18S030 isn't supported by the TL866

https://mikesarcade.com/cgi-bin/spies.cgi?action=url&type=info&page=PromRef.txt is your friend

It's not like you're trying to program one.
Posted By: Golden Child Re: lx800 self test - 02/22/22 11:59 PM
Thanks for the link, Al. It's a good cross reference.


I modified my arduino program to give the upper byte addresses below the hex data to better see how the address map was arranged:

Code
void setup() {
  Serial.begin(9600);

  pinMode(28,OUTPUT);
  pinMode(30,OUTPUT);
  pinMode(31,OUTPUT);
  pinMode(32,OUTPUT);	
  pinMode(33,OUTPUT);	
  pinMode(34,OUTPUT);	
}

char buffer[32];

int dumparray[32];

void loop() 
{
  for (int i=0;i<32;i++) 
  {
    digitalWrite(28,LOW);
    for (int j=0;j<5;j++) digitalWrite(30+j,(i & (1<<j)));
    delay(10);
    int outval = 0;
    for (int j=0;j<8;j++) outval |= (digitalRead(40+j) ? (1<<j) : 0);
   
    dumparray[i]=outval;
  
    snprintf(buffer, sizeof(buffer), "%x: %x\n", i,outval);
    Serial.write(buffer);
  }
  
  for (int i=0;i<32;i++) 
  {
  
    snprintf(buffer, sizeof(buffer), "%02x ", dumparray[i]);
    Serial.write(buffer);
  }
  Serial.println();
  
 
  for (int i=0;i<32;i++) 
  {
    snprintf(buffer, sizeof(buffer), "%02x ", ((i * 0x800) >> 8) & 0xff);
    Serial.write(buffer);
  }
  Serial.println();
  
  delay(10000);
}

and the result:

Code
0: fe
1: fe
2: fe
3: fe
4: fd
5: fd
6: fd
7: fd
8: ff
9: ff
a: fe
b: fe
c: fe
d: fe
e: fe
f: fe
10: ff
11: ff
12: fb
13: f7
14: ff
15: ff
16: ff
17: ff
18: ff
19: ff
1a: ef
1b: df
1c: bf
1d: 7f
1e: ff
1f: ff
fe fe fe fe fd fd fd fd ff ff fe fe fe fe fe fe ff ff fb f7 ff ff ff ff ff ff ef df bf 7f ff ff 
00 08 10 18 20 28 30 38 40 48 50 58 60 68 70 78 80 88 90 98 a0 a8 b0 b8 c0 c8 d0 d8 e0 e8 f0 f8

This one is kind of interesting, because it maps the same chip into different ranges, wrapping the address around since it's a (16k) 27128 chip I believe.

From 0x0000 to 0x1fff and then from 0x5000 to 0x7fff.

The "fuse" rom has to match the contents of the rom, because you can flexibly map things all over the place, as long as the rom knows where to look

I think the unusual mapping here acts as a bit of a copy protection "dongle" since you also need the matching fuse rom.
Posted By: Golden Child Re: lx800 self test - 02/23/22 02:47 PM
This eprom chip just doesn't give a consistent read. I ran it through the TL866 multiple times and every single time it gives different results.

Do eproms go bad?

Looking at it with meld there's a couple of ranges with bad reads.

[Linked Image from i.imgur.com]

So why not try to dump it with arduino since I could read a 32x8 rom, is a 2764 that much more difficult?

The hard part is hooking up all the little wires on the breadboard to the mega2560.

Code
// Dump 2764
//
// 22,23,25 -> not PROG, not E, not G
// 40-52 -> A0..A12
// 30-37 -> Q0..Q7
// and hooking up the GND and 5V to the arduino 5V

void setup() {
  Serial.begin(9600);

  pinMode(22,OUTPUT); // not PROG
  pinMode(23,OUTPUT); // not E
  pinMode(25,OUTPUT); // not G

  for (int i=0;i<=12;i++)
  { 
    pinMode(40+i,OUTPUT); // A0-A12
  } 
}

char buffer[32];
int dumparray[32];

void loop() 
{
  int groupsize = 16;
  
  for (int base=0;base<(1<<13);base+=groupsize)
  {
  
  for (int i=0;i<groupsize;i++)   
  {
    digitalWrite(22,HIGH);  // NOT PROG
    digitalWrite(23,LOW);
    digitalWrite(25,LOW);
    
    for (int j=0;j<=12;j++) digitalWrite(40+j,((i+base) & (1<<j))!=0 );  // a0..a12
    delay(10);

    int numsamples = 50;

    int samplesarray[numsamples] = {};
    
    for (int sample = 0; sample < numsamples; sample++)
    {
    
      int outval = 0;
      for (int j=0;j<8;j++) outval |= (digitalRead(30+j) ? (1<<j) : 0);
     
      dumparray[i]=outval;
    
      samplesarray[sample] = outval;
      if ((sample > 0) && (samplesarray[sample] != samplesarray[sample-1]))
      {
        snprintf(buffer, sizeof(buffer), "%02x: %02x\n", (base+i), outval);
        Serial.write(buffer);   
      }
  
    }
  
  }
  
   snprintf(buffer, sizeof(buffer), "%08x  ", base);
   Serial.write(buffer);
  
  for (int i=0;i<groupsize;i++) 
  {
  
    snprintf(buffer, sizeof(buffer), "%02x ", dumparray[i]);
    Serial.write(buffer);
  }
  Serial.println();
 
  }  
  delay(10000);
}


[Linked Image from i.imgur.com]


And it gives pretty much the same results as the tl866, with bad reads every time, so I asked it to take 50 samples of an address and print it out every time the read was different from the previous read:

For example: memory address 802 seems to oscillate all over the place (the right digit seems ok)

802: ff
802: 2f
802: af
802: bf
802: ff
802: 7f
802: bf
802: 6f
802: bf
802: ff
802: af
802: 7f
802: 4f
802: ff
802: 4f
802: ff
802: 7f
802: ff
802: cf
802: 4f
802: bf
802: ff
802: 7f
802: bf
802: 5f
802: 7f
802: 5f
802: 4f
802: af
802: 6f
802: bf
802: ff
802: 7f
802: ff
802: 7f
802: 6f
802: df
802: cf
802: 4f
802: ef
802: af
802: cf
802: ff

location 804 seems a bit better:

804: 8a
804: 8b
804: 8a
804: 8b
804: 8a
804: 8b
804: 8a
804: 8b
804: 8a
804: 8b
804: 8a
804: 8b

808 is pretty unstable

808: e7
808: d7
808: 67
808: 77
808: 67
808: f7
808: a7
808: b7
808: 67
808: 77
808: 67
808: f7
808: e7
808: 67
808: 77
808: b7
808: 67
808: e7
808: 77
808: 67
808: d7
808: b7
808: d7
808: b7
808: 67
808: 77
808: 67
808: e7
808: 77
808: d7
808: 77
808: a7
808: 77
808: b7
808: 67
808: a7
808: 77
808: 67
808: b7
808: f7


I think these fall in noncritical areas of font data, maybe I could reconstruct them once everything else is working.
Posted By: R. Belmont Re: lx800 self test - 02/23/22 03:14 PM
The dedicated dumpers like Team Europe do things like increasing and decreasing the voltage to the chip slightly and that often stabilizes the reads from otherwise shaky EPROMs. I've heard of success with freezing the chip and then reading it immediately too, although I have no idea how that works smile
Posted By: Haze Re: lx800 self test - 02/23/22 03:21 PM
a lot of those techniques end up giving you 'stable, but still bad' reads however.

EEPROMS go bad, flash ROMs go bad (frequently) even mask ROMs go bad.

it's no different to floppies or cassettes in that sense, everything can fail, and eventually will fail.
Posted By: Al Kossow Re: lx800 self test - 02/23/22 06:35 PM
I've been dumping eproms for decades and for some reason 2764s are particularly troublesome with inconsistent reads/verifies.
Someone just sent me some dumps he made from a bunch of Northgate OmniKey keyboards, and two of them are bad.
I should probably start logging which brands are the most troublesome.

Currently, I'm having the best luck reading them with a BP Micro BP-1200
Posted By: Golden Child Re: lx800 self test - 02/23/22 08:55 PM
Interesting that 2764s are problematic. I'd tell you what make this chip is, but it has no markings on it after scraping off the label.

I did have a idea, why not do a bunch of reads and count the number of reads that each bit is set.

So if we sample it 32 times and get a different result, then we read it 2048 (0x800) times and see how many times each bit is set.

If the count is less than 0x400, then we'll call it a low, otherwise a high.


Code
// Dump 2764
//
// 22,23,25 -> not PROG, not E, not G
// 40-52 -> A0..A12
// 30-37 -> Q0..Q7
// and hooking up the GND and 5V to the arduino 5V

void setup() {
  Serial.begin(9600);

  pinMode(22,OUTPUT); // not PROG
  pinMode(23,OUTPUT); // not E
  pinMode(25,OUTPUT); // not G

  for (int i=0;i<=12;i++)
  { 
    pinMode(40+i,OUTPUT); // A0-A12
  } 
}

char buffer[32];

int dumparray[32];

void loop() 
{
  int groupsize = 16;
  
  for (int base=0;base<(1<<13);base+=groupsize)
  {
  
  for (int i=0;i<groupsize;i++)   
  {
    digitalWrite(22,HIGH);  // NOT PROG
    digitalWrite(23,LOW);
    digitalWrite(25,LOW);
    
    for (int j=0;j<=12;j++) digitalWrite(40+j,((i+base) & (1<<j))!=0 );  // a0..a12  
    delay(10);

    int numsamples = 50;

    int samplesarray[numsamples] = {};
    
    int badreadflag = 0;
    
    for (int sample = 0; sample < numsamples; sample++)
    {
    
      int outval = 0;
      for (int j=0;j<8;j++) outval |= (digitalRead(30+j) ? (1<<j) : 0);
     
      dumparray[i]=outval;
    
      samplesarray[sample] = outval;
      if ((sample > 0) && (samplesarray[sample] != samplesarray[sample-1]))
      {
        badreadflag++;
        snprintf(buffer, sizeof(buffer), "%02x: %02x\n", (base+i), outval);
        Serial.write(buffer);   
      }
  
    }
    
    if (badreadflag)
    {
      int bitsamplecount[8]={};
      int dosamples = 2048;
      
      for(int sample=0;sample<dosamples;sample++)
      {
        int outval = 0;
        for (int j=0;j<8;j++) outval |= (digitalRead(30+j) ? (1<<j) : 0);
        
        for (int j=0;j<8;j++) bitsamplecount[j] += (outval & (1<<j)) ? 1 : 0;    
      }
    
      int calcval = 0;
      for (int j=0;j<8;j++) 
      {
        calcval |=   ((bitsamplecount[j] > (dosamples/2)) << j);

        snprintf(buffer, sizeof(buffer), "%02x: bit %x count = %02x\n", (base+i), j, bitsamplecount[j]);
        Serial.write(buffer);   

      }
      snprintf(buffer, sizeof(buffer), "%02x: calc value = %02x\n", (base+i), calcval);
      Serial.write(buffer);   
      dumparray[i] = calcval;
    
    }   
    
  }
  
   snprintf(buffer, sizeof(buffer), "%08x  ", base);
   Serial.write(buffer);
  
  for (int i=0;i<groupsize;i++) 
  {
  
    snprintf(buffer, sizeof(buffer), "%02x ", dumparray[i]);
    Serial.write(buffer);
  }
  Serial.println();
 }  
  delay(10000);
}


so you see that if a bit is good, the count will read either 0x0 or 0x800, otherwise is will read something in between.

At least it gives you an idea of which individual bits are suspect.


Code
800: 9f
800: 8f
800: af
...
800: 9f
800: bf
800: 9f
800: bit 0 count = 800
800: bit 1 count = 800
800: bit 2 count = 800
800: bit 3 count = 800
800: bit 4 count = 382
800: bit 5 count = 3b9
800: bit 6 count = 00
800: bit 7 count = 800
800: calc value = 8f
801: a8
801: 2a
801: e8
801: 08
...
801: 8a
801: 48
801: 08
801: 4a
801: c8
801: bit 0 count = 00
801: bit 1 count = 2c8
801: bit 2 count = 00
801: bit 3 count = 800
801: bit 4 count = 00
801: bit 5 count = 268
801: bit 6 count = 314
801: bit 7 count = 407
801: calc value = 88

802: 00
802: 02
802: 42
802: 40
802: 02
802: 00
802: 42
802: 40
802: bit 0 count = 00
802: bit 1 count = 260
802: bit 2 count = 00
802: bit 3 count = 00
802: bit 4 count = 00
802: bit 5 count = 36
802: bit 6 count = 288
802: bit 7 count = 00
802: calc value = 00

etc.
...
Posted By: Lord Nightmare Re: lx800 self test - 02/24/22 04:34 AM
rather than voting the bits to get the correct result, remember this about JEDEC eproms:
if the bit is programmed/set and the floating gate has a non-zero voltage in it, it will read as a 0.
if the bit is unprogrammed or the floating gate voltage has decayed to below the detectable threshold, it will read as a 1.

This effectively means:
if a bit ALWAYS reads as a 1, it should (probably, unless it has decayed past the point where it reads inconsistently) be a 1.
if a bit ALWAYS reads as a 0, it should be a 0.
if a bit SOMETIMES reads as a 0, it should be a 0.


So what you should do is read the bad chip 100 times and binary AND together all of the dumps.

LN
Posted By: Lord Nightmare Re: lx800 self test - 02/24/22 04:37 AM
Also, heating up a bitrotten eprom like this (heated using a hair dryer or even a heatgun) may make it read more reliably. Kevtris did some tests with bitrotten eproms and a heatgun some time ago, although I don't remember the results offhand.

Slightly lowering(!) the voltage to the chip also helps, because the floating gate threshold comparator has a lower external voltage to compare against.
So if the arduino you're using for dumping can run stably at 4.5v instead of 5v, this might help too.
Posted By: Golden Child Re: lx800 self test - 02/24/22 10:37 PM
I see what you mean by the default value being 1. So if it reads 0 at all, then it should probably be a 0.

Considering these roms are around 35 years old it's not surprising.

I came across a text file that had some comments by kevtris that was interesting:

Quote
<kevtris> well, you may want to talk about how an EPROM works first in
basic terms

<kevtris> here's how an EPROM works: the EPROM is arranged as a bunch of
individual memory cells that can hold a "1" when not programmed,
or a "0" if programmed

<kevtris> the data is stored on the gate of a transistor; to program the
EPROM, you inject electrons into the gate, where they get
stuck. An ultraviolet light erases the EPROM, because it lets the
electrons "leak" off the gate

<kevtris> Since no insulator is perfect, these electrons will eventually
"leak" off, so the cell reverts back to an erased state, taking
the data with it

<kevtris> the datasheets typically specify a 10 year guaranteed minimum
programming lifetime

<kevtris> However, 20-25 years is about what they usually last

<kevtris> heat, ultraviolet light, and other things can cause premature
erasure

<kevtris> There's no way to know when this effect will occur, and likely
it will start out slowly, taking one or two cells out, then more as
time goes on

<kevtris> if caught, it *may* be possible to "fix" the problems, but this
is difficult, at best to do

<kevtris> I have seen bitrot first hand, so it is a real phenomena

<kevtris> I had 5 identical "things"* and all 5 had EPROMs on them

<kevtris> * = "things"... hard to describe. It was a modem with 4 LED
displays and a keypad, meant to enter stock quotations :-)

<kevtris> I noticed that 2 of them didn't work right; they would crash
when I tried various things on them

<kevtris> so I pulled all 5 EPROMs eventually (to re-use them) and sure
enough, those 2 had some bit-rot on the EPROMs

<kevtris> some of the bits had reverted to a non-programmed
(erased) state... i.e. bits changed from 0 to 1, and there were
no 1 to 0 changes

<kevtris> 1 = erased, 0 = programmed

<kevtris> you cannot program a "1" if the cell has a 0 on it unless you
erase the whole EPROM

<kevtris> using the EPROM causes slow degradation too, but we're talking
10+ years of 24/7 use here, at over a million reads a second

<kevtris> casual playing of proto's will not impact the lifetime more than
a couple minutes or seconds probably

<blackjax> however, some one throwing uncovered eproms onto a scanner
to brag about his find could cause some harm

<kevtris> naaah

<kevtris> fluorescent light won't hurt them

<kevtris> shortwave UV is what erases them

<blackjax> the stuff that we can't see

<kevtris> flo's may hurt it but it'd take 2-5 years to erase it under
continuous exposure

<kevtris> sunlight would take several weeks of full sun and no clouds

<kevtris> there really is no sub for a good eraser

<reio-ta> bj: so that means dont take your proto in with ya when you go
into a suntan chamber

<kevtris> naah that won't work either

<kevtris> that's the wrong wavelength

<kevtris> that's UVa

<kevtris> you need UVb or shorter... germicidal lamps are usually the ones
used
Posted By: Golden Child Re: lx800 self test - 02/26/22 04:45 PM
Trying to understand exactly how the PTS sensor (print timing sensor) works, there's a disk with slots (or slits) in it with a photosensor.

When the drive belt moves, so does this sensor disk.

I count 48 slits on this picture, and making a mark on the sensor disk and on the paper where the printhead is, then sliding it left or right and marking when that point on the disk comes around again, I get .8 of an inch (I make 10 marks across 8 inches from the origin mark) for a full revolution.

Counting the slits and multiplying by 10 revs/8 inches 1.25 revs/inch * 48 slits/rev = 60 slits per inch or 60 dpi (which sounds like a reasonable dpi since it can print at 120dpi or 240dpi)

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 02/27/22 01:42 AM
Ok, I think I've figured out why the fx-80 won't print properly. I had a misconception about what fires the pins, it's strictly based upon writing directly to the printhead pins, nothing more. For some reason, I thought it was tied to the PTS signal. Looking at the diagram, I got the idea that it was dependent on the PTS signal. It is, but only indirectly, as everything goes through the main cpu.

[Linked Image from i.imgur.com]


[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 02/27/22 03:54 PM
The rom corruption can be seen in some of the italic characters from the self test: * + , - . / 01234

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 02/28/22 01:39 AM
I was able to patch the font corruption by finding the same italics sequence from a different rom:

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 03/07/22 06:36 AM
Okay, I think I know how the jx-80 handles its color ribbon.

Functionally, the main board of the jx-80 is the same as the fx-80. In fact, the schematic given in the jx-80 manual is for the FXMB fx-80 main board. However, it doesn't show the additional board that the jx-80 has which controls the ribbon motor and ribbon home sensor.

Looking at the manual for the LX-300 also gives some clues as to how the ribbon and the sensor works (they call is a CS Motor, Color Select maybe?), even though it's a completely different design.

The upgrade rom works for both the FX and the JX printers and it includes the CS Motor support, which looks at E800 for the CS home sensor (MSBit) and writes to E801 for the motor drive bits 6 and 7 (with bit 5 to activate the motor circuit possibly).

[Linked Image from i.imgur.com]


Just to give you an idea, here's the code that reads the ribbon home sensor at E800,

and also where it writes the stepper patterns to E801, writing 20, A0, 60, E0 and keeping the current value in $93FC.



Code
1f8b: 70 69 00 e8  MOV     A,($E800)
1f8f: 47 80        ONI     A,$80
1f91: b8           RET     
1f92: b9           RETS    
1f93: 52           DCR     B
1f94: cf           JR      $1FA4
1f95: 70 69 fc 93  MOV     A,($93FC)
1f99: 07 df        ANI     A,$DF
1f9b: 70 79 fc 93  MOV     ($93FC),A
1f9f: 70 79 01 e8  MOV     ($E801),A
1fa3: b8           RET     
1fa4: b2           PUSH    DE
1fa5: 70 6c fc 93  MOV     D,($93FC)
1fa9: 69 00        MVI     A,$00
1fab: 74 6b 00     NEI     C,$00
1fae: d7           JR      $1FC6
1faf: 74 4c 80     ONI     D,$80
1fb2: c5           JR      $1FB8
1fb3: 74 4c 40     ONI     D,$40
1fb6: c6           JR      $1FBD
1fb7: c7           JR      $1FBF
1fb8: 74 4c 40     ONI     D,$40
1fbb: c5           JR      $1FC1
1fbc: c6           JR      $1FC3
1fbd: 69 e0        MVI     A,$E0
1fbf: 69 60        MVI     A,$60
1fc1: 69 a0        MVI     A,$A0
1fc3: 69 20        MVI     A,$20
1fc5: d6           JR      $1FDC
1fc6: 74 4c 80     ONI     D,$80
1fc9: c5           JR      $1FCF
1fca: 74 4c 40     ONI     D,$40
1fcd: c6           JR      $1FD4
1fce: c7           JR      $1FD6
1fcf: 74 4c 40     ONI     D,$40
1fd2: c5           JR      $1FD8
1fd3: c6           JR      $1FDA
1fd4: 69 20        MVI     A,$20
1fd6: 69 a0        MVI     A,$A0
1fd8: 69 60        MVI     A,$60
1fda: 69 e0        MVI     A,$E0
1fdc: 70 79 fc 93  MOV     ($93FC),A
1fe0: 70 79 01 e8  MOV     ($E801),A
1fe4: a2           POP     DE


Since other roms have different fuse roms, the CS7 (chip select 7) would show up at different addresses in the memory map.

Code
CS7 (pattern 7f) is at e800/e801:
                                                                                       vv
fe fe fe fe fd fd fd fd ff ff fe fe fe fe fe fe ff ff fb f7 ff ff ff ff ff ff ef df bf 7f ff ff 
00 08 10 18 20 28 30 38 40 48 50 58 60 68 70 78 80 88 90 98 a0 a8 b0 b8 c0 c8 d0 d8 e0 e8 f0 f8

Code
the address map for the M20214GA puts the same 7f pattern chip select 7 (CS7) at c800/c801
                                                                           vv
fe fe fe fe fe fe fe fe bf bf bf bf bf bf bf bf fd f7 fb ff ff ff ff ff ff 7f ef df ff ff ff ff
00 08 10 18 20 28 30 38 40 48 50 58 60 68 70 78 80 88 90 98 a0 a8 b0 b8 c0 c8 d0 d8 e0 e8 f0 f8
M20214GA
Posted By: Golden Child Re: lx800 self test - 03/24/22 03:47 PM
Yay! Got the mx80 to do something somewhat recognizable. Kinda. Still more stuff to figure out.

It will line feed and form feed from the buttons.

[Linked Image from i.imgur.com]

It uses an 8049 along with an 8041 to handle cpu chores, along with an 8155 to handle some i/o.

I forgot to set a cpu clock for the 8041, leaving it zero and I couldn't figure out why the debugger wouldn't want to stop when I set the focus to the 8041, it just took off when I changed the focus. Once I set it to 6mhz it acted normally.
Posted By: Golden Child Re: lx800 self test - 03/25/22 02:04 PM
Something that looks a little better:

[Linked Image from i.imgur.com]

The next step is to add some code to i8155.cpp that will handle the ALT 3 port strobed mode for port c which handles the strobed mode for input port A and uses PC3-PC5 as output ports.

Port A is connected to the centronics data and PC0 will trigger an interrupt on the 8049, PC1 will hook up to centronics busy and PC2 hooks up to centronics strobe.

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 03/28/22 10:33 AM
yes! finally figured out why the mx80 wouldn't go online when I hit the online button...

Port 10 is hooked up to the Online lamp, so I thought it was just an output. Turns out the 8049 reads the port bit 10 to check to see if it's online, so after adding that to the read function, now it goes online and toggles.

This had me baffled for a while now.

Code
uint8_t epson_mx80_device::port1_r()
{
	// p10 = online lamp
	// p11 = prav' sp20
	// p12 = err' sp23
	// p13 = dirav' sp21
	// p14 = dir sp22
	// p15 = ack
	// p16 = busy
	// p17 = auto feed (connects also to SW2-3)

	return 	BIT(m_8049_p1, 0) << 0 |  // reads online lamp status at 1:61a   YES!  THIS FIXED THE ONLINE SWITCH!
			BIT(m_slave_p2, 0) << 1 |
			BIT(m_slave_p2, 3) << 2 |
			BIT(m_slave_p2, 1) << 3 |
			BIT(m_slave_p2, 2) << 4 |
			BIT(m_8049_p1, 5) << 5 |
			BIT(m_8049_p1, 6) << 6 |
			BIT(ioport("DIPSW2")->read(),2) << 7;  // p17 = auto feed
}

void epson_mx80_device::port1_w(uint8_t data)
{
	m_8049_p1 = data;
	m_bitmap_printer->set_led_state(bitmap_printer_device::LED_ONLINE, BIT(data, 0));
	logerror("P1 W %x %s\n", data, machine().describe_context());
}


Small victories keep me going 8-)
Posted By: Golden Child Re: lx800 self test - 04/10/22 04:54 PM
First signs of life from the RX-80... Don't know why it stops the self test yet...

Main CPU is a UPD7810 running at 11 MHz.
8K of external rom
uses 256 bytes of ram inside upd7810, no external ram chips

According to http://www.trs-80.org/epson-mx80/:

"In 1983, Epson introduced two new dot matrix printers: the RX-80 and the FX-80.
The FX-80 was a more advanced printer than the MX-80, offering faster printing speeds and
programmable character sets, among other features.

The RX-80 was a lower cost replacement for the MX-80."


[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 04/12/22 02:30 AM
Finally figured out why it wouldn't print properly, it needed a pts (print timing sensor) pulse adjustment, being multiplied by 1/2. I think it's because the disc has pulses every 1/60 inch but the stepper goes at 120dpi.

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 04/12/22 11:26 AM
Modeling the stepper as a spring and getting the spring tuning just right gives you clean draft printing:

[Linked Image from i.imgur.com]
[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 05/12/22 07:15 AM
Got a little discouraged when the rx80 would mostly work but would go off in the weeds. Maybe time to work on something else, maybe the mx80.

Worked around the strobed input that was lacking from the 8155 and now the mx80 is receiving data:

So when you get a strobe from the computer, the 8155 will generate an interrupt and tell you that the printer's busy. When you read the data it clears the interrupt and the busy.

Just reworked my existing code a little to do those functions and shoehorn the data into port a read:

[Linked Image from i.imgur.com]
Posted By: Golden Child Re: lx800 self test - 05/13/22 11:10 AM
It has the capability to switch into an IBM character set and I remember that you could hit the print screen key on IBM PCs and send the text screen to the printer:


top print is with regular char set

bottom print is with IBM char set


[Linked Image from i.imgur.com]

You can also toggle the output from the command line with CTRL+printscreen.
Posted By: Golden Child Re: lx800 self test - 05/14/22 06:18 PM
I had so much trouble with making signals work, I made a passthrough centronics device that would let me monitor the status:

[Linked Image from i.imgur.com]



It's pretty simple, just watching the signals flowing forward and backwards, but useful to help see what's happening.
Code
	virtual DECLARE_WRITE_LINE_MEMBER( input_strobe ) override { m_ctx->write_strobe(state); m_input_strobe = state;}
	virtual DECLARE_WRITE_LINE_MEMBER( input_data0 ) override { if (state) m_data |= 0x01; else m_data &= ~0x01; m_ctx->write_data0(state);m_input_data0 = state;}
	virtual DECLARE_WRITE_LINE_MEMBER( input_data1 ) override { if (state) m_data |= 0x02; else m_data &= ~0x02; m_ctx->write_data1(state);m_input_data1 = state;}
	virtual DECLARE_WRITE_LINE_MEMBER( input_data2 ) override { if (state) m_data |= 0x04; else m_data &= ~0x04; m_ctx->write_data2(state);m_input_data2 = state;}
	virtual DECLARE_WRITE_LINE_MEMBER( input_data3 ) override { if (state) m_data |= 0x08; else m_data &= ~0x08; m_ctx->write_data3(state);m_input_data3 = state;}
	virtual DECLARE_WRITE_LINE_MEMBER( input_data4 ) override { if (state) m_data |= 0x10; else m_data &= ~0x10; m_ctx->write_data4(state);m_input_data4 = state;}
	virtual DECLARE_WRITE_LINE_MEMBER( input_data5 ) override { if (state) m_data |= 0x20; else m_data &= ~0x20; m_ctx->write_data5(state);m_input_data5 = state;}
	virtual DECLARE_WRITE_LINE_MEMBER( input_data6 ) override { if (state) m_data |= 0x40; else m_data &= ~0x40; m_ctx->write_data6(state);m_input_data6 = state;}
	virtual DECLARE_WRITE_LINE_MEMBER( input_data7 ) override { if (state) m_data |= 0x80; else m_data &= ~0x80; m_ctx->write_data7(state);m_input_data7 = state;}

	DECLARE_WRITE_LINE_MEMBER( busy_w ) { output_busy(state); m_busy = state;}   // centronics output busy
	DECLARE_WRITE_LINE_MEMBER( ack_w )  { output_ack(state); m_ack = state;} ;    // centronics output ack
	DECLARE_WRITE_LINE_MEMBER( perror_w ) { output_perror(state); m_perror = state;}; // centronics perror
	DECLARE_WRITE_LINE_MEMBER( select_w ) { output_select(state); m_select = state;}; // centronics select out
	DECLARE_WRITE_LINE_MEMBER( fault_w ) { output_fault(state); m_fault = state;};  // centronics fault
Posted By: Lord Nightmare Re: lx800 self test - 05/15/22 01:43 AM
I'm wondering if the cycle timing tables being wrong for the upd7810 and upd780x is causing some of the columns to be offset improperly in the printouts.
I have a local patch which partially fixes this, but I never made my way completely through the upd7810 databook to fix all the timing mistakes in the tables.

There's a LOT of mistakes!

LN
Posted By: Golden Child Re: lx800 self test - 05/15/22 02:55 AM
Hi LN,

Yes, there's a bunch of cycle counts that aren't exact. I think that it's probably the PTS sensor that's the problem as the cpu runs much faster than the mechanical movement.
Anything's possible, I won't rule anything out 8-)


I keep having trouble with the mx80 and the alignment. I've got the PTS multiplier value set to 1:1 and that seems to fix the graphics, and mess up the text. If I set to 1:2 that fixes the text and messes up the graphics.


Graphic printout with 1:2 (clearly seems to be missing in between columns) at 120x72

[Linked Image from i.imgur.com]



Graphic printout with 1:1 (120x72) 120dpi horizontally is the mx80 limit. This looks okay without glitches.
[Linked Image from i.imgur.com]
Posted By: Lord Nightmare Re: lx800 self test - 05/16/22 05:27 PM
I'm up to EQAW (in alphabetical order) in the upd7810 databook, fixing the timings in upd7810_table.cpp. I haven't migrated all of these fixes to the upd7803 and 7807 though, although some of them are obviously also wrong there.
Posted By: Lord Nightmare Re: lx800 self test - 05/18/22 04:01 AM
Up to MUL now.
I found 2 bugs in the upd7810_table.cpp file while fixing cycle timings:
1. The first few prefix 40 opcodes are calling the 1-byte &upd7810_device::illegal handler instead of the 2-byte &upd7810_device::illegal2 handler, I don't know what sort of havoc this would cause since it only affects illegal opcodes. Because the opcodes have a prefix byte, they are 2 bytes long, so it should (if I'm understanding this right) call the illegal2 handler.
2. The more serious issue is a probable bug with the prefix 4D opcodes MOV_TXB_A, MOV_TM0_A, MOV_TM1_A, and MOV_ZCM_A which I'm pretty sure (based on the upd7810 instruction set databook) are in the wrong order in the table, causing the wrong opcodes to execute!
EDIT: scratch #2, the documentation conflicts with itself; I suspect the current order of things are correct since otherwise I suspect stuff would work much worse than it already does, if it has to do with serial communications and zero cross timing. Golden Child, correct me if I'm completely wrong here.

LN
Posted By: Vas Crabb Re: lx800 self test - 05/18/22 05:40 AM
Originally Posted by Lord Nightmare
2. The more serious issue is a probable bug with the prefix 4D opcodes MOV_TXB_A, MOV_TM0_A, MOV_TM1_A, and MOV_ZCM_A which I'm pretty sure (based on the upd7810 instruction set databook) are in the wrong order in the table, causing the wrong opcodes to execute!
I think you’re wrong. The SFR codes are in a table on page 21: https://www.cpcwiki.eu/imgs/4/4b/UPD78C11A_Datasheet.pdf
  • TXB = 011000
  • TM0 = 011010
  • TM1 = 011011


This matches the code in MAME.
Posted By: Lord Nightmare Re: lx800 self test - 05/18/22 06:19 AM
Vas, I just figured that out as well, the upd78C10 document conflicts with itself; the diagram on page 93 (11-2 document page) matches what MAME does, while the documentation of the MOV opcode itself shows things the other way, which I'm assuming is just another documentation error like the SSPD thing.
Golden Child might be able to chime in since the printers they're working with probably do use those opcodes.
Posted By: Golden Child Re: lx800 self test - 05/18/22 01:12 PM
I don't know if I can offer any insight on that particular thing.

I was going great on the RX80 and it was all pretty much working except for going off into the weeds and running the carriage completely off to the right. After resetting it, it would work just fine.


I started looking at EOM as a possibility and the datasheet says in figure 6-4 "If LV0 level inversion is enabled and either set LV0 or reset LV0 are enabled, the set or reset will override the inversion. Inversion will only work with LRE1 = 0 and LRE0 = 0. The same concept is true for LV1".

Now if I modify the void upd7810_device::upd7810_write_EOM()

to implement that, it *really* doesn't work...and they also give a diagram at 6-2 of the output control circuit showing LRE0 and LRE1 connecting to the S and R of the Master/Slave FF.
Posted By: Lord Nightmare Re: lx800 self test - 05/19/22 03:09 AM
I figured out the difference between illegal and illegal2 handlers: the logerror print message for the former prints 1 byte for opcode, and the latter prints 2. This means the mistake in the prefix 40 table was effectively completely harmless, just making some error messages in MAME slightly wrong.
© Forums