org 100h siob_data equ 083h siob_ctrl equ 082h lcd_data equ 0dbh lcd_ctrl equ 0dah celcius_offset equ 08ah pressure_offset equ 0cah humid_offset equ 09eh spinner_offset equ 0e7h ; line 4, column 20 on a 20x4 panel bs_char equ 01h ; cgram slot holding our backslash bs_cgram equ 048h ; set cgram address, slot 1 row 0 bdos equ 0005h conout equ 02h conin equ 01h dconio equ 06h printstr equ 09h start: ld c,printstr ; tell the operator how to quit ld de,quitmsg call bdos call init_lcd call init_siob call lcd_delay call load_glyphs ld a,celcius_offset ; write celcius label out (lcd_ctrl),a call lcd_delay ld hl,clabel call print_str_to_lcd ld a,pressure_offset ; write pressure label out (lcd_ctrl),a call lcd_delay ld hl,plabel call print_str_to_lcd ld a,humid_offset ; write humidity label out (lcd_ctrl),a call lcd_delay ld hl,hlabel call print_str_to_lcd main: call read_char_b ; never blocks now jr nc,check_quit ; wire is idle, go look at the keyboard ld hl,rx_seen ld (hl),0ffh ; note traffic, a holds the char untouched call parse_char jr main ; drain the sio while it still has data check_quit: call spin_tick ; keep the spinner turning while idle ld c,dconio ; direct console i/o, no echo ld e,0ffh ; ff polls, returning zero if no key call bdos cp a,051h ; Q ret z cp a,071h ; q ret z jr main ; no key, or a key we ignore init_siob: ld a,018h ; channel reset out (siob_ctrl),a ld a,04h out (siob_ctrl),a ; wr4 baud settings ld a,0c4h out (siob_ctrl),a ld a,03h out (siob_ctrl),a ; rx enable ld a,0c1h out (siob_ctrl),a ld a,05h out (siob_ctrl),a ; tx settings ld a,0eah out (siob_ctrl),a ret init_lcd: ld a,38h out (lcd_ctrl),a ; function 8 bit, 2 lines, 5x8 dot font call lcd_delay ld a,0ch out (lcd_ctrl),a ; display on, cursor off, no blink call lcd_delay ld a,01h out (lcd_ctrl),a ; clear display ret load_glyphs: ; the a00 rom has yen at 5ch, so draw our own backslash ld a,bs_cgram out (lcd_ctrl),a call lcd_delay ld hl,glyph_bs ld e,08h ; eight rows, and e survives lcd_delay glyph_row: ld a,(hl) out (lcd_data),a ; cgram address auto increments call lcd_delay inc hl dec e jr nz,glyph_row ret lcd_delay: ld bc,500 delay1: dec bc ld a,b or c jr nz,delay1 ret convert_to_hpa: ; shift the decimal point in (pressure) two places left ld hl,pressure ld b,0 ; count of digits ahead of the point find_point: ld a,(hl) or a ret z ; no point in the string, leave it alone cp a,2eh ; period jr z,found_point inc hl inc b jr find_point found_point: ld a,b cp 3 ; need three whole digits to shift by two ret c dec hl ; step back over the ones digit dec hl ; hl now holds the new tenths digit ld a,(hl) ld (hl),2eh ; drop the point in two places left inc hl ld (hl),a ; and the saved digit after it inc hl ld (hl),00h ; null terminate, discarding the rest ret read_char_b: ; carry set with the char in a, carry clear if nothing waiting in a,(siob_ctrl) bit 0,a jr z,no_char in a,(siob_data) scf ; flag a as a real character ret no_char: or a ; clears carry, a holds status not data ret parse_char: ; char should be in register a cp a,25h ; percent sign (end of transmission) jr z,print_telem_to_lcd cp a,0dh ; carriage return jr z,null_term cp a,0ah ; new line jr z,parse_char_done cp a,054h ; T character jr z,set_char_idx_t ; point the char_idx to the temperature buffer cp a,050h ; P character jr z,set_char_idx_p ; point the char_idx to the pressure buffer cp a,048h ; H character jr z,set_char_idx_h ; point the char_idx to the humidity buffer push af ld a,(char_cnt) ; get number stored in char_cnt cp char_max ; compare it to maximum character count jr nc,store_full ; if no carry, the buffer is full inc a ; otherwise increment ld (char_cnt),a ; and store back in char_cnt pop af ld bc,(char_idx) ; get address stored in char_idx ld (bc),a ; deref that address and store char inc bc ld (char_idx),bc ; store incremented address in char_idx parse_char_done: ret store_full: pop af ret print_telem_to_lcd: call move_to_tempaddr ld hl,temperature call print_str_to_lcd call convert_to_hpa call move_to_presaddr ld hl,pressure call print_str_to_lcd call move_to_humidaddr ld hl,humidity call print_str_to_lcd ld a,1h ret null_term: ; null terminate end of parsed string in memory ld a,00h ld bc,(char_idx) ld (bc),a jr parse_char_done set_char_idx_t: ld bc,temperature ld (char_idx),bc xor a ; reset char_cnt to zero ld (char_cnt),a ret set_char_idx_p: ld bc,pressure ld (char_idx),bc xor a ; reset char_cnt to zero ld (char_cnt),a ret set_char_idx_h: ld bc,humidity ld (char_idx),bc xor a ; reset char_cnt to zero ld (char_cnt),a ret print_str_to_lcd: ; place memory address in hl first ld a,(hl) or a jr z,print_done out (lcd_data),a call lcd_delay inc hl jr print_str_to_lcd print_done: ret spin_tick: ; count down, then step the spinner one frame ld hl,(spin_cnt) dec hl ld (spin_cnt),hl ld a,h ; dec hl leaves the flags alone or l ret nz ; not time yet ld hl,spin_ticks ; reload, so the interval stays regular ld (spin_cnt),hl ld a,(rx_seen) ; did anything arrive this interval? or a ret z ; no, hold the frame and stop spinning xor a ld (rx_seen),a ; clear it for the next interval ld a,(spin_idx) inc a and 03h ; four frames, then wrap ld (spin_idx),a ld hl,spinner ld d,00h ld e,a add hl,de ; hl now points at the frame character ld a,spinner_offset out (lcd_ctrl),a ; park the cursor bottom right call lcd_delay ld a,(hl) ; fetch late, lcd_delay wrecks a and bc but not hl out (lcd_data),a call lcd_delay ret move_to_tempaddr: ld a,85h out (lcd_ctrl),a call lcd_delay ret move_to_presaddr: ld a,0c3h out (lcd_ctrl),a call lcd_delay ret move_to_humidaddr: ld a,99h out (lcd_ctrl),a call lcd_delay ret clabel: DB 'degree C',0 plabel: DB 'hPa',0 hlabel: DB '% RH',0 quitmsg: DB 'Press Q to quit',0dh,0ah,'$' spinner: DB '-','/','|',bs_char glyph_bs: DB 00h,10h,08h,04h,02h,01h,00h,00h spin_ticks equ 3000 ; idle polls per frame, tune on real hardware spin_idx DB 03h ; wraps to the first frame on the next tick spin_cnt DW spin_ticks rx_seen DB 00h ; set on every byte off the sio char_max equ 15 char_cnt DB 0 char_idx DW scratch scratch: DS 16 temperature: DS 16 humidity: DS 16 pressure: DS 16