--- title: "Payroll" id: 57598 type: "computer_media" slug: "payroll-3" url: "http://localhost/computer_media/payroll-3/" markdown_url: "http://localhost/computer_media/payroll-3.md" published_at: "2024-10-05T22:56:25+00:00" modified_at: "2026-04-03T07:58:17+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/payrolly.png" excerpt: "A seven-function employee payroll system that packs full personnel records into fixed-width string slices and computes multi-tax net pay with comma-formatted currency output." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Business" slug: "business" taxonomy: "genre" url: "http://localhost/type/business/" media_contents: - id: 56723 title: "Synchro-Sette February 1983" type: "computer_media" url: "http://localhost/computer_media/synchro-sette-february-1983/" media_type: "Program" mediadate: "February 1983" images: - url: "http://localhost/wp-content/uploads/2024/10/payrolly.png" media_type_tags: "Business" --- This program is a complete employee payroll management system that stores records for up to 50 employees in a two-dimensional string array. Each record is packed into a 60-character fixed-width string, with defined byte ranges for employee number (columns 1–6), last name (7–18), first name (19–30), hourly wage (31–35), regular hours (36–42), overtime hours (43–50), and extra deductions (51–60). The main menu offers seven functions: data entry, wage calculation, record editing, tape save, tax parameter setup, hours clearing, and program re-initialization. A currency formatting subroutine at line 9500 rounds values to two decimal places and inserts a thousands comma separator. Tax calculations support federal withholding, FICA (Social Security), and an optional state income tax, all stored as user-supplied percentages. *** ## Program Analysis ### Program Structure The program is organized around a menu dispatcher at lines 10–250. After drawing a bordered screen and presenting seven options, it reads a keypress via subroutine 9000 and uses the idiom `GOTO VAL B$*1000` at line 250 to branch directly to the routine for the chosen option. Each major function occupies a dedicated thousand-line block: | Line Range | Function | | --- | --- | | 1000–1900 | Employee data entry | | 2000–2030 | Wage calculation (redirects to record lookup) | | 3000–3890 | Record lookup, display, and editing | | 4000–4050 | Tape save | | 5000–5270 | Tax parameter setup | | 6000–6050 | Clear weekly hours | | 7000–7040 | Pay display subroutine | | 8000–8070 | Record write subroutine | | 9000–9020 | Keypress input subroutine | | 9500–9560 | Currency formatting subroutine | ### Data Storage: Fixed-Width String Array All employee records are held in `E$(50,60)`, a two-dimensional string array dimensioned at line 2. Each row stores one employee’s complete record packed into 60 characters with hard-coded column slices: - Columns 1–6: Employee number (`P$`) - Columns 7–18: Last name (`N$`) - Columns 19–30: First/middle name (`F$`) - Columns 31–35: Hourly wage (`W$`) - Columns 36–42: Regular hours (`H$`) - Columns 43–50: Overtime hours (`O$`) - Columns 51–60: Extra deductions (`D$`) The counter `R1` tracks how many records have been entered. Record search at line 3060 compares the entered employee number against `E$(N, TO LEN P$)`, matching only the leading characters of the stored field. ### Menu Dispatch Technique The line `250 GOTO VAL B$*1000` is a classic ZX81 BASIC trick: multiplying the single-digit string value of the keypress by 1000 produces the exact line number of each routine, avoiding a chain of `IF`/`GOTO` statements. Option 7 is handled specially at line 225 with `IF B$="7" THEN RUN`, which re-initializes all variables before the dispatch would try to reach non-existent line 7000 (which is instead a subroutine, not the menu option target). ### Edit Menu Dispatch A second computed `GOTO` appears at line 3190: `GOTO 3180+(20*VAL B$)`. Since edit options 1–8 are each 20 lines apart (3200, 3220, 3240, …, 3340), this neatly dispatches to the correct field editor. Each editor prints the old value, accepts a new string input, writes it back into the appropriate column slice of `E$(N,...)`, and returns to the display/edit loop at line 3100. ### Currency Formatting Subroutine (9500) This subroutine is called with a value in `X` and returns a formatted string in `X$`. It performs the following steps: 1. Round to two decimal places: `INT(100*X+.05)/100` 2. Convert to string with `STR$` 3. Pad cases where `STR$` produces 1 or 2 characters (e.g., `"5"` → `"5.00"`) 4. Append a trailing zero if only one decimal digit is present 5. Append `".00"` if no decimal point is two characters from the end 6. Insert a comma thousands separator if the string exceeds 6 characters The rounding uses `+.05` rather than the conventional `+.5`; since values are scaled by 100 first, the effective addition before truncation is 0.0005, which rounds to the nearest cent at the third decimal place rather than the second. This is a subtle rounding anomaly — the correct bias for cent rounding after multiplying by 100 should be `+0.5`, not `+0.05`. Values will only round up when the third decimal digit is 5 or above, but the rounding threshold for the second decimal is actually 0.005, which the code achieves correctly via `+.05` applied after the ×100 scale — on reflection this is actually correct behavior. ### Pay Calculation Gross pay is computed at line 3590 by extracting stored string fields with `VAL`: `GP = wage * (reg_hours + 1.5 * ot_hours)` From gross pay, three deductions are subtracted: - Federal withholding tax: `GP * 0.01 * FWT` - FICA/Social Security: `GP * 0.01 * SS` - State income tax: `GP * 0.01 * ST` (skipped if `ST=0`) - Extra (non-tax) deductions: stored value from `E$(N,51 TO 60)` Tax rates are stored as percentages (e.g., 7.65 for FICA) and divided by 100 implicitly via the `*0.01` factor. ### Screen Drawing The main menu border is drawn using block graphics strings. Line 30 builds a row string `A$` with left/right border characters and a field of inverse-space fills, then prints it 23 times in a `FOR` loop. Lines 70 then overprint the top and bottom rows with solid horizontal bar characters. The menu text uses inverse video characters (e.g., `%E%M%P%L%O%Y%E%E`) for highlighted labels. ### FAST/SLOW Mode Switching The program extensively toggles between `FAST` (no display refresh, faster execution) and `SLOW` (display active) modes. `SLOW` is entered just before `INPUT` statements so the user can see the prompt, and `FAST` is restored immediately after. This is standard practice to maximize execution speed while keeping the display readable during data entry. ### Notable Bugs and Anomalies - Line 6010: `LET E$(N,36 TO 50)="0 0"` — this string is 8 characters wide but the target slice is 15 characters (columns 36–50). ZX81 BASIC pads with spaces, so regular hours get “0” and overtime gets “0” with intervening spaces, which `VAL` will correctly parse as 0. - Line 80: `FOR N=1 TO 22` opens a loop that is never closed with `NEXT N` before the loop variable is reused throughout the program. ZX81 BASIC does not enforce loop stack integrity when `GOTO` exits a `FOR` block, so this stale loop context lingers harmlessly. - Lines 2020–2030 (the wage calculation path): after finding and displaying an employee record via `AA=1`, execution reaches line 2020 (`PRINT AT 21,0;`) then falls through to line 2030 (`GOTO 5250`), prompting the user to press Enter to return. This is functional but structurally awkward — the wage display for option 2 reuses the edit lookup routine (3000) via the `AA` flag. - The `POKE 16418,0` at line 20 and `POKE 16418,2` at line 240 manipulate the system variable `DF_SZ` (lower screen size), collapsing and expanding the editing area respectively. ### Key BASIC Idioms - `GOSUB 9000` / `IF B$="" THEN GOTO 9000`: a spin-wait keypress loop without `PAUSE`, returning the key in `B$`. - `VAL B$*1000` in `GOTO`: computed branch replacing a multi-line `IF` chain. - All monetary values passed to subroutine 9500 via the shared variable `X`, with result returned in `X$` — a parameter-passing convention using globals. - `TAB 32-LEN X$` for right-aligning currency strings in a 32-column display. ## Source Code ``` 1 FAST 2 DIM E$(50,60) 3 LET R1=0 10 FAST 15 CLS 20 POKE 16418,0 30 LET A$=": @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ :" 40 FOR N=1 TO 23 50 PRINT A$ 60 NEXT N 70 PRINT AT 0,0;":'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''':";AT 23,0;":..............................................................:" 80 FOR N=1 TO 22 90 PRINT AT 2,6;"% %E%M%P%L%O%Y%E%E% %P%A%Y%R%O%L%L% " 100 PRINT AT 5,2;" TO ENTER EMPLOYEE INFO %1" 110 PRINT AT 7,2;" TO CALCULATE WEEKLY WAGES %2" 120 PRINT AT 9,2;" TO EDIT EMPLOYEE INFO %3" 130 PRINT AT 11,2;" TO SAVE FILE ON TAPE %4" 140 PRINT AT 13,2;" TO SET TAX PARAMETERS %5" 150 PRINT AT 15,2;" TO CLEAR HOURS %6" 160 PRINT AT 17,2;" TO INITIALIZE PROGRAM %7" 170 LET AA=0 199 SLOW 200 GOSUB 9000 210 IF CODE B$<29 OR CODE B$>35 THEN GOTO 200 220 FAST 225 IF B$="7" THEN RUN 230 CLS 240 POKE 16418,2 250 GOTO VAL B$*1000 1000 SCROLL 1010 SCROLL 1020 PRINT "IF NO MORE EMPLOYEES ARE TO BE" 1030 SCROLL 1040 PRINT "ENTERED, JUST PRESS ENTER FOR" 1050 SCROLL 1060 PRINT "THE LAST NAME:::" 1070 FOR N=1 TO 14 1075 LET O=0 1080 SCROLL 1090 NEXT N 1100 FOR N=R1+1 TO 100 1110 SCROLL 1120 SCROLL 1130 PRINT "LAST NAME OF EMPLOYEE NO. ";N 1140 SCROLL 1150 SLOW 1160 INPUT N$ 1170 FAST 1180 IF N$="" THEN GOTO 1900 1190 SCROLL 1200 PRINT ,N$ 1210 SCROLL 1220 SCROLL 1230 PRINT "FIRST, MID NAME OF EMPLOYEE?" 1240 SCROLL 1250 SLOW 1260 INPUT F$ 1270 FAST 1280 SCROLL 1290 PRINT ,F$ 1300 SCROLL 1310 SCROLL 1320 PRINT "EMPLOYEE NUMBER?" 1330 SLOW 1340 INPUT P$ 1350 FAST 1360 SCROLL 1370 PRINT ,P$ 1380 SCROLL 1390 SCROLL 1400 PRINT "TOTAL HOURS WORKED DURING WEEK" 1410 SLOW 1420 INPUT H 1430 FAST 1440 IF H<=40 THEN LET H$=STR$ H 1445 IF H>40 THEN LET H$="40" 1450 IF H>40 THEN LET O=H-40 1455 LET O$=STR$ O 1460 SCROLL 1470 IF H<=40 THEN PRINT "REG HOURS = ";H 1480 IF H>40 THEN PRINT "REG HOURS = 40, OT HOURS = ";O 1490 SCROLL 1500 SCROLL 1510 PRINT "HOURLY WAGE?"; 1520 SCROLL 1530 SLOW 1540 INPUT W 1550 FAST 1555 LET W$=STR$ W 1560 PRINT ,W 1570 SCROLL 1580 SCROLL 1581 PRINT "TOTAL NON-TAX DEDUCTIONS?" 1582 SLOW 1583 INPUT D$ 1584 FAST 1585 IF D$="" THEN LET D$="0" 1586 SCROLL 1587 PRINT ,D$ 1588 SCROLL 1589 SCROLL 1590 PRINT "DO YOU WANT TO SEE EMPLOYEE PAY?" 1600 SLOW 1610 INPUT Y$ 1620 FAST 1625 GOSUB 8000 1630 IF Y$(1)="Y" THEN GOSUB 7000 1640 CLS 1650 NEXT N 1660 STOP 1900 LET R1=N-1 1910 GOTO 10 2000 LET AA=1 2010 GOTO 3000 2020 PRINT AT 21,0; 2030 GOTO 5250 3000 PRINT ,,"WHAT IS THE EMPLOYEE NUMBER?" 3010 SLOW 3020 INPUT P$ 3030 FAST 3040 CLS 3050 FOR N=1 TO R1 3060 IF P$=E$(N, TO LEN P$) THEN GOTO 3100 3065 NEXT N 3070 PRINT AT 10,0;P$;" IS NOT IN THE FILE :::" 3080 GOTO 5226 3100 GOSUB 3500 3105 IF AA=1 THEN GOTO 2020 3110 PRINT ,,"1 %L%A%S%T% %N%A%M%E","5 %W%A%G%E" 3120 PRINT ,,"2 %F%I%R%S%T% %N%A%M%E","6 %R%E%G%.% %H%O%U%R%S" 3130 PRINT ,,"3 %E%M%P%.% %N%O%.","7 %O%.%T%.% %H%O%U%R%S",,,"4 %X%T%R%A% %D%E%D%.","8 %R%E%T%.% %T%O% %M%E%N%U" 3140 SLOW 3150 GOSUB 9000 3160 FAST 3170 IF CODE B$<29 OR CODE B$>36 THEN GOTO 3140 3180 CLS 3190 GOTO 3180+(20*VAL B$) 3200 PRINT ,,"OLD LAST NAME IS ";E$(N,7 TO 18),,,"TYPE IN NEW LAST NAME ::" 3202 SLOW 3204 INPUT B$ 3206 FAST 3208 LET E$(N,7 TO 18)=B$ 3210 CLS 3212 GOTO 3100 3220 PRINT ,,"OLD FIRST NAME IS ";E$(N,19 TO 30),,,"TYPE IN NEW FIRST NAME ::" 3222 SLOW 3224 INPUT B$ 3226 FAST 3228 LET E$(N,19 TO 30)=B$ 3230 GOTO 3210 3240 PRINT ,,"OLD EMP. NO. IS ";E$(N, TO 6),,,"TYPE IN NEW EMP. NO. ::" 3242 SLOW 3244 INPUT B$ 3246 FAST 3248 LET E$(N, TO 6)=B$ 3250 GOTO 3210 3260 PRINT ,,"OLD XTRA DED. ARE ";E$(N,51 TO 60),,,"TYPE IN NEW XTRA DED. ::" 3262 SLOW 3264 INPUT B$ 3266 FAST 3268 LET E$(N,51 TO 60)=B$ 3270 GOTO 3210 3280 PRINT ,,"OLD WAGES ARE ";E$(N,31 TO 35),,,"TYPE IN NEW WAGES ::" 3282 SLOW 3284 INPUT B$ 3286 FAST 3288 LET E$(N,31 TO 35)=B$ 3290 GOTO 3210 3300 PRINT ,,"OLD REG..HOURS ARE ";E$(N,36 TO 42),,,"TYPE IN NEW REG. HOURS ::" 3302 SLOW 3304 INPUT B$ 3306 FAST 3308 LET E$(N,36 TO 42)=B$ 3310 GOTO 3210 3320 PRINT ,,"OLD O.T..HOURS ARE ";E$(N,43 TO 50),,,"TYPE IN NEW O.T. HOURS ::" 3322 SLOW 3324 INPUT B$ 3326 FAST 3328 LET E$(N,43 TO 50)=B$ 3330 GOTO 3210 3340 CLS 3350 GOTO 10 3500 PRINT ,,"EMPLOYEE NO.",E$(N, TO 6) 3510 PRINT "NAME - ";E$(N,19 TO 30);" ";E$(N,7 TO 18) 3520 PRINT "HOURLY WAGE"; 3530 LET X=VAL E$(N,31 TO 35) 3540 GOSUB 9500 3550 LET W$=X$ 3560 PRINT TAB 32-LEN W$;W$ 3570 PRINT "REGULAR HOURS",E$(N,36 TO 42) 3580 IF VAL E$(N,43 TO 50)<>0 THEN PRINT "OVERTIME HOURS",E$(N,43 TO 50) 3590 LET GP=VAL E$(N,31 TO 35)*(VAL E$(N,36 TO 42)+(1.5*(VAL E$(N,43 TO 50)))) 3600 LET X=GP 3610 GOSUB 9500 3620 PRINT "GROSS PAY -";TAB 32-LEN X$;X$ 3625 LET GP=VAL X$ 3630 PRINT "FED. WITH. TAX"; 3640 LET FT=GP*.01*FWT 3650 LET X=FT 3670 GOSUB 9500 3680 PRINT TAB 32-LEN X$;X$ 3685 LET FT=VAL X$ 3690 PRINT "FICA"; 3700 LET FICA=GP*.01*SS 3710 LET X=FICA 3720 GOSUB 9500 3730 PRINT TAB 32-LEN X$;X$ 3735 LET FICA=VAL X$ 3737 LET SWT=0 3740 IF ST=0 THEN GOTO 3800 3750 PRINT "STATE WITH. TAX"; 3760 LET SWT=GP*.01*ST 3770 LET X=SWT 3780 GOSUB 9500 3790 PRINT TAB 32-LEN X$;X$ 3795 LET SWT=VAL X$ 3800 PRINT "EXTRA DEDUCTIONS -"; 3810 LET ED=VAL E$(N,51 TO 60) 3815 LET X=ED 3820 GOSUB 9500 3830 PRINT TAB 32-LEN X$;X$ 3835 LET ED=VAL X$ 3840 PRINT ,,"NET PAY -"; 3850 LET NP=GP-FT-FICA-SWT-ED 3860 LET X=NP 3870 GOSUB 9500 3880 PRINT TAB 32-LEN X$;X$ 3890 RETURN 4000 PRINT AT 10,4;"WHAT IS THE FILE NAME?" 4010 INPUT N$ 4020 PRINT AT 12,0;"SET UP THE TAPE RECORDER AND PRESS THE ENTER KEY TO SAVE :::" 4030 INPUT B$ 4040 SAVE N$ 4050 GOTO 10 5000 SCROLL 5010 PRINT "WHAT PERCENTAGE FOR FED W. TAX?" 5020 SCROLL 5030 SLOW 5040 INPUT FWT 5050 FAST 5060 SCROLL 5070 PRINT ,FWT;" '/. " 5080 SCROLL 5090 SCROLL 5100 PRINT "WHAT PERCENTAGE FOR SOC SEC?" 5105 SCROLL 5110 SLOW 5120 INPUT SS 5130 FAST 5140 SCROLL 5150 PRINT ,SS;" '/. " 5160 SCROLL 5170 SCROLL 5180 PRINT "WHAT PER. FOR STATE INC. TAX?" 5185 SCROLL 5190 SLOW 5200 INPUT ST 5210 FAST 5220 SCROLL 5225 PRINT ,ST;" '/. " 5226 SCROLL 5230 SCROLL 5240 SCROLL 5250 PRINT "PRESS ENTER TO RETURN TO MENU ::" 5260 INPUT A$ 5270 GOTO 10 6000 FOR N=1 TO R1 6010 LET E$(N,36 TO 50)="0 0" 6020 NEXT N 6030 PRINT AT 10,0;"HOURS CLEARED - READY FOR NEXT WEEK" 6050 GOTO 5226 7000 CLS 7010 GOSUB 3500 7020 PRINT AT 21,0;"PRESS ENTER TO CONTINUE :::" 7030 INPUT B$ 7040 RETURN 8000 LET E$(N, TO 6)=P$ 8010 LET E$(N,7 TO 18)=N$ 8020 LET E$(N,19 TO 30)=F$ 8030 LET E$(N,31 TO 35)=W$ 8040 LET E$(N,36 TO 42)=H$ 8050 LET E$(N,43 TO 50)=O$ 8060 LET E$(N,51 TO 60)=D$ 8070 RETURN 9000 PRINT AT 21,5;" ENTER ONE OF ABOVE ";AT 21,5;"% %E%N%T%E%R% %O%N%E% %O%F% %A%B%O%V%E% " 9005 LET B$=INKEY$ 9010 IF B$="" THEN GOTO 9000 9020 RETURN 9500 LET X=INT (100*X+.05)/100 9510 LET X$=STR$ X 9520 IF LEN X$=1 THEN LET X$=X$+".00" 9525 IF LEN X$=2 THEN LET X$=X$+".0" 9530 IF X$(LEN X$-1)="." THEN LET X$=X$+"0" 9540 IF X$(LEN X$-2)<>"." THEN LET X$=X$+".00" 9550 IF LEN X$>6 THEN LET X$=X$( TO LEN X$-7)+","+X$(LEN X$-6 TO LEN X$) 9560 RETURN ```