OPT l * OPT is the directive that tells the assembler what SHIFTK EQU 128 * to do with the output. It is optional, but here it is ALT EQU 64 * used to create a program listing. Very helpful when CTRL EQU 32 * something goes wiggle and you have to find out where. PSCRLCK EQU 16 PNUMLCK EQU 8 * All that these EQU statements do is EQUate the name CAPLOCK EQU 4 * of the variable with the value. These values are used NUMLOCK EQU 2 * to map out a byte of memory: STATUS. These are the SCRLOCK EQU 1 * conditions that are stored in STATUS. LEDSTAT EQU 128 * Unlike the previous block of eight EQU's, these are LCDREG EQU 64 * used to map out PORTC. In my design, PORTC does many LCDSTRB EQU 32 * things: Handles keyboard I/O, controls the LCD, sets DTMFON EQU 16 * an indicator LED, and activates the DTMF unit. Whew! CLKIN EQU 8 * Basically what the compiler does with these is this: CLKOUT EQU 4 * Every time it sees "LEDSTAT", it will replace it DATAIN EQU 2 * with 128. This is done for the sole purpose of code DATAOUT EQU 1 * readability. ORG $61 * Sets the location to $61 ($0 - $200 is on-chip RAM). DATA RMB 1 * Reserve Memory Byte. Tells the HC11 that whenever I PARITY RMB 1 * talk about DATA, I really mean address $61. Again, STATUS RMB 1 * only for readability. DATA is the place where ASCII LEDS RMB 1 * and scancodes are kept. PARITY is used for parity LCDDATA RMB 1 * calculations. STATUS is to keep track of what keys SPACE RMB 1 * are being pressed. LEDS keeps track of what LEDS are PHYSS RMB 1 * lit up. LCDDATA is for ASCII on its way to the LCD. LINEN RMB 1 * SPACE, PHYSS, and LINEN are used by LCD routine. ORG $FFFE * $FFFE and $FFFF store the address the HC11 will jump FDB $D000 * to after a reset. Form Double Byte stores the desired ORG $FFD6 * address there. $FFD6 and $FFD7 store the address for FDB SCIIN * the SCI interrupt service routine. SCIIN is the label ORG $D000 * for this address. (The actual address is $D433) LDS #$60 * This cost me at least two days of hair-pulling. I LDY #$1000 * origianally had the stack loaded with $100(256). This LDX #$0000 * placed all my variables (DATA, PARITY, etc...) past LDAA #%11110101 * the 256 byte limit for direct addressing. Nothing STAA $1007 * would work. So I reduced the stack size and all was LDAA #%00010101 * well. These are just preliminary things. LoaD Stack STAA $1003 * immediatly with $60, LoaDY with $1000 for indexed LDAA #$30 * addressing, same with X. Set up PORTC data direction STAA $102B * register, and give PORTC its initial state. Then set LDAA #$00 * up the SCI for 9600 bps, transmitter and receiver STAA $102C * enabled, and using interrupt mode. This code is not LDAA #%00101100 * fully optimised yet. Maybe later I will get around to STAA $102D * that.... LCDINIT LDX #2000 * LCD initiallization routine. Here, the index register JSR DELAY1 * X is used to hold a delay value. Jump Sub-Routine LDAA #%00111000 * DELAY1 counts down from whatever is in X. LDX #4996 * DISPCOM is the routine to send a command to the LCD. JSR DELAY1 JSR DISPCOM * Since the code is a bit repetitive for awhile, I will LDX #1363 * use this space to talk about indexed addressing. It JSR DELAY1 * looks and sounds scary, but its not once you know how JSR DISPCOM * it works. Take this line: BSET $03,Y #DTMFON. LDX #30 * BSET = BIT Set, $03,Y = Byte address, DTMFON = bit JSR DELAY1 * What this does is sets the said bit of the said byte. JSR DISPCOM * Really the only thing scary about this is how the LDX #1363 * address of the byte is derived. Well, here's how: JSR DELAY1 * $03 is the number of bytes you want to offset the JSR DISPCOM * number in index register Y by. In other words: LDX #10 * Byte address = $03 + Y. Remember when I loaded Y with JSR DELAY1 * $1000? This is why. LOL, Y = Why... Ok, that was bad. LDAA #%00001110 * So if address = $03 + Y, and Y = $1000, then address JSR DISPCOM * is equal to $1003. This is the address for PORTC. Now LDX #10 * the bit part. # means immediately, and DTMFON is JSR DELAY1 * really just 128, so you could re-write the statement LDAA #%00000001 * like this: BSET $1003 #128. Unfortunatly, indexed JSR DISPCOM * addressing must be used here. But after this line, if LDX #544 * PORTC looked like this: 00000101. JSR DELAY1 * It now looks like this: 10000101. LDAA #128 * LoaD Accumulator A immediately (#) with 128. STAA PHYSS * Store Accumulator A at PHYSS. This is the address LDAA #1 * that will be used by the LCD. STAA SPACE * SPACE is used to keep tabs on the X posistion of the STAA LINEN * cursor on the face of the LCD. LINEN is used to hold BSET $03,Y #DTMFON * the Y posistion. Bit SET: This disables the DTMF unit LDX #MESS1 * so that its quite until its called. LDX #MESS1 LoaDs JSR DISPMES * X with the location of MESSage 1. DISPMES is the sub LDX #0 * that displays the message. More on that sub later... BRA RSTFLAG * Most of the icky setup crap is out of the way now. FULLRST JSR ERROR * FULLRST is called when the user hits CTRL+ALT+DEL. LDAA #1 * Yes, I really put that in here. This sub first calls STAA LCDDATA * the sub ERROR, which sends $FF to the keyboard to JSR DISPCOM * reset it. Next, the LCD is cleared and its variables LDAA #1 * are reset. Again, this code is rather unoptimized. STAA SPACE * Here is another example. Everything between JSR ERROR STAA LINEN * and RSTFLAG could have been replaced with one line: LDAA #128 * JSR CLS. I didn't have that sub written at this time STAA PHYSS * so I did this. Again, if I ever get around to it.... RSTFLAG CLR DATA * These lines sync the status flags with the keyboard's CLR PARITY * reset condition. It also flushes any garbage that CLR STATUS * still remains in any of the variables. This is CLR LEDS * incredibly redundant. I am curious as to how efficent CLR LCDDATA * I can make this code. Maybe when I'm done commenting. MAIN BSET $03,Y #CLKOUT * Here is where thing start to click. When the clock BRSET $03,Y #CLKIN * * line is left high (Idle), it signals the keyboard JSR RECEIVE * that its ok to send something. If the HC11 pulls the LDAA DATA * clock line low, the keyboard will buffer any keys CMPA #$F0 * pressed and wait until the clock line is again idle BNE MAIN1 * to send them. $F0 is what the keyboard sends to tell JMP RELEAS * us that a key has been released. If the byte is not MAIN1 CMPA #$AA * $F0, BRAnch to MAIN1. $AA means the keyborad has been BNE MAIN2 * reset and has completed its self test. If byte is $AA BRA RSTFLAG * then BRAnch to RSTFLAG (redundancy). MAIN2 CMPA #$E0 * CoMPare A to $E0. $E0 is the first byte sent when an BNE MAIN3 * extended key is pressed. If $E0, then goto EXTEND. JMP EXTEND * Else, goto MAIN3. $59 is the right-hand shift and MAIN3 CMPA #$12 * $12 is the left-hand shift key. If pressed, the HC11 BNE MAIN4 * sets the SHIFTK bit of the STATUS byte. X = $0 here BSET STATUS,X #SHIFTK * so the offset ends up being the actual end location. BRA MAIN * If this was the case set the bit and go back to MAIN. MAIN4 CMPA #$59 * This was a speed optimization on my part. It used to BNE MAIN5 * keep running through MAIN12 when a shift key was hit. BSET STATUS,X #SHIFTK * This was unnessesary because this is the only action BRA MAIN * taken when shift is pressed. By returning to main, I MAIN5 CMPA #$14 * was simply saving clock cycles. $14 is the left CTRL BNE MAIN6 * key. Again, if pressed, its respective STATUS bit is BSET STATUS,X #CTRL * set and program is returned to MAIN. The other CTRL BRA MAIN * and ALT keys are extended keys, and so aren't in this MAIN6 CMPA #$11 * loop. They are located down in the EXTEND loop. $11 = BNE MAIN7 * the left ALT. Once again, bit set, goto MAIN. I tried BSET STATUS,X #ALT * to lay these out in the order of their relevence and BRA MAIN * still maintain perfect functionallity. $58 is the MAIN7 CMPA #$58 * scancode for Caps Lock. This is a bit complicated. If BNE MAIN8 * byte = $58 and CAPLOCK is clear, then goto CAPS. BRCLR STATUS,X #CAPLOCK CAPS * Otherwise, simply set the capslock bit. BSET STATUS,X #CAPLOCK BRA MAIN MAIN8 CMPA #$7E * $7E is Scroll Lock. Having no idea what the function BNE MAIN9 * of this apparently useless key is, I assigned it the BRCLR STATUS,X #PSCRLCK SCRL * task of turning on DTMF mode. This part of MAIN worx BSET STATUS,X #PSCRLCK * just like the part for Caps did. MAIN9 CMPA #$77 * $77. Numlock. Yey. Again, works just like the two BNE MAIN9C * parts prior to it do. Num Lock has no function over BRCLR STATUS,X #PNUMLCK NUMS * the num pad, rather it controls whether or not the BSET STATUS,X #PNUMLCK * charecters are echoed to the SCI. MAIN9C CMPA #$04 * $04 is the F3 key. When this is found, the program BNE MAIN9A * JuMPs to the communication test sub. Its kinda JMP RUTHERE * useless, but hey, I have 12 F-Keys and 12k of space. MAIN9A CMPA #$05 * $05 is the F1 key. Ya know? The one you normally hit BNE MAIN9B * when you're in a program and you don't know what to JMP HELPME * do. This one goes to the help routine. MAIN9B CMPA #$06 * $06 = F2. This key has no other reason to exist other BNE MAIN10 * than to call the CLS sub. Thats it. JSR CLS JMP MAIN MAIN10 CMPA #$8F * $8F is the last scancode on the map. The program BLO MAIN11 * Branch's if LOwer than $8F to MAIN11 because if we JMP MAIN * haven't already checked for it, and its valid, its MAIN11 BRSET LEDS,X #CAPLOCK CAPS_ON * something that can be printed on the LCD. BRSET STATUS,X #SHIFTK SHIFTON * These lines check the status of shift and caps to CANCEL LDX #NOSHIFT * determine what section of the table it should look JSR GETASC * at. CANCEL cancels out the caps lock if shift is held BRA MAIN12 * while caps is active. CAPS_ON BRSET STATUS,X #SHIFTK CANCEL * If either caps is active or shift is pressed, goto SHIFTON LDX #SHIFT * the SHIFT section of the lookup table. Whatever the JSR GETASC * case, the program must call get ASC to find the ASCII MAIN12 CMPA #0 * equivalent. If the lookup table returned $0, then the BEQ RETURN * character is not to be printed, and the program goes JSR WHERE * back to MAIN and does everything again. RETURN JMP MAIN * JuMP MAIN. GETASC TAB * Transfer Accumulator A to Accumulator B. ABX * Add Accumulator B to Index register X. LDAA $00,X * LoaD Acc A with the data at X. I transfered A to B STAA DATA * because Acc A cannot be added to X. I don't know why, RTS * but thats the situation. CAPS BSET STATUS,X #CAPLOCK * LoaDs Acc A with LEDS and Exclusive OR's it with $04. LDAA LEDS * Then it STores Acc A at LEDS and JuMPs to LEDSHOW. EORA #$04 * The next two subs work in the same manner. X-ORing STAA LEDS * worx like this: XOR gives a low if both inputs are JMP LEDSHOW * the same (high or low), and high if they are diffent. NUMS BSET STATUS,X #PNUMLCK * Here's the truth table for XOR: ~~~~~~> | I | I | O | LDAA LEDS * What this means in this application: |---|---|---| EORA #$02 * XOR is implemented as nothing more than | 0 | 0 | 0 | STAA LEDS * a toggle switch. By XORing with a known | 0 | 1 | 1 | JMP LEDSHOW * value, it effectivly just changes the | 1 | 0 | 1 | SCRL BSET STATUS,X #PSCRLCK * state of the unknown input. So ya see? | 1 | 1 | 0 | LDAA LEDS * Other logic truth tables will be given later, but I EORA #$01 * don't have the room right here. Also, I'll produce a STAA LEDS * highly optimized version of this code (probably not JMP LEDSHOW * as well commented). EXTEND JSR RECEIVE * This is the loop to decode extended keys. The program LDAA DATA * BRAnches here after it sees $E0 come down the pipe. CMPA #$F0 * This loop goes back to receive to get the next byte, BNE EXTEND1 * and then things progress in much the same way as the JMP REL_EXT * MAIN loop. I won't comment everything here because it EXTEND1 CMPA #$11 * would be very repetitive and boring. BNE EXTEND2 * AND OR When you see NAND or BSET STATUS,X #ALT * | I | I | O | | I | I | O | NOR, it means that JMP MAIN * |---|---|---| |---|---|---| the output of the EXTEND2 CMPA #$14 * | 0 | 0 | 0 | | 0 | 0 | 0 | gate is inverted. IE BNE EXTEND3 * | 0 | 1 | 0 | | 0 | 1 | 1 | where output would BSET STATUS,X #CTRL * | 1 | 0 | 0 | | 1 | 0 | 1 | normally be 1, it is JMP MAIN * | 1 | 1 | 1 | | 1 | 1 | 1 | now 0 and viceversa. EXTEND3 CMPA #$71 * Here is the CTRL+ALT+DEL routine. This only happens BNE EXT3U * when the Delete key ($71) is hit, and only continues LDAB STATUS * if Control and Alt are also held down. If all these ANDB #32 * conditions are met, PORTA is lit up, cleared, and BEQ EXT3U * then the program JuMPs to FULLRST. This whole sub LDAB STATUS * follows pretty much the same progression. ANDB #64 BEQ EXT3U LDAA #255 STAA $1000 JSR DELAY STAA $1000 JMP FULLRST EXT3U CMPA #$75 * This is strange. Since there's not an ASCII character BNE EXT3D * to just move the cursor in any given direction, I had LDAA #21 * to develop some method to pass this operation on to STAA DATA * the LCD sub. I chose this: I used some values that JSR WHERE * wern't useful control codes and reassigned them. I JMP RELEXT3 * then had the LCD code check for these numbers before EXT3D CMPA #$72 * it printed anything. If one was found, it moved the BNE EXT3L * cursor rather then printing the ASCII equivillent of LDAA #22 * the number. STAA DATA JSR WHERE JMP RELEXT3 EXT3L CMPA #$6B BNE EXT3R LDAA #23 STAA DATA JSR WHERE JMP RELEXT3 EXT3R CMPA #$74 BNE EXTEND4 LDAA #24 STAA DATA JSR WHERE JMP RELEXT3 EXTEND4 CMPA #$5A * $5A is an enter key. BNE EXTEND5 LDAA #$0D STAA DATA JSR WHERE EXTEND5 CMPA #$4A * $4A = '/' BNE EXTEND6 LDAA #$2F STAA DATA JSR WHERE EXTEND6 JMP MAIN RELEAS JSR RECEIVE * When the program gets a $F0 from the keyboard, it LDAA DATA * means that a key has been released. This sub figures RELEAS3 CMPA #$12 * out which one it was and changes status flags as BNE RELEAS4 * nessisary. BCLR STATUS,X #SHIFTK RELEAS4 CMPA #$59 BNE RELEAS5 BCLR STATUS,X #SHIFTK RELEAS5 CMPA #$14 BNE RELEAS6 BCLR STATUS,X #CTRL RELEAS6 CMPA #$11 BNE RELEAS7 BCLR STATUS,X #ALT RELEAS7 CMPA #$58 BNE RELEAS8 BCLR STATUS,X #CAPLOCK RELEAS8 CMPA #$7E BNE RELEAS9 BCLR STATUS,X #PSCRLCK RELEAS9 CMPA #$77 BNE RELEA10 BCLR STATUS,X #PNUMLCK RELEA10 JMP MAIN REL_EXT JSR RECEIVE * Extended release. When and extended key is released, LDAA DATA * first a $E0 is sent, then a $F0. Again, this sub CMPA #$11 * out which one it was and changes status flags as BNE RELEXT2 * nessisary. BCLR STATUS,X #ALT JMP MAIN RELEXT2 CMPA #$14 BNE RELEXT3 BCLR STATUS,X #CTRL RELEXT3 JMP MAIN LEDSHOW LDAA LEDS ORAA #$20 ANDA #$27 LDAA #$ED * This sub changes the LEDs on the keyboard. Nothing STAA DATA * much here, sends $ED (LED Change) command to keyboard JSR TRANS * and then sends the code that tells the keyboard how JSR RECEIVE * it should set the LEDs. LDAA LEDS ANDA #$07 STAA DATA JSR TRANS JMP MAIN DELAY DECA * Used this because the X register was in use at the BNE DELAY * moment the delay was needed, and I didn't want to use RTS * the stack at that point. RECEIVE JSR IBITON * This is the sub that does most of the talking to the BSET $03,Y #CLKOUT * Release the clock line so the keyboard can talk. LDAB #08 * Loads B with the number of bits to recieve. CLR PARITY * CLeaRs the PARITY variable. CLR DATA * CLeaRs the DATA variable. BRCLR $03,Y #CLKIN * * Waits until the keyboard starts to generate a clock. BRSET $03,Y #DATAIN RECEIVE * If the start bit is set something is wierd. Restart. RECDATA ROR DATA * ROR = ROtate Right. Since the LSB is sent first, we JSR HIGHLOW * do this. JSR HIGHLOW waits for the falling edge of BRSET $03,Y #DATAIN RECSET * the clock. If the next bit is a 1, then BRAnch to BCLR DATA #%10000000 * RECSET. Otherwise store a zero in the 128 place, so BRA RECNEXT * that when all 8 bits are recieved, what was the most RECSET BSET DATA #%10000000 * significant bit will be in the least significant bits INC PARITY * place. For every 0 in the byte, PARITY is incremented RECNEXT DECB * so that a parity calculation can be made later. B is BNE RECDATA * decremented. Loop until 8 bits have been retrieved. JSR HIGHLOW LDAA $1003 * Next, get the parity bit. LSRA * LSRA = Logical Shift Right A EORA PARITY * Exclusive OR A with PARITY. ANDA #$01 * Determine if the parity turned out even or odd. Check BEQ R_ERROR * ones place. If it turned out even, something went JSR HIGHLOW * wrong. Ask the keyboard to send it again. BRCLR $03,Y #DATAIN R_ERROR * Just in case the parity slipped by, but the byte was BCLR $03,Y #CLKOUT * incorrect anyway, we check the stop bit(Should be 1). JSR IBITOFF * If its 0, ask for a re-send. If all is ok, hold the RTS * clock line low so that the keyboard doesnt send when * we aren't ready for it. R_ERROR LDAA #$FE * $FE is the re-send command. STAA DATA JSR TRANS BRA RECEIVE * Try to recieve again. HIGHLOW BRCLR $03,Y #CLKIN * * Wait for clock line to go high. BRSET $03,Y #CLKIN * * Wait for clock line to go low. RTS TRANS LDX #8 * Again, load X with the number of bits. CLR PARITY JSR IBITON BCLR $03,Y #CLKOUT * Take control of the clock line and wait a bit. LDAA #$13 JSR DELAY CLRA BCLR $03,Y #DATAOUT * Take the data line low while releasing clock. The BSET $03,Y #CLKOUT * keyboard will start generating a clock signal. JSR HIGHLOW * Wait for the clock to go low. LDAA DATA * Get the data to be sent. LOOPT RORA * Rotate right, since LSB is sent first, we at to the BCS MARK * carry bit of the CCR (Condition Code Register) to see SPACE0 BCLR $03,Y #DATAOUT * what the LSB is. BCS = Branch if Carry Set. If its BRA NEXT * 0 then clear the data line. If its set, then the data MARK BSET $03,Y #DATAOUT * line is set and PARITY is incremented. INC PARITY NEXT JSR HIGHLOW * Wait for clock to go low again. DEX * DEcrement X, and loop until X = 0. BNE LOOPT LDAB PARITY * Time for the parity calculation again... ANDB #$01 BNE CLR_PAR SET_PAR BSET $03,Y #DATAOUT * If the number of zero's is even, set the parity bit. BRA TR_ACKN CLR_PAR BCLR $03,Y #DATAOUT * If the number of zero's is even, set the parity bit. TR_ACKN JSR HIGHLOW BSET $03,Y #DATAOUT * Set the stop bit. JSR HIGHLOW * Wait again for clock cycle. BRSET $03,Y #DATAIN ERROR * Check for error. BRCLR $03,Y #CLKIN * BSET $03,Y #CLKOUT JSR IBITOFF * Re-enable interrupts. RTS ERROR LDAA #$FF * If something goes really wrong or you simply want a STAA DATA * reset, this is what happens. $FF is the reset JSR TRANS * command. A is loaded with $FF and then we JSR to RTS * TRANS and RTS. RUTHERE LDAA #$EE * This sub tests the bi-directional communication of STAA DATA * the keyboard to make sure everything is working as it JSR TRANS * should. This was put here more for debugging reasons, JSR RECEIVE * but I saw no real reason to take it out and so I just LDAA DATA * left it here. When the user hits F3, the program CMPA #$EE * sends the echo command to the keyboard. If everything BEQ THEREUR * is working right, the keyboard should send the same LDX #MESS10 * thing ($EE) back again. If it does, then MESS11 is JSR DISPMES * displayed. Otherwise, MESS10 goes up. THEREUR LDX #MESS11 JSR DISPMES ENDTHER JMP MAIN WHERE LDAA LEDS * This sub tells the HC11 where to put the ASCII byte ANDA #1 * it tripped upon. It checks the status of SCRLOCK and BNE DTMFL * NUMLOCK and places the byte according to these rules: LDAA LEDS * -If SCRLOCK = 1, send byte to DTMF unit. ANDA #2 * -If NUMLOCK = 1, send byte to SCI and echo to LCD. BNE SCIOUT * -If NUMLOCK and SCRLOCK = 0, echo to LCD and stop. BACK2P LDAA DATA * -If SCRLOCK = 1, echo to SCI and LCD is disabled. STAA LCDDATA * This is basicaly the piece of the program that routes JSR LCDMAIN * the output to where the user wants it to go. Pretty BACK2R RTS * eventful huh? No? Not really? Yeah, I guess not... DELAY2 LDX #455 * Delays are very nessisary in this program. Since the BRA DELAY1 * MCU can do everything at break-neck speed, often its DELAY3 LDX #14 * far too fast for the peripherals attached to it. All DELAY1 DEX * that happens here is X is loaded with a number and it BNE DELAY1 * is then decremented until X = 0. DELAY2 and DELAY3 RTS * are just different time values (1.13 and 0.04 ms). SCIOUT JSR IBITON * First the I-Bit is set so an interrupt won't screw up SCIOUT1 LDAB $102E * the transmission. It probably wouldn't hurt anything ANDB #$80 * to allow the program to be interrupted here, but I BEQ SCIOUT1 * didn't take the chance. Second, the program looks at LDAA DATA * the SCI Status register to see if its ok to send. If STAA $102F * not, it keeps checking until the pending character JSR IBITOFF * has been sent. If it is ok, the current CHR is sent, BRA BACK2P * I-Bit is turned off, and the program resumes. DTMFL LDAA DATA * Yes, I know. I said I would transform all this into a CMPA #$31 * lookup table. I thought about it, and decided that it BEQ DTMF0 * worked the way it was, and the time it took to run CMPA #$30 * through all the checks was negligible compared to the BEQ DTMF1 * 50ms delay at the end. Also, code size wasn't much of CMPA #$32 * an issue here. I mean c'mon, Im using 6.5 of 12K that BEQ DTMF2 * has been alotted to me. For the sake of thoroughness, CMPA #$33 * I will put the values of each key here: BEQ DTMF3 * $30 - 0 $34 - 4 $38 - 8 $43 - C CMPA #$34 * $31 - 1 $35 - 5 $39 - 9 $44 - D BEQ DTMF4 * $32 - 2 $36 - 6 $41 - A $2A - * CMPA #$35 * $33 - 3 $37 - 7 $42 - B $23 - # BEQ DTMF5 CMPA #$36 * The purpose of all this is to convert the ASCII value BEQ DTMF6 * of the given key into a 4-bit number that the DTMF CMPA #$37 * IC and demultiplexer understand. The hex values above BEQ DTMF7 * are ASCII values. Here is the table for the 4-bit CMPA #$38 * numbers vs. the DTMF tone generated: BEQ DTMF8 * Tones Produced CMPA #$39 * |---|----------------| All Numbers are in decimal BEQ DTMF9 * N |208| 941 + 1336 = 0 | and all frequencies are in CMPA #$41 * u |000| 697 + 1209 = 1 | Hertz. BEQ DTMFA * m |016| 697 + 1336 = 2 | CMPA #$42 * b |032| 697 + 1477 = 3 | BEQ DTMFB * e |064| 770 + 1209 = 4 | CMPA #$43 * r |080| 770 + 1336 = 5 | BEQ DTMFC * |096| 770 + 1477 = 6 | CMPA #$44 * |128| 852 + 1209 = 7 | BEQ DTMFD * |144| 852 + 1336 = 8 | CMPA #$23 * |160| 852 + 1477 = 9 | BEQ DTMFE * |048| 697 + 1633 = A | CMPA #$2A * |112| 770 + 1633 = B | BEQ DTMFF * |176| 852 + 1633 = C | BRA ENDDTMF * |240| 941 + 1633 = D | DTMF0 LDAA #00 * |224| 941 + 1477 = # | BRA ENDRL * |192| 941 + 1209 = * | DTMF1 LDAA #208 * |---|----------------| BRA ENDRL * This program was written in an odd fashion. I love DTMF2 LDAA #16 * the JSR mnemonic. Sometimes, the program calls subs BRA ENDRL * from subs of subs (LOL). This is a bad thing if you DTMF3 LDAA #32 * don't give the CPU enough stack space. Every time a BRA ENDRL * JSR is executed, the CPU pushes the contents of the DTMF4 LDAA #64 * program counter (16 bits) onto the stack. So for BRA ENDRL * every JSR, 2 bytes of stack is used. Interrupts are DTMF5 LDAA #80 * even worse stack eaters. Upon an interrupt, these BRA ENDRL * things are pushed onto the stack in this order: DTMF6 LDAA #96 * Program Counter (2 bytes), Y (2 bytes), X (2 bytes), BRA ENDRL * A (1 byte), B (1 byte), CCR (1 byte). That is a total DTMF7 LDAA #128 * of 9 bytes. Not much you say? What happens if you BRA ENDRL * nest interrupts? Buh-bye stack... I loaded the stack DTMF8 LDAA #144 * with $60 (96 bytes). Here is an analysis of the event BRA ENDRL * with the highest stack usage: When the program gets DTMF9 LDAA #160 * an SCI interrupt while in the DELAY1 loop that has BRA ENDRL * been called from DISPCOM, which has been called by DTMFA LDAA #48 * LCDMAIN, which was called by where, which was called BRA ENDRL * by MAIN. Here is stack usage: DTMFB LDAA #112 * JSR WHERE (+2), JSR LCDMAIN (+2), JSR DISPDAT (+2), BRA ENDRL * JSR DELAY3 (+2), SCI interrupt (+9), JSR LCDMAIN (+2) DTMFC LDAA #176 * JSR DISPDAT (+2), JSR DELAY3 (+2). Thats a really BRA ENDRL * deep stack. Realisticly, I could have gotten away DTMFD LDAA #240 * with only 30 bytes of stack, but I was playing it BRA ENDRL * safe when I began writting. BTW, that is 23 bytes of DTMFE LDAA #224 * used stack. This will be a consideration if you nest BRA ENDRL * interrupts and subs. DTMFF LDAA #192 ENDRL LDX #20000 * 20000 is about a 50ms delay... BCLR $03,Y #DTMFON * Turns on DTMF unit... STAA $1004 * Gives it the number... JSR DELAY1 * Waits for duration of 20000... BSET $03,Y #DTMFON * Turns off DTMF unit... CLR $1004 * Clears PORTB... ENDDTMF JMP BACK2R * Resumes program... SCIIN LDAA $102E * This is the interrupt service routine. The address ANDA #$20 * for this ISR was stored at the interrupt vector that BEQ ENDINT * I wanted it assosiated with ($FFD6). $FFD6 is the SCI LDAA $102F * event vector. This Sub checks to see if something was ANDA #$7F * sent down the cable to the HC11. If not, it ReTurns STAA LCDDATA * from Interrupt. If yes, it removes the parity bit, JSR LCDMAIN * STores it at LCDDATA and then goes to the LCD routine ENDINT RTI * to display it on the screen. LCDMAIN LDAA LCDDATA * This is the display program. KEY.ASM can be thought CMPA #$0D * of as two seperate programs co-existing on the same BEQ CR * silicon: An LCD program, and everything else. When CMPA #08 * I put all the different programs together, the LCD BEQ LEFTA * program became one huge sub-routine. What the LCDMAIN CMPA #24 * loop does is determines the type of character it got, BEQ RIGHTA * and takes one of three actions. If the character is a CMPA #23 * valid ASCII print character, it prints it to the LCD. BEQ LEFTA * If its an ASCII control character, it moves the CMPA #21 * cursor accordingly. If its an arrow key, it moves the BEQ UPA * cursor accordingly. If its printable, DISPDAT is CMPA #22 * invoked. If it just moves the cursor around, or CLS's BEQ DOWNA * then the DISPCOM sub is used. LCDMAIN is a sub that JSR DISPDAT * calls other subs, which in turn, might call yet more BIGRTN RTS * subs. This can be a problem as was described earlier. CLS LDAA #1 * CLS is the CLear Screen sub. This is called every JSR DISPCOM * time we wish to clear the screen. All this does is LDAA #1 * LoaD Acc A with 1 (The command to clear and home the STAA SPACE * cursor), calls DISPCOM, and resets the posistion STAA LINEN * flags. Nothing exotic. LDAA #128 STAA PHYSS RTS CR BRA NEXTLC1 * CR = Carriage Return (The ENTER Key). * Calls NEXTLC1 and RTS's eventually. :) RIGHTA LDAA SPACE * These next four subs are boring. They are called when CMPA #20 * their respective arrow keys are hit. They just move BEQ NEXTLC1 * the cursor and make sure it doesn't go somewhere that INC SPACE * you can't see it. Its just another version of a INC PHYSS * DISPCOM call. I'm going to use this space for random LDAA PHYSS * commentary rather than describe each statement. But JSR DISPCOM * before I do, I will mention the one new instruction. RETURNE BRA BIGRTN * ABA: Add Acc A to B. There. I did it. I hope you're * happy now. LEFTA LDAA SPACE * I am currently listening to Yngwie Malmsteen as I CMPA #1 * write this. Pretty sweet guitar. The best shreader BEQ BACKUP * I think I've ever heard. Miracle Of Life is great, DEC SPACE * but then again so are Tocatta and Blitzkrieg. I think DEC PHYSS * my absolute favorite of his is Concerto Suite in Eb LDAA PHYSS * minor. He friggin' rox! . JSR DISPCOM * What to talk about here... I know! Hows about a more RETURNC BRA BIGRTN * complete description of the formula used to determine * delay times. When using an 8MHz crystal, each cycle UPA JSR PREVL * takes 500ns. Knowing this, and how many cycles each LDAA PHYSS * instruction takes, we arrive at this conclusion: SUBA #1 * Time = Cycles * 500 LDAB SPACE * Take my delay code: ABA * DELAY1 DEX STAA PHYSS * BNE DELAY1 JSR DISPCOM * RTS BRA BIGRTN * We are only concerned with the first two instructions * since those are the ones that will be repeating. DOWNA JSR NEXTL * DEX takes 3 cycles, as does BNE. This means that the LDAA PHYSS * loop takes 6 * 500ns, or 3us. This means that each LDAB SPACE * iteration of the loop takes an additional 3us. The SUBA #1 * formula is: Time = 6 * 500 * X ABA * X = Time / 500 / 6 STAA PHYSS * Note: Time is always in ns. The loop must be expanded JSR DISPCOM * to get delays longer than 196.608ms (X = 0). This can BRA BIGRTN * be easily done by using two DEX's and one INX per * loop. Or even more... NEXTLC JSR COMMON * If you have been following my code, then by now you BRA RETURND * have seen the messy and tangled collection of subs NEXTLC1 JSR COMMON * that direct the cursors movement about the screen. BRA RETURNE * Good luck tracing it. You'll need it. DISPDAT INC SPACE * These next two subs are neat little devices. This one INC PHYSS * does the work of moving the data to the LCD, latching STAA $1004 * it in, incrementing the flags, and keeping tabs on BSET $03,Y #LCDREG * the posistion of the cursor. The LCDREG bit of PORTC BSET $03,Y #LCDSTRB * is set to tell the LCD that its about to get data. BCLR $03,Y #LCDSTRB * Then the strobe is pulsed to latch the contents of LDAB SPACE * PORTB into the LCD. If the character that was just CMPB #21 * given to the LCD was in the last column, the program BEQ NEXTLC * calls the sequence of subs that switch lines. JSR DELAY3 RETURND RTS DISPCOM STAA $1004 * This does the same thing as DISPDAT - kind-of. The BCLR $03,Y #LCDREG * LCDREG bit is cleared (instead of set) to indicate a BSET $03,Y #LCDSTRB * command, rather than data, is on its way. Also the BCLR $03,Y #LCDSTRB * delay length must be longer for most commands. Also, JSR DELAY2 * there is no need to keep track of the cursor since RTS * its what is being moved around. BACKUP JSR PREVL * The code starts getting really wiggle right about CLR SPACE * here. CLR = CLeaR. This sub is called when a command LDAA #20 * is issued to move the cursor beyond the left-hand STAA SPACE * boundry of the line. Gets the PHYSical address from LDAA PHYSS * PREVL, and adds 19 to it. This causes the cursor to ADDA #19 * show up at the end of the previous line. STAA PHYSS JSR DELAY2 JSR DISPCOM BRA RETURNC NEXTL INC LINEN * Increments LINEN then CoMPares it against the given LDAA LINEN * values to determine what address it should go fetch. CMPA #2 * Keep in mind that the variables SPACE and LINEN are BEQ C2 * entierly relitive. They are here only to keep track CMPA #3 * of the cursor. The only variable whose contents the BEQ C3 * LCD ever sees are those of PHYSS. In a similar way, CMPA #4 * PHYSS is of no use to the program. Its just a place BEQ C4 * to keep the PHYSical Space of the cursor so it can be CMPA #5 * manipulated and sent to the LCD. No conditions are BEQ C1 * dependant upon it. NEXTLR RTS * ReTurn from Sub-routine. PREVL DEC LINEN * This does the same thing as NEXTL does, only in LDAA LINEN * reverse. No new trix here. DEC is the opposite of CMPA #0 * INC. DECrement and INCrement, ya know? BEQ C4P CMPA #1 BEQ C1P CMPA #2 BEQ C2P CMPA #3 BEQ C3P PREVLR RTS C2 LDAA #192 * Look-up routines for PHYSS. Depending on which line STAA PHYSS * is being switched to, these load PHYSS with the BRA NEXTLR * appropriate address. C3 LDAA #148 * 192 is the start of line 2 STAA PHYSS * 148 is the start of line 3 BRA NEXTLR * 212 is the start of line 4 C4 LDAA #212 * 128 is the start of line 1 STAA PHYSS * Lower in the program, you will see STAB instead of BRA NEXTLR * STAA, and LDAB instead of LDAA. Don't freak, its just C1 LDAA #128 * accumulator B. STAA PHYSS LDAB #1 * These two lines set reset LINEN to 1 when the cursor STAB LINEN * reaches the end of the final line. BRA NEXTLR C2P LDAA #192 * The same function as above, just back-ass-wards. STAA PHYSS BRA PREVLR C3P LDAA #148 STAA PHYSS BRA PREVLR C4P LDAA #212 STAA PHYSS LDAA #4 STAA LINEN BRA PREVLR C1P LDAA #128 STAA PHYSS BRA PREVLR IBITON SEI * This sets the I-bit in order to mask all interrupts. BSET $03,Y #LEDSTAT * It also lights up the busy LED I put on the front RTS * panel. SEI = SEt I. IBITOFF CLI * The opposite of the above sub. Re-enables interrupts BCLR $03,Y #LEDSTAT * and turns off the busy LED. RTS * CLI = CLear I. COMMON JSR NEXTL * This sub is just a space saver. Only called when the LDAB #1 * cursor is moved beyond the right hand boundry of the STAB SPACE * current line. Essentially the same as BACKUP, only LDAA PHYSS * this time its going forward. Because of this, we JSR DELAY2 * don't need the code to move the cursor to the end of JSR DISPCOM * the line. Its already where its supposed to be. Just RTS * remember to reset SPACE. HELPME JSR IBITON * This is my little help program. LDX #MESS2 * When the user presses F1, it usually because they JSR MESCOM * don't know what the hell else they're supposed to do, CMPA #$76 * so I thought it would be logical (and useful) to put BEQ ENDHELP * this function in here. First, interrupts are masked. LDX #MESS3 * I did this so that the SCI chatter wouldn't overwrite JSR MESCOM * what the user is trying to read. That might be kinda CMPA #$76 * obnoxious. X is loaded with the location of the text BEQ ENDHELP * to be displayed. Then we Jump to Sub-Routine MESCOM. LDX #MESS4 * If the key that was hit was escape, then the sub JSR MESCOM * terminates and the program resumes. Thats about all CMPA #$76 * there is to that sub. BEQ ENDHELP LDX #MESS5 JSR MESCOM CMPA #$76 BEQ ENDHELP LDX #MESS6 JSR MESCOM CMPA #$76 BEQ ENDHELP LDX #MESS7 JSR MESCOM CMPA #$76 BEQ ENDHELP LDX #MESS8 JSR MESCOM CMPA #$76 BEQ ENDHELP LDX #MESS9 JSR MESCOM CMPA #$76 BEQ ENDHELP ENDHELP JSR IBITOFF * Re-enable interrupts. JSR CLS * Clear the screen. JMP MAIN * JuMP back to MAIN. MESCOM JSR DISPMES * Wait! One more jump!!! MESCOM1 JSR RECEIVE * The three RECIEVE calls are to compensate for the JSR RECEIVE * other characters that are sent when a key is hit and JSR RECEIVE * released. Scancode, $F0, scancode. JSR IBITON LDAA DATA * Get the key that was hit. CMPA #$76 * Is it escape? If yes, BRAnch to MESCOM2. BEQ MESCOM2 CMPA #$29 * Is it Space? If yes, BRAnch to MESCOM2. BEQ MESCOM2 BRA MESCOM1 * Otherwise, loop again. MESCOM2 RTS DISPMES LDAA #128 * This is for later... PSHX * PuSH X onto stack JSR DISPCOM * Here is what the first line was for. It causes LDX #900 * DISPCOM to change the cursor posistion to the JSR DELAY1 * beginning of the LCD. PULX * PULl X off of stack. LDAA #1 * Resets SPACE, PHYSS, and LINEN. STAA SPACE STAA LINEN LDAA #128 STAA PHYSS DISPME LDAA $00,X * Here is the main message loop. Acc A is loaded with CMPA #64 * whatever is at address X. I designated ASCII 64(@) to BEQ ENDMESS * be my end character. When the program sees this, it PSHX * exits its loop. X is pushed onto the stack because JSR DISPDAT * DISPDAT includes a call to DELAY1. If I didn't do PULX * this, the program would get stuck in an infinet loop. INX * INX = INcrement X. BRA DISPME ENDMESS CLRA * CLear A RTS MESS1 FCC ' LCD/KYBD Interface ' FCC ' Coded By JSpark ' FCC ' 9-14-00 ' FCC ' V2.7 @' MESS2 FCC 'Interface Help: ' FCC 'Hit ESC at any time ' FCC 'to leave this mode. ' FCC '-==- @' MESS3 FCC 'Function Keys: ' FCC 'F1: Help Screen ' FCC 'F2: Clear Screen CLS' FCC '-==- @' MESS4 FCC 'F3: Sends an echo ' FCC ' command to the ' FCC ' keyboard. ' FCC '-==- @' MESS5 FCC 'NUM LOCK enables or ' FCC 'disables echo to the' FCC 'COM port. ' FCC '-==- @' MESS6 FCC 'SCROLL LOCK enables ' FCC 'or disables DTMF ' FCC 'mode. ' FCC '-==- @' MESS7 FCC 'CAPS LOCK functions ' FCC 'more or less normaly' FCC 'as on a x86 machine.' FCC '-==- @' MESS8 FCC 'When DTMF mode is on' FCC 'no key strokes will ' FCC 'be echoed to LCD/SCI' FCC '-==- @' MESS9 FCC 'This is the end of ' FCC 'the help routine, so' FCC 'go away. >:-P ' FCC '-==- @' MESS10 FCC 'Um... Like, there's ' FCC 'no keyboard attached' FCC 'or its not talking. ' FCC 'Fix it and reset. @' MESS11 FCC ' Echo command ($EE) ' FCC ' sent and returned. ' FCC ' The keyboard is ' FCC ' working normally. @' NOSHIFT FCB $00 ; 00 FCB $00 ; 01 F9 FCB $00 ; 02 FCB $00 ; 03 F5 FCB $00 ; 04 F3 FCB $00 ; 05 F1 FCB $00 ; 06 F2 FCB $00 ; 07 F12 FCB $00 ; 08 FCB $00 ; 09 F10 FCB $00 ; 0A F8 FCB $00 ; 0B F6 FCB $00 ; 0C F4 FCB $09 ; 0D TAB FCC '`' ; 0E ` or ~ FCB $00 ; 0F FCB $00 ; 10 FCB $00 ; 11 Left ALT FCB $00 ; 12 Left SHIFT FCB $00 ; 13 FCB $00 ; 14 Left Ctrl FCC 'q' ; 15 Q FCC '1' ; 16 1 or ! FCB $00 ; 17 FCB $00 ; 18 FCB $00 ; 19 FCC 'z' ; 1A Z FCC 's' ; 1B S FCC 'a' ; 1C A FCC 'w' ; 1D W FCC '2' ; 1E 2 or @ FCB $00 ; 1F FCB $00 ; 20 FCC 'c' ; 21 C FCC 'x' ; 22 X FCC 'd' ; 23 D FCC 'e' ; 24 E FCC '4' ; 25 4 or $ FCC '3' ; 26 3 or # FCB $00 ; 27 FCB $00 ; 28 FCC ' ' ; 29 Space FCC 'v' ; 2A V FCC 'f' ; 2B F FCC 't' ; 2C T FCC 'r' ; 2D R FCC '5' ; 2E 5 or % FCB $00 ; 2F FCB $00 ; 30 FCC 'n' ; 31 N FCC 'b' ; 32 B FCC 'h' ; 33 H FCC 'g' ; 34 G FCC 'y' ; 35 Y FCC '6' ; 36 6 or ^ FCB $00 ; 37 FCB $00 ; 38 FCB $00 ; 39 FCC 'm' ; 3A M FCC 'j' ; 3B J FCC 'u' ; 3C U FCC '7' ; 3D 7 or & FCC '8' ; 3E 8 or * FCB $00 ; 3F FCB $00 ; 40 FCC ',' ; 41 , or < FCC 'k' ; 42 K FCC 'i' ; 43 I FCC 'o' ; 44 O FCC '0' ; 45 0 or ) FCC '9' ; 46 9 or ( FCB $00 ; 47 FCB $00 ; 48 FCC '.' ; 49 . or > FCC '/' ; 4A / or ? FCC 'l' ; 4B L FCC ';' ; 4C ; or : FCC 'p' ; 4D P FCC '-' ; 4E - or _ FCB $00 ; 4F FCB $00 ; 50 FCB $00 ; 51 FCB $27 ; 52 ' or " FCB $00 ; 53 FCC '[' ; 54 [ or { FCC '=' ; 55 = OR + FCB $00 ; 56 FCB $00 ; 57 FCB $00 ; 58 Caps Lock FCB $00 ; 59 Right Shift FCB $0D ; 5A Enter FCC ']' ; 5B ] or } FCB $00 ; 5C FCC '\' ; 5D \ or | FCB $00 ; 5E FCB $00 ; 5F FCB $00 ; 60 FCB $00 ; 61 FCB $00 ; 62 FCB $00 ; 63 FCB $00 ; 64 FCB $00 ; 65 FCB $08 ; 66 Backspace FCB $00 ; 67 FCB $00 ; 68 FCC '1' ; 69 NUM - 1 or END FCB $00 ; 6A FCC '4' ; 6B NUM - 4 or LEFT FCC '7' ; 6C NUM - 7 or HOME FCB $00 ; 6D FCB $00 ; 6E FCB $00 ; 6F FCC '0' ; 70 NUM - 0 or INS FCC '.' ; 71 NUM - . or DEL FCC '2' ; 72 NUM - 2 or DOWN FCC '5' ; 73 NUM - 5 FCC '6' ; 74 NUM - 6 or RIGHT FCC '8' ; 75 NUM - 8 or UP FCB $1B ; 76 ESC FCB $00 ; 77 NUM LOCK FCB $00 ; 78 F11 FCC '+' ; 79 NUM - + (Plus) FCC '3' ; 7A NUM 3 or PAGE DOWN FCC '-' ; 7B NUM - - (Minus) FCC '*' ; 7C NUM - * FCC '9' ; 7D NUM - 9 or PAGE UP FCB $00 ; 7E SCROLL LOCK FCB $00 ; 7F FCB $00 ; 80 FCB $00 ; 81 FCB $00 ; 82 FCB $00 ; 83 F7 FCB $00 ; 84 FCB $00 ; 85 FCB $00 ; 86 FCB $00 ; 87 FCB $00 ; 88 FCB $00 ; 89 FCB $00 ; 8A FCB $00 ; 8B FCB $00 ; 8C FCB $00 ; 8D FCB $00 ; 8E FCB $00 ; 8F SHIFT FCB $00 ; 00 FCB $00 ; 01 F9 FCB $00 ; 02 FCB $05 ; 03 F5 FCB $03 ; 04 F3 FCB $01 ; 05 F1 FCB $02 ; 06 F2 FCB $00 ; 07 F12 FCB $00 ; 08 FCB $00 ; 09 F10 FCB $00 ; 0A F8 FCB $06 ; 0B F6 FCB $04 ; 0C F4 FCB $09 ; 0D TAB FCC '~' ; 0E ` or ~ FCB $00 ; 0F FCB $00 ; 10 FCB $00 ; 11 Left ALT FCB $00 ; 12 Left SHIFT FCB $00 ; 13 FCB $00 ; 14 Left Ctrl FCC 'Q' ; 15 Q FCC '!' ; 16 1 or ! FCB $00 ; 17 FCB $00 ; 18 FCB $00 ; 19 FCC 'Z' ; 1A Z FCC 'S' ; 1B S FCC 'A' ; 1C A FCC 'W' ; 1D W FCC '@' ; 1E 2 or @ FCB $00 ; 1F FCB $00 ; 20 FCC 'C' ; 21 C FCC 'X' ; 22 X FCC 'D' ; 23 D FCC 'E' ; 24 E FCC '$' ; 25 4 or $ FCC '#' ; 26 3 or # FCB $00 ; 27 FCB $00 ; 28 FCC ' ' ; 29 Space FCC 'V' ; 2A V FCC 'F' ; 2B F FCC 'T' ; 2C T FCC 'R' ; 2D R FCC '%' ; 2E 5 or % FCB $00 ; 2F FCB $00 ; 30 FCC 'N' ; 31 N FCC 'B' ; 32 B FCC 'H' ; 33 H FCC 'G' ; 34 G FCC 'Y' ; 35 Y FCC '^' ; 36 6 or ^ FCB $00 ; 37 FCB $00 ; 38 FCB $00 ; 39 FCC 'M' ; 3A M FCC 'J' ; 3B J FCC 'U' ; 3C U FCC '&' ; 3D 7 or & FCC '*' ; 3E 8 or * FCB $00 ; 3F FCB $00 ; 40 FCC '<' ; 41 , or < FCC 'K' ; 42 K FCC 'I' ; 43 I FCC 'O' ; 44 O FCC ')' ; 45 0 or ) FCC '(' ; 46 9 or ( FCB $00 ; 47 FCB $00 ; 48 FCC '>' ; 49 > or . FCC '?' ; 4A / or ? FCC 'L' ; 4B L FCC ':' ; 4C ; or : FCC 'P' ; 4D P FCC '_' ; 4E - or _ FCB $00 ; 4F FCB $00 ; 50 FCB $00 ; 51 FCB $22 ; 52 ' or " FCB $00 ; 53 FCC '{' ; 54 [ or { FCC '+' ; 55 = OR + FCB $00 ; 56 FCB $00 ; 57 FCB $00 ; 58 Caps Lock FCB $00 ; 59 Right Shift FCB $0D ; 5A Enter FCC '}' ; 5B ] or } FCB $00 ; 5C FCC '|' ; 5D \ or | FCB $00 ; 5E FCB $00 ; 5F FCB $00 ; 60 FCB $00 ; 61 FCB $00 ; 62 FCB $00 ; 63 FCB $00 ; 64 FCB $00 ; 65 FCB $08 ; 66 Backspace FCB $00 ; 67 FCB $00 ; 68 FCC '1' ; 69 NUM - 1 or END FCB $00 ; 6A FCC '4' ; 6B NUM - 4 or LEFT FCC '7' ; 6C NUM - 7 or HOME FCB $00 ; 6D FCB $00 ; 6E FCB $00 ; 6F FCC '0' ; 70 NUM - 0 or INS FCC '.' ; 71 NUM - . or DEL FCC '2' ; 72 NUM - 2 or DOWN FCC '5' ; 73 NUM - 5 FCC '6' ; 74 NUM - 6 or RIGHT FCC '8' ; 75 NUM - 8 or UP FCB $1B ; 76 ESC FCB $00 ; 77 NUM LOCK FCB $00 ; 78 F11 FCC '+' ; 79 NUM - + (Plus) FCC '3' ; 7A NUM 3 or PAGE DOWN FCC '-' ; 7B NUM - - (Minus) FCC '*' ; 7C NUM - * FCC '9' ; 7D NUM - 9 or PAGE UP FCB $00 ; 7E SCROLL LOCK FCB $00 ; 7F FCB $00 ; 80 FCB $00 ; 81 FCB $00 ; 82 FCB $00 ; 83 F7 FCB $00 ; 84 FCB $00 ; 85 FCB $00 ; 86 FCB $00 ; 87 FCB $00 ; 88 FCB $00 ; 89 FCB $00 ; 8A FCB $00 ; 8B FCB $00 ; 8C FCB $00 ; 8D FCB $00 ; 8E FCB $00 ; 8F