summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/hp9845_printer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/machine/hp9845_printer.cpp')
-rw-r--r--src/mame/machine/hp9845_printer.cpp971
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();
+ }
+ }
+ }
+}