Previous Thread
Next Thread
Print Thread
Page 46 of 78 1 2 44 45 46 47 48 77 78
Joined: Feb 2014
Posts: 826
Likes: 36
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 826
Likes: 36
In the absence of the Silentype /// manual, I wanted to know how the different ESC codes worked since I couldn't get bidirectional printing to look right, sometimes it looks good and other times it looks bad.

I was able to find a table of the ESC codes along with a jump table of the routine address - 1.

ESC codes at $42F4 and Jump table is $4309

[Linked Image from i.imgur.com]

So far I've figured out that

ESC < is bidirectional printing
ESC > is unidirectional printing
ESC B is bold on
ESC b is bold off
ESC W [n] is wide width, takes a number that specifies the character width (numbers from 0 to 7, 8 acts like 0)
ESC w turns off wide width
ESC ? is remap the ESC key to character ie ESC ? | makes any occurence of | act like the ESC code
ESC = is reset the silentype to default settings
ESC . [n] is inter-character gap
ESC ^ [n] is line spacing (in 7/4 pixels, so ESC ^ 4 will feed the paper 7 pixels between lines, single line spacing is normally 6)
ESC H [chr$[n]] is move horizontally precisely [n] pixels so ESC H chr$(3) will move the printhead 3 pixels to the right.
ESC V [chr$[n]] is move vertically precisely [n] * (7/4) pixels so ESC V chr$(4) will move the printhead down exactly 7 pixels.

ESC F [n] is choose font 0,1,2 0=Normal, 1 = Compact, 2 = Roman (though they all seem identical, perhaps the "bigger" driver has the additional character sets)

ESC J [chr$([n])] is set Left Margin
ESK K [chr$([n])] is set Right Margin

Last edited by Golden Child; 08/15/19 07:38 PM.
Joined: Feb 2014
Posts: 826
Likes: 36
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 826
Likes: 36
I figured out a couple of more escape codes:

ESC L [chr$([n])] is set the number of lines that a FF will generate (up to 15 I think)
ESC C is set right margin clipping on/off (text that goes further that the right margin will get clipped and not wrapped to the next line)
ESC c is turn clipping on/off

so the only ones I haven't figured out yet are ESC G and the ESC E/e pair.


I wanted to see the silentype printout close up, so I was playing with the copyrozbitmap function (took me awhile to figure out how the parameters work, all good fun.) I want to make it so I can zoom in/out and pan with a joystick.


[Linked Image from i.imgur.com]


edit:
Just on a whim, I thought the ESC G might do graphics, and since there's only 7 elements on the printhead, maybe it will use the codes from 128 to 255.

Yes! It works!

ESC G to go into graphics mode, then send it characters >= 128, bit data is in lower 7 bits. Graphics mode seems to be terminated by sending it the "H" character.

I remember back when I was using the silentype with my apple 2 how much I wanted to be able to send bit patterns to the printer. It's so easy with the Apple /// driver. 8-)

[Linked Image from i.imgur.com]

It's kind of cool that it will print the 7 pixels of bit data just as soon as you send it.

You can see the pixel pattern in the "staircase" in the lower right. Lowest bit is the bottommost pixel.

PRINT #1;CHR$(27)"G";
PRINT #1;CHR$(128+1);
PRINT #1;CHR$(128+2);
PRINT #1;CHR$(128+4);
PRINT #1;CHR$(128+8);
PRINT #1;CHR$(128+16);

[Linked Image from i.imgur.com]

Last edited by Golden Child; 08/20/19 04:55 PM.
Joined: Feb 2014
Posts: 826
Likes: 36
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 826
Likes: 36
It appears that ESC E/e pair just turn on/off double spacing. For every CR, you get an extra CR. BOGO.

I guess I was expecting it to be more complicated than that 8-)

It would've been cool if they'd included a sample basic program that would show how every ESC code worked. I think I'll write one.

One last mystery to solve is why the bidirectional printing doesn't line up properly. Sometimes it does, and other times it doesn't.

I noticed that if I printed a line that was over 40 characters long that it would be off by one pixel printing characters on the return trip leftward. When it prints less than 40 characters, it lines up perfectly.

[Linked Image from i.imgur.com]

Joined: Mar 2001
Posts: 16,912
Likes: 56
R
Very Senior Member
Offline
Very Senior Member
R
Joined: Mar 2001
Posts: 16,912
Likes: 56
ComputerEyes/2 was much more painful since it actually polls the composite sync and expects the true raster V position to be the V position of the capture (the horizontal position is handled internally by the hardware). But it gives nicer results, especially in double-hi-res greyscale:

