diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index b1748c1..fcb38e7 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1903,6 +1903,8 @@ repeat( {expr}, {count})	String	repeat {expr} {count} times
 resolve( {filename})		String	get filename a shortcut points to
 reverse( {list})		List	reverse {list} in-place
 round( {expr})			Float	round off {expr}
+screen( [{size} [, {col}]])	List	vim on-screen text
+screenattr( {row}, {col})	Dict	vim on-screen text attributes
 screencol()			Number	current cursor column
 screenrow()			Number	current cursor row
 search( {pattern} [, {flags} [, {stopline} [, {timeout}]]])
@@ -4880,6 +4882,19 @@ round({expr})							*round()*
 <			-5.0
 		{only available when compiled with the |+float| feature}
 
+screen([{size} [, {col}]])					*screen()*
+		Return a List of the on-screen text (ascii only) drawn by vim.
+		Each item in the list holds one screen line. The first element
+		of the List contains the topmost line.  {size} limits the
+		length of each line returned. If not provided, {size} defaults
+		to 'columns'.  {col} is the column offset from which each
+		line starts. The first column is 1.
+
+screenattr({row} , {col})					*screenattr()*
+		Return a Dict containing the character attributes for the
+		character at row {row} and column {col}. Topmost row is 1
+		and leftmost column is 1.
+
 screencol()							*screencol()*
 		The result is a Number, which is the current screen column of
 		the cursor. The leftmost column has number 1.
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index edb9b82..1ad6708 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -753,6 +753,11 @@ Date and Time:				*date-functions* *time-functions*
 	reltime()		get the current or elapsed time accurately
 	reltimestr()		convert reltime() result to a string
 
+			*screen-functions*
+Screen:
+	screen()		Get screen text drawn by vim
+	screenattr()		Get screen attributes for text drawn by vim
+
 			*buffer-functions* *window-functions* *arg-functions*
 Buffers, windows and the argument list:
 	argc()			number of entries in the argument list
diff --git a/src/eval.c b/src/eval.c
index 106f1c5..e4909a5 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -668,6 +668,8 @@ static void f_reverse __ARGS((typval_T *argvars, typval_T *rettv));
 #ifdef FEAT_FLOAT
 static void f_round __ARGS((typval_T *argvars, typval_T *rettv));
 #endif
+static void f_screen __ARGS((typval_T *argvars, typval_T *rettv));
+static void f_screenattr __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_screencol __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_screenrow __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_search __ARGS((typval_T *argvars, typval_T *rettv));
@@ -8038,6 +8040,8 @@ static struct fst
 #ifdef FEAT_FLOAT
     {"round",		1, 1, f_round},
 #endif
+    {"screen",		0, 2, f_screen},
+    {"screenattr",	0, 2, f_screenattr},
     {"screencol",	0, 0, f_screencol},
     {"screenrow",	0, 0, f_screenrow},
     {"search",		1, 4, f_search},
