Discussion:
I feel stupid... "Invalid combination of opcode and operand"
(too old to reply)
001
2006-08-16 20:41:38 UTC
Permalink
I'm pretty new to assembly and started off by writing an OS, which is
really a wrong decision I think... but hey, there's nothing cooler than
this;) I entered protected mode, set up a GDT and messed up my screen a
little, now I'm programming the PIC.

My problem now is that standard error that NASM cries once in a while
(as seen in my subject) and it occurs with the 'out' instruction,
something I've never used before but I hoped it would work like this,
and... it doesn't... Can anyone please help me out?

This is what causes the problem and it should be in these lines I think
(unless it's a register)...

out 0x20,al ;Initialization Command Word to PIC1
out 0xA0,ah ;Initialization Command Word to PIC2
out al,0x20 ;Remap IRQ 0-7 to Interrupts 32-39
out bh,0x28 ;Remap IRQ 8-15 to Interrupts 40-47
out al,4 ;IRQ2 connection to slave
out bh,2
out al,0x01 ;Architecture and software/hardware-setting
out bh,0x01
out al,0xFF ;Mask all IRQs
Bjarni Juliusson
2006-08-16 23:09:07 UTC
Permalink
Post by 001
out 0xA0,ah ;Initialization Command Word to PIC2
out al,0x20 ;Remap IRQ 0-7 to Interrupts 32-39
out bh,0x28 ;Remap IRQ 8-15 to Interrupts 40-47
out al,4 ;IRQ2 connection to slave
out bh,2
out al,0x01 ;Architecture and software/hardware-setting
out bh,0x01
out al,0xFF ;Mask all IRQs
The only addressing modes allowed for out are:

out imm8, al
out imm8, [e]ax
out dx, al
out dx, [e]ax

Thus, only ports 0-255 can be addressed by an immediate value, and to
access the higher ports, you must load the address into dx. The data to
output is always taken from the accumulator: al, ax or eax.

I suggest you learn to use the manual. It is all there.


Bjarni
--
INFORMATION WANTS TO BE FREE
001
2006-08-17 09:03:53 UTC
Permalink
imm8 means immediate 8-bit right? Okay, then I've got it... and I've
got to use a register with 'a' at the end you say? I'm gonna give it a
try... Thank you for your help, and I'm definitely gonna consult the
NASM-manual next time.
Post by Bjarni Juliusson
Post by 001
out 0xA0,ah ;Initialization Command Word to PIC2
out al,0x20 ;Remap IRQ 0-7 to Interrupts 32-39
out bh,0x28 ;Remap IRQ 8-15 to Interrupts 40-47
out al,4 ;IRQ2 connection to slave
out bh,2
out al,0x01 ;Architecture and software/hardware-setting
out bh,0x01
out al,0xFF ;Mask all IRQs
out imm8, al
out imm8, [e]ax
out dx, al
out dx, [e]ax
Thus, only ports 0-255 can be addressed by an immediate value, and to
access the higher ports, you must load the address into dx. The data to
output is always taken from the accumulator: al, ax or eax.
I suggest you learn to use the manual. It is all there.
Bjarni
--
INFORMATION WANTS TO BE FREE
Bjarni Juliusson
2006-08-17 21:02:53 UTC
Permalink
Post by 001
imm8 means immediate 8-bit right? Okay, then I've got it... and I've
got to use a register with 'a' at the end you say? I'm gonna give it a
try... Thank you for your help, and I'm definitely gonna consult the
NASM-manual next time.
imm8 is an 8 bit immediate, yes, and you can only use al, ax or eax.

Also, I mean for you to read the *x86* manual, that's where the
documentation for the x86 architecture is...


Bjarni
--
INFORMATION WANTS TO BE FREE
Jean-François Michaud
2006-08-17 18:14:51 UTC
Permalink
Post by 001
I'm pretty new to assembly and started off by writing an OS, which is
really a wrong decision I think... but hey, there's nothing cooler than
this;) I entered protected mode, set up a GDT and messed up my screen a
little, now I'm programming the PIC.
I think its a great decision. If I may suggest; look into the Forth
language if you want to put together your OS. You will have a running
OS running in no time and it will be as flexible as you want it to be.
Writin an OS in assembly is definately not the best way to go.

