Now we need to write our executable program. For now we will focus on our program and not worry about how to get it punched into 80-byte cards.
First we need to establish a base register. This code is written to be relocatable so it should run from anywhere we place it in memory. We can move it to somewhere else other than location 1024 by modifying the PSW and the three CCWs used to read in the program.
BEGIN DS 0D BALR R10,0 * Establish a Base Address USING *,R10 * Tell the assembler *
Next we need to get the device address for the card reader used for the IPL process. This is where we will expect to find our data cards. When the CPU completes the IPL process and loads our initial PSW but before execution begins, the address of the IPL device is saved. Since we are loading a BC PSW the IPL device address is saved in locations 2 and 3.
SLR R3,R3 * Clear Register 3 ICM R3,B'0011',2 * Load IPL Device Address LA R4,X'E' * Assume Printer at 00E *
We will assume the printer is at device address X’00E’ which is usually a standard place to define a printer. The standard location for the card reader is X’00C’ and that for the card punch is X’00D’.
Now we can handle some housekeeping to make our program relocatable. If we were running with the help of an operating system we would probably have a relocating loader. This means we could use an address constant (ADCON) in our CCW to point to the buffer. When the loader read our program into memory the value in the ADCON would be relocated or updated to reflect our actual location in memory.
Since we are Bare Metal programming we don’t have an operating system or relocating loader. In fact our loader is just some CCWs that are executed by the channel at IPL. We will use a LA and STCM to get the actual address of the buffer at run time and store it into our CCWs.
LA R1,RDBUF * Get the address of the I/O Buf STCM R1,B'0111',RDCCW+1 Place address into Read CCW STCM R1,B'0111',PRCCW+1 Place address into Print CCW
Now we are ready to enter the main loop for our 80-80 List program. The first thing we want to do is attempt to read a card from the same card reader that was used as the IPL device. Before issuing the Start I/O we need to place the address of our read CCW into the CAW.
LA R1,RDCCW * Get address of Read CCW ST R1,72 * Place into CAW (location 72)
Our Read CCW should look very familiar since it is almost exactly like the CCWs used in our IPL process. We will not set any flags in the CCW this time. Since we are not command chaining our CCW program will consist of a single CCW. We are using command code X’02’ to read, the address is filled in at run time, and we are expecting 80 bytes of data. This time we will leave the SLI bit off and if the card reader attempts to transfer more or less than 80 bytes an exceptional condition will occur.
RDCCW DC X'02',AL3(0),X'0000',AL2(80)
Now we can issue the Start I/O instruction to cause the channel to execute our channel program. We have saved the address of the card reader in register three. We then check the condition code to see if the channel is processing our request. If not we load a Wait PSW and halt execution.
SIO 0(R3) * Issure Start I/O to Channel BNZ ERR1 * If Chan not processing - error
Now we have to wait for the I/O to complete. Since we have masked all I/O interrupts the CPU will not generate an interrupt when the I/O is complete. Instead we will use the Test I/O instruction to check on the progress of the channel program. If the I/O is complete we will branch out of our wait loop. If the return code indicates the channel or device is not operational we will load a Wait PSW. If the channel is still busy we will loop back and issue the Test I/O again.
RDTIO DS 0H TIO 0(R3) * Test for I/O Completion BZ PRT * Branch if I/O Complete BC 1,ERR2 * Branch if not operational B RDTIO * Loop back and wait for I/O
Now we need to check for successful completion of our channel program. We know that it completed and the completion status was stored into the CSW as a result of our issuing a Test I/O against the device. We will test the status bits and we only expect to find Channel End and Device End set. If we find anything else some type of exceptional condition has occurred. We use a CLC to compare the two bytes of status bits in the CSW (located at fixed location 68) to a constant with just the Channel End and Device End bits set X’0C00′
PRT DS 0H CLC CEDE,68 * Compare status bits BNE ERR0 * Branch if not CE+DE . . CEDE DC X'0C00' * CE+DE
If we get anything else other than CE+DE we will save the status bits of the CCW into the address portion of a Wait PSW and then load that PSW to halt execution. The reason we store the status bits in the CSW is so they become the wait code (the address portion of the wait PSW) and are visible on the operator console.
ERR0 MVC PSWERR0+6(2),68 * Move CSW status bits to PSW LPSW PSWERR0 * Load Wait PSW . . PSWERR0 DC X'00',X'02',X'0000',x'00',x'000000'
If there is no error then we issue a Start and I/O to the printer to print out the contents of the card we just read. Just as with reading a card we store the address of the CCW into the CAW and then issue the Start I/O against the device. We check the condition code from the Start I/O to verify the channel program is active.
LA R1,PRCCW * Point to Print CCW ST R1,72 * Store into CAW SIO 0(R4) * Start I/O on Printer BNZ ERR3 * Branch if Error
The CCW to print is similar to the CCW to read a card from the card reader. Each device has its own set of command codes. Printers have a number of command codes they respond to. Below is a quick summary of a few command codes for a printer.
01 - Write and Do Not Space 09 - Write and Space 1 Line 11 - Write and Space 2 Lines 19 - Write and Space 3 Lines 0B - Space 1 Line Immediate 13 - Space 2 Lines Immediate 1B - Space 3 Lines Immediate
We will use command code X’09’ to write a line and then space down to the next line.
PRCCW DC X'09',AL3(0),X'0000',AL2(80)
As with the read CCW, the data address is filled in at run time. We will print 80 bytes of data and no flags are set. Our channel program will consist of a single CCW.
Next we enter a Test I/O loop to wait for completion of the I/O. For now when the I/O is complete we assume it was successful. We could check the CSW status bits for any exceptional conditions but we don’t for now to make our program smaller and simpler.
PRTTIO DS 0H TIO 0(R4) * Wait for I/O to Complete BZ RDLP * Done - Go read next card BC 1,ERR4 * Branch if error B PRTTIO * Loop back and test again [Next - Creating The IPL Deck]