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:
- Round to two decimal places:
INT(100*X+.05)/100 - Convert to string with
STR$ - Pad cases where
STR$produces 1 or 2 characters (e.g.,"5"→"5.00") - Append a trailing zero if only one decimal digit is present
- Append
".00"if no decimal point is two characters from the end - 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 ifST=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, whichVALwill correctly parse as 0. - Line 80:
FOR N=1 TO 22opens a loop that is never closed withNEXT Nbefore the loop variable is reused throughout the program. ZX81 BASIC does not enforce loop stack integrity whenGOTOexits aFORblock, 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 theAAflag. - The
POKE 16418,0at line 20 andPOKE 16418,2at line 240 manipulate the system variableDF_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 withoutPAUSE, returning the key inB$.VAL B$*1000inGOTO: computed branch replacing a multi-lineIFchain.- All monetary values passed to subroutine 9500 via the shared variable
X, with result returned inX$— a parameter-passing convention using globals. TAB 32-LEN X$for right-aligning currency strings in a 32-column display.
Content
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
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
