fm2606
Posts: 3
Joined: Sat Jun 22, 2019 5:19 pm

writing to terminal using gpu and asm

Sat Jun 22, 2019 5:34 pm

Are there any tutorials or examples of using the VideoCore GPU to write to the terminal using the GPU in pure assembly without using LIBC or GTK. I can write single lines to the terminal using asm but what I am talking about is being able to write at a given X,Y coordinate and create UIs similar to when raspi-config is run in the terminal.

I tried to run this example https://adamransom.github.io/posts/rasp ... rt-1.html in the terminal but got a seg fault. I believe this is because it is supposed to be run as a standalone without an OS so there has to be some memory map conversion needed to be run under Raspbian in a terminal. Granted this was for the green LED but I don't think it would have mattered if I was asking the GPU for the firmware version or some other piece of info. I used this example just because it was pretty short and straight forward.

Thanks

fm2606

lpoulain
Posts: 24
Joined: Mon May 20, 2019 12:35 am

Re: writing to terminal using gpu and asm

Sun Jun 23, 2019 1:13 am

The closest I can think of is the samples on https://github.com/bztsrc/raspi3-tutorial. Granted, they are using C instead of assembly, but you will find how to print to UART (05_uart0) and on the screen (0A_pcscreenfont)

For the rest, any specific reason why you want to use assembly only? It's generally used to perform operations you cannot do in C (like accessing registers or raising an interrupt), as C is probably as performant as asm with all the compiler optimizations.

bzt
Posts: 393
Joined: Sat Oct 14, 2017 9:57 pm

Re: writing to terminal using gpu and asm

Fri Jun 28, 2019 5:22 pm

Hi,

There are three things here to consider:

1) GPU vs. ARM
Most tutorials are running on the ARM, and not on the GPU. Compiler and assemblers for the GPU exists, but they are not free nor Open Source as far as I know. (But I could be wrong)

2) Writing on the terminal
You would do the same way as in the tutorials no matter if GPU or ARM executes the code. You access the UART MMIO and use it's registers to send and receive a character.

3) terminal dialogs
This is implemented in ncurses lib. Look it up. If you want to omit loading a library in bare metal, you can do basic stuff by manually printing CSI sequences. There's a fairly standard subset which all terminals and terminal emulators understand. If you want more advanced stuff, then you have to use ncurses (or curses) because they also detect the terminal type and use the appropriate CSI codes for them.

Standard CSI sequences include VT-100 sequences (vt100.net), and some of them are also specified in ANSI (Wikipedia on ANSI escape sequences). A very good summary of the sequences can be found here: http://www.termsys.demon.co.uk/vtansi.htm.