[Linked Image from rbelmont.mameworld.info]

ETA: And a little MAMEception: here's a MAME screenshot from the IIgs driver used as source for a //e double-hi-res capture:

[Linked Image from rbelmont.mameworld.info]

Last edited by R. Belmont; 08/25/19 11:39 PM.
Joined: Feb 2014
Posts: 826
Likes: 36
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 826
Likes: 36
I think I found out why the silentype /// bidirectional printing is a little strange. There's a small stretch of code that does some addition on a two byte value, and curiously it adds an extra 1 when the low byte overflows.

You 6502 guys would be able to figure this out. Me, it takes forever. 8-) I had to keep looking at it over and over again to figure out what was happening because it's subtle.

[Linked Image from i.imgur.com]

It calls jsr $4ada which is just a lda #$05, rts. After that returns it clears the carry flag, adds $3e86 and pushes the flags on the stack. Then it adds $3ea2 which is the intercharacter gap, and stores it to $3e86. Then it adjusts $3e87 and increments it if the second addition had a carry flag set, then pulls the flags off the stack and increments $3e87 again if the first addition had the carry flag set.

Before it adds $3ea2 it doesn't clear the carry flag, so when that first addition of #$05 and $3e86 overflows it gets an additional +1.

If the line is 40 characters (starting at a horizontal position of 12 and each character being 5 pixels wide + 1 pixel gap you get an hpos of 6*40+12=252).
If the line is 41 characters the hpos crosses the 255 value and when it adds 6 to 252 it jumps from 252 to 259 with the addition of the carry flag.

So 259=0x103 instead of 258=0x102.

I wonder if this was actually deliberate as the stepper motors were inaccurate and that was empirically determined to compensate for that. Or it could just be a bug. Of course we're not emulating the mass and acceleration of the motors and printhead, but an idealized stepper motor which perfectly and instantaneously jumps from location to location.

So what if we try to patch it?

Code
< 4278: 08       php
< 4279: 6D A2 3E adc $3ea2
< 427C: 8D 86 3E sta $3e86
< 427F: 90 03    bcc $4284
< 4281: EE 87 3E inc $3e87
< 4284: 28       plp
< 4285: 90 03    bcc $428a
< 4287: EE 87 3E inc $3e87
---
> 4278: 90 03    bcc $427d
> 427A: EE 87 3E inc $3e87
> 427D: 18       clc
> 427E: 6D A2 3E adc $3ea2
> 4281: 8D 86 3E sta $3e86
> 4284: 90 03    bcc $4289
> 4286: EE 87 3E inc $3e87
> 4289: EA       nop


I can get the code in with one byte left over as a nop, and that seems to get the bidirectional lines to line up as expected.

Some other interesting bits from fiddling:

When the silentype is active, you can't read any of the joystick buttons or paddles. Closing the silentype device with CLOSE #1 will make them available again.

?PDL(0) will give you ?RESOURCE UNAVAILABLE ERROR.

[Linked Image from i.imgur.com]

3F4D seems to be the main driver entry so "bp 3f4d" will stop right on entry to the driver.

Executing a source command (eg "source debugcmd1.txt") from the debugger window will only work if the debugger is paused first. Otherwise, it seems to have no effect.

Executing a trace command with the apple /// must be done with the maincpu as an argument (ie 'trace mytrace1.txt,maincpu,,{tracelog "A=%02x X=%02x Y=%02x P=%02x PC=%02x >>> ",a,x,y,p,pc}' otherwise you end up with completely empty trace files.

The history command will blow up the apple3 debugger if you type "history ,5". You need to give it "history maincpu,5". Just plain old "history" works but you don't always want 200 history entries 8-)

Joined: Jun 2014
Posts: 90
P
Member
Offline
Member
P
Joined: Jun 2014
Posts: 90
The first BCC could be +4 instead of +3, but otherwise the patch looks fine. Yes, it looks like a bug unless the bounds were known to only ever result in a single overflow.

Joined: Feb 2014
Posts: 826
Likes: 36
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 826
Likes: 36
Originally Posted by peter ferrie
The first BCC could be +4 instead of +3, but otherwise the patch looks fine. Yes, it looks like a bug unless the bounds were known to only ever result in a single overflow.


That's a good idea to save two cycles. Also I think the second BCC could be +4 too, skipping the NOP. 8-)

Joined: Feb 2014
Posts: 826
Likes: 36
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 826
Likes: 36
I wanted to keypost some basic programs into the apple 3 driver but the emu.keypost("\"") wouldn't give me a proper quote.

