Iffat Jahan
CV

Assembly · In the CV

Table Builder and Sorter in 8086 Assembly

Wrote a program that builds a data table from user input and sorts it by any column.

  • 8086 Assembly
  • DOS interrupts (INT 21h)

About

Assembly code for the 8086 emulator that builds a table of 1 to 10 columns and 1 to 50 rows, which can be sorted right after it is created. Numeric data is sorted in descending order and text data in ascending order.

Source code

tablesort.asm8086 assembly · 1052 lines · 21.0 KB
.MODEL SMALL
.STACK 100H

.DATA
    MAX_ROWS     EQU 50
    MAX_COLS     EQU 10
    CELL_SIZE    EQU 9       ; 8 bytes + 1 for '$' terminator

    MSG_WELCOME      DB 13,10,'=== TABLE MANAGEMENT SYSTEM ===',13,10,'$'
    MSG_COL_COUNT    DB 13,10,'Enter number of columns (1-10): $'
    MSG_ROW_COUNT    DB 13,10,'Enter number of rows (1-50): $'
    MSG_COL_NAME     DB 13,10,'Enter name for column $'
    MSG_COL_TYPE     DB '=> Type (S)tring or (N)umeric? $'
    MSG_ROW_DATA     DB 13,10,'=== Enter Row Data ===',13,10,'$'
    MSG_CELL_PROMPT  DB 13,10,'Enter value for cell $'
    ERR_INVALID_TYPE DB 13,10,'ERROR: Invalid type! Use S or N',13,10,'$'
    ERR_EMPTY_CELL   DB 13,10,'ERROR: Cell cannot be empty!',13,10,'$'
    MSG_DISPLAY      DB 13,10,'=== CURRENT TABLE ===',13,10,'$'  
    ERR_INVALID_NUM  DB 13,10,'ERROR: Invalid number! Please try again.',13,10,'$'
    MSG_SORT_PROMPT  DB 13,10,'Enter column number to sort (1-$'
    MSG_SORTED       DB 13,10,'=== SORTED TABLE ===',13,10,'$'
    SPACER           DB ' | $'
    NEWLINE          DB 13,10,'$'
    DASH_LINE        DB '-------------------------------------------------',13,10,'$'
    COLON_SPACE      DB ': $'
    CLOSE_PAREN      DB '): $'
    DOT              DB '.$'  
    
    COL_TYPES    DB MAX_COLS DUP(0)    ; 0 = numeric, 1 = string
    COL_NAMES    DB MAX_COLS * CELL_SIZE DUP('$')    ; Column names storage
    TABLE_DATA   DB MAX_ROWS * MAX_COLS * CELL_SIZE DUP('$')  ; Table data storage
    TEMP_BUFFER  DB CELL_SIZE DUP(0)  ; Temporary buffer
    INPUT_BUFFER DB 10 DUP(0)         ; Input buffer for numbers

    ROW_SIZE     DW 0                 ; Size of each row in bytes
    NUM_COLS     DB 0                 ; Number of columns
    NUM_ROWS     DB 0                 ; Number of rows
    CURRENT_COL  DB 0                 ; Current column index
    CURRENT_ROW  DB 0                 ; Current row index
    SORT_COL     DB 0                 ; Column to sort by

.CODE
MAIN PROC
    MOV AX, @DATA
    MOV DS, AX
    MOV ES, AX

    LEA DX, MSG_WELCOME     ; Display welcome message
    MOV AH, 09H
    INT 21H
                            ; Get user input and process data
    CALL GET_COLUMN_COUNT
    CALL GET_COLUMN_METADATA
    CALL GET_ROW_COUNT
    CALL INPUT_TABLE_DATA
    
    ; Display initial table
    LEA DX, MSG_DISPLAY
    MOV AH, 09H
    INT 21H
    CALL DISPLAY_TABLE
    CALL SORT_TABLE_HANDLER   ; Handle sorting and display sorted table
    ; Exit program
    MOV AH, 4CH
    INT 21H
MAIN ENDP

GET_COLUMN_COUNT PROC
GET_COL_RETRY:
    ; Prompt for column count
    LEA DX, MSG_COL_COUNT
    MOV AH, 09H
    INT 21H

    ; Get number input
    CALL GET_NUMBER_INPUT
    CMP AX, 1
    JB GET_COL_INVALID
    CMP AX, MAX_COLS
    JA GET_COL_INVALID
    
    ; Store valid column count
    MOV NUM_COLS, AL
    LEA DX, NEWLINE
    MOV AH, 09H
    INT 21H
    RET

