-
+ 3C2674A38D90D63804BB65359BBF557C2ABA45C46F55158975ACB06508EC610D7DAE55E9F29CAC537BCA67D3C2E7645D3F7425FC6E3127C4304B035D5D786013
m/devices/uart.asm
(0 . 0)(1 . 449)
645 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
646 ;; ;;
647 ;; This file is part of 'M', a MIPS system emulator. ;;
648 ;; ;;
649 ;; (C) 2019 Stanislav Datskovskiy ( www.loper-os.org ) ;;
650 ;; http://wot.deedbot.org/17215D118B7239507FAFED98B98228A001ABFFC7.html ;;
651 ;; ;;
652 ;; You do not have, nor can you ever acquire the right to use, copy or ;;
653 ;; distribute this software ; Should you use this software for any purpose, ;;
654 ;; or copy and distribute it to anyone or in any manner, you are breaking ;;
655 ;; the laws of whatever soi-disant jurisdiction, and you promise to ;;
656 ;; continue doing so for the indefinite future. In any case, please ;;
657 ;; always : read and understand any software ; verify any PGP signatures ;;
658 ;; that you use - for any purpose. ;;
659 ;; ;;
660 ;; See also http://trilema.com/2015/a-new-software-licensing-paradigm . ;;
661 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
662
663 ;;;;;;;;;;;;;;;;;;;;;;;;;
664 ;; UART Console Device ;;
665 ;;;;;;;;;;;;;;;;;;;;;;;;;
666
667 ;-----------------------------------------------------------------------------
668 ; Console UART MMIO:
669 DECLARE_BUS_DEVICE UART, 0x3F8, 0x40C
670 ;-----------------------------------------------------------------------------
671
672 ;-----------------------------------------------------------------------------
673 ; Console UART IRQ:
674 ;-----------------------------------------------------------------------------
675 %define UART0_IRQ 2 ; UART0 Receiver Slave IRQ
676 ;-----------------------------------------------------------------------------
677
678 section .text
679
680 ;-----------------------------------------------------------------------------
681 _PD_Read_Word_UART: ; Word reads from UART: always 0
682 xor eax, eax
683 ret ; Fin.
684 ;-----------------------------------------------------------------------------
685 _PD_Write_Word_UART: ; Word writes to POWER do nothing!
686 ret
687 ;-----------------------------------------------------------------------------
688 _PD_Read_Byte_UART: ; Read Byte from UART
689 call _UART_Read_Reg ; Read this byte from UART
690 mov eax, edx ; Return the resulting byte.
691 ret ; Fin.
692 ;-----------------------------------------------------------------------------
693 _PD_Write_Byte_UART: ; Write Byte to UART
694 call _UART_Write_Reg ; write this byte to UART
695 ret ; Fin.
696 ;-----------------------------------------------------------------------------
697 _Device_Init_UART:
698 call _Cure_TTY ; Cure the tty:
699 call _UART_Reset ; Reset the UART
700 mov rdi, _UART_Slave ; UART Slave
701 call _Create_Thread ; Start the UART Slave Thread
702 ret
703 ;-----------------------------------------------------------------------------
704 _Device_Shutdown_UART: ; Nothing needed
705 mov dword [UART_Die], 0x01 ; Ask (unblocked) slave to die
706 call _Uncure_TTY ; bring back the old tty settings, for clean shell
707 ret
708 ;-----------------------------------------------------------------------------
709
710
711 ;-----------------------------------------------------------------------------
712 ;; UART State
713 ;-----------------------------------------------------------------------------
714 section .bss
715 UART0 resb UART_State_size ; UART-0 State
716 UART_Die resd 1 ; Shutdown Trigger
717 ;-----------------------------------------------------------------------------
718
719 section .text
720
721 ;-----------------------------------------------------------------------------
722 ; Slave Thread which connects UART0 to linux console
723 ;-----------------------------------------------------------------------------
724 _UART_Slave:
725 call _Read_Char_Blocking
726 mov dl, byte [IOBUF]
727 call _UART_Receive_Byte ; put DL in the UART receiver queue
728 SetSlaveIRQ UART0_IRQ ; trigger the slave IRQ
729 cmp dword [UART_Die], 0x01 ; time to die?
730 jne _UART_Slave ; if not, keep going.
731 jmp _exit_thread ; terminate thread
732 ;-----------------------------------------------------------------------------
733
734 ;-----------------------------------------------------------------------------
735 ; UARTism
736 ;-----------------------------------------------------------------------------
737 ;; UART State
738 struc UART_State
739 .LCR resb 1 ; Line Control
740 .LSR resb 1 ; Line Status
741 .MSR resb 1 ; Modem Status
742 .IIR resb 1 ; Interrupt ID
743 .IER resb 1 ; Interrupt Enable
744 .DLL resb 1 ;
745 .DLH resb 1 ;
746 .FCR resb 1 ; FIFO Control
747 .MCR resb 1 ; Modem Control
748 .SCR resb 1 ; Modem Control
749 ;; FIFO:
750 .FIFO resb 32 ; The FIFO itself
751 .FIFO_First resb 1 ; index of 1st byte in the FIFO
752 .FIFO_Last resb 1 ; index of last byte in the FIFO
753 .FIFO_Count resb 1 ; number of bytes in the FIFO
754 endstruc
755 ;-----------------------------------------------------------------------------
756
757 %define UART_LSR_DATA_READY 0x1
758 %define UART_LSR_FIFO_EMPTY 0x20
759 %define UART_LSR_TRANSMITTER_EMPTY 0x40
760
761 ; Enable Transmitter holding register int.
762 %define UART_IER_THRI 0x02
763
764 ; Enable receiver data interrupt
765 %define UART_IER_RDI 0x01
766
767 ; Modem status interrupt (Low priority)
768 %define UART_IIR_MSI 0x00
769
770 %define UART_IIR_NO_INT 0x01
771
772 ; Transmitter holding register empty
773 %define UART_IIR_THRI 0x02
774
775 ; Receiver data interrupt
776 %define UART_IIR_RDI 0x04
777
778 ; Receiver line status interrupt (High p.)
779 %define UART_IIR_RLSI 0x06
780
781 ; Character timeout
782 %define UART_IIR_CTI 0x0c
783
784 ; Divisor latch access bit
785 %define UART_LCR_DLAB 0x80
786
787 ; R/W: Divisor Latch Low, DLAB=1
788 %define UART_DLL 0
789
790 ; R/W: Divisor Latch High, DLAB=1
791 %define UART_DLH 1
792
793 ; R/W: Interrupt Enable Register
794 %define UART_IER 1
795
796 ; R: Interrupt ID Register
797 %define UART_IIR 2
798
799 ; W: FIFO Control Register
800 %define UART_FCR 2
801
802 ; R/W: Line Control Register
803 %define UART_LCR 3
804
805 ; W: Modem Control Register
806 %define UART_MCR 4
807
808 ; R: Line Status Register
809 %define UART_LSR 5
810
811 ; R: Modem Status Register
812 %define UART_MSR 6
813
814 ; R/W:
815 %define UART_SCR 7
816 ;-----------------------------------------------------------------------------
817
818 ;-----------------------------------------------------------------------------
819 ;; Access to UART regs:
820 %define U0r(R) byte [UART0 + UART_State. %+ R]
821 ;-----------------------------------------------------------------------------
822 ;; Clear FIFO
823 ;-----------------------------------------------------------------------------
824 _UART_FIFO_Clear:
825 mov U0r(FIFO_Last), 0
826 mov U0r(FIFO_First), 0
827 mov U0r(FIFO_Count), 0
828 ret
829 ;-----------------------------------------------------------------------------
830
831 ;-----------------------------------------------------------------------------
832 ;; Take a byte from the UART FIFO. Byte will be in DL.
833 ;-----------------------------------------------------------------------------
834 _UART_FIFO_Get:
835 xor edx, edx ; edx := 0
836 cmp U0r(FIFO_Count), 0 ; is FIFO nonempty?
837 je _UART_FIFO_Get_Nope ; ... if empty, skip and return 0
838 ;; if FIFO is nonempty:
839 xor eax, eax ; eax := 0
840 mov al, U0r(FIFO_First) ; al := fifo_first
841 mov dl, U0r(FIFO + eax) ; dl := FIFO[al]
842 inc eax ; eax := eax + 1
843 and eax, 0x1F ; eax := eax & 0x1f
844 mov U0r(FIFO_First), al ; fifo_first := al
845 mov al, U0r(FIFO_Count) ; al := fifo_count
846 test eax, eax ; is al 0 ?
847 jz _UART_FIFO_Get_Nope ; ... if 0, skip and do not update count
848 dec eax ; eax := eax - 1
849 mov U0r(FIFO_Count), al ; fifo_count := al
850 _UART_FIFO_Get_Nope:
851 ret
852 ;-----------------------------------------------------------------------------
853
854 ;-----------------------------------------------------------------------------
855 ;; Reset UART-0
856 ;-----------------------------------------------------------------------------
857 _UART_Reset:
858 mov U0r(LCR), 3
859 mov U0r(LSR), UART_LSR_TRANSMITTER_EMPTY | UART_LSR_FIFO_EMPTY
860 mov U0r(MSR), 0
861 mov U0r(IIR), UART_IIR_NO_INT
862 mov U0r(IER), 0
863 mov U0r(DLL), 0
864 mov U0r(DLH), 0
865 mov U0r(FCR), 0
866 mov U0r(MCR), 0
867 mov U0r(SCR), 0
868 call _UART_FIFO_Clear ; zap the FIFO
869 ret
870 ;-----------------------------------------------------------------------------
871 ;; Update UART IRQ
872 ;-----------------------------------------------------------------------------
873 _UART_IRQ:
874 test U0r(LSR), UART_LSR_DATA_READY
875 jz _UART_IRQ_Not_RDI
876 test U0r(IER), UART_IER_RDI
877 jz _UART_IRQ_Not_RDI
878 _UART_IRQ_RDI:
879 mov U0r(IIR), UART_IIR_RDI ; IIR=UART_IIR_RDI
880 jmp _UART_IRQ_Maybe_Pending ; See if interrupt pending
881 _UART_IRQ_Not_RDI:
882 test U0r(LSR), UART_LSR_FIFO_EMPTY
883 jz _UART_IRQ_Not_THRI
884 test U0r(IER), UART_IER_THRI
885 jz _UART_IRQ_Not_THRI
886 _UART_IRQ_THRI:
887 mov U0r(IIR), UART_IIR_THRI ; IIR=UART_IIR_THRI
888 jmp _UART_IRQ_Maybe_Pending ; See if interrupt pending
889 _UART_IRQ_Not_THRI:
890 mov U0r(IIR), UART_IIR_NO_INT ; IIR=UART_IIR_NO_INT
891 _UART_IRQ_Maybe_Pending: ; See if interrupt pending
892 ;; see if interrupt pending
893 cmp U0r(IIR), UART_IIR_NO_INT ; IIR==UART_IIR_NO_INT ?
894 jne _UART_IRQ_Pending ; if !=, interrupt is pending
895 _UART_IRQ_Not_Pending: ; interrupt is not pending:
896 ClrIRQ UART0_IRQ
897 ret
898 _UART_IRQ_Pending: ; interrupt is pending:
899 SetIRQ UART0_IRQ
900 ret
901 ;-----------------------------------------------------------------------------
902
903 ;-----------------------------------------------------------------------------
904 ;; Place a byte (DL) into the UART's receiver buffer.
905 ; TODO: mutex?
906 ;-----------------------------------------------------------------------------
907 _UART_Receive_Byte:
908 xor eax, eax ; eax := 0
909 mov al, U0r(FIFO_Last) ; al := fifo_last
910 mov U0r(FIFO + eax), dl ; FIFO[al] := dl
911 inc eax ; eax := eax + 1
912 and eax, 0x1F ; eax := eax & 0x1f
913 mov U0r(FIFO_Last), al ; fifo_last := al
914 mov al, U0r(FIFO_Count) ; eax := fifo_count
915 inc eax ; eax := eax + 1
916 mov edx, 32 ; edx := 32
917 cmp eax, edx ; is eax > 32 ?
918 cmovg eax, edx ; ... if yes, eax := 32
919 mov U0r(FIFO_Count), al ; fifo_count := al
920 or U0r(LSR), UART_LSR_DATA_READY ; set the 'ready' flag
921 call _UART_IRQ ; update IRQ
922 ret
923 ;-----------------------------------------------------------------------------
924
925 ;-----------------------------------------------------------------------------
926 ;; Read UART Register. Offset in EAX, result in DL.
927 ;-----------------------------------------------------------------------------
928 _UART_Read_Reg:
929 xor edx, edx ; result := 0
930 sub eax, UART_BASE ; Adjust for base of UART
931 and eax, 0x7 ; Adjusted offset is a 4-bit quantity
932 test U0r(LCR), UART_LCR_DLAB
933 jz _UART_Read_Reg_Not_DLAB
934 _UART_Read_Reg_DLAB:
935 cmp eax, UART_DLL ; offset=DLL?
936 je _UART_Read_Reg_DLAB_DLL
937 cmp eax, UART_DLH ; offset=DLH?
938 je _UART_Read_Reg_DLAB_DLH
939 jmp _UART_Read_Reg_Not_DLAB ; neither of these
940 _UART_Read_Reg_DLAB_DLL:
941 mov dl, U0r(DLL) ; return DLL
942 ret
943 _UART_Read_Reg_DLAB_DLH:
944 mov dl, U0r(DLH) ; return DLH
945 ret
946 _UART_Read_Reg_Not_DLAB:
947 cmp eax, 0 ; offset=0 ?
948 je _UART_Read_Reg_0
949 cmp eax, UART_IER ; offset=IER ?
950 je _UART_Read_Reg_IER
951 cmp eax, UART_MSR ; offset=MSR ?
952 je _UART_Read_Reg_MSR
953 cmp eax, UART_MCR ; offset=MCR ?
954 je _UART_Read_Reg_MCR
955 cmp eax, UART_IIR ; offset=IIR ?
956 je _UART_Read_Reg_IIR
957 cmp eax, UART_LCR ; offset=LCR ?
958 je _UART_Read_Reg_LCR
959 cmp eax, UART_LSR ; offset=LSR ?
960 je _UART_Read_Reg_LSR
961 cmp eax, UART_SCR ; offset=SCR ?
962 je _UART_Read_Reg_SCR
963 ACHTUNG "Bad UART Read Command." ; TODO: print detail
964 ret
965
966 _UART_Read_Reg_0:
967 cmp U0r(FIFO_Count), 0 ; is FIFO nonempty?
968 je _UART_Read_Reg_0_Empty ; ... if empty, skip
969 ;; FIFO is not empty:
970 call _UART_FIFO_Get ; get byte from FIFO
971 and U0r(LSR), ~UART_LSR_DATA_READY ; lower 'ready' flag
972 cmp U0r(FIFO_Count), 0 ; is FIFO still nonempty?
973 je _UART_Read_Reg_0_Empty ; ... if empty, leave 'ready' flag down
974 ;; FIFO still has data:
975 or U0r(LSR), UART_LSR_DATA_READY ; else, raise the 'ready' flag.
976 _UART_Read_Reg_0_Empty:
977 call _UART_IRQ ; update IRQ
978 ret ; return DL
979
980 _UART_Read_Reg_IER:
981 mov dl, U0r(IER)
982 and edx, 0xF
983 ret ; return IER & 0xF
984
985 _UART_Read_Reg_MSR:
986 mov dl, U0r(MSR)
987 ret ; return MSR
988
989 _UART_Read_Reg_MCR:
990 mov dl, U0r(MCR)
991 ret ; return MCR
992
993 _UART_Read_Reg_IIR:
994 mov dl, U0r(IIR)
995 ret ; return IIR
996
997 _UART_Read_Reg_LCR:
998 mov dl, U0r(LCR)
999 ret ; return LCR
1000
1001 _UART_Read_Reg_LSR:
1002 cmp U0r(FIFO_Count), 0 ; is FIFO nonempty?
1003 je _UART_Read_Reg_LSR_Empty ; if empty, go to 'empty'
1004 ;; FIFO is not empty:
1005 or U0r(LSR), UART_LSR_DATA_READY ; raise the 'ready' flag.
1006 jmp _UART_Read_Reg_LSR_Done ; return
1007 _UART_Read_Reg_LSR_Empty:
1008 and U0r(LSR), ~UART_LSR_DATA_READY ; lower the 'ready' flag
1009 _UART_Read_Reg_LSR_Done:
1010 mov dl, U0r(LSR)
1011 ret ; return LSR
1012
1013 _UART_Read_Reg_SCR:
1014 mov dl, U0r(SCR)
1015 ret ; return SCR
1016 ;-----------------------------------------------------------------------------
1017
1018 ;-----------------------------------------------------------------------------
1019 ;; Write UART Register. Offset in EAX, payload in DL.
1020 ;-----------------------------------------------------------------------------
1021 _UART_Write_Reg:
1022 sub eax, UART_BASE ; Adjust for base of UART
1023 and eax, 0x7 ; Adjusted offset is a 4-bit quantity
1024 test U0r(LCR), UART_LCR_DLAB
1025 jz _UART_Write_Reg_Not_DLAB
1026 _UART_Write_Reg_DLAB:
1027 cmp eax, UART_DLL ; offset=DLL?
1028 je _UART_Write_Reg_DLAB_DLL
1029 cmp eax, UART_DLH ; offset=DLH?
1030 je _UART_Write_Reg_DLAB_DLH
1031 jmp _UART_Write_Reg_Not_DLAB ; neither of these
1032 _UART_Write_Reg_DLAB_DLL:
1033 mov U0r(DLL), dl ; write DLL
1034 ret
1035 _UART_Write_Reg_DLAB_DLH:
1036 mov U0r(DLH), dl ; write DLH
1037 ret
1038 _UART_Write_Reg_Not_DLAB:
1039 cmp eax, 0 ; offset=0 ?
1040 je _UART_Write_Reg_0
1041 cmp eax, UART_IER ; offset=IER?
1042 je _UART_Write_Reg_IER
1043 cmp eax, UART_FCR ; offset=FCR?
1044 je _UART_Write_Reg_FCR
1045 cmp eax, UART_LCR ; offset=LCR?
1046 je _UART_Write_Reg_LCR
1047 cmp eax, UART_MCR ; offset=MCR?
1048 je _UART_Write_Reg_MCR
1049 cmp eax, UART_SCR ; offset=SCR?
1050 je _UART_Write_Reg_SCR
1051 ACHTUNG "Bad UART Write Command." ; TODO: print detail
1052 ret
1053
1054 _UART_Write_Reg_0:
1055 and U0r(LSR), ~UART_LSR_FIFO_EMPTY
1056 test U0r(MCR), (1 << 4)
1057 jz _UART_Write_Reg_0_Not_Loopback
1058 _UART_Write_Reg_0_Loopback:
1059 call _UART_Receive_Byte ; put DL in the receiver FIFO
1060 jmp _UART_Write_Reg_0_Fin ; wrap up
1061 _UART_Write_Reg_0_Not_Loopback: ; if NOT loopback :
1062 call _Write_Char ; ... then write char in DL to console
1063 _UART_Write_Reg_0_Fin:
1064 or U0r(LSR), UART_LSR_FIFO_EMPTY ; mark the send buffer as empty
1065 call _UART_IRQ ; refresh IRQ
1066 ret ; fin
1067
1068 _UART_Write_Reg_IER:
1069 and edx, 0xF ; 4-bit qty
1070 mov U0r(IER), dl ; IER := dl & 0xF
1071 call _UART_IRQ ; refresh IRQ
1072 ret ; fin
1073
1074 _UART_Write_Reg_FCR:
1075 mov U0r(FCR), dl ; FCR := dl
1076 test dl, 0x2 ; if FCR & 2:
1077 jz _UART_Write_Reg_FCR_No_Clear
1078 call _UART_FIFO_Clear ; ... then zap the FIFO.
1079 _UART_Write_Reg_FCR_No_Clear:
1080 ret
1081
1082 _UART_Write_Reg_LCR:
1083 mov U0r(LCR), dl ; LCR := dl
1084 ret ; fin
1085
1086 _UART_Write_Reg_MCR:
1087 mov U0r(MCR), dl ; MCR := dl
1088 ret ; fin
1089
1090 _UART_Write_Reg_SCR:
1091 mov U0r(SCR), dl ; SCR := dl
1092 ret ; fin
1093 ;-----------------------------------------------------------------------------