summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/bitmap_printer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/bitmap_printer.cpp')
-rw-r--r--src/devices/machine/bitmap_printer.cpp484
1 files changed, 484 insertions, 0 deletions
diff --git a/src/devices/machine/bitmap_printer.cpp b/src/devices/machine/bitmap_printer.cpp
new file mode 100644
index 00000000000..ca552a14694
--- /dev/null
+++ b/src/devices/machine/bitmap_printer.cpp
@@ -0,0 +1,484 @@
+// license:BSD-3-Clause
+// copyright-holders: Golden Child
+/*********************************************************************
+
+ bitmap_printer.cpp
+
+ Implementation of Bitmap Printer
+
+**********************************************************************/
+
+#include "emu.h"
+#include "video.h"
+#include "screen.h"
+#include "emuopts.h"
+#include "fileio.h"
+#include "png.h"
+#include "bitmap_printer.h"
+#include "corestr.h"
+
+/***************************************************************************
+ DEVICE DECLARATION
+***************************************************************************/
+
+DEFINE_DEVICE_TYPE(BITMAP_PRINTER, bitmap_printer_device, "bitmap_printer", "Bitmap Printer Device")
+
+//**************************************************************************
+// INPUT PORTS
+//**************************************************************************
+
+#define PORT_ADJUSTER_16MASK(_default, _name) \
+ configurer.field_alloc(IPT_ADJUSTER, (_default), 0xffff, (_name)); \
+ configurer.field_set_min_max(0, 100);
+
+INPUT_PORTS_START(bitmap_printer)
+ PORT_START("DRAWMARKS")
+ PORT_CONFNAME(0x3, 0x02, "Draw Inch Marks")
+ PORT_CONFSETTING(0x0, "Off")
+ PORT_CONFSETTING(0x1, "with marks")
+ PORT_CONFSETTING(0x2, "with numbers")
+
+ PORT_START("TOPMARGIN")
+ PORT_ADJUSTER_16MASK(18, "Printer Top Margin")
+ PORT_MINMAX(0,500)
+
+ PORT_START("BOTTOMMARGIN")
+ PORT_ADJUSTER_16MASK(18, "Printer Bottom Margin")
+ PORT_MINMAX(0,500)
+
+INPUT_PORTS_END
+
+
+ioport_constructor bitmap_printer_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(bitmap_printer);
+}
+
+//-------------------------------------------------
+// device_add_mconfig - add device configuration
+//-------------------------------------------------
+
+void bitmap_printer_device::device_add_mconfig(machine_config &config)
+{
+ // video hardware (simulates paper)
+ screen_device &screen(SCREEN(config, m_screen, SCREEN_TYPE_RASTER));
+ screen.set_refresh_hz(60);
+ screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
+ screen.set_size(m_paper_width, PAPER_SCREEN_HEIGHT);
+ screen.set_visarea(0, m_paper_width - 1, 0, PAPER_SCREEN_HEIGHT - 1);
+ screen.set_screen_update(FUNC(bitmap_printer_device::screen_update_bitmap));
+
+ STEPPER(config, m_pf_stepper, (uint8_t) 0xa);
+ STEPPER(config, m_cr_stepper, (uint8_t) 0xa);
+}
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+bitmap_printer_device::bitmap_printer_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, type, tag, owner, clock),
+ m_cr_direction(1),
+ m_xpos(0),
+ m_ypos(0),
+ m_screen(*this, "screen"),
+ m_pf_stepper(*this, "pf_stepper"),
+ m_cr_stepper(*this, "cr_stepper"),
+ m_top_margin_ioport(*this, "TOPMARGIN"),
+ m_bottom_margin_ioport(*this, "BOTTOMMARGIN"),
+ m_draw_marks_ioport(*this, "DRAWMARKS"),
+ m_printhead_color(0x00EE00),
+ m_printhead_bordercolor(0xEE0000),
+ m_printhead_bordersize(2),
+ m_printhead_xsize(10),
+ m_printhead_ysize(20),
+ m_page_dirty(0),
+ m_paper_width(0),
+ m_paper_height(0),
+ m_hdpi(0),
+ m_vdpi(0),
+ m_clear_pos(0),
+ m_newpage_flag(0),
+ m_led_state{0,1,1,1,1},
+ m_num_leds(1),
+ m_pf_stepper_ratio0(1),
+ m_pf_stepper_ratio1(1),
+ m_cr_stepper_ratio0(1),
+ m_cr_stepper_ratio1(1)
+{
+}
+
+bitmap_printer_device::bitmap_printer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ bitmap_printer_device(mconfig, BITMAP_PRINTER, tag, owner, clock)
+{
+}
+
+bitmap_printer_device::bitmap_printer_device(const machine_config &mconfig, const char *tag, device_t *owner, int paper_width, int paper_height, int hdpi, int vdpi) :
+ bitmap_printer_device(mconfig, tag, owner, u32(0))
+{
+ m_paper_width = paper_width;
+ m_paper_height = paper_height;
+ m_hdpi = hdpi;
+ m_vdpi = vdpi;
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void bitmap_printer_device::device_start()
+{
+ m_page_bitmap.allocate(m_paper_width, m_paper_height);
+ m_page_bitmap.fill(0xffffff); // Start with a white piece of paper
+
+ save_item(NAME(m_page_bitmap));
+ save_item(NAME(m_xpos));
+ save_item(NAME(m_ypos));
+ save_item(NAME(m_cr_direction));
+ save_item(NAME(m_pf_stepper_ratio0));
+ save_item(NAME(m_pf_stepper_ratio1));
+ save_item(NAME(m_cr_stepper_ratio0));
+ save_item(NAME(m_cr_stepper_ratio1));
+ save_item(NAME(m_printhead_color));
+ save_item(NAME(m_printhead_bordercolor));
+ save_item(NAME(m_printhead_bordersize));
+ save_item(NAME(m_printhead_xsize));
+ save_item(NAME(m_printhead_ysize));
+ save_item(NAME(m_page_dirty));
+ save_item(NAME(m_paper_width));
+ save_item(NAME(m_paper_height));
+ save_item(NAME(m_hdpi));
+ save_item(NAME(m_vdpi));
+ save_item(NAME(m_clear_pos));
+ save_item(NAME(m_newpage_flag));
+}
+
+void bitmap_printer_device::device_reset_after_children()
+{
+ m_ypos = get_top_margin();
+}
+
+void bitmap_printer_device::device_reset()
+{
+}
+
+//-------------------------------------------------
+// SCREEN UPDATE FUNCTIONS
+//-------------------------------------------------
+
+int bitmap_printer_device::calc_scroll_y(bitmap_rgb32& bitmap)
+{
+ return bitmap.height() - m_distfrombottom - m_ypos;
+}
+
+uint32_t bitmap_printer_device::screen_update_bitmap(screen_device &screen,
+ bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ static constexpr u32 top_edge_color = 0xEEE8AA;
+ static constexpr u32 bottom_edge_color = 0xEE8844;
+ static constexpr u32 coverup_color = 0xDDDDDD;
+
+ int scrolly = calc_scroll_y(bitmap);
+
+ copyscrollbitmap(bitmap, m_page_bitmap, 0, nullptr, 1, &scrolly, cliprect);
+
+ // draw a line on the very top of the top edge of page
+ bitmap.plot_box(0, bitmap.height() - m_distfrombottom - m_ypos, m_paper_width, 2, top_edge_color);
+ // draw a line on the bottom edge of page
+ bitmap.plot_box(0, bitmap.height() - m_distfrombottom - m_ypos + m_paper_height, m_paper_width, 2, bottom_edge_color);
+ // cover up visible parts of current page at the bottom
+ bitmap.plot_box(0, bitmap.height() - m_distfrombottom - m_ypos + m_paper_height + 2, m_paper_width, m_distfrombottom, coverup_color);
+
+ draw_printhead(bitmap, std::max(m_xpos, 0) , bitmap.height() - m_distfrombottom);
+
+ draw_inch_marks(bitmap);
+
+ return 0;
+}
+
+//-------------------------------------------------
+// BITMAP CLEARING FUNCTIONS
+//-------------------------------------------------
+
+void bitmap_printer_device::clear_to_pos(int to_line, u32 color)
+{
+ int from_line = m_clear_pos;
+ to_line = std::min(m_page_bitmap.height(), to_line);
+ if (to_line >= from_line)
+ {
+ bitmap_clear_band(m_page_bitmap, from_line, to_line, color);
+ }
+ m_clear_pos = std::max(m_clear_pos, to_line + 1);
+}
+
+void bitmap_printer_device::bitmap_clear_band(int from_line, int to_line, u32 color)
+{
+ bitmap_clear_band(m_page_bitmap, from_line, to_line, color);
+}
+
+void bitmap_printer_device::bitmap_clear_band(bitmap_rgb32 &bitmap, int from_line, int to_line, u32 color)
+{
+ bitmap.plot_box(0, from_line, m_paper_width, to_line - from_line + 1, color);
+}
+
+//-------------------------------------------------
+// PRINTHEAD FUNCTIONS
+//-------------------------------------------------
+
+void bitmap_printer_device::set_printhead_color(int headcolor, int bordcolor)
+{
+ m_printhead_color = headcolor;
+ m_printhead_bordercolor = bordcolor;
+}
+
+void bitmap_printer_device::set_printhead_size(int xsize, int ysize, int bordersize)
+{
+ m_printhead_xsize = xsize;
+ m_printhead_ysize = ysize;
+ m_printhead_bordersize = bordersize;
+}
+
+void bitmap_printer_device::set_led_state(int led, int value)
+{
+ m_led_state[led] = value;
+ m_num_leds = std::max(m_num_leds, led);
+}
+
+void bitmap_printer_device::setheadpos(int x, int y)
+{
+ if (m_xpos != x)
+ {
+ m_newpage_flag = 0;
+ }
+ m_xpos = x;
+ m_ypos = y;
+}
+
+u32 bitmap_printer_device::dimcolor(u32 incolor, int factor)
+{
+ return (((incolor & 0xff0000) >> 16) / factor << 16) |
+ (((incolor & 0xff00) >> 8) / factor << 8) |
+ (((incolor & 0xff) >> 0) / factor);
+}
+
+void bitmap_printer_device::draw_printhead(bitmap_rgb32 &bitmap, int x, int y)
+{
+ int bordx = m_printhead_bordersize;
+ int bordy = m_printhead_bordersize;
+ int offy = 9 + bordy;
+ int sizex = m_printhead_xsize;
+ int sizey = m_printhead_ysize;
+ bitmap.plot_box(x - sizex / 2- bordx, y + offy - bordy, sizex + 2 * bordx, sizey + bordy * 2,
+ m_led_state[0] ? m_printhead_bordercolor : dimcolor(m_printhead_bordercolor, 4));
+
+ for (int i = 1; i <= m_num_leds; i++)
+ bitmap.plot_box(x - sizex / 2, y + offy + ((i -1) * sizey / m_num_leds), sizex,
+ ((i+1) * sizey / m_num_leds) - (i * sizey / m_num_leds),
+ m_led_state[i] ? m_printhead_color : dimcolor(m_printhead_color, 4));
+}
+
+//-------------------------------------------------
+// DRAW INCH MARKS AND NUMBERS
+//-------------------------------------------------
+
+void bitmap_printer_device::draw7seg(u8 data, bool is_digit, int x0, int y0, int width, int height, int thick, bitmap_rgb32 &bitmap, u32 color, u32 erasecolor)
+{
+ // pass nonzero erasecolor to erase blank segments
+ const u8 pat[] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 };
+ u8 seg = is_digit ? pat[data & 0xf] : data;
+
+ if (BIT(seg,0) || erasecolor) bitmap.plot_box(x0, y0, width, thick, BIT(seg,0) ? color : erasecolor);
+ if (BIT(seg,1) || erasecolor) bitmap.plot_box(x0+width, y0+thick, thick, height, BIT(seg,1) ? color : erasecolor);
+ if (BIT(seg,2) || erasecolor) bitmap.plot_box(x0+width, y0+2*thick+height, thick, height, BIT(seg,2) ? color : erasecolor);
+ if (BIT(seg,3) || erasecolor) bitmap.plot_box(x0, y0+2*thick+2*height, width, thick, BIT(seg,3) ? color : erasecolor);
+ if (BIT(seg,4) || erasecolor) bitmap.plot_box(x0-thick, y0+2*thick+height, thick, height, BIT(seg,4) ? color : erasecolor);
+ if (BIT(seg,5) || erasecolor) bitmap.plot_box(x0-thick, y0+thick, thick, height, BIT(seg,5) ? color : erasecolor);
+ if (BIT(seg,6) || erasecolor) bitmap.plot_box(x0, y0+thick+height, width, thick, BIT(seg,6) ? color : erasecolor);
+ if (BIT(seg,7) || erasecolor) bitmap.plot_box(x0+width+thick, y0+2*thick+2*height, thick, thick, BIT(seg,7) ? color : erasecolor); // draw dot
+}
+
+void bitmap_printer_device::draw_number(int number, int x, int y, bitmap_rgb32& bitmap)
+{
+ std::string s(std::to_string(number));
+
+ int width = std::max(m_hdpi / 15, 1); // 1/10 of inch
+ int height = std::max(m_vdpi / 30, 1);
+ int thick = std::max(m_vdpi / 72, 1);
+
+ for (int i = s.length() - 1; i >= 0; i--)
+ draw7seg( s.at(i) - 0x30, true,
+ x + ( i - s.length()) * (3 * width) - (width), y + height * 3 / 2,
+ width, height, thick, bitmap, 0x000000, 0);
+}
+
+void bitmap_printer_device::draw_inch_marks(bitmap_rgb32& bitmap)
+{
+ static constexpr u32 dark_grey_color = 0x202020;
+ static constexpr u32 light_grey_color = 0xc0c0c0;
+
+ int drawmarks = m_draw_marks_ioport->read();
+ if (!drawmarks) return;
+
+ for (int i = 0; i < m_vdpi * 11; i += m_vdpi / 4)
+ {
+ int adj_i = i + calc_scroll_y(bitmap) % m_paper_height;
+ int barbase = m_vdpi / 6;
+ int barwidth = ((i % m_vdpi) == 0) ? barbase * 2 : barbase;
+ int barcolor = ((i % m_vdpi) == 0) ? dark_grey_color : light_grey_color;
+ if (adj_i < bitmap.height())
+ {
+ bitmap.plot_box(bitmap.width() - 1 - barwidth, adj_i, barwidth, 1, barcolor);
+ if ((i % m_vdpi) == 0)
+ {
+ if (drawmarks & 2)
+ draw_number(i / m_vdpi, bitmap.width(), adj_i, bitmap);
+ }
+ }
+ }
+}
+
+//-------------------------------------------------
+// DRAW PIXEL FUNCTIONS
+//-------------------------------------------------
+
+void bitmap_printer_device::draw_pixel(int x, int y, int pixelval)
+{
+ if (y >= m_page_bitmap.height()) y = m_page_bitmap.height() - 1;
+ if (x >= m_page_bitmap.width()) x = m_page_bitmap.width() - 1;
+
+ m_page_bitmap.pix(y, x) = pixelval;
+
+ m_page_dirty = 1;
+};
+
+int bitmap_printer_device::get_pixel(int x, int y)
+{
+ if (y >= m_page_bitmap.height()) y = m_page_bitmap.height() - 1;
+ if (x >= m_page_bitmap.width()) x = m_page_bitmap.width() - 1;
+
+ return m_page_bitmap.pix(y, x);
+};
+
+unsigned int& bitmap_printer_device::pix(int y, int x) // reversed y x
+{
+ if (y >= m_page_bitmap.height()) y = m_page_bitmap.height() - 1;
+ if (x >= m_page_bitmap.width()) x = m_page_bitmap.width() - 1;
+
+ return m_page_bitmap.pix(y,x);
+};
+
+//-------------------------------------------------
+// WRITE SNAPSHOT TO FILE
+//-------------------------------------------------
+
+void bitmap_printer_device::write_snapshot_to_file()
+{
+ machine().popmessage("writing printer snapshot");
+
+ emu_file file(machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ std::error_condition const filerr = machine().video().open_next(file, "png");
+
+ if (!filerr)
+ {
+ static const rgb_t png_palette[] = { rgb_t::white(), rgb_t::black() };
+
+ // save the paper into a png
+ util::png_write_bitmap(file, nullptr, m_page_bitmap, 2, png_palette);
+ }
+}
+
+//-------------------------------------------------
+// STEPPER AND MARGIN FUNCTIONS
+//-------------------------------------------------
+
+int bitmap_printer_device::get_top_margin() { return m_top_margin_ioport->read(); }
+int bitmap_printer_device::get_bottom_margin() { return m_bottom_margin_ioport->read(); }
+
+bool bitmap_printer_device::check_new_page()
+{
+ bool retval = false;
+
+ // idea here is that you update the position, then check the page, this will do the saving of the page
+ // if this routine returns true, means there's a new page and you should clear the yposition
+ if (m_newpage_flag == 1)
+ {
+ // if you change m_ypos you have to change the stepper abs position too
+ m_ypos = get_top_margin(); // lock to the top of page until we seek horizontally
+ m_pf_stepper->set_absolute_position(get_top_margin() / m_pf_stepper_ratio0 * m_pf_stepper_ratio1);
+ }
+
+ // If we are at the bottom of the page we will
+ // write the page to a file, then erase the top part of the page
+ // so we can still see the last page printed.
+ if (m_ypos > m_page_bitmap.height() - 1 - get_bottom_margin())
+ {
+ // clear paper to bottom from current position
+ clear_to_pos(m_paper_height - 1, rgb_t::white());
+
+ // save a snapshot
+ write_snapshot_to_file();
+
+ m_newpage_flag = 1;
+
+ // clear page down to visible area, starting from the top of page
+ m_clear_pos = 0;
+ clear_to_pos(m_paper_height - 1 - PAPER_SCREEN_HEIGHT),
+
+ m_ypos = get_top_margin(); // lock to the top of page until we seek horizontally
+ m_pf_stepper->set_absolute_position(get_top_margin() / m_pf_stepper_ratio0 * m_pf_stepper_ratio1);
+ retval = true;
+ }
+ else
+ {
+ clear_to_pos ( m_ypos + m_distfrombottom);
+ }
+ return retval;
+}
+
+int bitmap_printer_device::update_stepper_delta(stepper_device * stepper, uint8_t pattern)
+{
+ int lastpos = stepper->get_absolute_position();
+ stepper->update(pattern);
+ int delta = stepper->get_absolute_position() - lastpos;
+ return delta;
+}
+
+// When sending patterns to the update_cr_stepper and update_pf_stepper
+// functions, the stepper device uses a "standard drive table"
+// so you have to match that drive table by using a bitswap function.
+// If the stepper drive is in the opposite direction, just reverse the
+// bits in the bitswap.
+
+void bitmap_printer_device::update_cr_stepper(int pattern)
+{
+ int delta = update_stepper_delta(m_cr_stepper, pattern);
+
+ if (delta != 0)
+ {
+ m_newpage_flag = 0;
+
+ if (delta > 0) {m_cr_direction = 1;}
+ else if (delta < 0) {m_cr_direction = -1;}
+ }
+ m_xpos = m_cr_stepper->get_absolute_position() * m_cr_stepper_ratio0 / m_cr_stepper_ratio1;
+}
+
+void bitmap_printer_device::update_pf_stepper(int pattern)
+{
+ update_stepper_delta(m_pf_stepper, pattern);
+ m_ypos = m_pf_stepper->get_absolute_position() * m_pf_stepper_ratio0 / m_pf_stepper_ratio1;
+ check_new_page();
+}
+
+void bitmap_printer_device::set_pf_stepper_ratio(int ratio0, int ratio1)
+{
+ m_pf_stepper_ratio0 = ratio0;
+ m_pf_stepper_ratio1 = ratio1;
+}
+
+void bitmap_printer_device::set_cr_stepper_ratio(int ratio0, int ratio1)
+{
+ m_cr_stepper_ratio0 = ratio0;
+ m_cr_stepper_ratio1 = ratio1;
+}
+