GET_COL_INVALID:
    ; Handle invalid input
    LEA DX, ERR_INVALID_NUM
    MOV AH, 09H
    INT 21H
    JMP GET_COL_RETRY
GET_COLUMN_COUNT ENDP

GET_COLUMN_METADATA PROC
    MOV CURRENT_COL, 0

COL_META_LOOP:
    ; Loop through columns
    MOV AL, CURRENT_COL
    CMP AL, NUM_COLS
    JAE COL_META_DONE

    ; Display column number prompt
    LEA DX, MSG_COL_NAME
    MOV AH, 09H
    INT 21H

    ; Display current column number
    MOV AL, CURRENT_COL
    ADD AL, '1'
    MOV DL, AL
    MOV AH, 02H
    INT 21H

    LEA DX, COLON_SPACE
    MOV AH, 09H
    INT 21H

    ; Get column name
    CALL CALCULATE_COL_NAME_ADDR
    MOV DI, BX
    CALL GET_STRING_INPUT

TYPE_RETRY:
    ; Get column type
    LEA DX, MSG_COL_TYPE
    MOV AH, 09H
    INT 21H

    ; Read character and convert to uppercase
    MOV AH, 01H
    INT 21H
    CMP AL, 'a'
    JB CHECK_TYPE
    CMP AL, 'z'
    JA CHECK_TYPE
    SUB AL, 32

CHECK_TYPE:
    ; Validate type input
    CMP AL, 'S'
    JE SET_STRING_TYPE
    CMP AL, 'N'
    JE SET_NUMERIC_TYPE
    
    ; Invalid type handling
    LEA DX, ERR_INVALID_TYPE
    MOV AH, 09H
    INT 21H
    JMP TYPE_RETRY

SET_STRING_TYPE:
    ; Set column as string type
    MOV AL, CURRENT_COL
    XOR AH, AH
    MOV BX, OFFSET COL_TYPES
    ADD BX, AX
    MOV BYTE PTR [BX], 1
    LEA DX, NEWLINE
    MOV AH, 09H
    INT 21H
    JMP NEXT_COL_META

SET_NUMERIC_TYPE:
    ; Set column as numeric type
    MOV AL, CURRENT_COL
    XOR AH, AH
    MOV BX, OFFSET COL_TYPES
    ADD BX, AX
    MOV BYTE PTR [BX], 0
    LEA DX, NEWLINE
    MOV AH, 09H
    INT 21H

NEXT_COL_META:
    ; Move to next column
    INC CURRENT_COL
    JMP COL_META_LOOP

COL_META_DONE:
    RET
GET_COLUMN_METADATA ENDP

GET_ROW_COUNT PROC
GET_ROW_RETRY:
    ; Prompt for row count
    LEA DX, MSG_ROW_COUNT
    MOV AH, 09H
    INT 21H

    ; Get number input
    CALL GET_NUMBER_INPUT
    CMP AX, 1
    JB GET_ROW_INVALID
    CMP AX, MAX_ROWS
    JA GET_ROW_INVALID

    ; Store valid row count
    MOV NUM_ROWS, AL
    LEA DX, NEWLINE
    MOV AH, 09H
    INT 21H
    RET

GET_ROW_INVALID:
    ; Handle invalid row count
    LEA DX, ERR_INVALID_NUM
    MOV AH, 09H
    INT 21H
    JMP GET_ROW_RETRY
GET_ROW_COUNT ENDP

INPUT_TABLE_DATA PROC
    MOV CURRENT_ROW, 0

ROW_INPUT_LOOP:
    ; Loop through rows
    MOV AL, CURRENT_ROW
    CMP AL, NUM_ROWS
    JAE INPUT_DATA_COMPLETE

    ; Display row header
    LEA DX, MSG_ROW_DATA
    MOV AH, 09H
    INT 21H

    ; Display row number
    LEA DX, DOT
    MOV AH, 09H
    INT 21H
    MOV AL, CURRENT_ROW
    INC AL
    CALL PRINT_NUMBER
    LEA DX, NEWLINE
    MOV AH, 09H
    INT 21H

    MOV CURRENT_COL, 0

