.model tiny
locals

.code

  org 0100h
Start:

  ; CGA 320x200, 4 Colors
  mov   ax, 4
  int   10h

  ; CGA Palette
  mov   ah, 0Bh
  mov   bh, 01h         ; Select palette ID
  ; Palette 0 (Green, Red, Yellow)
  ; Palette 1 (Cyan, Magenta, White)
  mov   bl, 0           ; Palette
  int   10h

  ; Draw vertical line
  push  100             ; X
  push  50              ; Y1
  push  150             ; Y2
  push  2               ; Color
  call  VLine

  ; Draw horizontal line
  push  100             ; Y
  push  50              ; X1
  push  250             ; X2
  push  2               ; Color
  call  HLine

  ; Draw pixel
  push  150             ; X
  push  50              ; Y
  push  3               ; Color
  call  PSet

  ; Draw line
  push  0               ; X1
  push  0               ; Y1
  push  319             ; X2
  push  199             ; Y2
  push  3               ; Color
  call  Line

  ; Wait for a key press
  mov   ax, 0
  int   16h

  ; VLine Test
  mov   cx, 0           ; CX will be our X coordinate (0 to 319)

DrawLoop:
  push  cx              ; Save CX for loop routine

  ; Simple RNG
  ; Read the low byte of the system timer
  push  ds
  xor   ax, ax
  mov   ds, ax
  mov   al, ds:[046Ch]  ; Read timer tick low byte
  pop   ds
    
  and   al, 3           ; Mask bits to get 0-3
  jnz   @@ColorOk       ; Color is not zero
  mov   al, 1           ; Make color 1 if 0
@@ColorOk:
  mov   ah, 0           ; AX is our color!

  ;Draw vertical line
  push  cx              ; Parameter X
  push  0               ; Parameter Y1
  push  199             ; Parameter Y2
  push  ax              ; Parameter Color
  call  VLine

  pop   cx              ; Restore our X coordinate
  inc   cx              ; Increment X
  cmp   cx, 320         ; Have we reached the end?
  jne   DrawLoop        ; If not, keep drawing

  ; Wait for a key press
  mov   ax, 0
  int   16h

  ; Text 80x25, 16 Colors
  mov   ax, 3
  int   10h

  ; End program
  int   20h

PSet PROC
  push  bp
  mov   bp, sp
  push  ds
  push  di

  ; Set DS to CGA Video Segment
  mov   ax, 0B800h
  mov   ds, ax

  mov   ax, [bp+6]      ; Get Y
  mov   bx, [bp+8]      ; Get X

  ; Calculate Offset: (Y >> 1) * 80
  ; Check if Y is odd to add the 2000h bank offset
  shr   ax, 1           ; AX = Y / 2
  pushf                 ; Save carry flag (set if Y was odd)
    
  mov   dx, 80
  mul   dx              ; AX = (Y/2) * 80

  popf                  ; Restore carry flag
  jnc   @@EvenLine
  add   ax, 2000h       ; If Y was odd, move to Bank 1

@@EvenLine:

  ; Add X contribution: X / 4 (since 4 pixels per byte)
  mov   di, bx
  shr   di, 1
  shr   di, 1
  add   di, ax          ; DI now holds the byte offset in segment

  ; Now we need to mask the specific 2 bits within the byte
  ; The pixel position within the byte is X AND 3
  mov   cx, bx
  and   cx, 3           ; CX = X % 4
    
  ; CGA bit order is: 
  ; Pixel 0: bits 7-6 | Pixel 1: bits 5-4
  ; Pixel 2: bits 3-2 | Pixel 3: bits 1-0
  ; We need to shift our color and mask based on this.
  ; Shift count = (3 - (X%4)) * 2
  xor   cx, 3           ; Flip it: 0->3, 1->2, 2->1, 3->0
  shl   cx, 1           ; Multiply by 2

  mov   al, [bp+4]      ; Get Color
  and   al, 03h         ; Mask to ensure only 2 bits
  shl   al, cl          ; Shift color into position

  ; Create bit mask (00b at pixel position, 11b elsewhere)
  mov   ah, 03h
  shl   ah, cl
  not   ah              ; AH is now our mask (e.g., 11001111b)

  ; Apply to memory
  mov   dl, [di]        ; Read existing byte
  and   dl, ah          ; Clear the old pixel bits
  or    dl, al          ; Set the new pixel bits
  mov   [di], dl        ; Write back

  pop   di
  pop   ds
  pop   bp
  ret   6               ; Clean up 3 words (6 bytes) from stack