The core of the Indirect threaded Forth address interpreter is 10 lines
of assembly code. The rest is written in Forth (by using labels and
writing down longs to reference labels in your assembler!).

Let me know if you're interrested. I can share some thoughts and code
to help you out.
Post by 001
My problem now is that standard error that NASM cries once in a while
(as seen in my subject) and it occurs with the 'out' instruction,
something I've never used before but I hoped it would work like this,
and... it doesn't... Can anyone please help me out?
This is what causes the problem and it should be in these lines I think
(unless it's a register)...
out 0x20,al ;Initialization Command Word to PIC1
out 0xA0,ah ;Initialization Command Word to PIC2
out al,0x20 ;Remap IRQ 0-7 to Interrupts 32-39
out bh,0x28 ;Remap IRQ 8-15 to Interrupts 40-47
out al,4 ;IRQ2 connection to slave
out bh,2
out al,0x01 ;Architecture and software/hardware-setting
out bh,0x01
out al,0xFF ;Mask all IRQs
Let me get back home. I don't have my code with me right now. I can
post the code I have have on pic programming later. I use AT&T syntax
though so you will have to convert. Looks like you're well under way
though. You're almost there.

Regards
Jean-Francois Michaud
Jean-François Michaud
2006-08-18 01:05:13 UTC
Permalink
Alright here goes:

# # We initialise the primary PIC (Primary 8259)
movb $0x11, %al # Initialisation command
out %al, $0x20
movb $0x20, %al # Beginning of the remapping 0x20-0x27
(interrupt 32-39)
out %al, $0x21
movb $0x04, %al
out %al, $0x21
movb $0x01, %al
out %al, $0x21

# We initialise the secondary PIC (Secondary 8259)
movb $0x11, %al # Initialisation command
out %al, $0xA0
movb $0x28, %al # Beginning of the remapping 0x28-0x2F
(interrupt 40-47)
out %al, $0xA1
movb $0x02, %al
out %al, $0xA1
movb $0x01, %al
out %al, $0xA1

# We unmask interrupts 0 and 1 on the primary PIC
mov $0xFC, %al
out %al, $0x21
mov $0xFF, %al
out %al, $0xA1

A little extra: This last bit is for initializing the timer to emit an
interrupt just about every 0.0005 seconds. It's plenty precise for non
critical applications.

movb $0x36, %al # Bit 7,6 = 00 Timer counter 0
# Bit 5,4 = 11 Write LSB then MSB
# Bit 3-1 = 011 Generate square wave
# Bit 0 = 0 Binary counter
outb %al, $0x43
movw $0x254, %cx # 1193182.7 Hz/596 = 2002 Hz ( every 0.0005
second )
movb %cl, %al
outb %al, $0x40
movb %ch, %al
outb %al, $0x40

And if every you are interrested in exploring Forth further, this will
get you started on writing your OS bug free more quickly than you can
imagine. This is the core of the indirect threaded address interpreter
(NEXT, DOCOLON and EXIT).

# %ebx is the first item on the stack (TOS)
# %ebp is the parameters stack, ( second item on the stack )
# %edi is the return stack
# %esi is the next word to execute (I)

.align 4
.macro NEXT
addl $4, %esi
movl (%esi), %edx
jmp *(%edx)
.endm

.align 4
#___________________________________________________________________________
#
# This is the word that allows for indirect threading, in conjunction
with
# NEXT and RETURN. It pushes the word we are coming from on the return
stack
# so we can come back to it later and starts the execution of the new
word
# list for the current word to execute.
#___________________________________________________________________________
#
# ( -- )
DOCOLON_ADDR:
.byte 0x00
.byte 0x03
.ascii "(:)"
.word 0x0000
.byte 0x00
.long VIDEOINIT_ADDR
DOCOLON:
.long DOCOLON_ASM
DOCOLON_ASM:
addl $4, %edi
movl %esi, (%edi) # We put the current list pointer on the return
stack
movl (%esi), %esi # We go to the word to execute's address
NEXT

.align 4
#________________________________________________________________________
#
# This word allows us to go back to the calling word by poping the
return
# stack. This is a part of the address interpreter.
#________________________________________________________________________
#
# ( -- )
EXIT_ADDR:
.byte 0x00
.byte 0x04
.ascii "EXIT"
.word 0x0000
.long DOVARIABLE_ADDR
EXIT:
.long EXIT_ASM
EXIT_ASM:
movl (%edi), %esi
subl $4, %edi
NEXT

