Skip to content

Instantly share code, notes, and snippets.

@cr1901
Last active August 29, 2015 14:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cr1901/72f4501921d55b3330b8 to your computer and use it in GitHub Desktop.
Save cr1901/72f4501921d55b3330b8 to your computer and use it in GitHub Desktop.
NASM Macros for Bare Metal/DOS x86 (8086-80386 PM)
[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 +]
%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
%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