COL_INPUT_LOOP:
    ; Loop through columns
    MOV AL, CURRENT_COL
    CMP AL, NUM_COLS
    JAE ROW_INPUT_COMPLETE

    ; Display cell prompt
    LEA DX, MSG_CELL_PROMPT
    MOV AH, 09H
    INT 21H

    ; Display column letter
    MOV AL, CURRENT_COL
    ADD AL, 'A'
    MOV DL, AL
    MOV AH, 02H
    INT 21H

    MOV DL, ' '
    MOV AH, 02H
    INT 21H

    ; Display column name
    MOV DL, '('
    INT 21H
    CALL CALCULATE_COL_NAME_ADDR
    MOV DX, BX
    MOV AH, 09H
    INT 21H
    MOV DL, ')'
    MOV AH, 02H
    INT 21H

    LEA DX, COLON_SPACE
    MOV AH, 09H
    INT 21H

    ; Calculate cell address
    CALL CALCULATE_CELL_ADDR
    MOV DI, BX

    ; Check column type and get appropriate input
    MOV AL, CURRENT_COL
    XOR AH, AH
    MOV BX, OFFSET COL_TYPES
    ADD BX, AX
    CMP BYTE PTR [BX], 0
    JE INPUT_NUMERIC_CELL

    ; String input
    CALL GET_STRING_INPUT
    JMP NEXT_COL_INPUT

INPUT_NUMERIC_CELL:
    ; Numeric input
    CALL GET_NUMERIC_INPUT

NEXT_COL_INPUT:
    ; Next column
    INC CURRENT_COL
    JMP COL_INPUT_LOOP

ROW_INPUT_COMPLETE:
    ; Next row
    INC CURRENT_ROW
    JMP ROW_INPUT_LOOP

INPUT_DATA_COMPLETE:
    RET
INPUT_TABLE_DATA ENDP

DISPLAY_TABLE PROC
    ; Display table header and rows
    CALL PRINT_TABLE_HEADER
    CALL PRINT_TABLE_ROWS
    RET
DISPLAY_TABLE ENDP

PRINT_TABLE_HEADER PROC
    ; Print top border
    LEA DX, DASH_LINE
    MOV AH, 09H
    INT 21H

    MOV CURRENT_COL, 0

HEADER_LOOP:
    ; Loop through columns to print headers
    MOV AL, CURRENT_COL
    CMP AL, NUM_COLS
    JAE HEADER_COMPLETE

    ; Print spacer
    LEA DX, SPACER
    MOV AH, 09H
    INT 21H

    ; Print column name
    CALL CALCULATE_COL_NAME_ADDR
    MOV DX, BX
    MOV AH, 09H
    INT 21H

    ; Next column
    INC CURRENT_COL
    JMP HEADER_LOOP

HEADER_COMPLETE:
    ; Complete header line
    LEA DX, SPACER
    MOV AH, 09H
    INT 21H
    LEA DX, NEWLINE
    MOV AH, 09H
    INT 21H
    RET
PRINT_TABLE_HEADER ENDP

PRINT_TABLE_ROWS PROC
    MOV CURRENT_ROW, 0

PRINT_ROW_LOOP:
    ; Loop through rows
    MOV AL, CURRENT_ROW
    CMP AL, NUM_ROWS
    JAE PRINT_ROWS_COMPLETE

    ; Print row separator
    LEA DX, DASH_LINE
    MOV AH, 09H
    INT 21H

    MOV CURRENT_COL, 0

PRINT_COL_LOOP:
    ; Loop through columns
    MOV AL, CURRENT_COL
    CMP AL, NUM_COLS
    JAE PRINT_ROW_COMPLETE

    ; Print spacer
    LEA DX, SPACER
    MOV AH, 09H
    INT 21H

    ; Get cell address
    CALL CALCULATE_CELL_ADDR
    MOV DI, BX

    ; Check column type for display formatting
    MOV AL, CURRENT_COL
    XOR AH, AH
    MOV BX, OFFSET COL_TYPES
    ADD BX, AX
    CMP BYTE PTR [BX], 0
    JE PRINT_NUMERIC_CELL

    ; Print string cell
    MOV DX, DI
    MOV AH, 09H
    INT 21H
    JMP NEXT_PRINT_COL

PRINT_NUMERIC_CELL:
    ; Print numeric cell
    MOV DX, DI
    MOV AH, 09H
    INT 21H

