I think the quality of my videos is improving with time, as of now I'm particularly proud of this one, but who knows what I'll think in a couple of years :)
Thanks for the lessons! This is loads of fun! My solution for #1 (more than necessary but needed some printing functions) is below. #2 is still pending. ;;; Function to print integers in base 10 ;;; - load 16-bit integer into dx ;;; - break into digits ;;; + divide by 10 ;;; + push remainder (al) ;;; + if quotient (ah) == 0, done; else loop ;;; - print number ;;; + pop ;;; + add to '0' (32?) ;;; + print digit ;;; + if stack empty, done; else loop ;;; - Exit ;; 512 byes long ending in 0x55 0xaa [bits 16] ; use 16 bits [org 0x7c00] ; sets the start address ;;; Four NOPs just so I can locate my code in RAM. Not needed to work. nop nop nop nop ;; initialize stack mov bp, 0x8000 mov sp, bp ;; set bx to the number to print mov ax, 0xffff ;; call the print_number function call print_number ;; done, so infinite loop jmp $ ; end prog ;;; the number to print is stored in ax print_number: xor cx, cx ; set up counter for number of digits mov bx, 0x0a ; prepare divisor cvt_number_loop: xor dx, dx ; clear dx to hopefully prep for dx:ax / bx div bx ; divide ax by 10; q = al, r = ah ;; ax = quotient ; dx = remainder ;; why no workie for 0x8000-0xffff? why is it determined to be SIGNED?!? push dx ; push the remainder onto the stack inc cx ; increment the digit counter test ax, ax ; see if quotient is zero jz cvt_number_done ; if so, exit number conversion loop jmp cvt_number_loop ; next loop iteration cvt_number_done: xor ax, ax ; clear out ax print_digits: pop bx ; pop the high-order digit off the stack dec cx ; decrement the digit counter mov al, bl ; put it into al to print add al, '0' ; renormalize to '0' to print ASCII digit push cx ; save counter from being clobbered call __print pop cx ; restore saved counter test cx, cx ; see if counter is zero jz done_printing ; if so, exit print loop jmp print_digits ; if not, print next digit done_printing: call __print_crlf ; print CRLF after last digit ;; done printing the number so return ret __print_crlf: pusha mov al, 0x0a call __print mov al, 0x0d call __print popa ret ;;; al needs to have the character in it to print __print: pusha mov ah, 0x0e int 0x10 popa ret ;;; quick and dirty debug print function; prints one character ;;; that is stored in dl __debug_print: pusha mov al, dl call __print call __print_crlf popa ret times 510-($-$$) db 0 db 0x55, 0xaa
There... Has to be an easier way...? I'm thoroughly stumped on finding any solution, so if this works then kudos. I'll copy paste it when I get home. But your process has provided good leads (thank you) so I'll ponder some more until then. Edit: your pseudo code at the top is very concise. Maybe there is no easier way. Oh I see, you said it was more than necessary👌
your a god send you know that right? your practically the only tutorial on how to make operating systems on yt and practically the internet, and you perfectly explained everything. i cant express my thanks I have for you.
This is very useful for me to learn some assembly! :) PS: I love the reference to "Tom disintegrates at the thought of xnopyt", if that's what it is lol
Here is a process to print all of your registers in decimal, to help debug. To keep the file smaller it uses one general label instead of separate labels for each register. If you like it and find a way to make it smaller please share! I have left out the comments for your viewing pleasure. (having this process would have made writing this process easier). __printAllReg: pusha mov ah, 0x0e mov bx, labelRegs printLabels: mov al, [bx] cmp al, 0 je zeroESI int 0x10 inc bx jmp printLabels zeroESI: xor esi, esi popReg: inc esi cmp esi, 9 je end pop ax __printInt: mov bx, 10 xor cx, cx stackIntDigits: xor dx, dx div bx add dx, 48 push dx inc cx cmp ax, 0 jne stackIntDigits printStack: pop ax mov ah, 0x0e int 0x10 dec cx cmp cx, 0 jne printStack printSpaceThenLoop: mov al, 32 int 0x10 jmp popReg end: jmp $ labelRegs: db 10, 13, "DI: SI: BP: SP: BX: DX: CX: AX:", 10, 13, 0
After a couple hours of debugging, I finally have a fixed version of problem 2. Reading through these comments, it seems like a lot of people struggled with this problem-the reason I did, and likely many others, is that the stack takes 16 bit values, as we're in 16 bit mode, but we read and store characters as individual bytes (8 bits). This manifests in a couple of places. In the readstr function, we take input into al, but push ax, as we need to push a 16 bit value. This means that, if I receive the character 'a', what the stack will actually receive is 0x0e, 'a'. This is because ah and al are simply the high and low parts of ax, so when we push ax, we push push ah & al. ah is set to 0x0e from turning on teletype mode, and remains that way throughout basically the whole program. The issue this created is that when I tried to pop off of the stack directly into the buffer, it wouldn't just write the individual byte that came from al, but also the value 0x0e. The way I circumvented this is by first popping into ax, which is necessary, as we are popping a 16 bit value, so we must use a 16 bit register. Then I do `mov [bx], al`, which moves just the lower half of ax into the buffer, circumventing the issue. There may be a way to do this without using ax as an intermediate buffer, but I don't know it. If part of this is unclear feel free to ask further questions-code below: [org 0x7c00] bits 16 jmp main ; ; Print a string ; - bx: address of string ; puts: mov al, [bx] cmp al, 0 je exit mov ah, 0x0e ; display character in tty mode int 0x10 ; - read from al inc bx jmp puts exit: ret ; ; Read a string into `buffer` ; readstr: mov bx, buffer jmp readchar readchar: mov ah, 0 int 0x16 cmp al, 0x0D ; check for carriage return je storestr push ax inc bx jmp readchar storestr: dec bx pop ax mov [bx], al cmp bx, buffer jle exit jmp storestr main: call readstr mov bx, buffer call puts jmp $ str: db "hello world", 0 ; not used in this program buffer: times 64 db 0 times 510-($-$$) db 0 db 0x55, 0xAA
For the second assignment (homework), this is what I managed to do, [org 0x7c00] mov bp, 0x8000 mov sp, bp inputLabel: mov ax, 0 int 0x16 mov bh, al push bx cmp al, 13 je print jmp inputLabel print: pop bx mov ah, 0x0e mov al, bh int 0x10 cmp bp, sp je exit jmp print exit: jmp $ times 510-($-$$) db 0 db 0x55, 0xaa
I was stuck. Your solution unstuck me by showing me how pop avoids printing the scan codes. Thank you. Instead of popping I found success printing forwards with: sub bp, 2 mov al, [bp]
On 1:18, do I understand right, you say that the real address of base and top of a stack are SS:BP and SS:SP respectively? Or I misunderstood, as I expected SS is the one pointing on the Stack segment beginning and SP is an offset of the current (top) data. Why do we need to set BP, if Push command does not use it?
We're only offsetting the data since in that example we're only accessing data; we're not performing any jumps. If we were to perform a jump, we should also offset the code
>>2:22 Cring on amd64 C Calling Conventions :( Push 4 or 6 arguments into registers, and if you need more, push to the stack. It annoys, but it's fast.
I have very much simplified the part about functions, I'm considering only functions with no arguments. I'll talk about functions with arguments briefly in a future video
[org 0x7c00] equals to "mov ds, 0x7c0" in real mode, where we have (our) MBR loaded to this real memory address 0x7c00 (and we know it from 8088 CPU documentation)
Paging and segmentation are independent ways to access memory. The relevant difference in this case is that paging is managed by the OS, whereas segmentation must be managed at compile time. In our community OS we have implemented paging, hence we do not use those registers anymore.
There is a way to connect qemu to GDB over a local network. I've done it a few times a few years ago, I'm not quite sure how to do it now. What have you found so far?
Print Number function printnumber: mov ah, 0x0e mov bx, 0 mov al, 48 print: cmp bx, 10 je exit int 0x10 inc bx inc al jmp print exit: ret jmp $ ; infinite loop to hold screen times 510 - ($-$$) db 0 db 0x55, 0xaa
A lot of important details left out about segmentation and how procedure calls utilize the stack. As result, not explaining the concepts as they should be.
Did anyone notice xnopyt at 2:30 [ ruclips.net/video/-ORLmYVztCM/видео.html ] This series is amazing, although i watched it 3 times to actually be able to do the homework you gave
Mostly university, but also the OSDev wiki. You should have a pretty solid understanding of C programming before approaching the subject from that point of view though
my solution to challenge 2, to read, print, then echo user input using the stack. does not use pop but rather reallocates the stack pointer manually after printing. is there a better way to do it with pop? [org 0x7c00] ;offset for memory addresses section .text global _start _start: mov ah, 0x0e ;selects BIOS operation (14) to perform on interupt 0x10 mov bx, msg ;the variable name serves as pointer start of string, [] derefs call print call new_line jmp read print: mov al, [bx] cmp al, 0 je return int 0x10 inc bx jmp print read: mov bx, sp ; copy the stack pointer (start of string) to bx sub bx, 2 ; account for the first char we push read_loop: mov ah, 0x00 int 0x16 ; read keyboard for char cmp al, 0x0D ; check for enter key je process ; echo if enter push ax ; push the current char to stack mov ah, 0x0e ; op14 for interupt 10 int 0x10 ; interupt 10, prints char jmp read_loop new_line: ; prints new line and moves cursor to start mov ah, 0x0e mov al, 0x0d ; carriage return (to beginning of line) int 0x10 mov al, 0x0a ; line feed (to next line) int 0x10 ret process: call new_line ; print input pop_loop: push 0 ; mark end of string at top of stack push bx ; the data at sp will be address of start of the string mov al, [bx] cmp al, 0 je process_done mov ah, 0x0e ;selects BIOS operation (14) print char to perform on interupt 0x10 int 0x10 ; video BIOS interupt (write char) sub bx, 2 ; move towards the top of the stack by one char jmp pop_loop process_done: pop ax ; get string start mov sp, ax ; resest sp to string start call new_line jmp read msg: db "type and press enter to echo", 0 return: ret times 510-($-$$) db 0 ;$ - current address, $$ - sector address db 0x55, 0xAA
Tried both challenges. 1) Works fine but something weird happens with 2) [org 0x7c00] jmp main ; 1) print: ;;; Initialization mov cx, 0 ; Inits counter. mov bx, 10 ; Sets divisor to 10. ;;; Convertion to decimal. print_convertNextNum: inc cl ; Adds the number to the count. mov dx, 0 div bx ; ax//bl -> ax AND ax%bl -> dx. push dx ; Saves number in stack. cmp al, 0 jne print_convertNextNum ;;; Writing the digits. print_writeNextNum: pop dx ; Gets next number. mov al, dl add al, 48 ; Changes the number to the right ASCII char. mov ah, 0x0e ; Prints the number int 0x10 dec cl ; Decreases counter. jnz print_writeNextNum ret ; 2) ; Doesn't work for some reason stackString: ;;; Initialization. mov cx, 0 ; cx will count the characters in string. mov ax, 0 ; ax will get the char values one by one. mov dx, 0 ; dx will be used to catch when [Enter] is pressed. ;;; Reading. stackString_readNextChar: xor ah, ah ; Reset ah. int 0x16 ; Asks for next char. push ax ; Push keypress to the stack. inc cx cmp ah, 0x1C ; Quits loop when [Enter] is pressed. jne stackString_readNextChar times 2 add sp, cx ; Goes back to the start of the string. (cx cells = 2cx bytes) sub sp, 2 ;;; Writing. stackString_writeNextChar: xor ax, ax pop ax ; Gets the key pressed. mov ah, 0x0e int 0x10 sub sp, 4 ; Goes to next char. (two cells = 4 bytes) dec cx jnz stackString_writeNextChar times 2 add sp, cx ; Goes back to the start of the string. add sp, 2 ; Goes back to the top of the stack. ret main: ; Stack init. mov bp, 0x8000 mov sp, bp mov ax, 2015 call stackString ; Generates the boot section. jmp $ times 510-($-$$) db 0 db 0x55, 0xaa
I feel useless here. I am for the love of life cannot seem to figure out the homework. I want to do the echo input. I already have the input loop, but the closest thing I got by relying on the comment section is echoing the input in reverse order.
That's a start! You somehow have to find a way to store the input in memory and keep a pointer to its beginning. Then you should be able to print it :)
Hello, I am trying to combine the keyboard input with the stack and was testing following code: mov bp, 0x8000 mov sp, bp call addVal call printVal jmp $ ; Jumping to current memory section to start the booting times 510-($-$$) db 0 ; Defining 512 bytes in total with regard of this code db 0x55, 0xaa ; last two bytes which mark this file as the bootloader! addVal: mov bh, 'A' push bx ret printVal: pop bx mov ah, 0x0e mov al, bh int 0x10 ret Shouldn´t this work in the way that both of the functions get called after each other and then "booting" or am I missing something (nothing gets printed)?
In the video, he said that stack is automatically handled when you create function with call and ret. I'm trying something and I send you my soluce if it works.
call and ret use pusha and popa. This means that call addVal will push all 16-bit registers onto the stack. You then set bh to A and push it onto the stack. After that you use ret which has the effects of popa. At this point you still have bh on top of all the other registers which results in those values being mixed up. Remember that you can only put and take stuff on/from the top of the stack This would work if you were to use jmp instead of call because jmp does not touch the stack. Hope i could help you
thanks for all i make keyboard listenner its read and write real time my first asm code! listenKeyboard: mov al, 0 mov ah, 0 int 0x16 mov ah, 14 int 0x10 jmp listenKeyboard
Solution to No.2: org 0x7c00 mov bp, 0x8000 mov sp, bp mov bh, '~' push bx jmp decimalNum decimalNum: mov ah, 0 int 0x16 mov bh, al push bx cmp al, '/' je printSpace continueFromSpace: cmp al, '/' je printOut mov ah, 0x0e int 0x10 jmp decimalNum printSpace: mov ah, 0x0e mov al, 32; int 0x10 mov al, '/' jmp continueFromSpace printOut: mov ah, 0x0e mov al, bh cmp al, '/' jne output jmp continue continue: cmp al, '~' je exit pop bx jmp printOut output: cmp al, '~' je continue int 0x10 jmp continue exit: jmp $ times 510-($-$$) db 0 db 0x55, 0xaa It prints in reverse tho so a perfect solution for direct print may be to push it into another register and then pop that into output?
@@DaedalusCommunity cant wait for the next video, also do you have a link for the solution for the getting keyboard input to string thing? would be helpful, since im learning assembly with this course
@@DaedalusCommunity the solution could be to push the string you type into the stack, then clear the screen somehow then pop it? i dont know much about assembly so im just kinda figuring things out
I did my homework 1) [org 0x7c00] ; Init Stack mov bp, 0x8000 mov sp, bp ; Prints number mov ax, 0x1A4 ; 420 call print_num call print_nl ; Prints number mov ax, 0x18 ; 24 call print_num call print_nl ; Prints number mov ax, 0x1E ; 30 call print_num call print_nl ; Prints number mov ax, 0x5 ; 5 call print_num call print_nl ; Prints number mov ax, 0x1D ; 29 call print_num call print_nl ; Forever Loop jmp $ print_num: ; Print a number given in ax pusha ; Stores all registers in the stack mov cx, 0 ; Counter for the digits mov bx, 0x0a ; Divisor set to 10 pn_loop: mov dx, 0 div bx ; Division -> ax / bx -> ax ; ax % bx -> dx push dx inc cx cmp ax, 0 ; jmps out of the loop if the quotient is zero je pn_print jmp pn_loop pn_print: ; Print setup mov al, 0x0 mov ah, 0x0e pn_print_loop: ; Prints numbers dec cx pop bx add bx, '0' ; Converts the Digit to a Assci number digit mov al, bl int 0x10 cmp cx, 0 ; jumps out of the loop if the digit counter is zero je pn_exit jmp pn_print_loop pn_exit: popa ; Pops the stored registers from the stack ret ; Returns to the point in the program where this function was called print_nl: ; Jumps int the next line on screen pusha mov ah, 0x0e mov al, 0xd int 0x10 mov al, 0xa int 0x10 popa ret ; Magic times 510 - ($ - $$) db 0 db 0x55, 0xaa
Hello, I've tried using call and ret instead of pusha and popa, however it wont work for me without doing it myself . CODE /// [org 0x7c00] mov al, 'A' mov ah, 0x0e int 0x10 ; Prints A pusha ;
I know most of this stuff already but goddamn these videos are good! Informative and personal, friendly and easily understandable.
Keep going!
I think the quality of my videos is improving with time, as of now I'm particularly proud of this one, but who knows what I'll think in a couple of years :)
can you please tell me where you learned all this stuff? I'm trying to learn it myself.
@@Epic-so3ek it was revealed to him in a dream
Thanks for the lessons! This is loads of fun!
My solution for #1 (more than necessary but needed some printing functions) is below. #2 is still pending.
;;; Function to print integers in base 10
;;; - load 16-bit integer into dx
;;; - break into digits
;;; + divide by 10
;;; + push remainder (al)
;;; + if quotient (ah) == 0, done; else loop
;;; - print number
;;; + pop
;;; + add to '0' (32?)
;;; + print digit
;;; + if stack empty, done; else loop
;;; - Exit
;; 512 byes long ending in 0x55 0xaa
[bits 16] ; use 16 bits
[org 0x7c00] ; sets the start address
;;; Four NOPs just so I can locate my code in RAM. Not needed to work.
nop
nop
nop
nop
;; initialize stack
mov bp, 0x8000
mov sp, bp
;; set bx to the number to print
mov ax, 0xffff
;; call the print_number function
call print_number
;; done, so infinite loop
jmp $ ; end prog
;;; the number to print is stored in ax
print_number:
xor cx, cx ; set up counter for number of digits
mov bx, 0x0a ; prepare divisor
cvt_number_loop:
xor dx, dx ; clear dx to hopefully prep for dx:ax / bx
div bx ; divide ax by 10; q = al, r = ah
;; ax = quotient ; dx = remainder
;; why no workie for 0x8000-0xffff? why is it determined to be SIGNED?!?
push dx ; push the remainder onto the stack
inc cx ; increment the digit counter
test ax, ax ; see if quotient is zero
jz cvt_number_done ; if so, exit number conversion loop
jmp cvt_number_loop ; next loop iteration
cvt_number_done:
xor ax, ax ; clear out ax
print_digits:
pop bx ; pop the high-order digit off the stack
dec cx ; decrement the digit counter
mov al, bl ; put it into al to print
add al, '0' ; renormalize to '0' to print ASCII digit
push cx ; save counter from being clobbered
call __print
pop cx ; restore saved counter
test cx, cx ; see if counter is zero
jz done_printing ; if so, exit print loop
jmp print_digits ; if not, print next digit
done_printing:
call __print_crlf ; print CRLF after last digit
;; done printing the number so return
ret
__print_crlf:
pusha
mov al, 0x0a
call __print
mov al, 0x0d
call __print
popa
ret
;;; al needs to have the character in it to print
__print:
pusha
mov ah, 0x0e
int 0x10
popa
ret
;;; quick and dirty debug print function; prints one character
;;; that is stored in dl
__debug_print:
pusha
mov al, dl
call __print
call __print_crlf
popa
ret
times 510-($-$$) db 0
db 0x55, 0xaa
There... Has to be an easier way...? I'm thoroughly stumped on finding any solution, so if this works then kudos. I'll copy paste it when I get home. But your process has provided good leads (thank you) so I'll ponder some more until then.
Edit: your pseudo code at the top is very concise. Maybe there is no easier way. Oh I see, you said it was more than necessary👌
i like so much your videos because they aren't long and are good explained. Congrats and keep going!
your a god send you know that right? your practically the only tutorial on how to make operating systems on yt and practically the internet, and you perfectly explained everything. i cant express my thanks I have for you.
Thanks! There are a lot of other useful resources! You'll find some of them in the github link in the description :)
2:33 I appreciate this reference.
Great visuals, really helped me understand segmentation
This is very useful for me to learn some assembly! :)
PS: I love the reference to "Tom disintegrates at the thought of xnopyt", if that's what it is lol
Here is a process to print all of your registers in decimal, to help debug. To keep the file smaller it uses one general label instead of separate labels for each register. If you like it and find a way to make it smaller please share! I have left out the comments for your viewing pleasure. (having this process would have made writing this process easier).
__printAllReg:
pusha
mov ah, 0x0e
mov bx, labelRegs
printLabels:
mov al, [bx]
cmp al, 0
je zeroESI
int 0x10
inc bx
jmp printLabels
zeroESI:
xor esi, esi
popReg:
inc esi
cmp esi, 9
je end
pop ax
__printInt:
mov bx, 10
xor cx, cx
stackIntDigits:
xor dx, dx
div bx
add dx, 48
push dx
inc cx
cmp ax, 0
jne stackIntDigits
printStack:
pop ax
mov ah, 0x0e
int 0x10
dec cx
cmp cx, 0
jne printStack
printSpaceThenLoop:
mov al, 32
int 0x10
jmp popReg
end:
jmp $
labelRegs:
db 10, 13, "DI: SI: BP: SP: BX: DX: CX: AX:", 10, 13, 0
This is brilliant! Thank you!
Thanks to your video, i can know what 'DS', 'CS', 'SS', 'ES' mean exactly. Great!
2:31
void xnopyt() {
print("ah")
return;
}
Hahahaha
function disintegrates at the thought of xnopyt
@@asp-uwu Why is this so funny?
Commenting for the algorithm...
Bro, This series is so so great!!!!!!!!!!
5:18 You rickrolled us and no one noticed
Glad you noticed! Not ever shalt thou let me down!
@@DaedalusCommunity I thought there was something about that sentence... but nicely obfuscated
0:24 when ur codin in python when doing lists "pop" has the same defiition
After a couple hours of debugging, I finally have a fixed version of problem 2.
Reading through these comments, it seems like a lot of people struggled with this problem-the reason I did, and likely many others, is that the stack takes 16 bit values, as we're in 16 bit mode, but we read and store characters as individual bytes (8 bits). This manifests in a couple of places.
In the readstr function, we take input into al, but push ax, as we need to push a 16 bit value. This means that, if I receive the character 'a', what the stack will actually receive is 0x0e, 'a'. This is because ah and al are simply the high and low parts of ax, so when we push ax, we push push ah & al. ah is set to 0x0e from turning on teletype mode, and remains that way throughout basically the whole program.
The issue this created is that when I tried to pop off of the stack directly into the buffer, it wouldn't just write the individual byte that came from al, but also the value 0x0e. The way I circumvented this is by first popping into ax, which is necessary, as we are popping a 16 bit value, so we must use a 16 bit register. Then I do `mov [bx], al`, which moves just the lower half of ax into the buffer, circumventing the issue.
There may be a way to do this without using ax as an intermediate buffer, but I don't know it.
If part of this is unclear feel free to ask further questions-code below:
[org 0x7c00]
bits 16
jmp main
;
; Print a string
; - bx: address of string
;
puts:
mov al, [bx]
cmp al, 0
je exit
mov ah, 0x0e ; display character in tty mode
int 0x10 ; - read from al
inc bx
jmp puts
exit:
ret
;
; Read a string into `buffer`
;
readstr:
mov bx, buffer
jmp readchar
readchar:
mov ah, 0
int 0x16
cmp al, 0x0D ; check for carriage return
je storestr
push ax
inc bx
jmp readchar
storestr:
dec bx
pop ax
mov [bx], al
cmp bx, buffer
jle exit
jmp storestr
main:
call readstr
mov bx, buffer
call puts
jmp $
str: db "hello world", 0 ; not used in this program
buffer: times 64 db 0
times 510-($-$$) db 0
db 0x55, 0xAA
4:21 shouldn't this be (ds
For the second assignment (homework), this is what I managed to do,
[org 0x7c00]
mov bp, 0x8000
mov sp, bp
inputLabel:
mov ax, 0
int 0x16
mov bh, al
push bx
cmp al, 13
je print
jmp inputLabel
print:
pop bx
mov ah, 0x0e
mov al, bh
int 0x10
cmp bp, sp
je exit
jmp print
exit:
jmp $
times 510-($-$$) db 0
db 0x55, 0xaa
I was stuck. Your solution unstuck me by showing me how pop avoids printing the scan codes. Thank you. Instead of popping I found success printing forwards with:
sub bp, 2
mov al, [bp]
@@joshuahall3622 Can maybe explain why that works? I tried that and it works, but I have no clue why
Okay I figured it out
@@texotek3809 Can you tell me too why?, And how to write it.. it is not working for me
It prints for me, but prints the text in reverse order. I kinda know why it does that, but I have no clue how to flip it.
On 1:18, do I understand right, you say that the real address of base and top of a stack are SS:BP and SS:SP respectively? Or I misunderstood, as I expected SS is the one pointing on the Stack segment beginning and SP is an offset of the current (top) data. Why do we need to set BP, if Push command does not use it?
Yes, the stack base and top addresses are offset by the stack segment register
Great, Great series!
Not clear, on 1:21. If we move bh, 'A' but push bx, than the stack must grow by 2 bytes, and the lower byte will contain garbage?
Unless you set bx first, yes
At 5:44, wouldn't you have to set cs to 0x7c0? I thought that the processor would start executing instructions here?
We're only offsetting the data since in that example we're only accessing data; we're not performing any jumps. If we were to perform a jump, we should also offset the code
>>2:22
Cring on amd64 C Calling Conventions :(
Push 4 or 6 arguments into registers, and if you need more, push to the stack. It annoys, but it's fast.
I have very much simplified the part about functions, I'm considering only functions with no arguments. I'll talk about functions with arguments briefly in a future video
is it ok if i use .s instead of .asm? I know .s and .asm are both assembly so is that ok?
stackoverflow.com/q/34098596/13373511
@@DaedalusCommunity ohhhhhhhhhh
where should I initialize the base pointer though?
btw chapter 4 is missing on the github page.
I'm not calling it a kibibyte. I'd rather push and pop my eyeballs out.
I mean, it's ambiguous to call it a kilobyte, but I do get your point lol
When i set bp to 0x8000 and sp to bp, running the os will make a lot of weird characters appear on screen infinitely.
[org 0x7c00] equals to "mov ds, 0x7c0" in real mode, where we have (our) MBR loaded to this real memory address 0x7c00 (and we know it from 8088 CPU documentation)
I think there's a _db_ missing at 5:18.
True, thanks!
Excellent one!!
Got confused here. Why did you push bx after assigning A to bh?
Modern operating systems use paging tho correct? How does CS, DS, SS & ES registers help in the context of paging?
Paging and segmentation are independent ways to access memory. The relevant difference in this case is that paging is managed by the OS, whereas segmentation must be managed at compile time. In our community OS we have implemented paging, hence we do not use those registers anymore.
may you please tell us how to use qemu to debug? I am currently researching that on my own.
There is a way to connect qemu to GDB over a local network. I've done it a few times a few years ago, I'm not quite sure how to do it now. What have you found so far?
i couldnt do the function one i dont know how it works i mean how c i convert it to its ascii value without a database
can i get some help
Hi, what specifically do you need help with? If you want an ascii table, google "ascii table" and you will find it
Print Number function
printnumber:
mov ah, 0x0e
mov bx, 0
mov al, 48
print:
cmp bx, 10
je exit
int 0x10
inc bx
inc al
jmp print
exit:
ret
jmp $ ; infinite loop to hold screen
times 510 - ($-$$) db 0
db 0x55, 0xaa
A lot of important details left out about segmentation and how procedure calls utilize the stack. As result, not explaining the concepts as they should be.
If you need help you can join the discord!
alt?
0:06 pun moment
Did anyone notice xnopyt at 2:30
[ ruclips.net/video/-ORLmYVztCM/видео.html ]
This series is amazing, although i watched it 3 times to actually be able to do the homework you gave
My favorite one is this one: ruclips.net/video/aMgCBYgVwsI/видео.html
@@DaedalusCommunity bye tom
@@DaedalusCommunity I was wondering why the word seemed oddly familiar...
can you please tell me where you learned all this stuff? I'm trying to figure it out myself.
Mostly university, but also the OSDev wiki. You should have a pretty solid understanding of C programming before approaching the subject from that point of view though
my solution to challenge 2, to read, print, then echo user input using the stack. does not use pop but rather reallocates the stack pointer manually after printing. is there a better way to do it with pop?
[org 0x7c00] ;offset for memory addresses
section .text
global _start
_start:
mov ah, 0x0e ;selects BIOS operation (14) to perform on interupt 0x10
mov bx, msg ;the variable name serves as pointer start of string, [] derefs
call print
call new_line
jmp read
print:
mov al, [bx]
cmp al, 0
je return
int 0x10
inc bx
jmp print
read:
mov bx, sp ; copy the stack pointer (start of string) to bx
sub bx, 2 ; account for the first char we push
read_loop:
mov ah, 0x00
int 0x16 ; read keyboard for char
cmp al, 0x0D ; check for enter key
je process ; echo if enter
push ax ; push the current char to stack
mov ah, 0x0e ; op14 for interupt 10
int 0x10 ; interupt 10, prints char
jmp read_loop
new_line: ; prints new line and moves cursor to start
mov ah, 0x0e
mov al, 0x0d ; carriage return (to beginning of line)
int 0x10
mov al, 0x0a ; line feed (to next line)
int 0x10
ret
process:
call new_line
; print input
pop_loop:
push 0 ; mark end of string at top of stack
push bx ; the data at sp will be address of start of the string
mov al, [bx]
cmp al, 0
je process_done
mov ah, 0x0e ;selects BIOS operation (14) print char to perform on interupt 0x10
int 0x10 ; video BIOS interupt (write char)
sub bx, 2 ; move towards the top of the stack by one char
jmp pop_loop
process_done:
pop ax ; get string start
mov sp, ax ; resest sp to string start
call new_line
jmp read
msg:
db "type and press enter to echo", 0
return:
ret
times 510-($-$$) db 0 ;$ - current address, $$ - sector address
db 0x55, 0xAA
you are a good man
Tried both challenges. 1) Works fine but something weird happens with 2)
[org 0x7c00]
jmp main
; 1)
print:
;;; Initialization
mov cx, 0 ; Inits counter.
mov bx, 10 ; Sets divisor to 10.
;;; Convertion to decimal.
print_convertNextNum:
inc cl ; Adds the number to the count.
mov dx, 0
div bx ; ax//bl -> ax AND ax%bl -> dx.
push dx ; Saves number in stack.
cmp al, 0
jne print_convertNextNum
;;; Writing the digits.
print_writeNextNum:
pop dx ; Gets next number.
mov al, dl
add al, 48 ; Changes the number to the right ASCII char.
mov ah, 0x0e ; Prints the number
int 0x10
dec cl ; Decreases counter.
jnz print_writeNextNum
ret
; 2)
; Doesn't work for some reason
stackString:
;;; Initialization.
mov cx, 0 ; cx will count the characters in string.
mov ax, 0 ; ax will get the char values one by one.
mov dx, 0 ; dx will be used to catch when [Enter] is pressed.
;;; Reading.
stackString_readNextChar:
xor ah, ah ; Reset ah.
int 0x16 ; Asks for next char.
push ax ; Push keypress to the stack.
inc cx
cmp ah, 0x1C ; Quits loop when [Enter] is pressed.
jne stackString_readNextChar
times 2 add sp, cx ; Goes back to the start of the string. (cx cells = 2cx bytes)
sub sp, 2
;;; Writing.
stackString_writeNextChar:
xor ax, ax
pop ax ; Gets the key pressed.
mov ah, 0x0e
int 0x10
sub sp, 4 ; Goes to next char. (two cells = 4 bytes)
dec cx
jnz stackString_writeNextChar
times 2 add sp, cx ; Goes back to the start of the string.
add sp, 2 ; Goes back to the top of the stack.
ret
main:
; Stack init.
mov bp, 0x8000
mov sp, bp
mov ax, 2015
call stackString
; Generates the boot section.
jmp $
times 510-($-$$) db 0
db 0x55, 0xaa
doesnt the stack grow downwards? UwU
Correct, and that is why I represented it growing from right to left
a thumbs up 👍
I feel useless here. I am for the love of life cannot seem to figure out the homework. I want to do the echo input. I already have the input loop, but the closest thing I got by relying on the comment section is echoing the input in reverse order.
That's a start! You somehow have to find a way to store the input in memory and keep a pointer to its beginning. Then you should be able to print it :)
@@DaedalusCommunity man, if only there was a way to visual what's going on behind the scenes.
2:30
hey, xnopyt isn't a real word!
** Vapes into oblivion **
0:32
Hello, I am trying to combine the keyboard input with the stack and was testing following code:
mov bp, 0x8000
mov sp, bp
call addVal
call printVal
jmp $ ; Jumping to current memory section to start the booting
times 510-($-$$) db 0 ; Defining 512 bytes in total with regard of this code
db 0x55, 0xaa ; last two bytes which mark this file as the bootloader!
addVal:
mov bh, 'A'
push bx
ret
printVal:
pop bx
mov ah, 0x0e
mov al, bh
int 0x10
ret
Shouldn´t this work in the way that both of the functions get called after each other and then "booting" or am I missing something (nothing gets printed)?
at printVal, at the mov al, bh; i think you need to use bl enstead of bh
In the video, he said that stack is automatically handled when you create function with call and ret. I'm trying something and I send you my soluce if it works.
call and ret use pusha and popa.
This means that call addVal will push all 16-bit registers onto the stack. You then set bh to A and push it onto the stack.
After that you use ret which has the effects of popa.
At this point you still have bh on top of all the other registers which results in those values being mixed up.
Remember that you can only put and take stuff on/from the top of the stack
This would work if you were to use jmp instead of call because jmp does not touch the stack.
Hope i could help you
@@janb.9425 oh thanks. I didn't understand that. Thanks
Nice
This is super helpful!
Commenting for the algorithm V2 hehe
thanks for all
i make keyboard listenner
its read and write real time
my first asm code!
listenKeyboard:
mov al, 0
mov ah, 0
int 0x16
mov ah, 14
int 0x10
jmp listenKeyboard
yo this video is POP-ING HAWAWHAWHAWHAWHEHERHETHETHETHAHTEAIYHIEYHIEHY!!11
What's POPpin RUclips, it's ye boy kmac2021 here
too bad i didnt have this when i made my first hoppy os. anyways, time to stop screwing around and go make my gdt;
Good job but I’m not speaking English please I need more pictures and how I practice code
Solution to No.2:
org 0x7c00
mov bp, 0x8000
mov sp, bp
mov bh, '~'
push bx
jmp decimalNum
decimalNum:
mov ah, 0
int 0x16
mov bh, al
push bx
cmp al, '/'
je printSpace
continueFromSpace:
cmp al, '/'
je printOut
mov ah, 0x0e
int 0x10
jmp decimalNum
printSpace:
mov ah, 0x0e
mov al, 32;
int 0x10
mov al, '/'
jmp continueFromSpace
printOut:
mov ah, 0x0e
mov al, bh
cmp al, '/'
jne output
jmp continue
continue:
cmp al, '~'
je exit
pop bx
jmp printOut
output:
cmp al, '~'
je continue
int 0x10
jmp continue
exit:
jmp $
times 510-($-$$) db 0
db 0x55, 0xaa
It prints in reverse tho so a perfect solution for direct print may be to push it into another register and then pop that into output?
ur smart
Nope, just a pretty average cs student
@@DaedalusCommunity cant wait for the next video, also do you have a link for the solution for the getting keyboard input to string thing? would be helpful, since im learning assembly with this course
@@worldender4430 not yet, no one's found it and I'd like people to try
@@DaedalusCommunity interesting, guess im not the only one who cant solve it then :D
@@DaedalusCommunity the solution could be to push the string you type into the stack, then clear the screen somehow then pop it? i dont know much about assembly so im just kinda figuring things out
Random comment
Hilarious response
1)
[bits 16]
[org 0x10]
start:
cli
mov ax, 0
mov ss, ax
mov bp, 0x7c00
mov sp, bp
push 0
mov ax, 231
stackify:
aam
push ax
cmp ah, 0
je print
mov al, ah
jmp stackify
print:
pop ax
cmp al, 0
je end
mov ah, 0x0e
add al, '0'
int 0x10
jmp print
end:
hlt
times 510 - ($ - $$) db 0
dw 0xaa55
2)
[bits 16]
[org 0x7c00]
start:
mov ax, 0
mov ss, ax
mov bp, 0x7c00
mov sp, bp
input:
mov ah, 0
int 0x16
cmp al, 0x0D
je input_end
push ax
mov ah, 0x0e
int 0x10
jmp input
input_end:
mov ah, 0x0e
mov al, 10
int 0x10
mov al, 13
int 0x10
display:
cmp sp, bp
je end
dec bp
dec bp
mov ah, 0x0e
mov al, [bp]
int 0x10
jmp display
end:
hlt
times 510 - ($ - $$) db 0
dw 0xaa55
Algorithm
You're a good person
I did my homework 1)
[org 0x7c00]
; Init Stack
mov bp, 0x8000
mov sp, bp
; Prints number
mov ax, 0x1A4 ; 420
call print_num
call print_nl
; Prints number
mov ax, 0x18 ; 24
call print_num
call print_nl
; Prints number
mov ax, 0x1E ; 30
call print_num
call print_nl
; Prints number
mov ax, 0x5 ; 5
call print_num
call print_nl
; Prints number
mov ax, 0x1D ; 29
call print_num
call print_nl
; Forever Loop
jmp $
print_num: ; Print a number given in ax
pusha ; Stores all registers in the stack
mov cx, 0 ; Counter for the digits
mov bx, 0x0a ; Divisor set to 10
pn_loop:
mov dx, 0
div bx ; Division -> ax / bx -> ax ; ax % bx -> dx
push dx
inc cx
cmp ax, 0 ; jmps out of the loop if the quotient is zero
je pn_print
jmp pn_loop
pn_print: ; Print setup
mov al, 0x0
mov ah, 0x0e
pn_print_loop: ; Prints numbers
dec cx
pop bx
add bx, '0' ; Converts the Digit to a Assci number digit
mov al, bl
int 0x10
cmp cx, 0 ; jumps out of the loop if the digit counter is zero
je pn_exit
jmp pn_print_loop
pn_exit:
popa ; Pops the stored registers from the stack
ret ; Returns to the point in the program where this function was called
print_nl: ; Jumps int the next line on screen
pusha
mov ah, 0x0e
mov al, 0xd
int 0x10
mov al, 0xa
int 0x10
popa
ret
; Magic
times 510 - ($ - $$) db 0
db 0x55, 0xaa
Hello, I've tried using call and ret instead of pusha and popa, however it wont work for me without doing it myself
.
CODE ///
[org 0x7c00]
mov al, 'A'
mov ah, 0x0e
int 0x10 ; Prints A
pusha ;