9 changed files with 758 additions and 1 deletions
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash |
||||
set -euo pipefail |
||||
ARG1=${1:-} |
||||
|
||||
TARGET=bitbang |
||||
|
||||
function clean { rm -f $TARGET.hex $TARGET.p $TARGET.lst ; } |
||||
|
||||
clean |
||||
|
||||
if test "$ARG1" = "clean" ; then |
||||
exit 0 |
||||
fi |
||||
|
||||
asl -r 2 -L $TARGET.asm && p2hex $TARGET.p -r 0x0000-0xffff |
||||
|
||||
if test $? -eq 0 -a "$ARG1" == "flash" ; then |
||||
mspdebug rf2500 "prog $TARGET.hex" |
||||
fi |
@ -0,0 +1,185 @@
@@ -0,0 +1,185 @@
|
||||
; |
||||
; for MSP430G2553 |
||||
; |
||||
; Based off of Matthias Koch's mecrisp code |
||||
; |
||||
;------------------------------------------------------------------------------ |
||||
; Base Definitions |
||||
;------------------------------------------------------------------------------ |
||||
|
||||
cpu msp430 |
||||
include "../common/registers.asm" |
||||
include "../common/regconvention.asm" |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; Memory map |
||||
;------------------------------------------------------------------------------ |
||||
|
||||
RamBegin equ 200h ; Start of RAM |
||||
RamEnd equ 400h ; End of RAM, 512 Bytes |
||||
FlashBegin equ 0C000h ; Start of Flash, 16 kb, Flash end always is $FFFF. |
||||
ProgBegin equ 0F000h ; Start of code |
||||
|
||||
org ProgBegin ; ... let the assembler know where to place the code |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; Load up functions |
||||
;------------------------------------------------------------------------------ |
||||
|
||||
include "core.asm" |
||||
include "timers.asm" |
||||
include "uart_lp.asm" |
||||
include "write.asm" |
||||
include "i2c.asm" |
||||
; include "../common/analog.asm" |
||||
; include "../common/flash.asm" |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; Static data |
||||
;------------------------------------------------------------------------------ |
||||
|
||||
s_hello: static_string "Hello! %d %%\n\r" |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
Reset: ; Main entry point |
||||
;------------------------------------------------------------------------------ |
||||
mov #returnstack_begin, sp ; Initialise return stack pointer |
||||
mov.w #WDTPW+WDTHOLD, &WDTCTL ; Watchdog off |
||||
|
||||
mov #datastack_begin, ds ; Initialise return stack pointer |
||||
|
||||
; Initialize hardware |
||||
|
||||
; 8 MHz ! Take care: If you change clock, you have to change Flash clock divider, too ! |
||||
; Current divider 19+1 --> 400kHz @ 8 MHz. Has to be in range 257 kHz - 476 kHz. |
||||
|
||||
mov.b &CALBC1_8MHZ, &BCSCTL1 ; Set DCO |
||||
mov.b &CALDCO_8MHZ, &DCOCTL ; to 8 MHz. |
||||
|
||||
clr.b &IE1 ; Clear interrupt flags of oscillator fault, NMR and flash violation. |
||||
mov.w #FWKEY, &FCTL1 ; Lock flash against writing |
||||
mov.w #FWKEY|FSSEL_1|19, &FCTL2 ; MCLK/20 for Flash Timing Generator |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; init subsystems |
||||
;------------------------------------------------------------------------------ |
||||
call #uart_init |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
eint |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; this is where we dance |
||||
;------------------------------------------------------------------------------ |
||||
|
||||
uart_puts "Welcome!\n\r" |
||||
|
||||
|
||||
; i2c test code |
||||
; assume we have a standard I2C EEPROM on P2.4(scl) and P2.5(sda) |
||||
; address #50h |
||||
call #i2c_init |
||||
i2c_delay |
||||
mov.b #(50h << 1), a0 ; address (write) |
||||
call #i2c_start ; start condition on I2C bus |
||||
call #i2c_write_byte ; send address |
||||
jc + ; got NACK (no response), finish |
||||
mov.b #01, a0 ; byte we're sending (eep address) |
||||
call #i2c_write_byte |
||||
; mov.b #0BEh, a0 ; byte we're sending (data for eep) |
||||
; call #i2c_write_byte |
||||
; mov.b #0EFh, a0 ; byte we're sending (data for eep) |
||||
; call #i2c_write_byte |
||||
; call #i2c_stop ; full stop after writing to eep |
||||
i2c_delay |
||||
mov.b #(50h << 1)|1, a0 ; address (read) |
||||
call #i2c_start ; re-start |
||||
call #i2c_write_byte ; send address |
||||
jc + ; got NACK (no response), finish |
||||
mov.b #0, a0 ; send ACK after reading a byte |
||||
call #i2c_read_byte ; read into a0 |
||||
mov.b #1, a0 ; send NACK after reading last byte |
||||
call #i2c_read_byte |
||||
+ call #i2c_stop |
||||
; the second read byte is now in a0 |
||||
pushda a0 |
||||
mov #s_hello, a0 |
||||
call #uart_write_str ; print the byte |
||||
; end of i2c test code |
||||
|
||||
|
||||
bis.b #01000001b, &P1DIR ; make P1.0 and P1.6 output |
||||
|
||||
- bic.b #00000001b, &P1OUT ; clear P1.0 (red off) |
||||
bis.b #01000000b, &P1OUT ; set P1.6 (green on) |
||||
|
||||
call #uart_recv ; wait for a byte, comes in a0 |
||||
call #uart_emit ; ... and send it back |
||||
|
||||
bis.b #00000001b, &P1OUT ; set P1.0 (red on) |
||||
bic.b #01000000b, &P1OUT ; clear P1.6 (green off) |
||||
|
||||
call #uart_recv ; wait for a byte, comes in a0 |
||||
call #uart_emit ; ... and send it back |
||||
|
||||
pushda #32769 |
||||
mov #s_hello, a0 |
||||
call #uart_write_str |
||||
|
||||
jmp - ; loop |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
|
||||
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------ |
||||
coldreset: |
||||
clr &WDTCTL ; Reset with cold start |
||||
|
||||
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; Interrupt vectors |
||||
;------------------------------------------------------------------------------ |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
null_handler: ; Catches unwired interrupts |
||||
reti |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; choose the isr functions |
||||
irq_vector_port1 equ null_handler |
||||
irq_vector_port2 equ null_handler |
||||
irq_vector_adc equ null_handler |
||||
irq_vector_tx equ wake_tx ; used for uart wait in lpm |
||||
irq_vector_rx equ wake_rx ; used for uart wait in lpm |
||||
irq_vector_timera1 equ null_handler |
||||
irq_vector_timera0 equ null_handler |
||||
irq_vector_watchdog equ null_handler |
||||
irq_vector_comp equ null_handler |
||||
irq_vector_timerb1 equ null_handler |
||||
irq_vector_timerb0 equ wake_timer ; used for timed sleep |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
org 0FFE0h ; Interrupt table |
||||
|
||||
.word null_handler ; 01: 0FFE0 Unused |
||||
.word null_handler ; 02: 0FFE2 Unused |
||||
.word irq_vector_port1 ; 03: 0FFE4 Port 1 |
||||
.word irq_vector_port2 ; 04: 0FFE6 Port 2 |
||||
.word null_handler ; 05: 0FFE8 Unused |
||||
.word irq_vector_adc ; 06: 0FFEA ADC10 |
||||
.word irq_vector_tx ; 07: 0FFEC USCI Transmit - Terminal |
||||
.word irq_vector_rx ; 08: 0FFEE USCI Receive - interrupts |
||||
.word irq_vector_timera1 ; 09: 0FFF0 Timer A |
||||
.word irq_vector_timera0 ; 10: 0FFF2 Timer A |
||||
.word irq_vector_watchdog ; 11: 0FFF4 Watchdog |
||||
.word irq_vector_comp ; 12: 0FFF6 Comparator |
||||
.word irq_vector_timerb1 ; 13: 0FFF8 Timer B |
||||
.word irq_vector_timerb0 ; 14: 0FFFA Timer B |
||||
.word null_handler ; 15: 0FFFC NMI. Unused. |
||||
.word Reset ; 16: 0FFFE Main entry point |
||||
|
||||
end |
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
; |
||||
; based off of Matthias Koch's mecrisp code (GPL3) |
||||
; |
||||
; (assumes 'ds' register alias) |
||||
|
||||
; ---------------------------------------------------------- |
||||
; RAM allocations |
||||
; ---------------------------------------------------------- |
||||
|
||||
rampointer set RamBegin ; Beginning of Ram |
||||
|
||||
; Creating labels for variables and buffers in RAM |
||||
ramallot macro Name, Size |
||||
Name equ rampointer |
||||
rampointer set rampointer + Size |
||||
endm |
||||
|
||||
|
||||
; allocate datastack (ideally with uncritical data around it...) |
||||
datastack_length equ 64 ; Size of the datastack, has to be even |
||||
ramallot datastack_end, datastack_length |
||||
datastack_begin equ datastack_end + datastack_length |
||||
|
||||
; allocate return stack |
||||
returnstack_length equ 64 ; Size of the return stack, has to be even |
||||
ramallot returnstack_end, returnstack_length |
||||
returnstack_begin equ returnstack_end + returnstack_length |
||||
|
||||
|
||||
|
||||
; ---------------------------------------------------------- |
||||
; Data stack |
||||
; ---------------------------------------------------------- |
||||
|
||||
popda macro dst ; Fetch from datastack - 2 Bytes |
||||
mov @ds+, dst ; Increment stackpointer by two after fetching content. |
||||
endm |
||||
|
||||
|
||||
pushda macro src ; Put something on datastack - 8 Bytes |
||||
decd ds ; Adjust stack pointer |
||||
mov src, @ds ; and put value in. |
||||
endm |
||||
|
||||
|
||||
; ---------------------------------------------------------- |
||||
; Macros |
||||
; ---------------------------------------------------------- |
||||
|
||||
; Sized strings |
||||
static_string macro Text ; Text should not have parentheses |
||||
.word STRLEN(Text) ; word b/c alignment - asl freaks when .byte and escape in Text |
||||
.byte Text |
||||
align 2 |
||||
endm |
@ -0,0 +1,183 @@
@@ -0,0 +1,183 @@
|
||||
; |
||||
; Software I2C driver |
||||
; |
||||
; based on https://calcium3000.wordpress.com/2016/08/19/i2c-bit-banging-tutorial-part-i/ |
||||
; |
||||
|
||||
; the driver assumes that there are pull-up resistors on both SDA and SCL |
||||
; (as clearing a line is done by making it an input) |
||||
; clock stretching is allowed; but possible to save some bytes (~50?) when disabled |
||||
; (you'll need to comment out *_stretch parts below) |
||||
|
||||
; SCL:P2.4 SDA:P2.5 |
||||
SCL equ 00010000b |
||||
SDA equ 00100000b |
||||
I2C_PSEL equ P2SEL |
||||
I2C_P2SEL equ P2SEL2 |
||||
I2C_INPORT equ P2IN |
||||
I2C_PDIR equ P2DIR |
||||
I2C_POUT equ P2OUT |
||||
|
||||
; "half i2c cycle" (configure i2c bus speed here) |
||||
; for 400kHz (fast i2c) on 8MHz MCU clock it should be 10 cycles |
||||
; so 100kHz (normal speed) should be 40 |
||||
; use the nop instead of a loop to really get 10 cycles |
||||
i2c_delay_loop: ; call is 5 cycles |
||||
mov #8, t1 ; 2 cycles |
||||
- dec t1 ; 2 cycles |
||||
jnz - ; 2 cycles |
||||
; nop ; 1 cycle |
||||
; nop ; 1 cycle |
||||
ret ; 3 cycles |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; Sample usage |
||||
;------------------------------------------------------------------------------ |
||||
; i2c test code |
||||
; assume we have a standard I2C EEPROM on P2.4(scl) and P2.5(sda) |
||||
; address #50h |
||||
|
||||
; call #i2c_init |
||||
; i2c_delay |
||||
; mov.b #(50h << 1), a0 ; address (write) |
||||
; call #i2c_start ; start condition on I2C bus |
||||
; call #i2c_write_byte ; send address |
||||
; jc + ; got NACK (no response), finish |
||||
; mov.b #01, a0 ; byte we're sending (eep address) |
||||
; call #i2c_write_byte |
||||
; i2c_delay |
||||
; mov.b #(50h << 1)|1, a0 ; address (read) |
||||
; call #i2c_start ; re-start |
||||
; call #i2c_write_byte ; send address |
||||
; jc + ; got NACK (no response), finish |
||||
; mov.b #0, a0 ; send ACK after reading a byte |
||||
; call #i2c_read_byte ; read into a0 |
||||
; mov.b #1, a0 ; send NACK after reading last byte |
||||
; call #i2c_read_byte |
||||
; + call #i2c_stop |
||||
; ; the second read byte is now in a0 |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; Implementation |
||||
;------------------------------------------------------------------------------ |
||||
|
||||
; registers: |
||||
; t0 used for bit loops |
||||
; t1 used for delay loop |
||||
; a0 used for data |
||||
; a1 used for n/ack |
||||
|
||||
scl_high macro |
||||
bic.b #SCL, &I2C_PDIR |
||||
endm |
||||
|
||||
scl_low macro ; needs to preserve carry |
||||
bis.b #SCL, &I2C_PDIR |
||||
endm |
||||
|
||||
scl_high_stretch: |
||||
scl_high ; needs to make SCL input |
||||
- bit.b #SCL, &I2C_INPORT |
||||
jz - ; wait for stretching to finish |
||||
ret |
||||
|
||||
sda_high macro |
||||
bic.b #SDA, &I2C_PDIR |
||||
endm |
||||
|
||||
sda_low macro |
||||
bis.b #SDA, &I2C_PDIR |
||||
endm |
||||
|
||||
|
||||
i2c_delay macro |
||||
call #i2c_delay_loop |
||||
endm |
||||
|
||||
i2c_init: |
||||
bic.b #SDA|SCL, &I2C_PSEL |
||||
bic.b #SDA|SCL, &I2C_P2SEL |
||||
bic.b #SDA|SCL, &I2C_POUT |
||||
scl_high |
||||
sda_high |
||||
ret |
||||
|
||||
i2c_start: |
||||
scl_high |
||||
sda_high |
||||
i2c_delay |
||||
sda_low |
||||
i2c_delay |
||||
scl_low |
||||
i2c_delay |
||||
ret |
||||
|
||||
i2c_stop: |
||||
sda_low |
||||
i2c_delay |
||||
scl_high |
||||
i2c_delay |
||||
sda_high |
||||
i2c_delay |
||||
ret |
||||
|
||||
; bit in carry |
||||
i2c_write_bit: |
||||
jc + |
||||
sda_low |
||||
jmp ++ |
||||
+ sda_high |
||||
+ i2c_delay |
||||
scl_high |
||||
i2c_delay |
||||
scl_low |
||||
ret |
||||
|
||||
; rotates I2C bit into LSB of a0 |
||||
i2c_read_bit: |
||||
sda_high |
||||
i2c_delay |
||||
scl_high |
||||
i2c_delay |
||||
bit #SDA, &I2C_INPORT |
||||
rlc.b a0 ; (bit7 will end up in carry) |
||||
scl_low ; (this needs to preserve carry) |
||||
ret ; (b/c want to keep carry for i2c_read_byte) |
||||
|
||||
; rotates I2C bit into LSB of a0 (stretching allowed) |
||||
i2c_read_bit_stretch: |
||||
sda_high |
||||
i2c_delay |
||||
call #scl_high_stretch |
||||
i2c_delay |
||||
bit #SDA, &I2C_INPORT |
||||
rlc.b a0 |
||||
scl_low |
||||
ret |
||||
|
||||
; a0: byte to send |
||||
; returns NACK in carry (C=NACK) |
||||
i2c_write_byte: |
||||
mov #8, t0 |
||||
- rlc.b a0 ; shift MSbit to carry |
||||
call #i2c_write_bit |
||||
dec t0 |
||||
jnz - |
||||
; call #i2c_read_bit |
||||
call #i2c_read_bit_stretch |
||||
rrc.b a0 ; move NACK to carry |
||||
ret |
||||
|
||||
; a0: LSbit is NACK (1=NACK) |
||||
; returns byte in a0 |
||||
i2c_read_byte: |
||||
mov #8, t0 |
||||
- dec t0 |
||||
jz + |
||||
call #i2c_read_bit |
||||
jmp - |
||||
;+ call #i2c_read_bit ; NACK is now in carry |
||||
+ call #i2c_read_bit_stretch ; NACK is now in carry |
||||
call #i2c_write_bit |
||||
ret |
||||
|
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
; |
||||
; timers |
||||
; |
||||
|
||||
; assumes irq_vector_timerb0 is "wake_timer" |
||||
delayticks_lp: ; a0:ticks (on a 32768 Hz crystal) |
||||
push sr ; save GIE flag |
||||
mov.w #CCIE, &TBCCTL0 ; enable CC interrupt |
||||
clr &TBR ; clear the counter |
||||
mov.w a0, &TBCCR0 ; set the required delay |
||||
mov.w #TASSEL_1|MC_1, &TBCTL ; ACLK, up-mode |
||||
bis #LPM3|GIE, sr ; sleep in LPM3 |
||||
clr &TBCTL ; stop timer |
||||
reti ; reti also restores SR from stack |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
wake_timer: ; waking up from sleep through an interrupt |
||||
bic #LPM4, 0(sp) ; clears all lpm flags |
||||
reti |
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
; |
||||
; uart_lp: basic uart using USCI_A0 on P1.1/P1.2 with LPM wait |
||||
; |
||||
; based off of Matthias Koch's mecrisp code |
||||
; |
||||
|
||||
; assumes "core.asm" |
||||
|
||||
; (note that MSP430G2 FET can only do 9600 baud) |
||||
|
||||
uart_init: ; Initialize 9600 baud on 8MHz clock |
||||
; Init pins |
||||
mov.b #06h, &P1SEL ; use P1.1/P1.2 for USCI_A0 |
||||
mov.b #06h, &P1SEL2 ; use P1.1/P1.2 for USCI_A0 |
||||
bis.b #UCSSEL_2,&UCA0CTL1 ; SMCLK as clock source |
||||
; UCBRx and UCBRSx from table 15-4 (p424) in SLAU144J |
||||
mov.b #65,&UCA0BR0 ; 8MHz 9600 baud --> 833 cycles/bit |
||||
mov.b #3,&UCA0BR1 ; 8MHz 9600 baud |
||||
mov.b #UCBRS_2,&UCA0MCTL ; modulation UCBRSx = 2 |
||||
; mov.b #69,&UCA0BR0 ; 8MHz 115200 baud --> 69 cycles/bit |
||||
; mov.b #0,&UCA0BR1 ; 8MHz 115200 baud |
||||
; mov.b #UCBRS_4,&UCA0MCTL ; modulation UCBRSx = 4 |
||||
bic.b #UCSWRST,&UCA0CTL1 ; **initialize USCI state machine** |
||||
ret |
||||
|
||||
uart_emit: ; output the character in a0 |
||||
mov #UCA0TXIFG, t0 |
||||
call #_uart_sleeproutine ; sleep until available |
||||
mov.b a0, &UCA0TXBUF |
||||
ret |
||||
|
||||
jq_uart_emit: ; branch to addr if ready to emit ( addr -> ) |
||||
bit.b #UCA0TXIFG, &IFG2 |
||||
jz + |
||||
mov a0, @sp ; change return address |
||||
+ ret |
||||
|
||||
uart_recv: ; return incoming byte ( -> byte ) |
||||
mov #UCA0RXIFG, t0 |
||||
call #_uart_sleeproutine ; sleep until available |
||||
mov.b &UCA0RXBUF, a0 |
||||
ret |
||||
|
||||
jq_uart_recv: ; branch to add if incoming byte ( addr -> ) |
||||
bit.b #UCA0RXIFG, &IFG2 |
||||
jz + |
||||
mov a0, @sp ; change return address |
||||
+ ret |
||||
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; internals |
||||
;------------------------------------------------------------------------------ |
||||
|
||||
_uart_sleeproutine: ; sleep (or loop) until tx||rx (selected in t0) happens |
||||
push sr |
||||
dint |
||||
nop |
||||
bis.b t0, &IE2 ; enable interrupt source for tx||rx |
||||
|
||||
- bit.b t0, &IFG2 ; check if available |
||||
jnz + |
||||
bit #GIE, @sp ; have interrupts been enabled before? |
||||
jnc - ; if not, simply poll for a character |
||||
|
||||
bis #LPM3|GIE, sr ; if yes, sleep and wait for interrupt |
||||
_uart_here_we_dream: |
||||
jmp - |
||||
|
||||
+ bic.b t0, &IE2 ; disable interrupt source for tx||rx |
||||
nop |
||||
reti |
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; irq |
||||
;------------------------------------------------------------------------------ |
||||
|
||||
wake_tx: |
||||
wake_rx: |
||||
cmp #_uart_here_we_dream, 2(sp) |
||||
jne + |
||||
bic #LPM3|GIE, 0(sp) |
||||
+ reti |
@ -0,0 +1,199 @@
@@ -0,0 +1,199 @@
|
||||
; |
||||
; write: basic string writing and manipulation |
||||
; |
||||
|
||||
; assumes "core.asm" (data stack and macros) |
||||
; assumes uart_emit symbol |
||||
|
||||
; sized string: w_size=n b_c1 b_c2 ... b_cn |
||||
|
||||
|
||||
; print a single hexadecimal digit |
||||
; expects: |
||||
; - a0: value to print (0-15) |
||||
; - a1: "emit" routine to use |
||||
write_custom_hexdigit: |
||||
add.b #'0', a0 |
||||
cmp #':', a0 ; ':' = '9'+1 |
||||
jl + |
||||
add.b #39, a0 ; get to lowercase alpha |
||||
+ mov a1, pc ; jump to "emit" (call/ret shortcut) |
||||
|
||||
|
||||
; print a byte, hexadecimal, unsigned |
||||
; expects: |
||||
; - a0: value to print |
||||
; - a1: "emit" routine to call |
||||
; - a2: if non zero, always print two digits |
||||
uart_write_byte_hex: |
||||
mov #uart_emit, a1 |
||||
write_custom_byte_hex: |
||||
push s3 |
||||
mov.b a0, s3 |
||||
rrc.b a0 |
||||
rrc.b a0 |
||||
rrc.b a0 |
||||
rrc.b a0 |
||||
and #0Fh, a0 |
||||
jnz + |
||||
cmp #0, a2 |
||||
jz ++ |
||||
+ call #write_custom_hexdigit |
||||
+ mov.b s3, a0 |
||||
pop s3 |
||||
and #0Fh, a0 |
||||
jmp write_custom_hexdigit ; (call/ret shortcut) |
||||
|
||||
|
||||
; print a word, hexadecimal, unsigned, always four digits |
||||
; expects: |
||||
; - a0: value to print |
||||
; - a1: "emit" routine to call |
||||
; clobbers: a2 |
||||
uart_write_word_hex_p: |
||||
mov.w #uart_emit, a1 |
||||
write_custom_word_hex_p: |
||||
push.b a0 ; save the lower byte |
||||
swpb a0 |
||||
mov #1, a2 ; want leading zeroes |
||||
call #write_custom_byte_hex ; only prints LSB of a0 |
||||
pop a0 |
||||
jmp write_custom_byte_hex |
||||
|
||||
|
||||
; print a word, hexadecimal, unsigned, not padded |
||||
; expects: |
||||
; - a0: value to print |
||||
; - a1: "emit" routine to call |
||||
; clobbers: a2 |
||||
uart_write_word_hex: |
||||
mov.w #uart_emit, a1 |
||||
write_custom_word_hex: |
||||
push.b a0 ; save the lower byte |
||||
swpb a0 |
||||
mov #0, a2 ; assume we won't want leading zeroes |
||||
and #0FFh, a0 ; restrict and test |
||||
jz + ; do not print zeroes |
||||
call #write_custom_byte_hex |
||||
mov #1, a2 ; want leading zeroes (as we printed the upper byte) |
||||
+ pop a0 |
||||
jmp write_custom_byte_hex |
||||
|
||||
|
||||
; print a word, decimal, unsigned |
||||
; expects: |
||||
; - a0: value to print |
||||
; - a1: "emit" routine to call |
||||
; clobbers: t0 |
||||
uart_write_word_dec: |
||||
mov #uart_emit, a1 |
||||
write_custom_word_dec: |
||||
push a1 ; save "emit" |
||||
call #to_bcd ; a1|a0 is now BCD |
||||
mov a1, t0 |
||||
pop a1 ; restore "emit" |
||||
and #0Fh, t0 |
||||
jz + ; skip leading 0 |
||||
push a0 |
||||
mov t0, a0 |
||||
call #write_custom_hexdigit ; print fifth digit |
||||
pop a0 |
||||
jmp write_custom_word_hex_p |
||||
+ jmp write_custom_word_hex |
||||
|
||||
|
||||
; print a sized string |
||||
; expects: |
||||
; - a0: sized string address |
||||
; - data stack: data for variables printing (%..) |
||||
; - if called as write_custom: a1: address for "emit" routine |
||||
; clobbers: a0, potentially (flags): a2, t0, t1 |
||||
uart_write_str: |
||||
mov #uart_emit, a1 |
||||
write_custom_str: |
||||
push s0 |
||||
push s1 |
||||
mov @a0+, s1 ; set up loop: s0:current, s1:end |
||||
add a0, s1 ; s1 = a0.orig + @a0.orig + 2 |
||||
mov a0, s0 ; a0 = a0.orig + 2 |
||||
|
||||
- mov.b @s0+, a0 |
||||
cmp #'%', a0 ; check for "print value" char |
||||
jne .emit |
||||
|
||||
; logic to deal with % flags |
||||
mov.b @s0+, a0 |
||||
|
||||
cmp #'B', a0 ; byte in hex |
||||
jne + |
||||
popda a0 ; get the value to be printed |
||||
mov #1, a2 ; want leading zeroes |
||||
call #write_custom_byte_hex |
||||
jmp - |
||||
|
||||
+ cmp #'W', a0 ; word (16bit) in hex, padded 4 digits |
||||
jne + |
||||
popda a0 ; get the value to be printed |
||||
call #write_custom_word_hex_p |
||||
jmp - |
||||
|
||||
+ cmp #'d', a0 ; unsigned decimal (16bit) |
||||
jne + |
||||
popda a0 ; get the value to be printed |
||||
call #write_custom_word_dec |
||||
jmp - |
||||
|
||||
+ ; another flag comes here ... |
||||
|
||||
.emit |
||||
call a1 ; "emit" |
||||
cmp s1, s0 ; repeat until s0 reaches s1 |
||||
jlo - |
||||
|
||||
pop s1 |
||||
pop s0 |
||||
ret |
||||
|
||||
|
||||
; convert 16 bit unsigned to BCD (binary coded decimal) |
||||
; from slaa024 (chapter5, BINDEC) |
||||
; expects: |
||||
; - a0: number to convert |
||||
; returns: |
||||
; - a1|a0 BCD |
||||
; clobbers: |
||||
; - t0, t1 |
||||
to_bcd: |
||||
mov #16, t0 ; loop counter |
||||
clr a1 ; 0, result MSD |
||||
clr t1 ; 0, result LSD |
||||
- rla a0 ; MSbit to carry |
||||
dadd t1, t1 ; 2*result+carry to LSD |
||||
dadd a1, a1 ; to MSD |
||||
dec t0 ; loop |
||||
jnz - |
||||
mov t1, a0 ; fix return value register |
||||
ret |
||||
|
||||
|
||||
|
||||
; expected usage: uart_puts "text\n" |
||||
; will send "text\n" to uart |
||||
uart_puts macro Text |
||||
call #_write_puts_pc |
||||
static_string Text |
||||
endm |
||||
|
||||
|
||||
;------------------------------------------------------------------------------ |
||||
; internals |
||||
;------------------------------------------------------------------------------ |
||||
|
||||
_write_puts_pc: ; sized string on stack (as a return address) |
||||
mov @sp, a0 |
||||
add @a0, @sp ; fix the return address |
||||
incd @sp |
||||
bit #1, @sp ; add 1 if odd |
||||
jz + |
||||
inc @sp |
||||
+ jmp uart_write_str |
@ -1,4 +1,17 @@
@@ -1,4 +1,17 @@
|
||||
|
||||
|
||||
|
||||
## register conventions |
||||
|
||||
See `common/regconvention.asm`. |
||||
I renamed the registers and am pretty much using only the aliases, according to the "convention" specified in that file. |
||||
|
||||
|
||||
## function calling conventions |
||||
|
||||
I am mixing two approaches: I try to make function parameters to be passed in registers (`a0`,`a1`,...) to try to avoid memory read/writes. However some functions do use the data stack that's set up in `core.asm`. You need to look at the comments around the function definitions to find out which one is being used. (Starts at 04-.) |
||||
|
||||
|
||||
|
||||
|
||||
[mas]: http://john.ccac.rwth-aachen.de:8000/as/ |
||||
|
Loading…
Reference in new issue