// license:BSD-3-Clause // copyright-holders:F. Ulivi /********************************************************************* hp9845_printer.cpp HP9845 internal printer HLE The internal printer of HP9845-series machines is a thermal printer. It prints by heating the resistors in a fixed line of 560 elements. A stepper motor advances the paper (and in some cases backs it up). A HP nanoprocessor controls the operation of the printer. The NP runs firmware from a 2 kB ROM and it has 256 bytes of RAM. A second 2 kB ROM acts as character generator. Character cells are 7 pixels wide and 12 pixels high. Shape of characters in the character generator is "compressed" in a peculiar way (see patent) so that the 7x12 cell fits in 8 bytes. These 8 bytes are addressed according to a Gray code, not a binary code, to speed up access. Each step of the motor moves the paper by 1/154 of inch. Each line in a normal sized character is 2 steps high. Lines of big sized characters are 3 steps high. The horizontal size of pixels is equal to 2 steps. HLE is necessary because it's very difficult to dump ROMs (they're non-standard HP parts and they're soldered on the PCB). Content of char.gen. was reconstructed by looking at the shape of each character on paper. Thanks to Ansgar Kueckes for letting me run tests "remotely" on his printer. Output of printer: - bitbanger 1: raw dump of all bytes from PPU to printer - bitbanger 2: output of printer pixels. Each line has this form: :, where line_no is the line number (counted in motor steps, see above) and is the 560-pixel line. Each character of is either a space (no dot) or '*'. This stream can be post-processed to produce a bitmapped graphic. Each '*' is to be translated to a 2x2 black square to preserve the 1:1 aspect-ratio of pixels. What's not implemented: - A realistic simulation of printer speed - Form Feed - Handling of page length & top/bottom margins - ESC & l [ST] commands (set vertical size of lines & top margin of paper) References: - US Patent 4,180,854 - HP 09845-93000, sep 81, HP9845 BASIC programming (appendix A) *********************************************************************/ #include "emu.h" #include "hp9845_printer.h" // Debugging #define VERBOSE 0 #include "logmacro.h" // Bit manipulation namespace { template constexpr T BIT_MASK(unsigned n) { return (T)1U << n; } template void BIT_CLR(T& w , unsigned n) { w &= ~BIT_MASK(n); } template void BIT_SET(T& w , unsigned n) { w |= BIT_MASK(n); } template void COPY_BIT(bool bit , T& w , unsigned n) { if (bit) { BIT_SET(w , n); } else { BIT_CLR(w , n); } } } // Character constants constexpr uint8_t CH_BS = 0x08; constexpr uint8_t CH_TAB = 0x09; constexpr uint8_t CH_LF = 0x0a; constexpr uint8_t CH_CR = 0x0d; constexpr uint8_t CH_SH_OUT = 0x0e; constexpr uint8_t CH_SH_IN = 0x0f; constexpr uint8_t CH_ESC = 0x1b; // Bits in m_attrs constexpr unsigned ATTRS_NEW_BIT = 7; // Redefined character constexpr unsigned ATTRS_TAB_BIT = 6; // Tab position constexpr unsigned ATTRS_U_L_BIT = 5; // Underlined constexpr unsigned ATTRS_BIG_BIT = 4; // Big character constexpr uint8_t ATTRS_REDEF_NO_MASK = 0x0f; // Redefined character number constexpr uint8_t ATTRS_NEW_MASK = BIT_MASK(ATTRS_NEW_BIT); constexpr uint8_t ATTRS_BIG_MASK = BIT_MASK(ATTRS_BIG_BIT); constexpr uint8_t ATTRS_U_L_MASK = BIT_MASK(ATTRS_U_L_BIT); // Various timing constants constexpr unsigned MAX_PIXELS_BURNED = 62; // Maximum number of pixels burned in parallel constexpr unsigned USEC_LT_16_PIXELS = 4960; // microseconds to burn < 16 pixels constexpr unsigned USEC_LT_32_PIXELS = 5984; // microseconds to burn < 32 pixels constexpr unsigned USEC_LT_48_PIXELS = 6464; // microseconds to burn < 48 pixels constexpr unsigned USEC_GE_48_PIXELS = 6976; // microseconds to burn >= 48 pixels constexpr unsigned USEC_1_STEP = 3456; // microseconds for 1 motor step // Device type definition DEFINE_DEVICE_TYPE(HP9845_PRINTER, hp9845_printer_device, "hp9845_prt", "HP9845 internal printer") hp9845_printer_device::hp9845_printer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig , HP9845_PRINTER , tag , owner , clock), m_irl_handler(*this), m_flg_handler(*this), m_sts_handler(*this), m_prt_graph_out(*this , "prt_graphic"), m_prt_alpha_out(*this , "prt_alpha"), m_prt_chargen(*this , "prt_chargen") { } ROM_START(hp9845_printer) ROM_REGION(0x800, "prt_chargen", 0) ROM_LOAD("1818-2687.bin", 0, 0x800, CRC(cce64de8) SHA1(1dfabd32f4bdef85f88a514bd1978b80b25a6e80)) ROM_END const tiny_rom_entry *hp9845_printer_device::device_rom_region() const { return ROM_NAME(hp9845_printer); } MACHINE_CONFIG_START(hp9845_printer_device::device_add_mconfig) MCFG_DEVICE_ADD("prt_alpha", BITBANGER, 0) MCFG_DEVICE_ADD("prt_graphic", BITBANGER, 0) MACHINE_CONFIG_END void hp9845_printer_device::device_start() { m_irl_handler.resolve_safe(); m_flg_handler.resolve_safe(); m_sts_handler.resolve_safe(); save_item(NAME(m_display_mode)); save_item(NAME(m_shifted)); save_item(NAME(m_current_u_l)); save_item(NAME(m_current_big)); save_item(NAME(m_ibf)); save_item(NAME(m_inten)); save_item(NAME(m_busy)); save_item(NAME(m_ib)); save_item(NAME(m_pos)); save_item(NAME(m_line)); save_item(NAME(m_attrs)); save_item(NAME(m_redef_count)); save_item(NAME(m_redef_idx)); save_item(NAME(m_redef_chars)); save_item(NAME(m_replace_count)); save_item(NAME(m_redef_buff)); save_item(NAME(m_next_replace)); save_item(NAME(m_rep_str_len)); save_item(NAME(m_rep_str_ptr)); save_item(NAME(m_octal_accum)); save_item(NAME(m_fsm_state)); save_item(NAME(m_cur_line)); m_timer = timer_alloc(0); } void hp9845_printer_device::device_reset() { state_reset(); m_cur_line = 0; m_inten = false; m_ibf = false; m_busy = false; update_flg(); m_sts_handler(true); } void hp9845_printer_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { m_busy = false; update_fsm(); update_flg(); } READ16_MEMBER(hp9845_printer_device::printer_r) { uint16_t res = 0; switch (offset) { case 0: case 1: // Bit 7: Interrupt enabled // Bit 5: 1 // Bit 1: Printer powered (1) // Bit 0: Interrupt pending if (m_inten) { BIT_SET(res , 7); } BIT_SET(res , 5); BIT_SET(res , 1); if (m_inten && !m_ibf && !m_busy) { BIT_SET(res , 0); } break; default: break; } return res; } WRITE16_MEMBER(hp9845_printer_device::printer_w) { switch (offset) { case 0: // New character m_ib = (uint8_t)data; m_ibf = true; update_fsm(); update_flg(); break; case 1: // Control word m_inten = BIT(data , 7); update_flg(); break; default: break; } } void hp9845_printer_device::state_reset() { m_display_mode = false; m_shifted = false; m_current_u_l = false; m_current_big = false; memset(m_attrs , 0 , sizeof(m_attrs)); memset(m_redef_buff, 0, sizeof(m_redef_buff)); m_redef_count = 0; m_replace_count = 0; m_next_replace = REDEF_BUFF_LEN - 1; m_rep_str_len = 0; m_rep_str_ptr = 0; start_new_line(); m_fsm_state = FSM_NORMAL_TEXT; } void hp9845_printer_device::insert_char(uint8_t ch) { if (m_pos == 80) { crlf(); } COPY_BIT(m_current_big , m_attrs[ m_pos ] , ATTRS_BIG_BIT); COPY_BIT(m_current_u_l , m_attrs[ m_pos ] , ATTRS_U_L_BIT); if (ch == '_') { BIT_SET(m_attrs[ m_pos ] , ATTRS_U_L_BIT); } else { m_line[ m_pos ] = ch; // Check for redefined characters unsigned redef_idx; if (is_ch_redef(ch , redef_idx)) { m_attrs[ m_pos ] = (m_attrs[ m_pos ] & ~ATTRS_REDEF_NO_MASK) | (uint8_t)redef_idx | ATTRS_NEW_MASK; } else { BIT_CLR(m_attrs[ m_pos ] , ATTRS_NEW_BIT); } } m_pos++; } void hp9845_printer_device::start_new_line() { m_pos = 0; memset(m_line, ' ', sizeof(m_line)); for (auto& x : m_attrs) { x &= ~(ATTRS_NEW_MASK | ATTRS_BIG_MASK | ATTRS_U_L_MASK); } } uint8_t hp9845_printer_device::get_ch_matrix_line(const uint8_t *matrix_base , unsigned line_no , const uint8_t *seq) { uint8_t res = 0; switch (line_no) { case 0: { uint8_t b0 = matrix_base[ seq[ 0 ] ]; if (BIT(b0 , 0)) { res = b0 >> 1; } } break; case 1: { uint8_t b3 = matrix_base[ seq[ 3 ] ]; if (BIT(b3 , 0)) { res = matrix_base[ seq[ 0 ] ] >> 1; } } break; case 2: { uint8_t b1 = matrix_base[ seq[ 1 ] ]; if (!BIT(b1 , 0)) { res = b1 >> 1; } } break; case 3: { uint8_t b2 = matrix_base[ seq[ 2 ] ]; if (!BIT(b2 , 0)) { res = b2 >> 1; } } break; case 4: case 5: case 6: case 7: case 8: res = matrix_base[ seq[ line_no - 1 ] ] >> 1; break; case 9: { uint8_t b1 = matrix_base[ seq[ 1 ] ]; if (BIT(b1 , 0)) { res = b1 >> 1; } else { uint8_t b5 = matrix_base[ seq[ 5 ] ]; if (BIT(b5 , 0)) { res = matrix_base[ seq[ 0 ] ] >> 1; } } } break; case 10: { uint8_t b2 = matrix_base[ seq[ 2 ] ]; if (BIT(b2 , 0)) { res = b2 >> 1; } else { uint8_t b6 = matrix_base[ seq[ 6 ] ]; if (BIT(b6 , 0)) { res = matrix_base[ seq[ 0 ] ] >> 1; } } } break; case 11: { uint8_t b7 = matrix_base[ seq[ 7 ] ]; if (BIT(b7 , 0)) { res = matrix_base[ seq[ 0 ] ] >> 1; } } break; } return res; } unsigned hp9845_printer_device::print_560_pixels(unsigned line_no , const uint8_t *pixels) { std::ostringstream buff; buff << line_no; buff << ':'; unsigned pixel_count = 0; for (unsigned i = 0; i < 70; i++) { uint8_t pop_count = pixels[ i ]; pop_count = (pop_count & 0x55) + ((pop_count >> 1) & 0x55); pop_count = (pop_count & 0x33) + ((pop_count >> 2) & 0x33); pop_count = (pop_count & 0x0f) + ((pop_count >> 4) & 0x0f); pixel_count += pop_count; for (uint8_t mask = 0x80; mask; mask >>= 1) { buff << ((mask & pixels[ i ]) ? '*' : ' '); } } if (pixel_count) { buff << '\n'; std::string out{buff.str()}; for (auto c : out) { m_prt_graph_out->output(c); } } LOG("pc=%u\n" , pixel_count); return pixel_count; } static const uint8_t linear_seq[] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 }; static const uint8_t gray_seq[] = { 0 , 1 , 3 , 2 , 6 , 7 , 5 , 4 }; uint8_t hp9845_printer_device::get_ch_pixels(uint8_t ch , uint8_t attrs , unsigned matrix_line) const { uint8_t pixels; bool new_ch = BIT(attrs , ATTRS_NEW_BIT); if (new_ch) { const uint8_t *matrix = &m_redef_buff[ (attrs & ATTRS_REDEF_NO_MASK) * 8 ]; pixels = get_ch_matrix_line(matrix, matrix_line, linear_seq); } else { const uint8_t *matrix = &m_prt_chargen[ ch * 8 ]; pixels = get_ch_matrix_line(matrix, matrix_line, gray_seq); } return pixels; } attotime hp9845_printer_device::burn_time(unsigned pixel_count) { if (pixel_count == 0) { return attotime::zero; } unsigned full_burns = pixel_count / MAX_PIXELS_BURNED; unsigned rem_pixels = pixel_count - full_burns * MAX_PIXELS_BURNED; unsigned usec = full_burns * USEC_GE_48_PIXELS; if (rem_pixels < 16) { usec += USEC_LT_16_PIXELS; } else if (rem_pixels < 32) { usec += USEC_LT_32_PIXELS; } else if (rem_pixels < 48) { usec += USEC_LT_48_PIXELS; } else { usec += USEC_GE_48_PIXELS; } return attotime::from_usec(usec); } void hp9845_printer_device::print_line() { attotime print_time; bool any_big = false; // Normal-sized character range is [0..22], every other line // Big-sized characters range is [-8..22], one line every 3 for (int line_off = -8; line_off < 23; line_off++) { if ((line_off + int(m_cur_line)) < 0) { continue; } bool norm_line = line_off >= 0 && line_off % 2 == 0; bool big_line = (line_off + 8) % 3 == 0; if (norm_line || big_line) { uint8_t pixel_line[ 70 ]; memset(pixel_line, 0, sizeof(pixel_line)); unsigned pixel_line_idx = 0; uint8_t pixel_line_mask = 0x80; for (unsigned pos = 0; pos < m_pos; pos++) { uint8_t ch = m_line[ pos ]; uint8_t attrs = m_attrs[ pos ]; uint8_t pixels = 0; if (line_off == 22 && BIT(attrs , ATTRS_U_L_BIT)) { // Underline pixels = 0x7f; } else if (BIT(attrs , ATTRS_BIG_BIT) && big_line) { any_big = true; unsigned matrix_line = (line_off + 8) / 3; pixels = get_ch_pixels(ch, attrs, matrix_line); } else if (!BIT(attrs , ATTRS_BIG_BIT) && norm_line) { unsigned matrix_line = line_off / 2; pixels = get_ch_pixels(ch, attrs, matrix_line); } for (uint8_t mask = 0x40; mask; mask >>= 1) { if (pixels & mask) { pixel_line[ pixel_line_idx ] |= pixel_line_mask; } pixel_line_mask >>= 1; if (pixel_line_mask == 0) { pixel_line_mask = 0x80; pixel_line_idx++; } } } unsigned pixel_count = print_560_pixels(unsigned(line_off + int(m_cur_line)) , pixel_line); print_time += burn_time(pixel_count); } } m_cur_line += 24; // Computing an accurate printing time is a lot more complex than this. // This should be a reasonable estimate. if (any_big) { print_time += attotime::from_usec(USEC_1_STEP * 32); } else { print_time += attotime::from_usec(USEC_1_STEP * 24); } LOG("A time=%.6f\n" , print_time.as_double()); m_timer->adjust(print_time); m_busy = true; } void hp9845_printer_device::print_graphic_line() { unsigned pixel_count = print_560_pixels(m_cur_line , m_line); attotime print_time{burn_time(pixel_count)}; print_time += attotime::from_usec(USEC_1_STEP * 2); m_cur_line += 2; LOG("G time=%.6f\n" , print_time.as_double()); m_timer->adjust(print_time); m_busy = true; } void hp9845_printer_device::crlf() { print_line(); start_new_line(); } void hp9845_printer_device::set_tab() { if (m_pos > 0) { BIT_SET(m_attrs[ m_pos - 1 ] , ATTRS_TAB_BIT); } } void hp9845_printer_device::clear_tabs() { for (auto& x : m_attrs) { BIT_CLR(x , ATTRS_TAB_BIT); } } void hp9845_printer_device::move_to_next_tab() { for (unsigned i = m_pos; i < 80; i++) { if (BIT(m_attrs[ i ] , ATTRS_TAB_BIT)) { m_pos = i + 1; return; } } crlf(); } void hp9845_printer_device::update_flg() { m_flg_handler(!m_ibf && !m_busy); m_irl_handler(!m_ibf && !m_busy && m_inten); } bool hp9845_printer_device::is_ch_redef(uint8_t ch , unsigned& redef_number) const { for (unsigned i = 0; i < m_redef_count; i++) { if (m_redef_chars[ i ] == ch) { redef_number = i; return true; } } return false; } uint8_t hp9845_printer_device::allocate_ch_redef(uint8_t& idx) { if (m_redef_count >= REDEF_CH_COUNT) { idx = REDEF_CH_COUNT - 1; return true; } else if (m_next_replace < 8) { return false; } else { uint8_t max_idx = (m_next_replace - 8) / 8; if (max_idx < m_redef_count) { idx = max_idx; } else { idx = m_redef_count++; } return true; } } bool hp9845_printer_device::is_ch_replaced(uint8_t ch , uint8_t& len , uint8_t& ptr) const { uint8_t idx = REDEF_BUFF_LEN - 1; for (unsigned i = 0; i < m_replace_count; i++) { if (m_redef_buff[ idx-- ] == ch) { len = m_redef_buff[ idx-- ]; ptr = idx; return true; } else { idx -= (m_redef_buff[ idx ] + 1); } } return false; } uint8_t hp9845_printer_device::free_redef_space() const { return m_next_replace - m_redef_count * 8; } uint8_t hp9845_printer_device::apply_shifting(uint8_t ch) const { if ((ch & 0x60) == 0 || !m_shifted) { return ch; } else { return ch | 0x80; } } bool hp9845_printer_device::parse_octal(uint8_t ch) { if (ch >= '0' && ch <= '7') { m_octal_accum = (m_octal_accum * 8) + (ch - '0'); return true; } else { return false; } } bool hp9845_printer_device::parse_ch(uint8_t ch) { bool consume = true; switch (m_fsm_state) { case FSM_NORMAL_TEXT: if (m_display_mode) { insert_char(apply_shifting(ch)); if (ch == CH_CR) { crlf(); } else if (ch == CH_ESC) { m_fsm_state = FSM_WAIT_ESC_Z; } } else { switch (ch) { case CH_BS: if (m_pos > 0) { m_pos--; } break; case CH_TAB: move_to_next_tab(); break; case CH_LF: { auto save_pos = m_pos; crlf(); m_pos = save_pos; } break; case CH_CR: m_fsm_state = FSM_AFTER_CR; break; case CH_SH_OUT: m_shifted = true; break; case CH_SH_IN: m_shifted = false; break; case CH_ESC: m_fsm_state = FSM_AFTER_ESC; break; default: if ((ch & 0xe4) == 0x80) { m_current_u_l = false; } else if ((ch & 0xe4) == 0x84) { m_current_u_l = true; } else if ((ch & 0x60) != 0) { insert_char(apply_shifting(ch)); } break; } } break; case FSM_AFTER_CR: if (ch == CH_LF) { // CR + LF sequence crlf(); } else { // CR + something not LF auto save_cur_line = m_cur_line; crlf(); m_cur_line = save_cur_line; // Ch is pushed back for next iteration of FSM consume = false; } m_fsm_state = FSM_NORMAL_TEXT; break; case FSM_AFTER_ESC: m_fsm_state = FSM_NORMAL_TEXT; switch (ch) { case '1': set_tab(); break; case '3': clear_tabs(); break; case 'E': LOG("State reset\n"); state_reset(); break; case 'Y': m_display_mode = true; break; case '&': m_fsm_state = FSM_AFTER_ESC_AMP; break; case '?': if (m_pos > 0) { crlf(); } m_pos = 0; m_fsm_state = FSM_COLLECT_ESC_QMARK; break; default: // Unknown ESC sequence break; } break; case FSM_AFTER_ESC_AMP: switch (ch) { case 'd': m_fsm_state = FSM_AFTER_ESC_AMP_D; break; case 'k': m_fsm_state = FSM_AFTER_ESC_AMP_K; break; case 'l': m_fsm_state = FSM_AFTER_ESC_AMP_L; m_octal_accum = 0; break; case 'n': m_fsm_state = FSM_AFTER_ESC_AMP_N; m_octal_accum = 0; break; case 'o': m_fsm_state = FSM_AFTER_ESC_AMP_O; m_octal_accum = 0; break; default: // Unknown ESC & sequence m_fsm_state = FSM_NORMAL_TEXT; break; } break; case FSM_COLLECT_ESC_QMARK: m_line[ m_pos++ ] = ch; if (m_pos == 70) { print_graphic_line(); start_new_line(); m_fsm_state = FSM_NORMAL_TEXT; } break; case FSM_AFTER_ESC_AMP_K: switch (ch) { case '0': case '1': m_octal_accum = ch != '0'; m_fsm_state = FSM_AFTER_ESC_AMP_K_01; break; default: m_fsm_state = FSM_NORMAL_TEXT; break; } break; case FSM_AFTER_ESC_AMP_K_01: if (ch == 'S') { m_current_big = m_octal_accum; } m_fsm_state = FSM_NORMAL_TEXT; break; case FSM_AFTER_ESC_AMP_D: switch (ch) { case 'D': case 'E': case 'F': case 'G': case 'L': case 'M': case 'N': case 'O': m_current_u_l = true; break; case '@': case 'A': case 'B': case 'C': case 'H': case 'I': case 'J': case 'K': m_current_u_l = false; break; default: break; } m_fsm_state = FSM_NORMAL_TEXT; break; case FSM_AFTER_ESC_AMP_N: if (!parse_octal(ch)) { if (ch == 'c') { unsigned dummy; if (is_ch_redef(m_octal_accum, dummy) || !allocate_ch_redef(m_redef_idx)) { // Character redefined twice or space exhausted m_redef_idx = 0xff; } LOG("Redef ch %02x @%u\n" , m_octal_accum , m_redef_idx); if (m_redef_idx < REDEF_CH_COUNT) { m_redef_chars[ m_redef_idx ] = m_octal_accum; } m_octal_accum = 0; m_fsm_state = FSM_AFTER_ESC_AMP_N_C; } else { m_fsm_state = FSM_NORMAL_TEXT; } } break; case FSM_AFTER_ESC_AMP_O: if (!parse_octal(ch)) { if (ch == 'c') { uint8_t dummy1; uint8_t dummy2; if (is_ch_replaced(m_octal_accum, dummy1, dummy2) || free_redef_space() < 2) { // Character replaced twice or space exhausted m_redef_idx = 0xff; } else { m_redef_idx = m_next_replace; m_redef_buff[ m_redef_idx-- ] = m_octal_accum; m_redef_buff[ m_redef_idx ] = 0; m_next_replace -= 2; m_replace_count++; } m_octal_accum = 0; m_fsm_state = FSM_AFTER_ESC_AMP_O_C; } else { m_fsm_state = FSM_NORMAL_TEXT; } } break; case FSM_AFTER_ESC_AMP_O_C: if (!parse_octal(ch)) { if (ch == 'L') { if (m_redef_idx < REDEF_BUFF_LEN) { uint8_t max_len = std::min(m_octal_accum , free_redef_space()); m_next_replace -= max_len; m_redef_buff[ m_redef_idx-- ] = max_len; LOG("Repl ch %02x with str @%u, len=%u\n" , m_redef_buff[ m_redef_idx + 2 ] , m_redef_idx , max_len); } if (m_octal_accum) { m_fsm_state = FSM_AFTER_ESC_AMP_O_C_L; } else { m_fsm_state = FSM_NORMAL_TEXT; } } else { m_fsm_state = FSM_NORMAL_TEXT; } } break; case FSM_AFTER_ESC_AMP_O_C_L: if (m_redef_idx < REDEF_BUFF_LEN && m_redef_idx >= m_next_replace) { m_redef_buff[ m_redef_idx-- ] = ch; } if (--m_octal_accum == 0) { m_fsm_state = FSM_NORMAL_TEXT; } break; case FSM_AFTER_ESC_AMP_L: if (!parse_octal(ch)) { // TODO: ESC & l [ST] m_fsm_state = FSM_NORMAL_TEXT; } break; case FSM_AFTER_ESC_AMP_N_C: if (!parse_octal(ch)) { if (ch >= 'p' && ch <= 'w') { if (m_redef_idx < REDEF_CH_COUNT) { m_redef_buff[ m_redef_idx * 8 + ch - 'p' ] = m_octal_accum; } m_octal_accum = 0; } else if (ch >= 'P' && ch <= 'W') { if (m_redef_idx < REDEF_CH_COUNT) { m_redef_buff[ m_redef_idx * 8 + ch - 'P' ] = m_octal_accum; } m_fsm_state = FSM_NORMAL_TEXT; } else { m_fsm_state = FSM_NORMAL_TEXT; } } break; case FSM_WAIT_ESC_Z: insert_char(ch); if (ch == 'Z') { m_display_mode = false; m_fsm_state = FSM_NORMAL_TEXT; } else if (ch != CH_ESC) { m_fsm_state = FSM_NORMAL_TEXT; } break; } return consume; } void hp9845_printer_device::update_fsm() { while (!m_busy && m_ibf) { bool consume = false; if (m_rep_str_len != 0) { consume = parse_ch(m_redef_buff[ m_rep_str_ptr ]); } else if ((m_fsm_state == FSM_NORMAL_TEXT || m_fsm_state == FSM_AFTER_CR || m_fsm_state == FSM_COLLECT_ESC_QMARK) && is_ch_replaced(apply_shifting(m_ib), m_rep_str_len, m_rep_str_ptr)) { LOG("Subst ch %02x with str @%u (len = %u)\n" , m_ib , m_rep_str_ptr , m_rep_str_len); if (m_rep_str_len == 0) { consume = true; } } else { consume = parse_ch(m_ib); } if (consume) { if (m_rep_str_len) { m_rep_str_len--; m_rep_str_ptr--; } if (m_rep_str_len == 0) { m_prt_alpha_out->output(m_ib); m_ibf = false; update_flg(); } } } }