masm mov mybyte, -130 why this works? - masm

Why this code get compiled if there is an error here. -130 does not fit on byte.
.686
.model flat,stdcall
.data
mybyte db 0
.code
Start:
mov mybyte, -130
ret 0
end Start

Related

Reverse the array in Assembly Language 80X86

Write an assembly program that defines the following array 10 25 56 80 3 , then create procedure reverse to display the array in reverse.
I can only get the size of the array. I tried to use push/pop.
Thank you!
.586
.MODEL FLAT
INCLUDE io.h
.stack 4096
.DATA
array1 DWORD 10,25,56,80,3 DUP (?)
size1 DWORD 5
outputLbl BYTE "The reverse is", 0
result BYTE 11 DUP (?), 0
.CODE
_MainProc PROC
mov eax,5
lea ebx,array1 ;get address
call reverse
dtoa result, eax
output outputLbl, result
_MainProc ENDP
reverse PROC
pushad
mov ecx, (5)/2
mov esi,0
mov edi,((5)-1)
L1:
mov eax, array1[esi*4]
mov ebx, array1[edi*4]
mov array1[esi*4], ebx
mov array1[edi*4], eax
loop L1
popad
ret
reverse ENDP
END

My bootloader works, but doesn't load my kernel into memory

I've looked more into creating my own bootloader, rather than using grub. I soon came up with this: It takes care of switching to 32bit pm, it loads my kernel from the disk & it jumps to it to execute it.
I'm catting my kernel & my bootloader like this: cat boot.bin kernel > img.bin
I'm assembling my bootloader like this: nasm -f bin boot.s -o boot.bin
i686-elf-ld -o kernel -Ttext=0x1000 kernel_entry.bin kernel.bin --oformat binary
I'm compiling my kernel like this: i686-elf-gcc *.o -Ttext=0x1000 -o kernel.bin -ffreestanding -O2 -nostdlib -lgcc
(*.o are all compiled C files which I compile like this: i686-elf-gcc -c file.c -o file.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra
[org 0x7c00]
[bits 16]
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7c00
jmp 0:skip ; far jump
skip:
; load kernel
mov bx, 0x1000
mov dh, 17 ; reading 20 sectors should be enough ._.
mov dl, [BOOT_DRIVE]
call dsk_load
call load_kernel
dsk_load:
mov [SECTORS], dh
mov ch, 0x00 ; C = 0
mov dh, 0x00 ; H = 0
mov cl, 0x02 ; S = 2
next_group:
mov di, 5 ; retry 5 times
again:
mov ah, 0x02
mov al, [SECTORS]
int 0x13
jc maybe_retry
sub [SECTORS], al ; set remaining sectors
jz done
mov cl, 0x01 ; read sector 1
xor dh, 1 ; next head
jnz next_group
inc ch ; next cylinder
jmp next_group
maybe_retry:
mov ah, 0x00 ; reset drive
int 0x13
dec di
jnz again
jmp dsk_err ; we've tried too many times, give up
dsk_err:
mov bx, BOOTLOADER_SIG
call print
mov bx, DISK_READ_FAIL
call print
jmp $
done:
ret
; print string
print:
; print loop
print_loop:
mov ah, 0x0e
mov al, [bx] ; load current character
cmp al, 0
je print_return ; return when finished
int 0x10 ; print character
inc bx ; next character
jmp print_loop
print_return:
ret
load_kernel:
; If all that went well, we can switch to protected mode
cli
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0 , eax
jmp CODE_SEG:init_32_pm ; make a far jump
[bits 32]
init_32_pm:
set_up_stack:
mov esp, stack_end
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
jmp 0x1000 ; jump to kernel_entry.s
; our beloved gdt
gdt_start:
gdt_null: ; null descriptor
dd 0x0
dd 0x0
gdt_code: ; code segment descriptor
dw 0xffff ; limit (bits 0-15)
dw 0x0 ; base (bits 0-15)
db 0x0 ; base (bits 16-23)
db 10011010b ; 1st flags, type flags
db 11001111b ; 2nd flags, Limit (bits 16-19)
db 0x0 ; base (bits 24 - 31)
gdt_data: ; data segment descriptor
dw 0xffff ; limit (bits 0-15)
dw 0x0 ; base (bits 0-15)
db 0x0 ; base (bits 16 -23)
db 10010010b ; 1st flags, type flags
db 11001111b ; 2nd flags, Limit (bits 16-19)
db 0x0 ; base (bits 24 - 31)
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; gdt size
dd gdt_start ; gdt start address
; some handy constants
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
BOOT_DRIVE db 0
SECTORS db 0
BOOTLOADER_SIG db "------ bootloader ------", 0x0d, 0xa, 0
DISK_READ_FAIL db "An error occurred while loading the kernel! Please restart your computer.", 0x0d, 0xa, 0
times 510-($-$$) db 0
dw 0xaa55
section .bss
stack_begin:
resb 4096 ; 4kib stack
stack_end:
; 9 sectors
The code that resides over at 0x1000 is this:
; kernel_entry.s
[bits 32]
[extern kmain]
call kmain
jmp $
times 510-($-$$) db 0
dw 0xaa55
; 1 sectors
My bootloader doesn't crash but it does not load my kernel, which should print some things to the screen.
This is the kmain function:
void kmain(void)
{
/* Initialize terminal */
tty_init();
tty_puts("Hello kernel!", VGA_COLOR_LIGHT_CYAN);
}
Assume the tty functions are working, since they were doing just fine when testing with grub instead of my own bootloader. Does anyone know what's going on? (Testing in bochs shows no errors)

Small model DOS .exe compiled and linked by OpenWatcom crashes

I'm trying to create a small DOS .exe program. I wrote the entry point in NASM assembly
; st.nasm
global _small_code_
global _printmsg_
extern _main0_
segment code
_small_code_:
..start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, stacktop
mov ah, 9 ; WRITE_STDOUT
mov dx, hello_msg
int 0x21
call _main0_
; call _printmsg_
; mov ax, 3
mov dx, ax
add dx, hello_msg
mov ah, 9 ; WRITE_STDOUT
int 0x21
mov ah, 0x4c ; EXIT, exit code in al
int 0x21
_printmsg_:
ret
push dx
xchg ax, dx
mov ah, 9 ; WRITE_STDOUT
mov dx, hello_msg ; !!
int 0x21
pop dx
ret ; !! restore AX?
segment data
hello_msg: db 'Hello, World!', 13, 10, '$'
segment stack stack
resb 1024
stacktop:
Please note that I'm not sure how the ..start: code and the segments should look like, I copy-pasted that part from somewhere.
I wrote the main program in C:
/* prog.c */
void _printmsg(const char *msg);
int add(int a, int b) {
return a + b * 2;
}
void other() {
_printmsg("Hello!\r\n$"); /*CRASH*/
/*_printmsg(0);*/ /*OK*/
}
int _main0() {
return 5;
}
I compile it with this:
$ nasm -f obj -o st.obj st.nasm
$ owcc -bdos -mcmodel=s -fno-stack-check -Os -s -march=i86 -o prog.exe prog.c st.obj
The resulting prog.exe is:
$ xxd prog.exe
00000000: 4d5a 8b00 0100 0200 0300 4000 ffff 0100 MZ........#.....
00000010: 4b04 0000 0a00 0100 2000 0000 0000 0000 K....... .......
00000020: 0b00 0100 1000 0100 0000 0000 0000 0000 ................
00000030: d1e2 01d0 c3b8 0000 e934 00b8 0500 c300 .........4......
00000040: 4865 6c6c 6f21 0d0a 2400 b801 008e d8b8 Hello!..$.......
00000050: 0100 8ed0 bc4b 04b4 09ba 3b00 cd21 e8da .....K....;..!..
00000060: ff89 c281 c23b 00b4 09cd 21b4 4ccd 21c3 .....;....!.L.!.
00000070: 5292 b409 ba3b 00cd 215a c348 656c 6c6f R....;..!Z.Hello
00000080: 2c20 576f 726c 6421 0d0a 24 , World!..$
Disassembly of prog.exe:
$ ndisasm -e 0x20 -b 16 prog.exe
00000000 0B00 or ax,[bx+si]
00000002 0100 add [bx+si],ax
00000004 1000 adc [bx+si],al
00000006 0100 add [bx+si],ax
00000008 0000 add [bx+si],al
0000000A 0000 add [bx+si],al
0000000C 0000 add [bx+si],al
0000000E 0000 add [bx+si],al
00000010 D1E2 shl dx,1
00000012 01D0 add ax,dx
00000014 C3 ret
00000015 B80000 mov ax,0x0
00000018 E93400 jmp 0x4f
0000001B B80500 mov ax,0x5
0000001E C3 ret
0000001F 004865 add [bx+si+0x65],cl
00000022 6C insb
00000023 6C insb
00000024 6F outsw
00000025 210D and [di],cx
00000027 0A24 or ah,[si]
00000029 00B80100 add [bx+si+0x1],bh
0000002D 8ED8 mov ds,ax
0000002F B80100 mov ax,0x1
00000032 8ED0 mov ss,ax
00000034 BC4B04 mov sp,0x44b
00000037 B409 mov ah,0x9
00000039 BA3B00 mov dx,0x3b
0000003C CD21 int 0x21
0000003E E8DAFF call 0x1b
00000041 89C2 mov dx,ax
00000043 81C23B00 add dx,0x3b
00000047 B409 mov ah,0x9
00000049 CD21 int 0x21
0000004B B44C mov ah,0x4c
0000004D CD21 int 0x21
0000004F C3 ret
00000050 52 push dx
00000051 92 xchg ax,dx
00000052 B409 mov ah,0x9
00000054 BA3B00 mov dx,0x3b
00000057 CD21 int 0x21
00000059 5A pop dx
0000005A C3 ret
0000005B 48 dec ax
0000005C 656C gs insb
0000005E 6C insb
0000005F 6F outsw
00000060 2C20 sub al,0x20
00000062 57 push di
00000063 6F outsw
00000064 726C jc 0xd2
00000066 64210D and [fs:di],cx
00000069 0A24 or ah,[si]
prog.exe puts DOSBox to an infinite loop. Oddly enough, if I remove the string literal from the C source file (in the other function, which isn't even called), it successfully returns. What's wrong in the assembly file?
Please note that this is the first time I'm using OpenWatcom, and this is the first time I build a DOS .exe file.
I don't want to write a main function, because that would cause the OpenWatcom libc to be linked to the output executable, making it unnecessarily large.
The primary problem is in how you define the code segment. The Watcom C/C++ compiler when using the SMALL memory model requires the code segment to be called _TEXT with a class of CODE. This mismatch between the assembly code and the C code leads to the code segment being in different physical segments and the call _main0_ jumping to the wrong place in memory causing exceptions to be thrown and the program hanging or crashing.
You can also get the Watcom linker to generate the required STACK in the DOS EXE by creating a segment called _STACK with attribute STACK and class STACK. If you create the stack segment this way, you won't need to initialize SS:SP at the beginning of your program.
The other sections that Watcom uses in the SMALL memory model are:
_DATA segment with a class of DATA for read/write data
CONST segment with a class of DATA for string literals (that aren't expected to be modified)
CONST2 segment with a class of DATA for other read only data
_BSS segment with class of BSS for uninitialized data.
Watcom expects that the segments CONST, CONST2, _DATA and _BSS to all be in the same group called DGROUP. All the data in the same group can be referenced by the name of the group. When you set up DGROUP in the way Watcom expects then all you have to do is initialize DS to the DGROUP segment and not the individual segments within the group.
The special label that starts with .. acts as the DOS entry point where execution should start. Thus ..start is used to generate an entry point in the DOS EXE header telling the program loader where to start executing when the program is loaded into memory.
A revised version of your assembly code could have looked like this:
; st.nasm
; DGROUP in watcom C/C++ for small model is:
GROUP DGROUP CONST CONST2 _DATA _BSS
global _small_code_
global _printmsg_
extern _main0_
; Code Segment (16-bit code)
segment _TEXT use16 class=CODE
_small_code_:
; .. denotes the label to be used as the DOS entry point
..start:
mov ax, DGROUP
mov ds, ax
mov ah, 9 ; WRITE_STDOUT
mov dx, hello_msg
int 0x21
call _main0_
; call _printmsg_
; mov ax, 3
mov dx, ax
add dx, hello_msg
mov ah, 9 ; WRITE_STDOUT
int 0x21
mov ah, 0x4c ; EXIT, exit code in al
int 0x21
_printmsg_:
ret
push dx
xchg ax, dx
mov ah, 9 ; WRITE_STDOUT
mov dx, hello_msg ; !!
int 0x21
pop dx
ret ; !! restore AX?
; Read only string literals here
segment CONST class=DATA
hello_msg: db 'Hello, World!', 13, 10, '$'
; Other read only data here
segment CONST2 class=DATA
; Read/Write data here
segment _DATA class=DATA
; Uninitialized data segment
segment _BSS class=BSS
; Stack segment 1k in size
segment _STACK STACK class=STACK
resb 1024
This code assumes that SS != DS, however it would have to be compiled with OWCC's option -Wc,-zu that passes the -zu to WCC (Watcom Compiler). -zu modifies code generation so that:
-zu SS != DGROUP (i.e., do not assume stack is in data segment)
If you wish to set SS==DS==DGROUP there are a number of ways to do it. One option I may suggest is putting _STACK in DGROUP with all the other program data. You would need a label after resb 1024 like stack_top: so you can load that offset into SP at startup after you set SS to the same value as DS. This change would result in assembly code that looks like:
; st.nasm
; DGROUP in watcom C/C++ for small model is:
GROUP DGROUP CONST CONST2 _DATA _BSS _STACK
; _STACK has been added to DGROUP so we can set SS==DS==DGROUP
global _small_code_
global _printmsg_
extern _main0_
; Code Segment (16-bit code)
segment _TEXT use16 class=CODE
_small_code_:
; .. denotes the label to be used as the DOS entry point
..start:
mov ax, DGROUP
mov ds, ax
mov ss, ax ; Set stack SS:SP to DGROUP:stack_top
mov sp, stack_top
mov ah, 9 ; WRITE_STDOUT
mov dx, hello_msg
int 0x21
call _main0_
; call _printmsg_
; mov ax, 3
mov dx, ax
add dx, hello_msg
mov ah, 9 ; WRITE_STDOUT
int 0x21
mov ah, 0x4c ; EXIT, exit code in al
int 0x21
_printmsg_:
ret
push dx
xchg ax, dx
mov ah, 9 ; WRITE_STDOUT
mov dx, hello_msg ; !!
int 0x21
pop dx
ret ; !! restore AX?
; Read/Write data here
segment _DATA class=DATA
; Read only string literals here
segment CONST class=DATA
hello_msg: db 'Hello, World!', 13, 10, '$'
; Other read only data here
segment CONST2 class=DATA
; Uninitialized data segment
segment _BSS class=BSS
; Stack segment 1k in size
segment _STACK STACK class=STACK
resb 1024
stack_top:

Automatic code insertion in tasm

I am trying to write a simple C startup to produce DOS COM file.
I have not yet written argc, argv implementations.
As a test, I'm trying to put dummy argv[0], and argc=1.
But I get 2 unexpected lines as in the dump :
;---------------------------------------------------------------
Turbo Debugger Log
CPU 80486
cs:0100B85902 mov ax,0259
cs:0103 50 push ax
cs:0104 BF5002 mov di,0250
cs:0107 FF35 push word ptr [di]
cs:0109 90 nop ; << Unexpected 1
cs:010A 0E push cs ; << Unexpected 2
cs:010B E83C00 call 014A
cs:010E E80000 call 0111
cs:0111 B44C mov ah,4C
cs:0113 CD21 int 21
cs:0115 55 push bp
cs:0116 8BEC mov bp,sp
cs:0118 8B4604 mov ax,[bp+04]
cs:011B 50 push ax
cs:011C B40E mov ah,0E
cs:011E CD10 int 10
cs:0120 58 pop ax
cs:0121 5D pop bp
cs:0122 C3 ret
cs:0123 50 push ax
cs:0124 55 push bp
cs:0125 8BEC mov bp,sp
cs:0127 C746026900 mov word ptr [bp+02],0069
cs:012C 5D pop bp
cs:012D E8E5FF call 0115
cs:0130 59 pop cx
cs:0131 C3 ret
cs:0132 55 push bp
cs:0133 8BEC mov bp,sp
cs:0135 56 push si
cs:0136 33F6 xor si,si
cs:0138 EB01 jmp 013B
cs:013A 46 inc si
cs:013B 8B5E04 mov bx,[bp+04]
Terminated, exit code 7
Stack :
5B7C:0028 FFFF
5B7C:0026 FFFF
5B7C:0024 FFFF
5B7C:0022 FFFF
5B7C:0020 FFFF
5B7C:001E FFFF
5B7C:001C FFFF
5B7C:001A FFFF
5B7C:0018 FFFF
5B7C:0016 0DEE
5B7C:0014 1C80
5B7C:0012 0280
5B7C:0010 2225
5B7C:000E 01AE
5B7C:000C 2225
5B7C:000A 01E4
5B7C:0008 F01D
5B7C:0006 FEF0
5B7C:0004 9A00
5B7C:0002 9FFF
5B7C:0000 20CD
5B7C:FFFE 0000
5B7C:FFFC 0259 ; << argv
5B7C:FFFA 0001 ; << argc
5B7C:FFF8 5B7C ; << Unexpected CS
5B7C:FFF6 0111
5B7C:FFF4 3206
5B7C:FFF2 5B7C
5B7C:FFF0 0115
5B7C:FFEE 3206
;---------------------------------------------------------------
I could not understand why these lines are added by the assembler.
Could segment-padding be the cause? Also, could there be a stack-frame 'like' construct?
Please enlighten me on this.
Here's the source :
;---------------------------------------------------------------
_TEXT SEGMENT WORD PUBLIC USE16 'CODE'
_TEXT ENDS
_DATA SEGMENT WORD PUBLIC USE16 'DATA'
_DATA ENDS
DGROUP GROUP _DATA , _TEXT
_TEXT SEGMENT
assume cs: _TEXT, ds: _DATA, ss: nothing, es: nothing
ORG 0100h
;----------------------------------
; Program Starts Here...
;----------------------------------
start:
; Push arguments in reverse order...
;====================================
mov ax, offset DGROUP:argv
push ax
mov di, offset DGROUP:argc
push word ptr [di]
call far ptr _main
call near ptr __terminate
;----------------------------------
;----------------------------------
; Function Declarations...
;----------------------------------
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
__terminate proc DIST
PUBLIC __terminate
mov ah, 4ch
int 21h
__terminate endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;----------------------------------
_TEXT ends
_DATA SEGMENT
argc dw 1
arg0 db 'NEW_C0', 0h
argv dw DGROUP:arg0
_DATA ENDS
extrn _main
end start
;---------------------------------------------------------------
From a document I found titled "Turbo Assembler 2.0 New Features":
The default condition is SMART enabled. When SMART is enabled, a qualifying
FAR jump will be replaced by a NEAR or a SHORT jump. Also, when SMART is
enabled, a qualifying FAR call will be replaced by a PUSH CS instruction
and a NEAR call.
So TASM, being smart at recognizing that _main and your startup code are in the same segment (you are producing a COM file, after all), takes the liberty to replace:
call far ptr _main
by the shorter and faster near call:
push cs
call _main
The push is necessary because TASM must assume that main itself was compiled with a far return; it expects a 32-bit return address on the stack.
You should ask yourself whether you actually want/need this far call in a COM file.
As for the nop, I have no idea. It could be word padding in an effort to make the far return more efficient, but I find Jester's explanation (reserve space for a true far call) very plausible too.

Assembly - Error: junk '40' after expression

I'm using an i686-elf-as gcc cross compiler and it's failing to compile an assembly file.
The file is used alongside grub to boot my own operating system but when i try defining any globals or enter the _irq part it spits out tons of errors which are mainly
boot.s:78: Error: no such instruction: `irq4'
boot.s:81: Error: junk `0' after expression
boot.s:82: Error: junk `36' after expression
How would I stop this from happening?
Below is the entire boot.s file
# Declare constants used for creating a multiboot header.
.set ALIGN, 1<<0
.set MEMINFO, 1<<1
.set FLAGS, ALIGN | MEMINFO
.set MAGIC, 0x1BADB002
.set CHECKSUM, -(MAGIC + FLAGS)
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM
.section .bootstrap_stack, "aw", #nobits
stack_bottom:
.skip 16384 # 16 KiB
stack_top:
.section .text
.global _start
.type _start, #function
_start:
movl $stack_top, %esp
call kernel_main
cli
hlt
.Lhang:
jmp .Lhang
.global _irq0
.global _irq1
.global _irq2
.global _irq3
.global _irq4
.global _irq5
.global _irq6
.global _irq7
.global _irq8
.global _irq9
.global _irq10
.global _irq11
.global _irq12
.global _irq13
.global _irq14
.global _irq15
_irq0:
cli
push byte 0
push byte 32
jmp irq_common_stub
_irq1:
cli
push byte 0
push byte 33
jmp irq_common_stub
_irq2:
cli
push byte 0
push byte 34
jmp irq_common_stub
_irq3:
cli
push byte 0
push byte 35
jmp irq_common_stub
_irq4:
cli
push byte 0
push byte 36
jmp irq_common_stub
_irq5:
cli
push byte 0
push byte 37
jmp irq_common_stub
_irq6:
cli
push byte 0
push byte 38
jmp irq_common_stub
_irq7:
cli
push byte 0
push byte 39
jmp irq_common_stub
_irq8:
cli
push byte 0
push byte 40
jmp irq_common_stub
_irq9:
cli
push byte 0
push byte 41
jmp irq_common_stub
_irq10:
cli
push byte 0
push byte 42
jmp irq_common_stub
_irq11:
cli
push byte 0
push byte 43
jmp irq_common_stub
_irq12:
cli
push byte 0
push byte 44
jmp irq_common_stub
_irq13:
cli
push byte 0
push byte 45
jmp irq_common_stub
_irq14:
cli
push byte 0
push byte 46
jmp irq_common_stub
_irq15:
cli
push byte 0
push byte 47
jmp irq_common_stub
extern _irq_handler
irq_common_stub:
pusha
push %ds
push %es
push %fs
push %gs
mov %ax, 0x10
mov %ds, %ax
mov %es, %ax
mov %fs, %ax
mov %gs, %ax
mov %eax, %esp
push %eax
mov %eax, _irq_handler
call eax
pop %eax
pop %gs
pop %fs
pop %es
pop %ds
popa
add %esp, 8
iret
.size _start, . - _start
You're mixing Intel and AT&T syntax assembly language. GNU as uses AT&T syntax traditionally. Intel syntax is that used by assemblers such as NASM, MASM, YASM, and historical assemblers designed for the x86 platform.
movl $stack_top, %esp is a perfectly valid example of AT&T syntax assembly language. push byte 35 is a perfectly valid example of Intel syntax assembly language. The two syntaxes, however, are incompatible, and cannot be combined.
I recommend looking up an assembly language tutorial that uses as on Linux, and learning how to use assembly language in the first place before jumping into something as complex and headache-inducing as systems development. ;)
http://asm.sourceforge.net/ -- Perhaps this tutorial/resource site could be of use to you. Good luck!

Resources