************************************************************************** * * Title: Hardware Controller * * Objective: CMPEN 472 Homework 5 * * Revision: V1.0 * * Date: Feb. 21, 2025 * * Programmer: Jacob McDonnell * * Company: The Pennsylvania State University * Department of Computer Science and Engineering * * Algorithm: Simple Serial I/O, Parallel I/O use, time delay-loop, and PWM control * * Register Use: A & B to control LEDS initially, Light Level, current byte, etc * X & Y to hold the counter in the loop and address of strings and length of string. * * Memory Use: RAM Locations from $3000 for data, * RAM Locations from $3100 for program * * Input: Parameters hard-coded in the program - PORTB * Serial Port for User Input * * Output: LED 1 at PORTB bit 4 * LED 2 at PORTB bit 5 * LED 3 at PORTB bit 6 * LED 4 at PORTB bit 7 * Serial Port for String Output * * Observation: This program will respond to user input to turn on and off LEDs 1, 2, & 3, * Dim LED 4 from 100% to 0%, Dim LED 4 from 0% to 100%, and echo user input * back to the terminal in Type Writer Mode. * * Note: ON CSM-12C128 board, * Switch 1 is at PORTB bit 0, and * LED 4 is at PORTB bit 7. * * Comments: This program is developed and simulated using CodeWarrior * development software and targeted for Axion * Manufacturing's CSM-12C128 board running at 24MHz. * ************************************************************************** * Parameter Declearation Section * * Export Symbols xdef pgstart ; export 'pgstart' symbol absentry pgstart ; for assembly entry point * Symbols and Macros PORTA equ $0000 ; i/o port A addresses DDRA equ $0002 ; data direction register for PORTA PORTB equ $0001 ; i/o port B addresses DDRB equ $0003 ; data direction register for PORTB SCIBDH equ $00C8 ; Serial port (SCI) Baud Register H SCIBDL equ $00C9 ; Serial port (SCI) Baud Register L SCICR2 equ $00CB ; Serial port (SCI) Control Register 2 SCISR1 equ $00CC ; Serial port (SCI) Status Register 1 SCIDRL equ $00CF ; Serial port (SCI) Data Register CR equ $0d ; carriage return, ASCII 'Return' key LF equ $0a ; line feed, ASCII 'next line' character NULL equ $00 ; NULL Terminator character ************************************************************************** * Data Section: address used [ $3000 to $30FF ] RAM Memory * org $3000 ; Reserved RAM memory starting address ; for Data for CMPEN 472 class Counter dc.w $0036 ; X register count number for time Delay ; loop for 10 useconds ; The work to calculate this number is in ; the comments for the delay10usec subroutine. LEVEL dc.b $0005 ; Light Level that the LED should be str ds.b $000F ; Array of 16 bytes to read a string dc.b NULL lenStr dc.w $000F ; Length of str array * There is a second Data Section at the end of the file. * ************************************************************************** * Program Section: address used [ $3100 to $3FFF ] RAM Memory * org $3100 ; Program start address, in RAM pgstart lds #$3100 ; initialize the stack pointer ldaa #%11110001 ; LED 1,2,3,4 at PORTB bit 4,5,6,7 staa DDRB ; set PORTB bit 4,5,6,7 as output ldaa #$0C ; Enable SCI port Tx and Rx units staa SCICR2 ; disable SCI interrupts ldd #$0001 ; Set SCI Baud Register = $0001 => 1.5M baud at 24MHz (for simulation) std SCIBDH ; SCI port baud rate change mainLoop ldx #msg ; Load the address of msg into X jsr WriteString ; Jump to WriteString to output message on serial ldx #str ; Load the address of str into X ldy lenStr ; Load length of str into Y jsr Zeros ; jump to Zeros to zero out str ldx #str ; Reload address of str into X ldy lenStr ; Load the length of the string into Y jsr ReadString ; Jump to ReadString to read user input into str ldx #str ; Reload Address of str into X jsr CheckInput ; Jump to CheckInput to handle user input bra mainLoop ; Loop back to mainLoop always TypeWriter ldx #twMsg ; Load Type Writer welcome message address jsr WriteString ; Jump to WriteString to write message to serial twReadLoop jsr getchar ; Read Character from Serial beq twReadLoop ; While Character == 0, branch to twReadLoop jsr putchar ; Write Character back to terminal staa PORTB ; Write Character to PORTB bra twReadLoop ; Branch always to twReadLoop ************************************************************************** * Subroutine Section: address used [ $3100 to $3FFF ] RAM Memory * ;************************************************************************* ; CheckInput subroutine ; ; This subroutine will check the input string and match the option. ; ; Input: Address of null terminated string in X. ; Output: No Output, Control flow changed to proper subroutine. ; Registers in use: X for the address of the string, A & B to read characters from ; from the string. ; Memory locations in use: Memory Address for serial line, address of the string ; ; Comments: This subroutine will not return a value, it will jump to the proper subroutine ; based on the input given. ; CheckInput psha ; Save A to the Stack pshb ; Save B to the Stack ldab 1,x+ ; Load Character from string in X to B cmpb #'Q' ; Compare Character in A to 'Q' beq quitCheck ; If B == 'Q', branch to quitCheck cmpb #'L' ; Compare Character in A to 'L' bne FCheck ; If A != 'L', branch to FCheck ldaa 1,x+ ; Load Next character from string into A ldab 1,x+ ; Load Next character from string into B bne none ; If B != 0, then branch to none (unknown string) cmpa #'4' ; Compare Character in A to '4' beq L4Check ; If A == '4', branch to L4Check suba #'0' ; Subtract character '0' from A ; This allows the number in A to be the LED number (if correct) ble none ; If A < '0', branch to none (unknown string) cmpa #4 ; Compare A to 4 bhs none ; Branch to None if A > 3 jsr TurnOnLED ; Jump to TurnOnLED bra doneCheck ; Branch always to doneCheck L4Check jsr LowToHigh ; Jump to LowToHigh subroutine bra doneCheck ; Branch always to doneCheck FCheck cmpb #'F' ; Compare Character in A to 'L' bne none ; If A != 'F', branch to none (unknown string) ldaa 1,x+ ; Load Next character from string into A ldab 1,x+ ; Load Next character from string into B bne none ; If B != 0, then branch to none (unknown string) cmpa #'4' ; Compare Character in A to '4' beq F4Check ; If A == '4', branch to F4Check suba #'0' ; Subtract character '0' from A ble none ; If A < '0', branch to none (unknown string) cmpa #4 ; Compare A to 4 bhs none ; Branch to None if A > 3 jsr TurnOffLED ; Jump to TurnOffLED bra doneCheck ; Branch always to doneCheck F4Check jsr HighToLow ; Jump to HighToLow subroutine bra doneCheck ; Branch always to doneCheck quitCheck ldaa 1,x+ ; Load next character from string in X to A cmpa #'U' ; Compare A to 'U' bne none ; If A != 'U', branch to none (unknown string) ldaa 1,x+ ; Load next character from string in X to A cmpa #'I' ; Compare A to 'I' bne none ; If A != 'I', branch to none (unknown string) ldaa 1,x+ ; Load next character from string in X to A cmpa #'T' ; Compare A to 'T' bne none ; If A != 'T', branch to none (unknown string) ldaa 1,x+ ; Load next character from string in X to A bne none ; If A != 0, branch to none (unknown string) jmp TypeWriter ; Jump to TypeWriter portion of main routine. ; NOTE: Jumping to TypeWriter will not return ; as it is an infinite loop. none ldx #unknown ; Load address of uknown command string into X jsr WriteString ; Write unknown command string to serial doneCheck pulb ; Restore B from the stack pula ; Restore A from the stack rts ; Return to caller ;************************************************************************* ; Zeros subroutine ; ; This subroutine will write zeros to every byte in a given array. ; ; Input: Address of an array in X and its length in Y ; Output: Zeros in every byte of an array. ; Registers in use: X for the address of the array, Y for the length, and A for 0 ; Memory locations in use: Memory Address of the array ; ; Comments: This subroutine requires serial to be setup and putchar subroutine. ; Zeros psha ; Save A to the Stack clra ; Clear A zerosLoop staa 1,x+ ; Load A into byte at X dbne y,zerosLoop ; Decrement Y and loop if Y != 0 pula ; Restore A from the stack rts ; Return to caller ;************************************************************************* ; WriteString subroutine ; ; This subroutine will write a given null terminated string to the serial. ; ; Input: Address of null terminated string in X ; Output: Null terminated string written to serial ; Registers in use: X for the address of the string and A for the current byte ; Memory locations in use: Memory Address for serial line, address of the string ; ; Comments: This subroutine requires serial to be setup and putchar subroutine. ; WriteString psha ; Save A to the stack writeLoop ldaa 1,x+ ; Load the byte at addr in X, then add 1 beq doneWrite ; if A == 0, branch to doneWrite jsr putchar ; Jump to putchar to write byte to serial bra writeLoop ; branch always to writeLoop doneWrite pula ; restore A from the stack rts ; return to caller ;************************************************************************* ; ReadString subroutine ; ; This subroutine will read a string from the serial line to a given address. ; ; Input: Address of an array in X ; Output: Null terminated string in the given array ; Registers in use: X for the address of the string Y for the length of the string, ; and A for the current byte ; Memory locations in use: Memory Address for serial line, address of the string ; ; Comments: This subroutine requires serial to be setup and getchar subroutine. ; ReadString psha ; Save accumulator A to the stack pshy ; Save Y to the stack pshx ; Save X to the stack readLoop jsr getchar ; Jump to putchar to write byte to serial beq readLoop ; While A == 0, loop cmpa #CR ; If A == CR, exit loop beq doneRead ; Branch to doneRead if A == CR staa 1,x+ ; Save the byte to the addr in X, then add 1 jsr putchar ; Write Character back to the terminal dey ; Decrement Y by 1 beq doneRead ; If Y == 0, no more room, stop reading bra readLoop ; branch always to readLoop doneRead ldaa #LF ; Load Line Feed into A jsr putchar ; Write LF to terminal pulx ; Restore X from the stack pulY ; Restore Y from the stack pula ; restore A from the stack rts ; return to caller ;************************************************************************* ; TurnOnLED subroutine ; ; This subroutine will dim turn on a specified LED ; ; Input: LED number 1 to 3, in accumulator A ; Output: Specified LED turned on ; Registers in use: A accumulator to specify the LED ; Memory locations in use: PORTB memory location associated with PORTB on the chip ; ; Comments: This subroutine requires PORTB to be setup ; TurnOnLED cmpa #1 ; Compare A to 1 bne onCheckTwo ; If A != 1, check next number bset PORTB,%00010000 ; Turn On LED 1 bra onDone ; Jump to onDone onCheckTwo cmpa #2 ; Compare A to 2 bne onCheckThr ; If A != 2, check next number bset PORTB,%00100000 ; Turn On LED 2 bra onDone ; Jump to onDone onCheckThr bset PORTB,%01000000 ; Turn On LED 3 onDone rts ; Return to caller ;************************************************************************* ; TurnOffLED subroutine ; ; This subroutine will dim turn of a specified LED ; ; Input: LED number 1 to 3, in accumulator A ; Output: Specified LED turned of ; Registers in use: A accumulator to specify the LED ; Memory locations in use: PORTB memory location associated with PORTB on the chip ; ; Comments: This subroutine requires PORTB to be setup ; TurnOffLED cmpa #1 ; Compare A to 1 bne ofCheckTwo ; If A != 1, check next number bclr PORTB,%00010000 ; Turn Off LED 1 bra ofDone ; Jump to onDone ofCheckTwo cmpa #2 ; Compare A to 2 bne ofCheckThr ; If A != 2, check next number bclr PORTB,%00100000 ; Turn Off LED 2 bra ofDone ; Jump to onDone ofCheckThr bclr PORTB,%01000000 ; Turn Off LED 3 ofDone rts ; Return to caller ;************************************************************************* ; HighToLow subroutine ; ; This subroutine will dim LED4 from 100% to 0% in 400ms ; ; Input: No Input, all parameters are hard coded ; Output: LED4 dimmed from 100% to 0% in 400ms, wasted cycles ; Registers in use: A accumulator to control the light level of the LED ; Memory locations in use: PORTB memory location associated with PORTB on the chip ; ; Comments: This subroutine requires dimmer subroutine ; HighToLow psha ; Save accumulator A to the stack ldaa #100 ; load 100 into accumulator A decrease tbeq A,doneDec ; Test if A == 0, skip loop if so staa LEVEL ; Save A to LEVEL jsr dimmer ; jump to dimmer subroutine jsr dimmer ; jump to dimmer subroutine jsr dimmer ; jump to dimmer subroutine jsr dimmer ; jump to dimmer subroutine deca ; decrement accumulator A by 1 bra decrease ; loop to decrease always doneDec pula ; Restore A from the stack rts ; Return to caller ;************************************************************************* ; LowToHigh subroutine ; ; This subroutine will dim LED4 from 0% to 100% in 400ms ; ; Input: No Input, all parameters are hard coded ; Output: LED4 dimmed from 0% to 100% in 400ms, wasted cycles ; Registers in use: A accumulator to control the light level of the LED ; Memory locations in use: PORTB memory location associated with PORTB on the chip ; LEVEL memory location for byte of light level. ; ; Comments: This subroutine requires dimmer subroutine ; LowToHigh psha ; Save accumulator A to the stack ldaa #0 ; load 100 into accumulator A increase cmpa #100 ; Compare A to 100 beq doneInc ; Test if A == 100, jump to doneInc staa LEVEL ; Save A to LEVEL jsr dimmer ; jump to dimmer subroutine jsr dimmer ; jump to dimmer subroutine jsr dimmer ; jump to dimmer subroutine jsr dimmer ; jump to dimmer subroutine inca ; increment accumulator A by 1 bra increase ; loop to increase always doneInc pula ; Restore A from the stack rts ; Return to caller ;************************************************************************* ; dimmer subroutine ; ; This subroutine will dim LED4 to a given level ; ; Input: Two 1 byte counters, ONN and OFF, for how many times ; LED4 should be on and off for. ; Output: LED4 dimmed to a given level, wasted cycles ; Registers in use: A accumulator to counter number of times looped ; Memory locations in use: PORTB memory location associated with PORTB on the chip ; LEVEL memory location for byte of light level. ; ; Comments: This subroutine requires delay10usec subroutine ; dimmer bset PORTB,%10000000 ; Turn LED4 on psha ; Save A to the stack ldaa LEVEL ; Load the light level into accumulator A onDelay tbeq A, skipToOff ; Test if A == 0, skip loop if so jsr delay10usec ; delay for 10 microseconds deca ; decrement accumulator A by 1 bra onDelay ; jump back to onDelay always skipToOff bclr PORTB,%10000000 ; Turn off LED4 ldaa #100 ; load 100 into accumulator A suba LEVEL ; Subtract LEVEL to get off count offDelay tbeq A,doneLoop ; Test if A == 0, skip loop if so jsr delay10usec ; delay 10 microseconds deca ; decrement accumulator A by 1 bra offDelay ; jump back to offDelay always doneLoop pula ; restore A from the stack rts ; return to caller ;************************************************************************* ; delay10usec subroutine ; ; This subroutine causes a 10 usec. delay ; ; Input: a 16bit count number in 'Counter' ; Output: time delay, cpu cycle wasted ; Registers in use: X register, as counter ; Memory locations in use: a 16bit input number at 'Counter' ; ; Comments: Code relies on counter being $39 to be exactly 10 usec work is below ; Given: freq = 24MHz = 24000000 sec = 10 usec = 0.00001 ; freq = cycles / seconds ; ; cycles = freq * seconds = 24000000Hz * 0.00001 = 240 ; ; This sub routine is 12 + 4 * 'Counter' cycles long, solving for 'Counter' ; the result is found to be 57. ; delay10usec pshx ; Save register x to the stack ldx Counter ; load counter into register x innerLoop dex ; decrement register x by 1 bne innerLoop ; loop while register x is not 0 pulx ; restore register x from the stack nop ; extra nop to make exactly 10 usec rts ; return to caller ;************************************************************************* ; putchar subroutine ; ; This subroutine writes a single byte to a serial line ; ; Input: A single ASCII byte in accumulator A ; Output: Sends one character to SCI port ; Registers in use: Accumulator A with input byte ; Memory locations in use: SCISR1 and SCIDRL status and data registers ; putchar brclr SCISR1,#%10000000,putchar ; wait for transmit buffer empty staa SCIDRL ; send a character rts ; Return to caller ;************************************************************************* ; putchar subroutine ; ; This subroutine reads one byte from the SCI port ; ; Input: One byte from the SCI port ; Output: One byte in accumulator A ; Registers in use: Accumulator A for output byte ; Memory locations in use: SCISR1 and SCIDRL status and data registers ; getchar brclr SCISR1,#%00100000,getchar7 ; If no input on SCI port, return 0 ldaa SCIDRL ; Read one byte from SCI port into A rts ; Return to caller getchar7 clra ; Set A to 0 rts ; Return to caller ************************************************************************** * Data Section: address used [ $3100 to $3FFF ] RAM Memory * ; unknown: string to warn the user of unknown output unknown dc.b 'Error: Unknown Command',CR,LF,NULL ; twMsg: welcome message when type writer loads twMsg dc.b 'Welcome to Type Writer, you may type below.',CR,LF dc.b 'Restart to enter main menu again.',CR,LF,NULL ; msg: this is the main option menu string msg dc.b 'L1: Turn on LED1',CR,LF dc.b 'F1: Turn off LED1',CR,LF dc.b 'L2: Turn on LED2',CR,LF dc.b 'F2: Turn off LED2',CR,LF dc.b 'L3: Turn on LED3',CR,LF dc.b 'F3: Turn off LED3',CR,LF dc.b 'L4: LED4 goes from 0% light level to 100% light level in 0.4 seconds',CR,LF dc.b 'F4: LED4 goes from 100% light level to 0% light level in 0.4 seconds',CR,LF dc.b 'QUIT: Quit menu program, run Type writer program.',CR,LF,NULL