]> pd.if.org Git - mmurtl/blob - ossource/main.asm
autocommit for file dated 1995-02-09 11:17:04
[mmurtl] / ossource / main.asm
1 ;   MMURTL Operating System Source Code\r
2 ;   Copyright 1991,1992,1993,1994  Richard A. Burgess\r
3 ;   ALL RIGHTS RESERVED\r
4 ;   Version 1.0\r
5 ;\r
6 ; This is the main Data/Code file for the MMURTL OS.\r
7 ; It contains important structures and initialization code\r
8 ; including the first instructions executed after a boot!\r
9 ; This should follow the IDT, GDT, PDR & Public call table in memory.\r
10 \r
11 .DATA\r
12 .INCLUDE MOSEDF.INC\r
13 .INCLUDE JOB.INC\r
14 .INCLUDE TSS.INC\r
15 \r
16 .ALIGN DWORD\r
17 ;=============================================================================\r
18 ; Kernel Structures - See MOSEDF.INC for structure details.\r
19 ;=============================================================================\r
20 \r
21 PUBLIC MonTSS           DB sTSS dup (0) ; Initial TSS for OS/Monitor\r
22 PUBLIC DbgTSS           DB sTSS dup (0) ; Initial TSS for Debugger\r
23 PUBLIC pFreeTSS         DD NIL                  ; Pointer to Free List of Task State Segs\r
24 PUBLIC pDynTSSs         DD 0                    ; ptr to alloced mem for dynamic TSSs\r
25 PUBLIC _nTSSLeft        DD nTSS-2               ; For stats (less static TSSs)\r
26 \r
27 ;------------------\r
28 \r
29 PUBLIC rgLBs    DB (nLB*sLINKBLOCK) dup (0)     ; pool of LBs\r
30 PUBLIC pFreeLB  DD NIL                                          ; Ptr to Free List of Link Block\r
31 PUBLIC _nLBLeft DD nLB                                          ; For Monitor stats\r
32 \r
33 ;------------------\r
34 ; The RUN Queue for "Ready to Run" tasks\r
35 \r
36 PUBLIC RdyQ  DB (sQUEUE*nPRI) dup (0)   ; Priority Based Ready Queue\r
37 \r
38 ;------------------\r
39 ; Two static Job Control Blocks (JCBs) to get us kick-started.\r
40 ; The rest of them are in allocated memory\r
41 \r
42 PUBLIC MonJCB      DB sJCB dup (0)              ; Monitor JCB\r
43 PUBLIC DbgJCB      DB sJCB dup (0)              ; Debugger JCB\r
44 \r
45 ;------------------\r
46 \r
47 PUBLIC GDTLimit         DW 0000h                ;Global Descriptor Table Limit\r
48 PUBLIC GDTBase          DD 00000000h    ;base\r
49 PUBLIC IDTLimit         DW 0000h        ;Interrupt Descriptor Table Limit\r
50 PUBLIC IDTBase          DD 00000000h    ;base\r
51 \r
52 ;------------------\r
53 PUBLIC rgSVC    DB (sSVC*nSVC) dup (0)          ; Setup an array of Service Descriptors\r
54 \r
55 ;------------------\r
56 ;Exchanges take up a fair amount of memory. In order to use certain kernel\r
57 ;primitives, we need exchanges before they are allocated dynamically.\r
58 ;We have an array of 3 exchanges set aside for this purpose.  After\r
59 ;the dynamic array is allocated an initialized, we copy these into\r
60 ;the first three dynamic exchanges.  These static exchanges are used\r
61 ;by the Monitor TSS, Debugger TSS, and memory Management.\r
62 \r
63 PUBLIC nExch  DD 3                                      ; This will be changed after allocation\r
64                                                                         ; of dynamic exchanges.\r
65 rgExchTmp  DB (sEXCH * 3) dup (0)       ; Setup three static temporary Exchanges\r
66                                                                         ; for Monitor and Debugger TSSs\r
67                                                                         ; These are moved to a dynamic Exch Array\r
68                                                                         ; as soon as it's allocated.\r
69 PUBLIC   prgExch    DD OFFSET rgExchTmp         ; Pointer to current array of Exchanges.\r
70 PUBLIC   pExchTmp   DD 0                                ; Pointer to dynamic array Exchanges.\r
71 PUBLIC   _nEXCHLeft DD nDynEXCH                 ; For Monitor stats\r
72 \r
73 ;-------------------\r
74 ;Scheduling management variables\r
75 \r
76 PUBLIC TSS                      DD 00000000h    ; Used for jumping to next task\r
77 PUBLIC TSS_Sel          DW 0000h                ; "    "\r
78 \r
79 .ALIGN DWORD\r
80 \r
81 PUBLIC pRunTSS          DD NIL                  ; Pointer to the Running TSS\r
82 PUBLIC SwitchTick       DD 0                    ; Tick of last task switch\r
83 PUBLIC dfHalted     DD 0                        ; nonzero if processor was halted\r
84 \r
85 PUBLIC _nSwitches       DD 0                    ; # of switches for statistics\r
86 PUBLIC _nSlices         DD 0                    ; # of sliced switches for stats\r
87 PUBLIC _nReady          DD 0                    ; # of task Ready to Run for stats\r
88 PUBLIC _nHalts          DD 0                    ; # of times CPU halted for stats\r
89 \r
90 ;THESE ARE THE INITIAL STACKS FOR THE OS Monitor AND Debugger\r
91 \r
92 OSStack         DD 0FFh DUP (00000000h) ;1K OS Monitor Stack\r
93 OSStackTop      DD 00000000h\r
94 OSStackSize     EQU OSStackTop-OSStack\r
95 \r
96 Stack1          DD 0FFh DUP (00000000h) ;1K Debugger Stack\r
97 Stack1Top       DD 00000000h\r
98 Stack1Size      EQU Stack1Top-Stack1\r
99 \r
100 ;-----------------\r
101 \r
102 rgNewJob        DB 'New Job'                    ;Placed in New JCBs\r
103 cbNewJob        EQU 7\r
104 \r
105 rgOSJob         DB 'MMOS Monitor'               ;Placed in first JCB\r
106 cbOSJob         EQU 12\r
107 \r
108 rgDbgJob        DB 'Debugger    '               ;Name for Job\r
109 cbDbgJob        DD 12                                   ;Size of Job Name\r
110 \r
111 PUBLIC _BootDrive  DD 00                        ;Source drive of the boot\r
112 ;=============================================================================\r
113 ;=============================================================================\r
114 ; This begins the OS Code Segment\r
115 .CODE\r
116 ;\r
117 .VIRTUAL 10000h                         ;64K boundry. This lets the assembler know\r
118                                                         ;that this is the address where we execute\r
119 ;\r
120 ; BEGIN OS INITIALIZATION CODE\r
121 ;\r
122 ; This code is used to initialize the permanent OS structures\r
123 ; and calls procedures that initialize dynamic structures too.\r
124 ;\r
125 ; "Will Robinson, WARNING, WARNING!! Dr. Smith is approaching!!!"\r
126 ;  BEWARE ON INITIALIZATION.  The kernel structures and their\r
127 ;  initialization routines are so interdependent, you must pay\r
128 ;  close attention before you change the order of ANYTHING.\r
129 ;  (Anything before we jump to the monitor code that is)\r
130 \r
131 EXTRN InitCallGates NEAR\r
132 EXTRN InitOSPublics NEAR\r
133 EXTRN _Monitor NEAR\r
134 EXTRN InitIDT NEAR\r
135 EXTRN InitFreeLB NEAR\r
136 EXTRN InitDMA NEAR\r
137 EXTRN Set8259 NEAR\r
138 EXTRN InitKBD NEAR\r
139 EXTRN AddTSSDesc NEAR\r
140 EXTRN InitMemMgmt NEAR\r
141 EXTRN InitNewJCB NEAR\r
142 EXTRN InitVideo NEAR\r
143 EXTRN InitFreeTSS NEAR\r
144 EXTRN InitDynamicJCBs NEAR\r
145 EXTRN InitDynamicRQBs NEAR\r
146 EXTRN DbgTask NEAR\r
147 ;=============================================================================\r
148 ; Set up the initial Stack Pointer (SS = DS already).\r
149 ; This is the first code we execute after the loader\r
150 ; code throws us into protected mode. It's a FAR jump\r
151 ; from that code...\r
152 ;=============================================================================\r
153 \r
154 .START\r
155 PUBLIC OSInitBegin:\r
156                 LEA EAX,OSStackTop              ; Setup initial OS Stack\r
157                 MOV ESP,EAX                                     ; FIRST THING IN OS CODE.\r
158                 MOV _BootDrive, EDX                     ; Left there from BootSector Code\r
159 \r
160 ;=============================================================================\r
161 ; Set up OS Common Public for IDT and GDT Base and Limits - SECOND THING IN OS\r
162 ;=============================================================================\r
163 \r
164                 SGDT FWORD PTR GDTLimit         ;A formality - we know where they are!\r
165                 SIDT FWORD PTR IDTLimit         ;\r
166 \r
167 ;=============================================================================\r
168 ; Setup Operating System Structures and motherboard hardware\r
169 ; THIS IS RIGHT AFTER WE GET A STACK.\r
170 ; YOU CAN'T ALLOCATE ANY OS RESOURCES UNTIL THIS CODE EXECUTES!!!\r
171 ;=============================================================================\r
172 \r
173                 CALL InitCallGates                      ; Sets up all call gates as DUMMYs\r
174                                                                         ;  except AddCallGate which\r
175                                                                            must be made valid first!\r
176 \r
177                 CALL InitOSPublics                      ; Sets up OS PUBLIC call gates\r
178 \r
179                 CALL InitIDT                            ;Sets up default Interrupt table\r
180                                                                         ;NOTE: This uses CallGates!\r
181 \r
182                 MOV ECX,nLB                                     ; count of Link Blocks\r
183                 MOV EDX,sLinkBlock                      ; EDX is size of a Link Block\r
184                 CALL InitFreeLB                         ; Init the array of Link Blocks\r
185 \r
186                 CALL InitDMA                            ; Sets up DMA with defaults\r
187 \r
188                 CALL Set8259                    ; Set up 8259s for ints (before KBD)\r
189 \r
190                 CALL InitKBD                    ; Initialize the Kbd hardware\r
191 \r
192                 PUSH 0                                          ; Highest IRQ number (all IRQs)\r
193                 CALL FWORD PTR _EndOfIRQ        ; Tell em to work\r
194 \r
195                 ;Set time counter divisor to 11938 - 10ms ticks\r
196                 ;Freq in is 1.193182 Mhz/11932 = 100 per second\r
197                 ;or 1 every 10 ms.  (2E9Ch = 11932 decimal)\r
198 \r
199                 MOV AL,9Ch                                      ; Makes the timer Tick (lo)\r
200                 OUT 40h,AL                      ; 10 ms apart by setting\r
201                 MOV AL,02Eh                                     ; clock divisior to (hi byte)\r
202                 OUT 40h,AL                      ; 11,932\r
203 \r
204                 STI                                                     ; We are ready to GO (for now)\r
205 \r
206 ;=============================================================================\r
207 ; The following code finishes the initialization procedures BEFORE the\r
208 ; OS goes into paged memory mode.\r
209 ; We set up an initial Task by filling a static TSS, creating and loading\r
210 ; a descriptor entry for it in the GDT and we do the same for the debugger.\r
211 ;=============================================================================\r
212 \r
213                 ; Make the default TSS for the CPU to switch from a valid one.\r
214                 ; This TSS is a valid TSS after the first task switch.\r
215                 ; IMPORTANT - Allocate Exch and InitMemMgmt calls depend on\r
216                 ; pRunTSS being valid.  They can not be called before\r
217                 ; this next block of code!!!  Note that this TSS does NOT\r
218                 ; get placed in the linked list with the rest of the TSSs.\r
219                 ; It will never be free. We also have to manaully make it's\r
220                 ; entry in the GDT.\r
221 \r
222                 ;The following code section builds a descriptor entry for\r
223                 ;the initial TSS (for Montitor program) and places it into the GDT\r
224 \r
225                 MOV EAX, sTSS                           ; Limit of TSS (TSS + SOFTSTATE)\r
226                 MOV EBX, 0089h                  ; G(0),AV(0),LIM(0),P(1),DPL(0),B(0)\r
227                 MOV EDX, OFFSET MonTSS          ; Address of TSS\r
228                 MOV EDI, OFFSET rgTSSDesc       ; Address of GDT entry to fill\r
229                 CALL AddTSSDesc\r
230 \r
231                 ;Now that we have valid Descriptor, we set up the TSS itself\r
232                 ;and Load Task Register with the descriptor (selector)\r
233                 ;Note that none of the TSS register values need to be filled in\r
234                 ;because they will be filled by the processor on the first\r
235                 ;task switch.\r
236 \r
237                 MOV EBX, OFFSET MonTSS          ; Get ptr to initial TSS in EBX\r
238                 MOV pRunTSS,EBX                         ; this IS our task now!!!\r
239                 MOV EAX, OFFSET rgTSSDesc       ; ptr to initial TSS descriptor\r
240                 SUB EAX, OFFSET GDT         ; Sub offset of GDT Base to get Sel of TSS\r
241                 MOV WORD PTR [EBX+TSS_IOBitBase], 0FFh; I/O Permission\r
242                 MOV [EBX+Tid],AX                ; Store TSS Selector in TSS (Task ID)\r
243                 LTR WORD PTR [EBX+Tid]                          ; Setup the Task Register\r
244                 MOV BYTE PTR [EBX+Priority], 25         ; Priority 25 (monitor is another APP)\r
245                 MOV DWORD PTR [EBX+TSS_CR3], OFFSET PDir1       ;Physical address of PDir1\r
246                 MOV WORD PTR [EBX+TSSNum], 1            ;Number of first TSS (Duh)\r
247 \r
248                 ;Set up Job Control Block for Monitor (always Job 1)\r
249                 ;JOB 0 is not allowed. First JCB IS job 1!\r
250 \r
251                 MOV EAX, OFFSET MonJCB                  ;\r
252                 MOV DWORD PTR [EAX+JobNum], 1   ;Number the JCB\r
253                 MOV EBX, OFFSET MonTSS                  ;Must put ptr to JCB in TSS\r
254                 MOV [EBX+TSS_pJCB], EAX                 ;pJCB into MonTSS\r
255                 MOV EBX, OFFSET PDir1                   ;Page Directory\r
256                 MOV ESI, OFFSET rgOSJob                 ;Job Name\r
257                 MOV ECX, cbOSJob                                ;Size of Name\r
258                 XOR EDX, EDX                                    ;NO pVirtVid yet (set up later in init)\r
259                 CALL InitNewJCB\r
260 \r
261 \r
262 ; IMPORTANT - You can't call AllocExch before pRunTSS is VALID!\r
263 \r
264 ; THIS IS THE FIRST POINT AllocExh is valid\r
265 \r
266                 MOV EAX, pRunTSS\r
267                 ADD EAX, TSS_Exch                       ;Alloc exch for initial (first) TSS\r
268                 PUSH EAX\r
269                 CALL FWORD PTR _AllocExch\r
270 \r
271 \r
272 \r
273 ;=============================================================================\r
274 ;\r
275 ; Set up DEBUGGER Task and Job\r
276 ; The debugger is set up as another job with its one task.\r
277 ; The debugger must not be called (Int03) until this code executes,\r
278 ; AND the video is initialized.\r
279 ; We can't use NewTask because the debugger operates independent of\r
280 ; the kernel until it is called (INT 03 or Exception)\r
281 ;\r
282                 ;The following code section builds a descriptor entry for\r
283                 ;the initial TSS (for Debugger) and places it into the GDT\r
284 \r
285                 MOV EAX, sTSS                           ; Limit of TSS (TSS + SOFTSTATE)\r
286                 MOV EBX, 0089h                  ; G(0),AV(0),LIM(0),P(1),DPL(0),B(0)\r
287                 MOV EDX, OFFSET DbgTSS          ; Address of Debugger TSS\r
288                 MOV EDI, OFFSET rgTSSDesc+8     ; Address of GDT entry to fill in\r
289                 CALL AddTSSDesc\r
290 \r
291                 ;Now that we have valid Descriptor, we set up the TSS itself\r
292 \r
293                 MOV EAX, OFFSET rgTSSDesc+8     ; ptr to second TSS descriptor\r
294                 SUB EAX, OFFSET GDT         ; Sub offset of GDT Base to get Sel of TSS\r
295                 MOV EBX, OFFSET DbgTSS          ; Get ptr to initial TSS in EBX\r
296                 MOV WORD PTR [EBX+TSS_IOBitBase], 0FFh; I/O Permission\r
297                 MOV [EBX+Tid],AX                ; Store TSS Selector in TSS (Task ID)\r
298                 MOV BYTE PTR [EBX+Priority], 1          ; Debugger is HIGH Priority\r
299                 MOV DWORD PTR [EBX+TSS_CR3], OFFSET PDir1       ;Physical address of PDir1\r
300                 MOV EDX, OFFSET DbgTask\r
301                 MOV [EBX+TSS_EIP],EDX\r
302                 MOV WORD PTR [EBX+TSS_CS],OSCodeSel             ; Put OSCodeSel in the TSS\r
303                 MOV WORD PTR [EBX+TSS_DS],DataSel               ; Put DataSel in the TSS\r
304                 MOV WORD PTR [EBX+TSS_ES],DataSel               ;\r
305                 MOV WORD PTR [EBX+TSS_FS],DataSel               ;\r
306                 MOV WORD PTR [EBX+TSS_GS],DataSel               ;\r
307                 MOV WORD PTR [EBX+TSS_SS],DataSel               ;\r
308                 MOV WORD PTR [EBX+TSS_SS0],DataSel              ;\r
309                 MOV EAX, OFFSET Stack1Top\r
310                 MOV DWORD PTR [EBX+TSS_ESP],EAX                 ; A 1K Stack in the Dbg TSS\r
311                 MOV DWORD PTR [EBX+TSS_ESP0],EAX                        ;\r
312                 MOV DWORD PTR [EBX+TSS_EFlags],00000202h        ; Load the Flags Register\r
313                 MOV WORD PTR [EBX+TSSNum], 2                            ; Number of Dubegger TSS\r
314 \r
315                 ;Set up Job Control Block for Debugger\r
316                 ;JOB 0 is not allowed. First JCB IS job 1, debugger is always 2\r
317 \r
318                 MOV EAX, OFFSET DbgJCB\r
319                 MOV DWORD PTR [EAX+JobNum], 2                   ;Number the JCB\r
320                 MOV [EBX+TSS_pJCB], EAX         ;EBX still points to DbgTSS\r
321                 MOV EBX, OFFSET PDir1           ;Page Directory (OS PD to start)\r
322                 MOV ESI, OFFSET rgDbgJob        ;Name\r
323                 MOV ECX, cbDbgJob                       ;size of name\r
324                 MOV EDX, 1                                      ;Debugger gets video 1\r
325                 CALL InitNewJCB\r
326 \r
327                 ;Now allocate the default exchange for Debugger\r
328 \r
329                 MOV EAX, OFFSET DbgTSS\r
330                 ADD EAX, TSS_Exch                       ;Alloc exch for Debugger TSS\r
331                 PUSH EAX\r
332                 CALL FWORD PTR _AllocExch\r
333 \r
334 ;================================================================\r
335 ; ALSO NOTE: The InitMemMgmt call enables PAGING!  Physical addresses\r
336 ; will not necessarily match Linear addresses beyond this point.\r
337 ; Pay attention!\r
338 ;\r
339 \r
340                 CALL InitMemMgmt                        ;InitMemMgmt allocates an exch so\r
341                                                                         ;this the first point it can be called.\r
342                                                                         ;It also calls SEND!\r
343 \r
344 \r
345 ;================================================================\r
346 ; FIRST POINT memory management calls are valid\r
347 ;================================================================\r
348 \r
349                 ;Now we will allocate two virtual video screens (1 Page each)\r
350                 ;for the Monitor and Debugger and place them in the JCBs\r
351                 ;Also we set pVidMem for Monitor to VGATextBase address\r
352                 ;cause it has the active video by default\r
353 \r
354                 PUSH 1                                          ; 1 page\r
355                 MOV EAX, OFFSET MonJCB          ; Ptr to Monitor JCB\r
356                 ADD EAX, pVirtVid                       ; Offset in JCB to pVirtVid\r
357                 PUSH EAX\r
358                 CALL FWORD PTR _AllocOSPage     ; Get 'em!\r
359                 MOV EAX, OFFSET MonJCB          ; Make VirtVid Active for Monitor\r
360                 MOV DWORD PTR [EAX+pVidMem], VGATextBase\r
361 \r
362 \r
363                 PUSH 1                                          ; 1 page\r
364                 MOV EAX, OFFSET DbgJCB          ;\r
365                 ADD EAX, pVirtVid\r
366                 PUSH EAX\r
367                 CALL FWORD PTR _AllocOSPage     ; Get 'em!\r
368                 MOV EAX, OFFSET DbgJCB          ;\r
369                 MOV EBX, [EAX+pVirtVid]\r
370                 MOV [EAX+pVidMem], EBX          ; Video NOT active for Debugger\r
371 \r
372                 CALL InitVideo                          ;Set Text Screen 0\r
373 \r
374 \r
375 ;               MOV EAX,30423042h       ;BB on CYAN - So we know we are here!\r
376 ;               MOV DS:VGATextBase+00h,EAX\r
377 \r
378 ;================================================================\r
379 ; FIRST POINT video calls are valid\r
380 ; FIRST POINT Debugger is working  (only because it needs the video)\r
381 \r
382 ; At this point we can allocate pages for dynamic structures and\r
383 ; initialize them.\r
384 ;================================================================\r
385 \r
386 ; Allocate 8 pages (32768 bytes) for 64 Task State Segments (structures).\r
387 ; Then call InitFreeTSS (which fills them all in with default values)\r
388 \r
389                 PUSH 8                                          ; 8 pages for 64 TSSs (32768 bytes)\r
390                 MOV EAX, OFFSET pDynTSSs        ;\r
391                 PUSH EAX\r
392                 CALL FWORD PTR _AllocOSPage     ; Get 'em!\r
393 \r
394                 XOR EAX, EAX                            ; Clear allocated memory for TSSs\r
395                 MOV ECX, 8192                           ; (512 * 64 = 32768 = 8192 * 4)\r
396                 MOV EDI, pDynTSSs                       ; where to store 0s\r
397                 REP STOSD                                       ; Do it\r
398 \r
399                 MOV EAX, pDynTSSs\r
400                 MOV ECX, nTSS-2                         ; count of dynamic TSSs (- OS & Dbgr TSS)\r
401                 CALL InitFreeTSS                        ; Init the array of Process Control Blocks\r
402 \r
403                 CALL InitDynamicJCBs\r
404                 CALL InitDynamicRQBs\r
405 \r
406 ;================================================================\r
407 ; Allocate 1 page (4096 bytes) for 256 Exchanges (16*256=4096).\r
408 ; Exchanges are 16 bytes each.  Then zero the memory which has\r
409 ; the effect of initializing them because all fields in an Exch\r
410 ; are zero if not allocated.\r
411 \r
412                 PUSH 1                                          ; 1 pages for 256 Exchs (4096 bytes)\r
413                 MOV EAX, OFFSET pExchTmp        ; Returns ptr to allocated mem in pJCBs\r
414                 PUSH EAX                                        ;\r
415                 CALL FWORD PTR _AllocOSPage     ; Get it!\r
416 \r
417                 XOR EAX, EAX                            ; Clear allocated memory\r
418                 MOV ECX, 1024                           ; (4*1024=4096)\r
419                 MOV EDI, pExchTmp                       ; where to store 0s\r
420                 REP STOSD                                       ; Store EAX in 1024 locations\r
421 \r
422                 ;Now we move the contents of the 3 static exchanges\r
423                 ;into the dynamic array.  This is 60 bytes for 3\r
424                 ;exchanges.\r
425 \r
426                 MOV ESI, prgExch                        ; Source (static ones)\r
427                 MOV EDI, pExchTmp                       ; Destination (dynamic ones)\r
428                 MOV ECX, 12                                     ; 12 DWords (3 Exchanges)\r
429                 REP MOVSD                                       ; Move 'em!\r
430                 MOV EAX, pExchTmp                       ; The new ones\r
431                 MOV prgExch, EAX                        ; prgExch now points to new ones\r
432                 MOV nExch, nDynEXCH                     ; 256 to use (-3 already in use)\r
433 \r
434 ;================================================================\r
435 \r
436                 PUSH 7                                          ; Look at LPT IRQ\r
437                 CALL FWORD PTR _UnMAskIRQ       ;\r
438 \r
439                 CALL _Monitor                                   ;Head for the Monitor!!\r
440 \r
441                 ;The Monitor call never comes back (it better not...)\r
442 \r
443                 HLT                     ;Well, you never know (I AM human)\r
444 \r
445 \r
446 ;\r
447 \r
448 ;================== End of Main.asm ==============================\r