The Let's Play Archive

EXAPUNKS

by Carbon dioxide

Part 46: Cybermyth studios

Part 46 - Cybermyth studios

=== Trash World Inbox ===

Quackles has a nice improvement for last week's task.

Quackles posted:

Optimization time!
code:
GRAB 300
LINK 800
@REP 5
KILL 
@END
		;LOWEST NUMBER IS 210 
		;NUMBERS ARE IN A CONTIGUOUS BLOCK

		;WE NEED TO GO GRAB _A_ TICKET THEN BACKTRACK UP TO 4 SPACES
		;WE START AT 213 TO IMPROVE PERFORMANCE IN THE WORST CASE

COPY 213 X
MARK TICKETLOOP
REPL GETTICKET
ADDI X 5 X
TEST MRD
FJMP TICKETLOOP
COPY M X
MARK TICKETLOOP2
REPL GETTICKET2
ADDI X 1 X
TEST MRD
FJMP TICKETLOOP2
VOID M

COPY F X
COPY X M
SEEK -1
COPY M F
COPY X F
COPY M F
SEEK -3
COPY F M
COPY F M
COPY F M
WIPE

MARK GETTICKET
GRAB X
SUBI X 4 M
			;HAVE THIS CHAIN TO TICKETLOOP 2??

MARK GETTICKET2
GRAB X
COPY 1 M
COPY F #NEXT
DROP
LINK 800
COPY M X
GRAB 200
					;SO WE HAVE A LITTLE PROBLEM. NO WAY TO GET FILE PTR LOC, SO WHAT IF TARGET LOC IS BEFORE 1ST ENTRY?
					;WE GOTTA CHECK FOR THIS EDGE CASE.
TEST F > #DATE
TJMP EDGECASE1
COPY #DATE M
MARK EDGECASE1RET

MARK FINDAPPT
TEST F = X
TJMP GETAPPT
SEEK 2
JUMP FINDAPPT
MARK GETAPPT
COPY F M 	;'TEST'
MARK COPYFWD
SEEK -6
TEST F = #DATE
TJMP WRITEAPPT
SEEK -1

COPY F X
COPY F T
SEEK 1
COPY X F
COPY T F
SEEK -3
COPY F X
SEEK 2
COPY X F
SEEK -3

JUMP COPYFWD
MARK WRITEAPPT
SEEK 2
COPY M X
TEST X = #DATE
TJMP EDGECASE2RET
						;EDGE CASE PART 2 - COPY NEXT DAY'S DATE SO TODAY'S IS AT LIST START
COPY X F
SEEK -3
COPY F X
COPY F T
SEEK 1
COPY X F
COPY T F

SEEK -9999
MARK EDGECASE2RET
COPY #DATE F
COPY M F 				;NTH'S NAME
COPY M F 				;'TEST'
HALT

MARK EDGECASE1
SEEK -1
COPY F M
SEEK -1
COPY #DATE F
JUMP EDGECASE1RET
I had a similar solution to CO₂'s to begin with, which got similar performance. However, on re-evaluating it, I realized there's an easy way to make it better: since the tickets are a contiguous group of 5, between 210 through 298 in every case, we can just check for a hit by adding 5 if no match is found (so 210, 215, 220, etc). From there, we backtrack four spaces and grab numbers going up by 1 to find the lowest ticket number.

We start by trying to grab ticket 213, as this improves performance in the worst case where tickets 294-298 are present (we grab ticket 298, then subtracting 4 leaves us with the lowest number, ticket 294 directly), and go from there.

My file writing mechanism is drastically different from CO₂'s. I can't say which is faster. Instead of deleting NthDimension's entry from the file, the EXA that finds it sends it over M to its parent, which still remains in the main room with the ticket lineup. The parent EXA stores that row to a file - we'll read it back later.

