├── .gitignore ├── BIOS.asm ├── LICENSE ├── README.md ├── alarm.asm ├── bootloader.asm ├── echo.asm ├── hello.asm ├── hello.s ├── hello1.asm ├── hola.asm ├── hola.s ├── p.asm ├── powers.asm ├── powers.s └── triangle.asm /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /BIOS.asm: -------------------------------------------------------------------------------- 1 | BIOS-based disk I-O to access MS-DOS file structure 2 | 3 | ; rawread.asm 4 | ; 5 | ; this program reads a DOS cluster using only BIOS disk calls. All 6 | ; of the tasks usually done by DOS, e.g. FAT lookup, cluster to 7 | ; logical sector translation, logical to physical translation, are 8 | ; all done by this program instead. The idea is to be able to create 9 | ; a program that can access DOS disks from a bootable floppy without 10 | ; having to have DOS. 11 | ; 12 | ; well, that's what it used to do. Now it's supposed to do something 13 | ; completely different. Its job is to scan the entire surface of the 14 | ; hard drive, looking for the specified string. If that string is 15 | ; found, it is to print the full path and directory entry, including 16 | ; the file date and time. 17 | ; 18 | ; but wait! There's more. Now what we have is a number of raw 19 | ; routines which could prove useful for manipulating a DOS file 20 | ; structure outside of the DOS environment. The main routine still 21 | ; should be kept (if renamed), since the order in which these things 22 | ; are done is important (e.g. later calls depend on data set up by 23 | ; earlier calls). 24 | ; 25 | ; get filename 26 | ; parse filename into subdirs 27 | ; locate root dir and cluster size 28 | ; follow subdir routing to filename 29 | ; report file size, date & time 30 | ; 31 | .MODEL small 32 | .STACK 0200h 33 | .586P 34 | 35 | .DATA 36 | PartEntry STRUC 37 | Bootable db ? ;80h = bootable, 00h = nonbootable 38 | BeginHead db ? ;beginning head 39 | BeginSector db ? ;beginning sector 40 | BeginCylinder db ? ;beginning cylinder 41 | FileSystem db ? ;name of file system 42 | EndHead db ? ;ending head 43 | EndSector db ? ;ending sector 44 | EndCylinder db ? ;ending cylinder 45 | StartSector dd ? ;starting sector (relative to beg. of disk) 46 | PartSectors dd ? ;number of sectors in partition 47 | PartEntry ENDS 48 | 49 | BootSector STRUC 50 | Jump db ? ;E9 xx xx or EB xx 90 51 | JumpTarget dw ? ;E9 xx xx or EB xx 90 52 | OemName db '????????' ;OEM name & version 53 | ;Start of BIOS parameter block 54 | BytesPerSec dw ? ;bytes per sector 55 | SecPerClust db ? ;sectors per cluster 56 | ResSectors dw ? ;number of reserved sectors 57 | FATs db ? ;number of file allocation tables 58 | RootDirEnts dw ? ;number of root-dir entries 59 | Sectors dw ? ;total number of sectors 60 | Media db ? ;media descriptor byte 61 | FATsecs dw ? ;number of sectors per FAT 62 | SecPerTrack dw ? ;sectors per track 63 | Heads dw ? ;number of heads 64 | HiddenSecs dd ? ;number of hidden sectors 65 | HugeSectors dd ? ;num sectors if Sectors==0 66 | ;End of BIOS parameter block 67 | BootSector ENDS 68 | 69 | DirEntry STRUC 70 | FileName db '????????' ;name 71 | Extension db '???' ;extension 72 | Attributes db ? ;attributes 73 | Reserved db 10 dup (?) ;reserved 74 | Time dw ? ;time stamp 75 | Date dw ? ;date stamp 76 | StartCluster dw ? ;starting cluster 77 | FileSize dd ? ;file size 78 | DirEntry ENDS 79 | 80 | BootFileName db "CONFIG SYS" ;the boot loader for this OS 81 | MBR DB 0200h DUP (?) 82 | buff DB 0200h * 40h DUP (?) 83 | ClustOffs dd ? 84 | 85 | CR EQU 0DH 86 | LF EQU 0AH 87 | 88 | 89 | .CODE 90 | main PROC 91 | STARTUPCODE ;initialize stuff 92 | call FetchMBR C ;fetch the master boot record 93 | jc @@exit 94 | mov cx,4 ;search up to four partitions 95 | add bx,01aeh ;point to partition table (-10h) 96 | @@FindBootable: 97 | add bx,10h ;point to next entry 98 | cmp BYTE ptr [bx],80h ;is it a bootable partition? 99 | loopnz @@FindBootable 100 | call FetchSector C, \ 101 | WORD ptr [(PartEntry PTR bx).BeginHead], \ 102 | WORD ptr [(PartEntry PTR bx).BeginSector], \ 103 | WORD ptr [(PartEntry PTR bx).BeginCylinder], \ 104 | OFFSET MBR, ds ;SEG MBR 105 | ; 106 | ; here's the point at which our OS loader would begin, with the 107 | ; BootSector structure in memory. 108 | ; 109 | mov bx, OFFSET MBR 110 | call CalcClustOff C, \ 111 | WORD ptr [(BootSector PTR bx).ResSectors], \ 112 | WORD ptr [(BootSector PTR bx).FATsecs], \ 113 | WORD ptr [(BootSector PTR bx).FATs], \ 114 | WORD ptr [(BootSector PTR bx).RootDirEnts], \ 115 | WORD ptr [(BootSector PTR bx).BytesPerSec], \ 116 | WORD ptr [(BootSector PTR bx).SecPerClust] 117 | mov WORD ptr [ClustOffs],ax 118 | mov WORD ptr [ClustOffs+2],dx 119 | call CalcClust2 C, \ 120 | WORD ptr [(BootSector PTR bx).ResSectors], \ 121 | WORD ptr [(BootSector PTR bx).FATsecs], \ 122 | WORD ptr [(BootSector PTR bx).FATs] 123 | ; now dx:ax contains the logical sector for cluster 2 124 | call LsectToGeom C, \ 125 | ax, dx, \ 126 | WORD ptr [(BootSector PTR bx).HiddenSecs] , \ 127 | WORD ptr [((BootSector PTR bx).HiddenSecs)+2],\ 128 | [(BootSector PTR bx).Heads], \ 129 | [(BootSector PTR bx).SecPerTrack] 130 | 131 | mov dl,80h 132 | mov bx,offset buff 133 | mov al,[(BootSector PTR MBR).SecPerClust] 134 | mov ah,2h ; get ready to read 135 | int 13h 136 | ; now find our desired filename within buffer (which has the root dir) 137 | 138 | call FindFile C, \ 139 | bx, 200h * 40h, offset BootFileName 140 | xor dh,dh 141 | mov dl,[(BootSector PTR MBR).SecPerClust] 142 | mov si,ax 143 | mov ax,[(DirEntry PTR si).StartCluster] 144 | mul dx 145 | add ax,WORD ptr [ClustOffs] 146 | adc dx,WORD ptr [ClustOffs+2] 147 | ; now dx:ax contains logical sector number for start of file 148 | 149 | call LsectToGeom C, \ 150 | ax, dx, \ 151 | WORD ptr [(BootSector PTR MBR).HiddenSecs] , \ 152 | WORD ptr [((BootSector PTR MBR).HiddenSecs)+2],\ 153 | [(BootSector PTR MBR).Heads], \ 154 | [(BootSector PTR MBR).SecPerTrack] 155 | mov dl,80h 156 | mov ax,204h ; read in 2k worth of data 157 | int 13h 158 | 159 | @@exit: 160 | EXITCODE ;exit to DOS 161 | ENDP main 162 | 163 | ; 164 | ; FetchMBR - fetches the Master Boot Record from the first physical 165 | ; hard disk and stores it in the location MBR. 166 | ; 167 | ; INPUT: none 168 | ; OUTPUT: AX is error code if CY set, ES:BX ==> MBR 169 | ; DESTROYED: none 170 | ; 171 | FetchMBR PROC C 172 | USES cx, dx ;save registers we'll use 173 | mov dx,80h ;first physical disk 174 | mov cx,1 ;head 1, sector 0 175 | mov bx,ds ; 176 | mov es,bx ;point to boot record buffer 177 | mov bx,OFFSET MBR ;read into boot record 178 | mov ax,0201h ;read one sector 179 | int 13h ;BIOS read 180 | ret ;return to main 181 | FetchMBR ENDP 182 | 183 | ; 184 | ; FetchSector - fetches the physical sector described by the passed 185 | ; parameters and stores it in the named buffer 186 | ; 187 | ; INPUT: head, sector, cylinder, buffer 188 | ; OUTPUT: AX is error code if CY set, ES:BX ==> Boot 189 | ; DESTROYED: none 190 | ; 191 | FetchSector PROC C head:BYTE, sector:BYTE, cylinder:BYTE, buffer:DWORD 192 | USES cx, dx ;save registers we'll use 193 | mov ch, [cylinder] ; 194 | mov cl, [sector] ; 195 | mov dh, [head] ; 196 | mov dl, 80h ;first physical hard drive 197 | les bx, [buffer] ; 198 | mov ax,0201h ;read one sector 199 | int 13h ;BIOS read 200 | ret ;return to main 201 | FetchSector ENDP 202 | 203 | ; 204 | ; GeomToLsect - converts to logical sector number from the physical 205 | ; geometry (head, cylinder, track). See LsectToGeom. 206 | ; 207 | ; INPUT: cx, dx are set with cylinder/track, and head respectively 208 | ; HiddenSecs, Heads, SecPerTrack 209 | ; OUTPUT: lsect 210 | ; DESTROYED: none 211 | ; 212 | GeomToLsect PROC C lsect:DWORD, dHiddenSecs:DWORD, \ 213 | dHeads:WORD, dSecPerTrack:WORD, buffer:DWORD 214 | USES ax ;save registers we'll use 215 | mov ax, WORD ptr [lsect] ;load lsect into DX:AX 216 | mov dx, WORD ptr [lsect+2] ; 217 | stc ;add one additional 218 | adc ax, WORD ptr [dHiddenSecs] ;add starting sector 219 | adc dx, WORD ptr [dHiddenSecs+2] ; 220 | div [dSecPerTrack] ; 221 | mov cl,dl ;store sector in cl 222 | xor dx,dx ; 223 | div [dHeads] ; 224 | mov dh,dl ;store head in dh 225 | mov ch,al ;store low 8 bits of cylinder in ch 226 | shr ax,1 ; 227 | shr ax,1 ; 228 | and al,0c0h ;pass through two hi bits only 229 | or cl,ah ;mov bits into location 230 | ret ; 231 | GeomToLsect ENDP 232 | 233 | ; 234 | ; LsectToGeom - converts from logical sector number to the physical 235 | ; geometry (head, cylinder, track) in the form required 236 | ; by the BIOS (Int 13h) disk read and write calls. 237 | ; 238 | ; INPUT: lsect, HiddenSecs, Heads, SecPerTrack 239 | ; OUTPUT: cx, dx are set with cylinder/track, and head respectively 240 | ; DESTROYED: none 241 | ; 242 | LsectToGeom PROC C lsect:DWORD, lHiddenSecs:DWORD, \ 243 | lHeads:WORD, lSecPerTrack:WORD, buffer:DWORD 244 | USES ax ;save registers we'll use 245 | mov ax, WORD ptr [lsect] ;load lsect into DX:AX 246 | mov dx, WORD ptr [lsect+2] ; 247 | stc ;add one additional 248 | adc ax, WORD ptr [lHiddenSecs] ;add starting sector 249 | adc dx, WORD ptr [lHiddenSecs+2] ; 250 | div [lSecPerTrack] ; 251 | mov cl,dl ;store sector in cl 252 | xor dx,dx ; 253 | div [lHeads] ; 254 | mov dh,dl ;store head in dh 255 | mov ch,al ;store low 8 bits of cylinder in ch 256 | shr ax,1 ; 257 | shr ax,1 ; 258 | and al,0c0h ;pass through two hi bits only 259 | or cl,ah ;mov bits into location 260 | ret ; 261 | LsectToGeom ENDP 262 | 263 | ; 264 | ; CalcClust2 - calculates the starting logical sector number of 265 | ; cluster 2, (the beginning of data space for 266 | ; partitions). 267 | ; 268 | ; INPUT: ResSectors, FATsecs, FATs 269 | ; OUTPUT: dx:ax contains the starting logical sector number 270 | ; DESTROYED: none 271 | ; 272 | CalcClust2 PROC C cResSectors:WORD, cFATsecs:WORD, cFATs:BYTE 273 | xor dx,dx ; 274 | mov ax,[cFATsecs] ; 275 | mul [cFATs] ; 276 | add ax,[cResSectors] ; 277 | adc dx,0 ; 278 | ret 279 | CalcClust2 ENDP 280 | 281 | ; 282 | ; CalcClustOff - calculates the starting logical sector number of 283 | ; cluster 0, which isn't really a cluster, but the 284 | ; number returned is useful for calculations converting 285 | ; cluster number to logical sector 286 | ; 287 | ; INPUT: ResSectors, FATsecs, FATs 288 | ; OUTPUT: dx:ax contains the starting logical sector number 289 | ; DESTROYED: none 290 | ; 291 | CalcClustOff PROC C dResSectors:WORD, dFATsecs:WORD, dFATs:BYTE, \ 292 | dRootDirEnts:WORD, dBytesPerSec:WORD, dSecPerClust:BYTE 293 | LOCAL clustLo:WORD, clustHi:WORD 294 | xor dh,dh 295 | mov ax,[dFatSecs] 296 | mov dl,[dFATs] 297 | mul dx 298 | add ax,[dResSectors] 299 | adc dx,0 300 | ; call CalcClust2 C, [dResSectors], [dFATsecs], [dFATs] 301 | ; now dx:ax = FATs * FATsecs + ResSectors 302 | mov [clustLo],ax 303 | mov [clustHi],dx 304 | mov dx,20h ; bytes per dir entry 305 | mov ax,[dRootDirEnts] ; 306 | mul dx ; multiply 'em out 307 | div [dBytesPerSec] ; and divide by bytes/sec 308 | add [clustLo],ax ; 309 | adc [clustHi],dx ; create the aggregate 310 | mov al,[dSecPerClust] ; 311 | xor ah,ah ; 312 | shl ax,1 ; AX = SecPerClust * 2 313 | sub [clustLo],ax ; 314 | sbb [clustHi],0 ; propagate carry flag 315 | mov ax,[clustLo] ; 316 | mov dx,[clustHi] ; 317 | ret 318 | CalcClustOff ENDP 319 | 320 | ; 321 | ; FindFile - given a memory buffer containing the directory data 322 | ; and a static file name for which to search, this routine 323 | ; finds the file and returns a pointer to its directory 324 | ; entry in ds:si 325 | ; 326 | ; INPUT: dirbuffer, filespec 327 | ; OUTPUT: ax contains pointer to directory entry (or NULL) 328 | ; DESTROYED: none 329 | ; 330 | FindFile PROC C dirbuffer:WORD, limit:WORD, filespec:WORD 331 | USES cx, dx, di, si, es 332 | mov cx,ds ; 333 | mov es,cx ; es and ds point to same segment 334 | cld ; always count forward 335 | mov ax,[dirbuffer] ; load 'em up 336 | add [limit],ax 337 | mov dx,[filespec] ; 338 | keepsearching: 339 | mov cx,11 ; size of dos filename (8.3) 340 | mov si,dx ; 341 | mov di,ax ; 342 | repe cmpsb ; compare 'em 343 | jz foundit ; 344 | add ax,20h ; size of directory entry 345 | cmp ax,[limit] 346 | jb keepsearching 347 | xor ax,ax 348 | 349 | foundit: 350 | ret 351 | FindFile ENDP 352 | END 353 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Pourya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # assembly 2 | OpenGL Programming in NASM for Win32 3 | 4 | For fun, here is a complete assembly language program that implements an OpenGL application running under GLUT on Windows systems 5 | 6 | 7 | Differences between NASM, MASM, and GAS 8 | The complete syntactic specification of each assembly language can be found elsewhere, but you can learn 99% of what you need to know by looking at a comparison table: 9 | 10 | Good to know: 11 | 12 | NASM and MASM use what is sometimes called the Intel syntax, while GAS uses what is called the AT&T syntax. 13 | 14 | GAS uses % to prefix registers 15 | 16 | GAS is source(s) first, destination last; MASM and NASM go the other way. 17 | 18 | GAS denotes operand sizes on instructions (with b, w, l suffixes), rather than on operands 19 | GAS uses $ for immediates, but also for addresses of variables. 20 | 21 | GAS puts rep/repe/repne/repz/repnz prefixes on separate lines from the instructions they modify 22 | 23 | MASM tries to simplify things for the programmer but makes headaches instead: 24 | 25 | it tries to "remember" segments, variable sizes and so on. 26 | 27 | The result is a requirement for stupid ASSUME directives, and the inability to tell what an instruction does by looking at it (you have to go look for declarations; e.g. dw vs. equ). 28 | 29 | 30 | MASM writes FPU registers as ST(0), ST(1), etc. 31 | 32 | 33 | NASM treats labels case-sensitively; MASM is case-insensitive. 34 | 35 | 36 | 37 | 38 | There are many object file formats. Some you should know about include 39 | 40 | 41 | OMF: used in DOS but has 32-bit extensions for Windows. Old. 42 | 43 | AOUT: used in early Linux and BSD variants 44 | 45 | COFF: "Common object file format" 46 | 47 | Win, Win32: Microsoft’s version of COFF, not exactly the same! Replaces OMF. 48 | 49 | Win64: Microsoft’s format for Win64. 50 | 51 | ELF, ELF32: Used in modern 32-bit Linux and elsewhere 52 | 53 | ELF64: Used in 64-bit Linux and elsewhere 54 | 55 | macho32: NeXTstep/OpenStep/Rhapsody/Darwin/macOS 32-bit 56 | 57 | macho64: NeXTstep/OpenStep/Rhapsody/Darwin/macOS 64-bit 58 | 59 | The NASM documentation has great descriptions of these. 60 | 61 | You’ll need to get a linker that (1) understands the object file formats you produce, and (2) can write executables for the operating systems you want to run code on. 62 | 63 | Some linkers out there include 64 | 65 | LINK.EXE, for Microsoft operating systems. 66 | 67 | 68 | which exists on all Unix systems; Windows programmers get this in any gcc distribution. 69 | 70 | 71 | -------------------------------------------------------------------------------- /alarm.asm: -------------------------------------------------------------------------------- 1 | Alarm 2 | 3 | cseg segment para public 'code' 4 | org 100h 5 | alarm proc far 6 | 7 | ; Memory-resident program to intercept the timer interrupt and display the 8 | ; system time in the upper right-hand corner of the display. 9 | ; This program is run as 'ALARM hh:mm x', where hh:mm is the alarm time and 10 | ; x is '-' to turn the display off. Any other value of x or no value will 11 | ; turn the clock on 12 | 13 | intaddr equ 1ch*4 ; interrupt address 14 | segaddr equ 62h*4 ; segment address of first copy 15 | mfactor equ 17478 ; minute conversion factor * 16 16 | whozat equ 1234h ; signature 17 | color equ 14h ; color attribute 18 | 19 | assume cs:cseg,ds:cseg,ss:nothing,es:nothing 20 | jmp p150 ; start-up code 21 | 22 | jumpval dd 0 ; address of prior interrupt 23 | signature dw whozat ; program signature 24 | state db 0 ; '-' = off, all else = on 25 | wait dw 18 ; wait time - 1 second or 18 ticks 26 | hour dw 0 ; hour of the day 27 | atime dw 0ffffh ; minutes past midnite for alarm 28 | acount dw 0 ; alarm beep counter - number of seconds (5) 29 | atone db 5 ; alarm tone - may be from 1 to 255 - the 30 | ; higher the number, the lower the frequency 31 | aleng dw 8080h ; alarm length (loop count) may be from 1-FFFF 32 | 33 | dhours dw 0 ; display hours 34 | db ':' 35 | dmins dw 0 ; display minutes 36 | db ':' 37 | dsecs dw 0 ; display seconds 38 | db '-' 39 | ampm db 0 ; 'A' or 'P' for am or pm 40 | db 'm' 41 | 42 | tstack db 16 dup('stack ') ; temporary stack 43 | estack db 0 ; end of stack 44 | holdsp dw 0 ; original sp 45 | holdss dw 0 ; original ss 46 | 47 | p000: ; interrupt code 48 | push ax ; save registers 49 | push ds 50 | pushf 51 | 52 | push cs 53 | pop ds ; make ds=cs 54 | mov ax,wait ; check wait time 55 | dec ax ; zero? 56 | jz p010 ; yes - 1 second has elapsed 57 | mov wait,ax ; not this time 58 | jmp p080 ; return 59 | 60 | p010: cli ; disable interrupts 61 | mov ax,ss ; save stack 62 | mov holdss,ax 63 | mov holdsp,sp 64 | mov ax,ds 65 | mov ss,ax ; point to internal stack 66 | mov sp,offset estack 67 | sti ; allow interrupts 68 | 69 | push bx ; save other registers 70 | push cx 71 | push dx 72 | push es 73 | push si 74 | push di 75 | push bp 76 | 77 | mov ax,18 ; reset wait time 78 | mov wait,ax 79 | 80 | mov al,state ; are we disabled? 81 | cmp al,'-' 82 | jnz p015 ; no 83 | jmp p070 84 | 85 | p015: mov ah,0 ; read time 86 | int 1ah ; get time of day 87 | mov ax,dx ; low part 88 | mov dx,cx ; high part 89 | mov cl,4 90 | shl dx,cl ; multiply by 16 91 | mov bx,ax 92 | mov cl,12 93 | shr bx,cl ; isolate top 4 bits of ax 94 | add dx,bx ; now in upper 95 | mov cl,4 96 | shl ax,cl ; multiply by 16 97 | mov bx,mfactor ; compute minutes 98 | div bx ; minutes in ax, remainder in dx 99 | cmp ax,atime ; time to sound the alarm? 100 | jnz p020 ; no 101 | call p100 ; yes - beep the speaker twice 102 | push ax 103 | mov ax,acount ; get beep count 104 | dec ax ; down by 1 105 | mov acount,ax ; save beep count 106 | cmp ax,0 ; is it zero? 107 | jnz p018 ; no - keep alarm on 108 | mov ax,0ffffh ; turn off alarm 109 | mov atime,ax 110 | p018: pop ax 111 | 112 | p020: mov dsecs,dx ; save remainder 113 | mov bx,60 ; compute hours 114 | xor dx,dx ; zero it 115 | div bx ; hours in ax, minutes in dx 116 | mov dmins,dx ; save minutes 117 | 118 | cmp ax,0 ; midnight? 119 | jnz p030 ; no 120 | mov ax,12 ; yes 121 | jmp p040a ; set am 122 | 123 | p030: cmp ax,12 ; before noon? 124 | jb p040a ; yes - set am 125 | jz p040p ; noon - set pm 126 | sub ax,12 ; convert the rest 127 | p040p: mov bl,'p' 128 | jmp p040x 129 | 130 | p040a: mov bl,'a' 131 | 132 | p040x: mov ampm,bl 133 | aam ; fix up hour 134 | cmp ax,hour ; top of the hour? 135 | jz p060 ; no 136 | 137 | mov hour,ax 138 | call p120 ; beep the speaker once 139 | 140 | p060: add ax,3030h ; convert hours to ascii 141 | xchg ah,al 142 | mov dhours,ax 143 | 144 | mov ax,dmins ; get minutes 145 | aam 146 | add ax,3030h ; convert to ascii 147 | xchg ah,al 148 | mov dmins,ax 149 | 150 | mov ax,dsecs ; get seconds (remainder) 151 | xor dx,dx 152 | mov bx,60 153 | mul bx 154 | mov bx,mfactor 155 | div bx ; seconds in ax 156 | aam 157 | add ax,3030h 158 | xchg ah,al 159 | mov dsecs,ax 160 | 161 | xor ax,ax ; check monitor type 162 | mov es,ax 163 | mov ax,es:[410h] ; get config byte 164 | and al,30h ; isolate monitor type 165 | cmp al,30h ; color? 166 | mov ax,0b000h ; assume mono 167 | jz p061 ; its mono 168 | 169 | mov ax,0b800h ; color screen address 170 | 171 | p061: mov dx,es:[463h] ; point to 6845 base port 172 | add dx,6 ; point to status port 173 | 174 | mov es,ax ; point to monitor 175 | mov bh,color ; color in bh 176 | mov si,offset dhours ; point to time 177 | mov di,138 ; row 1, col 69 178 | cld 179 | mov cx,11 ; loop count 180 | 181 | p062: mov bl,[si] ; get next character 182 | 183 | p063: in al,dx ; get crt status 184 | test al,1 ; is it low? 185 | jnz p063 ; no - wait 186 | cli ; no interrupts 187 | 188 | p064: in al,dx ; get crt status 189 | test al,1 ; is it high? 190 | jz p064 ; no - wait 191 | 192 | mov ax,bx ; move color & character 193 | stosw ; move color & character again 194 | sti ; interrupts back on 195 | inc si ; point to next character 196 | loop p062 ; done? 197 | 198 | p070: pop bp ; restore registers 199 | pop di 200 | pop si 201 | pop es 202 | pop dx 203 | pop cx 204 | pop bx 205 | cli ; no interrupts 206 | mov ax,holdss 207 | mov ss,ax 208 | mov sp,holdsp 209 | sti ; allow interrupts 210 | 211 | p080: popf 212 | pop ds 213 | pop ax 214 | jmp cs:[jumpval] 215 | 216 | p100 proc near ; beep the speaker twice 217 | call p120 218 | push cx 219 | mov cx,20000 220 | p105: loop p105 ; wait around 221 | pop cx 222 | call p120 223 | push cx 224 | mov cx,20000 225 | p106: loop p106 ; wait around 226 | pop cx 227 | call p120 228 | ret 229 | p100 endp 230 | 231 | p120 proc near ; beep the speaker once 232 | push ax 233 | push cx 234 | mov al,182 235 | out 43h,al ; setup for sound 236 | mov al,0 237 | out 42h,al ; low part 238 | mov al,atone ; get alarm tone 239 | out 42h,al ; high part 240 | in al,61h 241 | push ax ; save port value 242 | or al,3 243 | out 61h,al ; turn speaker on 244 | mov cx,aleng ; get loop count 245 | p125: loop p125 ; wait around 246 | pop ax ; restore original port value 247 | out 61h,al ; turn speaker off 248 | pop cx 249 | pop ax 250 | ret 251 | p120 endp 252 | 253 | p150: ; start of transient code 254 | mov dx,offset copyr 255 | call p220 ; print copyright 256 | mov ax,0 257 | mov es,ax ; segment 0 258 | mov di,segaddr+2 ; this program's prior location 259 | mov ax,es:[di] ; get prior code segment 260 | mov es,ax ; point to prior program segment 261 | mov di,offset signature 262 | mov cx,es:[di] ; is it this program? 263 | cmp cx,whozat 264 | jnz p160 ; no - install it 265 | call p200 ; set state & alarm 266 | int 20h ; terminate 267 | 268 | p160: mov di,segaddr+2 ; point to int 62h 269 | mov ax,0 270 | mov es,ax ; segment 0 271 | mov ax,ds ; get current ds 272 | mov es:[di],ax ; set int 62h 273 | mov si,offset jumpval 274 | mov di,intaddr ; point to timer interrupt 275 | mov bx,es:[di] ; get timer ip 276 | mov ax,es:[di+2] ; and cs 277 | mov [si],bx ; save prior ip 278 | mov [si+2],ax ; and cs 279 | mov bx,offset p000 280 | mov ax,ds 281 | cli ; clear interrupts 282 | mov es:[di],bx ; set new timer interrupt 283 | mov es:[di+2],ax 284 | sti ; set interrupts 285 | push ds 286 | pop es 287 | call p200 ; set state & alarm 288 | mov dx,offset p150 ; last byte of resident portion 289 | inc dx 290 | int 27h ; terminate 291 | 292 | p200 proc near ; set state & alarm 293 | mov si,80h ; point to command line 294 | mov ax,0 295 | mov di,0ffffh ; init hours 296 | mov bh,0 297 | mov ch,0 298 | mov dh,0 ; : counter 299 | mov es:[state],bh ; turn clock on 300 | mov cl,[si] ; get length 301 | jcxz p210 ; it's zero 302 | 303 | p203: inc si ; point to next char 304 | mov bl,[si] ; get it 305 | cmp bl,'-' ; is it a minus? 306 | jnz p204 ; no 307 | mov es:[state],bl ; turn clock off 308 | push dx 309 | mov dx,offset msg3 ; print msg 310 | call p220 311 | pop dx 312 | jmp p206 313 | 314 | p204: cmp dh,2 ; seen 2nd colon? 315 | jz p206 ; yes - ignore seconds 316 | cmp bl,':' ; colon? 317 | jnz p205 ; no 318 | inc dh 319 | cmp dh,2 ; second colon? 320 | jz p206 ; yes - ignore seconds 321 | push cx 322 | push dx 323 | mov cx,60 324 | mul cx ; multiply current ax by 60 325 | pop dx 326 | pop cx 327 | mov di,ax ; save hours 328 | mov ax,0 329 | jmp p206 330 | p205: cmp bl,'0' 331 | jb p206 ; too low 332 | cmp bl,'9' 333 | ja p206 ; too high - can be a problem 334 | sub bl,'0' ; convert it to binary 335 | push cx 336 | push dx 337 | mov cx,10 338 | mul cx ; multiply current value by 10 339 | add ax,bx ; and add latest digit 340 | pop dx 341 | pop cx 342 | p206: loop p203 ; done yet? 343 | cmp di,0ffffh ; any time to set? 344 | jz p210 ; no 345 | add ax,di ; add hours 346 | cmp ax,24*60 347 | jb p209 ; ok 348 | mov dx,offset msg1 ; print error message 349 | call p220 350 | jmp p210 351 | 352 | p209: mov es:[atime],ax ; save minutes past midnight 353 | mov ax,5 354 | mov es:[acount],ax ; set alarm count 355 | mov dx,offset msg2 ; print set msg 356 | call p220 357 | p210: ret 358 | p200 endp 359 | 360 | p220 proc near ; print message 361 | push ax 362 | mov ah,9 363 | int 21h 364 | pop ax 365 | ret 366 | p220 endp 367 | 368 | copyr db 'Alarm - Clock',10,13,'$' 369 | msg1 db 'Invalid time - must be from 00:00 to 23:59',10,13,'$' 370 | msg2 db 'Resetting alarm time',10,13,'$' 371 | msg3 db 'Turning clock display off',10,13,'$' 372 | 373 | alarm endp 374 | cseg ends 375 | end alarm 376 | -------------------------------------------------------------------------------- /bootloader.asm: -------------------------------------------------------------------------------- 1 | Boot loader for a roll-your-own operating system 2 | 3 | ; loader.asm 4 | 5 | PartEntry STRUC 6 | Bootable db ? ;80h = bootable, 00h = nonbootable 7 | BeginHead db ? ;beginning head 8 | BeginSector db ? ;beginning sector 9 | BeginCylinder db ? ;beginning cylinder 10 | FileSystem db ? ;name of file system 11 | EndHead db ? ;ending head 12 | EndSector db ? ;ending sector 13 | EndCylinder db ? ;ending cylinder 14 | StartSector dd ? ;starting sector (relative to beg. of disk) 15 | PartSectors dd ? ;number of sectors in partition 16 | PartEntry ENDS 17 | 18 | BootSector STRUC 19 | bsJump db 0EBh, (extra - bsJump), 090h 20 | ; E9 XX XX or EB xx 90 21 | OemName db 8 dup (?) ; OEM name and version 22 | ; start of BIOS parameter block 23 | BytesPerSec dw ? ; bytes per sector 24 | SecPerClust db ? ; sectors per cluster 25 | ResSectors dw ? ; number of reserved sectors 26 | FATs db ? ; number of FATs 27 | RootDirEnts dw ? ; number of root directory entries 28 | Sectors dw ? ; total number of sectors (see HugeSectors) 29 | Media db ? ; media descriptor byte (0f0h for floppies) 30 | FATsecs dw ? ; number of sectors per FAT 31 | SecPerTrack dw ? ; sectors per track 32 | Heads dw ? ; number of heads 33 | HiddenSecs dd ? ; number of hidden sectors 34 | HugeSectors dd ? ; number of sectors if Sectors equals 0 35 | ; end of BIOS parameter block 36 | DriveNumber db ? ; 37 | Reserved1 db ? ; 38 | BootSignature db ? ; 39 | VolumeID dd ? ; 40 | VolumeLabel db 11 dup (?) 41 | FileSysType db 8 dup (?) 42 | extra dw ? 43 | BootSector ENDS 44 | 45 | DirEntry STRUC 46 | FileName db '????????' ;name 47 | Extension db '???' ;extension 48 | Attributes db ? ;attributes 49 | Reserved db 10 dup (?) ;reserved 50 | Time dw ? ;time stamp 51 | Date dw ? ;date stamp 52 | StartCluster dw ? ;starting cluster 53 | FileSize dd ? ;file size 54 | DirEntry ENDS 55 | 56 | CR EQU 0DH 57 | LF EQU 0AH 58 | 59 | yonder segment para public use16 at 2000h 60 | org 0h 61 | destination proc far 62 | destination endp 63 | yonder ends 64 | 65 | 66 | code segment para public use16 '_CODE' 67 | .386 68 | assume cs:code, ds:code, es:code, ss:code 69 | org 7c00h 70 | main PROC 71 | MBR: 72 | Boot bootsector < ,'BEROSET ',512,1,1,2,224,2880,0f0h,9,18,2,\ 73 | 0,0,0,0,29h,02a04063ch,'BEROSET 001',\ 74 | 'FAT12 ',07df1h> 75 | over: 76 | mov ax,cs ; 77 | cli 78 | mov ss,ax ; point ss:sp to CS:7c00h 79 | mov sp,7c00h ; which sets up a stack in first 64K 80 | sti 81 | mov ds,ax 82 | mov es,ax 83 | ;**************************************************************************** 84 | ; 85 | ; CalcClustOff - calculates the starting logical sector number of 86 | ; cluster 0, which isn't really a cluster, but the 87 | ; number returned is useful for calculations converting 88 | ; cluster number to logical sector 89 | ; 90 | ; INPUT: ResSectors, FATsecs, FATs 91 | ; OUTPUT: dx:ax contains the starting logical sector number 92 | ; DESTROYED: none 93 | ; 94 | ;**************************************************************************** 95 | CalcClustOff PROC 96 | xor dh,dh 97 | mov ax,[Boot.FatSecs] 98 | mov dl,[Boot.FATs] 99 | mul dx 100 | add ax,[Boot.ResSectors] 101 | adc dx,0 102 | ; now dx:ax = FATs * FATsecs + ResSectors 103 | mov word ptr [ClustOffs],ax 104 | mov word ptr [ClustOffs+2],dx 105 | mov dx,20h ; bytes per dir entry 106 | mov ax,[Boot.RootDirEnts] 107 | mul dx ; multiply 'em out 108 | div word ptr [Boot.BytesPerSec] ; and divide by bytes/sec 109 | add word ptr [ClustOffs],ax 110 | adc word ptr [ClustOffs+2],dx ; create the aggregate 111 | mov al,[Boot.SecPerClust] ; 112 | xor ah,ah ; 113 | shl ax,1 ; AX = SecPerClust * 2 114 | sub word ptr [ClustOffs],ax ; 115 | sbb word ptr [ClustOffs+2],0 ; propagate carry flag 116 | ; mov ax,word ptr [ClustOffs] ; 117 | ; mov dx,word ptr [ClustOffs+2] ; 118 | ; ret 119 | CalcClustOff ENDP 120 | 121 | ; mov WORD ptr [ClustOffs],ax 122 | ; mov WORD ptr [ClustOffs+2],dx 123 | mov bx,offset Boot 124 | call CalcClust2 C, \ 125 | WORD ptr [(BootSector PTR bx).ResSectors], \ 126 | WORD ptr [(BootSector PTR bx).FATsecs], \ 127 | WORD ptr [(BootSector PTR bx).FATs] 128 | ; now dx:ax contains the logical sector for cluster 2 129 | call LsectToGeom C, \ 130 | WORD ptr [(BootSector PTR bx).HiddenSecs] , \ 131 | WORD ptr [((BootSector PTR bx).HiddenSecs)+2],\ 132 | [(BootSector PTR bx).Heads], \ 133 | [(BootSector PTR bx).SecPerTrack] 134 | 135 | mov dl,[(BootSector PTR bx).DriveNumber] 136 | mov bx,offset buff 137 | retry1: 138 | mov al,[(BootSector PTR MBR).SecPerClust] 139 | mov ah,2h ; get ready to read 140 | int 13h 141 | jc retry1 142 | ; now find our desired filename within buffer (which has the root dir) 143 | 144 | call FindFile C, \ 145 | bx, 200h * 40h, offset BootFileName 146 | xor dh,dh 147 | mov dl,[(BootSector PTR MBR).SecPerClust] 148 | mov si,ax 149 | mov ax,[(DirEntry PTR si).StartCluster] 150 | mul dx 151 | add ax,WORD ptr [ClustOffs] 152 | adc dx,WORD ptr [ClustOffs+2] 153 | ; now dx:ax contains logical sector number for start of file 154 | 155 | call LsectToGeom C, \ 156 | WORD ptr [(BootSector PTR MBR).HiddenSecs] , \ 157 | WORD ptr [((BootSector PTR MBR).HiddenSecs)+2],\ 158 | [(BootSector PTR MBR).Heads], \ 159 | [(BootSector PTR MBR).SecPerTrack] 160 | retry2: 161 | mov si,offset Boot 162 | mov dl,[(BootSector PTR si).DriveNumber] 163 | mov ah,2h 164 | ; read in a cluster's worth of data 165 | mov al,[(BootSector PTR si).SecPerClust] 166 | ; point to our magic location 167 | mov bx,seg destination 168 | mov es,bx 169 | mov bx,offset destination 170 | int 13h 171 | jc retry2 172 | @@exit: 173 | jmp destination 174 | ENDP main 175 | 176 | ;**************************************************************************** 177 | ; 178 | ; LsectToGeom - converts from logical sector number to the physical 179 | ; geometry (head, cylinder, track) in the form required 180 | ; by the BIOS (Int 13h) disk read and write calls. 181 | ; 182 | ; INPUT: dx:ax=lsect, HiddenSecs, Heads, SecPerTrack 183 | ; OUTPUT: cx, dx are set with cylinder/track, and head respectively 184 | ; DESTROYED: none 185 | ;**************************************************************************** 186 | LsectToGeom PROC C lHiddenSecs:DWORD, \ 187 | lHeads:WORD, lSecPerTrack:WORD, buffer:DWORD 188 | USES ax ;save registers we'll use 189 | stc ;add one additional 190 | adc ax, WORD ptr [lHiddenSecs] ;add starting sector 191 | adc dx, WORD ptr [lHiddenSecs+2] ; 192 | div [lSecPerTrack] ; 193 | mov cl,dl ;store sector in cl 194 | xor dx,dx ; 195 | div [lHeads] ; 196 | mov dh,dl ;store head in dh 197 | mov ch,al ;store low 8 bits of cylinder in ch 198 | shr ax,1 ; 199 | shr ax,1 ; 200 | and al,0c0h ;pass through two hi bits only 201 | or cl,ah ;mov bits into location 202 | ret ; 203 | LsectToGeom ENDP 204 | 205 | ;**************************************************************************** 206 | ; 207 | ; CalcClust2 - calculates the starting logical sector number of 208 | ; cluster 2, (the beginning of data space for 209 | ; partitions). 210 | ; 211 | ; INPUT: ResSectors, FATsecs, FATs 212 | ; OUTPUT: dx:ax contains the starting logical sector number 213 | ; DESTROYED: none 214 | ; 215 | ;**************************************************************************** 216 | CalcClust2 PROC C cResSectors:WORD, cFATsecs:WORD, cFATs:BYTE 217 | xor dx,dx ; 218 | mov ax,[cFATsecs] ; 219 | mul [cFATs] ; 220 | add ax,[cResSectors] ; 221 | adc dx,0 ; 222 | ret 223 | CalcClust2 ENDP 224 | 225 | ;**************************************************************************** 226 | ; 227 | ; FindFile - given a memory buffer containing the directory data 228 | ; and a static file name for which to search, this routine 229 | ; finds the file and returns a pointer to its directory 230 | ; entry in ds:si 231 | ; 232 | ; INPUT: dirbuffer, filespec 233 | ; OUTPUT: ax contains pointer to directory entry (or NULL) 234 | ; DESTROYED: none 235 | ;**************************************************************************** 236 | 237 | FindFile PROC C dirbuffer:WORD, limit:WORD, filespec:WORD 238 | USES cx, dx, di, si, es 239 | mov cx,ds ; 240 | mov es,cx ; es and ds point to same segment 241 | cld ; always count forward 242 | mov ax,[dirbuffer] ; load 'em up 243 | add [limit],ax 244 | mov dx,[filespec] ; 245 | keepsearching: 246 | mov cx,11 ; size of dos filename (8.3) 247 | mov si,dx ; 248 | mov di,ax ; 249 | repe cmpsb ; compare 'em 250 | jz foundit ; 251 | add ax,20h ; size of directory entry 252 | cmp ax,[limit] 253 | jb keepsearching 254 | xor ax,ax 255 | 256 | foundit: 257 | ret 258 | FindFile ENDP 259 | 260 | 261 | BootFileName db "BEROSET SYS" ;the boot loader for this OS 262 | ; MBR db 0200h DUP (?) 263 | buff db 0200h * 40h DUP (?) 264 | ClustOffs dd ? 265 | org 7dfeh 266 | dw 0AA55h ; signature byte 267 | code ends 268 | 269 | END 270 | -------------------------------------------------------------------------------- /echo.asm: -------------------------------------------------------------------------------- 1 | .model small 2 | .stack 64 ; 64 byte stack 3 | .386 4 | .code 5 | start: movzx cx,byte ptr ds:[80h] ; size of parameter string 6 | mov ah, 40h ; write 7 | mov bx, 1 ; ... to standard output 8 | mov dx, 81h ; ... the parameter string 9 | int 21h ; ... by calling DOS 10 | mov ah, 4ch 11 | int 21h 12 | end start 13 | -------------------------------------------------------------------------------- /hello.asm: -------------------------------------------------------------------------------- 1 | .386P 2 | .model flat 3 | extern _ExitProcess@4:near 4 | extern _GetStdHandle@4:near 5 | extern _WriteConsoleA@20:near 6 | public _go 7 | 8 | .data 9 | msg byte 'Hello, World', 10 10 | handle dword ? 11 | written dword ? 12 | 13 | .stack 14 | 15 | .code 16 | _go: 17 | ; handle = GetStdHandle(-11) 18 | push -11 19 | call _GetStdHandle@4 20 | mov handle, eax 21 | 22 | ; WriteConsole(handle, &msg[0], 13, &written, 0) 23 | push 0 24 | push offset written 25 | push 13 26 | push offset msg 27 | push handle 28 | call _WriteConsoleA@20 29 | 30 | ; ExitProcess(0) 31 | push 0 32 | call _ExitProcess@4 33 | 34 | end 35 | -------------------------------------------------------------------------------- /hello.s: -------------------------------------------------------------------------------- 1 | .global go 2 | 3 | .data 4 | msg: .ascii "Hello, World\n" 5 | handle: .int 0 6 | written: .int 0 7 | 8 | .text 9 | go: 10 | /* handle = GetStdHandle(-11) */ 11 | pushl $-11 12 | call _GetStdHandle@4 13 | mov %eax, handle 14 | 15 | /* WriteConsole(handle, &msg[0], 13, &written, 0) */ 16 | pushl $0 17 | pushl $written 18 | pushl $13 19 | pushl $msg 20 | pushl handle 21 | call _WriteConsoleA@20 22 | 23 | /* ExitProcess(0) */ 24 | pushl $0 25 | call _ExitProcess@4 26 | -------------------------------------------------------------------------------- /hello1.asm: -------------------------------------------------------------------------------- 1 | .model small 2 | 3 | .stack 128 4 | 5 | .code 6 | start: mov ax, @data 7 | mov ds, ax 8 | mov ah, 9 9 | lea dx, Msg 10 | int 21h 11 | mov ah, 4ch 12 | int 21h 13 | 14 | .data 15 | Msg byte 'Hello, there.', 13, 10, '$' 16 | 17 | end start 18 | -------------------------------------------------------------------------------- /hola.asm: -------------------------------------------------------------------------------- 1 | global _main 2 | extern _puts 3 | 4 | section .text 5 | _main: push rbx ; Call stack must be aligned 6 | lea rdi, [rel message] ; First argument is address of message 7 | call _puts ; puts(message) 8 | pop rbx ; Fix up stack before returning 9 | ret 10 | 11 | section .data 12 | message: db "Hola, mundo", 0 ; C strings need a zero byte at the end 13 | -------------------------------------------------------------------------------- /hola.s: -------------------------------------------------------------------------------- 1 | .global main 2 | 3 | .text 4 | main: # This is called by C library's startup code 5 | mov $message, %rdi # First integer (or pointer) parameter in %rdi 6 | call puts # puts(message) 7 | ret # Return to C library code 8 | message: 9 | .asciz "Hola, mundo" # asciz puts a 0 byte at the end 10 | -------------------------------------------------------------------------------- /p.asm: -------------------------------------------------------------------------------- 1 | A small program that calculates and prints terms of the Fibonacci series 2 | 3 | ; fibo.asm 4 | ; assemble using nasm: 5 | ; nasm -o fibo.com -f bin fibo.asm 6 | ; 7 | ;**************************************************************************** 8 | ; Alterable Constant 9 | ;**************************************************************************** 10 | ; You can adjust this upward but the upper limit is around 150000 terms. 11 | ; the limitation is due to the fact that we can only address 64K of memory 12 | ; in a DOS com file, and the program is about 211 bytes long and the 13 | ; address space starts at 100h. So that leaves roughly 65000 bytes to 14 | ; be shared by the two terms (num1 and num2 at the end of this file). Since 15 | ; they're of equal size, that's about 32500 bytes each, and the 150000th 16 | ; term of the Fibonacci sequence is 31349 digits long. 17 | ; 18 | maxTerms equ 15000 ; number of terms of the series to calculate 19 | 20 | ;**************************************************************************** 21 | ; Number digits to use. This is based on a little bit of tricky math. 22 | ; One way to calculate F(n) (i.e. the nth term of the Fibonacci seeries) 23 | ; is to use the equation int(phi^n/sqrt(5)) where ^ means exponentiation 24 | ; and phi = (1 + sqrt(5))/2, the "golden number" which is a constant about 25 | ; equal to 1.618. To get the number of decimal digits, we just take the 26 | ; base ten log of this number. We can very easily see how to get the 27 | ; base phi log of F(n) -- it's just n*lp(phi)+lp(sqrt(5)), where lp means 28 | ; a base phi log. To get the base ten log of this we just divide by the 29 | ; base ten log of phi. If we work through all that math, we get: 30 | ; 31 | ; digits = terms * log(phi) + log(sqrt(5))/log(phi) 32 | ; 33 | ; the constants below are slightly high to assure that we always have 34 | ; enough room. As mentioned above the 150000th term has 31349 digits, 35 | ; but this formula gives 31351. Not too much waste there, but I'd be 36 | ; a little concerned about the stack! 37 | ; 38 | digits equ (maxTerms*209+1673)/1000 39 | 40 | ; this is just the number of digits for the term counter 41 | cntDigits equ 6 ; number of digits for counter 42 | 43 | org 100h ; this is a DOS com file 44 | ;**************************************************************************** 45 | ;**************************************************************************** 46 | main: 47 | ; initializes the two numbers and the counter. Note that this assumes 48 | ; that the counter and num1 and num2 areas are contiguous! 49 | ; 50 | mov ax,'00' ; initialize to all ASCII zeroes 51 | mov di,counter ; including the counter 52 | mov cx,digits+cntDigits/2 ; two bytes at a time 53 | cld ; initialize from low to high memory 54 | rep stosw ; write the data 55 | inc ax ; make sure ASCII zero is in al 56 | mov [num1 + digits - 1],al ; last digit is one 57 | mov [num2 + digits - 1],al ; 58 | mov [counter + cntDigits - 1],al 59 | 60 | jmp .bottom ; done with initialization, so begin 61 | 62 | .top 63 | ; add num1 to num2 64 | mov di,num1+digits-1 65 | mov si,num2+digits-1 66 | mov cx,digits ; 67 | call AddNumbers ; num2 += num1 68 | mov bp,num2 ; 69 | call PrintLine ; 70 | dec dword [term] ; decrement loop counter 71 | jz .done ; 72 | 73 | ; add num2 to num1 74 | mov di,num2+digits-1 75 | mov si,num1+digits-1 76 | mov cx,digits ; 77 | call AddNumbers ; num1 += num2 78 | .bottom 79 | mov bp,num1 ; 80 | call PrintLine ; 81 | dec dword [term] ; decrement loop counter 82 | jnz .top ; 83 | .done 84 | call CRLF ; finish off with CRLF 85 | mov ax,4c00h ; terminate 86 | int 21h ; 87 | 88 | 89 | ;**************************************************************************** 90 | ; 91 | ; PrintLine 92 | ; prints a single line of output containing one term of the 93 | ; Fibonacci sequence. The first few lines look like this: 94 | ; 95 | ; Fibonacci(1): 1 96 | ; Fibonacci(2): 1 97 | ; Fibonacci(3): 2 98 | ; Fibonacci(4): 3 99 | ; 100 | ; INPUT: ds:bp ==> number string, cx = max string length 101 | ; OUTPUT: CF set on error, AX = error code if carry set 102 | ; DESTROYED: ax, bx, cx, dx, di 103 | ; 104 | ;**************************************************************************** 105 | PrintLine: 106 | mov dx,eol ; print combined CRLF and msg1 107 | mov cx,msg1len+eollen ; 108 | call PrintString ; 109 | 110 | mov di,counter ; print counter 111 | mov cx,cntDigits ; 112 | call PrintNumericString 113 | 114 | call IncrementCount ; also increment the counter 115 | 116 | mov dx,msg2 ; print msg2 117 | mov cx,msg2len ; 118 | call PrintString ; 119 | 120 | mov di,bp ; recall address of number 121 | mov cx,digits ; 122 | ; deliberately fall through to PrintNumericString 123 | 124 | ;**************************************************************************** 125 | ; 126 | ; PrintNumericString 127 | ; prints the numeric string at DS:DI, suppressing leading zeroes 128 | ; max length is CX 129 | ; 130 | ; INPUT: ds:di ==> number string, cx = max string length 131 | ; OUTPUT: CF set on error, AX = error code if carry set 132 | ; DESTROYED: ax, bx, cx, dx, di 133 | ; 134 | ;**************************************************************************** 135 | PrintNumericString: 136 | ; first scan for the first non-zero byte 137 | mov al,'0' ; look for ASCII zero 138 | cld ; scan from MSD to LSD 139 | repe scasb ; 140 | mov dx,di ; points to one byte after 141 | dec dx ; back up one character 142 | inc cx ; 143 | ; deliberately fall through to PrintString 144 | 145 | ;**************************************************************************** 146 | ; 147 | ; PrintString 148 | ; prints the string at DS:DX with length CX to stdout 149 | ; 150 | ; INPUT: ds:dx ==> string, cx = string length 151 | ; OUTPUT: CF set on error, AX = error code if carry set 152 | ; DESTROYED: ax, bx 153 | ; 154 | ;**************************************************************************** 155 | PrintString: 156 | mov bx, 1 ; write to stdout 157 | mov ah, 040h ; write to file handle 158 | int 21h ; ignore return value 159 | ret ; 160 | 161 | ;**************************************************************************** 162 | ; 163 | ; AddNumbers 164 | ; add number 2 at ds:si to number 1 at es:di of width cx 165 | ; 166 | ; 167 | ; INPUT: es:di ==> number1, ds:si ==> number2, cx= max width 168 | ; OUTPUT: CF set on overflow 169 | ; DESTROYED: ax, si, di 170 | ; 171 | ;**************************************************************************** 172 | AddNumbers: 173 | std ; go from LSB to MSB 174 | clc ; 175 | pushf ; save carry flag 176 | .top 177 | mov ax,0f0fh ; convert from ASCII BCD to BCD 178 | and al,[si] ; get next digit of number2 in al 179 | and ah,[di] ; get next digit of number1 in ah 180 | popf ; recall carry flag 181 | adc al,ah ; add these digits 182 | aaa ; convert to BCD 183 | pushf ; 184 | add al,'0' ; convert back to ASCII BCD digit 185 | stosb ; save it and increment both counters 186 | dec si ; 187 | loop .top ; keep going until we've got them all 188 | popf ; recall carry flag 189 | ret ; 190 | 191 | ;**************************************************************************** 192 | ; 193 | ; IncrementCount 194 | ; increments a multidigit term counter by one 195 | ; 196 | ; INPUT: none 197 | ; OUTPUT: CF set on overflow 198 | ; DESTROYED: ax, cx, di 199 | ; 200 | ;**************************************************************************** 201 | IncrementCount: 202 | mov cx,cntDigits ; 203 | mov di,counter+cntDigits-1 204 | std ; go from LSB to MSB 205 | stc ; this is our increment 206 | pushf ; save carry flag 207 | .top 208 | mov ax,000fh ; convert from ASCII BCD to BCD 209 | and al,[di] ; get next digit of counter in al 210 | popf ; recall carry flag 211 | adc al,ah ; add these digits 212 | aaa ; convert to BCD 213 | pushf ; 214 | add al,'0' ; convert back to ASCII BCD digit 215 | stosb ; save and increment counter 216 | loop .top ; 217 | popf ; recall carry flag 218 | ret ; 219 | 220 | ;**************************************************************************** 221 | ; 222 | ; CRLF 223 | ; prints carriage return, line feed pair to stdout 224 | ; 225 | ; INPUT: none 226 | ; OUTPUT: CF set on error, AX = error code if carry set 227 | ; DESTROYED: ax, bx, cx, dx 228 | ; 229 | ;**************************************************************************** 230 | CRLF: mov dx,eol ; 231 | mov cx,eollen ; 232 | jmp PrintString ; 233 | 234 | ;**************************************************************************** 235 | ; static data 236 | ;**************************************************************************** 237 | eol db 13,10 ; DOS-style end of line 238 | eollen equ $ - eol 239 | 240 | msg1 db 'Fibonacci(' ; 241 | msg1len equ $ - msg1 242 | 243 | msg2 db '): ' ; 244 | msg2len equ $ - msg2 245 | ;**************************************************************************** 246 | ; initialized data 247 | ;**************************************************************************** 248 | term dd maxTerms ; 249 | ;**************************************************************************** 250 | ; unallocated data 251 | ; 252 | ; A better way to do this would be to actually ask for a memory 253 | ; allocation and use that memory space, but this is a DOS COM file 254 | ; and so we are given the entire 64K of space. Technically, this 255 | ; could fail since we *might* be running on a machine which doesn't 256 | ; have 64K free. If you're running on such a memory poor machine, 257 | ; my advice would be to not run this program. 258 | ; 259 | ;**************************************************************************** 260 | ; static data 261 | counter: ; 262 | num1 equ counter+cntDigits ; 263 | num2 equ num1+digits ; 264 | -------------------------------------------------------------------------------- /powers.asm: -------------------------------------------------------------------------------- 1 | .386P 2 | .model flat 3 | extern _printf:near 4 | public _main 5 | 6 | .code 7 | _main: 8 | push esi ; callee-save registers 9 | push edi 10 | 11 | mov esi, 1 ; current value 12 | mov edi, 31 ; counter 13 | L1: 14 | push esi ; push value to print 15 | push offset format ; push address of format string 16 | call _printf 17 | add esp, 8 ; pop off parameters passed to printf 18 | add esi, esi ; double value 19 | dec edi ; keep counting 20 | jnz L1 21 | 22 | pop edi 23 | pop esi 24 | ret 25 | 26 | format: byte '%d', 10, 0 27 | 28 | end 29 | -------------------------------------------------------------------------------- /powers.s: -------------------------------------------------------------------------------- 1 | .global _main 2 | 3 | .text 4 | format: .asciz "%d\n" 5 | 6 | _main: 7 | pushl %esi /* callee save registers */ 8 | pushl %edi 9 | 10 | movl $1, %esi /* current value */ 11 | movl $31, %edi /* counter */ 12 | L1: 13 | pushl %esi /* push value of number to print */ 14 | pushl $format /* push address of format */ 15 | call _printf 16 | addl $8, %esp 17 | 18 | addl %esi, %esi /* double value */ 19 | decl %edi /* keep counting */ 20 | jnz L1 21 | 22 | popl %edi 23 | popl %esi 24 | ret 25 | -------------------------------------------------------------------------------- /triangle.asm: -------------------------------------------------------------------------------- 1 | global _main 2 | extern _glClear@4 3 | extern _glBegin@4 4 | extern _glEnd@0 5 | extern _glColor3f@12 6 | extern _glVertex3f@12 7 | extern _glFlush@0 8 | extern _glutInit@8 9 | extern _glutInitDisplayMode@4 10 | extern _glutInitWindowPosition@8 11 | extern _glutInitWindowSize@8 12 | extern _glutCreateWindow@4 13 | extern _glutDisplayFunc@4 14 | extern _glutMainLoop@0 15 | 16 | section .text 17 | title: db 'A Simple Triangle', 0 18 | zero: dd 0.0 19 | one: dd 1.0 20 | half: dd 0.5 21 | neghalf:dd -0.5 22 | 23 | display: 24 | push dword 16384 25 | call _glClear@4 ; glClear(GL_COLOR_BUFFER_BIT) 26 | push dword 9 27 | call _glBegin@4 ; glBegin(GL_POLYGON) 28 | push dword 0 29 | push dword 0 30 | push dword [one] 31 | call _glColor3f@12 ; glColor3f(1, 0, 0) 32 | push dword 0 33 | push dword [neghalf] 34 | push dword [neghalf] 35 | call _glVertex3f@12 ; glVertex(-.5, -.5, 0) 36 | push dword 0 37 | push dword [one] 38 | push dword 0 39 | call _glColor3f@12 ; glColor3f(0, 1, 0) 40 | push dword 0 41 | push dword [neghalf] 42 | push dword [half] 43 | call _glVertex3f@12 ; glVertex(.5, -.5, 0) 44 | push dword [one] 45 | push dword 0 46 | push dword 0 47 | call _glColor3f@12 ; glColor3f(0, 0, 1) 48 | push dword 0 49 | push dword [half] 50 | push dword 0 51 | call _glVertex3f@12 ; glVertex(0, .5, 0) 52 | call _glEnd@0 ; glEnd() 53 | call _glFlush@0 ; glFlush() 54 | ret 55 | 56 | _main: 57 | push dword [esp+8] ; push argv 58 | lea eax, [esp+8] ; get addr of argc (offset changed :-) 59 | push eax 60 | call _glutInit@8 ; glutInit(&argc, argv) 61 | push dword 0 62 | call _glutInitDisplayMode@4 63 | push dword 80 64 | push dword 80 65 | call _glutInitWindowPosition@8 66 | push dword 300 67 | push dword 400 68 | call _glutInitWindowSize@8 69 | push title 70 | call _glutCreateWindow@4 71 | push display 72 | call _glutDisplayFunc@4 73 | call _glutMainLoop@0 74 | ret 75 | --------------------------------------------------------------------------------