It's a pretty easy fix, just change the PORT_CHAR to "@" for KEYCODE_2, because the \" is also on KEYCODE_QUOTE, so the keypost gets the first one while it searches, finding the 2 key instead of the quote key.

Code
diff --git a/src/mame/drivers/apple3.cpp b/src/mame/drivers/apple3.cpp
index ecaa1453d0..e429ecf878 100644
--- a/src/mame/drivers/apple3.cpp
+++ b/src/mame/drivers/apple3.cpp
@@ -215,7 +215,7 @@ static INPUT_PORTS_START( apple3 )
        PORT_START("X0")
        PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc")      PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
        PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)      PORT_CHAR('1') PORT_CHAR('!')
-       PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('\"')
+       PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('@')
        PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('#')
        PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('4') PORT_CHAR('$')
        PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')


also I thought it'd be cool to have a emu.keydump() to help see the keys, a simple two line addition to luaengine.cpp:

Code
diff --git a/src/frontend/mame/luaengine.cpp b/src/frontend/mame/luaengine.cpp
index df730b7019..89bba51324 100644
--- a/src/frontend/mame/luaengine.cpp
+++ b/src/frontend/mame/luaengine.cpp
@@ -8,6 +8,7 @@
 
 ***************************************************************************/
 
+#include <iostream>
 #include <thread>
 #include <lua.hpp>
 #include "emu.h"
@@ -805,6 +806,7 @@ void lua_engine::initialize()
        emu["romname"] = [this](){ return machine().basename(); };
        emu["softname"] = [this]() { return machine().options().software_name(); };
        emu["keypost"] = [this](const char *keys){ machine().ioport().natkeyboard().post_utf8(keys); };
