TDXBITS - Bits, Bytes & Beyond
Tony DeLia - PeopleSoft Developer | Technical Consultant. Enjoying a productive career in the Information Technology profession, with significant expertise developing pc, client-server, mainframe, relational database & ERP package solutions (PeopleSoft/Oracle). Welcome to my Blog... enjoy the smattering of bits, bytes, & words (that's lo-level machine code lingo)... This blog will serve as a historical monument to some of my past work.
Monday, February 23, 2026
Sunday, February 2, 2025
A Matter of Time - Part 2 - COBOL
A Matter of Time - Part 1 - Introduction
Back again to build a version of the TDF_LAP() Timing function in COBOL. This is a set of 21 timing entries used to easily monitor timings for embedded or stand-alone routines or SQL statements.
Flashback: LEGO Guy Loves Assembler
Of course the LEGO Guy loves Assembler - virtually building programs brick by brick - but he also likes to warm up with other languages - this time COBOL. After building the lapse routine - TDF-LAP - he'll give it a quick test run. Just a main process that calls another one - with the nested timings produced for each.
Sample Log Output:
This version displays the level along with BEG/END times, lapsed time and the message.
TDF-LAP Function (Procedure) - TDFPROC.CPY
*****************************************************************
* TDF LAP - LAPSE BETWEEN BEGIN/END TIMES (SQL TIMINGS) *
*****************************************************************
* *
* FIELD I/O DESCRIPTION *
* ----------------- --- ---------------------------------- *
* TDX-LAP-OPER I OPERATION TYPE (BEG/END) *
* TDX-LAP-LEV I LAPSE INDEX SET (0 THRU 20) *
* TDX-LAP-MSG I DISPLAY MESSAGE TEXT *
* TDX-LAP-DIFF O BEG/END LAPSE IN SECONDS *
* TDX-LAP-TXT-HMS O BEG/END LAPSE IN HHHH:MM:SS.SSSSS *
* *
*****************************************************************
* *
* EXAMPLE: *
* ---------------------------------------------------------- *
* MOVE 0 TO TDX-LAP-LEV *
* MOVE 'BEG' TO TDX-LAP-OPER *
* PERFORM TDF-LAP *
* <snip> *
* MOVE 0 TO TDX-LAP-LEV *
* MOVE 'END' TO TDX-LAP-OPER *
* MOVE 'PROCESS-MAIN' TO TDX-LAP-MSG *
* PERFORM TDF-LAP *
* ---------------------------------------------------------- *
* RESULTS: *
* L(00) BEG(2024:12:17_16:41:42) END(2024:12:17_16:41:43) - *
* HMS(0000:00:01.35000) MSG: PROCESS-MAIN *
* ---------------------------------------------------------- *
* *SINGLE LINE DISPLAYED FOR EACH NESTED LEVEL* *
*****************************************************************
TDF-LAP.
IF TDX-LAP-LEV-OK
COMPUTE TDX-LAP-IDX = TDX-LAP-LEV + 1
MOVE FUNCTION CURRENT-DATE TO TDX-LAP-END(TDX-LAP-IDX)
IF TDX-LAP-OPER-END
MOVE TDX-LAP-BEG(TDX-LAP-IDX) TO TDX-SEC-IN
PERFORM TDF-SECONDS
MOVE TDX-SEC-OUT TO TDX-LAP-BEG-SEC
MOVE TDX-SEC-IN( 1:4) TO TDX-LAP-TXT-BEG-Y
MOVE TDX-SEC-IN( 5:2) TO TDX-LAP-TXT-BEG-M
MOVE TDX-SEC-IN( 7:2) TO TDX-LAP-TXT-BEG-D
MOVE TDX-SEC-IN( 9:2) TO TDX-LAP-TXT-BEG-H
MOVE TDX-SEC-IN(11:2) TO TDX-LAP-TXT-BEG-I
MOVE TDX-SEC-IN(13:2) TO TDX-LAP-TXT-BEG-S
MOVE TDX-LAP-END(TDX-LAP-IDX) TO TDX-SEC-IN
PERFORM TDF-SECONDS
MOVE TDX-SEC-OUT TO TDX-LAP-END-SEC
MOVE TDX-SEC-IN( 1:4) TO TDX-LAP-TXT-END-Y
MOVE TDX-SEC-IN( 5:2) TO TDX-LAP-TXT-END-M
MOVE TDX-SEC-IN( 7:2) TO TDX-LAP-TXT-END-D
MOVE TDX-SEC-IN( 9:2) TO TDX-LAP-TXT-END-H
MOVE TDX-SEC-IN(11:2) TO TDX-LAP-TXT-END-I
MOVE TDX-SEC-IN(13:2) TO TDX-LAP-TXT-END-S
COMPUTE TDX-LAP-DIFF = TDX-LAP-END-SEC
- TDX-LAP-BEG-SEC
MOVE TDX-LAP-DIFF TO TDX-HMS-SEC
PERFORM TDF-HMS
MOVE TDX-LAP-LEV TO TDX-LAP-TXT-LEV
MOVE TDX-HMS-TXT TO TDX-LAP-TXT-HMS
MOVE TDX-LAP-MSG TO TDX-LAP-TXT-MSG
DISPLAY TDX-LAP-TXT
END-IF
MOVE FUNCTION CURRENT-DATE TO TDX-LAP-BEG(TDX-LAP-IDX)
ELSE
DISPLAY '***ERROR - LEVELS 0 THRU 20 ONLY***'
END-IF
.
TDF-LAP-EXIT.
EXIT.
*****************************************************************
An additional function - TDF-SECONDS - is used to convert the dates to seconds. The intrinsic function INTEGER-OF-DATE returns the number of days since January 1, 1601 for both BEG/END values. The days are converted to seconds with a full day being removed. The time (Hours, minutes, seconds) are then converted and added to the total seconds. This allows an accurate lapsed time to be calculated by the TDF-LAP primary function.Yet one more function is utilized to convert the lapsed seconds into HHHH:MI.SS.SSSSS format - TDF-HMS.
* TDF SECONDS - CONVERT FULL DATE FIELD TO SECONDS *
*****************************************************************
* *
* FIELD I/O DESCRIPTION *
* ----------------- --- ---------------------------------- *
* TDX-SEC-IN I FULL 20 CHARACTER DATE/TIME FIELD *
* TDX-SEC-OUT O CONVERSION TO SECONDS *
* *
*****************************************************************
* *
* EXAMPLE: *
* ---------------------------------------------------------- *
* MOVE FUNCTION CURRENT-DATE TO TDX-SEC-IN *
* PERFORM TDF-SECONDS *
* DISPLAY 'SECONDS: ' TDX-SEC-OUT *
* ---------------------------------------------------------- *
* RESULTS: 2024121715132474+050 => 013379008404.74000 *
* *
*****************************************************************
TDF-SECONDS.
COMPUTE TDX-SEC-INT
= FUNCTION INTEGER-OF-DATE(TDX-SEC-DATE)
COMPUTE TDX-SEC-OUT
= (TDX-SEC-INT * (24*60*60))
- (24*60*60)
+ (TDX-SEC-HH * 3600)
+ (TDX-SEC-MI * 60)
+ (TDX-SEC-SS)
+ (TDX-SEC-MS / 100)
.
TDF-SECONDS-EXIT.
EXIT.
*****************************************************************
This stand-alone function simply converts seconds into a more readable format - Hours, Minutes and Seconds. This will appear on the TDF-LAP Timing Log Output.
* TDF HMS - CONVERT SECONDS TO HHHH:MM:SS.SSSSS FORMAT *
*****************************************************************
* *
* FIELD I/O DESCRIPTION *
* ----------------- --- ---------------------------------- *
* TDX-HMS-SEC I NUMBER OF SECONDS *
* TDX-HMS-TXT O OUTPUT STRING: HHHH:MM:SS.SSSSS *
* *
*****************************************************************
* *
* EXAMPLE: *
* ---------------------------------------------------------- *
* MOVE 22506.256 TO TDX-HMS-SEC *
* PERFORM TDF-HMS *
* DISPLAY " OUT: " TDX-HMS-TXT *
* ---------------------------------------------------------- *
* RESULTS: 0006:15:06.25600 (22506.256 SECONDS) *
* *
*****************************************************************
TDF-HMS.
COMPUTE TDX-HMS-HH = TDX-HMS-SEC / 3600
COMPUTE TDX-HMS-SEC = TDX-HMS-SEC
- (TDX-HMS-HH * 3600)
COMPUTE TDX-HMS-MM = TDX-HMS-SEC / 60
COMPUTE TDX-HMS-SEC = TDX-HMS-SEC
- (TDX-HMS-MM * 60)
COMPUTE TDX-HMS-SS = TDX-HMS-SEC
MOVE TDX-HMS-HH TO TDX-HMS-TXT-HH
MOVE TDX-HMS-MM TO TDX-HMS-TXT-MM
MOVE TDX-HMS-SS TO TDX-HMS-TXT-SS
.
TDF-HMS-EXIT.
EXIT.
*****************************************************************
The working storage fields/arrays are contained in the TDFWORK.CPY copybook.
* TDF FUNCTION PARAMETERS / WORKING STORAGE *
*****************************************************************
01 TDX-HMS-PARMS.
05 TDX-HMS-SEC PIC 9(12)V9(5) COMP-3.
05 TDX-HMS-HH PIC 9(6) COMP-3.
05 TDX-HMS-MM PIC 9(3) COMP-3.
05 TDX-HMS-SS PIC 9(3)V9(5) COMP-3.
05 TDX-HMS-TXT.
10 TDX-HMS-TXT-HH PIC 9(4).
10 FILLER PIC X VALUE ':'.
10 TDX-HMS-TXT-MM PIC 9(2).
10 FILLER PIC X VALUE ':'.
10 TDX-HMS-TXT-SS PIC 9(2).9(5).
01 TDX-SECONDS-PARMS.
05 TDX-SEC-INT PIC 9(8).
05 TDX-SEC-OUT PIC 9(16)V9(5) COMP-3.
05 TDX-SEC-IN.
10 TDX-SEC-DATE PIC 9(8).
10 TDX-SEC-TIME.
15 TDX-SEC-HH PIC 9(2).
15 TDX-SEC-MI PIC 9(2).
15 TDX-SEC-SS PIC 9(2).
15 TDX-SEC-MS PIC 9(2).
10 TDX-SEC-GMT PIC S9(4).
01 TDX-LAP-PARMS.
05 TDX-LAP-OPER PIC X(3).
88 TDX-LAP-OPER-BEG VALUE 'BEG'.
88 TDX-LAP-OPER-END VALUE 'END'.
05 TDX-LAP-LEV PIC 99.
88 TDX-LAP-LEV-OK VALUES 0 THRU 20.
05 TDX-LAP-MSG PIC X(50).
05 TDX-LAP-BEG-SEC PIC 9(12)V9(5) COMP-3.
05 TDX-LAP-END-SEC PIC 9(12)V9(5) COMP-3.
05 TDX-LAP-DIFF PIC 9(12)V9(5) COMP-3.
05 TDX-LAP-IDX PIC 99.
05 TDX-LAP-MTX OCCURS 21 TIMES.
10 TDX-LAP-BEG PIC X(20).
10 TDX-LAP-END PIC X(20).
05 TDX-LAP-TXT.
10 FILLER PIC X(2) VALUE 'L('.
10 TDX-LAP-TXT-LEV PIC 99.
10 FILLER PIC X(6) VALUE ') BEG('.
10 TDX-LAP-TXT-BEG-Y PIC 9999.
10 FILLER PIC X VALUE ':'.
10 TDX-LAP-TXT-BEG-M PIC 99.
10 FILLER PIC X VALUE ':'.
10 TDX-LAP-TXT-BEG-D PIC 99.
10 FILLER PIC X VALUE '_'.
10 TDX-LAP-TXT-BEG-H PIC 99.
10 FILLER PIC X VALUE ':'.
10 TDX-LAP-TXT-BEG-I PIC 99.
10 FILLER PIC X VALUE ':'.
10 TDX-LAP-TXT-BEG-S PIC 99.
10 FILLER PIC X(6) VALUE ') END('.
10 TDX-LAP-TXT-END-Y PIC 9999.
10 FILLER PIC X VALUE ':'.
10 TDX-LAP-TXT-END-M PIC 99.
10 FILLER PIC X VALUE ':'.
10 TDX-LAP-TXT-END-D PIC 99.
10 FILLER PIC X VALUE '_'.
10 TDX-LAP-TXT-END-H PIC 99.
10 FILLER PIC X VALUE ':'.
10 TDX-LAP-TXT-END-I PIC 99.
10 FILLER PIC X VALUE ':'.
10 TDX-LAP-TXT-END-S PIC 99.
10 FILLER PIC X(6) VALUE ') HMS('.
10 TDX-LAP-TXT-HMS PIC X(16).
10 FILLER PIC X(7) VALUE ') MSG: '.
10 TDX-LAP-TXT-MSG PIC X(50).
*****************************************************************
Test all 21 nested timings:
Here's a simple COBOL procedure that loops 21 times to populate BEG times for nested levels 0 thru 20. Then it loops an additional 21 times to populate the END times for levels 20 down to 0. A log entry will be printed for each END operation. The LAPSE-LOOP just creates a delay - otherwise the timings will vary in microseconds only.
PERFORM UNTIL TDX-LAP-LEV > 20
MOVE 'BEG' TO TDX-LAP-OPER
PERFORM TDF-LAP
ADD 1 TO TDX-LAP-LEV
PERFORM LAPSE-LOOP
END-PERFORM
PERFORM LAPSE-LOOP
MOVE 21 TO TDX-LAP-LEV
PERFORM UNTIL TDX-LAP-LEV <= 0
PERFORM LAPSE-LOOP
SUBTRACT 1 FROM TDX-LAP-LEV
MOVE 'END' TO TDX-LAP-OPER
MOVE 'LEVEL TEST' TO TDX-LAP-MSG
PERFORM TDF-LAP
END-PERFORM
Full Test Output:
Running the test program TDDATE through OpenCobolIDE - the results are printed to the log.
Success! BEG/END times are registered for each level along with lapsed time. The first test loop set the BEG times while the second test loop marked the END and produced the output. Each level listed is a subset of the next with 20 being the lowest nest and 0 the primary. The entry levels can be used in any fashion - nested or not.
That's the end of the COBOL Timing Demonstration - Up next PC/370 Assembler.
Tuesday, January 28, 2025
A Matter of Time - Part 1 - Intro
It was only a matter of time before I posted an article about my TDF_LAP() function - which is literally a "matter of time". I created this useful function to measure the timings of routines and SQL statements in SQR batch processes. It's actually a set of timing entries that can be nested - levels 0 thru 20. In general, a simple BEG and END call will calculate the elapsed time in between and print the results on a log.
Above: LEGO Guy returns - he's going to replicate the TDF_LAP function in Asssembler. Stay tuned...
Flashback: LEGO Guy loves Assembler
Sample Log Output - AP Vendor/Sourcing Extract
Process_Main uses timing set number 1 and contains all the other routines which use the stand-alone level of zero. The nested levels end and appear first followed by the high level Process_Main routine which displays the total BEG/END information.
Below are the usage notes & parameter descriptions for the TDF_LAP() function. The LOG Y/N option is useful if a routine or SQL statement is executed multiple times - say 1000 or more. The calculated lapsed time (#O_sec) can then be aggregated & printed after all instances have executed instead of printing 1000+ log entries.
!**********************************************************************
!* LAPSE Between Begin/End Times *
!**********************************************************************
!* *
!* INPUT: $I_oper - Operation Type (BEG/END) *
!* #I_idx - Lapse Index Set (0 thru 20) *
!* $I_log - Display Log Output? (Y/N) *
!* $I_msg - Message Text *
!* OUTPUT: #O_sec - Lapsed in seconds *
!* $O_hms - Lapsed in HH:MI:SS Format *
!* *
!**********************************************************************
!* *
!* EXAMPLE: do TDF_LAP('BEG', 0, 'N','', #O_sec, $O_hms) *
!* ... *
!* do TDF_LAP('END', 0, 'Y', 'Lapsed', #O_sec, $O_hms) *
!* *
!* RESULTS: Displays Duration between BEG/END times. *
!* Returns Duration in both seconds and HH:MI:SS. *
!* Resets BEG time to END time for next lapse message. *
!* Index Set 0 used for timings (0 thru 20 available) - *
!* use different sets for nested timings (otherwise 0). *
!* *
!**********************************************************************
Sample - Nested Timings
The routine below is part of a much larger process - a custom Voucher Edit/Build module. The routine NSS_Edit_AP_Invoice_Dup will use timing levels 7, 8 and 9 to track each nested component of the routine.
!**********************************************************************
!* NSS Edit AP Invoice Duplicate *
!**********************************************************************
begin-procedure NSS_Edit_AP_Invoice_Dup
do TDF_LAP('BEG', 7, 'N', '', #O_sec, $O_hms)
let $DUP_VNDR = ' and a.vendor_setid = ~<SETID>~ ' ||
' and a.vendor_id = u.vendor_id '
let $DUP_VNDR = replace($DUP_VNDR, '<SETID>', $NSS_setid_vendor)
let $DUP_VNDR = replace($DUP_VNDR, '~', chr(39))
create-array name=DUPmtx size=5 field=DUPsql:char
let DUPmtx.DUPsql (0) = ' and a.business_unit = u.business_unit '
let DUPmtx.DUPsql (1) = $DUP_vndr
let DUPmtx.DUPsql (2) = ' and a.invoice_id = u.invoice_id '
let DUPmtx.DUPsql (3) = ' and a.invoice_dt = u.invoice_dt '
let DUPmtx.DUPsql (4) = ' and a.gross_amt = u.invoice_amount '
! Initialize Defaults at Origin/BU Levels
let #DUPdef = #OPT_dup_inv_comb_code
! Update Staging Detail with Specified/Default Combination Options
let $SQL_proc = 'NSS_Edit_AP_Invoice_Dup (Options)'
let $SQL_action = 'Update'
let $SQL_table = 'XX_API_STG_DTL'
do TDF_LAP('BEG', 8, 'N', '', #O_sec, $O_hms)
begin-sql On-Error=Error-Routine
update ps_xx_api_stg_dtl u
set (u.dup_invoice_ind,
u.dup_inv_comb_code) =
(select a.dup_invoice_ind,
decode(a.dup_invoice_ind,
'D', #DUPdef,
decode(a.dup_invoice_action,
'K',a.dup_inv_comb_code,0))
from ps_vendor_invoice a
where a.setid = $NSS_setid_vendor
and a.vendor_id = u.vendor_id
and a.effdt =
(select max(a2.effdt)
from ps_vendor_invoice a2
where a2.setid = a.setid
and a2.vendor_id = a.vendor_id
and a2.effdt <= sysdate))
where u.business_unit = $NSS_business_unit
and u.xx_batch_type = $NSS_xx_batch_type
and u.xx_batch_id = #NSS_xx_batch_id
and u.xx_line_status = 'P'
and exists
(select 'X'
from ps_vendor_invoice a
where a.setid = $NSS_setid_vendor
and a.vendor_id = u.vendor_id
and a.effdt <= sysdate)
end-sql
do TDF_LAP('END', 8, 'Y', $SQL_proc, #O_sec, $O_hms)
! Cycle Thru Distinct Combinations (Dynamic SQL)
do TDF_LAP('BEG', 8, 'N', '', #O_sec, $O_hms)
begin-select
dup.dup_inv_comb_code
let $DUPwhere = ''
let #DUPcode = &dup.dup_inv_comb_code
let #DUPmap = &dup.dup_inv_comb_code
while #DUPmap > 0
do TDF_ERRBIT(#DUPmap, #DUPidx, #DUPval)
let $DUPsql = DUPmtx.DUPsql (#DUPidx)
let $DUPwhere = $DUPwhere || $DUPsql
end-while
do NSS_Edit_AP_Invoice_Dup_SQL
from ps_xx_api_stg_dtl dup
where dup.business_unit = $NSS_business_unit
and dup.xx_batch_type = $NSS_xx_batch_type
and dup.xx_batch_id = #NSS_xx_batch_id
and dup.xx_line_status = 'P'
and dup.dup_inv_comb_code > 0
group by dup.dup_inv_comb_code
end-select
let $SQL_proc = 'NSS_Edit_AP_Invoice_Dup [SQL Cycles]'
do TDF_LAP('END', 8, 'Y', $SQL_proc, #O_sec, $O_hms)
let $SQL_proc = 'NSS_Edit_AP_Invoice_Dup (Total)'
do TDF_LAP('END', 7, 'Y', $SQL_proc, #O_sec, $O_hms)
end-procedure
!**********************************************************************
!* NSS Edit AP Invoice Duplicates (per SQL Combination) *
!**********************************************************************
begin-procedure NSS_Edit_AP_Invoice_Dup_SQL
let $SQL_proc = 'NSS_Edit_AP_Invoice_Dup_SQL'
let $SQL_action = 'Update'
let $SQL_table = 'XX_API_STG_DTL'
do TDF_LAP('BEG', 9, 'N', '', #O_sec, $O_hms)
begin-sql On-Error=Error-Routine
update ps_xx_api_stg_dtl u
set u.xx_err_ap = u.xx_err_ap + {ERR_AP_duplicate},
u.xx_errors = u.xx_errors + 1
where u.business_unit = $NSS_business_unit
and u.xx_batch_type = $NSS_xx_batch_type
and u.xx_batch_id = #NSS_xx_batch_id
and u.xx_line_status = 'P'
and u.dup_inv_comb_code = #DUPcode
and exists
(select 'X'
from ps_voucher a
where 1 = 1
[$DUPwhere]
and a.business_unit in
(select z.setcntrlvalue
from ps_set_cntrl_rec z
where z.setid = $NSS_setid_vendor
and z.recname = 'VENDOR')
and a.invoice_id = u.invoice_id
and a.entry_status <> 'X'
and a.close_status <> 'C');
commit;
end-sql
let $SQL_proc = $SQL_proc || ' [' || to_char(#DUPcode) || ']'
do TDF_LAP('END', 9, 'Y', $SQL_proc, #O_sec, $O_hms)
end-procedure
!**********************************************************************
Portion of LOG Output:
The TDF_LAP() calls are highlighted in the code above. The 'nested' timings use levels 7, 8 and 9 in this example. The entire routine took .49 micro-seconds as shown by the last line (level 7 timing). The two nested level 8 entries show .32 and .17 microseconds - level 9 is a subset of the second level 8 entry at .06 microseconds.
The TDF_LAP() function is a staple in all my SQR processes.
Coming up - COBOL and Assembler/370 versions.
Sunday, December 15, 2024
COBOL - Hyper-Extreme Numeric to Literal Translation Overkill
Back in March 2019 I posted a WinBatch routine that translates extremely large numeric values into it's literal English form. WinBatch - Hyper-Extreme Numeric to Literal Translation Overkill
The extremely nonsensical routine produces literal translations into the VIGINTILLIONS (I also have a more practical SQR version that is limited to Billions). Since I recently downloaded & installed OpenCobolIDE I've been having fun writing code - so why not replicate this practically useless but interesting function.
Above: The WinBatch GUI with ridiculous Numeric-To-Literal translation.The COBOL version will be kept much simpler - no need for a repeat of a GUI Check Interface. Just the function and a test module to produce the results.
TDFNLP.CPY - TDF-NL Function (Procedure)
* *
* MODULE: TDFNLP.CPY *
* AUTHOR: TONY DELIA. *
* DATE: 11/23/2024. *
* SYSTEM: WINDOWS 11/OpenCobolIDE 4.7.6 *
* DESC: COBOL USER FUNCTION - NUMERIC TO LITERAL. *
* *
*****************************************************************
* NOTE: REQUIRES TDFNLW.CPY COPYBOOK (W-S SECTION). *
* ALSO TDPROC.CPY/TDWORK.CPY FOR ADDL FUNCTIONS. *
*****************************************************************
* *
* ROUTINE DESCRIPTION *
* -------------- -------------------------------------------- *
* TDF-NL CONVERT NUMERIC VALUE TO LITERAL TEXT *
* *
*****************************************************************
*****************************************************************
* REVISIONS: *
*****************************************************************
* *
* DATE PROGRAMMER DESCRIPTION *
* ---------- --------------- ---------------------------------- *
* *
* 11/23/2024 DELIA,TONY ORIGINAL CODING. *
* *
*****************************************************************
*****************************************************************
* TDF NL - CONVERT NUMERIC VALUE TO LITERAL TEXT *
*****************************************************************
* *
* FIELD I/O DESCRIPTION *
* ----------------- --- ---------------------------------- *
* TDX-NL-AMT I INPUT NUMERIC STRING *
* TDX-NL-TEXT O RETURNS LITERAL TEXT CONVERSION *
* *
*****************************************************************
* *
* EXAMPLE: *
* ---------------------------------------------------------- *
* MOVE 789000000000093 TO TDX-NL-AMT *
* PERFORM TDF-NL *
* DISPLAY 'TEXT: ' TDX-NL-TEXT *
* ---------------------------------------------------------- *
* RESULTS: *
* ---------------------------------------------------------- *
* IN: 789000000000093 *
* OUT: Seven Hundred Eighty-Nine Trillion Ninety-Three *
* ---------------------------------------------------------- *
* IN: 72000009004000000000000 *
* OUT: Seventy-Two Sextillion Nine Quadrillion Four Trillion *
* ---------------------------------------------------------- *
* IN: 123056 *
* OUT: One Hundred Twenty-Three Thousand Fifty-Six *
* *
*****************************************************************
TDF-NL.
MOVE ZEROES TO TDX-NL-WRK
MOVE SPACES TO TDX-NL-TEXT
MOVE TDX-NL-AMT TO TDX-LEN-FLD
MOVE LENGTH OF TDX-NL-AMT TO TDX-LEN-VAL
PERFORM TDF-LENGTH
MOVE TDX-LEN-VAL TO TDX-NL-LEN
IF TDX-NL-LEN > LENGTH OF TDX-NL-WRK
MOVE '***MAXIMUM LENGTH EXCEEDED***' TO TDX-NL-TEXT
ELSE
COMPUTE TDX-NL-X = 67 - TDX-NL-LEN
MOVE TDX-NL-AMT(1:TDX-NL-LEN)
TO TDX-NL-WRK(TDX-NL-X:TDX-NL-LEN)
IF TDX-NL-WRK IS NOT NUMERIC
MOVE '***INPUT NOT NUMERIC VALUE***' TO TDX-NL-TEXT
ELSE
MOVE 1 TO TDX-NL-POS
MOVE 1 TO TDX-NL-LEV
MOVE 1 TO TDX-NL-PTR
PERFORM UNTIL TDX-NL-LEV > TDX-NL-MAX-B
MOVE TDX-NL-WRK(TDX-NL-POS:3) TO TDX-NL-SEG
IF TDX-NL-SEG <> '000'
IF TDX-NL-SEG-HI > 0
MOVE TDX-NL-LIT-A(TDX-NL-SEG-HI) TO TDX-NL-TMP
MOVE TDX-NL-TMP TO TDX-LEN-FLD
MOVE LENGTH OF TDX-NL-LIT-A TO TDX-LEN-VAL
PERFORM TDF-LENGTH
ADD 1 TO TDX-LEN-VAL
MOVE ' Hundred' TO TDX-NL-TMP(TDX-LEN-VAL:8)
ADD 8 TO TDX-LEN-VAL
MOVE TDX-NL-TMP(1:TDX-LEN-VAL)
TO TDX-NL-TEXT(TDX-NL-PTR:TDX-LEN-VAL)
ADD TDX-LEN-VAL TO TDX-NL-PTR
IF TDX-NL-SEG-LO > 0
MOVE SPACE TO TDX-NL-TEXT(TDX-NL-PTR:1)
END-IF
END-IF
IF TDX-NL-SEG-LO > 20
DIVIDE TDX-NL-SEG-LO BY 10
GIVING TDX-NL-X REMAINDER TDX-NL-MOD
ADD 18 TO TDX-NL-X
MOVE TDX-NL-MOD TO TDX-NL-SEG-LO
MOVE TDX-NL-LIT-A(TDX-NL-X) TO TDX-NL-TMP
MOVE TDX-NL-TMP TO TDX-LEN-FLD
MOVE LENGTH OF TDX-NL-LIT-A TO TDX-LEN-VAL
PERFORM TDF-LENGTH
MOVE TDX-NL-TMP(1:TDX-LEN-VAL)
TO TDX-NL-TEXT(TDX-NL-PTR:TDX-LEN-VAL)
ADD TDX-LEN-VAL TO TDX-NL-PTR
IF TDX-NL-SEG-LO > 0
MOVE '-' TO TDX-NL-TEXT(TDX-NL-PTR:1)
ADD 1 TO TDX-NL-PTR
END-IF
END-IF
IF TDX-NL-SEG-LO > 0
MOVE TDX-NL-LIT-A(TDX-NL-SEG-LO) TO TDX-NL-TMP
MOVE TDX-NL-TMP TO TDX-LEN-FLD
MOVE LENGTH OF TDX-NL-LIT-A TO TDX-LEN-VAL
PERFORM TDF-LENGTH
MOVE TDX-NL-TMP(1:TDX-LEN-VAL)
TO TDX-NL-TEXT(TDX-NL-PTR:TDX-LEN-VAL)
ADD TDX-LEN-VAL TO TDX-NL-PTR
MOVE SPACE TO TDX-NL-TEXT(TDX-NL-PTR:1)
END-IF
MOVE TDX-NL-LIT-B(TDX-NL-LEV) TO TDX-NL-TMP
MOVE TDX-NL-TMP TO TDX-LEN-FLD
MOVE LENGTH OF TDX-NL-LIT-B TO TDX-LEN-VAL
PERFORM TDF-LENGTH
IF TDX-LEN-VAL > 0
MOVE TDX-NL-TMP(1:TDX-LEN-VAL)
TO TDX-NL-TEXT(TDX-NL-PTR:TDX-LEN-VAL)
ADD TDX-LEN-VAL TO TDX-NL-PTR
MOVE SPACE TO TDX-NL-TEXT(TDX-NL-PTR:1)
ADD 1 TO TDX-NL-PTR
END-IF
END-IF
ADD 1 TO TDX-NL-LEV
ADD 3 TO TDX-NL-POS
END-PERFORM
IF TDX-NL-TEXT EQUAL SPACES
MOVE 'Zero' TO TDX-NL-TEXT
END-IF
END-IF
END-IF
.
TDF-NL-EXIT.
EXIT.
*****************************************************************
* END OF COPYBOOK *
*****************************************************************
TDFNLW.CPY - TDF-NL Working Storage/Variables
* *
* MODULE: TDFNLW.CPY *
* AUTHOR: TONY DELIA. *
* DATE: 11/23/2024. *
* SYSTEM: WINDOWS 11/OpenCobolIDE 4.7.6 *
* DESC: COBOL USER FUNCTION - NUMERIC TO LITERAL. *
* *
*****************************************************************
* NOTE: REQUIRES TDFNLP.CPY COPYBOOK (PROCEDURE SECTION). *
* ALSO TDPROC.CPY/TDWORK.CPY FOR ADDL FUNCTIONS. *
*****************************************************************
* *
* ROUTINE DESCRIPTION *
* -------------- -------------------------------------------- *
* TDF-NL CONVERT NUMERIC VALUE TO LITERAL TEXT *
* *
*****************************************************************
*****************************************************************
* REVISIONS: *
*****************************************************************
* *
* DATE PROGRAMMER DESCRIPTION *
* ---------- --------------- ---------------------------------- *
* *
* 11/23/2024 DELIA,TONY ORIGINAL CODING. *
* *
*****************************************************************
01 TDF-NL-PARMS.
05 TDX-NL-AMT PIC X(70).
05 TDX-NL-WRK PIC X(66) VALUE ZEROES.
05 TDX-NL-LEN PIC 99.
05 TDX-NL-X PIC 99.
05 TDX-NL-IDX PIC 999.
05 TDX-NL-MAX-A PIC 999 VALUE 27.
05 TDX-NL-MAX-B PIC 999 VALUE 22.
05 TDX-NL-POS PIC 9(4).
05 TDX-NL-LEV PIC 99.
05 TDX-NL-PTR PIC 9(4).
05 TDX-NL-MOD PIC 99.
05 TDX-NL-SEG.
10 TDX-NL-SEG-HI PIC 9.
10 TDX-NL-SEG-LO PIC 99.
05 TDX-NL-TMP PIC X(20).
05 TDX-NL-MTX-A.
* Direct Indexing Literals
10 FILLER PIC X(10) VALUE 'One'.
10 FILLER PIC X(10) VALUE 'Two'.
10 FILLER PIC X(10) VALUE 'Three'.
10 FILLER PIC X(10) VALUE 'Four'.
10 FILLER PIC X(10) VALUE 'Five'.
10 FILLER PIC X(10) VALUE 'Six'.
10 FILLER PIC X(10) VALUE 'Seven'.
10 FILLER PIC X(10) VALUE 'Eight'.
10 FILLER PIC X(10) VALUE 'Nine'.
10 FILLER PIC X(10) VALUE 'Ten'.
10 FILLER PIC X(10) VALUE 'Eleven'.
10 FILLER PIC X(10) VALUE 'Twelve'.
10 FILLER PIC X(10) VALUE 'Thirteen'.
10 FILLER PIC X(10) VALUE 'Fourteen'.
10 FILLER PIC X(10) VALUE 'Fifteen'.
10 FILLER PIC X(10) VALUE 'Sixteen'.
10 FILLER PIC X(10) VALUE 'Seventeen'.
10 FILLER PIC X(10) VALUE 'Eighteen'.
10 FILLER PIC X(10) VALUE 'Nineteen'.
10 FILLER PIC X(10) VALUE 'Twenty'.
* Combination Indexing Literals
10 FILLER PIC X(10) VALUE 'Thirty'.
10 FILLER PIC X(10) VALUE 'Forty'.
10 FILLER PIC X(10) VALUE 'Fifty'.
10 FILLER PIC X(10) VALUE 'Sixty'.
10 FILLER PIC X(10) VALUE 'Seventy'.
10 FILLER PIC X(10) VALUE 'Eighty'.
10 FILLER PIC X(10) VALUE 'Ninety'.
05 TDX-NL-MTX-X REDEFINES TDX-NL-MTX-A.
10 TDX-NL-LIT-A PIC X(10) OCCURS 27 TIMES.
05 TDX-NL-MTX-B.
10 FILLER PIC X(20) VALUE ' Vigintillion'.
10 FILLER PIC X(20) VALUE ' Novemdecillion'.
10 FILLER PIC X(20) VALUE ' Octodecillion'.
10 FILLER PIC X(20) VALUE ' Septendecillion'.
10 FILLER PIC X(20) VALUE ' Sexdecillion'.
10 FILLER PIC X(20) VALUE ' Quindecillion'.
10 FILLER PIC X(20) VALUE ' Quatuordecillion'.
10 FILLER PIC X(20) VALUE ' Tredecillion'.
10 FILLER PIC X(20) VALUE ' Duodecillion'.
10 FILLER PIC X(20) VALUE ' Undecillion'.
10 FILLER PIC X(20) VALUE ' Decillion'.
10 FILLER PIC X(20) VALUE ' Nonillion'.
10 FILLER PIC X(20) VALUE ' Octillion'.
10 FILLER PIC X(20) VALUE ' Septillion'.
10 FILLER PIC X(20) VALUE ' Sextillion'.
10 FILLER PIC X(20) VALUE ' Quintillion'.
10 FILLER PIC X(20) VALUE ' Quadrillion'.
10 FILLER PIC X(20) VALUE ' Trillion'.
10 FILLER PIC X(20) VALUE ' Billion'.
10 FILLER PIC X(20) VALUE ' Million'.
10 FILLER PIC X(20) VALUE ' Thousand'.
10 FILLER PIC X(20) VALUE ' '.
05 TDX-NL-MTX-Y REDEFINES TDX-NL-MTX-B.
10 TDX-NL-LIT-B PIC X(20) OCCURS 22 TIMES.
05 TDX-NL-TEXT PIC X(1000).
*****************************************************************
* END OF COPYBOOK *
*****************************************************************
TDTESTNL.CBL - Test Module
Snippet of Working Storage - WS-TEXT will hold the initial numeric value. WS-SCALE will be displayed to illustrate the length of the numeric field and WS-BIG-NUM is a big number - the maximum accepted by the TDF-NL function.
* WORKING STORAGE *
*****************************************************************
WORKING-STORAGE SECTION.
01 WS-TEXT PIC X(66).
01 WS-SCALE.
05 FILLER PIC X(10) VALUE '1...5...10'.
05 FILLER PIC X(10) VALUE '...15...20'.
05 FILLER PIC X(10) VALUE '...25...30'.
05 FILLER PIC X(10) VALUE '...35...40'.
05 FILLER PIC X(10) VALUE '...45...50'.
05 FILLER PIC X(10) VALUE '...55...60'.
05 FILLER PIC X(10) VALUE '...65...70'.
01 WS-BIG-NUM.
05 FILLER PIC X(15) VALUE '999999999999999'.
05 FILLER PIC X(15) VALUE '999999999999999'.
05 FILLER PIC X(15) VALUE '999999999999999'.
05 FILLER PIC X(15) VALUE '999999999999999'.
05 FILLER PIC X(6) VALUE '999999'.
COPY TDFNLW.
COPY TDFWORK.
*****************************************************************
Testing logic - some static samples are built into the TEST-NL procedure followed by an optional user entered loop. Enter any number or <ENTER> to break out.
* PROCEDURE DIVISION *
*****************************************************************
PROCEDURE DIVISION.
MAIN-PROCEDURE.
DISPLAY '*** STATIC TEST CASES ***'
PERFORM TEST-NL
DISPLAY '*** ENTER NUMBER OR <ENTER> ***'
ACCEPT WS-TEXT
PERFORM UNTIL WS-TEXT EQUALS SPACES
MOVE WS-TEXT TO TDX-NL-AMT
PERFORM TDF-NL
DISPLAY ' '
DISPLAY '---: ' WS-SCALE
DISPLAY ' IN: ' TDX-NL-AMT
DISPLAY 'OUT: ' TDX-NL-TEXT
DISPLAY '*** ENTER NUMBER OR <ENTER> ***'
ACCEPT WS-TEXT
END-PERFORM
STOP RUN.
*****************************************************************
OUTPUT:
Maximum Output:
---: 1...5...10...15...20...25...30...35...40...45...50...55...60...65...70
IN: 999999999999999999999999999999999999999999999999999999999999999999
OUT: Nine Hundred Ninety-Nine Vigintillion Nine Hundred Ninety-Nine Novemdecillion Nine Hundred Ninety-Nine Octodecillion Nine Hundred Ninety-Nine Septendecillion Nine Hundred Ninety-Nine Sexdecillion Nine Hundred Ninety-Nine Quindecillion Nine Hundred Ninety-Nine Quatuordecillion Nine Hundred Ninety-Nine Tredecillion Nine Hundred Ninety-Nine Duodecillion Nine Hundred Ninety-Nine Undecillion Nine Hundred Ninety-Nine Decillion Nine Hundred Ninety-Nine Nonillion Nine Hundred Ninety-Nine Octillion Nine Hundred Ninety-Nine Septillion Nine Hundred Ninety-Nine Sextillion Nine Hundred Ninety-Nine Quintillion Nine Hundred Ninety-Nine Quadrillion Nine Hundred Ninety-Nine Trillion Nine Hundred Ninety-Nine Billion Nine Hundred Ninety-Nine Million Nine Hundred Ninety-Nine Thousand Nine Hundred Ninety-Nine
There's not much else to say about this - except to repeat it's ridiculous but also interesting & fun.









