Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 10:37 01 Aug 2025 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : CMM2: serial console output limitations

Author Message
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 12:07am 08 Feb 2021
Copy link to clipboard 
Print this post

Hi folks,

The following is some code I'm working with whilst over-engineering a menu-system for my Scott Adams Adventure Game Interpreter. My intent is to be able to show the menu (and the rest of the UI such as it is) nicely for both Serial and VGA output driven from a single unified API. This also incidentally provides an approximation of multiple "windows" and a character mapped display. Note the attached code does not include any VGA implementation so if you look at the VGA screen you will just see a mess of VT100 escape codes, or you would if I hadn't disabled VGA output.

The example here uses it to scroll a bunch of multi-coloured text in a window on the Serial display.

My problem is that just occasionally and not (I think) predicatably the display goes wrong and the scroll jumps or bits of text get displayed outside the window area. It may just be arrogance but I don't think this is a "bug" in my code. I think that one thing that could cause such behaviour is if an occasional byte was getting lost in transmission - presumably this is a "thing"? if so what sort of loss rate might I expect ? and can I compensate ?

This code is basically my only experience with using a serial terminal and VT100 escape codes and I'm hoping some of the elder statesmen might be able to put me straight if I'm talking twaddle.


' Copyright (c) 2021 Thomas Hugo Williams
' For Colour Maximite 2, MMBasic 5.06

Option Explicit On
Option Default None

Const chm.BLACK%   = 0
Const chm.RED%     = 1
Const chm.GREEN%   = 2
Const chm.YELLOW%  = 3
Const chm.BLUE%    = 4
Const chm.MAGENTA% = 5
Const chm.CYAN%    = 6
Const chm.WHITE%   = 7

Dim chm.col_map$(255) ' map of attribute values to VT100 control codes
Dim chm.mem%(5000)    ' memory buffer for character and attribute data
Dim chm.max_num%      ' max number of windows allowed
Dim chm.num%          ' current number of windows created

' For the currently selected window:
Dim chm.id% = -1 ' id
Dim chm.a%       ' x-coordinate of left hand side
Dim chm.b%       ' y-coordinate of top
Dim chm.w%       ' width
Dim chm.h%       ' height
Dim chm.x%       ' x cursor-position
Dim chm.y%       ' y cursor-position
Dim chm.at%      ' attributes to use for printing
Dim chm.pc%      ' pointer to the character data
Dim chm.pa%      ' pointer to the attribute data

Cls

Option Console Serial
Print Chr$(27) "[?25l"; ' hide cursor

moses()
End

Sub moses()
 chm.init(2)
 Dim win1% = chm.new_win%(10, 5, 40, 25)
 Dim i%
 chm.switch(win1%) : chm.border()
 chm.bold(1)
 chm.print_at(0, 0)
 For i% = 1 To 100
   chm.foreground(1 + i% Mod 6)
   chm.print("Moses supposes his toeses are roses, but moses supposes eroneously.")
   chm.print(" For nobody's toeses are roses as moses supposes his toeses to be. ")
   Pause 200
 Next
End Sub

Sub chm.init(max_num%)
 If max_num% < 1 Or max_num% > 10 Then Error "invalid number of windows: " + Str$(max_num%)
 Dim chm.a_%(max_num% - 1)
 Dim chm.b_%(max_num% - 1)
 Dim chm.w_%(max_num% - 1)
 Dim chm.h_%(max_num% - 1)
 Dim chm.x_%(max_num% - 1)
 Dim chm.y_%(max_num% - 1)
 Dim chm.pc_%(max_num% - 1)
 Dim chm.at_%(max_num% - 1)
 chm.max_num% = max_num%

 Local i%, s$
 For i% = 0 To 255
   ' Foreground
   s$ = Chr$(27) + "[0m" + Chr$(27) + "["
   If i% And &b01000000 Then Cat s$, "1;"
   Select Case i% And &b00000111
     Case chm.BLACK%   : Cat s$, "30"
     Case chm.RED%     : Cat s$, "31"
     Case chm.GREEN%   : Cat s$, "32"
     Case chm.YELLOW%  : Cat s$, "33"
     Case chm.BLUE%    : Cat s$, "34"
     Case chm.MAGENTA% : Cat s$, "35"
     Case chm.CYAN%    : Cat s$, "36"
     Case chm.WHITE%   : Cat s$, "37"
     Case Else         : Error "unexpected"
   End Select
   Cat s$, "m" + Chr$(27) + "["

   ' Background
   Select Case (i% And &b00111000) >> 3
     Case chm.BLACK%   : Cat s$, "40"
     Case chm.RED%     : Cat s$, "41"
     Case chm.GREEN%   : Cat s$, "42"
     Case chm.YELLOW%  : Cat s$, "43"
     Case chm.BLUE%    : Cat s$, "44"
     Case chm.MAGENTA% : Cat s$, "45"
     Case chm.CYAN%    : Cat s$, "46"
     Case chm.WHITE%   : Cat s$, "47"
     Case Else         : Error "unexpected"
   End Select
   Cat s$, "m"

   ' Reverse video
   If i% And &b10000000 Then Cat s$, Chr$(27) + "[7m"

   chm.col_map$(i%) = s$
 Next
End Sub

Sub chm.foreground(col%)
 chm.at% = (chm.at% And &b11111000) Or col%
End Sub

Sub chm.background(col%)
 chm.at% = (chm.at% And &b11000111) Or (col% << 3)
End Sub

Sub chm.bold(z%)
 chm.at% = (chm.at% And &b10111111) Or (z% << 6)
End Sub

Sub chm.inverse(z%)
 chm.at% = (chm.at% And &b01111111) Or (z% << 7)
End Sub

Function chm.new_win%(x%, y%, w%, h%)
 If chm.num% > Bound(chm.a_%(), 1) Then Error "maximum number of windows reached: " + Str$(chm.num%)
 chm.a_%(chm.num%) = x% + 1 ' To account for VT100 using 1,1 as root instead of 0,0.
 chm.b_%(chm.num%) = y% + 1
 chm.w_%(chm.num%) = w%
 chm.h_%(chm.num%) = h%
 chm.x_%(chm.num%) = 0
 chm.y_%(chm.num%) = 0
 If chm.num% = 0 Then
   chm.pc_%(chm.num%) = Peek(VarAddr chm.mem%())
 Else
   chm.pc_%(chm.num%) = chm.pc_%(chm.num% - 1) + chm.w_%(chm.num% - 1) * chm.h_%(chm.num% - 1) * 2
 EndIf
 chm.at_%(chm.num%) = chm.WHITE%

 Memory Set chm.pc_%(chm.num%), 32, w% * h%
 Memory Set chm.pc_%(chm.num%) + w% * h%, chm.WHITE%, w% * h%

 chm.new_win% = chm.num%
 Inc chm.num%
End Function

Sub chm.switch(id%)
 If chm.id% > -1 Then
   chm.at_%(chm.id%) = chm.at%
   chm.x_%(chm.id%) = chm.x%
   chm.y_%(chm.id%) = chm.y%
 EndIf
 chm.id% = id%
 chm.a% = chm.a_%(id%)
 chm.b% = chm.b_%(id%)
 chm.w% = chm.w_%(id%)
 chm.h% = chm.h_%(id%)
 chm.x% = chm.x_%(id%)
 chm.y% = chm.y_%(id%)
 chm.at% = chm.at_%(id%)
 chm.pc% = chm.pc_%(id%)
 chm.pa% = chm.pc% + chm.w% * chm.h%
End Sub

Sub chm.print(s$)
 chm.print_at(chm.x%, chm.y%, s$)
End Sub

Sub chm.print_at(x%, y%, s$)
 Local x_% = x%, y_% = y%
 Local i% = 1
 Local ls% = Len(s$)
 Local nc% = Min(chm.w% - x_%, ls% - i% + 1)
 Local of%
 Local ps% = Peek(VarAddr s$)

 Do While nc% > 0
   If y_% = chm.h% Then
     chm.scroll_up()
     Inc y_%, -1
   EndIf

   of% = y_% * chm.w% + x_%
   Memory Copy ps% + i%, chm.pc% + of%, nc%
   Memory Set chm.pa% + of%, chm.at%, nc%

   Print chm.col_map$(chm.at%);
   Print Chr$(27) "[" Str$(chm.b% + y_%) ";" Str$(chm.a% + x_%) "H" Mid$(s$, i%, nc%);

   Inc i%, nc%
   Inc x_%, nc%
   If x_% = chm.w% Then x_% = 0 : Inc y_%
   nc% = Min(chm.w% - x_%, ls% - i% + 1)
 Loop

 ' Leaves chm.x% and chm.y% at the last printed char position.
 chm.x% = x_%
 chm.y% = y_%
End Sub

Sub chm.scroll_up()
'  Local tmpa%(chm.w%), tmpc%(chm.w%)
 Local pa% = chm.pa%
 Local pc% = chm.pc%

 ' Copy attribute and character data from first line (y = 0) to tmp{a|c}%.