To display a dialogue, you should do the following:
1) write (ESC)[2J to the UART to clear the terminal's screen on the other side
2) write (ESC)[999;999H to move cursor as far as possible
3) write (ESC)[6n to query the cursor position
4) read and parse (ESC)[(Y);(X)R, this will give you the terminal's dimensions
5) move your cursor where you want it with (ESC)[(Y);(X)H (probably at center of the terminal minus half of your dialog's size)
6) change color with (ESC)[(codes)m for example (ESC)[30;47m selects black text on white background
7) print what you want, probably lots of spaces to draw the dialog box

An easy way to get the sequences is to use the Linux command "dialog" on a Raspberry Pi. Create a dialog box with it and redirect it's output into a file. Exit, and check out the file which sequences it contains. It is very likely that "dialog" will use Linux terminal specific sequences too, to avoid that, you must set the TERM environment variable to "vt100" first.

The user input should generate similar sequences. For example, if user presses [up] arrow in the terminal, then it most certainly sends the cursor up sequence (ESC)[A, [down] arrow sends cursor down sequence (ESC)[B, and [Enter] sends carrige return and new line (0x0d + 0x0a, or '\r' + '\n').

Cheers,
bzt

jahboater
Posts: 4840
Joined: Wed Feb 04, 2015 6:38 pm

Re: writing to terminal using gpu and asm

Fri Jun 28, 2019 5:33 pm

bzt wrote:
Fri Jun 28, 2019 5:22 pm
An easy way to get the sequences is to use the Linux command "dialog" on a Raspberry Pi. Create a dialog box with it and redirect it's output into a file. Exit, and check out the file which sequences it contains. It is very likely that "dialog" will use Linux terminal specific sequences too, to avoid that, you must set the TERM environment variable to "vt100" first.
Another handy Linux command for this is "showkey -a"

fm2606
Posts: 3
Joined: Sat Jun 22, 2019 5:19 pm

Re: writing to terminal using gpu and asm

Sun Jun 30, 2019 1:32 pm

Thanks for the replies!

@bzt

For option 2, that would require me to connect a monitor to the GPIO pins correct?
3) This sounds more what I am looking for even though it would require writing my own print routines to take care of it, which is fine.

@jahboater...Neat little command.

@lpoulain...You are absolutely right it would be easier and quicker to do it the way you suggested.. Now to the question of why write in ASM...not sure I can answer it in a single statement or two without some background history of myself:

-- Grew up on the Commodore 64, learned BASIC and wrote my own games ("Creating Arcade Games on The C64" was my bible!). Didn't know about the books that taught assembly language on the C64, which is odd because I literally read Compute's Gazette magazine from cover to cover each month and read all the advertisements.
-- Through out the 80s and 90s always wanted to learn ASM on the PC but 1) never took the time and 2) was too cheap to buy an assembler and if there was a free version I didn't know about it. I would have been working on MS-DOS and/or Win 3.1 (yuck!)
-- Took a computer systems/architecture class where we had to disassemble a program and find the loop holes to keep it from "blowing up" . I really enjoyed the class but didn't pursue it after the class was over as I was trying to finish my degree, work full time and raise a family.
-- "Discovered" RPi a year or so ago and decided to learn asm. I like the idea of the RISC ISA being easier then CISC ISA. Learning ASM on RPi and the RPi in general reminds me of my days as a kid programming on the C64. I miss the days of simplicity.
-- Always disagreed when people would suggest to others who wanted to write their own game engine that they should use someone else's game engine because it works. Pretty sure the people who want to write their own game engine, and have to ask about it on a forum, aren't going to write a commerically viable game so why not write the whole thing themselves and probably learn something while they are at it.

So, same thing here. I would prefer to do it myself instead of using someone else's code - just for simple fact of learning as much as I can. Now, if I need something usable, and need it tomorrow for my job or whatever, then yeah I will use Python or whatever language is most appropriate. But if I am learning something, I'd like to learn as much as possible. It doesn't mean I will shun someone else's code, but I definitely would tear it apart until I understood it.

That's just the way I think and like to approach something I truly want to learn like ASM for RPi

User avatar
DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: writing to terminal using gpu and asm

Sun Jun 30, 2019 4:45 pm

The responses so far missed that you are attempting to do this in Linux. Unfortunately the forum lumps bare metal and assembly language, so you are asking about using assembly on Linux?


For outputing to the terminal, you just call SWI 0 with R7=4 (write), R0=1 (stdout), R1=Address of buffer, R2=length. For control codes, vt100 I believe is the standard used by the Linux/Unix terminals so that will give you the ability to do the text mode full screen positional dialogs like you want.

You could use OABI as well in which case the SWI to call is SWI 0x900004 (system call write), this is technically more correct as it follows the guidelines set for ARM based OS's using SWI's as originally required by the designers of the ARM CPU, just because they decided to change things to break many other things does not mean it is a good idea to follow there breaking OS's guidelines.

As far as using the GPU to do this, that is a different game. You will probably have to go through the OpenGL interface to accomplish that.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

bzt
Posts: 393
Joined: Sat Oct 14, 2017 9:57 pm

Re: writing to terminal using gpu and asm

Mon Jul 01, 2019 4:07 pm

fm2606 wrote:
Sun Jun 30, 2019 1:32 pm
For option 2, that would require me to connect a monitor to the GPIO pins correct?
Erhm, not sure I follow you. Those were not options, but required steps. Also you can't connect a monitor to the GPIO pins, there's a HDMI port for that.

In theory you should connect a video terminal to the GPIO, like the DEC VT100, but those are not manufactured for a long time now. So instead you connect a PC running a terminal emulator application. Just a side note, the original terminal was basically a monitor with a keyboard connected to the machine over the cheap serial line; and it had keys like "Do", "Copy", "Paste" etc. you will not find on any modern keyboards. Modern keyboards have many Fn keys instead.

You should use a serial cable with 3 wires: DATA TRANSFER, DATA RECEIVE, GROUND. Or more likely an USB serial cable because recent PCs lack RS232 ports. There don't use red, green is transfer, white is receive, black is ground; and they have nice little endings to plug them into the GPIO header.

Code: Select all

RPi GPIO (pins 14 / 15)  --------- RS232/USB (aka COM1, PC running minicom, PuTTY or other similar terminal emulator)
fm2606 wrote:3) This sounds more what I am looking for even though it would require writing my own print routines to take care of it, which is fine.
Your print routine does not have to do anything. The CSI sequences are printed just as any other characters. It is the terminal's duty to interpret them (and indeed they do).

I'll try to explain how this works in more detail.

1. Layer - you application
This could run on the GPU in theory, but most tutorials are for the ARM. This is the one you want to write in Assembly. At this level you construct the sequences into strings to print.

2. Layer - print
This is a user space function used to print strings. This level does not care what's in the strings.
On bare metal you have to implement this yourself, no matter what. On Linux, you can link your application with libc and use printf, or you can implement it yourself. For the latter it should call the system call for write(1, string, length), see the SVC instruction and it's arguments for Linux. The system call hides the other layers from you, so that's all you need for Linux.

3. Layer - hardware
Now if you are not using Linux rather bare metal, then you have to implement the glue code that connects the print interface with the actual hardware. This usually done by iterating on the string and sending each character to the UART (or render the glyph on screen), one by one.

With UART, you have two options: PL011 chip and miniAUX, both are integrated (think of them as COM1 and COM2). You have to initialize them apriori to connect one of them to GPIO header pins 14 and 15, set baud rate etc. Sending one character usually consist of a loop, waiting the UART to became ready to send, and a send operation. These are implemented by reading/writing MMIO addresses of the appropriate UART chip. There are many C examples for this, and it's pretty straightforward to convert those into Assembly.