PSet ENDP

HLine PROC
  push  bp
  mov   bp, sp
  push  ds
  push  di
  push  si

  mov   ax, 0B800h
  mov   ds, ax

  ; Calculate Row Base Address (Y/2 * 80)
  mov   ax, [bp+10]     ; AX = Y
  shr   ax, 1           ; AX = Y/2
  pushf                 ; Save odd/even row flag

  mov   dx, ax          ; Start Shift-and-Add Optimization
  shl   ax, 1
  shl   ax, 1
  shl   ax, 1
  shl   ax, 1           ; AX = (Y/2)*16
  mov   bx, ax
  shl   ax, 1
  shl   ax, 1           ; AX = (Y/2)*64
  add   ax, bx          ; AX = (Y/2)*80

  popf
  jnc   @@EvenRow
  add   ax, 2000h
@@EvenRow:
  mov   si, ax          ; SI = Base offset for this row

  mov   dx, [bp+8]      ; DX = Current X (starting at X1)

@@LineLoop:
  ; Calculate pixel address
  mov   di, dx
  shr   di, 1
  shr   di, 1           ; DI = X / 4
  add   di, si          ; DI = Byte address (Base + X-Offset)

  ; Bitmask logic
  mov   cx, dx
  and   cx, 3
  xor   cx, 3
  shl   cx, 1           ; Shift count

  mov   al, [bp+4]      ; Color
  and   al, 03h
  shl   al, cl

  mov   ah, 03h
  shl   ah, cl
  not   ah              ; Mask

  ; Read, modify, write
  mov   bl, [di]
  and   bl, ah
  or    bl, al
  mov   [di], bl

  inc   dx              ; Move to next X
  cmp   dx, [bp+6]      ; Compare with X2
  jbe   @@LineLoop

  pop   si
  pop   di
  pop   ds
  pop   bp
  ret   8
HLine ENDP

VLine PROC
  push  bp
  mov   bp, sp
  push  ds
  push  di
  push  si

  mov   ax, 0B800h
  mov   ds, ax

  mov   si, [bp+8]      ; SI = Current Y (Start)
  mov   dx, [bp+10]     ; DX = X coordinate

@@LineLoop:
  ; Calculate address: (Y/2) * 80
  mov   ax, si
  shr   ax, 1           ; AX = Y / 2
  pushf                 ; Save the carry (LSB of Y) for bank selection
    
  ; Shift-and-Add: (Y/2)*64 + (Y/2)*16
  mov   bx, ax          ; BX = (Y/2)
  shl   ax, 1
  shl   ax, 1
  shl   ax, 1
  shl   ax, 1           ; AX = (Y/2) * 16
  mov   di, ax          ; Store *16 in DI temporarily
  shl   ax, 1
  shl   ax, 1           ; AX = (Y/2) * 64
  add   ax, di          ; AX = (Y/2)*64 + (Y/2)*16 = 80
    
  popf                  ; Restore carry
  jnc   @@EvenRow
  add   ax, 2000h       ; If Y was odd, go to Bank 1
@@EvenRow:
    
  ; Add X contribution (X / 4)
  mov   di, dx
  shr   di, 1
  shr   di, 1
  add   di, ax          ; DI = Final memory offset

  ; Bitmask Logic
  mov   cx, dx
  and   cx, 3           ; X % 4
  xor   cx, 3           ; Reverse for CGA bit order
  shl   cx, 1           ; Shift count = (3 - (X%4)) * 2

  mov   al, [bp+4]      ; Get Color
  and   al, 03h
  shl   al, cl          ; Shift color into position

  mov   ah, 03h
  shl   ah, cl
  not   ah              ; AH = Bitmask (e.g., 11110011b)

  ; Read, modify, write
  mov   bl, [di]
  and   bl, ah          ; Clear bits
  or    bl, al          ; Set color
  mov   [di], bl

  ; Loop control
  inc   si              ; Next Y
  cmp   si, [bp+6]      ; Check against Y2
  jbe   @@LineLoop

  pop   si
  pop   di
  pop   ds
  pop   bp
  ret   8               ; Clean up 4 parameters
