1
0
mirror of https://github.com/moshix/mvs.git synced 2026-05-09 01:17:31 +00:00
Files
moshix.mvs/slanted.bash
moshix 16bb567813 JES2 inspired slanted text generator
uses the HASPPRPRU method to print slanted words
2026-03-21 08:36:30 +01:00

150 lines
5.9 KiB
Bash

#!/usr/bin/env bash
# slanted.bash — HASP-style block letter renderer
# All rights reserved 2026 by moshhix
# Adapted from IBM HASP Print/Punch Service BLKPRT routine.
# The mainframe assembly works as follows:
#
# 1. Each letter is stored as 12 rows of 2-byte (16-bit) masks in BLOCKA table
# 2. For each output line (line counter 0,2,4,...,22 = 12 lines):
# For each character in the text:
# - Load 2-byte mask for current row
# - BLKLOOP: shift mask left (ALR R15,R15), if carry (high bit was 1),
# print the character; otherwise leave blank
# - Loop 12 times for 12 columns per block letter
# 3. Slant: position += 6, then subtract (line_counter / 2)
# This shifts top rows right and bottom rows left, creating italic effect
# 4. 2 blank columns between each block letter, up to 8 characters
#
# This bash script follows the same approach using bitmask arrays.
#
# Usage: ./slanted.bash "WORD"
FONT_HEIGHT=12
CHAR_WIDTH=12
# BLOCKA equivalent: font bitmasks
# 12 hex values per character (one per row), representing 12-bit patterns
# Bit 11 (0x800) = leftmost column, bit 0 (0x001) = rightmost column
# Where a bit is 1, print the character itself; where 0, print a space.
declare -A GLYPH
GLYPH[" "]="000 000 000 000 000 000 000 000 000 000 000 000"
GLYPH["A"]="1FE 303 303 303 3FE 3FE 303 303 303 303 303 303"
GLYPH["B"]="FFC FFC C03 C03 FFC FFC C03 C03 C03 C03 FFC FFC"
GLYPH["C"]="FFF FFF C03 C00 C00 C00 C00 C00 C00 C03 FFF FFC"
GLYPH["D"]="FFC FFC C03 C03 C03 C03 C03 C03 C03 C03 FFC FFC"
GLYPH["E"]="FFF FFF C00 C00 FF0 FF0 C00 C00 C00 C00 FFF FFF"
GLYPH["F"]="FFF FFF C00 C00 C00 FF0 FF0 C00 C00 C00 C00 C00"
GLYPH["G"]="7FE 7FE C03 C00 C00 C3F C3F C03 C03 C03 7FE 7FE"
GLYPH["H"]="C03 C03 C03 C03 FFF FFF C03 C03 C03 C03 C03 C03"
GLYPH["I"]="FFF FFF 060 060 060 060 060 060 060 060 FFF FFF"
GLYPH["J"]="3FF 3FF 003 003 003 003 C03 C03 C03 E03 7FE 3FC"
GLYPH["K"]="C03 C03 C06 C0C C30 FE0 FE0 C30 C0C C06 C03 C03"
GLYPH["L"]="C00 C00 C00 C00 C00 C00 C00 C00 C00 C00 FFF FFF"
GLYPH["M"]="C03 E07 F0F D9B CF3 C63 C03 C03 C03 C03 C03 C03"
GLYPH["N"]="E03 E03 F03 D83 CC3 C63 C33 C1B C0F C07 C03 C03"
GLYPH["O"]="7FE 7FE C03 C03 C03 C03 C03 C03 C03 C03 7FE 7FE"
GLYPH["P"]="FFF FFF C03 C03 FFF FFF C00 C00 C00 C00 C00 C00"
GLYPH["Q"]="7FE C03 C03 C03 C03 C03 C1B C0F C07 C03 7FF 7FF"
GLYPH["R"]="FFF FFF C03 C03 FFF FFE C30 C18 C0C C06 C03 C03"
GLYPH["S"]="7FE C03 C00 C00 7FC 7FC 003 003 C03 C03 7FE 7FE"
GLYPH["T"]="FFF FFF 060 060 060 060 060 060 060 060 060 060"
GLYPH["U"]="C03 C03 C03 C03 C03 C03 C03 C03 C03 C03 FFF FFF"
GLYPH["V"]="C03 606 30C 186 0CC 0CC 186 30C 606 3FC 1F8 0F0"
GLYPH["W"]="C03 C03 C63 CF3 DFB F0F E07 C03 C03 C03 C03 C03"
GLYPH["X"]="C03 C03 606 30C 1F8 1F8 30C 606 C03 C03 C03 C03"
GLYPH["Y"]="C03 C03 606 30C 1F8 0F0 060 060 060 060 060 060"
GLYPH["Z"]="FFF 003 006 00C 018 030 060 0C0 180 300 FFF FFF"
GLYPH["0"]="7FE 7FE C0F C1B C33 C63 CC3 D83 F03 E03 7FE 3FC"
GLYPH["1"]="060 0E0 1E0 060 060 060 060 060 060 060 FFC FFC"
GLYPH["2"]="7FE C03 C03 003 00E 030 0C0 300 C00 C00 FFC FFC"
GLYPH["3"]="7FE C03 003 003 1FE 1FE 003 003 C03 C03 7FE 7FE"
GLYPH["4"]="C03 C03 C03 C03 FFF FFF 003 003 003 003 003 003"
GLYPH["5"]="FFF C00 C00 C00 FFC FFC 003 003 C03 C03 7FE 7FE"
GLYPH["6"]="7FE C03 C00 C00 FFC FFF C03 C03 C03 C03 7FE 7FE"
GLYPH["7"]="FFC FFC 003 006 00C 018 030 060 0C0 180 300 300"
GLYPH["8"]="7FE C03 C03 C03 7FE 7FE C03 C03 C03 C03 7FE 7FE"
GLYPH["9"]="7FE C03 C03 C03 7FE 7FE 003 003 C03 C03 7FE 7FE"
GLYPH["-"]="000 000 000 000 FFF FFF 000 000 000 000 000 000"
GLYPH["."]="000 000 000 000 000 000 000 000 000 0F0 0F0 000"
GLYPH["!"]="0F8 0F8 0F8 0F8 0F8 0F8 0F8 0F8 000 000 0F8 0F8"
GLYPH["?"]="7FC C03 003 006 01C 030 030 000 000 030 030 000"
if [[ $# -eq 0 ]]; then
echo "Usage: $0 \"WORD\""
echo "Supports: A-Z 0-9 space - . ! ?"
exit 1
fi
word="${1^^}" # uppercase
# BLKPRT rendering
# Like the assembly, we build each output line by scanning all characters
# and using bitmask shift-and-test to place characters or blanks.
echo ""
max_width=0
# BLKBLD: loop through 12 output lines (assembly: line counter 0,2,...,22)
for ((line=0; line<FONT_HEIGHT; line++)); do
# Slant calculation (assembly SKIP520 logic)
# Assembly: LA R5,6(,R5) → start 6 positions right of center
# SRL R4,1 → line_index = line_counter / 2
# SLR R5,R4 → position -= line_index
# Net offset = 6 - line_index
# Normalized for terminal (min offset = 6-11 = -5, add 5):
indent=$((FONT_HEIGHT - 1 - line))
if ((indent > 0)); then
printf -v lead "%${indent}s" ""
else
lead=""
fi
row_text="$lead"
# BLKLUP: loop through each character in the word
for ((c=0; c<${#word}; c++)); do
char="${word:$c:1}"
# BLOCKTR equivalent: look up character in glyph table
[[ -z "${GLYPH[$char]+x}" ]] && char=" "
# Get the bitmask row for this character at this line
read -ra masks <<< "${GLYPH[$char]}"
mask=$((16#${masks[$line]}))
# BLKLOOP: shift mask left and test high bit, 12 columns
# Assembly: ALR R15,R15 (shift left); BC 12,SKIP530 (skip if no carry)
# MVC 0(1,R5),$POSTSAV (print char if carry)
for ((bit=CHAR_WIDTH-1; bit>=0; bit--)); do
if (( mask & (1 << bit) )); then
# Carry set: print the text character itself ($POSTSAV)
row_text+="${char}"
else
# No carry: leave blank
row_text+=" "
fi
done
# 2 blanks between blocks (assembly: LA R5,2(,R5))
if ((c < ${#word} - 1)); then
row_text+=" "
fi
done
printf "%s\n" "$row_text"
# max width for stats
trimmed="$row_text"
while [[ "${trimmed: -1}" == " " ]] && [[ -n "$trimmed" ]]; do
trimmed="${trimmed%?}"
done
len=${#trimmed}
((len > max_width)) && max_width=$len
done
echo ""
printf "Word: %-30s Characters: %2d Width: %d columns\n" \
"\"$1\"" "${#1}" "$max_width"