Now terminals and "dialogs" like raspi-config's boxes are sitting on top of all of these. Your application can print special sequences that the other side receiver should understand, and the other side may send sequences in response that your application must parse (such as key presses). Your application can do this on it's own, it's not that hard if you limit yourself to VT100 sequences only and you don't want to use special keys. But to help applications to handle the many different terminal types with many different keys (hence different sequences), the user space library libncurses was written (we could call this 0. Layer). You link your application with this library and you can use high level functions for drawing boxes and things alike. But under the hood, libncurses just calls the kernel's write(1, string, length) routine writing the appropriate escape sequence, that's all.

Hope this helps,
bzt

fm2606
Posts: 3
Joined: Sat Jun 22, 2019 5:19 pm

Re: writing to terminal using gpu and asm

Tue Jul 02, 2019 12:08 pm

So my apologizes as I now realize I used incorrect terminology.
bzt wrote: You should use a serial cable with 3 wires: DATA TRANSFER, DATA RECEIVE, GROUND. Or more likely an USB serial cable because recent PCs lack RS232 ports. There don't use red, green is transfer, white is receive, black is ground; and they have nice little endings to plug them into the GPIO header.
Above is what I meant about attaching a monitor. Just last week I saw a video of a guy cutting a co-workers mouse and creating the TTL cable and hooking it up to a laptop.

The second thing, and this goes along with the point DavidS made, I am wanting to do all this using ASM in a Linux environment running my program in LX Terminal.

Again, sorry for the lack of proper terms and correctly stating what I was after.
bzt wrote: Standard CSI sequences include VT-100 sequences (vt100.net), and some of them are also specified in ANSI (Wikipedia on ANSI escape sequences). A very good summary of the sequences can be found here: http://www.termsys.demon.co.uk/vtansi.htm.

To display a dialogue, you should do the following:
1) write (ESC)[2J to the UART to clear the terminal's screen on the other side
2) write (ESC)[999;999H to move cursor as far as possible
3) write (ESC)[6n to query the cursor position
4) read and parse (ESC)[(Y);(X)R, this will give you the terminal's dimensions
5) move your cursor where you want it with (ESC)[(Y);(X)H (probably at center of the terminal minus half of your dialog's size)
6) change color with (ESC)[(codes)m for example (ESC)[30;47m selects black text on white background
7) print what you want, probably lots of spaces to draw the dialog box
I was able to do the the above in LX Terminal but when I wrote out (ESC)[6n and the (ESC)[(Y);(X)R results where given, my program hung after the SVC 0 statement in the PRNT section.

Here is what I had:

Code: Select all

ldr           r1,  =eoscr                     @ eoscr defined as (ESC)[999;999H item #2 above
mov        r2,  #10
bl            prnt

ldr           r1,   =curpos	               @ curpos defined as (ESC)[6n item # 4 above
mov        r2,  #4
bl            prnt

prnt:
        mov        r0,  #1
	mov        r7,  #4
	svc          0
	bx            lr	
( I tried to properly format by taking out all the tabs but guess I missed some)

But it is a start. I have some work and a lot of learning still to do but enjoying it. Like I said in an earlier post it takes me back to days as a kid learning to program the C64!

bzt
Posts: 393
Joined: Sat Oct 14, 2017 9:57 pm

Re: writing to terminal using gpu and asm

Tue Jul 02, 2019 2:47 pm

fm2606 wrote:
Tue Jul 02, 2019 12:08 pm
Again, sorry for the lack of proper terms and correctly stating what I was after.
No worries, we figured it out after all :-) The only confusing thing is, that you want to use Linux and this is the Bare metal section, but now I see there's no separate Assembly section.
fm2606 wrote:I was able to do the the above in LX Terminal but when I wrote out (ESC)[6n and the (ESC)[(Y);(X)R results where given, my program hung after the SVC 0 statement in the PRNT section.
Well done!

As for the (ESC)[6n, the response must be read before you can continue. This could be a problem if you have the tty in line buffered mode (which is the default), because the response (ESC)[(Y);(X)R does not end in a newline character, therefore it's not sent to your application until you press an [Enter]. To overcome this, you'll need termios, because that's the only way to set unbuffered raw mode, baud rate etc. in a standard way (latter not important when your application's stdout is connected to a pseudo terminal, but essential when you want to use real serial line device). If I were you, I'd consider using libncurses. There you can set the fd into raw mode with a simple function call. Also you don't have to care about reading the response to a sequence, you'll going to have a lot higher abstraction level.
fm2606 wrote:But it is a start. I have some work and a lot of learning still to do but enjoying it. Like I said in an earlier post it takes me back to days as a kid learning to program the C64!
Yes :-D Only Linux is a "little bit" more complex than Commodore Basic was :-) It has all the legacy of unices ever since the Multics in the '60s, which could be overwhelming sometimes. The termios interface is a good example. On the other hand, being an old interface, it's well tested and guaranteed to work ;-)

Cheers,
bzt

Return to “Bare metal, Assembly language”