+2021-02-08  Andrew Burgess  <andrew.burgess@embecosm.com>
+
+       * gdb.tui/scroll.exp: New file.
+       * gdb.tui/tui-layout-asm-short-prog.exp: Update expected results.
+       * lib/tuiterm.exp (Term::_csi_M): Delete count lines, scroll
+       remaining lines up.
+       (Term::check_region_contents): New proc.
+       (Term::check_box_contents): Use check_region_contents.
+
 2021-02-06  Tom de Vries  <tdevries@suse.de>
 
        PR testsuite/26922
 
--- /dev/null
+# Copyright 2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Check scrolling in the command window.  This test only covers the
+# case where scrolling in the command window is caused by issuing many
+# non-inferior related commands, as once the inferior is given control
+# the terminal settings are modified and our tuiterm library really
+# gets confused.
+
+tuiterm_env
+
+standard_testfile tui-layout.c
+
+if {[build_executable "failed to prepare" ${testfile} ${srcfile}] == -1} {
+    return -1
+}
+
+Term::clean_restart 24 80 $testfile
+if {![Term::enter_tui]} {
+    unsupported "TUI not supported"
+    return
+}
+
+for {set i 0} {$i < 10} {incr i 1} {
+    Term::command "p $i"
+}
+
+# Now check that the contents of the command window are as expected.
+#
+# Well, we would if there wasn't a massive bug in GDB!!  The command
+# window contents will not be exactly what you'd expect after this
+# test has run.
+#
+# The expected output pattern given here is crafted so that it matches
+# those bits of the GDB output that will be correct, and ignores those
+# parts of the output that are known to be incorrect.
+#
+# If/when GDB is fixed it is expected that this test will continue to
+# pass, though it is possible that at that point the pattern here
+# could be improved.
+Term::check_region_contents "check cmd window" 0 16 80 8 \
+    [multi_line \
+        "\[^\r\n\]*\\\$7 = 6\[^\r\n\]+" \
+        "\\(gdb\\)\[^\r\n\]+" \
+        "\[^\r\n\]*\\\$8 = 7\[^\r\n\]+" \
+        "\\(gdb\\)\[^\r\n\]+" \
+        "\[^\r\n\]*\\\$9 = 8\[^\r\n\]+" \
+        "\\(gdb\\)\[^\r\n\]+" \
+        "\[^\r\n\]*\\\$10 = 9\[^\r\n\]+" \
+        "\\(gdb\\)"]
 
 # instruction in the program.
 Term::command "+ 13"
 Term::check_box_contents "check asm box contents again" 0 0 80 15 \
-    "^ *$hex\[^\n\]+\n +\n"
+    [multi_line \
+        "^ *$hex\[^\r\n\]+" \
+        "\\s+"]
 
 # Now scroll backward again, we should return to the start of the
 # program.
 
            variable _chars
 
            set y $_cur_row
-           set next_y [expr {$y + 1}]
-           while {$count > 0 && $next_y < $_rows} {
+           set next_y [expr {$y + $count}]
+           while {$next_y < $_rows} {
                for {set x 0} {$x < $_cols} {incr x} {
                    set _chars($x,$y) $_chars($x,$next_y)
                }
                incr y
                incr next_y
-               incr count -1
            }
-           _clear_lines $next_y $_rows
+           _clear_lines $y $_rows
        }
     }
 
        }
     }
 
+    # Check that the region of the screen described by X, Y, WIDTH,
+    # and HEIGHT match REGEXP.  This is like check_contents except
+    # only part of the screen is checked.  This can be used to check
+    # the contents within a box (though check_box_contents is a better
+    # choice for boxes with a border).
+    proc check_region_contents { test_name x y width height regexp } {
+       variable _chars
+
+       # Now grab the contents of the box, join each line together
+       # with a '\r\n' sequence and match against REGEXP.
+       set result ""
+       for {set yy $y} {$yy < [expr {$y + $height}]} {incr yy} {
+           if {$yy > $y} {
+               # Add the end of line sequence only if this isn't the
+               # first line.
+               append result "\r\n"
+           }
+           for {set xx $x} {$xx < [expr {$x + $width}]} {incr xx} {
+               append result [lindex $_chars($xx,$yy) 0]
+           }
+       }
+
+       if {![gdb_assert {[regexp -- $regexp $result]} $test_name]} {
+           dump_screen
+       }
+    }
+
     # Check the contents of a box on the screen.  This is a little
     # like check_contents, but doens't check the whole screen
     # contents, only the contents of a single box.  This procedure
            return
        }
 
-       # Now grab the contents of the box, join each line together
-       # with a newline character and match against REGEXP.
-       set result ""
-       for {set yy [expr {$y + 1}]} {$yy < [expr {$y + $height - 1}]} {incr yy} {
-           for {set xx [expr {$x + 1}]} {$xx < [expr {$x + $width - 1}]} {incr xx} {
-               append result [lindex $_chars($xx,$yy) 0]
-           }
-           append result "\n"
-       }
-
-       if {![gdb_assert {[regexp -- $regexp $result]} $test_name]} {
-           dump_screen
-       }
+       check_region_contents $test_name [expr {$x + 1}] [expr {$y + 1}] \
+           [expr {$width - 2}] [expr {$height - 2}] $regexp
     }
 
     # A debugging function to dump the current screen, with line