Last active
August 29, 2015 14:12
-
-
Save cr1901/72f4501921d55b3330b8 to your computer and use it in GitHub Desktop.
NASM Macros for Bare Metal/DOS x86 (8086-80386 PM)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[list -] | |
%ifndef GLOBAL_MACROS_ASM | |
%define GLOBAL_MACROS_ASM | |
;Macros to create a stack frame | |
%macro prolog 0.nolist | |
%ifidni CPU_TARGET, 8086 | |
push bp | |
mov bp, sp | |
%else | |
enter 0, 0 | |
%endif | |
%endmacro | |
%macro prolog 1.nolist | |
%ifidni CPU_TARGET, 8086 | |
push bp | |
mov bp, sp | |
sub sp, %1 | |
%else | |
enter %1, 0 | |
%endif | |
%endmacro | |
%macro epilog 0.nolist | |
%ifidni CPU_TARGET, 8086 | |
mov sp, bp | |
pop bp | |
%else | |
leave | |
%endif | |
%endmacro | |
;Macros to preserve registers | |
;Consider using strcat and pushing the save regs list, and popping it in multipop? | |
%macro multipush 1-*.nolist | |
%rep %0 | |
push %1 | |
%rotate 1 | |
%endrep | |
%endmacro | |
%macro multipop 1-*.nolist | |
%rep %0 | |
%rotate -1 | |
pop %1 | |
%endrep | |
%endmacro | |
;Macros to define locals and arguments (c_decl) | |
;First specify near or far call | |
;Pass in arguments from left-to-right. %defines will be created right-to-left | |
;(ascending stack depth- args near top of stack defined first, b/c the correct | |
;offsets for future params depend on the size of previous params). | |
; | |
;NASM's stacksize appears to not consider a pushed BP for large (although | |
;it does for others), and does not support near procedures. | |
;See http://www.nasm.us/doc/nasmdoc4.html#section-4.8.1 and preproc.c, case PP_ARG | |
%macro defargs 1-*.nolist | |
;%if (((%0 % 2) == 0) || (%0 == 1)) | |
; %error 'Expected an even number of parameters (3 or more).' | |
;%endif | |
;We can make these macros more general later... i.e. 386 and above | |
%ifidni %1, far | |
%assign bp_offset 6 | |
%assign num_params ((%0 - 1) / 2) | |
%elifidni %1, near | |
%assign bp_offset 4 | |
%assign num_params ((%0 - 1) / 2) | |
%else | |
%warning 'First parameter did not specify function call type. Assuming near.' | |
%assign bp_offset 4 | |
%assign num_params (%0 / 2) | |
%endif | |
%rotate 1 | |
%rep num_params | |
%xdefine %1 (bp_offset + bp) | |
%assign bp_offset bp_offset + %2 | |
%rotate 2 | |
%endrep | |
%undef bp_offset | |
%endmacro | |
%macro deflocals 1-*.nolist | |
%if ((%0 % 2) || (%0 == 0)) | |
%error 'Expected an even number of parameters (2 or more).' | |
%endif | |
%assign bp_offset 0 | |
%rep (%0 / 2) | |
%assign bp_offset bp_offset - %2 | |
%xdefine %1 (bp_offset + bp) | |
%rotate 2 | |
%endrep | |
%undef bp_offset | |
%endmacro | |
;Macros for dealing with pointer fun | |
%macro mkfptr 1.nolist | |
dw %1, seg %1 | |
%endmacro | |
%macro fptolin 1.nolist | |
(seg %1 << 4) + %1 | |
%endmacro | |
;Push far pointers | |
%macro pushfp 1.nolist | |
pushfp seg %1, %1 | |
%endmacro | |
%macro pushfp 2.nolist | |
mov dx, %1 | |
push dx | |
mov ax, %2 | |
push ax | |
%endmacro | |
;Macros for dealing with stack manipulation | |
;Allows any addressing mode compatible with mov AX... | |
%macro pushparamb 1.nolist | |
;%ifnum %1 | |
; %ifidni CPU_TARGET, 8086 | |
; mov ax, %1 | |
; push ax | |
; %else | |
; push %1 | |
; %endif | |
;%endif | |
xor ah, ah | |
mov al, %1 | |
push ax | |
%endmacro | |
;Treat doublewords as two word pushes (for now) | |
%macro pushparamw 1.nolist | |
;%ifnum %1 | |
; %ifidni CPU_TARGET, 8086 | |
; mov ax, %1 | |
; push ax | |
; %else | |
; push %1 | |
; %endif | |
;%endif | |
mov ax, %1 | |
push ax | |
%endmacro | |
;Useful if-else macro from NASM Manual. | |
%macro if 1.nolist | |
%push if | |
j%-1 %$ifnot | |
%endmacro | |
%macro else 0.nolist | |
%ifctx if | |
%repl else | |
jmp %$ifend | |
%$ifnot: | |
%else | |
%error "expected `if' before `else'" | |
%endif | |
%endmacro | |
%macro endif 0.nolist | |
%ifctx if | |
%$ifnot: | |
%pop | |
%elifctx else | |
%$ifend: | |
%pop | |
%else | |
%error "expected `if' or `else' before `endif'" | |
%endif | |
%endmacro | |
%macro print_msg 1.nolist | |
%ifidni API, MSDOS_API | |
mov ax, 900h | |
mov dx, %1 | |
int 21h | |
%endif | |
%endmacro | |
%macro pushsp 0.nolist ;8088-compatible | |
push bp | |
mov bp, sp | |
xchg bp, [bp] | |
%endmacro | |
;%macro do 0 | |
; %push do | |
; %$dotarget: | |
;%endmacro | |
; | |
;%macro while 1 | |
; %ifctx do | |
; %ifidni %1, cx | |
; loop %$dotarget | |
; %else | |
; loop%+1 %$dotarget | |
; %endif | |
; %pop | |
; %else | |
; | |
; | |
; %endif | |
;%endmacro | |
;%macro endw | |
;%macro syscall | |
; | |
;%endmacro | |
;Test code from: http://www.nasm.us/doc/nasmdoc4.html#section-4.8.1 | |
; %push mycontext ; save the current context | |
; %stacksize flat ;Vary this and run: nasm -E -Iinclude/ -DCPU_TARGET=8086 control\mouse.asm | |
; %arg i:word, j_ptr:word | |
; %assign %$localsize 0 ; see text for explanation | |
; %local k:word | |
; | |
; mov ax,[i] | |
; mov bx,[j_ptr] | |
; mov cx,[k] | |
; add ax,[bx] | |
; ret | |
; | |
; %pop ; restore original context | |
%endif | |
[list +] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%ifndef PREPROCESS | |
[list -] | |
%endif | |
%ifndef IBMPCIO_ASM | |
%define IBMPCIO_ASM | |
%macro make_addr 1 | |
%define %1(_x) 0 | |
%endmacro | |
%define bit(_x) (1 << _x) | |
%define bit(_x, _y) (_x << _y) | |
;Input arguments: | |
;1- base name of I/O address | |
;2- base I/O port | |
;Each subsequent pair- I/O reg name, number of bytes it takes. | |
%macro def_ports 1-* | |
%if ((%0 % 2) != 0 || (%0 < 4)) | |
%error 'Expected an even number (4 or more) of arguments.' | |
%endif | |
;Define some convenience macros | |
%assign num_params ((%0 - 2) / 2) | |
%define %1_BASE %2 | |
%define %1(_x) %1_BASE + %1_PORTS. %+ _x | |
;The latter will become PORTNAME(OFFSET), which is how to address | |
;an individual I/O port in this setup. | |
;Dynamically define a struct's members that defines the I/O ports | |
;for a specific piece of IBM PC hardware. | |
struc %1 %+ _PORTS | |
%rotate 2 | |
%rep num_params | |
. %+ %1 resb %2 | |
%rotate 2 | |
%endrep | |
endstruc | |
%endmacro | |
%macro def_const 3-* | |
%if ((%0 % 2) == 0 || (%0 < 3)) | |
%error 'Expected an odd number (3 or more) of arguments.' | |
%endif | |
%assign num_params ((%0 - 1) / 2) | |
%define base_name %1 | |
%rotate 1 | |
%rep num_params | |
%[base_name]_%1 equ %2 | |
%rotate 2 | |
%endrep | |
%endmacro | |
%macro const_group 2-* | |
%if ((%0 < 2)) | |
%error 'Expected 2 or more arguments.' | |
%endif | |
%assign num_params (%0 - 1) | |
%define base_bit %1 | |
%assign base_offset 0 | |
%rotate 1 | |
%rep num_params | |
%assign evaluated_expression (base_offset << base_bit) | |
%1 equ evaluated_expression | |
%assign base_offset base_offset + 1 | |
%rotate 1 | |
%endrep | |
%endmacro | |
;8237 DMA controller. | |
%define DMAREGS CH0ADDR, 0, CH4ADDR, 1, CH0WC, 0, CH4WC, 1, \ | |
CH1ADDR, 0, CH5ADDR, 1, CH1WC, 0, CH5WC, 1, \ | |
CH2ADDR, 0, CH6ADDR, 1, CH2WC, 0, CH6WC, 1, \ | |
CH3ADDR, 0, CH7ADDR, 1, CH3WC, 0, CH7WC, 1, \ | |
CMD, 0, STATUS, 1, REQ, 1, MASK, 1, MODE, 1, \ | |
PTRCLR, 1, MSTRCLR, 0, TEMP, 1, MASKCLR, 1, MASKALL, 1 | |
def_ports DMAC1, 0, DMAREGS | |
def_ports DMAC2, 0xC0, DMAREGS | |
;CMD Reg Constants | |
const_group 7, DACKLO, DACKHI | |
const_group 6, DREQHI, DREQLO | |
const_group 5, LATEWR, EXTWR | |
const_group 4, FIXPRI, ROTPRI | |
const_group 3, NORMTM, COMPTM | |
const_group 2, CNTLEN, CNTLDIS | |
const_group 1, HLDDIS, HLDEN | |
const_group 0, MEMDIS, MEMEN | |
;Mode Reg Constants | |
const_group 6, XFERDEM, XFERSNGL, XFERBLK, XFERCASC | |
const_group 5, ADDRINC, ADDRDEC | |
const_group 4, AUTODIS, AUTOEN | |
const_group 2, XFERVER, XFERWR, XFERRD | |
const_group 0, CH0SEL, CH1SEL, CH2SEL, CH3SEL | |
const_group 0, CH4SEL, CH5SEL, CH6SEL, CH7SEL | |
;Request Reg Constants | |
const_group 2, RSTREQ, SETREQ | |
;const_group 0, CH0SEL, CH1SEL, CH2SEL, CH3SEL- Shared w/ Mode Reg | |
;Mask Register Constants | |
const_group 2, RSTMASK, SETMASK | |
;const_group 0, CH0SEL, CH1SEL, CH2SEL, CH3SEL- Shared w/ Mode Reg | |
const_group 3, CLMSKCH3, STMSKCH3 ;Alternate Mask Register | |
const_group 2, CLMSKCH2, STMSKCH2 | |
const_group 1, CLMSKCH1, STMSKCH1 | |
const_group 0, CLMSKCH0, STMSKCH0 | |
;Status Register | |
CH3REQ equ bit(7) | |
CH2REQ equ bit(6) | |
CH1REQ equ bit(5) | |
CH0REQ equ bit(4) | |
CH3TC equ bit(3) | |
CH2TC equ bit(2) | |
CH1TC equ bit(1) | |
CH0TC equ bit(0) | |
;DMA Page register | |
def_ports PAGEREG, 0x81, CHAN2, 1, CHAN3, 1, CHAN1, 1 | |
;8259 PIC defines | |
%define PICREGS ICW1, 0, OCW2, 0, OCW3, 0, ISR, 0, IRR, 1, ICW2, 0, \ | |
ICW3, 0, ICW4, 0, OCW1, 1 | |
def_ports PIC1, 0x20, PICREGS | |
;ICW1 Constants | |
%xdefine ICW1MSK bit(4) | |
const_group 3, EDGETR, LVLTR | |
const_group 2, ADI8, ADI4 | |
const_group 1, CASCMD, SNGLMD | |
const_group 0, NOICW4, NEEDICW4 | |
;ICW2/ICW3/OCW1- No Constants needed | |
%xdefine IRQEN(_x) ~bit(_x) | |
;ICW4 | |
const_group 4, NOSFNM, SFNM | |
const_group 2, NONBUF, NONBUF2, BUFSLV, BUFMSTR | |
const_group 1, NORMEOI, AUTOEOI | |
const_group 0, MSC85MD, X86MD | |
;OCW2 | |
%xdefine OCW2MSK 0 | |
const_group 5, RAEOIC, EOI, EOINOP, SPEOI, RAEOIS, REOI, SETPRI, RSPEOI | |
;OCW3 | |
%xdefine OCW3MSK bit(3) | |
const_group 5, SMNOP, SMNOP2, SMSET, SMRST | |
const_group 2, NOPOLL, POLLCMD | |
const_group 0, RDPICNOP, RDPICNOP2, RDIRREG, RDISREG | |
;8253 PIT defines | |
def_ports PIT1, 0x40, CNT0R, 0, CNT0W, 1, CNT1R, 0, CNT1W, 1, \ | |
CNT2R, 0, CNT2W, 1, CTRL, 1 | |
const_group 6, CNT0SEL, CNT1SEL, CNT2SEL | |
const_group 4, LATCH, RDMSB, RDLSB, RDWORD | |
const_group 4, LDMSB, LDLSB, LDWORD | |
const_group 1, PITMD0, PITMD1, PITMD2, PITMD3, PITMD4, PITMD5 | |
const_group 0, BINCNT, BCDCNT | |
;8255 PPI defines | |
def_ports PPI, 0x60, PA0, 0, KEYCODE, 0, SWITCHES, 1, PB0, 1, PC0, 1, CTRL, 1 | |
const_group 7, BITSET, MODESET | |
const_group 5, PPIAMD0, PPIAMD1, PPIAMD2 | |
const_group 4, PAOUT, PAIN | |
const_group 3, PCUOUT, PCUIN | |
const_group 2, PPIBMD0, PPIBMD1 | |
const_group 1, PBOUT, PBIN | |
const_group 0, PCLOUT, PCLIN | |
def_const PPI, TIMER2_GATE, bit(0), SPEAKER_DATA, bit(1), IOCHAN, bit(5) | |
;The above defines the hardware on the mainboard. Below are extra useful defines | |
;for I/O. | |
;8250 UART defines | |
%define COM_REGS DIVL, 0, RXCHAR, 1, INTEN, 0, DIVH, 1, INTID, 1, LINECTL, 1, \ | |
MODEMCTL, 1, LINESTAT, 1, MODEMSTAT, 1, SCRATCH, 1 | |
def_ports COM1, 0x3F8, COM_REGS | |
def_ports COM2, 0x2F8, COM_REGS | |
def_ports COM3, 0x3E8, COM_REGS | |
def_ports COM4, 0x2E8, COM_REGS | |
;NEC 765 FDC defines | |
;Motorola 6845 CRTC defines | |
;LPT defines | |
;RTC defines (XT-class) | |
%undef bit | |
%endif | |
%ifndef PREPROCESS | |
[list +] | |
%endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%ifndef PM_TAB_ASM | |
%define PM_TAB_ASM | |
%define bit(_x) (1 << _x) | |
%define bit(_x, _y) (_x << _y) | |
struc special_descriptor | |
offset_l resw 1 | |
selector resw 1 | |
zero resb 1 | |
type_attr resb 1 | |
offset_2 resw 1 | |
; offset_1; // offset bits 0..15 | |
; selector; // a code segment selector in GDT or LDT | |
; uint8_t zero; // unused, set to 0 | |
; uint8_t type_attr; // type and attributes, see below | |
; uint16_t offset_2; // offset bits 16..31 | |
endstruc | |
%xdefine present bit(7) | |
%xdefine dpl_ring0 0 | |
%xdefine dpl_ring1 bit(1, 5) | |
%xdefine dpl_ring2 bit(2, 5) | |
%xdefine dpl_ring3 bit(3, 5) | |
%xdefine seg_desc bit(1, 4) | |
%xdefine special 0 | |
%xdefine busy_tss 3 | |
%xdefine ldt_desc 2 | |
%xdefine available_tss 1 | |
%xdefine accessed_desc 1 | |
struc data_descriptor | |
.limit: resw 1 | |
.base_lo: resw 1 | |
.base_hi: resb 1 | |
.access: resb 1 | |
%ifidni CPU_TARGET, 286 | |
.reserved: resw 1 | |
%endif | |
endstruc | |
%macro create_data_descriptor 4-5.nolist | |
istruc data_descriptor | |
at data_descriptor.limit, dw %1 | |
at data_descriptor.base_lo, dw %2 | |
at data_descriptor.base_hi, db %3 | |
at data_descriptor.access, db %4 | |
%ifidni CPU_TARGET, 286 | |
at data_descriptor.reserved, dw 0 | |
%endif | |
iend | |
%endmacro | |
%xdefine exec bit(1,3) | |
%xdefine noexec 0 | |
%xdefine conform bit(1,2) | |
%xdefine nonconform 0 | |
%xdefine expand_down bit(1,2) | |
%xdefine expand_up 0 | |
%xdefine readable bit(1,1) | |
%xdefine noread 0 | |
%xdefine writeable bit(1,1) | |
%xdefine nowrite 0 | |
struc gdtr_datatype | |
.limit: resw 1 | |
.base_lo: resw 1 | |
.base_hi: resb 1 | |
.reserved: resb 1 | |
endstruc | |
;Good lord, istruc is a PITA to use... macroize it to reduce errors | |
%macro create_gdtr_datatype 4.nolist | |
istruc gdtr_datatype ;Called GDT Datatype in 286 manual, Figure 10.l3 | |
at gdtr_datatype.limit, dw %1 ;256/8 = 32 descriptors (31 usable) | |
at gdtr_datatype.base_lo, dw %2 ;I do not know ahead of time the linear address | |
at gdtr_datatype.base_hi, db %3 ;that will be loaded into GDTR | |
at gdtr_datatype.reserved, db %4 | |
iend | |
%endmacro | |
;Error code format | |
%xdefine use_ldt bit(2) | |
%xdefine use_gdt 0 | |
%xdefine use_idt bit(1) | |
%xdefine external 1 | |
%xdefine internal 0 | |
%define create_selector(index, ti, rpl) (index << 3) + (ti << 2) + rpl | |
%undef bit | |
%endif | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment