************************************************************************** * * Title: Signal Generator * * Objective: CMPEN 472 Homework 10 * * Revision: V1.0 * * Date: Apr. 11, 2025 * * Programmer: Jacob McDonnell * * Company: The Pennsylvania State University * Department of Computer Science and Engineering * * Algorithm: Simple Serial I/O, Real Time Interrupts for Time Tracking, and * output compare timer for generating functions. * * Register Use: A & B to current byte, etc, * X & Y holds address of strings and length of string, * D to hold data for printing, reading, and updating time. * * Memory Use: RAM Locations from $3000 for data, * RAM Locations from $3100 for program * * Input: Serial Port for User Input * * Output: Serial Port for String Output * * Observation: The HC12 will output the time and a command prompt every second. * The user can input commands and the program will output a response * based on the input. * * Note: ON CSM-12C128 board, * * 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 CRGFLG EQU $0037 ; Clock and Reset Generator Flags CRGINT EQU $0038 ; Clock and Reset Generator Interrupts RTICTL EQU $003B ; Real Time Interrupt Control TIOS EQU $0040 ; Timer Input Capture (IC) or Output Compare (OC) select TIE EQU $004C ; Timer interrupt enable register TCNTH EQU $0044 ; Timer free runing main counter TSCR1 EQU $0046 ; Timer system control 1 TSCR2 EQU $004D ; Timer system control 2 TFLG1 EQU $004E ; Timer interrupt flag 1 TC5H EQU $005A ; Timer channel 5 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 buffer ds.b $0010 ; Array of 16 bytes to read a string dc.b NULL ; NULL terminated lenBuf dc.w $0010 ; Length of buffer array buffer2 ds.b $0010 ; Array of 16 bytes for reading and reversal dc.b NULL ; NULL terminated lenBuf2 dc.w $0010 ; length of buffer2 hours dc.w $0000 ; Buffer to hold the hours of the time minutes dc.w $0000 ; Buffer to hold the minutes of the time seconds dc.w $0000 ; Buffer to hold the seconds of the time counter dc.w $0000 ; Counter for RTI ISR for 1 second numBuf dc.b $0000 ; Used by ReadDecimal for reading numbers operator dc.b $0000 ; Used by ReadDecimal for reading numbers inputBuffer ds.b $0010 ; Input Buffer Length dc.b NULL lenInput dc.w $0010 ; Length of the Input Buffer outputBuf dc.b 's' ; Used to control what to output on 7 segment display outputVal dc.b $00 ; Used to track the output value of the wave outputCnt dc.w $0000 ; Used to track how many values have been outputted interval dc.w 3000 ; Used to set the timer module based on clock cycles numPoints dc.w 2048 ; Max Number of points for waves timeTrigger dc.b $00 ; Tracks when timer is triggered increment dc.w 1 ; Used for increment 31.25Hz -> 1 ; 125Hz -> 4 waveType dc.b 'S' ; Used to track wave type 'T' for increasing triangle, ; 't' for decreasing triangle, ; 'Q' for square high ; 'q' for square low ; 'S' for sawtooth * * There is a section Data Section at the end of the file ************************************************************************** * RTI Vector Section: address used [ $FFF0 to $FFF1 ] RAM Memory * org $FFF0 ; Memory location for RTI interrupt vector section for simulator dc.w rtiisr ; Real Time Interrupt vector * ************************************************************************** * Timer Interrupt Vector Section: address used [ $FFE4 to $FFE5 ] RAM Memory * org $FFE4 ; Timer channel 5 interrupt vector setup, on simulator dc.w oc5isr * ************************************************************************** * 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 ldx #msg ; Load the address of the welcome message into X jsr WriteString ; Write the string to the serial console bset RTICTL,%00011001; set RTI: dev=10*(2**10)=2.555msec for C128 board ; 4MHz quartz oscillator clock bset CRGINT,%10000000; enable RTI interrupt bset CRGFLG,%10000000; clear RTI IF (Interrupt Flag) ldaa #$FF ; Two 7 segment displays on PORTB staa DDRB ; Set all of PORTB as output cli ; Enable interrupts mainLoop ldaa #'>' ; Load '>' character jsr putchar ; Print to serial console ldaa #' ' ; Load ' ' character jsr putchar ; Print to serial console ldx #inputBuffer ; Load the address of inputBuffer into X ldy lenInput ; Load the length of inputBuffer into Y jsr ReadString ; Jump to ReadString to read input ldx #inputBuffer ; Load the address of inputBuffer into X jsr ExecuteCommand ; Jump to ExecuteCommand ldx #inputBuffer ; Load the address of inputBuffer into X ldy lenInput ; Load the length of inputBuffer into Y jsr Zeros ; Zero out input buffer bra mainLoop ; Loop back to mainLoop always TypeWrite sei ; Disable Interrupts bclr CRGINT,%10000000; Disable RTI interrupt ldx #twMsg ; Load the address of twMsg into X jsr WriteString ; Write the string to the serial console twLoop jsr getchar ; Read a character from the serial console tsta ; Compare A to 0 beq twLoop ; If A == 0, branch to twLoop staa PORTB ; Write A to PORTB jsr putchar ; Write character to serial console bra twLoop ; Branch always to twLoop ************************************************************************** * Subroutine Section: address used [ $3100 to $3FFF ] RAM Memory * ;************************************************************************* ; rtiisr subroutine ; ; This subroutine will increment the counter, seconds, minutes, & hours counters ; to track the time. This subroutine will be called ~400 times a second. ; ; Input: No input other than the timer to call the isr. ; Output: The counter, seconds, minutes, & hours buffers will ; be updated to track the time, the time & prompt will be ; printed every second. ; Registers in use: X for adding to the counter, seconds, minutes, & hours buffers. ; Memory locations in use: Memory Address for serial line, Memory addresses for RTIISR control, ; Buffer words for counters, seconds, minutes, & hours buffers. ; ; Comments: The counter buffer should be compared to 400, but for the simulator, ; the counter is compared to 200 to better simulate 1 second on my computer. ; rtiisr bset CRGFLG,%10000000; Clear RTI Interrupt Flag cli ; Enable interrupts ldx counter ; Load counter into X inx ; Increment counter by 1 stx counter ; Save X to counter cpx #200 ; Compare counter to 200, This is about 1 second on my computer bne rtiSkip ; If counter != 200, branch to rtiSkip ldx #0 ; Load 0 into X stx counter ; Save X to counter ldx seconds ; Load the seconds into X inx ; Increment the seconds by 1 stx seconds ; Save the new seconds to the location cpx #60 ; Compare X to 60 bne rtidone ; If X != 60, exit isr ldx #0 ; Reset the seconds stx seconds ; Save the new seconds to the location ldx minutes ; load the minutes into X inx ; Increment the minutes by 1 stx minutes ; Save the updated minutes cpx #60 ; Compare the minutes to 60 bne rtidone ; If X != 60, exit isr ldx #0 ; Reset the minutes stx minutes ; Save the updated minutes ldx hours ; Load the hours into X inx ; Increment the hours by 1 stx hours ; Save the updated hours cpx #24 ; Compare the hours to 24 bne rtidone ; If X != 24, exit the isr ldx #0 ; Reset the hours stx hours ; Save the updated hours rtidone jsr PrintTime ; Jump to PrintTime rtiSkip RTI ; Return from RTI ISR ;************************************************************************* ; oc5isr subroutine ; ; This subroutine will set a flag after a set number of cycles. ; ; Input: interval memory location for the number of cycles between triggers ; Output: The outputCnt counting the number of triggers, and timeTrigger to ; signal a timmer trigger. ; Registers in use: A for setting timeTrigger, D for increasing outputCnt and setting next count. ; Memory locations in use: Memory Address for oc5 timer, outputCnt, timeTrigger, numPoints, interval ; ; Comments: The timer will stop after outputCnt == numPoints ; oc5isr ldd interval ; Load the interval for the next clock cycle addd TC5H ; for next interrupt std TC5H ; bset TFLG1,%00100000 ; Clear CH5 interrupt flag ldaa #1 ; Load 1 into A staa timeTrigger ; Signal that timer went off ldd outputCnt ; Load the count of values outputed into D addd #1 ; Increase output count by 1 std outputCnt ; Update the count of outputted values cpd numPoints ; Compare D to numPoints blo oc5Done ; If D < numPoints, Done jsr StopTimerCH5 ; Stop Channel 5 Timer oc5Done RTI ; Return from interrupt ;************************************************************************* ; PrintWave subroutine ; ; This subroutine will print a one byte decimal value to the serial console. ; The outputVal will be incremented by increment. It can follow a square wave, ; a triangle wave, & a sawtooth wave pattern. ; ; Input: waveType to denote the patter, increment to increment the outputVal ; Output: outputVal printed to the serial console ; Registers in use: A for finding the wave type, B for reading the outputVal, ; D for math and checking of outputVal ; Memory locations in use: waveType to set the patter, outputVal for printing ; the output value ; ; Comments: The timer will stop after outputCnt == numPoints ; PrintWave pshd ; Save D to the stack pshy ; Save Y to the stack ldaa waveType ; Load the waveType into A cmpa #'T' ; Compare to 'T' lbeq TriangleInc ; If A == 'T', triangle wave increasing cmpa #'t' ; Compare A to 't' lbeq TriangleDec ; If A == 't', triangle wave decreasing cmpa #'Q' ; Compare A to 'Q' lbeq SquareWaveH ; If A == 'Q', square wave high cmpa #'q' ; Compare A to 'q' lbeq SquareWaveL ; If A == 'q', square wave low SawToothWav clra ; Clear A ldab outputVal ; Load the output value into B ldy #buffer ; Load the address of buffer into Y jsr PrintDecimalWord; Print the lower byte of the output value; psha ; Save A to the stack ldaa #CR ; Load CR into A jsr putchar ; Write CR to serial console ldaa #LF ; Load LF into A jsr putchar ; Write LF to serial console pula ; Restore A from the stack addd increment ; Add increment to D cpd #256 ; Compare D to 256 lblo DonePrint ; If D < 256, Done clrb ; Reset to Zero lbra DonePrint ; Branch to DonePrint SquareWaveH clra ; Clear A ldab #255 ; Load 255 into B ldy #buffer ; Load the address of buffer into Y jsr PrintDecimalWord; Print the lower byte of the output value; psha ; Save A to the stack ldaa #CR ; Load CR into A jsr putchar ; Write CR to serial console ldaa #LF ; Load LF into A jsr putchar ; Write LF to serial console pula ; Restore A from the stack clra ; Clear A ldab outputVal ; Load the output value into B addd increment ; Add increment to D cpd #256 ; Compare D to 256 lblo DonePrint ; If D < 256, done clrb ; Reset B to zero ldaa #'q' ; Load 'q' into A staa waveType ; Update wave type to square wave low bra DonePrint ; Branch to DonePrint SquareWaveL clra ; Clear A clrb ; Reset B to zero ldy #buffer ; Load the address of buffer into Y jsr PrintDecimalWord; Print the lower byte of the output value; psha ; Save A to the stack ldaa #CR ; Load CR into A jsr putchar ; Write CR to serial console ldaa #LF ; Load LF into A jsr putchar ; Write LF to serial console pula ; Restore A from the stack clra ; Clear A ldab outputVal ; Load the output value into B addd increment ; Add increment to D cpd #256 ; Compare D to 256 blo DonePrint ; If D < 256, done clrb ; Reset B to zero ldaa #'Q' ; Load 'Q' into A staa waveType ; Update wave type to square wave low bra DonePrint ; Branch to DonePrint TriangleInc clra ; Clear A ldab outputVal ; Load the output value into B ldy #buffer ; Load the address of buffer into Y jsr PrintDecimalWord; Print the lower byte of the output value; psha ; Save A to the stack ldaa #CR ; Load CR into A jsr putchar ; Write CR to serial console ldaa #LF ; Load LF into A jsr putchar ; Write LF to serial console pula ; Restore A from the stack addd increment ; Add increment to D cpd #256 ; Compare D to 256 blo DonePrint ; If D < 256, done ldaa #'t' ; Load 't' into A staa waveType ; Update wave type to decreasing triangle subd #1 ; Subtract 1 from D bra DonePrint ; Branch to DonePrint TriangleDec clra ; Clear A ldab outputVal ; Load the output value into B ldy #buffer ; Load the address of buffer into Y jsr PrintDecimalWord; Print the lower byte of the output value; psha ; Save A to the stack ldaa #CR ; Load CR into A jsr putchar ; Write CR to serial console ldaa #LF ; Load LF into A jsr putchar ; Write LF to serial console pula ; Restore A from the stack subd increment ; Subtract increment from D cpd #0 ; Compare D to 0 blt DonePrint ; If D < 0, done ldaa #'T' ; Load 'T' into A staa waveType ; Update wave type to increasing triangle clrb ; Clear B DonePrint stab outputVal ; Store updated output value puly ; Restore Y from the stack puld ; Restore D from the stack rts ; Return from Caller ;************************************************************************* ; StartTimer5oc subroutine ; ; This subroutine will enable & start the oc5 timer. ; ; Input: Interval to set the next clock cycle ; Output: No output other ; Registers in use: A used for setting up the oc5 timer, D for setting the next trigger ; Memory locations in use: All memory locations used for the oc5 timer. ; interval to set the next cycle. ; ; Comments: The timer will be enabled only on channel 5 for output compare. ; StartTimer5oc PSHD LDAA #%00100000 STAA TIOS ; set CH5 Output Compare STAA TIE ; set CH5 interrupt Enable LDAA #%10000000 ; enable timer, Fast Flag Clear not set STAA TSCR1 LDAA #%00000000 ; TOI Off, TCRE Off, TCLK = BCLK/1 STAA TSCR2 ; not needed if started from reset LDD interval ; 125usec with (24MHz/1 clock) ADDD TCNTH ; for first interrupt STD TC5H ; BSET TFLG1,%00100000 ; initial Timer CH5 interrupt flag Clear, not needed if fast clear set LDAA #%00100000 STAA TIE ; set CH5 interrupt Enable PULD RTS ;************************************************************************* ; StopTimerCH5 subroutine ; ; This subroutine will stop and disable the timer. ; ; Input: No Input ; Output: No output other ; Registers in use: A to disable the timer. ; Memory locations in use: TIE to disable the timer. ; ; Comments: The timer will be disabled on all channels. ; StopTimerCH5 psha ; Save A to the stack clra ; Clear A staa TIE ; Stop Timers pula ; Restore A from the stack rts ; Return ;************************************************************************* ; GenWave subroutine ; ; This subroutine will setup the proper variables to generate a wave and wait ; for the wave to finish generating. ; ; Input: No input but the variables for PrintWave are required. ; Output: No output except for the output of PrintWave ; Registers in use: A for reading the timeTrigger variable, ; D for reading outputCnt. ; Memory locations in use: outputCnt, outputVal, timeTrigger, numPoints. ; ; Comments: This subroutine does not have any direct input or output but calls ; PrintWave so the inputs for PrintWave should be set and the output ; of PrintWave should be expected. ; GenWave pshd ; Save D to the stack ldd #0 ; Clear D std outputCnt ; Clear outputCnt staa outputVal ; Clear outputVal jsr StartTimer5oc ; Start Timer on CH5 genLoop ldaa timeTrigger ; Load timeTrigger into A beq genLoop ; If A == 0, loop clra ; Clear A staa timeTrigger ; Clear timeTrigger jsr PrintWave ; Jump to PrintWave ldd outputCnt ; Load outputCnt into D cpd numPoints ; Compare D to numPoints blo genLoop ; If D < numPoints, Loop jsr StopTimerCH5 ; Turn off timer puld ; Restore D from the stack rts ; Return ;************************************************************************* ; PrintTime subroutine ; ; This subroutine will print the time, command prompt, and maybe an error prompt. ; ; Input: No input. ; Output: The time prompt, time, command prompt, the current input, ; and/or an error on the serial console. ; Registers in use: A for the characters to print, X for buffer addresses, ; Y for buffer lengths, D for the seconds/minutes/hours for calling TimeOnPortB ; Memory locations in use: Memory Address for serial line, Buffer words for counters, ; seconds, minutes, & hours buffers, and buffer to print time, ; outputBuf for tracking what to output on PORTB. ; ; Comments: This subroutine requires TimeOnPortB subroutine and to be setup. The subroutine ; will print the current user input if its not finished. ; PrintTime pshd ; Save D to the stack ldaa outputBuf ; Load outputBuf into A cmpa #'h' ; Compare A to 'h' bne pTimeIsM ; If A != 'h', branch to pTimeIsM ldd hours ; Load hours into B bra skipRest ; Jump to skipRest pTimeIsM cmpa #'m' ; Compare A to 'm' bne pTimeIsS ; If A != 'm', branch to pTimeIsS ldd minutes ; Load Minutes into D bra skipRest ; Jump to skipRest pTimeIsS ldd seconds ; Load seconds into D skipRest jsr TimeOnPortB ; Call TimeOnPortB to output time puld ; Restore D from the stack rts ; Return to caller ;************************************************************************* ; TimeOnPortB subroutine ; ; This subroutine will output the time given on on PORTB for two seven segment displays. ; ; Input: Two Digit Decimal number in register D. ; Output: The given two digit decimal number on PORTB for two 7 segment displays. ; Registers in use: D for the input, and for math to split the digits, X for math to split digits. ; Memory locations in use: PORTB memory location. ; ; Comments: This subroutine will only work with two digit decimal numbers, and one digit decimal ; numbers (leading zeros will be added). ; TimeOnPortB pshd ; Save D to the stack pshx ; Save X to the stack ldx #10 ; Load 10 into X to get digit idiv ; Divide D by X and save Digit into D pshb ; Save B to the stack (Lower Byte of D) exg x,d ; Swap X and D ldx #10 ; Load 10 into X to get digit lslb ; Shift B left by 1 lslb ; Shift B left by 1 lslb ; Shift B left by 1 lslb ; Shift B left by 1 orab 1,sp+ ; Or B with Digit on stack stab PORTB ; Save B to PORTB pulx ; Restore X from the stack puld ; Restore D from the stack rts ; Return from caller ;************************************************************************* ; ExecuteCommand subroutine ; ; This subroutine will parse user input and execute the proper command or error out. ; ; Input: An address of a NULL terminated string in X. ; Output: The output of the proper command or an error message. ; Registers in use: X for the address of the user input, A for individual characters, ; D & Y for numbers read from user input. ; Memory locations in use: Serial console memory locations. ; ; Comments: This subroutine will disable interrupts while setting the time and will ; reenable them after setting the time. ; ExecuteCommand pshd ; Save D to the stack pshy ; Save Y to the stack ldaa 1,x+ ; Load the character from X into A lbeq ecDone ; If A == 0, jump to ecDone cmpa #'t' ; Compare A to 't' bne isH ; If A != 't', branch to isH skipSpaces ldaa 1,+x ; Load the next character into X cmpa #' ' ; Compare A to ' ' character beq skipSpaces ; If A == ' ', loop to skipSpaces sei ; Disable interrupts ldd hours ; Load hours into D pshd ; Save hours to the stack jsr ReadDecimal ; Read Hour number exg y,d ; Exchange Y and D cpd #24 ; Compare D to 24 lbhs badHours ; If D >= 24, badHours cpd #0 ; Compare D to 0 lblt badHours ; If D < 0, badHours std hours ; Save D to hours ldaa -1,x ; Load the next character into A cmpa #':' ; Compare A to ':' lbne badHours ; If A != ':', badHours ldd minutes ; Load minutes into D pshd ; Save minutes to the stack jsr ReadDecimal ; Read minute number exg y,d ; Exchange Y and D cpd #60 ; Compare D to 60 lbhs badMinutes ; If D >= 60, badMinutes cpd #0 ; Compare D to 0 lblt badMinutes ; If D < 0, badMinutes std minutes ; Save D to minutes ldaa -1,x ; Load the next character into A cmpa #':' ; Compare A to ':' lbne badMinutes ; If A != ':', badMinutes ldd seconds ; Load seconds into D pshd ; Save seconds to the stack jsr ReadDecimal ; Read second number exg y,d ; Exchange Y and D cpd #60 ; Compare D to 60 lbhs badSeconds ; If D >= 60, badSeconds cpd #0 ; Compare D to 0 lblt badSeconds ; If D < 0, badSeconds std seconds ; Save D to seconds ldaa -1,x ; Load the next character into A cmpa #NULL ; Compare A to NULL lbne badSeconds ; If A != ':', badSeconds clra ; Set A to 0 staa counter ; Clear Counter cli ; Enable interrupts puld ; Restore D from the stack puld ; Restore D from the stack puld ; Restore D from the stack jsr PrintTime ; Print Time lbra ecDone ; Branch to ecDone isH cmpa #'h' ; Compare A to 'h' bne isM ; If A != 'h', branch to isM ldab 1,x+ ; Load next character into B cmpb #NULL ; Compare B to NULL lbne badCommand ; If B != CR, bad command staa outputBuf ; Store A into outputBuf jsr PrintTime ; Print Time lbra ecDone ; Branch to ecDone isM cmpa #'m' ; Compare A to 'm' bne isS ; If A != 'm', branch to isS ldab 1,x+ ; Load next character into B cmpb #NULL ; Compare B to NULL lbne badCommand ; If B != CR, bad command staa outputBuf ; Store A into outputBuf jsr PrintTime ; Print Time lbra ecDone ; Branch to ecDone isS cmpa #'s' ; Compare A to 's' bne isQ ; If A != 's', branch to isQ ldab 1,x+ ; Load next character into B cmpb #NULL ; Compare B to NULL lbne badCommand ; If B != CR, bad command staa outputBuf ; Store A into outputBuf jsr PrintTime ; Print Time lbra ecDone ; Branch to ecDone isQ cmpa #'q' ; Compare A to 'q' bne isGw ; If A != 'q', branch to isGw ldab 1,x+ ; Load next character into B cmpb #NULL ; Compare B to NULL lbne badCommand ; If B != NULL, branch to ecDone jmp TypeWrite ; Jump to TypeWrite isGw cmpa #'g' ; Compare A to 'g' lbne badCommand ; If A != 'g', branch to badCommand ldaa 1,x+ ; Load next character into B cmpa #'w' ; Compare A to 'w' bne isGt ; If A != 'w', branch to isGt ldab 1,x+ ; Load next charater into B cmpb #NULL ; Compare B to NULL bne isGw2 ; If B != NULL, branch to isGw2 ldx #swMsg ; Load address of sawtooth message jsr WriteString ; Write string ldaa #'S' ; Load 'S' for sawtooth staa waveType ; Save A to waveType ldd #8 ; Load 1 into D std increment ; Set Increment to 1 -> 31.25Hz jsr GenWave ; Jump to GenWave ldx #doneWave ; Load the address of doneWave jsr WriteString ; Write string lbra ecDone ; Branch always to ecDone isGw2 cmpb #'2' ; Compare B to '2' lbne badCommand ; If B != '2', bad ldab 1,x+ ; Load next character into B cmpb #NULL ; Compare to NULL lbne badCommand ; Not NULL? bad ldx #sw2Msg ; Load address of sawtooth 125Hz message jsr WriteString ; Write string ldaa #'S' ; Load 'S' for sawtooth staa waveType ; Save A to waveType ldd #4 ; Load 4 into D std increment ; Set Increment to 4 -> 125Hz jsr GenWave ; Jump to GenWave ldx #doneWave ; Load the address of doneWave jsr WriteString ; Write string lbra ecDone ; Branch always to ecDone isGt cmpa #'t' ; Compare A to 't' bne isGq ; If A != 't', branch to isGq ldab 1,x+ ; Load next character into B cmpb #NULL ; Compare to NULL lbne badCommand ; A != NULL? bad ldx #tMsg ; Load address of triangle message jsr WriteString ; Write string ldaa #'T' ; Load 'T' for triangle staa waveType ; Save A to waveType ldd #1 ; Load 1 into D std increment ; Set Increment to 1 -> 31.25Hz jsr GenWave ; Jump to GenWave ldx #doneWave ; Load the address of doneWave jsr WriteString ; Write string lbra ecDone ; Branch always to ecDone isGq cmpa #'q' ; Compare A to 'q' lbne badCommand ; A != 'q'? bad ldab 1,x+ ; Load next character into B cmpb #NULL ; Compare B to NULL bne isGq2 ; B != NULL? isGq2 ldx #sqMsg ; Load address of square message jsr WriteString ; Write string ldaa #'q' ; Load 'q' for square staa waveType ; Save A to waveType ldd #1 ; Load 1 into D std increment ; Set Increment to 1 -> 31.25Hz jsr GenWave ; Jump to GenWave ldx #doneWave ; Load the address of doneWave jsr WriteString ; Write string lbra ecDone ; Branch always to ecDone isGq2 cmpb #'2' ; Compare B to '2' lbne badCommand ; B != '2'? bad ldab 1,x+ ; Load next character into B cmpb #NULL ; Compare B to NULL bne badCommand ; B != NULL? bad ldx #sq2Msg ; Load address of square 125Hz message jsr WriteString ; Write string ldaa #'q' ; Load 'q' for square staa waveType ; Save A to waveType ldd #8 ; Load 8 into D std increment ; Set Increment to 4 -> 125Hz jsr GenWave ; Jump to GenWave ldx #doneWave ; Load the address of doneWave jsr WriteString ; Write string lbra ecDone ; Branch always to ecDone badSeconds puld ; Restore Seconds from the stack std seconds ; Restore seconds before change badMinutes puld ; Restore minutes from the stack std minutes ; Restore minutes before change badHours puld ; Restore hours from the stack std hours ; Restore hours before change cli ; Reenable interrupts badCommand pshx ; Save X to the stack ldx #badInput ; Load the address of badInput into X jsr WriteString ; Jump to WriteString pulx ; Restore X from the stack ecDone puly ; Restore Y from the stack puld ; Restore D from the stack rts ; Return to caller ;************************************************************************* ; ReadDecimal subroutine ; ; This subroutine will read an ASCII string of a number in decimal and convert it to ; its value. ; ; Input: A memory address in register X. ; Output: The value of the number in the Y register, and any errors printed ; to the serial line. Zero bit is set if error occurs. ; Registers in use: X for the address of the contents and for a buffer while printing, ; D for multiplication, B for the character, Y for output value. ; Memory locations in use: Memory Address for serial line, address of the string ; ; Comments: This subroutine will return the value in the Y register, and if an error occurs, ; the Zero bit in the CCR will be set. ; ReadDecimal pshd ; Save D to the stack ldy #0 ; Clear Y register dHLoop ldab 1,x+ ; Read Next character from X beq dHDone ; If B == 0, exit loop cmpb #'+' ; Compare B to '+' beq dHDone ; If B == '+', end of number cmpb #'-' ; Compare B to '-' beq dHDone ; If B == '-', end of number cmpb #'*' ; Compare B to '*' beq dHDone ; If B == '+', end of number cmpb #'/' ; Compare B to '/' beq dHDone ; If B == '-', end of number cmpb #':' ; Compare B to ':' beq dHDone ; If B == '-', end of number cmpb #' ' ; Compare B to space character beq dHDone ; If B == ' ', exit loop cmpb #'0' ; Compare B to '0' character blt dHError ; If B < '0', bad address, exit loop cmpb #'9' ; Compare B to '9' character bhi dHError ; If B > '9', check if 'A'-'F' characters subb #'0' ; Subtract '0' from B to get true value pshb ; Save B to the stack ldd #10 ; load 10 into D emul ; Multiply Y and D exg d,y ; Transfer data from D to Y pulb ; Restore b from the stack aby ; Add B to Y bra dHLoop ; Branch always to rHLoop dHDone clra ; clear A accumulator tap ; Transfer A into CCR to clear zero bit puld ; Restore D from the stack rts ; Return to caller dHError ldaa #4 ; Load 4 into A to set zero bit in CCR tap ; Transfer A into CCR to set zero bit and warn error puld ; Restore D from the stack rts ; Return to caller ;************************************************************************* ; strrev subroutine ; ; This subroutine will reverse a string from one buffer into another. ; ; Input: Address of null terminated string in X, address of a large enough ; buffer in Y. ; Output: The string in X reversed in Y. ; Registers in use: X for the address of the string, Y for the address of the buffer, ; A to read characters from the string. ; Memory locations in use: Memory Address for serial line, address of the string & buffer ; ; Comments: This subroutine will not check that the output buffer is large enough, that ; is the job of the caller. ; strrev pshx ; Save X to the stack pshy ; Save Y to the stack psha ; Save A to the stack revLoop ldaa 1,y- ; Load Character from Y into A, decrement Y beq revDone ; If Character is 0, exit loop staa 1,x+ ; Save character in address in X, increment X bra revLoop ; Loop back always clra ; Set A to Zero revDone staa 1,x+ ; Copy Null terminator into new string pula ; Restore A from the stack puly ; Restore Y from the stack pulx ; Restore X from the stack rts ; Return to caller ;************************************************************************* ; PrintDecimalWord subroutine ; ; This subroutine will print a given word of data to the serial in binary. ; ; Input: 1 word of data in register D, Buffer Address in Y ; Output: Decimal representation of the data on the serial console ; Registers in use: Y for the address of the buffer, X to count the number of bits ; written and for division, D for the input, A for characters. ; Memory locations in use: Memory addresses for serial, and operator to hold sign ; ; Comments: This subroutine requires serial to be setup and putchar subroutine. ; PrintDecimalWord pshx ; Save X to the stack pshy ; Save Y to the stack pshd ; Save D (A:B) to the stack cpd #0 ; Compare D to zero beq dIsZero ; Branch to hIsZero blt dIsNegative ; If D < 0, Jump to dIsNegative dAfterNeg psha ; Save A to the stack pshy ; Save Y to the stack pshx ; Save x to the stack ldaa #'0' ; Load the '0' character into A ldx #buffer2 ; Load the address of buffer2 into X ldy #5 ; Load 5 into Y jsr memset ; Write '0' to the first 5 bytes in buffer2 pulx ; Restore X from the stack puly ; Restore Y from the stack clra ; Set A to zero staa 0,y ; Load Zero into Y for Null Terminator pula ; Restore A from the stack dPrintLoop ldx #10 ; Load 10 in X for division idiv ; Divide D / 10 to get Hex Digit cpx #0 ; Compare X to 0 beq dCheck ; If X == 0, branch to check D is zero dDNotZero addb #'0' ; Add '0' to B to get ASCII Character stab 1,+y ; Save character from B to Y exg X,D ; Swap values in X and D bra dPrintLoop ; Loop to hPrintLoop dCheck cpd #0 ; Compare D to 0 bne dDNotZero ; If D != 0, branch back to hDNotZero dPrintDone ldaa operator ; Load operator into A to see if negative cmpa #'-' ; Compare A to '-' bne dNotNeg ; If A != '-', jump to dNotNeg staa 1,+y ; Save '-' into buffer dNotNeg ldx #buffer2 ; Load the address of buffer2 in X jsr strrev ; Reverse string in Y in buffer in X jsr WriteString ; Jump to write string to write the number ldy lenBuf2 ; Load the length of buffer2 into Y ldx #buffer2 ; Load the address of buffer2 into X jsr Zeros ; Fill buffer2 with zeros puld ; Restore D (A:B) from the stack puly ; Restore Y from the stack pulx ; Restore X from the stack rts ; Return to caller dIsZero ldaa #'0' ; Load '0' character into A jsr putchar ; Print character to the screen puld ; Restore D (A:B) from the stack puly ; Restore Y from the stack pulx ; Restore X from the stack rts ; Return to caller dIsNegative psha ; Save A to the stack ldaa #'-' ; Load '-' into A staa operator ; Save '-' to operator buffer pula ; Restore A from the stack nega ; Two's complement of A suba #1 ; Subtract 1 from A negb ; Two'complement of B subb #1 ; Subtract 1 from B addd #1 ; Add 1 to D bra dAfterNeg ; Jump back to dAfterNeg ;************************************************************************* ; 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 ;************************************************************************* ; memset subroutine ; ; This subroutine will write a given byte to every byte in a given array. ; ; Input: Address of an array in X and its length in Y, the byte in A ; Output: The given byte in every byte of an array. ; Registers in use: X for the address of the array, Y for the length, and A for the given byte ; Memory locations in use: Memory Address of the array ; ; Comments: This subroutine requires serial to be setup and putchar subroutine. ; memset staa 1,x+ ; Load A into byte at X dbne y,memset ; Decrement Y and loop if Y != 0 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 #CR ; Load CR into A jsr putchar ; Print to serial ldaa #LF ; Load LF into A jsr putchar ; Print to serial pulx ; Restore X from the stack pulY ; Restore Y from the stack pula ; restore A from the stack 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 2: address used [ $3100 to $3FFF ] RAM Memory * badInput dc.b 'Invalid Input',CR,LF,NULL ; Invalid Input Prompt ; twMsg: welcome message for typewrite twMsg dc.b 'Wave Generator and Clock stopped and Typewrite program started.',CR,LF dc.b 'You may type below.',CR,LF,NULL ; Messages for different waveforms swMsg dc.b 'sawtooth wave generation...',CR,LF,NULL sw2Msg dc.b 'sawtooth wave 125Hz generation...',CR,LF,NULL tMsg dc.b 'triangle wave generation...',CR,LF,NULL sqMsg dc.b 'square wave generation...',CR,LF,NULL sq2Msg dc.b 'square wave 125Hz generation...',CR,LF,NULL doneWave dc.b 'Done generating wave.',CR,LF,NULL ; msg: this is the main option menu string msg dc.b 'Commands:',CR,LF dc.b 'gw: generate sawtooth wave, printing 0 through 255, repeated for total 2048 points',CR,LF dc.b 'gw2: generate sawtooth wave of 125Hz, wave repeated for total 2048 points',CR,LF dc.b 'gt: generate triangle wave, printing 0 through 255, then 255 down to 0, repeated for total 2048 points',CR,LF dc.b 'gq: generate square wave, printing 0 for 255 times, then print 255 for 255 times, then repeated for total 2048 points',CR,LF dc.b 'gq2: generate square wave of 125Hz, wave repeated for total 2048 points',CR,LF dc.b 't: Set the time in format HH:MM:SS',CR,LF dc.b 'h: Display the hours on the 7 segment displays',CR,LF dc.b 'm: Display the minutes on the 7 segment displays',CR,LF dc.b 's: Display the seconds on the 7 segment displays',CR,LF dc.b 'q: Stop the clock and enter typewriter',CR,LF,NULL