NEXT_PRINT_COL:
    ; Next column
    INC CURRENT_COL
    JMP PRINT_COL_LOOP

PRINT_ROW_COMPLETE:
    ; Complete row line
    LEA DX, SPACER
    MOV AH, 09H
    INT 21H
    LEA DX, NEWLINE
    MOV AH, 09H
    INT 21H

    ; Next row
    INC CURRENT_ROW
    JMP PRINT_ROW_LOOP

PRINT_ROWS_COMPLETE:
    ; Print bottom border
    LEA DX, DASH_LINE
    MOV AH, 09H
    INT 21H
    RET
PRINT_TABLE_ROWS ENDP

SORT_TABLE_HANDLER PROC
    ; Prompt for sort column
    LEA DX, MSG_SORT_PROMPT
    MOV AH, 09H
    INT 21H

    ; Display valid column range
    MOV AL, NUM_COLS
    CALL PRINT_NUMBER

    LEA DX, CLOSE_PAREN
    MOV AH, 09H
    INT 21H

SORT_INPUT_RETRY:
    ; Get sort column input
    CALL GET_NUMBER_INPUT
    CMP AX, 1
    JB SORT_INPUT_INVALID
    CMP AL, NUM_COLS
    JA SORT_INPUT_INVALID

    ; Store sort column (0-indexed)
    DEC AL
    MOV SORT_COL, AL

    LEA DX, NEWLINE
    MOV AH, 09H
    INT 21H

    ; Perform sorting
    CALL BUBBLE_SORT

    ; Display sorted table
    LEA DX, MSG_SORTED
    MOV AH, 09H
    INT 21H
    CALL DISPLAY_TABLE
    RET

SORT_INPUT_INVALID:
    ; Handle invalid sort column
    LEA DX, ERR_INVALID_NUM
    MOV AH, 09H
    INT 21H
    JMP SORT_INPUT_RETRY
SORT_TABLE_HANDLER ENDP

BUBBLE_SORT PROC
    ; Calculate row size (columns * cell size)
    MOV AL, NUM_COLS
    MOV BL, CELL_SIZE
    MUL BL
    MOV ROW_SIZE, AX

    ; Check if only one row
    MOV CL, NUM_ROWS
    DEC CL
    JZ SORT_DONE      ; Only one row - nothing to sort

SORT_OUTER:
    ; Initialize swap flag and inner counter
    MOV DL, 0         ; Swap flag = false
    MOV CH, 0         ; Inner index = 0

SORT_INNER:
    ; Check inner loop condition
    CMP CH, CL
    JAE CHECK_SWAP    ; End of inner loop

    ; Calculate addresses for current and next row
    MOV CURRENT_ROW, CH
    MOV AL, SORT_COL
    MOV CURRENT_COL, AL
    CALL CALCULATE_CELL_ADDR
    MOV SI, BX        ; First cell

    MOV AL, CH
    INC AL
    MOV CURRENT_ROW, AL
    CALL CALCULATE_CELL_ADDR
    MOV DI, BX        ; Second cell

    ; Check column type for comparison
    MOV AL, SORT_COL
    XOR AH, AH
    MOV BX, OFFSET COL_TYPES
    ADD BX, AX
    CMP BYTE PTR [BX], 0
    JE COMPARE_NUMERIC

    ; String comparison (ascending order)
    PUSH CX           ; Save row counter
    CALL COMPARE_STRINGS
    POP CX            ; Restore row counter

    CMP AX, 0
    JG DO_SWAP       ; Swap if first > second (for ascending)
    JMP NO_SWAP_NEEDED

COMPARE_NUMERIC:
    ; Convert strings to integers
    PUSH SI
    CALL STRING_TO_INT_SI
    MOV BP, AX        ; Store first value
    POP SI

    PUSH DI
    MOV SI, DI
    CALL STRING_TO_INT_SI
    MOV BX, AX        ; Store second value
    POP DI

    ; For descending order: swap if first < second
    CMP BP, BX
    JGE NO_SWAP_NEEDED   ; Jump if first >= second (no swap needed)

DO_SWAP:
    PUSH CX           ; Save row counter
    CALL SWAP_TABLE_ROWS
    POP CX            ; Restore row counter
    MOV DL, 1        ; Set swap flag

NO_SWAP_NEEDED:
    ; Next comparison
    INC CH
    JMP SORT_INNER