'  Memory Copy pa%, Peek(VarAddr tmpa%()), chm.w%
'  Memory Copy pc%, Peek(VarAddr tmpc%()), chm.w%

 Local y%
 For y% = 1 To chm.h% - 1
   ' Copy attribute and character data from line y + 1 to line y.
   Memory Copy pa% + chm.w%, pa%, chm.w%
   Memory Copy pc% + chm.w%, pc%, chm.w%
   Inc pa%, chm.w%
   Inc pc%, chm.w%
 Next

 ' Paste attribute and character data from original first line to last line.
'  Memory Copy Peek(VarAddr tmpa%()), pa%, chm.w%
'  Memory Copy Peek(VarAddr tmpc%()), pc%, chm.w%
 Memory Set pa%, chm.WHITE%, chm.w%
 Memory Set pc%, 32, chm.w%

 chm.redraw()
End Sub

Sub chm.redraw()
 Local a% = -1, b%, x%, y%
 Local pa% = chm.pa%
 Local pc% = chm.pc%
 For y% = 0 To chm.h% - 1
   Print Chr$(27) "[" Str$(chm.b% + y%) ";" Str$(chm.a%) "H";
   For x% = 0 To chm.w% - 1
     b% = Peek(Byte pa% + x%)
     If a% <> b% Then a% = b% : Print chm.col_map$(a%);
     Print Chr$(Peek(Byte pc% + x%));
   Next x%
   Inc pa%, chm.w%
   Inc pc%, chm.w%
 Next y%
End Sub

Sub chm.border()
 Local s1$ = "+" + String$(chm.w%, "-") + "+"
 Local s2$ = "|" + Space$(chm.w%) + "|")
 Print Chr$(27) "[" Str$(chm.b% - 1) ";" Str$(chm.a% - 1) "H" s1$
 Local i%
 For i% = 0 To chm.h% - 1
   Print Chr$(27) "[" Str$(chm.b% + i%) ";" Str$(chm.a% - 1) "H" s2$
 Next
 Print Chr$(27) "[" Str$(chm.b% + chm.h%) ";" Str$(chm.a% - 1) "H" s1$
End Sub


Best wishes,

Tom
Edited 2021-02-08 10:11 by thwill
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6283
Posted: 01:12am 08 Feb 2021
Copy link to clipboard 
Print this post

No obvious problems with TeraTerm and Windows10

You are sending a lot of data down the serial line and I expect that your terminal can't keep up.

Remember that there is no flow control.

Jim
VK7JH
MMedit
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10310
Posted: 08:29am 08 Feb 2021
Copy link to clipboard 
Print this post

Output to the console using print is blocking if the output buffer fills so characters are not lost, as Jim identifies the problem must be at the terminal end
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 4044
Posted: 09:50am 08 Feb 2021
Copy link to clipboard 
Print this post

Tom,

You could either slow it down or for a test at the receiving end use a logging program which just collects data without showing it so it can keep up (then play the data back).

John
 
romba6

Newbie

Joined: 04/07/2020
Location: United Kingdom
Posts: 37
Posted: 12:14pm 08 Feb 2021
Copy link to clipboard 
Print this post

I've tested this multiple times with Teraterm (no delays set) on laptop and 100% rock solid.
I will, if I can!, set it to output on COM2 later where I have an RS232 to another PC. I have learnt a lot from this as I am also messing with VT100
 
Tinine
Guru

Joined: 30/03/2016
Location: United Kingdom
Posts: 1646
Posted: 12:31pm 08 Feb 2021
Copy link to clipboard 
Print this post

Plus, you spelled erroneously wrong  ...no wonder it doesn't work  
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 12:50pm 08 Feb 2021
Copy link to clipboard 
Print this post

  Tinine said  Plus, you spelled erroneously wrong  ...no wonder it doesn't work  


I didn't spell it "wrong", I spelt it erroneously.

Best wishes,

Tom
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4311
Posted: 01:20pm 08 Feb 2021
Copy link to clipboard 
Print this post

Thanks for all the comments. I am connecting using PuTTY from a Pi 3, I'll try TeraTerm on my Win10 power-house later but it sounds like my code is probably OK.

Best wishes,

Tom
MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10310
Posted: 02:53pm 08 Feb 2021
Copy link to clipboard 
Print this post

Wrong thread - ignore
Edited 2021-02-09 00:55 by matherp
 
frnno967
Senior Member

Joined: 02/10/2020
Location: United States
Posts: 104
Posted: 04:22pm 08 Feb 2021
Copy link to clipboard 
Print this post

Try Syncterm.
Jay Crutti: Ham Radio Operator, K5JCJ. Computer Enthusiast. Musician. Engineer.
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025