Straight and to the point I am stuck at a piece of my code. Within checkdigit proc I am trying to move the value that are at the corresponding memory locations into registers and using the [] to dereference the pointers. However, looking through this I am assuming you can not dereference a pointer twice, as I have pushed the addresses of these 'varaibles' on to the stack to use, and I guess creating another pointer. The current code will pass the memory locations of the values that I want to pass to the registers, however I am unable to dereference the memory location to get to my values. Any help would be appreciated even if it means I will not be able to use the current code. Thank you.
;valid pin program
include c:/Irvine/Irvine32.inc
NULL EQU 0
CR EQU 0dh
LF EQU 0ah
nextdigit EQU 4
range EQU 8[ebp]
digit EQU 12[ebp]
.data
outfile byte "resultpin.txt", NULL
infile byte "Pin.txt", NULL
input dword ?
output dword ?
buffer_size = 130
buffer byte buffer_size dup (?), NULL
bytesread dword ?
buffpos dword 0
pinpos dword 1
digitnum dword 0
range1 dword '4', '8', NULL
range2 dword '2', '5', NULL
range3 dword '5', '9', NULL
range4 dword '1', '4', NULL
range5 dword '3', '6', NULL
rangeray dword range1, range2, range3, range4, range5, NULL
rangepos dword 12
;memory offset is 60
equal byte "=", NULL
great byte "PIN is valid", NULL
fault1 byte "/Digit position ", NULL
fault2 byte "is invalid/", NULL
faultray dword fault1, fault2, NULL
;memory offset is 29
faultpos dword 17
gap byte LF, CR, NULL
.code
main proc
lea edx, outfile ;create & open output file
call CreateOutputFile
mov output, eax
lea esi,infile ;open input file linkage
call openfiles
lea esi,buffer
mov ecx,digitnum
call countdigits
call resetregs
lea esi,buffer
lea edi,rangeray
add edi,-60 ;start at beginning of rangeray
mov ecx,digitnum
L1:
push esi
push edi
push ebp
call checkdigit
pop ebp
pop edi
pop esi
loop L1
main endp
openfiles proc
lea edx,[esi] ;open file
call OpenInputFile
mov input, eax
mov eax, input ;read input
file
lea edx, buffer
mov ecx, buffer_size
call ReadFromFile
mov bytesRead, eax
ret
openfiles endp
countdigits proc
nextnumber:
mov dl,[esi] ;if not a
number bypass inc ecx
cmp dl,'0'
jl notdigit
cmp dl,'9'
jg notdigit
inc ecx
notdigit:
inc esi
mov dl,[esi]
cmp dl,NULL ;if esi
is NULL then return out of proc
jne nextnumber
mov digitnum,ecx
ret
countdigits endp
checkdigit proc
mov ebp,esp
mov ebx,digit
mov edx,range
cmp ebx,edx
jl notvalid
mov ebx,0
notvalid:
ret
checkdigit endp
resetregs proc
mov eax,0 ;reset
following registers
mov ebx,0
mov ecx,0
mov edx,0
mov dl,0
mov al,0
ret
resetregs endp
end main
Related
I'm trying to make a global variable by using sb-ext:defglobal (actually by using quicklisp library global-vars which wraps it and several other implementation-specific mechanics, but it does not matter). I'm having the following code in test.lisp file:
(sb-ext:defglobal *test* 42)
(defun test () *test*)
Then I call (compile-file "test.lisp") and (load "test"). After that, the test function disassembly looks like the following:
(disassemble #'test)
; disassembly for TEST
; Size: 27 bytes. Origin: #x52C519CC ; TEST
; CC: 498B4510 MOV RAX, [R13+16] ; thread.binding-stack-pointer
; D0: 488945F8 MOV [RBP-8], RAX
; D4: 488B05C5FFFFFF MOV RAX, [RIP-59] ; '*TEST*
; DB: 488B5001 MOV RDX, [RAX+1]
; DF: 488BE5 MOV RSP, RBP
; E2: F8 CLC
; E3: 5D POP RBP
; E4: C3 RET
; E5: CC10 INT3 16 ; Invalid argument count trap
But when I define the very same function in REPL and try to disassemble it, it looks like this:
(defun test2 () *test*)
(disassemble #'test2)
; disassembly for TEST2
; Size: 24 bytes. Origin: #x52C51A5C ; TEST2
; 5C: 498B4510 MOV RAX, [R13+16] ; thread.binding-stack-pointer
; 60: 488945F8 MOV [RBP-8], RAX
; 64: 488B142590EA3F50 MOV RDX, [#x503FEA90] ; *TEST*
; 6C: 488BE5 MOV RSP, RBP
; 6F: F8 CLC
; 70: 5D POP RBP
; 71: C3 RET
; 72: CC10 INT3 16 ; Invalid argument count trap
It is clear that when compiled from REPL, the code accessing the global variable is a bit more effective: it compiles to just one indirect MOV from some static address, while being compiled from the file, the access boils down to two indirect MOVs.
Supposedly I'm going to be using those global variables in some tight loops, so I'm concerned even about small scraps of effectiveness. So my question is, why the disassembly for the same function differs between REPL and .lisp file? Am I missing something obvious here?
Can some one explain the differences between the following two cases (specifically what the comments are saying if for me not understandable) which come from the CLHS on function:
;; This function assumes its callers have checked the types of the
;; arguments, and authorizes the compiler to build in that assumption.
(defun discriminant (a b c)
(declare (number a b c))
"Compute the discriminant for a quadratic equation."
(- (* b b) (* 4 a c))) => DISCRIMINANT
(discriminant 1 2/3 -2) => 76/9
;; This function assumes its callers have not checked the types of the
;; arguments, and performs explicit type checks before making any assumptions.
(defun careful-discriminant (a b c)
"Compute the discriminant for a quadratic equation."
(check-type a number)
(check-type b number)
(check-type c number)
(locally (declare (number a b c))
(- (* b b) (* 4 a c)))) => CAREFUL-DISCRIMINANT
(careful-discriminant 1 2/3 -2) => 76/9
I'm trying to learn some CL myself, so I'll provide the best answer I can. Common Lisp is a dynamic language as compared to a static language. For a static language, check out Haskell - it does a bunch of compile time checks to ensure types match up for all functions and lets you know if it fails. However, in Common Lisp, things are a little different.
However, in Common Lisp, variables aren't typed the way they are in
languages such as Java or C++. That is, you don't need to declare the
type of object that each variable can hold. Instead, a variable can
hold values of any type and the values carry type information that can
be used to check types at runtime. Thus, Common Lisp is dynamically
typed--type errors are detected dynamically. For instance, if you pass
something other than a number to the + function, Common Lisp will
signal a type error. On the other hand, Common Lisp is a strongly
typed language in the sense that all type errors will be
detected--there's no way to treat an object as an instance of a class
that it's not.
So the variables we declare as function arguments don't have a type by default. This may be a good read for you: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node15.html. In the first paragraph, it reads as follow:
It is important to note that in Lisp it is data objects that are
typed, not variables. Any variable can have any Lisp object as its
value. (It is possible to make an explicit declaration that a variable
will in fact take on one of only a limited set of values. However,
such a declaration may always be omitted, and the program will still
run correctly. Such a declaration merely constitutes advice from the
user that may be useful in gaining efficiency. See declare.)
So whenever you make your functions, those variables are using can have any Lisp object as it's value.
And if we take a venture at declare we see the following:
There are two distinct uses of declare , one is to declare Lisp
variables as "special" (this affects the semantics of the appropriate
bindings of the variables), and the other is to provide advice to help
the Common Lisp system (in reality the compiler) run your Lisp code
faster, or with more sophisticated debugging options.
Lastly, if we look at check-type we see:
check-type signals a correctable error of type type-error if the
contents of place are not of the type typespec.
In both cases for declare and check-type, we are giving the Common Lisp system advice on types and type checking. Let's look at the two example functions you provided.
First, the "discriminant" function uses the declare function to to assert that the arguments are indeed numbers and that the compiler doesn't need to check them. The careful-discriminant function uses check-type to ensure that each variable is indeed a number, and then performs the operation.
You may be asking "Why should I bother with that?", in which the answer is to provide either a more optimized function (discriminant) or a function that provides better debugging and more information on error (careful-discriminant). To show the difference, I fired up SBCL and defined both functions. Then, I used disassemble to show the machine code of each. Notice how careful-discriminant performs more checks than discriminant, leading to more machine code!
(disassemble #'discriminant)
; disassembly for DISCRIMINANT
; Size: 83 bytes. Origin: #x10023700D7 ; DISCRIMINANT
; 0D7: 498B5D10 MOV RBX, [R13+16] ; thread.binding-stack-pointer
; 0DB: 48895DF8 MOV [RBP-8], RBX
; 0DF: 840425F8FF1020 TEST AL, [#x2010FFF8] ; safepoint
; 0E6: 488B55E8 MOV RDX, [RBP-24]
; 0EA: 488B7DE8 MOV RDI, [RBP-24]
; 0EE: FF1425C0000020 CALL QWORD PTR [#x200000C0] ; GENERIC-*
; 0F5: 488955D8 MOV [RBP-40], RDX
; 0F9: 488B55F0 MOV RDX, [RBP-16]
; 0FD: BF08000000 MOV EDI, 8
; 102: FF1425C0000020 CALL QWORD PTR [#x200000C0] ; GENERIC-*
; 109: 488B7DE0 MOV RDI, [RBP-32]
; 10D: FF1425C0000020 CALL QWORD PTR [#x200000C0] ; GENERIC-*
; 114: 488BFA MOV RDI, RDX
; 117: 488B55D8 MOV RDX, [RBP-40]
; 11B: FF1425B8000020 CALL QWORD PTR [#x200000B8] ; GENERIC--
; 122: 488BE5 MOV RSP, RBP
; 125: F8 CLC
; 126: 5D POP RBP
; 127: C3 RET
; 128: CC10 INT3 16 ; Invalid argument count trap
NIL
(disassemble #'careful-discriminant)
; disassembly for CAREFUL-DISCRIMINANT
; Size: 422 bytes. Origin: #x10023701E3 ; CAREFUL-DISCRIMINANT
; 1E3: 4D8B4510 MOV R8, [R13+16] ; thread.binding-stack-pointer
; 1E7: 4C8945F8 MOV [RBP-8], R8
; 1EB: 840425F8FF1020 TEST AL, [#x2010FFF8] ; safepoint
; 1F2: EB44 JMP L1
; 1F4: 660F1F840000000000 NOP
; 1FD: 0F1F00 NOP
; 200: L0: 488B7DF0 MOV RDI, [RBP-16]
; 204: 4883EC10 SUB RSP, 16
; 208: 488B1571FFFFFF MOV RDX, [RIP-143] ; 'A
; 20F: 488B3572FFFFFF MOV RSI, [RIP-142] ; 'NUMBER
; 216: 4C894DD8 MOV [RBP-40], R9
; 21A: 488B056FFFFFFF MOV RAX, [RIP-145] ; #<SB-KERNEL:FDEFN SB-KERNEL:CHECK-TYPE-ERROR>
; 221: B906000000 MOV ECX, 6
; 226: 48892C24 MOV [RSP], RBP
; 22A: 488BEC MOV RBP, RSP
; 22D: FF5009 CALL QWORD PTR [RAX+9]
; 230: 4C8B4DD8 MOV R9, [RBP-40]
; 234: 488955F0 MOV [RBP-16], RDX
; 238: L1: 840425F8FF1020 TEST AL, [#x2010FFF8] ; safepoint
; 23F: 488B45F0 MOV RAX, [RBP-16]
; 243: 448D40F1 LEA R8D, [RAX-15]
; 247: 41F6C001 TEST R8B, 1
; 24B: 7512 JNE L2
; 24D: 4180F80A CMP R8B, 10
; 251: 740C JEQ L2
; 253: 41F6C00F TEST R8B, 15
; 257: 75A7 JNE L0
; 259: 8078F129 CMP BYTE PTR [RAX-15], 41
; 25D: 77A1 JNBE L0
; 25F: L2: EB47 JMP L4
; 261: 660F1F840000000000 NOP
; 26A: 660F1F440000 NOP
; 270: L3: 488B7DE8 MOV RDI, [RBP-24]
; 274: 4883EC10 SUB RSP, 16
; 278: 488B1519FFFFFF MOV RDX, [RIP-231] ; 'B
; 27F: 488B3502FFFFFF MOV RSI, [RIP-254] ; 'NUMBER
; 286: 4C894DD8 MOV [RBP-40], R9
; 28A: 488B05FFFEFFFF MOV RAX, [RIP-257] ; #<SB-KERNEL:FDEFN SB-KERNEL:CHECK-TYPE-ERROR>
; 291: B906000000 MOV ECX, 6
; 296: 48892C24 MOV [RSP], RBP
; 29A: 488BEC MOV RBP, RSP
; 29D: FF5009 CALL QWORD PTR [RAX+9]
; 2A0: 4C8B4DD8 MOV R9, [RBP-40]
; 2A4: 488955E8 MOV [RBP-24], RDX
; 2A8: L4: 840425F8FF1020 TEST AL, [#x2010FFF8] ; safepoint
; 2AF: 488B45E8 MOV RAX, [RBP-24]
; 2B3: 448D40F1 LEA R8D, [RAX-15]
; 2B7: 41F6C001 TEST R8B, 1
; 2BB: 7512 JNE L5
; 2BD: 4180F80A CMP R8B, 10
; 2C1: 740C JEQ L5
; 2C3: 41F6C00F TEST R8B, 15
; 2C7: 75A7 JNE L3
; 2C9: 8078F129 CMP BYTE PTR [RAX-15], 41
; 2CD: 77A1 JNBE L3
; 2CF: L5: EB3D JMP L7
; 2D1: 660F1F840000000000 NOP
; 2DA: 660F1F440000 NOP
; 2E0: L6: 498BF9 MOV RDI, R9
; 2E3: 4883EC10 SUB RSP, 16
; 2E7: 488B15B2FEFFFF MOV RDX, [RIP-334] ; 'C
; 2EE: 488B3593FEFFFF MOV RSI, [RIP-365] ; 'NUMBER
; 2F5: 488B0594FEFFFF MOV RAX, [RIP-364] ; #<SB-KERNEL:FDEFN SB-KERNEL:CHECK-TYPE-ERROR>
; 2FC: B906000000 MOV ECX, 6
; 301: 48892C24 MOV [RSP], RBP
; 305: 488BEC MOV RBP, RSP
; 308: FF5009 CALL QWORD PTR [RAX+9]
; 30B: 4C8BCA MOV R9, RDX
; 30E: L7: 840425F8FF1020 TEST AL, [#x2010FFF8] ; safepoint
; 315: 458D41F1 LEA R8D, [R9-15]
; 319: 41F6C001 TEST R8B, 1
; 31D: 7513 JNE L8
; 31F: 4180F80A CMP R8B, 10
; 323: 740D JEQ L8
; 325: 41F6C00F TEST R8B, 15
; 329: 75B5 JNE L6
; 32B: 418079F129 CMP BYTE PTR [R9-15], 41
; 330: 77AE JNBE L6
; 332: L8: 4C894DD8 MOV [RBP-40], R9
; 336: 488B55E8 MOV RDX, [RBP-24]
; 33A: 488B7DE8 MOV RDI, [RBP-24]
; 33E: FF1425C0000020 CALL QWORD PTR [#x200000C0] ; GENERIC-*
; 345: 488955E0 MOV [RBP-32], RDX
; 349: 4C8B4DD8 MOV R9, [RBP-40]
; 34D: 488B55F0 MOV RDX, [RBP-16]
; 351: BF08000000 MOV EDI, 8
; 356: FF1425C0000020 CALL QWORD PTR [#x200000C0] ; GENERIC-*
; 35D: 4C8B4DD8 MOV R9, [RBP-40]
; 361: 498BF9 MOV RDI, R9
; 364: FF1425C0000020 CALL QWORD PTR [#x200000C0] ; GENERIC-*
; 36B: 488BFA MOV RDI, RDX
; 36E: 4C8B4DD8 MOV R9, [RBP-40]
; 372: 488B55E0 MOV RDX, [RBP-32]
; 376: FF1425B8000020 CALL QWORD PTR [#x200000B8] ; GENERIC--
; 37D: 4C8B4DD8 MOV R9, [RBP-40]
; 381: 488BE5 MOV RSP, RBP
; 384: F8 CLC
; 385: 5D POP RBP
; 386: C3 RET
; 387: CC10 INT3 16 ; Invalid argument count trap
NIL
As seen here, Common Lisp can also be compiled, which confuses some people. It is better answered here: How is Lisp dynamic and compiled?.
The difference between the macro
check-type and type
declarations is that the former
cannot be ignored by the compiler (and, when the check fails, one can
interactively correct the inputs), while the latter are merely hints to
the compiler (and, much more importantly, to the readers of the code)
which may be ignored by the compiler.
A declaration affects what happens at compile time. A check-type form is a run time guard.
So, the declaration form says “hey compiler, the values held by the parameters a, b, c can only be numbers”. The check-type form says “hey function, at this point in execution, check that the given values are of the stated type”.
CHECK-TYPE : runtime type checking and repair
check-type does an actual runtime check. It also usually provides a way to interactively fix the value.
* (let ((a "1"))
(check-type a number)
(+ a 2))
debugger invoked on a SIMPLE-TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {10005184C3}>:
The value of A is "1", which is not of type NUMBER.
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [STORE-VALUE] Supply a new value for A.
1: [ABORT ] Exit debugger, returning to top level.
(SB-KERNEL:CHECK-TYPE-ERROR A "1" NUMBER NIL)
0] 0
Enter a form to be evaluated: 1
3
DECLARE : Type declaration
Common Lisp is dynamically typed: each data object has a type.
Common Lisp additionally allows one to use static types for variables and functions. There are
various types and compound types
ways to define new types with deftype
type declarations with declare
subtype checks with subtypep
runtime type checks with typep
runtime type conditional with typecase, ctypecase and etypecase
Now Common Lisp implementations use type declarations for various things and what they do with them is highly implementation specific.
The main usage of static type declarations with (declare (type ...)) in Common Lisp compilers is:
ignoring them. Typically Lisp interpreters and some compilers are completely ignoring them
use them for speed and space optimization. This is done by many compilers. They can use these type declarations to create specialized code.
use them for runtime type checks. Some implementations use type declarations for runtime checks.
use them for compile-time type checks. Some implementations are using type declarations for compile-time type checks. Examples are sbcl and cmucl.
Note that the Common Lisp standard does not say how these type declarations are used. It just provides a syntax to define and declare types. Common Lisp implementations then either make use of them or ignore them.
Especially sophisticated use of type declarations can be found with SBCL and CMUCL.
Example for type checks
Let's see how SBCL uses type declarations for both runtime and compile-time type checks:
Runtime type check with SBCL:
* (defun add (a b)
(declare (type number a b))
(list a b))
ADD
* (add 1 "3")
debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {10005184C3}>:
The value
"3"
is not of type
NUMBER
when binding B
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(ADD 1 "3") [external]
source: (SB-INT:NAMED-LAMBDA ADD
(A B)
(DECLARE (TYPE NUMBER A B))
(BLOCK ADD (LIST A B)))
0]
As we can see SBCL uses the type declaration for a runtime check. But different from check-type it does not offer to provide a different value and the corresponding re-check...
Compile-time type check with SBCL:
* (defun subtract (a b)
(declare (type number a b))
(concatenate 'string a "-" b " is " (- a b)))
; in: DEFUN SUBTRACT
; (CONCATENATE 'STRING A "-" B " is " (- A B))
;
; caught WARNING:
; Derived type of (SB-KERNEL:SYMEVAL 'A) is
; (VALUES NUMBER &OPTIONAL),
; conflicting with its asserted type
; SEQUENCE.
; See also:
; The SBCL Manual, Node "Handling of Types"
;
; compilation unit finished
; caught 1 WARNING condition
SUBTRACT
As you can see we are trying to use a number as a sequence. SBCL detects that at compile time and warns.
I have been trying to work out what the following recursive function does:
func4:
0x08048cfa <+0>: push edi
0x08048cfb <+1>: push esi
0x08048cfc <+2>: push ebx
0x08048cfd <+3>: mov ebx,DWORD PTR [esp+0x10] // First arg
0x08048d01 <+7>: mov edi,DWORD PTR [esp+0x14] // Second arg
0x08048d05 <+11>: test ebx,ebx // if (ebx == 0) { eax = 0; return ???;}
0x08048d07 <+13>: jle 0x8048d34 <func4+58>
0x08048d09 <+15>: mov eax,edi
0x08048d0b <+17>: cmp ebx,0x1 // if (ebx == 1) {return ???;}
0x08048d0e <+20>: je 0x8048d39 <func4+63>
0x08048d10 <+22>: sub esp,0x8
0x08048d13 <+25>: push edi
0x08048d14 <+26>: lea eax,[ebx-0x1]// eax = ebx-1
0x08048d17 <+29>: push eax
0x08048d18 <+30>: call 0x8048cfa <func4>
0x08048d1d <+35>: add esp,0x8 // esp += 8
0x08048d20 <+38>: lea esi,[edi+eax*1] // esi = edi + eax
0x08048d23 <+41>: push edi
0x08048d24 <+42>: sub ebx,0x2 // ebx -= 2
0x08048d27 <+45>: push ebx
0x08048d28 <+46>: call 0x8048cfa <func4>
0x08048d2d <+51>: add esp,0x10 // esp += 10
0x08048d30 <+54>: add eax,esi // eax += esi
0x08048d32 <+56>: jmp 0x8048d39 <func4+63>
0x08048d34 <+58>: mov eax,0x0 // eax = 0
0x08048d39 <+63>: pop ebx
0x08048d3a <+64>: pop esi
0x08048d3b <+65>: pop edi
0x08048d3c <+66>: ret
To date, I have figured out that it takes ebx, decrements it by one, passes it back to itself and recurses until it hits one of the base cases, then moves on to the next step of the recursion. However, I haven't fully understood what that branch of the recursion does, or what esp is doing in this context.
Any hints as to how to proceed? I have already stepped through it quite a few times with gdb, but have not really noticed any sort of pattern that would help me determine what is happening.
It seems that you don't know that the result is returned in the eax register. With that in mind the code is not difficult to understand. Assuming that the cdecl calling convention is used (because the stack is cleaned up by caller), it is same as this js function:
function func4(a, b)
{
if (a <= 0) return 0;
if (a == 1) return b;
return b + func4(a-1, b) + func4(a-2, b);
}
and is the asm code with comments
func4:
0x08048cfa <+0>: push edi ; save non-volatile registers
0x08048cfb <+1>: push esi
0x08048cfc <+2>: push ebx
0x08048cfd <+3>: mov ebx, [esp+0x10] ; ebx <- a
0x08048d01 <+7>: mov edi, [esp+0x14] ; edi <- b
0x08048d05 <+11>: test ebx, ebx ; if (a <= 0)
0x08048d07 <+13>: jle 0x8048d34 ; return 0
0x08048d09 <+15>: mov eax, edi ; result <- 0
0x08048d0b <+17>: cmp ebx, 0x1 ; if (a == 1)
0x08048d0e <+20>: je 0x8048d39 ; return result;
0x08048d10 <+22>: sub esp, 0x8 ; this is useless
0x08048d13 <+25>: push edi ; passing 2nd arguments
0x08048d14 <+26>: lea eax, [ebx-0x1] ;
0x08048d17 <+29>: push eax ; passing 1st arguments
0x08048d18 <+30>: call 0x8048cfa<func4> ; ax = func4(a - 1, b)
0x08048d1d <+35>: add esp, 0x8 ; clean up the stak after calling
0x08048d20 <+38>: lea esi, [edi+eax*1] ; temp = b + func4(a - 1, b)
0x08048d23 <+41>: push edi ; passing 2nd arguments
0x08048d24 <+42>: sub ebx, 0x2 ;
0x08048d27 <+45>: push ebx ; passing 1st arguments
0x08048d28 <+46>: call 0x8048cfa<func4> ; ax = func4(a - 2, b)
0x08048d2d <+51>: add esp, 0x10 ; clean up the stak and the useless 8 bytes
0x08048d30 <+54>: add eax, esi ; result = func4(a - 2, b) + temp
0x08048d32 <+56>: jmp 0x8048d39 ;
0x08048d34 <+58>: mov eax, 0x0 ; jump to here when a <= 0
0x08048d39 <+63>: pop ebx
0x08048d3a <+64>: pop esi
0x08048d3b <+65>: pop edi
0x08048d3c <+66>: ret
LEA is meant for calculating memory offsets, but it is widely used to doing fused multiplication and addition because it is quick and convenient. Two more advantages are: 1) you can assign the result to a register different from the source registers; 2) it doesn't affect the flags.
I want to put a new value into a pointer (in the value that he point of him) but I fail.
The program push to the stack
offset result, num1 and num2
The maximize need to be in result...
Now I need your help
org 100h
jmp main
result dw ?
num0 dw ?
num1 dw ?
main:
mov bp,sp
push (offset result)
push num0
push num1
call max
MOV AX,num0
call print_num
PRINTN
MOV AX,num1
call print_num
PRINTN
MOV AX,result
call print_num
PRINTN
ret
max PROC
push bp
mov bp,sp
MOV AX,[BP+6]
MOV BX,[BP+4]
MOV CX,[BP+2]
CMP AX,BX
JGE BIG
MOV [CX],BX
BIG:
MOV [CX],AX
mov sp,bp
pop bp
ret
max ENDP
include magshimim.inc \\our program built for us defines for the inputs...
I want to do:
MOV [CX] , AX
but the emu8086 doesn't really like me :)
Thanks for your help!
Problems found:
"push (offset result)" seems to store the wrong value because of the parenthesis.
As soon as procedure "max" begins, BP is pushed on stack, so the parameters are no longer in BP+6, +4 and +2, they are in BP+8, +6 and +4.
"[CX]" can't be used as pointer, let's change it by BX.
It is necessary to skip label "BIG" in case CX is greater.
Here is the code with the little fixes :
org 100h
jmp main
result dw ?
num0 dw 5 ;VALUE TO TEST.
num1 dw 2 ;VALUE TO TEST.
main:
mov bp,sp
push offset result ;PARENTHESIS SEEM TO STORE THE WRONG VALUE.
push num0
push num1
call max
MOV AX,num0
call print_num
PRINTN
MOV AX,num1
call print_num
PRINTN
MOV AX,result
call print_num
PRINTN
ret
max PROC
push bp ;"BP" IN STACK. PARAMTERS ARE NO LONGER IN BP+6,+4,+2.
mov bp,sp
MOV BX,[BP+8] ;BX = RESULT'S ADDRESS.
MOV AX,[BP+6] ;AX = NUM0'S VALUE.
MOV CX,[BP+4] ;CX = NUM1'S VALUE.
CMP AX,CX
JGE BIG
MOV [BX],CX
jmp finish ;NECESSARY TO SKIP "BIG".
BIG:
MOV [BX],AX
; mov sp,bp
finish:
pop bp
ret
max ENDP
I am new to programming in asm. I am trying to create a floppy that prints a message on the boot sector, jumps to sector 35 and prints the date, then jumps back to the boot sector and prints a prompt. I am having trouble (I think) jumping between the sectors... I had everything printing fine when it was all on the boot sector and I haven't changed the actual printing code. What I am getting currently is the first line of the message and then the date and prompt never print. The code is below; I am using NASM:
For the boot sector:
org 0x7c00 ;load to appropariate MBR location
start:
call cls ;call routine to clear screen
call dspmsg ;call routine to display message
mov ah,02h ;read disk sectors into memory
mov al,1 ;number of sectors to read/write (must be nonzero)
mov ch,1 ;cylinder number (0...79)
mov cl,18 ;sector number (1...18)
mov dh,0 ;head number (0...1)
mov dl,0 ;drive number (0...3, 0 for floppy)
mov bx, 0x1000
mov es,bx
mov bx,0x0000
int 13h
call word 0x1000:0x0000
push cs
pop ds
call dspmsg2
jmp $
%macro dsp 3
mov ah,13h ;function 13h (Display String)
mov al,1 ;Write mode is one
mov bh,0 ;Use video page of zero
mov bl,0AH ;Attribute
mov cx,%1 ;Character string length
mov dh,%2 ;position on row
mov dl,0 ;and column 28
push ds ;put ds register on stack
pop es ;pop it into es register
lea bp,%3 ;load the offset address of string into BP
int 10H
%endmacro
cls:
mov ah,06h ;function 06h (Scroll Screen)
mov al,0 ;scroll all lines
mov bh,0AH ;Attribute (light green on black)
mov ch,0 ;Upper left row is zero
mov cl,0 ;Upper left column is zero
mov dh,24 ;Lower left row is 24
mov dl,79 ;Lower left column is 79
int 10H ;BIOS Interrupt 10h (video services)
ret
msg: db 'OS321, made by CHRISTINE MCGINN (c) 2011'
dspmsg:
dsp 40,0,[msg]
ret
msg2: db '$'
dspmsg2:
;Display a message
dsp 1,2,[msg2]
ret
times 510-($-$$) db 0 ;Pad remainder of boot sector with 0s
dw 0xAA55 ;done setting the MBR
Then on sector 35:
org 0x0000
push cs
pop ds
call date
call cvtmo
call cvtday
call cvtcent
call cvtyear
call time
call cvthrs
call cvtmin
call cvtsec
call dsptimedate
retf
%macro dsp 3
mov ah,13h ;function 13h (Display String)
mov al,1 ;Write mode is one
mov bh,0 ;Use video page of zero
mov bl,0AH ;Attribute
mov cx,%1 ;Character string length
mov dh,%2 ;position on row
mov dl,0 ;and column 28
push ds ;put ds register on stack
pop es ;pop it into es register
lea bp,%3 ;load the offset address of string into BP
int 10H
%endmacro
%macro cvt 3
mov bh,%1 ;copy contents of %1 to bh
shr bh,1
shr bh,1
shr bh,1
shr bh,1
add bh,30h ;add 30h to convert to ascii
mov [tmdtfld + %2],bh
mov bh,%1
and bh,0fh
add bh,30h
mov [tmdtfld + %3],bh
%endmacro
date:
;Get date from the system
mov ah,04h ;function 04h (get RTC date)
int 1Ah ;BIOS Interrupt 1Ah (Read Real Time Clock)
ret
;CH - Century
;CL - Year
;DH - Month
;DL - Day
cvtmo:
;Converts the system date from BCD to ASCII
cvt dh,9,10
ret
cvtday:
cvt dl,12,13
ret
cvtcent:
cvt ch,15,16
ret
cvtyear:
cvt cl,17,18
ret
time:
;Get time from the system
mov ah,02h
int 1Ah
ret
;CH - Hours
;CL - Minutes
;DH - Seconds
cvthrs:
;Converts the system time from BCD to ASCII
cvt ch,0,1
ret
cvtmin:
cvt cl,3,4
ret
cvtsec:
cvt dh,6,7
ret
tmdtfld: db '00:00:00 00/00/0000'
dsptimedate:
;Display the system time
dsp 19,1,[tmdtfld]
ret
times 512-($-$$) db 0 ;Pad remainder of sector with 0s
Thank you for any help you can offer!
You've pose the question in a confusing way. You seem to have two problems:
1) Reading arbitrary sectors from the floppy
2) Having programs in each sector that do something (e.g, print a string)
I'd organize my program as a floppy driver ("call floppy_read_sector(x)") [which might
use the bios to do most of the dirty work, but that's an implementation detail),
and as as set of seperate position-independent code blocks that did the various tasks
as subroutines.
Your boot sector code should contain the floppy driver and the high level logic
to read sector(n), call the subroutine in the buffer you read the sector into,
and then do the next sector. (You don't have a lot of room so I don't know
if you can squeeze all this into the boot sector. Welcome to assembly language
programming where counting bytes is important).
Then you have to organize the construction of the floppy disk somehow.
Usually in a world in which one creates bootable floppies, you are allowed to build
much more complicated programs to fill them up. Exercise left to the reader.