CHECK_SWAP:
    ; Check if any swaps occurred
    CMP DL, 0
    JE SORT_DONE     ; Exit if no swaps
    DEC CL
    JNZ SORT_OUTER   ; Continue while CL > 0

SORT_DONE:
    RET
BUBBLE_SORT ENDP

; ========== UTILITY PROCEDURES ============

GET_NUMBER_INPUT PROC
    PUSH BX
    PUSH CX
    PUSH DX
    PUSH SI

    ; Set up input buffer
    LEA SI, INPUT_BUFFER
    MOV CX, 0          ; Character count

NUMBER_INPUT_LOOP:
    ; Read character
    MOV AH, 01H
    INT 21H

    ; Check for Enter key
    CMP AL, 13
    JE NUMBER_INPUT_DONE

    ; Check for Backspace
    CMP AL, 8
    JE NUMBER_BACKSPACE

    ; Validate digit
    CMP AL, '0'
    JB NUMBER_INPUT_LOOP
    CMP AL, '9'
    JA NUMBER_INPUT_LOOP

    ; Check buffer limit
    CMP CX, 5          ; Max 5 digits
    JAE NUMBER_INPUT_LOOP

    ; Store digit and increment counters
    MOV [SI], AL
    INC SI
    INC CX
    JMP NUMBER_INPUT_LOOP

NUMBER_BACKSPACE:
    ; Handle backspace
    CMP CX, 0
    JE NUMBER_INPUT_LOOP
    DEC SI
    DEC CX
    ; Erase character from display
    MOV DL, 8
    MOV AH, 02H
    INT 21H
    MOV DL, ' '
    INT 21H
    MOV DL, 8
    INT 21H
    JMP NUMBER_INPUT_LOOP

NUMBER_INPUT_DONE:
    ; Check for empty input
    CMP CX, 0
    JE NUMBER_INVALID

    ; Convert string to number
    LEA SI, INPUT_BUFFER
    XOR AX, AX
    XOR DX, DX

CONVERT_LOOP:
    ; Process each digit
    CMP CX, 0
    JE CONVERT_DONE
    MOV BL, [SI]
    SUB BL, '0'
    XOR BH, BH

    ; AX = AX * 10 + BX
    MOV DX, 10
    MUL DX
    ADD AX, BX

    ; Next digit
    INC SI
    DEC CX
    JMP CONVERT_LOOP

CONVERT_DONE:
    ; Restore registers and return
    POP SI
    POP DX
    POP CX
    POP BX
    RET

NUMBER_INVALID:
    ; Return 0 for invalid input
    MOV AX, 0
    POP SI
    POP DX
    POP CX
    POP BX
    RET
GET_NUMBER_INPUT ENDP

CALCULATE_COL_NAME_ADDR PROC
    ; Calculate column name address: col_index * cell_size
    MOV AL, CURRENT_COL
    MOV AH, CELL_SIZE
    MUL AH
    MOV BX, OFFSET COL_NAMES
    ADD BX, AX
    RET
CALCULATE_COL_NAME_ADDR ENDP

; FIXED CELL ADDRESS CALCULATION
CALCULATE_CELL_ADDR PROC
    ; Calculate cell address: (row * num_cols + col) * cell_size
    MOV AL, CURRENT_ROW
    XOR AH, AH          ; Clear AH for 16-bit multiplication
    MOV DL, NUM_COLS
    XOR DH, DH          ; DX = NUM_COLS
    MUL DX              ; AX = row_index * num_cols
    MOV DL, CURRENT_COL
    XOR DH, DH          ; DX = col_index
    ADD AX, DX          ; AX = row_index * num_cols + col_index
    MOV DX, CELL_SIZE   ; DX = 9
    MUL DX              ; DX:AX = AX * 9 (use full 16-bit multiplication)
    MOV BX, OFFSET TABLE_DATA
    ADD BX, AX          ; Add offset to table base
    RET
CALCULATE_CELL_ADDR ENDP

GET_STRING_INPUT PROC
    PUSH DI
    MOV CX, CELL_SIZE - 1  ; Max characters (8)
    MOV SI, 0              ; Character counter