Each new definition except for core definitions will have to start wil
DOCOLON_ASM and will have to end with EXIT.

For example (OVER is a core definition that supports the 2DUP example):

.align 4
#_______________________________________________________________
#
# Places a copy of the second element contained in the parameter
# stack on top of the parameter stack.
#_______________________________________________________________
#
# ( x1 x2 -- x1 x2 x1 )
OVER_ADDR:
.byte 0x00
.byte 0x04
.ascii "OVER"
.word 0x0000
.long TICK_DROP_ADDR
OVER:
.long OVER_ASM
OVER_ASM:
movl %ebx, 4(%ebp)
movl (%ebp), %ebx
addl $4, %ebp
NEXT

.align 4
#__________________________________________________________
#
# Duplicates the 2 topmost elements on the parameter stack.
#__________________________________________________________
#
# ( x1 x2 -- x1 x2 x1 x2 ) : 2DUP OVER OVER ;
TWO_DUP_ADDR:
.byte 0x00
.byte 0x04
.ascii "2DUP"
.word 0x0000
.long TWO_DROP_ADDR
TWO_DUP:
.long DOCOLON_ASM
.long OVER, OVER, EXIT

Regards
Jean-Francois Michaud
Tim Roberts
2006-08-22 04:50:46 UTC
Permalink
Post by Jean-François Michaud
...
# We unmask interrupts 0 and 1 on the primary PIC
mov $0xFC, %al
out %al, $0x21
mov $0xFF, %al
out %al, $0xA1
A little extra: This last bit is for initializing the timer to emit an
interrupt just about every 0.0005 seconds. It's plenty precise for non
critical applications.
In fact, it's TOO precise for anything but a toy operating system. DOS and
Windows program the timer to interrupt every 55 ms. Windows XP uses 64 ms
(although it can be changed). Linux uses 10 ms.

Remember that you will want to run your scheduler and reevaluate task
priorities during that timer interrupt. As you reduce that timer
interval, you spend a larger and larger fraction of your time just doing
that kind of housekeeping.
--
- Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Bob Masta
2006-08-22 12:50:57 UTC
Permalink
Post by Tim Roberts
Post by Jean-François Michaud
...
# We unmask interrupts 0 and 1 on the primary PIC
mov $0xFC, %al
out %al, $0x21
mov $0xFF, %al
out %al, $0xA1
A little extra: This last bit is for initializing the timer to emit an
interrupt just about every 0.0005 seconds. It's plenty precise for non
critical applications.
In fact, it's TOO precise for anything but a toy operating system. DOS and
Windows program the timer to interrupt every 55 ms. Windows XP uses 64 ms
(although it can be changed). Linux uses 10 ms.
Tim, are you sure about that XP interval? The normal XP timers (as
accessed from the API) have a resolution and minimum period of 10
msec, as opposed to the 55 msec of Win9x and earlier versions (and
DOS). That 55 msec (actually 54.9254 msec) is a full load (0 = 65536)
of the clock chip counting the 1.19318 MHz system clock, so no way to
get 64 msec from that... though I understand there are more advanced
timers in modern systems.

If 64 msec is correct, do you know the details of how they do it? And
why they do it, if the API is set up for 10 msec?

Best regards,


Bob Masta
dqatechATdaqartaDOTcom

