User:Johnburger/Demo/User/Code

From OSDev Wiki
Jump to navigation Jump to search

This code takes the starting parameters, as provided by the Executive, and works out how to behave.

  1. Save starting parameters into Data Segment.
  2. Point to the Top Left of Screen area.
  3. Is there room to draw a frame (larger than 2x2)? If so:
    1. Shrink the screen area by 2 in each direction.
    2. Call the .Frame routine.
    3. Point inside the frame as the new Top Left corner.
  4. Work out the horizontal deltae, in both units (0 or 1) and screen offset (0 or 2). If the width is only 1 unit, there won't be any horizontal bouncing.
  5. Works out the vertical deltae, in both units (0 or 1) and screen offset (0 or Row). If the height is only 1 unit, there won't be any vertical bouncing.
  6. Set the start position to be 1,1. Can now do zero-checks when bouncing up or left.
  7. Start the loop:
    1. Remember the current screen pointer.
    2. Add the current horizontal deltae into X-Pos and screen pointer.
    3. Is it larger than width, or has it become zero (if negative deltae)? If so:
      1. Negate both deltae (change their sign/direction).
      2. Add deltae back into X-Pos and screen pointer.
      3. Twice.
    4. Add the current vertical deltae into Y-Pos and screen pointer.
    5. Is it larger than height, or has it become zero (if negative deltae)? If so:
      1. Negate both deltae (change their sign/direction).
      2. Add deltae back into Y-Pos and screen pointer.
      3. Twice.
    6. Is the new screen pointer the same as the remembered one?
      1. Yes: increment character at screen position to show something happened.
      2. No: Draw a space at the old position, and the defined ball character at the new position.
    7. Delay. This is one of the following techniques:
      1. Call User.Halt to perform a HLT. This will pause the system until the next interrupt.
      2. Call User.Yield to perform a Task Switch immediately. This will really move things along!
      3. Load User.Delay into ECX and perform a LOOP. This is busy-waiting, but to a finely-tunable time.

Demo/User/Code.inc

;
; User/Code.inc
;

; This module defines the User code to bounce the ball. It:
; 1) Uses the initial register values to set up the Data;
; 2) Draws the Frame (assuming the defined Window is big enough);
; 3) Calculates the bounce parameters (room to move vertically or horizontally?)
; 4) Bounces the ball around (or only increments it if the Window is too small);
; Forever!
;
; Note that this code can easily write outside its Window - by using direct
; Screen access, it can poke anything anywhere. It could have been given a
; 'strip' of the Screen to access, by defining an LDT-local alias to that part
; of the screen comprising the strip. That wouldn't help accesses to the left
; or right of the Window, but it would detect other bugs!
;
; On entry:
; CS:EIP = User.Entry
; SS:ESP = Stack
; ES     = Screen
; DS     = User.Data
; EBX    = Row pointer Delta
; CL     = Width
; CH     = Height
; DL     = Left-most position
; DH     = Top-most position

                SEGMENT         User  VSTART=0  ALIGN=16

User.ColouredBall EQU           (User.BallColour << 8) | User.Ball

User.Entry:
                CLD                             ; Work forwards
                MOV             [User.Data.Row],EBX
                MOV             [User.Data.Left],DX  ; Left and Top
                MOV             [User.Data.Width],CX ; Width and Height

                PUSH            ES
                POP             DS              ; Don't need DS anymore

                MOV             EBP,EBX         ; Save this away

                ; Calculate Screen pointer
                MOV             AL,DH           ; Get Top
                MUL             BL              ; Multiply by row offset
                MOVZX           EDI,AX          ; Put into Screen ptr

                MOVZX           EAX,DL          ; Get Left
                LEA             EDI,[EDI+EAX*2] ; Add Left into Screen ptr

                ; Check if Frame is needed
                CMP             CL,2            ; Wide enough for frame?
                JBE             .DeltaX         ; Nope!
                CMP             CH,2            ; Tall enough for frame?
                JBE             .DeltaX         ; Nope!

                ; Reduce dimensions, Draw Frame, and move inside
                MOV             ESI,EDI         ; Remember current screen ptr
                SUB             CX,0202h        ; Shrink both width and height
                CALL            User.Frame      ; Draw frame...
                LEA             EDI,[ESI+EBP+2] ; ...and point inside it

.DeltaX:        ; Calculate DeltaX
                MOV             BL,+1           ; Set DeltaX
                MOV             ESI,2           ; Use this for DeltaX on pointer
                CMP             CL,1            ; Too narrow?
                JA              .DeltaY         ; Nope!
                MOV             BL,0            ; No DeltaX
                XOR             ESI,ESI         ; No DeltaX pointer

.DeltaY:        ; Calculate DeltaY
                MOV             BH,+1           ; Set DeltaY
;               MOV             EBP,EBP         ; Use this for DeltaY on pointer
                CMP             CH,1            ; Too short?
                JA              .Start          ; Nope!
                MOV             BH,0            ; No DeltaY
                XOR             EBP,EBP         ; No DeltaY pointer

.Start:
; Now that all the maths is out of the way, I need the pos to be 1-relative (not
; 0-relative)
                MOV             DH,1            ; Start Y
                MOV             DL,1            ; Start X
.Move:
                MOV             EAX,EDI         ; Remember old pointer

.MoveH:
                ADD             EDI,ESI         ; Add in DeltaX pointer
                ADD             DL,BL           ; Add in DeltaX
                JZ              .BounceH        ; Underflow?
                CMP             DL,CL           ; No. Overflow?
                JBE             .MoveV          ; No. No bounce!
.BounceH:
                NEG             ESI             ; Reverse DeltaX pointer
                NEG             BL              ; Reverse DeltaX
                LEA             EDI,[EDI+ESI*2] ; Fix up pointer
                ADD             DL,BL           ; Fix up position
                ADD             DL,BL           ; And move correctly

.MoveV:
                ADD             EDI,EBP         ; Add in DeltaY pointer
                ADD             DH,BH           ; Add in DeltaY
                JZ              .BounceV        ; Underflow?
                CMP             DH,CH           ; No. Overflow?
                JBE             .Update         ; No. No bounce!
.BounceV:
                NEG             EBP             ; Reverse DeltaY pointer
                NEG             BH              ; Reverse DeltaY
                LEA             EDI,[EDI+EBP*2] ; Fix up pointer
                ADD             DH,BH           ; Fix up position
                ADD             DH,BH           ; And move correctly

.Update:
                CMP             EDI,EAX         ; After all that, did we move?
                JE              .Increment      ; Nope! Just increment.
                MOV     BYTE    [EAX],' '       ; Erase old ball
                MOV     WORD    [EDI],User.ColouredBall ; And write in new one
                JMP             .Wait
.Increment:
                INC     BYTE    [EDI]           ; Increment screen position

.Wait:
;               HLT                             ; Illegal User-mode instruction!
                User.Halt                       ; So, use system-provided one
;                User.Yield                      ; This has a different effect

; This is an alternative to using HLT
;                PUSH            ECX
;                MOV             ECX,User.Delay  ; Wait for a little while
;                LOOP            $               ; About this long...
;                POP             ECX

                JMP             .Move