STRING_CHAR_LOOP:
    ; Read character
    MOV AH, 01H
    INT 21H
    CMP AL, 13          ; Enter key
    JE STRING_INPUT_DONE
    CMP AL, 8           ; Backspace
    JE STRING_BACKSPACE

    ; Check buffer limit
    CMP SI, CX
    JAE STRING_CHAR_LOOP

    ; Store character
    STOSB
    INC SI
    JMP STRING_CHAR_LOOP

STRING_BACKSPACE:
    ; Handle backspace
    CMP SI, 0
    JE STRING_CHAR_LOOP
    DEC DI
    DEC SI
    ; Erase character from display
    MOV DL, 8
    MOV AH, 02H
    INT 21H
    MOV DL, ' '
    INT 21H
    MOV DL, 8
    INT 21H
    JMP STRING_CHAR_LOOP

STRING_INPUT_DONE:
    ; Check for empty input
    CMP SI, 0
    JE STRING_EMPTY
    ; Terminate string
    MOV AL, '$'
    STOSB

    ; New line
    LEA DX, NEWLINE
    MOV AH, 09H
    INT 21H

    POP DI
    RET

STRING_EMPTY:
    ; Handle empty input
    LEA DX, ERR_EMPTY_CELL
    MOV AH, 09H
    INT 21H
    POP DI
    JMP GET_STRING_INPUT
GET_STRING_INPUT ENDP

GET_NUMERIC_INPUT PROC
    PUSH DI
    MOV CX, CELL_SIZE - 1  ; Max characters (8)
    MOV SI, 0              ; Character counter

NUMERIC_CHAR_LOOP:
    ; Read character
    MOV AH, 01H
    INT 21H
    CMP AL, 13          ; Enter key
    JE NUMERIC_INPUT_DONE
    CMP AL, 8           ; Backspace
    JE NUMERIC_BACKSPACE

    ; Validate character (digits or signs)
    CMP AL, '-'
    JE HANDLE_SIGN
    CMP AL, '+'
    JE HANDLE_SIGN
    CMP AL, '0'
    JB NUMERIC_CHAR_LOOP
    CMP AL, '9'
    JA NUMERIC_CHAR_LOOP

    ; Check buffer limit
    CMP SI, CX
    JAE NUMERIC_CHAR_LOOP

    ; Store character
    STOSB
    INC SI
    JMP NUMERIC_CHAR_LOOP

HANDLE_SIGN:
    ; Handle sign character (only allowed at start)
    CMP SI, 0
    JNE NUMERIC_CHAR_LOOP
    CMP SI, CX
    JAE NUMERIC_CHAR_LOOP
    STOSB
    INC SI
    JMP NUMERIC_CHAR_LOOP

NUMERIC_BACKSPACE:
    ; Handle backspace
    CMP SI, 0
    JE NUMERIC_CHAR_LOOP
    DEC DI
    DEC SI
    ; Erase character from display
    MOV DL, 8
    MOV AH, 02H
    INT 21H
    MOV DL, ' '
    INT 21H
    MOV DL, 8
    INT 21H
    JMP NUMERIC_CHAR_LOOP

NUMERIC_INPUT_DONE:
    ; Check for empty input
    CMP SI, 0
    JE NUMERIC_EMPTY
    ; Terminate string
    MOV AL, '$'
    STOSB

    ; New line
    LEA DX, NEWLINE
    MOV AH, 09H
    INT 21H

    POP DI
    RET

NUMERIC_EMPTY:
    ; Handle empty input
    LEA DX, ERR_EMPTY_CELL
    MOV AH, 09H
    INT 21H
    POP DI
    JMP GET_NUMERIC_INPUT
GET_NUMERIC_INPUT ENDP

STRING_TO_INT_SI PROC
    PUSH BX
    PUSH CX
    PUSH DX
    PUSH SI
    PUSH DI

    XOR DI, DI        ; Initialize result to 0
    XOR BX, BX        ; Sign flag (0=positive, 1=negative)
    XOR CX, CX        ; Temporary storage

    ; Check first character for sign
    MOV DL, [SI]
    CMP DL, '$'
    JE STI_DONE       ; Empty string
    CMP DL, '-'
    JE STI_NEGATIVE
    CMP DL, '+'
    JE STI_POSITIVE
    JMP STI_CONVERT_LOOP

STI_NEGATIVE:
    MOV BX, 1         ; Set negative flag
    INC SI            ; Move to next character
    JMP STI_CONVERT_LOOP

STI_POSITIVE:
    INC SI            ; Move to next character

