diff options
Diffstat (limited to 'src/mame/machine/hp9845_printer.cpp')
-rw-r--r-- | src/mame/machine/hp9845_printer.cpp | 971 |
1 files changed, 971 insertions, 0 deletions
diff --git a/src/mame/machine/hp9845_printer.cpp b/src/mame/machine/hp9845_printer.cpp new file mode 100644 index 00000000000..3741b737552 --- /dev/null +++ b/src/mame/machine/hp9845_printer.cpp @@ -0,0 +1,971 @@ +// 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: + <line_no>:<pixels>, where line_no is the line number (counted in + motor steps, see above) and <pixels> is the 560-pixel line. Each + character of <pixels> 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 <octal> [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<typename T> constexpr T BIT_MASK(unsigned n) + { + return (T)1U << n; + } + + template<typename T> void BIT_CLR(T& w , unsigned n) + { + w &= ~BIT_MASK<T>(n); + } + + template<typename T> void BIT_SET(T& w , unsigned n) + { + w |= BIT_MASK<T>(n); + } + + template<typename T> 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<uint8_t>(ATTRS_NEW_BIT); +constexpr uint8_t ATTRS_BIG_MASK = BIT_MASK<uint8_t>(ATTRS_BIG_BIT); +constexpr uint8_t ATTRS_U_L_MASK = BIT_MASK<uint8_t>(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_MEMBER(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 <octal> [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(); + } + } + } +} |