The EXA reading the appointments starts at Kapoor's row and repeatedly copies the previous line over the current line, going back until we find today's date. There's no way to make the overwriting exa force-halt when it hits the beginning of the file (believe me, I did try), and there's no way to test if we are at the beginning of the file, either (aside from forcing the issue with SEEK -9999). So instead, we check for what in the code above is referred to as the 'edge case'; if Kapoor's entry would be the only one for today (as determined by the first line of the file not having today's date), we replace the date on that entry with today's date, before copying the 'bad date' into the file on Kapoor's row instead.

Either way, once the copier makes space for Kapoor's row, it finds the last incidence of today's date, then replaces the next row with Nth's entry (read in from the parent EXA over M). If the edge case is in force (the date from the parent EXA will not be today's), it copies the first row of the file to the second row and sticks Nth's record in the first row instead.

Once that's done, it's just a matter of cleaning up. 273/93/7.
With such a comprehensive explanation, there's not much I can add. Your file writing code is quite elegant, and because it doesn't use M I wouldn't be surprised if it was faster.


=== Cybermyth studios ===



Time to help out Ghast... Wait, 1992? I was in 1998. Is this supposed to be a flashback or something?

I don't know, so let's just get to the task.


OST: Getting Started

The assignment this time:
- Each host contains two files: a list of accounts and a list of transactions. Although the entries in these files vary, the first value of each entry is a unique identifier that connects an account to one or more transactions.
- Determine the amount of back-pay owed to Ghast and Moss by subtracting the amount that they were paid (file 221) from the amount that they were owed (file 231). Then add their shell company (file 300) to the list of accounts payable (file 220) and issue a single payment to it for the total amount owed (file 221).
- Note that all monetary amounts are represented as two values: dollars, then cents.


That's quite a lot of information. Before I figure that out, let's look in the files. From the dates, it seems I'm definitely in 1992. The files in the RECEIVABLE host list some transactions related to companies. The RECORDS host has no transactions, but the files say that Ghast is on report for absenteeism and I (Moss) am on report for intoxication. Crap.

Something I hadn't noticed yet: my home host is named GHAST. Am I actually playing as Ghast? The same thing was the case for the other postgame tasks so far.

