I used FASM Struct libs to demonstrate this and 16-bit assembly. This will also work for 32 bit assembly and it is possible to do this without struct libraries, which I show later.
Code:
use16
org 100h ;ew, DOS
include "MACRO\STRUCT.INC"
jmp start
struct Enemy ;new class/object
hp dd 22
name db 'Zombie$'
getName dw Enemy_getName ;You can put functions, variables, whatever you'd like!
size db 0
ends
Enemy_getName:
mov ax,bx ;We can assume bx is the instance pointer since function new returns it that way. Register ES may also be a good choice.
add ax,Enemy.name ;Enemy.name is an offset to the name variable, so add it
ret
enemy Enemy ;use this as your default instance for copying
start:
mov ax,enemy ;push the default instance
mov bx,Enemy.size ;push the STRUCT's .size
call new
;new returns the instance pointer in bx, so do what you need to do with it
call [bx+Enemy.getName] ;We could just directly get the pointer as well with mov ax,[bx+Enemy.name]
mov dx,ax ;we'll use a DOS interrupt to print the new enemy's name. mov dx,[bx+Enemy.name] would've been more efficient.
mov ah,09 ;DOS print-string
int 21h ;DOS interrupt. I usually don't use them, but it's for demonstration purposes
ret
new: ;makes a new instance of an object. Could also be used to clone an instance.
;ax - Instance/object to copy
;bx - Size of class
;returns: bx - pointer to instance
;This could be modified to use pushes instead of registers, but I made an interrupt that pointed to this function.
pusha
mov si,ax ;What to copy
mov ax,[new_obj_ptr]
mov di,ax ;Where to paste
mov cx,bx ;Size of object
rep movsb ;very easy to copy the object.
popa
push ax
mov ax,[new_obj_ptr]
push ax
add ax,bx
mov [new_obj_ptr],ax
pop ax
mov bx,ax
pop ax
ret
new_obj_ptr dw _objects ;This will point to where to copy a new instance
_objects:
;This space is reserved for instances. Do not add anything after this. Optionally define 0s so no other memory is loaded here
times 0x400 db 0 ;allocate 1,024 bytes for instances
Just a reminder that if you are using 32-bit assembly, you may have to adjust the registers accordingly. You need to change pointer-style variables to 4 bytes too.
This simply creates an instance of Enemy and shows it's name. I'm not sure if it's 100% working on DOS, but the instance part definitely is.
A limitation of this system is that you cannot use destructors. If you want to use destructors, you could make a space reserved for only one class, and then manage the memory that way.
If you don't have structs, you can use raw pointers, but it'll be very awkward and inconvenient to use. Here's an example:
Code:
define Enemy.hp 0 ;if periods don't work, use underscores
define Enemy.name 4
define Enemy.getName 11
define Enemy.size 13
jmp start
Enemy: ;default instance
dd 24
db 'Zombie$'
dw Enemy_getName
Enemy_getName: ;You can technically put it inside the object, but why calculate the size of that and have to copy it?
mov ax,bx
add ax,Enemy.name
ret
start:
mov ax,Enemy
mov bx,Enemy.size
call new
;And you can do what you want with it normally
ret
With some extra work, you can probably also include parent and child classes.
Hope you liked the tutorial