STI_CONVERT_LOOP:
    ; Get next character
    MOV DL, [SI]
    CMP DL, '$'       ; End of string
    JE STI_DONE
    SUB DL, '0'       ; Convert ASCII to digit
    CMP DL, 0         ; Validate digit (0-9)
    JB STI_DONE
    CMP DL, 9
    JA STI_DONE

    ; Efficient multiply by 10 using shifts
    MOV AX, DI        ; AX = current value
    SHL AX, 1         ; AX = value*2
    MOV CX, AX        ; CX = value*2 (temporary)
    MOV AX, DI
    SHL AX, 3         ; AX = value*8
    ADD AX, CX        ; AX = value*10 (8+2)
    ADD AX, DX        ; Add new digit
    MOV DI, AX        ; Update result

    ; Next character
    INC SI
    JMP STI_CONVERT_LOOP

STI_DONE:
    ; Apply sign if negative
    MOV AX, DI
    CMP BX, 1
    JNE STI_EXIT
    NEG AX

STI_EXIT:
    ; Restore registers and return
    POP DI
    POP SI
    POP DX
    POP CX
    POP BX
    RET
STRING_TO_INT_SI ENDP

; FIXED STRING COMPARISON LOGIC
COMPARE_STRINGS PROC
    PUSH SI
    PUSH DI
    CLD               ; Clear direction flag (forward)

CS_LOOP:
    ; Compare bytes
    MOV AL, [SI]
    MOV BL, [DI]
    
    CMP AL, '$'
    JE CHECK_SECOND_END
    CMP BL, '$'
    JE FIRST_LARGER
    
    CMP AL, BL
    JB CS_LESS        ; String1 < String2
    JA CS_GREATER     ; String1 > String2
    
    ; Characters are equal, move to next
    INC SI
    INC DI
    JMP CS_LOOP

CHECK_SECOND_END:
    CMP BL, '$'
    JE CS_EQUAL
    JMP CS_LESS       ; String1 ended first, so it's less

FIRST_LARGER:
    JMP CS_GREATER

CS_LESS:
    MOV AX, -1
    JMP CS_DONE

CS_EQUAL:
    XOR AX, AX
    JMP CS_DONE

CS_GREATER:
    MOV AX, 1

CS_DONE:
    ; Restore registers and return
    POP DI
    POP SI
    RET
COMPARE_STRINGS ENDP

; FIXED ROW SWAPPING WITH PROPER ADDRESS CALCULATION
SWAP_TABLE_ROWS PROC
    PUSH AX
    PUSH BX
    PUSH CX
    PUSH SI
    PUSH DI

    ; Calculate address of first row
    XOR AX, AX
    MOV AL, CH        ; Row index
    XOR AH, AH        ; AX = row index (zero extended)
    MUL ROW_SIZE      ; AX = row_index * ROW_SIZE
    LEA SI, TABLE_DATA
    ADD SI, AX

    ; Calculate address of second row
    MOV AL, CH
    INC AL            ; Next row
    XOR AH, AH
    MUL ROW_SIZE      ; AX = (row_index+1) * ROW_SIZE
    LEA DI, TABLE_DATA
    ADD DI, AX

    ; Swap entire rows
    MOV CX, ROW_SIZE
SWAP_LOOP:
    MOV AL, [SI]
    MOV BL, [DI]
    MOV [SI], BL
    MOV [DI], AL
    INC SI
    INC DI
    LOOP SWAP_LOOP

    POP DI
    POP SI
    POP CX
    POP BX
    POP AX
    RET
SWAP_TABLE_ROWS ENDP

PRINT_NUMBER PROC
    PUSH AX
    PUSH DX
                   ; Check if single digit
    CMP AL, 10
    JB SINGLE_DIGIT
    ; Two-digit number handling
    AAM                 ; Convert to unpacked BCD
    ADD AX, 3030H       ; Convert to ASCII
    PUSH AX
    MOV DL, AH          ; Print tens digit
    MOV AH, 02H
    INT 21H
    POP DX
    MOV DL, DH          ; Print units digit
    INT 21H
    JMP PRINT_DONE

SINGLE_DIGIT:
    ; Single-digit number
    ADD AL, '0'
    MOV DL, AL
    MOV AH, 02H
    INT 21H

PRINT_DONE:
    POP DX
    POP AX
    RET
PRINT_NUMBER ENDP

END MAIN