testNASM – interrupt mechanism

; boot.asm
; bin version

; song “I hope when you decide, kindness will be your guide …”:
; “I did not program you, you program you: cha me. sinh con tro+`i sinh ti’nh/compute/program:
; I program/vote/ba^`u{di` ba co^ Die^~m’s pregnancy}/wish/love/aim ‘muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well’, and
; Everything/All/Allah/Nature/God programs/votes/ba^`u{di` ba co^ die^~m’s pregnancy}/wishes/loves/aims/’muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well’, and
; the rest, what’s in between God and I is you is up to you … hopefully you too will program along with God and I for
; ‘muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well’ ….

;from NASM manual:
; The BITS directive specifies whether NASM should generate code designed to run on a processor operating in 16-bit mode, 32-bit mode or 64-bit mode.
; You do not need to specify BITS 32 merely in order to use 32-bit instructions in a 16-bit DOS program; if you do, the assembler will generate incorrect code because it will be writing code targeted at a 32-bit platform, to be run on a 16-bit one:
; When NASM is in BITS 16 mode, instructions which use 32-bit data are prefixed with an 0x66 byte, and those referring to 32-bit addresses have an 0x67 prefix. In BITS 32 mode, the reverse is true: 32-bit instructions require no prefixes, whereas instructions using 16-bit data need an 0x66 and those working on 16-bit addresses need an 0x67.
; When NASM is in BITS 64 mode, most instructions operate the same as they do for BITS 32 mode.
[BITS 16]

; from the Programmer’s Reference Manual
;The segment containing the currently executing sequence of instructions is known as the current code segment;
;it is specified by means of the CS register. The 80386 fetches all instructions from this code segment, using
;as an offset the contents of the instruction pointer. CS is changed implicitly as the result of intersegment
;control-transfer instructions (for example, CALL and JMP), interrupts, and exceptions.
;The instruction pointer register (EIP) contains the offset address, relative to the start of the current code
;segment, of the next sequential instruction to be executed. The instruction pointer is not directly visible
;to the programmer; it is controlled implicitly by control-transfer instructions, interrupts, and exceptions.
;As Figure 2-9 shows, the low-order 16 bits of EIP is named IP and can be used by the processor as a unit.
;This feature is useful when executing instructions designed for the 8086 and 80286 processors.

; from http://wiki.osdev.org/Babystep2:
; In real mode, addresses are calculated as segment * 16 + offset. Since offset can be much larger than 16, there are many pairs
; of segment and offset that point to the same address.
; In real mode, addresses are calculated as segment * 16 + offset. Since offset can be much larger than 16, there are many pairs
; of segment and offset that point to the same address. For instance, some say that the bootloader is is loaded at 0000:7C00,
; while others say 07C0:0000. This is in fact the same address: 16 * 0x0000 + 0x7C00 = 16 * 0x07C0 + 0x0000 = 0x7C00.
; It doesn’t matter if you use 0000:7c00 or 07c0:0000, but if you use ORG you need to be aware of what’s happening

; from http://www.supernovah.com/Tutorials/BootSector2.php:
;The BIOS does not load the boot sector to a random spot in memory. The BIOS will always load the boot sector starting at the memory location 0x7C00.
; from http://www.supernovah.com/Tutorials/BootSector2.php
;As stated earlier, we cannot be sure if the BIOS set us up with the starting address of 0x7C0:0x0 or 0x0:0x7C00.
;We will use the second segment offset pair to execute our boot sector so we know for sure how the CPU will access
;our code. To do this, our very first instruction will be a far jump that simply jumps to the next instruction.
;The trick is, if we specify a segment, even if it is 0x0, the jmp will be a far jump and the CS register will be
;loaded with the value 0x0 and the IP register will be loaded with the address of the next instruction to be
;executed.
;[BITS 16]
;[ORG 0x7C00]
;jmp 0x0:Start
;Start:
; This code will set the CS segment to 0x0, set the IP register to the the very next instruction which will be slightly past 0x7C00, ….

; universal-loop
; {
; start-ORG-nguye^n-thu?y: maintain-gi`n-giu+~ba?o-to^`n (“muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”); // in “gia ba?o”, “ba?o” ~ maintain as in “ba?o thu?/to^`n” …
; try/if ;// tin messages …. the try/if is the “gia” of “gia ba?o” …
; maintain-gi`n-giu+~-ba?o-to^`n (“muo^n loa`i va` messageA va` messageB va` messageNEW va` tinLA`NH va`… ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”); // the message “stack” is loaded or push-pop with messages …; // push-and-pop-or-sent-and-receive (&messageNEW-hay-tinLA`NH); // tin and shakespeare’s version of “all roads lead to rome”: “doubt thou the stars are fire doubt truth to be a liar but never doubt I loved ‘muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well'”: 1/19/2014 Sunday Service … Gospel ~ Good News Tin La\nh …”Gia Ba?o”: the “gia” attempts to reach an agreement with the “ba?o” …// salinger on internet news: push/pop/create stack/heap by an expansion assignment (“muo^n loa`i” ; ;catch/else ;// unmaintainable tin/messages or kho’ tin hay kho^ng tin no messages … SBTN Uye^n Thi. commercial for MBR [master boot record] “kho’ tin nhu+ng co’ tha^.t …”
; ; go-to-jump-tro+?-ve^` start-ORG-nguye^n-thu?y: maintain-gi`n-giu+~-ba?o-to^`n (“muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”);
; go-to-jump-tro+?-ve^` start-ORG-nguye^n-thu?y: maintain-gi`n-giu+~-ba?o-to^`n (“muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”);
; }

; irish-catholic Pat Benatar song “heartbreaker, dreammaker, don’t you mess around with me …” ….
; perhaps “there’s beggary in a love that can be reckoned” when love is unconditional–gia ba?o chu’ hoa`ng to^n an hoa`ng phi hu`ng and 10 commandments–but
; the ten commandments say there’s a love that’s conditional … and the 10 commandments describe the limits or conditions of that love …
; from http://wiki.osdev.org/Babystep2:
;some say that the bootloader is loaded at [metaphorical address] 0000:7C00, while others say 07C0:0000.
;This is in fact the same [real] address: 16 * 0x0000 + 0x7C00 = 16 * 0x07C0 + 0x0000 = 0x7C00.

;%define ORIGIN ; ….. comment this out to use “org 0” instead of “org 0x07C0” …

; test segment:offset scheme
;%assign ORIGIN 0x0
;%assign ORIGIN 0x7c00
%assign ORIGIN 0x7990 ; 3/6/2014 home alone with mother …

;%ifdef ORIGIN
%if ORIGIN = 0x7c00
[ORG 0x7c00]
%define PROGRAMSEGMENT 0x0
;segment .text align=16
; segment:offset … ds:offset or cs:offset … 0:offset-from-0x7COO … that is, labels in code following is addressed as 0:0x7C00+offset-from-start-of-file
;Following code will set the CS segment to 0x0, set the IP register to the the very next instruction which will be slightly past 0x7C00, ….
jmp 0x0:start ; set up the ip stack pointer and cs segment register implicitly via jmp instruction
; jmp start ; set up the ip stack pointer and cs segment register implicitly via jmp instruction

;%else ;
%elif ORIGIN = 0x0
[ORG 0]
%define PROGRAMSEGMENT 0x07C0
;segment .text align=16
; segment:offset … ds:offset or cs:offset … 0x07C0:offset-from-0 … that is, labels in the code following is addressed as 0x07C0:0+offset-from-start-of-file
;Following code will set the CS segment to 0x07C0, set the IP register to the the very next instruction which will be slightly past 0x0, ….
jmp 0x07C0:start ; set up the ip stack pointer and cs segment register implicitly via jmp instruction
; jmp start ; set up the ip stack pointer and cs segment register implicitly via jmp instruction

; from http://wiki.osdev.org/Babystep2:
; In real mode, addresses are calculated as segment * 16 + offset. Since offset can be much larger than 16, there are many pairs
; of segment and offset that point to the same address.
; (0x07c00 – 0x07cf) / 0x10 = (7431) / 0x10 = 743.1
; (0x07c00 – 0x03e7) / 0x10 =
; (0x07c00 – 0x7990) / 0x10 = 0x0270 / 0x10 = 0x0027
; 31744 – 31120

%elif ORIGIN = 0x7990 ; grateful for hexadecimal conversion from http://www.mathsisfun.com/binary-decimal-hexadecimal-converter.html and hex calculator from http://www.miniwebtool.com/hex-calculator/?number1=270&operate=1&number2=7990
[ORG 0x7990]
%define PROGRAMSEGMENT 0x0027
;segment .text align=16
; segment:offset … ds:offset or cs:offset … 0x0027:offset-from-0x7990 … that is, labels in code following is addressed as 0:0x7990+offset-from-start-of-file
;Following code will set the CS segment to 0x0027, set the IP register to the the very next instruction which will be slightly past 0x7990, ….
jmp 0x0027:start ; set up the ip stack pointer and cs segment register implicitly via jmp instruction
; jmp start ; set up the ip stack pointer and cs segment register implicitly via jmp instruction

;%endif ; ORIGIN
%endif; ORIGIN

;%ifdef ORIGIN
%if ORIGIN = 0x7c00
%define MEMORYSEGMENTREALLOWBOUND 0x7C00 ; 31744
;%else
%elif ORIGIN = 0x0
%define MEMORYSEGMENTREALLOWBOUND 0x0000 ; 0
%elif ORIGIN = 0x7990 ; grateful for hexadecimal conversion from http://www.mathsisfun.com/binary-decimal-hexadecimal-converter.html and hex calculator from http://www.miniwebtool.com/hex-calculator/?number1=270&operate=1&number2=7990
%define MEMORYSEGMENTREALLOWBOUND 0x0027 ; 39
%endif ; ORIGIN
%define SEGMENTSIZE 512
%define MEMORYSEGMENTREALUPPERBOUND MEMORYSEGMENTREALLOWBOUND + SEGMENTSIZE

; there was a program on the internet [e.g. http://frz.ir/dl/tuts/8086_Assembly.pdf%5D written entirely
; using NASM pseudo-op “db”. For example,
; dw 0xfeeb will generate the same bit patterns as jmp $ in the binary file. The interrupt table and stacksegment and datasegment with pseudo-opcodes db, dw etc. here
; was “jmp-ed” over …

%define TRYIVT ; try out ivt codes … comment this out to exclude ivt codes
%ifdef TRYIVT
; interrupts are a type of messages “muo^n loa`i va` messageA va` messageB va` messageNEW va` tinLA`NH va`… ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”
; and the interrupt table [of outgoing becauses/answers/responses {do tha’i … } to incoming messages] is placed as close to the origin nguye^n thu?y 0x0:0x0 as possible with/by the BIOS
; girl immitating the suprememes pointing fingers on our trip to san francisco:
; I know, I know you must follow the sun
;Wherever it leads
;But remember
;If you should fall short of your desires
;Remember life holds for you one guarantee
;You’ll always have me
; And if you should miss my lovin
;One of these old days
;If you should ever miss the arms
;That used to hold you so close, or the lips
;That used to touch you so tenderly
;Just remember what I told you
;The day I set you free
;
;Ain’t no mountain high enough
;Ain’t no valley low enough
;Ain’t no river wild enough
;To keep me from you
; http://youtu.be/VqW2XigtDEU

; universal-loop
; {
; start-ORG-nguye^n-thu?y: maintain-gi`n-giu+~ba?o-to^`n (“muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”); // in “gia ba?o”, “ba?o” ~ maintain as in “ba?o thu?/to^`n” …
; try/if ;// tin messages …. the try/if is the “gia” of “gia ba?o” …
; maintain-gi`n-giu+~-ba?o-to^`n (“muo^n loa`i va` messageA va` messageB va` messageNEW va` tinLA`NH va`… ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”); // the message “stack” is loaded or push-pop with messages …; // push-and-pop-or-sent-and-receive (&messageNEW-hay-tinLA`NH); // tin and shakespeare’s version of “all roads lead to rome”: “doubt thou the stars are fire doubt truth to be a liar but never doubt I loved ‘muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well'”: 1/19/2014 Sunday Service … Gospel ~ Good News Tin La\nh …”Gia Ba?o”: the “gia” attempts to reach an agreement with the “ba?o” …// salinger on internet news: push/pop/create stack/heap by an expansion assignment (“muo^n loa`i” ; ;catch/else ;// unmaintainable tin/messages or kho’ tin hay kho^ng tin no messages … SBTN Uye^n Thi. commercial for MBR [master boot record] “kho’ tin nhu+ng co’ tha^.t …”
; ; go-to-jump-tro+?-ve^` start-ORG-nguye^n-thu?y: maintain-gi`n-giu+~-ba?o-to^`n (“muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”);
; go-to-jump-tro+?-ve^` start-ORG-nguye^n-thu?y: maintain-gi`n-giu+~-ba?o-to^`n (“muo^n loa`i ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”);
; }

; original interrupt vector table in the BIOS
orgbiosivt: times 1024 db 0 ; interrupt vector table: reserve space to push-pop BIOS’ ivt table
orgbiosivtend:
;the original ivtr structure:
orgbiosivtr DW 0 ; For limit storage ; size of ivt structure
DD 0 ; For base storage ; segment:offset address of ivt structure

; interrupt vector table
ivt: times 1024 db 0 ; interrupt vector table: reserve space to push-pop BIOS’ ivt table …
ivtend:
;the ivtr structure:
ivtr DW 0 ; For limit storage ; size of ivt structure
DD 0 ; For base storage ; segment:offset address of ivt structure

; interrup descriptor table
;idt:
;idt_end:
;the idtr structure:
;idtr DW 0 ; For limit storage
; DD 0 ; For base storage
%endif ; TRYIVT

interrupt5count dw 0
interrupt9count dw 0

; data segment
; section .data
;section datasegment align=16 ; start= follows=
;segment datasegment align=16 ; start= follows=
; align 16
;segment .data align=16
;datasegment dw 123
datasegment db ‘Hope Well’

; stack segment
; section .bss
; section stacksegment align=16 ; start= follows=
; segment stacksegment align=16 ; start= follows=
; align 16
;segment .stack align=16
stacksegment resb 64
stacktop:
stacklowerbound equ stacksegment
stackupperbound equ stacktop
stacksize equ $-stacksegment
; from NASM manual
;3.2.4 EQU: Defining Constants
;EQU defines a symbol to a given constant value: when EQU is used, the source line must contain a label. The action of EQU is to define the given label name to the value of its (only) operand. This definition is absolute, and cannot change later. So, for example,
;message db ‘hello, world’
;msglen equ $-message
;defines msglen to be the constant 12. msglen may not then be redefined later. This is not a preprocessor definition either: the value of msglen is evaluated once, using the value of $ (see section 3.5 for an explanation of $) at the point of definition, rather than being evaluated wherever it is referenced and using the value of $ at the point of reference.

;spprevious dw 0
;spnew dw 0
;spcounter times 10 dw 0

; from NASM manual
;message db ‘hello, world’
;msglen equ $-message

;stacktop = stacksegment – datasegment + 64

;segment .text align=16
; set up the data, stack, etc. segment registers
;segment .text align=16
start:
;mov AX, 0x0
;mov AX,seg DATASEGMENT1
;; mov AX, datasegment
;; mov AX, seg datasegment ; error: binary output format does not support segment base references
;; mov AX, [datasegment]
;%ifdef ORIGIN
%if ORIGIN = 0x7c00
mov AX, 0x0
; mov AX, 0x0 + datasegment
; mov AX, PROGRAMSEGMENT
mov DS,AX
; from http://www.supernovah.com/Tutorials/BootSector2.php
; The processor uses the SS:SP segment offset address to determine the location of the stack.
; from http://wiki.osdev.org/Stack:
;Take care when implementing your kernel. If you use segmentation, the DS segment should be configured to have it’s base at the same address as SS does. Otherwise you may run into problems when passing pointers to local variables into functions, because normal GPRs can’t access the stack the way you might think.
;mov AX,seg STACKSEGMENT
;mov AX, 0x0 + stacksegment
mov AX, 0x0
mov SS,AX
;mov SP, 0x0 + stacktop
mov SP, stacktop
;%else
%elif ORIGIN = 0x0

; mov AX, 0x07C0 + datasegment
; mov AX, datasegment
mov AX, 0x07C0
; mov AX, PROGRAMSEGMENT
mov DS,AX
; from http://www.supernovah.com/Tutorials/BootSector2.php
; The processor uses the SS:SP segment offset address to determine the location of the stack.
; from http://wiki.osdev.org/Stack:
;Take care when implementing your kernel. If you use segmentation, the DS segment should be configured to have it’s base at the same address as SS does. Otherwise you may run into problems when passing pointers to local variables into functions, because normal GPRs can’t access the stack the way you might think.
;mov AX,seg STACKSEGMENT
; mov AX, 0x07C0 + stacksegment
;mov AX, stacksegment
mov AX, 0x07C0
mov SS,AX
; mov SP, 0x07C0 + stacktop
mov SP, stacktop
; from http://frz.ir/dl/tuts/8086_Assembly.pdf
;MOV REG, memory
;MOV memory, REG
;MOV REG, REG
;MOV memory, immediate
;MOV REG, immediate
;REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.
;memory: [BX], [BX+SI+7], variable, etc…
;immediate: 5, -24, 3Fh, 10001101b, etc…
; mov CX, SP
; mov [spprevious], SP
; … some operation …
; mov [spnew], SP
; mov word [spcounter], spprevious – spnew
%elif ORIGIN = 0x7990 ; grateful for hexadecimal conversion from http://www.mathsisfun.com/binary-decimal-hexadecimal-converter.html and hex calculator from http://www.miniwebtool.com/hex-calculator/?number1=270&operate=1&number2=7990
mov AX, 0x0027
; mov AX, 0x0027 + datasegment
; mov AX, PROGRAMSEGMENT
mov DS,AX
; from http://www.supernovah.com/Tutorials/BootSector2.php
; The processor uses the SS:SP segment offset address to determine the location of the stack.
; from http://wiki.osdev.org/Stack:
;Take care when implementing your kernel. If you use segmentation, the DS segment should be configured to have it’s base at the same address as SS does. Otherwise you may run into problems when passing pointers to local variables into functions, because normal GPRs can’t access the stack the way you might think.
;mov AX,seg STACKSEGMENT
;mov AX, 0x0 + stacksegment
mov AX, 0x0027
mov SS,AX
;mov SP, 0x0027 + stacktop
mov SP, stacktop

%endif ; ORIGIN
;mov AX,seg STACKSEGMENT
; mov AX, stacksegment
; mov SS,AX
; mov SP,stacktop

; to use the stack, use “call” and “ret” instead of “jmp”
; effectively, the illegal “mov eip, label” ~ legal “jmp label”
; or just let the program flows, without the jmp, to instructions that follow
; jmp main ; jmp Loads EIP with the specified address

; PUSH instruction from programmer’s reference manual
;IF StackAddrSize = 16
;THEN
; IF OperandSize = 16 THEN
; SP := SP – 2;
; (SS:SP) := (SOURCE); (* word assignment *)
; ELSE
; SP := SP – 4;
; (SS:SP) := (SOURCE); (* dword assignment *)
; FI;
;ELSE (* StackAddrSize = 32 *)
; IF OperandSize = 16
; THEN
; ESP := ESP – 2;
; (SS:ESP) := (SOURCE); (* word assignment *)
; ELSE
; ESP := ESP – 4;
; (SS:ESP) := (SOURCE); (* dword assignment *)
; FI;
;FI;
; RET instruction
;IF instruction = near RET
;THEN;
; IF OperandSize = 16
; THEN
; IP := Pop();
; EIP := EIP AND 0000FFFFH;
; ELSE (* OperandSize = 32 *)
; EIP := Pop();
; FI;
; IF instruction has immediate operand THEN eSP := eSP + imm16; FI;
;FI
; CALL instruction
;IF rel16 or rel32 type of call
;THEN (* near relative call *)
; IF OperandSize = 16
; THEN
; Push(IP);
; EIP := (EIP + rel16) AND 0000FFFFH;
; ELSE (* OperandSize = 32 *)
; Push(EIP);
; EIP := EIP + rel32;
; FI;
;FI;

; mov [spprevious], SP
; … some operation …
; mov [spnew], SP
; mov word [spcounter + 2 * 0], spprevious – spnew

; mov [spprevious], SP
; call word main ; call = push + jmp; ret = pop + jmp
call word main ; call = push + jmp; ret = pop + jmp
; mov [spnew], SP
; mov word [spcounter + 2 * 0], spprevious – spnew

; from http://wiki.osdev.org/Babystep2:
; In real mode, addresses are calculated as segment * 16 + offset. Since offset can be much larger than 16, there are many pairs
; of segment and offset that point to the same address.
%define REALADDRESS(SEGMENTNO,OFFSETNO) SEGMENTNO*16+OFFSETNO

%define VERIFYSEGMENTADDRESSBOUND(SEGMENTADDRESSTOVERIFY, OFFSETADDRESSTOVERIFY) \
(REALADDRESS(SEGMENTADDRESSTOVERIFY,OFFSETADDRESSTOVERIFY) > MEMORYSEGMENTREALLOWBOUND) \
& (REALADDRESS(SEGMENTADDRESSTOVERIFY,OFFSETADDRESSTOVERIFY) < MEMORYSEGMENTREALUPPERBOUND)
; generate some virtual segment:offset address for use with a real address …
; TO DO: align the generated addresses to “natural” byte boundaries …
; %define GENERATESEGMENTADDRESS(REALADDRESSNO, &GENSEGMENTNO, &GENOFFSETNO) …………….
; %define GENERATEVIRTUALSEGMENTADDRESS(REALADDRESSNO, VIRTUALOFFSETADDRESSINPUT) (REALADDRESSNO – VIRTUALOFFSETADDRESSINPUT)/16
; %define GENERATEOFFSETNO(REALADDRESSNO, VIRTUALSEGMENTADDRESSINPUT) (REALADDRESSNO – VIRTUALSEGMENTADDRESSINPUT * 16)

; from http://geezer.osdevbrasil.net/johnfine/segments.htm:
;The way it really works
; Each segment register is really four registers: •A selector register
;•A base register
;•A limit register
;•An attribute register
;
;In all modes, every access to memory that uses a segment register uses the base, limit, and attribute portions of the segment register and does not use the selector portion.
;Every direct access to a segment register (PUSHing it on the stack, MOVing it to a general register etc.) uses only the selector portion. The base, limit, and attribute portions are either very hard or impossible to read (depending on CPU type). They are often called the “hidden” part of the segment register because they are so hard to read.
;Intel documentation refers to the hidden part of the segment register as a “descriptor cache”. This name obscures the actual behavior of the “hidden” part.
; In real mode (or V86 mode), when you write any 16-bit value to a segment register, the value you write goes into the selector and 16 times that value goes into the base. The limit and attribute are not changed.
;In pmode, any write to a segment register causes a descriptor to be fetched from the GDT or LDT and unpacked into the base, limit and attribute portion of the segment register. (Special exception for the NULL Selector).
;When the CPU switchs between real mode and pmode, the segment registers do not automatically change. The selectors still contain the exact bit pattern that was loaded into them in the previous mode. The hidden parts still contain the values they contained before, so the segment registers can still be used to access whatever segments they refered to before the switch.

;Writes to a segment register
;When I refer to “writing to a segment register”, I mean any action that puts a 16-bit value into a segment register.
;The obvious example is something like:
; MOV DS,AX
;However the same rules apply to many other situations, including: •POP to a segment register.
;•FAR JMP or CALL puts a value in CS.
;•IRET or FAR RET puts a value in CS.
;•Both hardware and software interrupts put a value in CS.
;•A ring transition puts a value in both SS and CS.
;•A task switch loads all the segment registers from a TSS.

; from the Programmer’s Reference Manual
;The segment containing the currently executing sequence of instructions is known as the current code segment;
;it is specified by means of the CS register. The 80386 fetches all instructions from this code segment, using
;as an offset the contents of the instruction pointer. CS is changed implicitly as the result of intersegment
;control-transfer instructions (for example, CALL and JMP), interrupts, and exceptions.

main:
; to use the stack, use “call” and “ret” instead of “jmp”
;jmp screensetup ; or just let the program flows, without the jmp, to instructions that follow

; call screensetup
call word screensetup
; call clearscreenpixels
call word clearscreenpixels

; mov [spprevious], SP
; … some operation …
; mov [spnew], SP
; mov word [spcounter + 2 * 0], spprevious – spnew
; mov [spprevious], SP
%define SAYHELLO 1
%ifdef SAYHELLO
; call sayhello
call word sayhello
%endif ; SAYHELLO
; mov [spnew], SP
; mov word [spcounter + 2 * 1], spprevious – spnew

%ifdef TRYIVT
call changeivt.loadorgbiosivtwithbiosivt
call changeivt.loadivtwithbiosivt
;call changeivt.loadivtwithorgbiosivt
;Setting:
;mov ax, [intnum*4] ; offset
;mov ax [intnum*4+2] ; segment
; pbs “peg and cat” …. suggestively similar “peg and cat” bedding at baby r us 3/13/2014
; substitute custom interrupt numbers 5 and 9 into new ivt table created by cloning from original BIOS ivt table: neighbor dude with custom truck 3/13/2014
call changeivt.insertcustominterruptsintoivt
call changeivt.setivt

; test custom interrupt numbers 5 and 9 … here …
int 5 ; manual interrupt
int 9 ; manual interrupt

; test safe stack calls SAFECALLS’s with int 5 here ………

; ………

; test int 9 here ………………..

; automatic interrupt: keyboard presses will create int 9 …
; test: however ctr-alt-del will have no effect on custom interrupt int 9 …
;xor cx, cx
waitforinterrupts: cmp word [interrupt9count], 10 ; key press wait loop …
jne waitforinterrupts
;loopne waitforinterrupts
; xor cx, cx
;WaitforData: in al, 64h ;Read kbd status port.
; test al, 10b ;Data in buffer?
; loopz WaitforData ;Wait until data available.

; restore original interrupt vector table from BIOS with original interrupt numbers 5 and 9
; call changeivt.loadivtwithorgbiosivt
call changeivt.loadbiosivtwithorgbiosivt
call changeivt.setorgivt

; ctr-alt-del should have an effect again here ………….
jmp seeyoulater

%endif ; TRYIVT
%ifdef TRYIVTORIG
; cli ; disable interrupts during change of interrupt vector table
;; call changeivt.loadorgbiosivtwithbiosivt
;call changeivt.loadivtwithbiosivt
;; call changeivt.loadivtwithorgbiosivt
;Setting:
;mov ax, [intnum*4] ; offset
;mov ax [intnum*4+2] ; segment
; pbs “peg and cat” …. suggestively similar “peg and cat” bedding at baby r us 3/13/2014
; substitute custom interrupt numbers 5 and 9 into new ivt table created by cloning from original BIOS ivt table: neighbor dude with custom truck 3/13/2014

; address of BIOS interrupts routines
interrupt5segment dw 0
interrupt5offset dw 0
interrupt9segment dw 0
interrupt9offset dw 0
mov ax, 0x0
mov gs, ax
mov ax, [gs:5*4] ; [0x0:5*4]
mov [interrupt5offset], ax
mov ax, [gs:5*4+2] ; [0x0:5*4+2]
mov [interrupt5segment], ax

mov ax, [gs:9*4] ; [0x0:9*4]
mov [interrupt5offset], ax
mov ax, [gs:9*4+2] ; [0x0:9*4+2]
mov [interrupt5segment], ax

; mov dword [interrupt5serviceroutine], [0x0:5*4]
; mov dword [interrupt9serviceroutine], [0x0:9*4]

cli ; pause interrupts

mov AX, safeinterrupt5sr ; offset
; mov dword [ivt + 5*4], AX
mov [gs:5*4], AX
mov AX, PROGRAMSEGMENT ; segment
; mov dword [ivt + 5*4+2], AX
mov [gs:5*4+2], AX

mov AX, safeinterrupt9sr ; offset
; mov dword [ivt + 9*4], AX
mov [gs:9*4], AX
mov AX, PROGRAMSEGMENT ; segment
; mov dword [ivt + 9*4+2], AX
mov [gs:9*4+2], AX
sti ; re-enable interrupts

;call changeivt.loadbiosivtwithivt
;; call changeivt.setivt ; inform processor where new ivt table is …
;sti ; re-enable interrupts

; test custom interrupt numbers 5 and 9 … here …
int 5
int 9

; restore original interrupt vector table from BIOS with original interrupt numbers 5 and 9
; call changeivt.loadivtwithorgbiosivt
;; call changeivt.loadbiosivtwithorgbiosivt
;; call changeivt.setorgivt
cli ; pause interrupts
mov ax, [interrupt5offset]
mov [gs:5*4], ax
mov ax, [interrupt5segment]
mov [gs:5*4+2], ax

mov ax, [interrupt9offset]
mov [gs:9*4], ax
mov ax, [interrupt9segment]
mov [gs:9*4+2], ax
sti ; re-enable interrupts

;sti ; re-enable interrupts
%endif ; TRYIVTORIG
;%define TRYIVT 1 ; non-zero
%ifdef TRYIVT

; from Programmer’s Reference Manual
;IF PE = 0
;THEN GOTO REAL-ADDRESS-MODE;
;ELSE GOTO PROTECTED-MODE;
;FI;
;REAL-ADDRESS-MODE:
; Push (FLAGS);
; IF = 0; (* Clear interrupt flag *)
; TF = 0; (* Clear trap flag *)
; Push(CS);
; Push(IP);
; (* No error codes are pushed *)
; CS := IDT[Interrupt number * 4].selector;
; IP := IDT[Interrupt number * 4].offset;

;interrrupts are a type of (messageA + messageB + messageC + messageD + tinLa`nh + …):
; from http://wiki.osdev.org/Interrupt_Vector_Table
; The IVT is typically located at 0000:0000H, and is 400H bytes in size (4 bytes for each interrupt). Although the default address can be changed using the LIDT instruction on newer CPUs, this is usually not done because it is both inconvenient and incompatible with other implementations and/or older software (e.g. MS-DOS programs). However, note that the code must remain in the first MiB of RAM.
; format of the ivt table entries [1024/4=256 entries] is
; +———–+———–+
; | Segment | Offset |
; +———–+———–+
; 4 2 0
; from https://www.uop.edu.jo/issa/Assembly/programming.pdf
;ivt table is 1k in real mode, 2k in protected mode
;ivt entry is 4 bytes in real mode, 8 bytes in protected mode
;size of the pointer to ivt table is 4 bytes for addresses from 00000000 to 000003FF, is 8 bytes in protected mode

;%define BIVTSTART 0x0; Start of BIOS ivt data area
;struc tBIOSIVT ; its structure
; .SEGMENT RESW 1
; .OFFSET RESW 1
;endstruc

; the ivt table defined in the data segment “datasegment” above
;;.ivt: times 1024 db 0 ; interrupt vector table: reserve space to push-pop BIOS’ ivt table
;;.ivtend:
;;fourcxvar dw 0
;; mov ax, 0x0
;; mov gs, ax
;; mov ax, [gs:5*4] ; [0x0:5*4]
;; mov [interrupt5offset], ax
;; mov ax, [gs:5*4+2] ; [0x0:5*4+2]
;; mov [interrupt5segment], ax

; from NASM manual:
;3.3 Effective Addresses
;An effective address is any operand to an instruction which references memory. Effective addresses, in NASM, have a very simple syntax: they consist of an expression evaluating to the desired address, enclosed in square brackets. For example:
;wordvar dw 123
; mov ax,[wordvar]
; mov ax,[wordvar+1]
; mov ax,[es:wordvar+bx] ; this is gives no error
; however:
; mov ax,[es:wordvar+cx] ; this gives “invalid effective address” error
; mov ax,[es:wordvar+2*bx] ; this gives “invalid effective address” error
; mov ax,[es:wordvar+10*bx] ; this gives “invalid effective address” error
; mov ax,[es:wordvar+4*bx] ; this gives “invalid effective address” error
; mov eax,[es:wordvar+4*ebx] ; this gives no error
; mov eax,[es:wordvar+10*ebx] ; this gives “invalid effective address” error
; also segment registers:
; mov gs, 0x0 ; “immediate” gives error
; mov gs, [worvar] ; “memory” is all right
; mov gs, ax ; “register” is all right

; from http://www.supernovah.com/Tutorials/Assembly3.php:
;16-bit Real Mode Addressing
;Non Memory Addressing Modes
;The non memory addressing modes in 16 bits are the same as 32-bit non memory addressing modes except that you can only use 16-bit registers or smaller. Also the largest displacement in 16-bit addresses can be at most 16 bits.
;Memory Addressing Modes
;In 16-bit real mode we can address memory using 16-bit or 8-bit registers. The addressing modes in 16 bits are much more restrictive than in 32 bits. The table below lists the components that can make up a 16-bit address.

;Displacement Base Index Scale
;no disp BX SI None
;8-bit disp BP DI
;16-bit disp

;32-bit Protected Mode Addressing
;Non Memory Addressing Modes
;These addressing modes do not access memory. These modes will work with either static data or registers.
;Memory Addressing Modes
;These addressing modes perform memory operations such as reading from and writing to memory. Because of the memory access, it is often slower than using the non memory addressing modes. Of course a program could not rely on immediate and register addressing modes alone, therefore the processor allows you to access memory in many different ways. Most instructions will only allow one operand to use a memory addressing mode while the other operand must use either the immediate or register addressing mode.
;Memory addresses are composed of several different components. The table below lists the components that can make up a memory address.

;Displacement Base Index Scale
;no disp EAX EAX 1
;16-bit disp EBX EBX 2
;32-bit disp ECX ECX 4
; EDX EDX 8
; ESI ESI
; EDI EDI
; EBP EBP
; ESP

; from Programmer’s Reference Manual:
;Figure 2-10. Effective Address Computation

; SEGMENT + BASE + (INDEX * SCALE) + DISPLACEMENT
;
; + +
; | — | + + + +
; + + | EAX | | EAX | | 1 |
; | CS | | ECX | | ECX | | | + +
; | SS | | EDX | | EDX | | 2 | | NO DISPLACEMENT |
; -| DS |- + -| EBX |- + -| EBX |- * -| |- + -| 8-BIT DISPLACEMENT |-
; | ES | | ESP | | — | | 4 | | 32-BIT DISPLACEMENT |
; | FS | | EBP | | EBP | | | + +
; | GS | | ESI | | ESI | | 6 |
; + + | EDI | | EDI | + +
; + + + +

changeivt:
.loadorgbiosivtwithbiosivt:
xor ax, ax ; segment of original BIOS IVT table is 0x0
mov gs, ax ; segment of original BIOS IVT table is 0x0
mov bx, 1024 ; setup loop counter
mov cx, 512 ; //1024 = 512 * sizeof(word) … setup loop counter
; loop instruction involves cx but 16 bit effective address requires bx …
.looploadorgbiosivtwithbiosivt:
mov word ax, [gs:bx] ; 16 bit effective address requires loop counter to use bx or bp instead of cx etc.
mov word [orgbiosivt + bx], ax
;mov dword [orgbiosivt + cx*4], [es:4*di]
sub bx, 2
loop .looploadorgbiosivtwithbiosivt
mov ax, [gs:0000]
mov [orgbiosivt + 0], ax ; since “loop” exists when CX is 0, 0th entry must be done manually
jmp .exitchangeivt

.loadbiosivtwithorgbiosivt:
xor ax, ax ; segment of original BIOS IVT table is 0x0
mov gs, ax ; segment of original BIOS IVT table is 0x0
mov bx, 1024 ; setup loop counter
mov cx, 512 ; //1024 = 512 * sizeof(word) … setup loop counter
.looploadbiosivtwithorigbiosivt:
mov word ax, [orgbiosivt + bx]
mov word [gs:bx], ax
sub bx, 2
loop .looploadbiosivtwithorigbiosivt
mov word ax, [orgbiosivt + 0] ; since “loop” exists when CX is 0, 0th entry must be done manually
mov word [gs:0], ax
jmp .exitchangeivt

.loadivtwithorgbiosivt:
xor ax, ax ; segment of original BIOS IVT table is 0x0
mov gs, ax ; segment of original BIOS IVT table is 0x0
mov bx, 1024 ; //1024 = 512 * sizeof(word) … setup loop counter
mov cx, 512 ; setup loop counter
.looploadivtwithorgbiosivt:
mov word ax, [orgbiosivt + bx]
mov word [ivt + bx], ax
sub bx, 2
loop .looploadivtwithorgbiosivt
mov word ax, [orgbiosivt + 0] ; since “loop” exists when CX is 0, 0th entry must be done manually
mov word [ivt + 0], ax
jmp .exitchangeivt

.loadivtwithbiosivt:
xor ax, ax ; segment of original BIOS IVT table is 0x0
mov gs, ax ; segment of original BIOS IVT table is 0x0
mov bx, 1024 ; //1024 = 512 * sizeof(word) … setup loop counter
mov cx, 512 ; setup loop counter
.looploadivtwithbiosivt:
mov word ax, [gs:bx] ; 16 bit effective address requires loop counter to use bx or bp instead of cx etc.
mov word [ivt + bx], ax
sub bx, 2
loop .looploadivtwithbiosivt
mov word ax, [gs:0] ; since “loop” exists when CX is 0, 0th entry must be done manually
mov word [ivt + 0], ax
jmp .exitchangeivt

.loadbiosivtwithivt:
xor ax, ax ; segment of original BIOS IVT table is 0x0
mov gs, ax ; segment of original BIOS IVT table is 0x0
mov bx, 1024 ; //1024 = 512 * sizeof(word) … setup loop counter
mov cx, 512 ; setup loop counter
.looploadbiosivtwithivt:
mov word ax, [ivt + bx]
mov word [gs:bx], ax
sub bx, 2
loop .looploadbiosivtwithivt
mov word ax, [ivt + 0] ; since “loop” exists when CX is 0, 0th entry must be done manually
mov word [gs:0], ax
jmp .exitchangeivt

.exitchangeivt:
ret
; from http://wiki.osdev.org/GDT_Tutorial
;gdtr DW 0 ; For limit storage
; DD 0 ; For base storage
;GDT:
;GDT_end:
;setGdt:
; xor EAX, EAX ; zero EAX register for use as scratch
; mov AX, DS ; the data segment “datasegment”
; shl EAX, 4 ; The linear address should here be computed as segment * 16 + offset. shift left 4 ~ multiply by 16
; add EAX, ”GDT” ; add offset to GDT structure in segment “datasegment”
; mov [gdtr + 2], eax ; initialize gdtr’s base storage to segment:offset address of GDT structure
; mov EAX, ”GDT_end”
; sub EAX, ”GDT” ; size of GDT structure = GDT end – GDT begin
; mov [gdtr], AX ; initialize gdtr’s to size of GDT structure = GDT end – GDT begin
; lgdt [gdtr] ; set the gdt with lgdt
; ret

; the idt or ivt table defined in the data segment “datasegment” above
;;.ivt: times 1024 db 0 ; interrupt vector table: reserve space to push-pop BIOS’ ivt table
;;.ivtend:
;;ivtend:
;; interrup descriptor table
;idt:
;idt_end:
;the idtr or ivtr structures defined in the data segment “datasegment” above:
;idtr DW 0 ; For limit storage
; DD 0 ; For base storage
;ivtr DW 0 ; For limit storage
; DD 0 ; For base storage
;.setidt: ; set the interrupt descriptor table IDT
.setivt: ; set the interrupt vector table IVT
xor EAX, EAX ; zero EAX register for use as scratch
mov AX, DS ; the data segment “datasegment”
shl EAX, 4 ; The linear address should here be computed as segment * 16 + offset. shift left 4 ~ multiply by 16
; add EAX, idt ; add offset to IDT structure in segment “datasegment”
add EAX, ivt ; add offset to IVT structure in segment “datasegment”
; mov [idtr + 2], eax ; initialize gdtr’s base storage to segment:offset address of IDT structure
mov [ivtr + 2], eax ; initialize ivtr’s base storage to segment:offset address of IVT structure
; mov EAX, idt_end
mov EAX, ivtend
; sub EAX, idt ; size of GDT structure = IDT end – IDT begin
sub EAX, ivt ; size of IDT structure = IVT end – IVT begin
; mov [idtr], AX ; initialize gdtr’s to size of IDT structure = IDT end – IDT begin
mov [ivtr], AX ; initialize ivtr’s to size of IVT structure = IVT end – IVT begin
; lgdt [idtr] ; set the idt with lgdt
;lgdt [ivtr] ; set the ivt with lgdt
lidt [ivtr] ; set the ivt with lgdt
; from Programmer’s Reference Manual
; IF instruction = LIDT
;THEN
; IF OperandSize = 16
; THEN IDTR.Limit:Base := m16:24 (* 24 bits of base loaded *)
; ELSE IDTR.Limit:Base := m16:32
; FI;
;ELSE (* instruction = LGDT *)
; IF OperandSize = 16
; THEN GDTR.Limit:Base := m16:24 (* 24 bits of base loaded *)
; ELSE GDTR.Limit:Base := m16:32;
; FI;
;FI;

;.exit:
;ret
jmp .exitchangeivt

.setorgivt: ; set the interrupt vector table IVT
xor EAX, EAX ; zero EAX register for use as scratch
; mov AX, DS ; the data segment “datasegment”
mov AX, 0x0 ; the data segment “datasegment”
shl EAX, 4 ; The linear address should here be computed as segment * 16 + offset. shift left 4 ~ multiply by 16
; add EAX, idt ; add offset to IDT structure in segment “datasegment”
; add EAX, ivt ; add offset to IVT structure in segment “datasegment”
add EAX, 0x0 ; add offset to IVT structure in segment “datasegment”
; mov [idtr + 2], eax ; initialize gdtr’s base storage to segment:offset address of IDT structure
mov [orgbiosivtr + 2], eax ; initialize ivtr’s base storage to segment:offset address of IVT structure
; mov EAX, idt_end
mov EAX, orgbiosivtend
; sub EAX, idt ; size of GDT structure = IDT end – IDT begin
sub EAX, orgbiosivt ; size of IVT structure = IVT end – IVT begin
mov AX, 400h ; initialize size of ivtr to … size of original BIOS IVT structure
; mov [idtr], AX ; initialize gdtr’s to size of IDT structure = IDT end – IDT begin
mov [orgbiosivtr], AX ; initialize ivtr’s to size of IVT structure = IVT end – IVT begin
; lgdt [idtr] ; set the idt with lgdt
;lgdt [orgbiosivtr] ; set the ivt with lgdt
lidt [orgbiosivtr] ; set the ivt with lgdt
; from Programmer’s Reference Manual
; IF instruction = LIDT
;THEN
; IF OperandSize = 16
; THEN IDTR.Limit:Base := m16:24 (* 24 bits of base loaded *)
; ELSE IDTR.Limit:Base := m16:32
; FI;
;ELSE (* instruction = LGDT *)
; IF OperandSize = 16
; THEN GDTR.Limit:Base := m16:24 (* 24 bits of base loaded *)
; ELSE GDTR.Limit:Base := m16:32;
; FI;
;FI;
;.exit:
;ret
jmp .exitchangeivt

; pbs “peg and cat” …. suggestively similar “peg and cat” bedding at baby r us 3/13/2014
; substitute custom interrupt numbers 5 and 9 into new ivt table created by cloning from original BIOS ivt table: neighbor dude with custom truck 3/13/2014

.insertcustominterruptsintoivt:
cli ; pause interrupts

mov AX, safeinterrupt5sr ; offset
; mov dword [ivt + 5*4], AX
;; mov [gs:5*4], AX ; insert offset part of address of custom interrupt service routine into BIOS ivt
mov [ivt + 5*4], AX ; insert offset part of address of custom interrupt service routine into ivt
mov AX, PROGRAMSEGMENT ; segment
; mov dword [ivt + 5*4+2], AX
;; mov [gs:5*4+2], AX ; insert segment part of address of custom interrup service routine into BIOS ivt
mov [ivt + 5*4+2], AX ; insert segment part of address of custom interrup service routine into ivt

mov AX, safeinterrupt9sr ; offset
; mov dword [ivt + 9*4], AX
;; mov [gs:9*4], AX ; insert offset part of address of custom interrupt service routine into BIOS ivt
mov [ivt + 9*4], AX ; insert offset part of address of custom interrupt service routine into ivt
mov AX, PROGRAMSEGMENT ; segment
; mov dword [ivt + 9*4+2], AX
;; mov [gs:9*4+2], AX ; insert segment part of address of custom interrupt service routine into BIOS ivt
mov [ivt + 9*4+2], AX ; insert segment part of address of custom interrupt service routine into ivt

sti ; re-enable interrupts

jmp .exitchangeivt

;;.removecustominterruptsfromivt

; restore original interrupt vector table from BIOS with original interrupt numbers 5 and 9
; call changeivt.loadivtwithorgbiosivt
;; call changeivt.loadbiosivtwithorgbiosivt
;; call changeivt.setorgivt
;; cli ; pause interrupts
;; mov ax, [interrupt5offset]
;; mov [gs:5*4], ax
;; mov ax, [interrupt5segment]
;; mov [gs:5*4+2], ax

;; mov ax, [interrupt9offset]
;; mov [gs:9*4], ax
;; mov ax, [interrupt9segment]
;; mov [gs:9*4+2], ax
;; sti ; re-enable interrupts

;sti ; re-enable interrupts

;; jmp .exitchangeivt

%endif ; TRYIVT

; gia ba?o suggested for balance to “say hello”:
seeyoulater:

; call exit
call word exit
; call hang
call word hang
ret ; return

; from http://www.supernovah.com/Tutorials/BootSector4.php:
;Video Memory
;As previously stated, what is printed to the screen is simply controlled by a special section of memory called
;the video memory (or VGA memory). This section of memory is then periodically copied to the video device
;memory which is then presented to the screen by the Digital Analog Converter (DAC). Currently we are in text
;mode 03h which is a form of EGA. The video memory for text mode 3h begins at 0xB8000. Text mode 03h is 80 characters wide
;and 25 characters tall. This gives us 2000 total characters (80 * 25). Each character consists of 2 bytes which
;yields 4000 bytes of memory in total. So this means that text mode 03h stores it’s video information (the information that is
;printed to the screen) at the memory address 0xB8000 and it takes up 4000 bytes of memory.
;Printing Character to the Screen
;The first we must do in order to print character to the screen is to get a segment register setup that points
;to the memory location 0xB8000 [= 753664 = 47104 * 16]. Remember that segments in real mode have the lower four bits implicitly
;set to zero and because each hex digit represents four bits we can easily drop the right most zero on the
;memory address when storing it in a segment register. We will use the ES segment register because we
;still want to access our data with the DS segment so we don’t run into problems when using instructions that
;implicitly use the DS segment by default.
;mov AX,0xB800 ;// = 47104
;mov ES,AX

;screen output …
;for the screen, the messages in (“muo^n loa`i” ;(“muo^n loa`i va` pixel1 va` pixel2 va` … ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”)

screensetup: ; point ES to video memory
.setupvideosegment:
mov AX,0xB800 ;// = 47104
mov ES,AX
; to use the stack, use “call” and “ret” instead of “jmp”
; or just let the program flows, without the jmp, to instructions that follow
;jmp clearscreenpixels
ret ; return

; from http://staff.ustc.edu.cn/~xyfeng/research/cos/resources/machine/mem.htm:
;0x0000:0x0000 1024 bytes Interrupt Vector Table
;0x0040:0x0000 256 bytes BIOS Data Area
;0x0050:0x0000 ? Free memory
;0x07C0:0x0000 512 bytes Boot sector code
;0x07E0:0x0000 ? Free memory
;0xA000:0x0000 64 Kb Graphics Video Memory
;0xB000:0x0000 32 Kb Monochrome Text Video Memory
;0xB800:0x0000 32 Kb Color Text Video Memory
;0xC000:0x0000 256 Kb1 ROM Code Memory
;0xFFFF:0x0000 16 bytes More BIOS data

;Clearing the Background
;Clearing the background is rather trivial. The goal is to set all of the attribute bytes to the background color
;you wish to clear it to. The basic idea is to create a loop that will set every other byte, starting at the first
;attribute byte, to the background color we wish to clear to. We must also be sure to only clear all of the attributes that
;are used to represent the string. In other words, be sure not to go past the last attribute byte. The last attribute byte is
;found at 80 * 25 * 2 – 1. The 80 is the width and the 25 is the height. The 2 is there because two bytes make up each
;character; one for the character and one for the attribute. Finally the 1 is subtracted because our first attribute byte is
;actually the second byte at the beginning The 1 simply takes into account that we start our count at one instead of zero.

;The right most hex digit sets the lower four bits of the attribute byte. The lower four bits control the character color while the upper
;four bits (the left most hex digit) control the background color and flash bit. We set the background and flash bits (upper four bits) to 0h
; because 0h corresponds to the color black with no flashing.

;color index hex 64-color palette index
;Black 0 00h 0
;Blue 1 01h 1
;Green 2 02h 2
;Cyan 3 03h 3
;Red 4 04h 4
;Magenta 5 05h 5
;Brown 6 06h 20
;Light Gray 7 07h 7
;Dark Gray 8 08h 56
;Bright Blue 9 09h 57
;Bright Green 10 0Ah 58
;Bright Cyan 11 0Bh 59
;Bright Red 12 0Ch 60
;Bright Magenta 13 0Dh 61
;Bright Yellow 14 0Eh 62
;Bright White 15 0Fh 63

; from http://gd.tuwien.ac.at/languages/c/programming-bbrown/advcw2.htm and
;offset = (( row * 0x50 + column ) * 2 ) + ( pagenum * 0x1000 )

clearscreenpixels:
mov CX,0x50 * 25 * 2 – 1
mov BX,1
.Loopthroughscreenpixels:
cmp BX,CX
ja .finishclearscreenpixels ;CF = 0 and ZF = 0
;ja Loads EIP with the specified address, if first operand of previous CMP instruction is greater than the second. ja is the same as jg, except that it performs an unsigned comparison.

mov byte [ES:BX],70h ;Set background to light gray
;and the text to black
;with no flashing text
add BX,2
jmp .Loopthroughscreenpixels ; jmp Loads EIP with the specified address

.finishclearscreenpixels:
; to use the stack, use “call” and “ret” instead of “jmp”
; or just let the program flows, without the jmp, to instructions that follow
;jmp exit
;jmp sayhello
ret

%ifdef SAYHELLO
sayhello:
mov byte [ES:0],’h’
mov byte [ES:2],’o’
mov byte [ES:4],’p’
mov byte [ES:6],’e’
mov byte [ES:8],’ ‘
mov byte [ES:10],’w’
mov byte [ES:12],’e’
mov byte [ES:14],’l’
mov byte [ES:16],’l’
; from NASM manual
; wordvar dw 123
; mov ax,[wordvar]
; mov ax,[wordvar+1]
; mov ax,[es:wordvar+bx]
; test stacksegment ; stack ~ buffer … to^nan does not have enough fat/buffer on him
; xor bl, bl
; from http://www.supernovah.com/Tutorials/Assembly4.php:
;When the processor pushes data onto the stack it does the following operations:
;1.Subtract 4 from SP or ESP
;2.Move the source data to the memory location [SS:SP] or [SS:ESP]
; push dword 117 ;Push the value 117 as a dword onto the stack
; push dword [0x500] ;Push the value at the memory location 0x500 onto the stack
; push byte ‘H’ ;Push the value 117 as a dword onto the stack ; nasm gives no error with the “byte” specification, see http://f.osdev.org/viewtopic.php?f=1&t=13399
; push byte ‘o’ ;Push the value 117 as a dword onto the stack
; push byte ‘p’ ;Push the value 117 as a dword onto the stack
; push byte ‘e’ ;Push the value 117 as a dword onto the stack
; push byte ‘W’ ;Push the value 117 as a dword onto the stack
; push byte ‘e’ ;Push the value 117 as a dword onto the stack
; push byte ‘l’ ;Push the value 117 as a dword onto the stack
; from http://www.supernovah.com/Tutorials/BootSector4.php:
; When the processor pushes data onto the stack it does the following operations:
;1.Subtract 4 from SP or ESP
;2.Move the source data to the memory location [SS:SP] or [SS:ESP]

; mov [spprevious], SP
; … some operation …
; mov [spnew], SP
; mov word [spcounter + 2 * 0], spprevious – spnew

; mov [spprevious], SP
push ‘H ‘ ;Push the value 117 as a dword onto the stack
;pushd ‘H ‘ ;Push the value 117 as a dword onto the stack
;pushw ‘H ‘ ;Push the value 117 as a dword onto the stack
;push word ‘H ‘ ;Push the value 117 as a dword onto the stack
;push dword ‘H ‘ ;Push the value 117 as a dword onto the stack
; mov [spnew], SP
; mov word [spcounter + 2 * 2], spprevious – spnew

push ‘O ‘ ;Push the value 117 as a dword onto the stack
push ‘P ‘ ;Push the value 117 as a dword onto the stack
push ‘E ‘ ;Push the value 117 as a dword onto the stack
push ‘W ‘ ;Push the value 117 as a dword onto the stack
push ‘E ‘ ;Push the value 117 as a dword onto the stack
push ‘L ‘ ;Push the value 117 as a dword onto the stack
;stacktop = stacksegment – datasegment + 64
; xor bl, bl
; mov byte bl, [0 + stacksegment – datasegment + 64 – 0] ; ‘l’
; mov byte [ES:30], bl
; mov byte bl, [0 + stacksegment – datasegment + 64 – 1] ; ‘e’
; mov byte [ES:32], bl
; mov byte bl, [0 + stacksegment – datasegment + 64 – 2] ; ‘W’
; mov byte [ES:34], bl
; mov byte bl, [0 + stacksegment – datasegment + 64 – 3] ; ‘e’
; mov byte [ES:36], bl
xor bl, bl

; STACK states at various points …
; *****************
; *****************
; *****************
; *****************
; ***************** << SP
; ***************** 2 bytes after call main
; *****************

; *****************
; *****************
; ***************** << SP
; ***************** + 2 bytes after call sayhello
; *****************
; ***************** 2 bytes after call main
; *****************

; ***************** << SP
; ***************** + 2 bytes after PUSH ‘H ‘
; *****************
; ***************** + 2 bytes after call sayhello
; *****************
; ***************** 2 bytes after call main
; *****************

; mov byte bl, [stacktop – 4 * 2 + 2] ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
mov byte bl, [stacktop – 2 – 2 – 2] ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
;pop byte bl; nasm gives error: invalid combination of opcode and operands
;pop bx;
mov byte [ES:60], bl ; ‘H ‘
mov byte bl, [stacktop – 2 * 2 – 2 – 2] ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
;pop byte bl; nasm gives error: invalid combination of opcode and operands
;pop bx;
mov byte [ES:62], bl ; ‘O ‘
mov byte bl, [stacktop – 2 * 2 – 2 – 2 – 2]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
;pop bx;
mov byte [ES:64], bl ; ‘P ‘
mov byte bl, [stacktop – 2 * 2 – 2 – 2 – 2 – 2]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
;pop bx;
mov byte [ES:66], bl ; ‘E ‘
mov byte bl, [stacktop – 2 * 2 – 2 – 2 – 2 – 2 – 2]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
;pop bx;
mov byte [ES:68], bl ; ‘W ‘
mov byte bl, [stacktop – 2 * 2 – 2 – 2 – 2 – 2 – 2 – 2]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
;pop bx;
mov byte [ES:70], bl ; ‘E ‘
mov byte bl, [stacktop – 2 * 2 – 2 – 2 – 2 – 2 – 2 – 2 – 2]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
;pop bx;
mov byte [ES:72], bl ; ‘L ‘
mov byte bl, [stacktop – 2 * 2 – 2 – 2 – 2 – 2 – 2 – 2 – 2]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
;pop bx;
mov byte [ES:74], bl

; from http://stackoverflow.com/questions/15792702/convert-byte-to-string-in-x86-assembly-language
;.data
;mystr db 33 dup(0)
;
;.code
;
;EaxToBinaryString:
; mov ebx, offset mystr
; mov ecx, 32
;EaxToBinaryString1:
; mov dl, ‘0’ ; replace ‘0’ with 0 if you don’t want an ASCII string
; rol eax, 1
; adc dl, 0
; mov byte ptr [ebx], dl
; inc ebx
; loop EaxToBinaryString1
; ret
; from http://stackoverflow.com/questions/1922134/printing-out-a-number-in-assembly-language
; mov al,4
; or al,30h ;Important! =>Convert Character to Number!
; mov i,al
;
; MOV AH, 2 ;
; MOV DL, i ; Print Character.
; INT 21H ;

; mov [spprevious], SP
; … some operation …
; mov [spnew], SP
; mov word [spcounter + 2 * 0], spprevious – spnew
; xor bl, bl
; mov byte bl, [spcounter + 2 * 0]
; mov byte [ES:76], bl
; mov byte bl, [spcounter + 2 * 1]
; mov byte [ES:7], bl
; mov byte bl, [spcounter + 2 * 2]
; mov byte [ES:], bl

xor bl, bl
;pop byte bl; nasm gives error: invalid combination of opcode and operands
pop bx; ‘L ‘
;mov byte bl, [stacktop – 0]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
mov byte [ES:56], bl
;mov byte bl, [stacktop – 4]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
pop bx; ‘E ‘
mov byte [ES:54], bl
;mov byte bl, [stacktop – 8]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
pop bx; ‘W ‘
mov byte [ES:52], bl
;mov byte bl, [stacktop – 12]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
pop bx; ‘E ‘
mov byte [ES:46], bl
;mov byte bl, [stacktop – 16]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
pop bx; ‘P ‘
mov byte [ES:44], bl
;mov byte bl, [stacktop – 20]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
pop bx; ‘O ‘
mov byte [ES:42], bl
;mov byte bl, [stacktop – 24]
;pop byte bl; nasm gives error: invalid combination of opcode and operands
pop bx; ‘H ‘
mov byte [ES:40], bl

; test datasegment
xor bl, bl
mov byte bl, [datasegment]
; mov byte bl, [0]
; mov byte bl, [DS:0]
mov byte [ES:20], bl
mov byte bl, [datasegment + 1]
; mov byte bl, [1]
mov byte [ES:22], bl
mov byte bl, [datasegment + 2]
; mov byte bl, [2]
mov byte [ES:24], bl
mov byte bl, [datasegment + 3]
; mov byte bl, [3]
mov byte [ES:26], bl
mov byte bl, [datasegment + 4]
; mov byte bl, [4]
mov byte [ES:28], bl
mov byte bl, [datasegment + 5]
; mov byte bl, [5]
mov byte [ES:30], bl
mov byte bl, [datasegment + 6]
; mov byte bl, [6]
mov byte [ES:32], bl
mov byte bl, [datasegment + 7]
; mov byte bl, [7]
mov byte [ES:34], bl
; mov byte [ES:16], [datasegment + 1]

; to use the stack, use “call” and “ret” instead of “jmp”
; or just let the program flows, without the jmp, to instructions that follow
;jmp exit
ret ; sayhello

; test interrupt-support stack boundaries
; from http://www.eecg.toronto.edu/~amza/www.mindsec.com/files/x86regs.html
;SS:EBP EBP BP : Stack Base pointer register
; Holds the base address of the [current] stack [frame]
;SS:ESP ESP SP : Stack pointer register
; Holds the top address of the stack

; from NASM manual:
;4.3 Multi-Line Macros: %macro
;Multi-line macros are much more like the type of macro seen in MASM and TASM: a multi-line macro definition in NASM looks something like this.
;%macro prologue 1
; push ebp
; mov ebp,esp
; sub esp,%1
;%endmacro
; from Programmer’s Reference Manual:
;1.The stack pointer (ESP) register. ESP points to the top of the push-down stack (TOS). It is referenced implicitly by PUSH and POP operations, subroutine calls and returns, and interrupt operations. When an item is pushed onto the stack (see Figure 2-7 ), the processor decrements ESP, then writes the item at the new TOS. When an item is popped off the stack, the processor copies it from TOS, then increments ESP. In other words, the stack grows down in memory toward lesser addresses.
; BOUND instruction:
;62 /r BOUND r16,m16&16 10 Check if r16 is within bounds
; (passes test)
;62 /r BOUND r32,m32&32 10 Check if r32 is within bounds
; (passes test)
;IF (LeftSRC < [RightSRC] OR LeftSRC > [RightSRC + OperandSize/8])
; (* Under lower bound or over upper bound *)
;THEN Interrupt 5;
;FI;

; note: because “int #” instruction will use stack to store CS:IP and FLAGS,
; Push (FLAGS);
; Push(CS);
; Push(IP);
; have to allow on the stack for that much room 32 bits + 32 bits = 4 bytes + 4 bytes …
; from Programmer’s Reference Manual for INT instruction:
; REAL-ADDRESS-MODE:
; Push (FLAGS);
; IF = 0; (* Clear interrupt flag *)
; TF = 0; (* Clear trap flag *)
; Push(CS);
; Push(IP);
; (* No error codes are pushed *)
; CS := IDT[Interrupt number * 4].selector;
; IP := IDT[Interrupt number * 4].offset;
; from Programmer’s Reference Manual
; IRET (Return From Interrupt) returns control to an interrupted procedure. IRET differs from RET in that it also pops the flags from the stack into the flags register. The flags are stored on the stack by the interrupt mechanism.
%endif ; SAYHELLO

; SAFEWAY grocery …
%macro SAFECALL 1
; cmp SP, stacksegment
; jl safecallinterrupt
; safecallinterrupt int 5
bound SP, stacklowerbound + 4 + 4 ;
call %1
%endmacro
%macro SAFEPUSH 1
; cmp SP, stacksegment
; jl safecallinterrupt
; safepushinterrupt int 5
bound SP, stacklowerbound + 4 + 4 ;
push %1
%endmacro
%macro SAFEPOP 1
; cmp SP, stacksegment
; jl safecallinterrupt
; safepopinterrupt int 5
bound SP, stacklowerbound + 4 + 4 ;
pop %1
%endmacro
%macro SAFERET 1
; cmp SP, stacksegment
; jl safecallinterrupt
; saferetinterrupt int 5
bound SP, stacklowerbound + 4 + 4 ;
ret %1
%endmacro
;%macro SAFEINT5 0
; my/your own interrupt 5 service routine
safeinterrupt5sr:
add [interrupt5count], 1
xor bl, bl
; mov byte bl, [stacktop – 4 * 2 + 2] ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
mov byte bl, ‘I’ ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
;pop byte bl; nasm gives error: invalid combination of opcode and operands
;pop bx;
mov byte [ES:90], bl ; ‘I ‘
mov byte bl, ‘N’ ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
mov byte [ES:92], bl ; ‘N ‘
mov byte bl, ‘T’ ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
mov byte [ES:94], bl ; ‘T ‘
mov byte bl, ‘5’ ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
mov byte [ES:96], bl ; ‘5 ‘
mov al, [interrupt5count]
call out1hex

; from Programmer’s Reference Manual
; IRET (Return From Interrupt) returns control to an interrupted procedure. IRET differs from RET in that it also pops the flags from the stack into the flags register. The flags are stored on the stack by the interrupt mechanism.
;ret
iret
;%endmacro ; SAFEINT5

;%macro SAFEINT9 0
; my/your own interrupt 9 service routine
safeinterrupt9sr:
add word [interrupt9count], 1
xor bl, bl
; mov byte bl, [stacktop – 4 * 2 + 2] ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
mov byte bl, ‘I’ ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
;pop byte bl; nasm gives error: invalid combination of opcode and operands
;pop bx;
mov byte [ES:100], bl ; ‘I ‘
mov byte bl, ‘N’ ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
mov byte [ES:102], bl ; ‘N ‘
mov byte bl, ‘T’ ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
mov byte [ES:104], bl ; ‘T ‘
mov byte bl, ‘9’ ; four routines were called: call main, call screensetup, call clearscreenpixels, call sayhello of which only two routines were left opened on the stack by the time this point is reached
mov byte [ES:106], bl ; ‘9 ‘
mov al, [interrupt9count]
call out1hex
call safeinterrupt9sr2

; from Programmer’s Reference Manual
; IRET (Return From Interrupt) returns control to an interrupted procedure. IRET differs from RET in that it also pops the flags from the stack into the flags register. The flags are stored on the stack by the interrupt mechanism.
;ret
iret
;%endmacro ; SAFEINT9

;%macro SAFEINT9 0

;keyboard input …
;for the screen, the messages in (“muo^n loa`i” ;(“muo^n loa`i va` key1 va` key2 va` … ddu+o+.c so^’ng la^u bi`nh thu+o+`ng; everyone live long and well”)

; from Programmer’s Reference Manual
;The 80386 provides a separate I/O address space, distinct from physical memory, that can be used to address the input/output ports that are used for external 16 devices. The I/O address space consists of 2^(16) (64K) individually addressable 8-bit ports; any two consecutive 8-bit ports can be treated as a 16-bit port; and four consecutive 8-bit ports can be treated as a 32-bit port. Thus, the I/O address space can accommodate up to 64K 8-bit ports, up to 32K 16-bit ports, or up to 16K 32-bit ports.
;The program can specify the address of the port in two ways. Using an immediate byte constant, the program can specify:
;• 256 8-bit ports numbered 0 through 255.
;• 128 16-bit ports numbered 0, 2, 4, . . . , 252, 254.
;• 64 32-bit ports numbered 0, 4, 8, . . . , 248, 252.
;Using a value in DX, the program can specify: • 8-bit ports numbered 0 through 65535
;• 16-bit ports numbered 0, 2, 4, . . . , 65532, 65534
;• 32-bit ports numbered 0, 4, 8, . . . , 65528, 65532
;The 80386 can transfer 32, 16, or 8 bits at a time to a device located in the I/O space. Like doublewords in memory, 32-bit ports should be aligned at addresses evenly divisible by four so that the 32 bits can be transferred in a single bus access. Like words in memory, 16-bit ports should be aligned at even-numbered addresses so that the 16 bits can be transferred in a single bus access. An 8-bit port may be located at either an even or odd address.
;The instructions IN and OUT move data between a register and a port in the I/O address space. The instructions INS and OUTS move strings of data between the memory address space and ports in the I/O address space.

; from http://www.brokenthorn.com/Resources/OSDev19.html
;Keyboard Controller Ports
;Port Read/Write Descripton
;Keyboard Encoder
;0x60 Read Read Input Buffer
;0x60 Write Send Command
;Onboard Keyboard Controller
;0x64 Read Status Register
;0x64 Write Send Command

; following code for custom INT 9 service routine is from Randall Hyde’s Art of Assembly http://www.plantation-productions.com/Webster/www.artofasm.com/DOS/ch20/CH20-5.html

SetCmd:
push cx
push ax ;Save command value.
cli ;Critical region, no ints now.

; keyboard code following …. last weekend visit to saratoga to toilet with sign “wait 30 seconds between each flush”
; Wait until the 8042 is done processing the current command.

xor cx, cx ;Allow 65,536 times thru loop.
Wait4Empty: in al, 64h ;Read keyboard status register.
test al, 10b ;Input buffer full?
loopnz Wait4Empty ;If so, wait until empty.
; from Programmer’s Reference Manual
; LOOP (Loop While ECX Not Zero) is a conditional transfer that automatically decrements the ECX register
; before testing ECX for the branch condition. If ECX is non-zero, the program branches to the target label
; specified in the instruction. The LOOP instruction causes the repetition of a code section until the operation
; of the LOOP instruction decrements ECX to a value of zero. If LOOP finds ECX=0, control transfers to the
; instruction immediately following the LOOP instruction. If the value of ECX is initially zero, then the LOOP
; executes 2^(32) times.
;LOOPNE (Loop While Not Equal) and LOOPNZ (Loop While Not Zero) are synonyms for the same instruction.
; These instructions automatically decrement the ECX register before testing ECX and ZF for the branch conditions.
; If ECX is non-zero and ZF=0, the program branches to the target label specified in the instruction.
; If LOOPNE or LOOPNZ finds that ECX=0 or ZF=1, control transfers to the instruction immediately following the LOOPNE or LOOPNZ instruction.
; from Programmer’s Reference Manual
;TEST (Test) performs the logical “and” of the two operands, clears OF and CF, leaves AF undefined, and updates SF, ZF, and PF.
; The flags can be tested by conditional control transfer instructions or by the byte-set-on-condition instructions. The operands may be doublewords, words, or bytes.
; The difference between TEST and AND is that TEST does not alter the destination operand. TEST differs from BT in that TEST is useful for testing the value of multiple bits in one operations, whereas BT tests a single bit.
; from Programmer’s Reference Manual
;Status Flags’ Functions
;Bit Name Function
; 0 CF Carry Flag — Set on high-order bit carry or borrow; cleared
; otherwise.
; 2 PF Parity Flag — Set if low-order eight bits of result contain
; an even number of 1 bits; cleared otherwise.
; 4 AF Adjust flag — Set on carry from or borrow to the low order
; four bits of AL; cleared otherwise. Used for decimal
; arithmetic.
; 6 ZF Zero Flag — Set if result is zero; cleared otherwise.
; 7 SF Sign Flag — Set equal to high-order bit of result (0 is
; positive, 1 if negative).
;11 OF Overflow Flag — Set if result is too large a positive number
; or too small a negative number (excluding sign-bit) to fit in
; destination operand; cleared otherwise.

; Okay, send the command to the 8042:

pop ax ;Retrieve command.
out 64h, al
sti ;Okay, ints can happen again.
pop cx
ret ; end SetCmd

safeinterrupt9sr2:
push ds
push ax
push cx

;mov ax, 40h
mov ax, PROGRAMSEGMENT
mov ds, ax

mov al, 0ADh ;Disable keyboard
call SetCmd
cli ;Disable interrupts.
xor cx, cx
Wait4Data: in al, 64h ;Read kbd status port.
test al, 10b ;Data in buffer?
loopz Wait4Data ;Wait until data available.

in al, 60h ;Get keyboard data.
; cmp al, DelScanCode ;Is it the delete key?
; jne OrigInt9
; mov al, KbdFlags ;Okay, we’ve got DEL, is
; and al, AltBit or CtrlBit ; ctrl+alt down too?
; cmp al, AltBit or CtrlBit
; jne OrigInt9
; jmp OrigInt9
; echo keyboard data to screen …
call out2hex
; If ctrl+alt+DEL is down, just eat the DEL code and don’t pass it through.

mov al, 0AEh ;Reenable the keyboard
call SetCmd

mov al, 20h ;Send EOI (end of interrupt)
out 20h, al ; to the 8259A PIC.
pop cx
pop ax
pop ds
;iret
ret

; If ctrl and alt aren’t both down, pass DEL on to the original INT 9
; handler routine.

;OrigInt9: mov al, 0AEh ;Reenable the keyboard
; call SetCmd
;
; pop cx
; pop ax
; pop ds
;jmp cs:OldInt9

;%endmacro ; SAFEINT9

%macro DISPLAYREGISTERCHARACTERS 1
;displaycharacter:
xor BL, BL
;mov byte BL, al ;
mov byte BL, %1 ;
mov byte [ES:300], BL ; ‘H ‘
;ret ; displaycharacter
%endmacro ; DISPLAYCHARACTER

;%macro OUTPUTHEXNUMBER 0
; following code for input/output numbers is from http://www.ece.msstate.edu/~janem/ECE3724/S03/Presentations/number_io.ppt
out2hex: ; output value in ‘al’ as 2 hex character
;push byte AL ; save al
;push word AX ; save al
;push dword EAX ; save al
push AX ; save al
shr AL, 4 ; get most sig. 4 bits into lower
call out1hex ; print most sig. hex digit
pop AX ; get back original al
and AL, 0x0F ; upper 4 bits = 0 – working with low 4 bits
call out1hex ; print least sig. hex digit
ret

; from http://faydoc.tripod.com/cpu/jmp.htm :
;Description
; Transfers program control to a different point in the instruction stream without recording return information. The destination (target) operand specifies the address of the instruction being jumped to. This operand can be an immediate value, a general-purpose register, or a memory location.
;
;This instruction can be used to execute four different types of jumps:
; Near jump A jump to an instruction within the current code segment (the segment currently pointed to by the CS register), sometimes referred to as an intrasegment jump.
; Short jump A near jump where the jump range is limited to –128 to +127 from the current EIP value.
; Far jump A jump to an instruction located in a different segment than the current code segment but at the same privilege level, sometimes referred to as an intersegment jump.
; Task switch A jump to an instruction located in a different task.
;
;A task switch can only be executed in protected mode (see Chapter 6, Task Management, in the Intel Architecture Software Developer’s Manual, Volume 3, for information on performing task switches with the JMP instruction).

out1hex:
cmp AL, 0x09 ; is 4-bit value above 9 – i.e. a “number” 0-9
;jg skip ; jg “jump if greater” is signed, ja “jump if above” is unsigned
ja isCharacter ; if “greater than”, then must be a character
add AL, 0x30
;call displaycharacter
DISPLAYREGISTERCHARACTERS AL
ret
;;out1hexhigh:
;; cmp AH, 0x09 ; is 4-bit value above 9 – i.e. a “number” 0-9
;; ;jg skip ; jg “jump if greater” is signed, ja “jump if above” is unsigned
;; ja isCharacter ; if “greater than”, then must be a character
;; add AH, 0x30
;; ;call displaycharacter
;; DISPLAYREGISTERCHARACTERS AH
;; ret
isCharacter:
add AL, 0x37
;call displaycharacter
DISPLAYREGISTERCHARACTERS AL
ret
;out4hex:
; push AX ; save al
; shr AH, 4 ; get most sig. 4 bits into lower
; call out1hexhigh ; print most sig. hex digit
; pop AX ; get back original al
; and AH, 0x0F ; upper 4 bits = 0 – working with low 4 bits
; call out1hexhigh ; print least sig. hex digit

; push AX ; save al
; shr AL, 4 ; get most sig. 4 bits into lower
; call out1hex ; print most sig. hex digit
; pop AX ; get back original al
; and AL, 0x0F ; upper 4 bits = 0 – working with low 4 bits
; call out1hex ; print least sig. hex digit

; ret ; out4hex
;out32bithex:
; push EAX ; save EAX
; shr EAX, 16 ; get the most sig. 16 bits into lower
; call out4hex ;
; pop EAX ; restore EAX

; ;push EAX ; save EAX
; call out4hex
; ;pop EAX ; restore EAX

; ret ; out4hex
;%endmacro ; OUTPUTHEXNUMBER

exit:
; to use the stack, use “call” and “ret” instead of “jmp”
; or just let the program flows, without the jmp, to instructions that follow
; jmp hang

hang:
jmp hang ; or, equivalently in nasm: jmp $
hlt ; halt the system

; times 510-($-$$) db 0 ; 2 bytes less now; $ = beginning of current line/expression = “times”, $$ = beginning of current section = “hang:”
db 0x55
db 0xAA
;********************************************
;*** NOTE ***
; from arpaci dusseau http://pages.cs.wisc.edu/~remzi/OSTEP/vm-segmentation.pdf
;an idea was born, and it is called segmenta-
;tion. It is quite an old idea, going at least as far back as the very early
;1960’s [H61, G62]. The idea is simple: instead of having just one base
;and bounds pair in our MMU, why not have a base and bounds pair per
;logical segment of the address space?
;[G62] “Fact Segmentation”
;M. N. Greenfield
;Proceedings of the SJCC, Volume 21, May 1962
;Another early paper on segmentation; so early that it has no references to other work.
;[H61] “Program Organization and Record Keeping for Dynamic Storage”
;A. W. Holt
;Communications of the ACM, Volume 4, Issue 10, October 1961
;An incredibly early and difficult to read paper about segmentation and some of its uses.

; from http://en.wikipedia.org/wiki/THE_multiprogramming_system
; The THE multiprogramming system was a computer operating system designed by a team led by Edsger W. Dijkstra, described in monographs in 1965-66[1] and published in 1968.[2] Dijkstra never named the system; “THE” is simply the abbreviation of “Technische Hogeschool Eindhoven”, then the name (in Dutch) of the Eindhoven University of Technology of the Netherlands. The THE system was primarily a batch system[3] that supported multitasking; it was not designed as a multi-user operating system. It was much like the SDS 940, but “the set of processes in the THE system was static”.[3]
;The THE system apparently introduced the first forms of software-based memory segmentation (the Electrologica X8 did not support hardware-based memory management),[3] freeing programmers from being forced to use actual physical locations on the drum memory. It did this by using a modified ALGOL compiler (the only programming language supported by Dijkstra’s system) to “automatically generate calls to system routines, which made sure the requested information was in memory, swapping if necessary”.[3]

; from NASM manual:
;NASM gives special treatment to symbols beginning with a period. A label beginning with a single period is treated as a local label, which means that it is associated with the previous non-local label. So, for example:
;label1 ; some code
;.loop
; ; some more code
; jne .loop
; ret
;label2 ; some code
;.loop
; ; some more code
; jne .loop
; ret
;In the above code fragment, each JNE instruction jumps to the line immediately before it, because the two definitions of .loop are kept separate by virtue of each being associated with the previous non-local label.

;from http://wiki.osdev.org/Interrupts
; if IRQ 6 is sent to the PIC by a device, the PIC would tell the CPU to service INT 0Eh, which presumably has code for interacting with whatever device sent the interrupt in the first place. Of course, there can be trouble when two or more devices share an IRQ; if you wonder how this works, check out Plug and Play.

; from http://www.techmasala.com/2006/03/31/foundation-stone-3-bios-part-2-the-interrupt-vector-table/:
;Foundation stone #3 – BIOS part 2 – The interrupt vector table
;by Ramesh on Friday,March 31, 2006 @ 9:50 am
;In my post Foundation stone #2 we saw that BIOS is the one that takes in charge when you switch on your PC. After collecting the inventory of available and properly working hardware, the BIOS sets up what is called as the Interrupts area. An interrupt is a signal to the processor that there is something that needs its attention. As such each and every piece of hardware that is put together in your PC is useless unless it is orchestrated well. Take for example the keyboard, if the attention is not given at the right time when you press a key and reciprocated accordingly wherever you are then you can call the thing that is sitting in front of you as dumb
;So when the BIOS is done with the inventory of hardware, it initializes a memory space of 1024 bytes starting at 0000:0000h (this is a representation of memory location in the form of segment:offset in hexadecimal). An interrupt is a small routine or code that has the necessary details of the interrupt and occupies 4 bytes. So starting at memory location 0000:0000h interrupts are stored. So a total of 256 interrupts can be stored in a the allotted 1024 bytes but all is not being initialized by the BIOS. There are different types of interrupts, hardware interrupts, software interrupts, user interrupts and so on. The BIOS fills up the hardware interrupts and the software interrupts are mostly added by the OS.
;The Interrupt Vector Table (IVT) is a mapping of the interrupt number and the memory location in the form of segment:offset. This memory location contains the interrupt code for that particular interrupt. It is the responsibility of the OS to keep track of the IVT and monitor for interrupt and notify the processor. So what happens when you press a key or release a key, the keyboard send signals that contain information on what key was pressed or released. This gets stored in the memory location assigned for the keyboard interrupt (traditionally interrupt 09h is for keyboard). The OS which is constantly looking for these interrupts immediately captures the information and sends it for processing accordingly. The interrupt number and other details could differ from one BIOS manufacturer to other. You can get a lot of information about BIOS and interrupts from the BIOS central site.

; conventionally [c.f. http://en.wikipedia.org/wiki/Conventional_memory, http://en.wikipedia.org/wiki/Power-on_self-test%5D people agree upon the following memory map … from http://www.supernovah.com/Tutorials/Assembly2.php:
;Default Memory
;When the computer boots, the BIOS loads the memory with a lot of different data. This data resides in different places throughout memory and we are only left with 630Kb of memory to work with in the middle of everything. Here is a table showing the map of the memory directly after the computer boots:
;All ranges are inclusive
;Address Range (in hex) Size Type Description
;0 – 3FF 1Kb Ram Real Mode Interrupt Vector Table (IVT)
;400 – 4FF 256 bytes Ram BIOS Data Area (BDA)
;500 – 9FBFF 630Kb Ram Free Memory
;9FC00 – 9FFFF 1Kb Ram Extended BIOS Area (EBDA)
;A0000 – BFFFF 128Kb Video Ram VGA Frame Buffer
;C0000 – C7FFF 32Kb Rom Video Bios
;C8000 – EFFFF 160kb Rom Misc.
;F0000 – FFFFF 64Kb

; from NASM manual
;Multi-line macros are much more like the type of macro seen in MASM and TASM: a multi-line macro definition in NASM looks something like this.
;%macro prologue 1
; push ebp
; mov ebp,esp
; sub esp,%1
;%endmacro

; from http://www.husseinsspace.com/teaching/udw/1996/asmnotes/chaptwo.htm:
;The SHR/SLR instructions
;format:
;SHR destination,1
;SHR destination,CL
; SHL destination,1
; SHL destination,CL
;SHR shifts the destination right bitwise either 1 position or a number of positions determined by the current value of the CL register. SHL shifts the destination left bitwise either 1 position or a number of positions determined by the current value of the CL register. The vacant positions are filled by zeros.
;example:
;shr ax,1
; shl ax,1
;The first example effectively divides ax by 2 and the second example effectively multiplies ax by 2. These commands are faster than using DIV and MUL for arithmetic involving powers of 2.

;****************************
; from Intel Programmer’s Reference Manual
;10.1 Processor State After Reset
;The contents of EAX depend upon the results of the power-up self test. The self-test may be requested externally by assertion of BUSY# at the end of RESET. The EAX register holds zero if the 80386 passed the test. A nonzero value in EAX after self-test indicates that the particular 80386 unit is faulty. If the self-test is not requested, the contents of EAX after RESET is undefined.
;DX holds a component identifier and revision number after RESET as Figure 10-1 illustrates. DH contains 3, which indicates an 80386 component. DL contains a unique identifier of the revision level.
;Control register zero (CR0) contains the values shown in Figure 10-2 . The ET bit of CR0 is set if an 80387 is present in the configuration (according to the state of the ERROR# pin after RESET). If ET is reset, the configuration either contains an 80287 or does not contain a coprocessor. A software test is required to distinguish between these latter two possibilities.
;The remaining registers and flags are set as follows:
; EFLAGS =00000002H
; IP =0000FFF0H
; CS selector =000H
; DS selector =0000H
; ES selector =0000H
; SS selector =0000H
; FS selector =0000H
; GS selector =0000H
; IDTR:
; base =0
; limit =03FFH
;All registers not mentioned above are undefined.
;These settings imply that the processor begins in real-address mode with interrupts disabled.
;10.2 Software Initialization for Real-Address Mode
;In real-address mode a few structures must be initialized before a program can take advantage of all the features available in this mode.
;10.2.1 Stack
;No instructions that use the stack can be used until the stack-segment register (SS) has been loaded. SS must point to an area in RAM.
;10.2.2 Interrupt Table
;The initial state of the 80386 leaves interrupts disabled; however, the processor will still attempt to access the interrupt table if an exception or nonmaskable interrupt (NMI) occurs. Initialization software should take one of the following actions: • Change the limit value in the IDTR to zero. This will cause a shutdown if an exception or nonmaskable interrupt occurs. (Refer to the 80386 Hardware Reference Manual to see how shutdown is signalled externally.)
;• Put pointers to valid interrupt handlers in all positions of the interrupt table that might be used by exceptions or interrupts.
;• Change the IDTR to point to a valid interrupt table.
;
;10.2.3 First Instructions
;After RESET, address lines A{31-20} are automatically asserted for instruction fetches. This fact, together with the initial values of CS:IP, causes instruction execution to begin at physical address FFFFFFF0H. Near (intrasegment) forms of control transfer instructions may be used to pass control to other addresses in the upper 64K bytes of the address space. The first far (intersegment) JMP or CALL instruction causes A{31-20} to drop low, and the 80386 continues executing instructions in the lower one megabyte of physical memory. This automatic assertion of address lines A{31-20} allows systems designers to use a ROM at the high end of the address space to initialize the system.

; from http://en.wikipedia.org/wiki/Interrupt_descriptor_table
;In the 8086 processor, the IDT resides at a fixed location in memory from address 0x0000 to 0x03ff, and consists of 256 four-byte real mode pointers (256 × 4 = 1024 bytes of memory). In the 80286 and later, the size and locations of the IDT can be changed in the same way as it is done in protected mode, though it does not change the format of it. A real mode pointer is defined as a 16-bit segment address and a 16-bit offset into that segment. A segment address is expanded internally by the processor to 20 bits thus limiting real mode interrupt handlers to the first 1 megabyte of addressable memory. The first 32 vectors are reserved for the processor’s internal exceptions, and hardware interrupts may be mapped to any of the vectors by way of a programmable interrupt controller.
; A commonly used x86 real mode interrupt is INT 10, the Video BIOS code to handle primitive screen drawing functions such as pixel drawing and changing the screen resolution.

; from http://software.intel.com/en-us/articles/introduction-to-x64-assembly
; XOR EAX, EAX ; zero out eax
; MOV ECX, 10 ; loop 10 times
;Label: ; this is a label in assembly
; INX EAX ; increment eax
; LOOP Label ; decrement ECX, loop if not 0

; from https://courses.engr.illinois.edu/ece390/books/artofasm/CH06/CH06-5.html#HEADING5-294
; mov ecx, 255
;ArrayLp: mov Array[ecx], cl
; loop ArrayLp
; mov Array[0], 0
;The last instruction is necessary because the loop does not repeat when cx is zero. Therefore, the last element of the array that this loop processes is Array[1], hence the last instruction.
; The loop instruction does not affect any flags.

; 2.17.2014 chu’ Ha^n telephoned about obtaining literature on American Philosophy and on US Census Data particularly
; US Census Data on black population expansion into US and into the world …
; following day: couple resembling co^ Be^ and David Lowe seen at Post Office when we tried to mail chu’ Kha’s preserved fruit to father in Michigan
; from http://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/
; also see http://navet.ics.hawaii.edu/~casanova/courses/ics312_spring14/slides/ics312_bits_2.pdf
;Tabula rasa
;The x86 instruction set does not have a special purpose instruction for zeroing a register. An obvious way of dealing with this would be to move a constant zero into the register, like this:
;mov eax, 0
;That works, and it is fast. Benchmarking this will typically show that it has a latency of one Sandybridge diecycle – the result can be used in a subsequent instruction on the next cycle. Benchmarking will also show that this has a throughput of three-per-cycle. The Sandybridge documentation says that this is the maximum integer throughput possible, and yet we can do better.
;It’s too big
;The x86 instruction used to load a constant value such as zero into eax consists of a one-byte opcode (0xB8) and the constant to be loaded. The problem, in this scenario, is that eax is a 32-bit register, so the constant is 32-bits, so we end up with a five-byte instruction:
;B8 00 00 00 00 mov eax, 0
;Instruction size does not directly affect performance – you can create lots of benchmarks that will prove that it is harmless – but in most real programs the size of the code does have an effect on performance. The cost is extremely difficult to measure, but it appears that instruction-cache misses cost 10% or more of performance on many real programs. All else being equal, reducing instruction sizes will reduce i-cache misses, and therefore improve performance to some unknown degree.
;Smaller alternatives
;Many RISC architectures have a zero register in order to optimize this particular case, but x86 does not. The recommended alternative for years has been to use xor eax, eax. Any register exclusive ored with itself gives zero, and this instruction is just two bytes long:
;33 C0 xor eax, eax
;Careful micro-benchmarking will show that this instruction has the same one-cycle latency and three-per-cycle throughput of mov eax, 0 and it is 60% smaller (and recommended by Intel), so all is well.
;Suspicious minds
;If you really understand how CPUs work then you should be concerned with possible problems with using xor eax, eax to zero the eax register. One of the main limitations on CPU performance is data dependencies. While a Sandybridge processor can potentially execute three integer instructions on each cycle, in practice its performance tends to be lower because most instructions depend on the results of previous instructions, and are therefore serialized. The xor eax, eax instruction is at risk for such serialization because it uses eax as an input. Therefore it cannot (in theory) execute until the last instruction that wrote to eax completes. For example, consider this code fragment below:
;1: add eax, 1
;2: mov ebx, eax
;3: xor eax, eax
;4: add eax, ecx
;Careful micro-benchmarking will show that this instruction has the same one-cycle latency and three-per-cycle throughput of mov eax, 0 and it is 60% smaller (and recommended by Intel), so all is well.
;Ideally we would like our awesome out-of-order processor to execute instructions 1 and 3 in parallel. There is a literal data dependency between them, but a sufficiently advanced processor could detect that this dependency is artificial. The result of the xor instruction doesn’t depend on the value of eax, it will always be zero.
;It turns out that for x86 processors have for years handled xor of a register with itself specially. Every out-of-order Intel and AMD processor that I am aware of can detect that there is not really a data dependency and it can execute instructions 1 and 3 in parallel. Which is great. The CPUs use register renaming to ‘create’ a new eax for the sequence of instructions starting with instruction 3.

; from http://stackoverflow.com/questions/4909563/why-should-code-be-aligned-to-even-address-boundaries-on-x86
;Because the (16 bit) processor can fetch values from memory only at even addresses, due to its particular layout: it is divided in two “banks” of 1 byte each, so half of the data bus is connected to the first bank and the other half to the other bank. Now, suppose these banks are aligned (as in my picture), the processor can fetch values that are on the same “row”.
; bank 1 bank 2
;+——–+——–+
;| 8 bit | 8 bit |
;+——–+——–+
;| | |
;+——–+——–+
;| 4 | 5 | ;+——–+——–+
;| 2 | 3 |
;+——–+——–+
;| 0 | 1 |
;+——–+——–+
; \ / \ /
; | | | |
; | | | |
; data bus (to uP)

;Now, since this fetch limitation, if the cpu is forced to fetch values which are located on an odd address (suppose 3), it has to fetch values at 2 and 3, then values at 4 and 5, throw away values 2 and 5 then join 4 and 3 (you are talking about x86, which as a little endian memory layout).
; That’s why is better having code (and data!) on even addresses.
;PS: On 32 bit processors, code and data should be aligned on addresses which are divisible by 4 (since there are 4 banks).
;Hope I was clear. 🙂
;share|improve this answer
;answered Feb 5 ’11 at 23:02
;BlackBear
;9,42131746
;bio
;website google.it
;location Trento, Italy
;age 19

; from http://lemire.me/blog/archives/2012/05/31/data-alignment-for-speed-myth-or-reality/
;Conclusion: On recent Intel processors, data alignment does not make processing measurably faster. Data alignment for speed is a myth.
;Acknowledgement: I am grateful to Owen Kaser for pointing me to the references on this issue.
;http://lemire.me/blog/archives/2012/05/31/data-alignment-for-speed-myth-or-reality/

;\[ d E S F a s a d o \]

;11/9/99

;DOS: nasm -f bin -o your_file.com your_file.asm
;1)mov ax,your_segment
; mov ds,ax

;2) mov ax,[your_segment]
; mov ds,ax

;first of all you cant use mov ds,something… secondly you are trying to put
;in DS an offset of the current CS.

;The second example is what you have to do.

;hope this help..

;–
;[ yOu HaVe To SeArCh AnD sEaRcH, rElAtE iNfO, pRoBe AnD pRobE, tHeRe Is NoT
;aNoThEr WaY ]
;[ dOnT nEvEr gIvE uP, uSe YoR bRaIn At LeAsT aT 1o0% ]

;ASM CodER, PC HW & Electrical Technitian
;desf…@ciudad.com.ar
;http://members.xoom.com/desfasado >>> dENarixs OS Project
;UIN: 30796163

; from http://devdocs.inightmare.org/x86-assembly-changing-interrupt-vector-table/
;(x86 Assembly) Changing Interrupt Vector Table

;(This tutorial was originally written in 2004 and featured in http://asm.inightmare.org/)

;Another thing I want to write tutorial is about changing interrupts. There are two ways you can do that using DOS interrupts and modifying interrupt vector table directly. Both ways are pretty simple, you need to know these DOS interrupts (int 21h):

;Function

;What does it do?

;Parameters

;AH = 25h Set interrupt vector AL – interrupt number to change
; DS:DX – pointer to interrupt function
;AH = 35h Get interrupt vector. Gets address of currently set interrupt. AL – interrupt number
; Returns:
; ES:BX – pointer to interrupt
;AH = 4Ch Exits DOS program 😉 AL – exit code (not sure what it does)

;It’s pretty simple, just take a look at the sample code here.

;The other way to make your own interrupt is to modify interrupt vector table directly. It’s mapped from 0000:0000 to 0000:0400h in memory. The structure is very simple:

;Offset

;Segment

;Int 0

;(Offset 0000)

;(Offset 0002)

;Int 1

;(Offset 0004)

;(Offset 0006)

;Int 2

;(Offset 0008)

;(Offset 0010)

;…

;…

;So getting interrupt offset is:

;mov ax, [intnum*4]

;And segment:

;mov ax [intnum*4+2]

;Setting:

;mov ax, [intnum*4] ; offset
;mov ax [intnum*4+2] ; segment

;Well and how to call the interrupt, I think we all know:

;int intnum

;Everything is pretty simple. NASM source code:

;DOS interrupt version – here
; Direct modifiying of intvec table – here

; from http://asm.inightmare.org/ints_vec.asm
; org 100h

;xor ax, ax
;mov es, ax

; Save interrupt address so we can restore it later
;mov bx, [es:69h*4]
;mov [old_int_off], bx
;mov bx, [es:69h*4+2]
;mov [old_int_seg], bx

; modify interrupt vector table on 0x69th entry to point to our interrupt
;mov dx, int_prog
;mov [es:69h*4], dx
;mov ax, cs
;mov [es:69h*4+2], ax

;nop

;int 69h ; execute our interrupt

;restore old interrupt
;mov ax, [old_int_seg]
;mov [es:69h*4+2], ax
;mov dx, [old_int_off]
;mov [es:69h*4], dx

;mov ax, 0x4c00 ; Exit
;int 21h

; Our interrupt just prints some text 🙂
;int_prog:

;pusha ; save old registers, just incase 😉

;mov ah, 9
;mov dx, our_text
;int 21h

;popa

;iret

;our_text db “Bleh… $”

;old_int_seg dw 0
;old_int_off dw 0

; from http://www.ece.msstate.edu/~janem/ECE3724/S03/Presentations/number_io.ppt

;This is the html version of the file http://www.ece.msstate.edu/~janem/ECE3724/S03/Presentations/number_io.ppt.
;Google automatically generates html versions of documents as we crawl the web.

;1

;Ways to Handle I/O (Input/Ouput)

;•For Output
;◦Use Irvine16 Functions
;◾Writechar, WriteBin, WriteInt, Writehex, Writestring
;◦Use DOS (Int 21h) Functions – (Table C-2)
;◾2 – write char, 6 – write char, 9- write string (Table C-3)
;◦Use Video BIOS (Int 10h) Functions
;◾9 – write char and attribute, 0A- write char, …
;•For Input
;◦Use Irvine16 Functions
;◾Readchar, Readint, ReadHex, Readstring
;◦Use DOS (Int 21h) Functions (Table C-2)
;◾1 – read char, 6 – read char, 7- read char
;◦Use Keyboard BIOS (Int 16h) Functions (Table C-5)
;◾10 – wait for key

;2

;Input/Output of Numbers

;•A common task is to input or output numbers in ASCII format
;•Output tasks:
;◦Output an 8-bit value as ASCII string in HEX format
;◦Output an 8-bit value as a ASCII string in BINARY format (see ‘pbin.asm’ example on WWW page)
;◦Output an 8-bit value as ASCII string in DECIMAL format
;•Input tasks:
;◦Input a string representing an 8-bit number in Hex format
;◦Input a string representing an two digit decimal number (unsigned)
;

;3

;Output an 8 bit number in Hex Format

;•Two Hex characters in 8-bits. Want to work with each set of 4-bits individually.
;•Each Hex character represents 4-bits in a number.
;◦0000 = ‘0’ (ASCII code = 30h)
;◦0001 = ‘1’ (ASCII code = 31h)
;◦1001 = ‘9’ (ASCII code = 39h)
;◦……
;◦1010 = ‘A’ (ASCII code = 41h)
;◦1011 = ‘B’ (ASCII code = 42h)
;◦……
;◦1111 = ‘F’ (ASCII code) = 46h ).
;•If 4-bits is between 0-9, then ASCII = 30h + 4bits
;•If 4-bits is between A-F, then ASCII = 37h + 4bits

;4

;Output an 8 bit number in Hex Format

;Approach: Write a subroutine called ‘Out1Hex’. This will output the lower 4 bits of register ‘AL’ as an Hex digit to the screen.

;To output an 8-bit value, the main routine(out2hex) will call ‘Out1Hex’ twice 1) for the most significant HEX digit, and

; 2) for the least significant Hex digit.

; out2hex proc
;; output value in ‘al’ as 2 hex character
; push ax ; save al
; shr al, 4 ; get most sig. 4 bits into lower
; call Out1Hex ; print most sig. hex digit
; pop ax ; get back original al
; and al, 0x0Fh ; upper 4 bits = 0 – working with low 4 bits
; call Out1Hex ; print least sig. hex digit
; out2hex endp

;5

;Out1Hex

;Pseudo code for Out1Hex:
;
; if ( AL > 09H) jump to SKIP
; AL = AL + 30H
; Use Int21H, function2 to print character
; return
;
; skip: AL = AL + 37H
; Use Int21H, function2 to print character
; return
;
;
;6

; from Programmer’s Reference Manual
; CMP (Compare) subtracts the source operand from the destination operand. It updates OF, SF, ZF, AF, PF, and CF but does not alter the source and destination operands. A subsequent Jcc or SETcc instruction can test the appropriate flags.
; from

;Out1Hex
;A procedure to convert a 4-bit hex number to ASCII and print the character to the screen.

;Out1hex proc

;Cmp al, 9 ;is 4-bit value above 9?

;Ja ischar ;if so, must be a character

;Add al, 30h ;if not, add 30h for conversion

;Jmp printit ;go to print label

;Ischar: add al, 37h ;was character – add 37h for ;conversion

;Printit: mov dl, al ;printing a character to screen

;Mov ah,2 ;using int 21h, function 2.

;Int 21h

;Ret ;return to main procedure

;Out1hex endp

;End Main

;7

;Output a 16-bit hex number? 32 bits?

;•How would you print out a 16 bit value?
;◦Call Out1Hex 4 times.
;◦Each call would have to have the 4 bits in the lower four bits of AL
;◦You would have to start with the Most significant bits
;◦After saving the value, use shr instruction to get the correct bits.
;•How would you printout a 32-bit value?
;◦Call ‘Out1Hex’ 8 times – once for each 4 bits of the 32-bit value.

;8

;Output an 8-bit number in Decimal Format

;•How would you output a number in Decimal format?
;•Assume that AL contains a value between 0 and 99 and you want to print this out as a decimal value
;•The value of the first digit is ‘AL divided by 10’ (quotient value of AL/10).
;•The value of the 2nd digit is REMAINDER of AL divided by 10!!

;9

;Out1Dec
;A procedure to convert an 8-bit unsigned decimal number stored in AL to ASCII and print the character to the screen.

;Out1Dec Proc

;Push ax ;save value

;And ah, ah ;clear ah

;Div 10 ;divide value by 10 (quotient in AL, ;remainder in AH)

;Add al, 30h ;convert 10’s digit to ASCII

;Call printchar

;Mov al, ah ;get 1’s digit

;Add al, 30h ;convert to ASCII

;Call printchar

;Out1Dec Endp

;10

;Input an 8-bit number in HEX format

;•An 8-bit hex number will require two ASCII characters to represent it
;•Need to get 4-bit value of digit from ASCII character code
;•If ASCII is between 30H and 39H (‘0’ and ‘9’), then four-bit value is ASCII value – 30H.
;•If ASCII is between 41H and 46H (‘A’ and ‘F’), then four-bit value is ASCII value – 37H

;11

;Input an 8-bit number in HEX format

;Assume AX has the two ASCII digits that represent a HEX number

;Example: AX = 4335 h, AH = 43h = ‘C’, AL=35h = ‘5’.

;Want to convert this to AL = C5h.

; in2hex proc
; push ax ;; save AX
; mov al,ah ;; get most sig. char into AL
; call inhex ;; convert ASCII hex code in AL to 4 bit value
; mov bl, al ;; save in BL
; pop ax ;; get AX back
; call inhex ;; convert ASCII hex code in AL to 4-bit value
; shl bl,4 ;; shift bl to left to move lower 4bit to upper
; or al, bl ;; combine upper and lower bits, AL has value!
; ret
; in2hex endp

;12

;inhex Subroutine

;Want to convert the ASCII code in AL that is a HEX digit to its 4-bit value

; Pseudo code: if (AL > 39h) jump to skip
; AL = AL – 30h
; return

; skip: AL = AL – 37H
; return

;13

;Input an 8-bit number in Decimal format

;Assume AX has the two ASCII digits that represent a DECIMAL number

;Example: AX = 3731 h, AH = 38h = ‘7’, AL=31h = ‘1’.

;Want to convert this to AL = 71 (decimal) = 47h !!

; Approach:
; a. Convert the most significant ASCII digit to its four bit value.

; b. Multiply this by 10 and save.

; c. Convert the least significant ASCII digit to its four bit value and ADD it to the value produced in ‘b’!!

; 71 = 7 * 10 + 1 = 71 = 47 h.

Leave a comment