Skip to content

Instantly share code, notes, and snippets.

@andrewstellman
Created May 16, 2025 21:08
Show Gist options
  • Select an option

  • Save andrewstellman/86b33ff92edd1320d2727e80f07eb9d9 to your computer and use it in GitHub Desktop.

Select an option

Save andrewstellman/86b33ff92edd1320d2727e80f07eb9d9 to your computer and use it in GitHub Desktop.

COBOL Terminal Cursor Control Demo

This is a small experimental COBOL project I built to learn a little bit about COBOL. The goal was simple: see if COBOL could support basic interactive console behavior—specifically, moving an asterisk around the screen using the W/A/S/D keys.

It uses raw ANSI escape sequences for cursor control and a small C helper to handle non-blocking keyboard input (so the user doesn't have to press Enter). The original version got stuck in a classic AI rehash loop—ChatGPT kept giving plausible but ineffective tweaks. I revisited it later using the five Sens-AI habits (Context, Research, Problem Framing, Refining, and Critical Thinking), and that broke the loop and got everything working.


🔧 How to Build and Run

Make sure you have:

  • A terminal that supports ANSI escape codes
  • cobc (from GNU COBOL)
  • gcc

Steps:

# Compile the C helper to capture raw keyboard input (without pressing enter)
gcc -fPIC -shared -o libgetch.so getch.c

# Compile and link the COBOL program with the C object file
cobc -x -o move-asterisk-raw move-asterisk-raw.cob -L. -lgetch

# Run the resulting binary
./move-asterisk-raw

The program should clear the screen and allow you to move the asterisk using the W, A, S, and D keys—without needing to hit Enter.

#include <termios.h>
#include <unistd.h>
void getch(char *out) {
struct termios oldt, newt;
char ch;
tcgetattr(STDIN_FILENO, &oldt); // Save current terminal settings
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO); // Disable canonical mode and echo
tcsetattr(STDIN_FILENO, TCSANOW, &newt); // Apply new settings
read(STDIN_FILENO, &ch, 1); // Read one character
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); // Restore original settings
*out = ch; // Pass back to COBOL
}
IDENTIFICATION DIVISION.
PROGRAM-ID. MoveAsteriskRaw.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 ROW PIC 99 VALUE 10.
01 COL-VALUE PIC 99 VALUE 10.
01 INPUT-KEY PIC X.
01 LOOP-FLAG PIC X VALUE "Y".
01 ROW-STR PIC 99.
01 COL-STR PIC 99.
01 CURSOR-CODE PIC X(20).
01 ESCAPE-SEQ PIC X VALUE X"1B".
01 CLEAR-CODE PIC X(10).
PROCEDURE DIVISION.
MAIN-LOOP.
PERFORM UNTIL LOOP-FLAG = "N"
*> Clear the screen
STRING ESCAPE-SEQ "[2J"
DELIMITED BY SIZE INTO CLEAR-CODE
DISPLAY CLEAR-CODE NO ADVANCING
*> Move the cursor and draw the asterisk
PERFORM SHOW-ASTERISK
*> Display instructions at a fixed position
DISPLAY ESCAPE-SEQ "[22;1H" NO ADVANCING
DISPLAY "Use W/A/S/D to move, Q to quit."
*> Get one raw character from the user
CALL "getch" USING INPUT-KEY
*> Interpret movement
EVALUATE FUNCTION UPPER-CASE(INPUT-KEY)
WHEN "W"
IF ROW > 1
SUBTRACT 1 FROM ROW
END-IF
WHEN "S"
IF ROW < 20
ADD 1 TO ROW
END-IF
WHEN "A"
IF COL-VALUE > 1
SUBTRACT 1 FROM COL-VALUE
END-IF
WHEN "D"
IF COL-VALUE < 20
ADD 1 TO COL-VALUE
END-IF
WHEN "Q"
MOVE "N" TO LOOP-FLAG
END-EVALUATE
END-PERFORM
DISPLAY ESCAPE-SEQ "[23;1H"
DISPLAY "Goodbye!"
STOP RUN.
SHOW-ASTERISK.
MOVE ROW TO ROW-STR
MOVE COL-VALUE TO COL-STR
STRING
ESCAPE-SEQ "[" DELIMITED BY SIZE
ROW-STR DELIMITED BY SIZE
";" DELIMITED BY SIZE
COL-STR DELIMITED BY SIZE
"H" DELIMITED BY SIZE
INTO CURSOR-CODE
DISPLAY CURSOR-CODE NO ADVANCING
DISPLAY "*" NO ADVANCING.
@andrewstellman
Copy link
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment