Added commands to build op code tables for instructions with one or no parameters.
This commit is contained in:
+137
-195
@@ -1,16 +1,12 @@
|
||||
\ExplSyntaxOn
|
||||
% Opcode, Opcode Name, Description, Flags Affected, Options
|
||||
% Maybe have an optional argument to relabel the operands Source and Destination
|
||||
\NewDocumentCommand{\createoptable}{ m m m m m }{
|
||||
\NewDocumentCommand{\createoptabletwoparam}{ m m m m m }{
|
||||
\clist_clear_new:N \l_options
|
||||
|
||||
\clist_set:Nn \l_options {#3}
|
||||
|
||||
\seq_clear:N \l_tmpa_seq
|
||||
|
||||
\tl_set:Nn \l_tmpa_seq { \begin{tabularx}{\textwidth}{ | c | X | c | c | } \hline }
|
||||
|
||||
\tl_put_right:Nn \l_tmpa_seq { Opcode & Instruction & Operand One & Operand Two \\ \hline }
|
||||
\__start_op_table:
|
||||
|
||||
\clist_map_inline:Nn \l_options {
|
||||
|
||||
@@ -20,29 +16,70 @@
|
||||
}{1}{
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2~/r,~[/r] & r & [r] \\ \hline }
|
||||
}{2}{
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2 [/r],~/r2 & [r] & r \\ \hline }
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2~[/r],~/r2 & [r] & r \\ \hline }
|
||||
}{5}{
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2~/r,~[imm] & r & [imm] \\ \hline }
|
||||
}{8}{
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2~imm,~/r & imm & r \\ \hline }
|
||||
}{9}{
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2 ~imm, [r] & imm & [r] \\ \hline }
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2 ~imm,~[r] & imm & [r] \\ \hline }
|
||||
}{A}{
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2~[imm], /r & imm & r \\ \hline }
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2~[imm],~/r & imm & r \\ \hline }
|
||||
}{C}{
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2~imm,~imm & imm & imm \\ \hline }
|
||||
}{D}{
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2 ~imm,~[imm] & imm & [imm] \\ \hline }
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1~##1 & #2~imm,~[imm] & imm & [imm] \\ \hline }
|
||||
}
|
||||
}{ \tl_put_right:Nn \l_tmpa_seq {#1~##1 & INVALID & -- & -- \\ \hline} }
|
||||
|
||||
}
|
||||
|
||||
\tl_put_right:Nn \l_tmpa_seq { \end{tabularx} }
|
||||
\tl_put_right:Nn \l_tmpa_seq { \paragraph{Description} #5 }
|
||||
\tl_put_right:Nn \l_tmpa_seq { \paragraph{Flags Affected} #4 }
|
||||
\__end_op_table:nn {#5} {#4}
|
||||
|
||||
\tl_use:N \l_tmpa_seq
|
||||
}
|
||||
|
||||
\NewDocumentCommand{\createoptableoneparam}{ m m m m m}{
|
||||
\__start_op_table:
|
||||
|
||||
\str_case:nnF{#3} {
|
||||
{imm}{
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1 & #2~imm & imm & None \\ \hline }
|
||||
}{reg}{
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1 & #2~/r & r & None \\ \hline }
|
||||
}
|
||||
}{ \tl_put_right:Nn \l_tmpa_seq {#1~##1 & INVALID & -- & -- \\ \hline} }
|
||||
|
||||
\__end_op_table:nn {#5} {#4}
|
||||
|
||||
\tl_use:N \l_tmpa_seq
|
||||
}
|
||||
|
||||
\NewDocumentCommand{\createoptablenoparam}{ m m m m }{
|
||||
|
||||
\__start_op_table:
|
||||
|
||||
\tl_put_right:Nn \l_tmpa_seq { #1 & #2 & None & None \\ \hline }
|
||||
|
||||
\__end_op_table:nn {#4} {#3}
|
||||
|
||||
\tl_use:N \l_tmpa_seq
|
||||
}
|
||||
|
||||
\cs_new:Nn \__start_op_table: {
|
||||
\seq_clear:N \l_tmpa_seq
|
||||
|
||||
\tl_set:Nn \l_tmpa_seq { \begin{tabularx}{\textwidth}{ | c | X | c | c | } \hline }
|
||||
|
||||
\tl_put_right:Nn \l_tmpa_seq { Opcode & Instruction & Operand One & Operand Two \\ \hline }
|
||||
}
|
||||
|
||||
\cs_new:Npn \__end_op_table:nn #1 #2 {
|
||||
\tl_put_right:Nn \l_tmpa_seq { \end{tabularx} }
|
||||
\tl_put_right:Nn \l_tmpa_seq { \paragraph{Description} #1 }
|
||||
\tl_put_right:Nn \l_tmpa_seq { \paragraph{Flags Affected} #2 }
|
||||
}
|
||||
|
||||
\ExplSyntaxOff
|
||||
|
||||
\chapter{Instruction Set Architecture}
|
||||
@@ -133,248 +170,153 @@ In general instructions are variable length depending on the number of arguments
|
||||
\section{Instructions}
|
||||
The /r represents a byte that specifies a register. /immX is for the immediate data type, where \textit{X} defines the width, which are 8, 16, or 32 bits. An address is defined as /addrX with the \textit{X} denoting the size of the data that will be written which can be 8, 16 or 32 bits, with the address itself always being 32-bits wide.
|
||||
\subsection{Add}
|
||||
\createoptable{01}{add}{0, 8}{ OF and ZF. }
|
||||
\createoptabletwoparam{01}{add}{0, 8}{ OF and ZF. }
|
||||
{
|
||||
Adds two unsigned values together, the source (operand one) and the destination (operand two), storing the result in destination. The overflow flag (OF) is set if the result is less than the source and the zero flag (ZF) is set when the result of addition is zero.
|
||||
}
|
||||
\subsection{Sub}
|
||||
\createoptable{02}{sub}{0, 8}{ UF and ZF. }
|
||||
\createoptabletwoparam{02}{sub}{0, 8}{ UF and ZF. }
|
||||
{
|
||||
Subtracts two unsigned values, the source (operand one) and the destination (operand two), storing the result in destination. The underflow flag (UF) is set if the result is greater than the source and the zero flag (ZF) is set when the result of subtraction is zero.
|
||||
}
|
||||
\subsection{Mul}
|
||||
\createoptable{03}{mul}{0, 8}{ OF and ZF. }
|
||||
\createoptabletwoparam{03}{mul}{0, 8}{ OF and ZF. }
|
||||
{
|
||||
Multiplies two unsigned values together, the source (operand one) and the destination (operand two), storing the result in destination. The overflow flag (OF) is set if the result is less than the source and the zero flag (ZF) is set when the result of addition is zero.
|
||||
}
|
||||
\subsection{Div}
|
||||
\createoptable{04}{div}{0, 8}{ UF and ZF. }
|
||||
\createoptabletwoparam{04}{div}{0, 8}{ UF and ZF. }
|
||||
{
|
||||
Divides two unsigned values, the source (operand one) and the destination (operand two), storing the result in destination. The underflow flag (UF) is set if the result is greater than the source and the zero flag (ZF) is set when the result of subtraction is zero.
|
||||
}
|
||||
\subsection{Mov}\label{sec:mov}
|
||||
\createoptable{05}{mov}{0, 1, 2, 5, 8, 9, A, D}{ None. }
|
||||
\createoptabletwoparam{05}{mov}{0, 1, 2, 5, 8, 9, A, D}{ None. }
|
||||
{
|
||||
Copies the source (operand one) value to the destination (operand two).
|
||||
}
|
||||
\subsection{AND}
|
||||
\createoptable{06}{and}{0, 8}{ ZF. }
|
||||
\createoptabletwoparam{06}{and}{0, 8}{ ZF. }
|
||||
{
|
||||
Bitwise AND's the source (operand one) with the destination (operand two) storing the result in the destination. If the result of the operation is zero then the Zero Flag (ZF) is set.
|
||||
}
|
||||
\subsection{OR}
|
||||
\createoptable{07}{or}{0, 8}{ ZF. }
|
||||
\createoptabletwoparam{07}{or}{0, 8}{ ZF. }
|
||||
{
|
||||
Bitwise OR's the source (operand one) with the destination (operand two) storing the result in the destination. If the result of the operation is zero then the Zero Flag (ZF) is set.
|
||||
}
|
||||
\subsection{XOR}
|
||||
\createoptable{08}{xor}{0, 8}{ ZF. }
|
||||
\createoptabletwoparam{08}{xor}{0, 8}{ ZF. }
|
||||
{
|
||||
Bitwise exclusive OR's (XOR) the source (operand one) with the destination (operand two) storing the result in the destination. If the result of the operation is zero then the Zero Flag (ZF) is set.
|
||||
}
|
||||
\subsection{NOT}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
09 & xor /r & /r & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Bit-wise NOT's the source (operand one) in place.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptableoneparam{09}{not}{reg}{ None. }
|
||||
{
|
||||
Bit-wise NOT's the source (operand one) in place.
|
||||
}
|
||||
\subsection{SHL}
|
||||
\createoptable{0A}{shl}{8}{ OF and ZF. }
|
||||
\createoptabletwoparam{0A}{shl}{8}{ OF and ZF. }
|
||||
{
|
||||
Shifts the destination (operand two) left \textit{operand one} times in place. If the source is smaller than before the operation the overflow flag (OF) is set. If the result of the operation is zero than the zero flag (ZF) is set.
|
||||
}
|
||||
\subsection{SHR}
|
||||
\createoptable{0B}{shr}{8}{ UF and ZF. }
|
||||
\createoptabletwoparam{0B}{shr}{8}{ UF and ZF. }
|
||||
{
|
||||
Shifts the destination (operand two) right \textit{operand one} times in place. If the source is larger than before the operation the underflow flag (UF) is set. If the result of the operation is zero than the zero flag (ZF) is set.
|
||||
}
|
||||
\subsection{NOP}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
0C & nop & None & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Performs no operation except for incrementing the program counter by one.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptablenoparam{0C}{nop}{ None. }
|
||||
{
|
||||
Performs no operation except for incrementing the program counter by one.
|
||||
}
|
||||
\subsection{CMP}
|
||||
\createoptable{0D}{cmp}{0, 8}{ OF, UF and ZF. }
|
||||
\createoptabletwoparam{0D}{cmp}{0, 8}{ OF, UF and ZF. }
|
||||
{
|
||||
Compares \textit{operand one} to \textit{operand two} by subtracting \textit{operand one} from \textit{operand two}, updating the status register with the results. If \textit{operand one} is larger than the result, and the result is not zero, the overflow flag (OF) is set. If \textit{operand one} is smaller than the result, and the result is not zero, the underflow flag (UF) is set. And if the result of the subtraction is zero the zero flag (ZF) is set.
|
||||
}
|
||||
\subsection{JMP}\label{sec:jmp}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
0E & jmp /imm32 & /imm32 & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Jumps unconditionally to the absolute memory address specified in \textit{operand one}.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptableoneparam{0E}{jmp}{imm}{ None. }
|
||||
{
|
||||
Jumps unconditionally to the absolute memory address specified in \textit{operand one}.
|
||||
}
|
||||
\subsection{JZ}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
0F & jz /imm32 & /imm32 & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Jumps to the absolute memory address specified in \textit{operand one} if the zero flag (ZF) is zet.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptableoneparam{0F}{jz}{imm}{ None. }
|
||||
{
|
||||
Jumps to the absolute memory address specified in \textit{operand one} if the zero flag (ZF) is zet.
|
||||
}
|
||||
\subsection{JG}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
10 & jg /imm32 & /imm32 & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Jumps to the absolute memory address specified in \textit{operand one} if the overflow flag (OF) is zet.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptableoneparam{10}{jg}{imm}{ None. }
|
||||
{
|
||||
Jumps to the absolute memory address specified in \textit{operand one} if the overflow flag (OF) is zet.
|
||||
}
|
||||
\subsection{JL}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
11 & jl /imm32 & /imm32 & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Jumps to the absolute memory address specified in \textit{operand one} if the underflow flag (UF) is zet.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptableoneparam{11}{jl}{imm}{ None. }
|
||||
{
|
||||
Jumps to the absolute memory address specified in \textit{operand one} if the underflow flag (UF) is zet.
|
||||
}
|
||||
%Maybe these in/out instructions could have a mode setting for choosing the width of the data written to the port.
|
||||
\subsection{Outb}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
12 & outb /imm8, /imm8 & /imm8 & /imm8 \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Writes a byte of data to the port number specified in the destination (operand two).
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptabletwoparam{12}{outb}{C}{ None. }
|
||||
{
|
||||
Writes a byte of data to the port number specified in the destination (operand two).
|
||||
}
|
||||
\subsection{Inb}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
13 & inb /imm8, /r & /imm8 & /r \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Reads a byte of data from the port number in source (operand one) to the destination (operand two) register.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptabletwoparam{13}{inb}{8}{ None. }
|
||||
{
|
||||
Reads a byte of data from the port number in source (operand one) to the destination (operand two) register.
|
||||
}
|
||||
\subsection{HLT}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
14 & hlt & None & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Halts the processor preventing it from executing the next instruction until an interrupt is received.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptablenoparam{14}{hlt}{ None. }
|
||||
{
|
||||
Halts the processor preventing it from executing the next instruction until an interrupt is received.
|
||||
}
|
||||
\subsection{CLI}\label{sec:cli}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
15 & cli & None & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Clears interrupts, preventing the processor from responding to interrupts.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptablenoparam{15}{cli}{ None. }
|
||||
{
|
||||
Clears interrupts, preventing the processor from responding to interrupts.
|
||||
}
|
||||
\subsection{ENI}\label{sec:eni}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
16 & eni & None & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Enables interrupts, allowing the processor to respond to interrupts.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptablenoparam{16}{eni}{ None. }
|
||||
{
|
||||
Enables interrupts, allowing the processor to respond to interrupts.
|
||||
}
|
||||
\subsection{INT}\label{sec:int}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
17 & int /imm8 & /imm8 & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Triggers a software interrupt of type \textit{operand one} causing the flow of execution to jump to the registered subroutine in the Interrupt Vector Table at index \textit{operand one}.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptableoneparam{17}{int}{imm}{ None. }
|
||||
{
|
||||
Triggers a software interrupt of type \textit{operand one} causing the flow of execution to jump to the registered subroutine in the Interrupt Vector Table at index \textit{operand one}.
|
||||
}
|
||||
\subsection{LIVT}\label{sec:livt}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
18 & livt /imm32 & /imm32 & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Registers a 1024 byte block of memory starting at \textit{operand one} as the Interrupt Vector Table.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptableoneparam{18}{livt}{imm}{ None. }
|
||||
{
|
||||
Registers a 1024 byte block of memory starting at \textit{operand one} as the Interrupt Vector Table.
|
||||
}
|
||||
\subsection{PUSHA}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
19 & pusha & None & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Pushes all general purpose registers to the stack in order starting from \textit{r1} to \textit{r8}.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptablenoparam{19}{pusha}{ None. }
|
||||
{
|
||||
Pushes all general purpose registers to the stack in order starting from \textit{r1} to \textit{r32}.
|
||||
}
|
||||
\subsection{POPA}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
20 & popa & None & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Pops all general purpose registers from the stack, restoring them, in the reverse order of \textit{r8} to \textit{r1}.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptablenoparam{20}{popa}{ None. }
|
||||
{
|
||||
Pops all general purpose registers from the stack, restoring them, in the reverse order of \textit{r32} to \textit{r1}.
|
||||
}
|
||||
\subsection{CALL}\label{sec:call}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
21 & call & None & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Calls a subroutine pushing the Program Counter (PC), plus one to return execution to the next opcode, to the stack.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptableoneparam{21}{call}{imm}{ None. }
|
||||
{
|
||||
Calls a subroutine pushing the Program Counter (PC), plus one to return execution to the next opcode, to the stack.
|
||||
}
|
||||
\subsection{RET}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
22 & ret & None & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Returns from the subroutine, popping the stack to restore the return address into the Program Counter (PC).
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptablenoparam{22}{ret}{ None. }
|
||||
{
|
||||
Returns from the subroutine, popping the stack to restore the return address into the Program Counter (PC).
|
||||
}
|
||||
\subsection{PUSH}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
23 & push /r & /r & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Pushes the contents of register \textit{Operand One} onto the top of the stack.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptableoneparam{23}{push}{reg}{ None. }
|
||||
{
|
||||
Pushes the contents of register \textit{Operand One} onto the top of the stack.
|
||||
}
|
||||
\subsection{POP}
|
||||
\begin{tabularx}{\textwidth}{ | c | X | c | c | }
|
||||
\hline
|
||||
Opcode & Instruction & Operand One & Operand Two \\
|
||||
\hline
|
||||
24 & pop /r & /r & None \\
|
||||
\hline
|
||||
\end{tabularx}
|
||||
\paragraph{Description} Pops 32-bits from the top of the stack into the register \textit{Operand One}.
|
||||
\paragraph{Flags Affected} None.
|
||||
\createoptableoneparam{24}{pop}{reg}{ None. }
|
||||
{
|
||||
Pops 32-bits from the top of the stack into the register \textit{Operand One}.
|
||||
}
|
||||
Reference in New Issue
Block a user