D A Q A R T A
Data AcQuisition And Real-Time Analysis
www.daqarta.com
Home of DaqGen, the FREEWARE signal generator
Tim Roberts
2006-08-24 03:45:38 UTC
Permalink
Post by Bob Masta
...
Post by Tim Roberts
In fact, it's TOO precise for anything but a toy operating system. DOS and
Windows program the timer to interrupt every 55 ms. Windows XP uses 64 ms
(although it can be changed). Linux uses 10 ms.
Tim, are you sure about that XP interval?
Actually, it needs to have a lot of caveats. I have read messages about
systems where the number seemed to be 16ms (which is what I actually meant;
1/64 second, not 64ms), and some where it was 10ms. Plus, the number can
be changed (system-wide) if timeBeginPeriod is used in ANY process.
Post by Bob Masta
The normal XP timers (as accessed from the API) have a resolution and
minimum period of 10 msec,
Where do you see that? I don't think the documents ever say that. In
fact, with timeBeginPeriod, you can set it down to 1 ms.
--
- Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Bob Masta
2006-08-24 15:25:33 UTC
Permalink
Post by Tim Roberts
Post by Bob Masta
...
Post by Tim Roberts
In fact, it's TOO precise for anything but a toy operating system. DOS and
Windows program the timer to interrupt every 55 ms. Windows XP uses 64 ms
(although it can be changed). Linux uses 10 ms.
Tim, are you sure about that XP interval?
Actually, it needs to have a lot of caveats. I have read messages about
systems where the number seemed to be 16ms (which is what I actually meant;
1/64 second, not 64ms), and some where it was 10ms. Plus, the number can
be changed (system-wide) if timeBeginPeriod is used in ANY process.
Post by Bob Masta
The normal XP timers (as accessed from the API) have a resolution and
minimum period of 10 msec,
Where do you see that? I don't think the documents ever say that. In
fact, with timeBeginPeriod, you can set it down to 1 ms.
--
I was actually thinking about the SetTimer and WM_TIMER system,
not the Multimedia Timers that use timeBeginPeriod, etc. The 10 msec
value comes from Petzold "Programming Windows", 5th edition, page 329,
where he says that "In Windows 98, the timer has the same 55-msec
resolution as the underlying PC timer. In Microsoft Windows NT, the
resolution is about 10 msec." But it does seem rather curious that he
says "about" 10 msec, since he takes pains just before that to explain
the PC timer actual value of 54.925 msec in some detail. Maybe he
didn't know the actual value for NT, and just on casual observation
didn't distinguish 16 msec from 10 msec... both being a long ways
from 55 msec.

I'm guessing that this value, whatever it is, is not affected by
timeBeginPeriod... at least I have never heard of that. I was
actually a bit surprised at your statement that timeBeginPeriod
affects (multimedia?) timers system-wide. But I guess if all
multimedia timers are really just counting ints from one source,
(as the WM_TIMER system does) it would stand to reason that
they would all have the resolution of whichever needed the smallest
value. In that case, if one app changed the system-wide resolution,
all the timer countdown values would need to be changed to match.

I would have guessed that they would have chosen to fix the main
counter at 1 msec to avoid such shenanigans. Maybe they were
trying to avoid excessive timer service overhead on all the (possible)
timers throughout the system, unless absolutely needed?

Best regards,


Bob Masta
dqatechATdaqartaDOTcom

D A Q A R T A
Data AcQuisition And Real-Time Analysis
www.daqarta.com
Home of DaqGen, the FREEWARE signal generator
Nicolo Parenti
2006-08-24 15:51:16 UTC
Permalink
Post by Bob Masta
But it does seem rather curious that he
says "about" 10 msec, since he takes pains just before that to explain
the PC timer actual value of 54.925 msec in some detail. Maybe he
didn't know the actual value for NT, and just on casual observation
didn't distinguish 16 msec from 10 msec... both being a long ways
from 55 msec.
If you use the same HW timing device, the closest you can get is 10.000166+ :-)

The base frequency is 1193180 Hz, and the counter can range from 1 to 65535 (0=65536). 65536/1193180 gives 54.925+ mSec ticks. 11932/1193180 gives 10.000166+ mSec

220, 221... whatever it takes.
--
Jim Mack
MicroDexterity Inc
www.microdexterity.com
Jean-François Michaud
2006-09-01 16:19:10 UTC
Permalink
Post by Tim Roberts
Post by Jean-François Michaud
...
# We unmask interrupts 0 and 1 on the primary PIC
mov $0xFC, %al
out %al, $0x21
mov $0xFF, %al
out %al, $0xA1
A little extra: This last bit is for initializing the timer to emit an
interrupt just about every 0.0005 seconds. It's plenty precise for non
critical applications.
In fact, it's TOO precise for anything but a toy operating system. DOS and
Windows program the timer to interrupt every 55 ms. Windows XP uses 64 ms
(although it can be changed). Linux uses 10 ms.
Remember that you will want to run your scheduler and reevaluate task
priorities during that timer interrupt. As you reduce that timer
interval, you spend a larger and larger fraction of your time just doing
that kind of housekeeping.
I'm afraid not. I'm planning on using a cooperative scheduler. No silly
overhead on context save and restoration. No overhead on priority
calculation either.

Regards
Jean-Francois Michaud

Loading...