@@ -15770,6 +15774,134 @@ f_round(argvars, rettv)
 #endif
 
 /*
+ * "screen()" function
+ */
+    static void
+f_screen(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    list_T	*l;
+    int		r;
+    int		off;
+    int		col = 0;
+    int		width = screen_Columns;
+
+    if (ScreenLines == NULL)
+	return;
+
+    if (rettv_list_alloc(rettv) == FAIL)
+	return;
+
+    if (argvars[0].v_type != VAR_UNKNOWN)
+    {
+	width = get_tv_number(&argvars[0]);
+	if (width <= 0)
+	    return;
+
+	if (argvars[1].v_type != VAR_UNKNOWN)
+	{
+	    col = get_tv_number(&argvars[1]) - 1;	/* -1 on type error */
+	    if (col < 0 || col >= screen_Columns)
+		return;
+	}
+
+	if (width + col > screen_Columns)
+	    width = screen_Columns - col;
+    }
+
+    for (r = 0; r < screen_Rows; r++)
+    {
+	off = LineOffset[r] + col;
+	list_append_string(rettv->vval.v_list, &ScreenLines[off], width);
+    }
+}
+
+/*
+ * "screenattr()" function
+ */
+    static void
+f_screenattr(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    int		    row = -1;
+    int		    col = -1;
+    int		    attr;
+    dict_T	    *dict;
+    attrentry_T	    *aep;
+
+    if (argvars[0].v_type != VAR_UNKNOWN)
+    {
+	row = get_tv_number(&argvars[0]) - 1;	/* -1 on type error */
+	if (argvars[1].v_type != VAR_UNKNOWN)
+	    col = get_tv_number(&argvars[1]) - 1;	/* -1 on type error */
+    }
+
+    if (row == -1 || col == -1)
+	return;
+
+    if (rettv_dict_alloc(rettv) != OK)
+	return;
+
+    attr = ScreenAttrs[LineOffset[row] + col];
+
+    if (attr > HL_ALL)					/* special HL attr. */
+    {
+
+#ifdef FEAT_GUI
+	if (gui.in_use)
+	    aep = syn_gui_attr2entry(attr);
+	else
+#endif
+	if (t_colors > 1)
+	    aep = syn_cterm_attr2entry(attr);
+	else
+	    aep = syn_term_attr2entry(attr);
+
+	if (aep == NULL)			    /* did ":syntax clear" */
+	    attr = 0;
+	else
+	    attr = aep->ae_attr;
+    }
+
+    dict = rettv->vval.v_dict;
+
+    dict_add_nr_str(dict, "bold", attr & HL_BOLD ? 1 : 0, NULL);
+    dict_add_nr_str(dict, "standout", attr & HL_STANDOUT ? 1 : 0, NULL);
+    dict_add_nr_str(dict, "underline", attr & HL_UNDERLINE ? 1 : 0, NULL);
+    dict_add_nr_str(dict, "undercurl", attr & HL_UNDERCURL ? 1 : 0, NULL);
+    dict_add_nr_str(dict, "italic", attr & HL_ITALIC ? 1 : 0, NULL);
+    dict_add_nr_str(dict, "inverse", attr & HL_INVERSE ? 1 : 0, NULL);
+
+    if (aep != NULL)
+    {
+#ifdef FEAT_GUI
+	if (gui.in_use)
+	{
+	    dict_add_nr_str(dict, "guifg", aep->ae_u.gui.fg_color, NULL);
+	    dict_add_nr_str(dict, "guibg", aep->ae_u.gui.bg_color, NULL);
+	}
+	else
+#endif
+	if (t_colors > 1)
+	{
+	    dict_add_nr_str(dict, "ctermfg", aep->ae_u.cterm.fg_color - 1,
+									  NULL);
+	    dict_add_nr_str(dict, "ctermbg", aep->ae_u.cterm.bg_color - 1,
+									  NULL);
+	}
+	else
+	{
+	    if (aep->ae_u.term.start != NULL)
+		dict_add_nr_str(dict, "ctermfg", 0, aep->ae_u.term.start);
+	    if (aep->ae_u.term.stop != NULL)
+		dict_add_nr_str(dict, "ctermbg", 0, aep->ae_u.term.stop);
+	}
+    }
+}
+
+/*
  * "screencol()" function
  *
  * First column is 1 to be consistent with virtcol().
diff --git a/src/testdir/Makefile b/src/testdir/Makefile
index 1920834..8f3fd72 100644
--- a/src/testdir/Makefile
+++ b/src/testdir/Makefile
@@ -28,7 +28,8 @@ SCRIPTS = test1.out test2.out test3.out test4.out test5.out test6.out \
 		test74.out test75.out test76.out test77.out test78.out \
 		test79.out test80.out test81.out test82.out test83.out \
 		test84.out test85.out test86.out test87.out test88.out \
-		test89.out test90.out
+		test89.out test90.out test91.out test92.out test93.out \
+		test94.out
 
 SCRIPTS_GUI = test16.out
 
@@ -67,7 +68,7 @@ test1.out: test1.in
 
 .in.out:
 	-rm -rf $*.failed test.ok $(RM_ON_RUN)
-	cp $*.ok test.ok
+	if [ -f $*.ok ]; then cp $*.ok test.ok; fi
 	# Sleep a moment to avoid that the xterm title is messed up
 	@-sleep .2
 	-$(RUN_VIM) $*.in
diff --git a/src/testdir/screen.vim b/src/testdir/screen.vim
new file mode 100644
index 0000000..5410702
--- /dev/null
+++ b/src/testdir/screen.vim
@@ -0,0 +1,21 @@
+function! ExpandLine(line)
+    return a:line . repeat(' ', &columns - len(a:line))
+endfun
+
+function! WriteScreen(lines, file, opt)
+    let l:screen = []
+    let l:nontextprefix = get(a:opt, 'nontextprefix', '') . '~'
+    let l:remaining = &columns - len(l:nontextprefix)
+    let l:filler = l:nontextprefix . repeat(' ', l:remaining)
+
+    let l:length = len(a:lines)
+    for l:row in range(0, &lines - 1 - &cmdheight) " Exclude vim's command line
+	if l:row < l:length
+	    call add(l:screen, ExpandLine(a:lines[l:row]))
+	else
+	    call add(l:screen, l:filler)
+	endif
+    endfor
+
+    call writefile(l:screen, a:file)
+endfun
diff --git a/src/testdir/test91.in b/src/testdir/test91.in
new file mode 100644
index 0000000..0642115
--- /dev/null
+++ b/src/testdir/test91.in
@@ -0,0 +1,14 @@
+Test for 'number'
+
+STARTTEST
+:so screen.vim
+:let lines = []
+:for i in range(1, 9)|call add(lines, printf("%7d", i))|endfor
+:call WriteScreen(lines, expand('%:r') . '.ok', {})
+:1,$d
+:set number
+:for i in range(0, 7)|0put =''|endfor
+:call writefile(screen()[0:-2], 'test.out')
+:q!
+ENDTEST
+
diff --git a/src/testdir/test92.in b/src/testdir/test92.in
new file mode 100644
index 0000000..3bfc2e9
--- /dev/null
+++ b/src/testdir/test92.in
@@ -0,0 +1,23 @@
+Test for 'relativenumber'
+
+STARTTEST
+:so screen.vim
+:let lines = []
+:call add(lines, '      4')
+:call add(lines, '      3')
+:call add(lines, '      2')
+:call add(lines, '      1')
+:call add(lines, '5      ')
+:call add(lines, '      1')
+:call add(lines, '      2')
+:call add(lines, '      3')
+:call add(lines, '      4')
+:call WriteScreen(lines, expand('%:r') . '.ok', {})
+:1,$d
+:set relativenumber
+:for i in range(0, 7)|0put =''|endfor
+:5
+:call writefile(screen()[0:-2], 'test.out')
+:q!
+ENDTEST
+
diff --git a/src/testdir/test93.in b/src/testdir/test93.in
new file mode 100644
index 0000000..ec20218
--- /dev/null
+++ b/src/testdir/test93.in
@@ -0,0 +1,18 @@
+Test for 'foldmethod=marker foldmarker={,}'
+
+STARTTEST
+:so screen.vim
+:let lines = []
+:call add(lines, '    int')
+:call add(lines, 'main()')
+:let folded = '+--  2 lines: '
+:let folded = folded . repeat('-', &columns - len(folded))
+:call add(lines, folded)
+:call WriteScreen(lines, expand('%:r') . '.ok', {})
+:1,$d
+:s/^/    int\rmain()\r{\r}/
+:set foldmethod=marker foldmarker={,}
+:call writefile(screen()[0:-2], 'test.out')
+:q!
+ENDTEST
+
diff --git a/src/testdir/test94.in b/src/testdir/test94.in
new file mode 100644
index 0000000..d6e666a
--- /dev/null
+++ b/src/testdir/test94.in
@@ -0,0 +1,18 @@
+Test for 'foldmethod=marker foldmarker={,} foldcolumn=1'
+
+STARTTEST
+:so screen.vim
+:let lines = []
+:call add(lines, '     int')
+:call add(lines, ' main()')
+:let folded = '++--  2 lines: '
+:let folded = folded . repeat('-', &columns - len(folded))
+:call add(lines, folded)
+:call WriteScreen(lines, expand('%:r') . '.ok', {'nontextprefix':' '})
+:1,$d
+:s/^/    int\rmain()\r{\r}/
+:set foldmethod=marker foldmarker={,} foldcolumn=1
+:call writefile(screen()[0:-2], 'test.out', { 'nontextprefix':' ' })
+:q!
+ENDTEST
+