The expected goal shows that I actually have to change two things:
- Add the shell company TRASH WORLD CLEANING to file 220, starting with the first unused ID number.
- Add a transaction to file 221, with the ID of the shell company, a date (I guess today's from the #DATE register), and an amount.
That amount is the amount Ghast and Moss are owed, which I can get by subtracting the total paid amount from the total owed amount.

Looking at the data, the total owed amount is often more than the EXA's max value of 9999. At least in the first few tests, the amount paid is not. So, I should be able to solve this problem by writing the amount paid as a negative, then adding the amount owed to it, so I never cross the 9999 boundary.

For the total amount owed, it looks like the payroll has the same amounts for every week, AND everyone gets paid every week. That means I can just calculate the amount for one week and multiply that by the number of weeks.

Enough with the theory, let's write some actual code.

code:
;XA

LINK 800
LINK 801
; FIND GHAST'S ID
GRAB 220
COPY M X

MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F X
MODE
TEST MRD
TJMP MOSSDONE
REPL GHASTID

MODE

; FIND MOSS TOO
SEEK -9999
COPY M X
JUMP FINDPERSON

MARK MOSSDONE
VOID M


MARK GHASTID
GRAB 221
COPY 0 M

;XB

GRAB 300
COPY F M
COPY F M
XB just grabs the information file and sends data over M.
XA goes to the host containing the paid amounts and finds Ghast's and Moss's account ids. This is done through a basic search loop. But I realized that I'll never have enough registers in one EXA to test if a transaction belongs to either Ghast or Moss in one go. So I REPL the EXA containing Ghast's id (it barely fits in the host when the original EXA is holding a file). It grabs the other file, then sends a message over M in local mode. The TEST MRD in the search loop causes the search for Moss to end without a REPL and in MOSSDONE.

That means the original EXA holding the list of accounts now has Moss's id in X, the other one has Ghast's id.



The EXA containing Ghast's id changes to local mode, creates an EXA I called LOCALCTRL and starts sending Ghast's dollar amounts to it. LOCALCTRL adds them up and stops once it gets a zero. Because the M's interacted badly, I removed the MRD check from below the FINDPERSON, and added a check after the REPL, which tests if XB sent a 0. However, this doesn't get triggered because the EXA waits on the REPL command until a space is freed up. Something to think about later. At least now I have Ghast's amount of paid dollars.

code:
LINK 800
LINK 801
; FIND GHAST'S ID
GRAB 220
COPY M X

MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F X
COPY X M
REPL GHASTID


; FIND MOSS TOO
SEEK -9999
COPY M X
TEST X = 0
FJMP FINDPERSON

HALT


MARK GHASTID
MODE
GRAB 221
REPL LOCALCTRL

MARK COLLECT_DOLLARS
TEST F = X
FJMP WRONGACCOUNT
SEEK 1
COPY F M
SEEK 1
TEST EOF
FJMP COLLECT_DOLLARS
MARK WRONGACCOUNT
SEEK 3
TEST EOF
FJMP COLLECT_DOLLARS

COPY 0 M
SEEK -9999
COPY 1 M

MARK COLLECT_CENTS
TEST F = X
FJMP WRONGACCOUNT_CT
SEEK 2
COPY F M
TEST EOF
FJMP COLLECT_CENTS
MARK WRONGACCOUNT_CT
SEEK 3
TEST EOF
FJMP COLLECT_CENTS

COPY 0 M

MODE
SEEK -9999
COPY M X ; OTHER ID
MODE
COPY X M
TEST X = 0
FJMP COLLECT_DOLLARS

HALT

MARK LOCALCTRL
MAKE
COPY 0 X
MARK ADDAMOUNT
COPY M T
FJMP ADDAMOUNTDONE
ADDI T X X
JUMP ADDAMOUNT

MARK ADDAMOUNTDONE
COPY X F ; DOLLARS IN F
COPY 0 X
TEST M = 0
FJMP ADDAMOUNT


SEEK -9999
COPY F X
COPY F T
ADDI F X X
ADDI F T T

SWIZ T 0043 F
SWIZ T 0021 T
SEEK -1
ADDI F X X

NOOP
I added several steps. First, after the amount of dollars, I add up the amount of cents too. Moss's id is temporarily sent to XB and then retrieved by the EXA that's looking through the transactions. It then sends Moss's dollar and cent amounts. XB finally sends a 0 which causes both the sender and the LOCALCTRL EXA to jump to a final state. At that point LOCALCTRL is holding a file with respectively, an amount in dollars, cents, dollars and cents. It adds these up, making sure to add cent amounts > 100 to the dollar value. It ends with total dollars in X and total cents in T.


Okay, with that done I need to find out how much they are due.

I can do a bit of cleanup first. That original EXA is still sitting at REPL GHASTID. Instead of letting it continue, I can have the other one just do a KILL. That means that I can remove the TEST and HALT after it. There's also a place where I had to do a TEST X = 0 and if that's true it can HALT. It saves some space to just DIVI T X T and use the divide by zero trick.

Dealing with the payroll is a bit tricky, because the accounts there have other IDs. I need to get Ghast's and Moss's name from file 300 once more.
You know what, doing that with M is going to cause timing issues. I'll just have another EXA handle that altogether.


Enter the extended XB. I'll go through it bit by bit.
code:
;XB

GRAB 300
COPY F X
COPY F T
REPL PAYROLL

COPY X M
VOID M
COPY T M
COPY M X
COPY X M
COPY 0 M

MODE
COPY 0 M

HALT

MARK PAYROLL
MODE
LINK 800
LINK 802
After grabbing the file, it copies Ghast's and Moss's name to X and T. The 'main' XB still sends these values over global M to XA. XA's FINDPERSON code stores the result in X and sends it to M. The first time round, XB VOIDs the M. The second time round, because XA can never REPL, the id on M is necessary. XB buffers it in X, then forwards it to XA's existing REPL when it is ready to receive. Finally, when XA asks for a 'third name', XB send COPY 0 M. The 0 tells XA to continue with the next part (the calculation).

The MODE change followed by a final M message is for the PAYROLL REPL. Once PAYROLL is done, it'll come back to the home host and wait for this M, which indicates that XA is ready. After that, the original XB can HALT and the PAYROLL can take over. By the way, by reordering the code a bit I can get rid of this HALT by having the EXA fall through into harmless code where it dies.


Now, for the PAYROLL itself. It switches to MODE to LOCAL and will stay there. This way it can never interfere with the XB-XA GLOBAL communication.
code:
MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F M

; FIND MOSS TOO
SEEK -9999
COPY M X
DIVI 1 M T
JUMP FINDPERSON
Since the IDs in the payroll host differ, XB has a copy of the FINDPERSON code. This time, it just sends the result on local M. It then waits for a message. If this is a 0, it dies. If not, it waits for another message from M into X (Moss's name). We need to listen to M twice because attempting to do arithmetic on a text string crashes the EXA.

code:
MARK OTHER
GRAB 231
COPY M X
COPY 1 M
COPY T M
REPL CTRL

COPY 8 T
MARK WT
SUBI T 1 T
TJMP WT

MARK FIND
TEST F = X
SEEK 3
FJMP FIND
SEEK -2
COPY F M
COPY F M

COPY M X
COPY X T
TJMP FIND
The OTHER REPL GRABs the file with the amounts owed. Then it copies the ID found by FINDPERSON to X. It sends 1 to M to tell FINDPERSON to look for Moss, followed by Moss's name which is still sitting in T.

After that it needs to wait a bunch of cycles. That's because before the FIND loop can start looking for Ghast's salary, the CTRL EXA first needs to get Moss's ID. I'll get to that EXA in a second.

The FIND loop itself simply looks for Ghast's ID and then copies both the dollars and cents to M. If it gets Moss's ID (which is a non-zero number) it'll look for his amount too, otherwise this EXA will continue to another part of the code.

Before we go to that next part I'll show the CTRL REPL.

code:
MARK CTRL
COPY M X
COPY 0 M
MAKE
COPY M F
COPY M F
COPY X M
COPY M F
COPY M F
COPY 0 M
SEEK -9999
COPY F X
COPY F T
ADDI F X X
ADDI F T T

SWIZ T 0043 F
SWIZ T 0021 T
SEEK -1
ADDI F X X

WIPE
MAKE
COPY X F
COPY T F
First, it waits for the second ID from FINDPERSON. It sends a 0 to let FINDPERSON know it can stop looking. Then it makes a temporary file, in which it stores Ghast's salary (dollars, then cents). This EXA sends Moss's ID to the FIND EXA and then stores Moss's salary too. It sends a 0 to tell the FIND EXA it's done. It adds the two salaries together, and then does the same trick as XA, with the SWIZ to add any hundreds of cents to the dollar amount.

Finally, the temporary file is WIPEd and a new one is made to store the total salary in. Having a file without intermediate garbage makes things simpler later.

code:
LINK -1
DROP
LINK 802

GRAB 230
COPY 0 X
MARK COUNT
ADDI X 1 X
SEEK 2
TEST EOF
FJMP COUNT
MULI X 4 M
DROP
COPY 0 X
LINK -1
GRAB 402
LINK 802
The EXA then parks the temporary file in the gateway host (since the payroll host is full), and goes back to GRAB 230 again. This time, it counts the number of people on the payroll. This is multiplied by 4 (because there are 4 items per person per week in file 231) and sent to M, where the FIND EXA which is still holding 231 can read it. Once that's done, it quickly goes back to grab the temporary file (402) again.

Before we continue, let's look at what the FIND EXA has been up to.

code:
; COUNT
SEEK -9999
COPY M X

MARK COUNTPAYMENTS
COPY 1 M
SEEK X
TEST EOF
FJMP COUNTPAYMENTS

COPY 0 M
HALT
This EXA receives the multiplied value and saves it to X. For example, if there are 5 people on the payroll, it'll save '20' to X. That way, by doing a SEEK X it jumps ahead exactly one week. So, what this does is count how many times everyone (including Ghast and Moss) were supposed to be paid.

Since X holds the value to SEEK and T is used for EOF, the actual counting is done by COPY 1 M in the loop, followed by a COPY 0 M to let the CTRL EXA know when it's done. The HALT here is optional if I structure the code so this EXA dies without further side effects after the COPY 0 M.

code:
MARK MORE
COPY M T
ADDI X T X
TJMP MORE
LINK -1
LINK -1
VOID M
MODE

COPY X M
COPY F M
COPY X M
COPY F M
WIPE
GRAB 300
SEEK 2
COPY F M
This is the final part of the CTRL EXA and XB's code as a whole. This EXA is back in the payroll host. In the MORE loop it keeps track in X of the number of payments, and uses T to check when it's done. Then it LINKs back home, and then waits until the original XB sends that one LOCAL message to say XA is done.

At that point this EXA truly takes control by switching to GLOBAL mode, sending the number of payments, then the total amount of dollars, then the number of payments again and then the amount of cents.

If it's not quite clear what I've just done, here's an example:
- Moss gets paid $1076.92 per week.
- Ghast gets paid $961.53 per week.
- In total, they get paid 1076.92 + 961.53 = $2038.45 per week.
- The payroll says they owe payment for 5 weeks.

In this case, the CTRL EXA sends: 5; 2038; 5; 45. That way, the receiving EXA knows to apply $2038 five times, and the cents amount too. Since 5 * 2038 is greater than 9999, this is the most straightforward way to handle it.

Finally, this EXA GRABs file 300 so it can send the shell company's name before it stops.


To handle all this, I added more code to XA.

If you remember, we left off XA with the dollars-already-paid amount in X, and the cents-already-paid in T. It's also holding a temporary file containing intermediate date, with the file pointer sitting at the end.

code:
;XA continued

KILL
SUBI 0 T F
SUBI 0 X X

MODE
COPY M T
COPY M F


MARK AGAIN
SEEK -1
ADDI F X X
SUBI T 1 T
TJMP AGAIN

COPY X F
At this point, we're certain that the only other EXA in this host is the one stuck trying to REPL. I can safely kill it. Then, we store the negative of the cents amount in F and the negative of the dollars amount in X. The EXA switches to GLOBAL MODE and puts the amount of payments in T and the amount of dollars per week in F.

The AGAIN loop adds the amount owed to the negative of the amount paid repeatedly. For example, if the amount paid is $100 and the amount owed is 5 payments of $60, the calculation is -100 + 60 + 60 ... and so on. The result, which is the actual final amount Ghast and Moss are owed, is stored in F.

code:
SEEK -3
COPY F X
COPY M T
SEEK 9999
COPY M F

MARK AGAIN2
SEEK -1
ADDI F X X
SUBI T 1 T
TJMP AGAIN2

SEEK -2
SWIZ X 0043 T
ADDI F T T
SWIZ X 0021 X
Next, we repeat the exact same trick but with the cents. Again, I have to do the SWIZ to handle hundreds of cents.

While testing I found out this bit of code failed in one particular case. Because I'm subtracting the cents separately from the dollars, it's possible to end up with a negative amount of cents. For instance, 56 dollars and -29 cents. That's actually supposed to be $55.81. There might be a way around this by doing something smart with the SWIZ code but I just slapped a workaround after it.

code:
; NEGATIVE CENTS
COPY T F
TEST X < 0
FJMP SKIP
ADDI 100 X X
SEEK -1
SUBI F 1 T
JUMP COMPLETE

MARK SKIP
SEEK -1
COPY F T

MARK COMPLETE
Temporarily store the dollar amount (which was in T) to F, so I can do a test. Then, if the cent amount is negative, add 100 to it, and subtract 1 from the dollar amount. The dollar amount is put back into T.

Now, all that's really left is clean-up and writing the final values to the file.

code:
MARK COMPLETE
WIPE
GRAB 220
SEEK 9999
SEEK -2
REPL DATE
ADDI F 1 X
SEEK 9999
COPY X F
COPY M F
The EXA now WIPEs its temporary file and GRABs 220 again. First, it finds the highest ID in the file and adds one to it. This value is saved to the end of the file, followed by the shell company's name, which we get on M from XB.

code:
DROP
GRAB 221
SEEK 9999
COPY X F
COPY M F
COPY T F
COPY M F
Then, it grabs file 221, and adds a new transaction, starting with the shell company's ID number, and then the DATE which it gets from the DATE REPL. It then copies the dollars amount from T, and it gets the cents amount from DATE as well, since it was overwritten in X by the ID.

code:
MARK DATE
LINK -1
LINK 804
NOOP
COPY #DATE M
COPY X M
The REPL itself doesn't have much code. It goes grab the current date from the hardware register, sends that, and then the cents.

The NOOP here is absolutely necessary. Without it, it would start sending just before the other EXA had the chance to read the shell company's name from M. If I had put the REPL DATE instruction any earlier I would've needed even more waiting cycles here. And I couldn't put it any later, because after that point, X gets overwritten.


That's every single piece of the code explained. While testing, I ran into one more bug. It is possible for the amount of cents in a payment to be 0. And I was already used 0 to let the other EXA know no more payments are coming. I fixed that by sending the amount + 1 and then subtracting 1 once the other EXA receives it. I had to do this for dollars as well because they share the receive code.

The full code is... hard to read because I shuffled blocks of code to remove a bunch of HALT instructions. It's very long, so I added it in an appendix at the bottom.



Oh. It's way too long.

Well, the good news is that even though I don't get a high score, the game still counts this task as completed. I'm glad, because I spent enough time on it that I really don't want to optimize it further.

I can see my score by looking at the detailed test run data. It's 536/246/16.

Outside the level, the histograms show up.


The top percentiles are 215, 92, and 4, but for size there's a tiny little peak in the histogram around 60. I can think of some optimizations but getting it down to 60-ish? I have no idea how. My speed though is close to tenth percentile, and I'm happy with that.



Next time, hydroponix.


=== Appendix ===

code:
;XA

LINK 800
LINK 801
; FIND GHAST'S ID
GRAB 220
COPY M X

MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F X
COPY X M
REPL GHASTID

; FIND MOSS TOO
SEEK -9999
COPY M X
JUMP FINDPERSON


MARK GHASTID
MODE
GRAB 221
REPL LOCALCTRL

MARK COLLECT_DOLLARS
TEST F = X
FJMP WRONGACCOUNT
SEEK 1
ADDI F 1 M
SEEK 1
TEST EOF
FJMP COLLECT_DOLLARS
MARK WRONGACCOUNT
SEEK 3
TEST EOF
FJMP COLLECT_DOLLARS

COPY 0 M
SEEK -9999
COPY 1 M

MARK COLLECT_CENTS
TEST F = X
FJMP WRONGACCOUNT_CT
SEEK 2
ADDI F 1 M ; HANDLE 0
TEST EOF
FJMP COLLECT_CENTS
MARK WRONGACCOUNT_CT
SEEK 3
TEST EOF
FJMP COLLECT_CENTS

COPY 0 M

MODE
SEEK -9999
COPY M X ; OTHER ID
MODE
COPY X M
DIVI T X T
FJMP COLLECT_DOLLARS


MARK LOCALCTRL
MAKE
COPY 0 X
MARK ADDAMOUNT
COPY M T
FJMP ADDAMOUNTDONE
SUBI T 1 T
ADDI T X X
JUMP ADDAMOUNT

MARK ADDAMOUNTDONE
COPY X F ; DOLLARS IN F
COPY 0 X
TEST M = 0
FJMP ADDAMOUNT


SEEK -9999
COPY F X
COPY F T
ADDI F X X
ADDI F T T

SWIZ T 0043 F
SWIZ T 0021 T
SEEK -1
ADDI F X X

KILL
SUBI 0 T F
SUBI 0 X X

MODE
COPY M T
COPY M F


MARK AGAIN
SEEK -1
ADDI F X X
SUBI T 1 T
TJMP AGAIN

COPY X F

SEEK -3
COPY F X
COPY M T
SEEK 9999
COPY M F

MARK AGAIN2
SEEK -1
ADDI F X X
SUBI T 1 T
TJMP AGAIN2

SEEK -2
SWIZ X 0043 T
ADDI F T T
SWIZ X 0021 X

; NEGATIVE CENTS
COPY T F
TEST X < 0
FJMP SKIP
ADDI 100 X X
SEEK -1
SUBI F 1 T
JUMP COMPLETE

MARK DATE
LINK -1
LINK 804
NOOP
COPY #DATE M
COPY X M

MARK SKIP
SEEK -1
COPY F T

MARK COMPLETE
WIPE
GRAB 220
SEEK 9999
SEEK -2
REPL DATE
ADDI F 1 X
SEEK 9999
COPY X F
COPY M F
DROP
GRAB 221
SEEK 9999
COPY X F
COPY M F
COPY T F
COPY M F

;XB

GRAB 300
COPY F X
COPY F T
REPL PAYROLL

COPY X M
VOID M
COPY T M
COPY M X
COPY X M
COPY 0 M

MODE
COPY 0 M

MARK OTHER
GRAB 231
COPY M X
COPY 1 M
COPY T M
REPL CTRL

COPY 8 T
MARK WT
SUBI T 1 T
TJMP WT

MARK FIND
TEST F = X
SEEK 3
FJMP FIND
SEEK -2
COPY F M
COPY F M

COPY M X
COPY X T
TJMP FIND

; COUNT
SEEK -9999
COPY M X

MARK COUNTPAYMENTS
COPY 1 M
SEEK X
TEST EOF
FJMP COUNTPAYMENTS

COPY 0 M

MARK PAYROLL
MODE
LINK 800
LINK 802
GRAB 230
REPL OTHER

MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F M

; FIND MOSS TOO
SEEK -9999
DIVI 1 M T
COPY M X
JUMP FINDPERSON


MARK CTRL
COPY M X
COPY 0 M
MAKE
COPY M F
COPY M F
COPY X M
COPY M F
COPY M F
COPY 0 M
SEEK -9999
COPY F X
COPY F T
ADDI F X X
ADDI F T T

SWIZ T 0043 F
SWIZ T 0021 T
SEEK -1
ADDI F X X

WIPE
MAKE
COPY X F
COPY T F
LINK -1
DROP
LINK 802

GRAB 230
COPY 0 X
MARK COUNT
ADDI X 1 X
SEEK 2
TEST EOF
FJMP COUNT
MULI X 4 M
DROP
COPY 0 X
LINK -1
GRAB 402
LINK 802

MARK MORE
COPY M T
ADDI X T X
TJMP MORE
LINK -1
LINK -1
VOID M
MODE

COPY X M
COPY F M
COPY X M
COPY F M
WIPE
GRAB 300
SEEK 2
COPY F M