VLine ENDP

Line PROC
  push  bp
  mov   bp, sp
  push  ds
  push  si
  push  di
  sub   sp, 2           ; Local storage for Error Term [bp-2]

  ; Calculate dX (CX) = abs(X2 - X1)
  mov   cx, [bp+8]
  sub   cx, [bp+12]
  jge   @@dX_Pos
  neg   cx
@@dX_Pos:

  ; Calculate dY (DX) = abs(Y2 - Y1)
  mov   dx, [bp+6]
  sub   dx, [bp+10]
  jge   @@dY_Pos
  neg   dx
@@dY_Pos:

  ; Initial error: [bp-2] = dX - dY
  mov   ax, cx
  sub   ax, dx
  mov   [bp-2], ax

  ; Set initial coordinates
  mov   si, [bp+12]     ; SI = Current X
  mov   di, [bp+10]     ; DI = Current Y

@@LineLoop:
  ; Draw pixel (inline)
  push  cx              ; Save dX
  push  dx              ; Save dY
    
  mov   ax, 0B800h
  mov   ds, ax
    
  mov   ax, di          ; Y
  shr   ax, 1
  pushf                 ; Save odd/even flag
    
  ; (Y/2)*80 math
  mov   bx, ax
  shl   ax, 1
  shl   ax, 1
  shl   ax, 1
  shl   ax, 1           ; *16
  mov   dx, ax
  shl   ax, 1
  shl   ax, 1           ; *64
  add   ax, dx          ; AX = *80
    
  popf
  jnc   @@Even
  add   ax, 2000h
@@Even:
  mov   bx, si          ; X
  mov   dx, bx          ; Save X for mask
  shr   bx, 1
  shr   bx, 1
  add   bx, ax          ; BX = Byte Offset
    
  mov   cx, dx
  and   cx, 3
  xor   cx, 3
  shl   cx, 1
  mov   al, [bp+4]      ; Color
  and   al, 3
  shl   al, cl
  mov   ah, 3
  shl   ah, cl
  not   ah
    
  mov   dl, [bx]
  and   dl, ah
  or    dl, al
  mov   [bx], dl
    
  pop   dx              ; Restore dY
  pop   cx              ; Restore dX
  ; End pixel

  ; Check if Done
  cmp   si, [bp+8]      ; X == X2?
  jne   @@Continue
  cmp   di, [bp+6]      ; Y == Y2?
  je    @@Done_Near
@@Continue:

  mov   ax, [bp-2]      ; AX = err
  shl   ax, 1           ; AX = e2

  ; if e2 > -dY
  mov   bx, dx
  neg   bx              ; BX = -dY
  cmp   ax, bx
  jle   @@CheckY
  sub   [bp-2], dx      ; err -= dY
    
  ; Step X
  mov   bx, [bp+8]      ; X2
  cmp   si, bx
  jge   @@X_Dec
  inc   si
  jmp   @@CheckY
@@X_Dec:
  dec   si

@@CheckY:
  ; if e2 < dX
  mov   ax, [bp-2]
  shl   ax, 1           ; Recalculate e2
  cmp   ax, cx
  jge   @@JumpBack      ; Trampoline logic!
    
  add   [bp-2], cx      ; err += dX
  ; Step Y
  mov   bx, [bp+6]      ; Y2
  cmp   di, bx
  jge   @@Y_Dec
  inc   di
  jmp   @@JumpBack

@@Y_Dec:
  dec   di

@@JumpBack:
  jmp   @@LineLoop      ; Standard JMP has no range limit

@@Done_Near:
  add   sp, 2           ; Clean local
  pop   di
  pop   si
  pop   ds
  pop   bp
  ret   10
Line ENDP

end Start