+       emu["keydump"] = [this](){ machine().ioport().natkeyboard().dump(std::cout); };
        emu["time"] = [this](){ return machine().time().as_double(); };
        emu["start"] = [this](const char *driver) {
                        int i = driver_list::find(driver);


Joined: Feb 2014
Posts: 826
Likes: 36
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 826
Likes: 36
I thought I'd check all of the emu.keypost characters and there's a few more misassignments.

6789 should be ^&*(


a little lua script to test:
Code
function iif(a,b,c) if a then return b else return c end end
for i=32,127 do emu.keypost(iif(i==32,"rem ","")..i.." "..string.char(i).." "..iif(i%10==0,"\nrem ","") ) io.write(i.." "..string.char(i).." "..iif(i%10==0,"\n","")) end emu.keypost("\n") print()


[Linked Image from i.imgur.com]

Code
diff --git a/src/mame/drivers/apple3.cpp b/src/mame/drivers/apple3.cpp
index ecaa1453d0..deedcef9e4 100644
--- a/src/mame/drivers/apple3.cpp
+++ b/src/mame/drivers/apple3.cpp
@@ -215,14 +215,14 @@ static INPUT_PORTS_START( apple3 )
        PORT_START("X0")
        PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Esc")      PORT_CODE(KEYCODE_ESC)      PORT_CHAR(27)
        PORT_BIT(0x002, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_1)      PORT_CHAR('1') PORT_CHAR('!')
-       PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('\"')
+       PORT_BIT(0x004, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_2)  PORT_CHAR('2') PORT_CHAR('@')
        PORT_BIT(0x008, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_3)  PORT_CHAR('3') PORT_CHAR('#')
        PORT_BIT(0x010, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_4)  PORT_CHAR('4') PORT_CHAR('$')
        PORT_BIT(0x020, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_5)  PORT_CHAR('5') PORT_CHAR('%')
-       PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('&')
-       PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('\'')
-       PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('(')
-       PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR(')')
+       PORT_BIT(0x040, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_6)  PORT_CHAR('6') PORT_CHAR('^')
+       PORT_BIT(0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_7)  PORT_CHAR('7') PORT_CHAR('&')
+       PORT_BIT(0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_8)  PORT_CHAR('8') PORT_CHAR('*')
+       PORT_BIT(0x200, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_CODE(KEYCODE_9)  PORT_CHAR('9') PORT_CHAR('(')
 
        PORT_START("X1")
        PORT_BIT(0x001, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Tab")      PORT_CODE(KEYCODE_TAB)      PORT_CHAR(9)

Joined: Feb 2014
Posts: 826
Likes: 36
G
Senior Member
Offline
Senior Member
G
Joined: Feb 2014
Posts: 826
Likes: 36
While we're at it, why don't we verify our keystrokes with what actually gets recognized by the computer?

First let's find out in memory where the screen memory actually is:

On the initial load of business basic, there's a directory listing. Let's choose something on screen that's recognizable: "VOLUMES.DOC".

Writing a little lua function to scan the memory, and then a few false starts: screen memory is split into two blocks one at 0x400 and 0x800 with every other character, so VOLUMES.DOC would be VLMSDC or OUE.O, and the high bit needs to be set.

Code
mem=emu.item(manager:machine().devices[":ram"].items["0/m_pointer"])

function scanmatch(byteseq,mem,start)  for addr = start,start+string.len(byteseq)-1 do if mem:read(addr)~=string.byte(byteseq,1+addr-start) then return false end  end return true end

function eachchar(s,func) ret="" for i=1,#s do ret=ret..string.char(func(string.byte(s,i))) end return ret end
function sethibit(achar) return achar | 0x80 end

function hex(a) return string.format("%02x",a) end

for i=0,0x40000 do if scanmatch(eachchar("VLMSDC",sethibit),mem,i) then print(hex(i)) end end


which tells us that the screen memory is at 0x38000 + 0x400 (for page 1) and 0x38000+0x800 (for page 2)

[MAME]> for i=0,0x40000 do if scanmatch(eachchar("VLMSDC",sethibit),mem,i) then print(hex(i)) end end
38b07
[MAME]> for i=0,0x40000 do if scanmatch(eachchar("OUE.O",sethibit),mem,i) then print(hex(i)) end end
38708


Knowing that we can write a little basic program to read a keystroke, print it in the upper left at hpos=1 and vpos =1 which would be address 0x38000+0x400 and we can read that to check against what we're sending:

Code
function myco() 
quitco=nil 
emu.keypost([[
new
90 home
100 get a$:HPOS=1:VPOS=1:? a$"  ASC="ASC(A$)"    "
105 for i = 1 to 300:next i
110 GOTO 100
run
]])
mem=emu.item(manager:machine().devices[":ram"].items["0/m_pointer"])
emu.wait(15) 
for i=32,126 do 
emu.keypost(string.char(i)) 
emu.wait(.5)
screenchar=mem:read(0x40000-0x8000+0x400) 
print("SENT: "..string.char(i).."   SCREEN:"..string.char(screenchar&0x7f)) 
if quitco then return end end end
co1=coroutine.create(myco) coroutine.resume(co1)


and the output looks like:

SENT: SCREEN:
SENT: ! SCREEN:!
SENT: " SCREEN:"
SENT: # SCREEN:#
SENT: $ SCREEN:$
SENT: % SCREEN:%
SENT: & SCREEN:&

Interestingly, afterward, typing CTRL+C to exit the basic program, you have to blind type "TEXT" and enter to get your normal screen back. I think that printing CHR$(3) does something strange to the console driver.

I initially wanted to use the basic KBD function but I couldn't get KBD to work. "PRINT KBD" would always give me zero. GET A$ works just fine. (edit: Oh I see how KBD works from the business basic vol 1 manual, you have to use ON KBD GOTO for KBD to get set properly)

The uppercase and lowercase characters seem to be reversed, there must be a reason for that, it'd be simple to flip the order of the PORT_CHAR to lowercase first, then uppercase character.


Let's also figure out how the addresses at a000 gets mapped into memory:

Code
function hextobytes(h) local a,b,c c="" h=string.upper(h)for i=1,string.len(h),2 do a=string.byte(h,i)-string.byte("0") if a>16 then a=a-7 end b=string.byte(h,i+1)-string.byte("0") if b>16 then b=b-7 end c=c..string.char(a*16+b) end return c end

for i=0,0x40000 do if scanmatch(hextobytes("4ce5a020ca"),mem,i) then print(hex(i)) end end

and it tells us that a000 gets mapped into 3a000.

[Linked Image from i.imgur.com]

Last edited by Golden Child; 08/29/19 04:32 AM.
Page 46 of 78 1 2 44 45 46 47 48 77 78

Link Copied to Clipboard
Who's Online Now
1 members (box), 20 guests, and 2 robots.
Key: Admin, Global Mod, Mod
ShoutChat
Comment Guidelines: Do post respectful and insightful comments. Don't flame, hate, spam.
Forum Statistics
Forums9
Topics9,088
Posts119,102
Members5,014
Most Online890
Jan 17th, 2020
Our Sponsor
These forums are sponsored by Superior Solitaire, an ad-free card game collection for macOS and iOS. Download it today!

Superior Solitaire
Forum hosted by www.retrogamesformac.com