Форум программистов, компьютерный форум, киберфорум
Assembler, MASM, TASM
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.82/825: Рейтинг темы: голосов - 825, средняя оценка - 4.82
 Аватар для Viacheslav78
270 / 18 / 0
Регистрация: 16.02.2016
Сообщений: 29
20.05.2016, 19:56
Студворк — интернет-сервис помощи студентам
LLVM

Arash Shahkar. ON MATCHING BINARY TO SOURCE CODE
Arjun Shah. Metamorphic Code Generator based on bytecode of LLVM IR
Christoph Erhardt. Design and Implementation of a TriCore Backend for the LLVM Compiler Framework
Daniele Cono D’Elia. Flexible On-Stack Replacement in LLVM
David Menendez. Termination-Checking for LLVM Peephole Optimizations
Eric Schkufza. STOCHASTIC PROGRAM OPTIMIZATION FOR x86 64 BINARIES
Federico Scrinzi. Behavioral Analysis of Obfuscated Code
Johan Fänge. A Tracing JIT Compiler for Erlang
Jozef Cel’uch. Java bytecode disassembler
Linhai Song. Understanding, Detecting, and Diagnosing Real-World Performance Bugs
Pooja Gupta. LLVM Backend Development for ReISC Architecture
Roel Jordans. High-level software-pipelining in LLVM
Simon Andreas Frimann Lund. A High Performance Backend for Array-Oriented Programming on Next-Generation Processing Units
Tilmann Scheller. Where is LLVM being used today

PE file & malware analysis

Alan Martin Sweeney. Malware Analysis & Antivirus Signature Creation
Blake Harrell Anderson. Integrating Multiple Data Views for Improved Malware Analysis
Christian Rossow. Using Malware Analysis to Evaluate Botnet Resilience
Christian Schönbein. PyBox - A Python Sandbox
Christopher B. Harrison. ODinn. An In-Vivo Hypervisor-based Intrusion Detection System for the Cloud
Christos Papadiotis. Enhancing AV-avoidance capabilities of a PE crypter
Dalbir Kaur R. Chhabra. Feature selection and clustering for malicious and benign software characterization
Dan Sinema. AUTOMATED REVERSE ENGINEERING OF MALWARE TO DEVELOP NETWORK SIGNATURES TO MATCH WITH KNOWN NETWORK SIGNATURES
Dimitrios A. Glynos. Packing Heat!
Fadel Omar Shaban. Spyware Detection Using Data Mining for Windows Portable Executable Files
Igor Santos, Xabier Ugarte-Pedrero Felix Brezo, Pablo G. Bringas. NOA. AN INFORMATION RETRIEVAL BASED MALWARE DETECTION SYSTEM
Jani Hakkarainen. Malware Analysis Environment for Windows Targeted Malware
Jarno Niemela. Statistical Analysis Of Malware Defence Methods
Jay-Evan J. Tevis. AUTOMATIC DETECTION OF SOFTWARE SECURITY VULNERABILITIES IN EXECUTABLE PROGRAM FILES
Jiyong Jang. Scaling Software Security Analysis to Millions of Malicious Programs and Billions of Lines of Code
Jonathan Joseph Blount. Adaptive rule-based malware detection employing learning classifier systems
Katja Hahn. Robust Static Analysis of Portable Executable Malware
Lutz Bohne. Pandora’s Bochs. Automatic Unpacking of Malware
MICHAEL STEPHEN THIEMS. OPTIMIZATION AND EXECUTABLE REGENERATION IN THE IMPACT BINARY REOPTIMIZATION FRAMEWORK
Neel Bavishi. An Executable Packer
Nicholas S. Kovach. Accelerating Malware Detection via a Graphics Processing Unit
PAN-MING. Research on Detecting Mechanism for Trojan horse Based on PE file
Peter Ekstrand Berg. Behavior-based Classification of Botnet Malware
Raja M. Khurram Shahzad. Syed Imran Haider. Detection of Spyware by Mining Executable Files
Ronny Merkel, Tobias Hoppe, Christian Kraetzer, Jana Dittmann. Statistical Detection of Malicious PE-Executables Fast Offline Analysisfor
Shaked Bar. Kuluoz. Malware and botnet analysis
Soon Chai Liang. Understanding behavioural detection of antivirus
Steven Strandlund Hansen. Thor Mark Tampus Larsen. Dynamic Malware Analysis. Detection and Family Classification using Machine Learning
Sverker Nilsson. Heapy. A Memory Profiler and Debugger for Python
Vasileios Pappas. Defending against Return-Oriented Programming
Vishwath R. Mohan. SOURCE-FREE BINARY MUTATION FOR OFFENSE AND DEFENSE
Yaser Alosefer. Analysing Web-based Malware Behaviour through Client Honeypots
Zhiqiang Lin. Reverse Engineering of Data Structures from Binary
2
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
20.05.2016, 19:56
Ответы с готовыми решениями:

Глоссарий для раздела Assembler, MASM, TASM
Thread, AFP, Charles Kludge, 6a6kin, Убежденный, Ethereal, выкладывайте свои глоссарии - разложу ваши сообщения по алфавиту, если найдете...

Организация тем в разделе Assembler, MASM, TASM
Всем привет! Друзья, возник вопрос: как лучше организовать темы в разделе Assembler, MASM, TASM? Какие ветки-подразделы сделали бы...

Полезные макросы для MASM и TASM
Не претендую на создание чего-то нового и гениального, но макросы довольно полезные. Часть из того, что я сделал, уже делали другие. Тем не...

65
 Аватар для Viacheslav78
270 / 18 / 0
Регистрация: 16.02.2016
Сообщений: 29
13.08.2016, 22:12
Offense & Defense:

Ankita Kapratwar. Static and Dynamic Analysis for Android Malware Detection
Babar, Sachin D. Security Framework and Jamming Detection for Internet of Things
Chiem Trieu Phong. A Study of Penetration Testing Tools and Approaches
CRAIG LEONARD TIDWELL. TESTING THE IMPACT OF TRAINING WITH SIMULATED SCENARIOS FOR INFORMATION SECURITY AWARENESS ON VIRTUAL COMMUNITY OF PRACTICE MEMBERS
Daniel Lowry Lough. A TAXONOMY OF COMPUTER ATTACKS WITH APPLICATIONS TO WIRELESS NETWORKS
Eugene H. Spafford. Computer Viruses as Artificial Life
Fred Cohen. Computer Viruses
Gencer Erdogan. Security Testing of Web Based Applications
Jarmo Puttonen. Virtual learning environment for penetration testing
JOHN SHADRACH STAFFORD. BEHAVIOR-BASED WORM DETECTION
Joshua David Lind. Betting on the Bitcoin Blockchain
Le Wang. Detection of Man-in-the-middle Attacks Using Physical Layer Wireless Security Techniques
Mahmood Nazari. Lokesh Galla. Denial of Service attack in IPv6 networks and counter measurements
MANUEL RODRIGUES CORREGEDOR. UTILIZING ROOTKITS TO ADDRESS THE VULNERABILITIES EXPLOITED BY MALWARE
Marco Ramilli. A Design Methodology for Computer Security Testing
Matteo Pomilia. A study on obfuscation techniques for Android malware
Matthias Rene Buchler. Semi-Automatic Security Testing of Web Applications with Fault Models and Properties
Miller, Bradley Austin. Scalable Platform for Malicious Content Detection Integrating Machine Learning and Manual Review
Nishant Shrestha. Security Assessment via Penetration Testing. A Network and System Administrator’s Approach
Pasquale Stirparo. MOBILEAK. SECURITY AND PRIVACY OF PERSONAL DATA IN MOBILE APPLICATIONS
Penetration Testing Guidance
Petter Svenhard & Amir Radaslic. A penetration test of an Internet service provider
Ravindar Reddy Ravula. CLASSIFICATION OF MALWARE USING REVERSE ENGINEERING AND DATAMINING TECHNIQUES
Ronny L. Bull. A Critical Analysis of Layer 2 Network Security in Virtualized Environments
Saied, Alan. Distributed Denial of Service (DDoS) attack detection and mitigation
Salman Javaid. Analysis and Detection of Heap-based Malwares Using Introspection in a Virtualized Environment
Sergio Pastrana Portillo. Attacks Against Intrusion Detection. Networks. Evasion, Reverse Engineering and Optimal Countermeasures
Susanne Vernersson. Penetration Testing in a Web Application Environment
Terry L. Nelms. IMPROVING DETECTION AND ANNOTATION OF MALWARE DOWNLOADS AND INFECTIONS THROUGH DEEP PACKET INSPECTION
THOMAS-QUENTIN MAILLART. MECHANISMS OF INTERNET EVOLUTION & CYBER RISK
Timothy D. Williams. The Value of Threat Models in Enterprise Security Testing of Database Systems & Services
Ulf Lindqvist. On the Fundamentals of Analysis and Detection of Computer Misuse
Vinod. Korrapati. Security Testing on Endpoint System
Vishwath R. Mohan. SOURCE-FREE BINARY MUTATION FOR OFFENSE AND DEFENSE
Wesley Jin. Practical, Large-Scale Detection of Obfuscated Malware Code Via Flow Dependency Indexing

Honeypot:

Albert W. Brzeczko, Jr. SCALABLE FRAMEWORK FOR TURN-KEY HONEYNET DEPLOYMENT
Amit D. Lakhani. Deception Techniques Using Honeypots
Åsmund Nergård Nyre. Increasing Survivability by Dynamic Deployment of Honeypots
Bassam Alkudhir, Eleftherios Chairetakis & Panagiotis Mystridis. Deployment of Low Interaction Honeypots in University Campus Network
Benoit Jacob. Automatic XSS detection and Snort signatures-ACLs generation by the means of a cloud-based honeypot system
Bertrand Sobesto. EMPIRICAL STUDIES BASED ON HONEYPOTS FOR CHARACTERIZING ATTACKERS BEHAVIOR
Brandon Scott Miller. Analysis of Attacks on Web Based Applications
Brian M. Bowen. Design and Analysis of Decoy Systems for Computer Security
Christian Döring. Improving network security with Honeypots
Christian Seifert. Cost-effective Detection of Drive-by-Download Attacks with Hybrid Client Honeypots
Corrado Leita, Marc Dacier. SGNET. a worldwide deployable framework to support the analysis of malware threat models
DARATZI PARASKEVI. ANALYSIS AND TESTING OF HONEYPOTS HONEYNET SYSTEMS
Davide BALZAROTTI. Plusieurs Axes d’Analyse de sites web compromis et malicieux
Erwin E. Frederick. TESTING A LOW-INTERACTION HONEYPOT AGAINST LIVE CYBER ATTACKERS
Georgios Portokalidis. Using Virtualisation to Protect Against Zero-Day Attacks
Gerard WAGENER. Self-Adaptive Honeypots Coercing and Assessing Attacker Behaviour
Gurdip Kaur. Jatinder Singh Saini. Implementation of High Interaction Honeypot to Analyze the Network Traffic and Prevention of Attacks on Protocol-Port Basis
Honeypots. A Force Multiplier in Educational Domain
HONEYPOT SECURITY.pdf
Honeypots in Network Security.pdf
JAMES JOSEPH YUILL. DEFENSIVE COMPUTER-SECURITY DECEPTION OPERATIONS. PROCESSES, PRINCIPLES AND TECHNIQUES
Jan Gobel. Amun. A Python Honeypot Technical Report
Jarmo (J.M.) van Lenthe. Combining Multiple Malware Detection Approaches for Achieving Higher Accuracy
John Børge Holen-Tjelta. Honeypots in network perimeter defense systems
JUNEWON PARK. Acquiring Digital Evidence from Botnet Attacks. Procedures and Methods
KAMALDEEP SEHGAL. Study on Web application Honey pots
Lucas Tamagna-Darr. Evaluating the effectiveness of an intrusion prevention - Honeypot hybrid
Mathias Gibbens. Harsha vardhan Rajendran. Honeypots
Michael Nørholt Petersen. Detecting network intrusions
Mr. Susheel George Joseph M.C.A, M.Tech, M.Phil(CS). Advanced Honeypot Architecture for Network Threats Quantification
Ng.Jun Ping. Enchancing Honeypot Stealthiness
Ondřej Koutský. Monitoring and Analysis of Cyber Attacks
Radhika Goel, Anjali Sardana, and R. C. Joshi. Wireless Honeypot. Framework, Architectures and Tools
Ratinder Kaur and Maninder Singh. A Hybrid Real-time Zero-day Attack Detection and Analysis System
Robin Berthier. Advanced Honeypot Architecture for Network Threats Quantification
Ronald Mitchell Campbell. The Legal and Ethical Issues of Deploying Honeypots
Saleh Ibrahim Bakr Almotairi. Using Honeypots to Analyse Anomalous Internet Activities
Sanjeev Kumar, Rakesh Sehgal, Paramdeep Singh. Nepenthes Honeypotsbased Botnet Detection
Simon Bell. Building a Honeypot to Research Cyber-Attack Techniques
Susan Marie Wade. SCADA Honeynets The attractiveness of honeypots as critical infrastructure security tools for the detection and analysis of advanced threats
Vusal Aliyev. Using honeypots to study skill level of attackers based on the exploited vulnerabilities in the network
Yaser Alosefer. Analysing Web-based Malware Behaviour through Client Honeypots
Yogendra Kumar Jain. Honeypot based Secure Network System
YONG TANG. DEFENDING AGAINST INTERNET WORMS

Honeytrap:

Abhishek Sharma. HONEYPOTS IN NETWORK SECURITY
Adam Schoeman. Amber. A Zero-Interaction Honeypot with Distributed Intelligence
Aditya K Sood. Rohit Bansal. Reverse Honey Trap. Striking Deep inside Online Web Antivirus Engines and Analyzers
Alec Yasinsac. Yanet Manzano. Honeytraps, A Network Forensic Tool
Andreas Pitsillidis. Spam Value Chain. Defensive Intervention Analysis
Ashwini Gabhane. Honey Trap Security Server. An Efficient Approach of Securing E-Banking Network
Chih-Hung Lin and Chung-Huang Yang. IMPLEMENTATION OF NETWORK FORENSICS BASED ON HONEYPOT
Daisuke Miyamoto. INTERCEPT. High-interaction Server-type Honeypot based on Live Migration
David J. Malan. Rapid Detection of Botnets through Collaborative Networks of Peers
DAVID MALANIK, LUKAS KOURIL. Honeypot as the Intruder Detection System
David Watson. Jamie Riden. The Honeynet Project. Data Collection Tools, Infrastructure, Archives and Analysis
Émerson Salvadori Virti. Honeypots as a Security Mechanism
Feng Zhang. Honeypot. А Supplemented Active Defense System for Network Security
Hrishikesh Arun Deshpande. HoneyMesh. Preventing Distributed Denial of Service Attacks using Virtualized Honeypots
Hu Jingfang. Li Busheng. The Application Research on Network Forensics
John E. Clark, Christopher P. Lee, Roshan Menon, Vanessa Rood. HoneyTrap Visualization for Monitoring Honeynets
JOHN SHADRACH STAFFORD. BEHAVIOR-BASED WORM DETECTION
Lai-Ming Shiue. Shang-Juh Kao. Countermeasure for Detection of Honeypot Deployment
M. Dornseif, F.C. Gärtner, and T. Holz. Vulnerability Assessment using Honepots
Matthijs Koot. Intrusion Detection Systems
Michael Eugene Sanders. Unknown Threat Detection With Honeypot Ensemble Analsyis Using Big Datasecurity Architecture
Mitsuaki Akiyama. Study on High Interaction Client Honeypot for Infiltrative Intrusion Detection
Nathalie Weiler. Honeypots for Distributed Denial of Service Attacks
NIHARIKA. RANJEET KAUR. HONEYPOTS FOR NETWORK SURVEILLANCE
Sebastian Poeplau. Jan Gassen. A Honeypot for Arbitrary Malware on USB Storage Devices
Web Based Security - Using Honeypots.pdf
Yousef T. Aburabie & Mohmed Omar Yousef T. Aburabie & Mohmed Omari. Network Forensics Tool “Honeytrap Project Honeytrap Project

Virustraps, Spamtrap:

10 Basics to Avoid Being Categorized as Spam.pdf
A Practitioner's Guide to Spam Traps.pdf
Cristian Lumezanu. Nick Feamster. Observing Common Spam in Tweets and Email.pdf
Demystifying the Anti-Spam Buzz.pdf
In Spam Blocklists and Volume.
Jaeyeon Jung. An Empirical Study of Spam Traffic and the Use of DNS Black Lists.pdf
McBoost. Boosting Scalability in Malware Collection and Analysis Using Statistical Classification of Executables.pdf
MDaemon configuration recommendations for dealing with spam related issues.pdf
Messaging, Malware and Mobile Anti-Abuse Working Group.pdf
Mind Your Blocks.pdf
Numaan Huq & Peter Szabo. TRAPPING UNKNOWN MALWARE IN A CONTEXT WEB
Oliver Hohlfeld. Longtime Behavior of Harvesting Spam Bots
Peter Haider. Bayesian Clustering for Email Campaign Detection
Spam, Real World Numbers and the Value of IP Reputation Lists.pdf
SPAM TRAPS AND HONEY POTS. DEFINITION, PREVENTION, AND ELIMINATION.pdf
The Ultimate Guide to Email Deliverablility.pdf
Trap_a_Bot_for_Fun_and_Profit_
Viruses Trojans and Spam.pdf
warning-signs-that-your-client
What-You-Need-to-Know-about-Sp
1
Модератор
Эксперт по электронике
 Аватар для ФедосеевПавел
8673 / 4510 / 1670
Регистрация: 01.02.2015
Сообщений: 13,939
Записей в блоге: 13
22.01.2017, 20:21
Кратко о работе с мышью в DOS с помощью сервисного прерывания int 33h

на примере учебного задания

Манипулятор "мышь" может физически подключаться к одному из портов COM, PS/2 или USB. В каждом из этих случаев работа на "нижнем" уровне будет очень сильно различаться. Кроме того, для разных "мышей" различаться может даже формат обмена данными. Поэтому, ещё в эпоху DOS разработчики постарались упростить работу программистам, в части взаимодействия пользовательской программы с мышью (её драйвером). Это выразилось созданием в драйвере почти унифицированного пользовательского интерфейса. Взаимодействие программы с драйвером реализовано через сервисное прерывание int 33h.

Таким образом, вся работа с мышью, свелась к конфигурированию мыши в начале программы, после которого взаимодействие происходит без (ну или почти без) дальнейшего участия программиста.

Я отдаю себе отчёт, что на форуме будут находится студенты, а не разработчики, поэтому обзор будет очень упрощённым и кратким.

Источниками для более подробного изучения работы с мышью могут послужить материалы изВзаимодействие с драйвером происходит через пользовательское прерывание int 33h. Для этого в регистр ax загружают номер вызываемой функции, при необходимости заполняют соответствующие регистры и вызывают int 33h. Как видно из описания Ральфа Брауна, некоторые функции стандартны, а некоторые присущи конкретным драйверам. К примеру, в исходниках и в документации CuteMouse видно, что данный драйвер поддерживает функции от 00h до 21h.

Итак. Я привык к разработке сверху вниз, поэтому и пояснения буду приводить в этом порядке. Не вижу смысла в перепечатывании справочной информации по функциям int 33h.

Возьму для обозначения цели задачу Управление манипулятором мышь - изменение формы курсора в зависимости от координат.
Составить программу по управлению манипулятором «мышь». Если координаты курсора «мышь» лежат в диапазоне 0<x<300 и 0<y<100 , то курсор мыши имеет форму «+» в противном случае – «0».
Из основной программы нужно уточнить возможность работы с мышью через int 33h. Вызываем функцию MouseInit (AX=0000h), которая в регистре AX вернёт результат инициализации - успех или неудача (отсутствие самой мыши или не установлен драйвер). Естественно, если нет возможности работы с мышью, то нет нужды её конфигурировать и дальше.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
        ;попытка инициализации мыши
        call    MouseInit
        cmp     ax,     MouseInitOk
        je      @@MouseInitOk
        ;если инициализация не удалась - вывести сообщение
        ;и завершить программу
        mov     ah,     09h
        lea     dx,     msgMouseFault
        int     21h
        jmp     @@Exit
 
@@MouseInitOk:
После подтверждения возможности работы с мышью, пробуем её сконфигурировать. Настроек не много:
  • Настройка ограничения перемещения указателя (функции AX=0007h, AX=0008h).
  • Настройка формы курсора в графическом режиме, атрибутов курсора в текстовом режиме (функции AX=0009h, AX=000Ah).
Далее можно работать с мышкой просто опрашивая её состояние (позицию, нажатия кнопок) в рабочем цикле программы. Но это неудобно. Поэтому определяют процедуру пользовательского обработчика событий мыши (перемещение, нажатия кнопок). Эту процедуру будет вызывать драйвер при возникновении необходимости.
При установке обработчика указывается условие, когда он будет вызываться драйвером. Условий раз-два и обчёлся - перемещение, нажатие или отпускание одной из кнопок. Условия можно совмещать - к примеру, нажатие левой кнопки и отпускание правой
Assembler
1
2
3
4
5
6
7
8
        ;установить пользовательский обработчик событий "мыши"
        push    es
        mov     ax,     seg MyMouseHandler
        mov     es,     ax
        mov     dx,     offset  MyMouseHandler
        mov     cx,     MouseEvent_PressLeftButton OR MouseEvent_ReleaseRightButton
        call    MouseSetHandler
        pop     es
Сам обработчик опишу позже, т.к. сейчас идёт описание основной программы.

Вызываем функцию "показать курсор" (AX=0001h). С этого момента на экране виден курсор мыши.

Собственно настройку можно считать завершённой. Далее выполняется основная часть программы.
Единственно, нужно сделать примечание по поводу дополнительных манипуляций с мышью на этом этапе.
Функцию "спрятать курсор" (AX=0002h) рекомендуется вызывать каждый раз перед тем, как произвести какие-либо изменения на том участке экрана, где находится курсор. Этим Вы избежите проблем, возникающих из-за взаимодействия курсора с данными на экране. После завершения изменений вызывается функция "показать курсор" и работа продолжается.

После завершения основной части программы следует выполнить действия по завершению работы с мышью. Этих действий немного: спрятать курсор и удалить пользовательский обработчик прерывания.
Assembler
1
2
3
4
5
6
        ;завершение работы с "мышью"
        ;спрятать курсор
        call    MouseHideCursor
        ;удалить пользовательский обработчик прерывания
        mov     cx,     MouseEvent_None
        call    MouseSetHandler
Вот собственно и вся работа с мышью из основной программы.

Теперь осталось пояснить работу пользовательского обработчика. Ведь именно в нём выполнялась основная часть работы по учебному заданию (смена формы курсора в зависимости от его позиции).
Собственный обработчик событий мыши - процедура с "дальним вызовом", поэтому завершается командой retf. На вход процедуре драйвером передаются в регистрах следующие данные
Code
1
2
3
4
5
6
АХ содержит условие вызова,
ВХ - состояние кнопок,
СХ, DX - X- и Y-координаты курсора,
SI, DI - счетчики последнего перемещения по горизонтали и вертикали
      (единицы измерения для этих счетчиков - мики, 1/200 дюйма),
DS - сегмент данных драйвера мыши.
Т.е. всё, что можно опрашивать в "рукопашном" режиме, поступает в обработчик сразу при возникновении события.

Возвращаемые координаты совпадают с пиксельными координатами соответствующей точки на экране в большинстве графических режимов, кроме 04, 05, 0Dh, 13h, в которых Х-координату мыши нужно разделить на 2, чтобы получить номер столбца соответствующей точки на экране. В текстовых режимах обе координаты надо разделить на 8, чтобы получить номер строки и столбца соответственно.

Ещё нужно отметить, что сегмент данных указывает не на пользовательские данные, а на сегмент данных драйвера, поэтому потребуются некоторые манипуляции, чтобы "дотянуться" к переменным своей программы.

Для выполнения поставленной учебной задачи нужно сверить границы регионов и в зависимости от номера региона изменить форму курсора. По содержимому CX и DX выбираем текущий регион. Далее, для каждого региона по значению переменной с номером текущей формы курсора оценивается, требуется ли изменение формы, или форма курсора соответствует региону. При необходимости - вызывается функция изменения формы курсора.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
;Обработчик событий "мыши"
;вход
;АХ содержит условие вызова,
;ВХ - состояние кнопок,
;СХ, DX - X- и Y-координаты курсора,
;SI, DI - счетчики последнего перемещения по горизонтали и вертикали
;      (единицы измерения для этих счетчиков - мики, 1/200 дюйма),
;DS - сегмент данных драйвера мыши.
;выход
; - нет
MyMouseHandler  proc
        push    ax
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    es
 
        ;загрузка в ax текущего номера курсора
        mov     ax,     seg MouseCurrentCursor
        mov     es,     ax
        mov     si,     offset MouseCurrentCursor
        mov     ax,     es:[si]
 
        ;выяснение, в каком регионе находится курсор
        shr     cx,     1       ;для графического видеорежима 13h нужно разделить на 2 координату X курсора
        cmp     cx,     0
        jb      @@SecondRegion
        cmp     cx,     300
        ja      @@SecondRegion
        cmp     dx,     0
        jb      @@SecondRegion
        cmp     dx,     100
        ja      @@SecondRegion
 
@@FirstRegion:
        ;для первого региона:
        ;если текущий курсор равен курсору для первого региона, то выйти
        ;иначе установить требуемый курсор и выйти
        cmp     ax,     0001h
        je      @@Exit
        call    MouseCursorCross
        mov     MouseCurrentCursor,     0001h
        jmp     @@Exit
 
@@SecondRegion:
        ;для второго региона:
        ;если текущий курсор равен курсору для второго региона, то выйти
        ;иначе установить требуемый курсор и выйти
        cmp     ax,     0002h
        je      @@Exit
        call    MouseCursorRectangle
        mov     MouseCurrentCursor,     0002h
        jmp     @@Exit
 
@@Exit:
        pop     es
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        retf
MyMouseHandler  endp
Вот собственно и всё.

Целиком учебная программа приведена в теме Управление манипулятором мышь - изменение формы курсора в зависимости от координат.
В примечании R71MT очень хорошо видна форма курсора "крест" при представлении масок в двоичном коде. Стоит обратить внимание на такой способ представления формы курсора, т.к. она позволяет создавать и редактировать сразу в коде.
2
Модератор
Эксперт по электронике
 Аватар для ФедосеевПавел
8673 / 4510 / 1670
Регистрация: 01.02.2015
Сообщений: 13,939
Записей в блоге: 13
11.11.2018, 17:46
Рисование окружности в графических режимах DOS

Введение

Для рисования окружности в графических режимах DOS применяют несколько алгоритмов:
1. Построение по параметрическому уравнению окружности (с использованием FPU)
2. Построение окружности с применением алгоритмов растеризации:
2.1 Midpoint circle algorithm
2.2 Алгоритм Брезенхэма
2.3 Алгоритм Мичнера
2.4 Модификации алгоритмов растеризации

Построение по параметрическому уравнению окружности (с использованием FPU)

Построение основано на вычислениях параметрического уравнения окружности
https://www.cyberforum.ru/cgi-bin/latex.cgi?\left\{\begin{matrix}x={X}_{c}+R\cdot cos(\alpha )\\ y={Y}_{c}+R\cdot sin(\alpha )\end{matrix}\right.
где
x, y – координаты точки окружности,
R – радиус окружности,
https://www.cyberforum.ru/cgi-bin/latex.cgi?{X}_{c}, https://www.cyberforum.ru/cgi-bin/latex.cgi?{Y}_{c} — координаты центра окружности.
Изменяя параметр https://www.cyberforum.ru/cgi-bin/latex.cgi?\alpha от 0 до https://www.cyberforum.ru/cgi-bin/latex.cgi?2\pi, вычисляя координаты (x, y) каждой точки и устанавливая её на экране, получим окружность. Т.е. задача сводится к табуляции двух функций от одной переменной.

Примечания к реализации.
1. Выбор шага приращения параметра, чтобы и скорость была высокой (за счёт пропуска сливающихся на экране точек) и окружность не имела разрывов между точками. Очевидно, что при построении окружности на экране будут установлены точки в количестве длины этой окружности, т.е. https://www.cyberforum.ru/cgi-bin/latex.cgi?2\pi R. Что означает достаточным приращение аргумента
https://www.cyberforum.ru/cgi-bin/latex.cgi?\Delta =\frac{2\pi}{2\pi R}=\frac{1}{R}

2. Некоторого ускорения можно добиться, вычисляя уравнение не для полной окружности, а для 1/4 или даже для 1/8. При этом координаты невычисленных по уравнению точек находятся из соображений симметричности их расположения относительно центра окружности.

3. Т.к. алгоритмические возможности улучшения уже исчерпаны, остаётся лишь оптимизация на уровне процессора — минимизация обращений к памяти и прочие трюки на вкус программиста. Совсем избавится от обращений к памяти не удастся из-за отсутствия прямого обмена между регистрами CPU и FPU – координаты будут передаваться в CPU через память.

4. Этот алгоритм имеет достоинство в явной возможности коррекции сжатия изображения, вызванной прямоугольной формой точек экрана. Зная соотношения сторон экрана (4:3, 16:9) и разрешение у каждого конкретного графического видео режима, можно ввести поправочные множители в параметрическое уравнение. При этом, строго математически, на экране будет рисоваться эллипс, но визуально он будет смотреться как окружность.
https://www.cyberforum.ru/cgi-bin/latex.cgi?\left\{\begin{matrix}x={X}_{c}+{K}_{x}\cdot R\cdot cos(\alpha ) \\ y={Y}_{c}+{K}_{y}\cdot R\cdot sin(\alpha )\end{matrix}\right.

5. К недостатку метода можно отнести его невысокую скорость относительно методов растеризации. Это заметно при попытке перемещения окружности по экрану - стирание на старом месте и рисование на новом месте. Построение при помощи FPU заметно медленнее.

Алгоритм множество раз реализовывался в различных темах форума. Ряд реализаций, разной степени «навороченности» можно посмотреть по ссылкам
Рисование спиралей и окружностей (некорректная отрисовка окружностей)
Вычисление выражение и прорисовка ответа в овале
Движение двух окружностей
https://www.cyberforum.ru/post9799088.html

Также, реализации этого метода можно встретить в учебниках ассемблера среди примеров использования FPU. Например, в ряде ответов я использовал исходник из книги
Рудаков П.И., Финогенов К.Г. программируем на языке ассемблера IBM PC - Изд. 2-е. - Обнинск: Издательство "Принтер", 1997.-584 с., илл.

Построение окружности с применением алгоритмов растеризации


Основная идея алгоритмов растеризации состоит в том, что для построения аналитических кривых (отрезок прямой, окружность, эллипс, кривые Безье) отказаться от работы с FPU и пользоваться целочисленной арифметикой в CPU и применять некоторые математические зависимости между числами (например, квадрат числа N равен сумме N нечётных слагаемых от 1 до 2N-1).

Изначальный ход рассуждений был подобен описанному в статье FAQ для раздела Assembler, MASM, TASM

Дальше, применив формулу суммы натуральных нечётных чисел арифметической прогрессии пришли к алгоритму
https://web.archive.org/web/20... g-circles/
При углах построения между 45 и 90 градусов справедливо соотношение
https://www.cyberforum.ru/cgi-bin/latex.cgi?{y}_{n}-{y}_{n+1}\leq 1
Далее, из алгебраической формулы окружности
https://www.cyberforum.ru/cgi-bin/latex.cgi?{y}^{2}+{x}^{2}={R}^{2}
https://www.cyberforum.ru/cgi-bin/latex.cgi?{({y}_{n+1})}^{2}={R}^{2}-{({x}_{n+1})}^{2}
Учитывая, что https://www.cyberforum.ru/cgi-bin/latex.cgi?{x}_{n+1}={x}_{n}+1
https://www.cyberforum.ru/cgi-bin/latex.cgi?{({y}_{n+1})}^{2}={R}^{2}-{({x}_{n}+1)}^{2}
https://www.cyberforum.ru/cgi-bin/latex.cgi?{({y}_{n+1})}^{2}={R}^{2}-{({x}_{n})}^{2}-2\cdot {({x}_{n})}-1
https://www.cyberforum.ru/cgi-bin/latex.cgi?{({y}_{n+1})}^{2}={({y}_{n})}^{2}-2\cdot {({x}_{n})}-1
С учётом формулы суммы нечётных чисел, получаем алгоритм
Реализация алгоритма на C
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
static void
draw_circle (Image         *image,
             int            radius,
             unsigned char  value)
{
  int x, y;
  int l;
  int r2, y2;
  int y2_new;
  int ty;
 
  /* cos pi/4 = 185363 / 2^18 (approx) */
  l = (radius * 185363) >> 18;
 
  /* At x=0, y=radius */
  y = radius;
 
  r2 = y2 = y * y;
  ty = (2 * y) - 1;
  y2_new = r2 + 3;
 
  for (x = 0; x <= l; x++) {
    y2_new -= (2 * x) - 3;
 
    if ((y2 - y2_new) >= ty) {
      y2 -= ty;
      y -= 1;
      ty -= 2;
    }
 
    image_set_pixel (image, x, y, value);
    image_set_pixel (image, x, -y, value);
    image_set_pixel (image, -x, y, value);
    image_set_pixel (image, -x, -y, value);
 
    image_set_pixel (image, y, x, value);
    image_set_pixel (image, y, -x, value);
    image_set_pixel (image, -y, x, value);
    image_set_pixel (image, -y, -x, value);
  }
}
Дальнейшие работы математиков были связаны с сокращением вычислений

Midpoint circle algorithm

Именно этот алгоритм послужил основой для более развитых алгоритмов Брезенхэма и Мичнера.
Он описан на английской страничке Wikipedia
https://en.wikipedia.org/wiki/... _algorithm
Его реализации на разных языках программирования приведены
http://rosettacode.org/wiki/Bi... _algorithm
Реализация алгоритма на языке C со странички Wikipedia
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void drawcircle(int x0, int y0, int radius)
{
    int x = radius-1;
    int y = 0;
    int dx = 1;
    int dy = 1;
    int err = dx - (radius << 1);
 
    while (x >= y)
    {
        putpixel(x0 + x, y0 + y);
        putpixel(x0 + y, y0 + x);
        putpixel(x0 - y, y0 + x);
        putpixel(x0 - x, y0 + y);
        putpixel(x0 - x, y0 - y);
        putpixel(x0 - y, y0 - x);
        putpixel(x0 + y, y0 - x);
        putpixel(x0 + x, y0 - y);
 
        if (err <= 0)
        {
            y++;
            err += dy;
            dy += 2;
        }
        
        if (err > 0)
        {
            x--;
            dx += 2;
            err += dx - (radius << 1);
        }
    }
}
Как можно убедиться на rosettacode, существуют модификации этого алгоритма без второго условия и постоянного приращения координаты x.

Реализация алгоритма на ассемблере TASM приведена по ссылке
https://www.cyberforum.ru/post12110426.html
Фрагмент реализации алгоритма на языке ассемблера
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
;Вспомогательная процедура алгоритма рисования окружности.
;Рисует 8 точек окружности.
PutPixels8      proc    Xc:WORD, Yc:WORD, X:WORD, Y:WORD, PixelColor:WORD
        push    ax
        push    bx
        push    cx
        push    dx
 
        mov     ax,     [PixelColor]
        mov     ah,     0Ch
        mov     bh,     0
        ;putpixel(x0 + x, y0 + y);
        mov     cx,     [Xc]
        add     cx,     [X]
        mov     dx,     [Yc]
        add     dx,     [Y]
        int     10h
        ;putpixel(x0 - x, y0 + y);
        mov     cx,     [Xc]
        sub     cx,     [X]
        ;mov    dx,     [Yc]
        ;add    dx,     [Y]
        int     10h
        ;putpixel(x0 - x, y0 - y);
        ;mov    cx,     [Xc]
        ;sub    cx,     [X]
        mov     dx,     [Yc]
        sub     dx,     [Y]
        int     10h
        ;putpixel(x0 + x, y0 - y);
        mov     cx,     [Xc]
        add     cx,     [X]
        ;mov    dx,     [Yc]
        ;sub    dx,     [Y]
        int     10h
 
        ;putpixel(x0 + y, y0 + x);
        mov     cx,     [Xc]
        add     cx,     [Y]
        mov     dx,     [Yc]
        add     dx,     [X]
        int     10h
        ;putpixel(x0 - y, y0 + x);
        mov     cx,     [Xc]
        sub     cx,     [Y]
        ;mov    dx,     [Yc]
        ;add    dx,     [X]
        int     10h
        ;putpixel(x0 - y, y0 - x);
        ;mov    cx,     [Xc]
        ;sub    cx,     [Y]
        mov     dx,     [Yc]
        sub     dx,     [X]
        int     10h
        ;putpixel(x0 + y, y0 - x);
        mov     cx,     [Xc]
        add     cx,     [Y]
        mov     dx,     [Yc]
        sub     dx,     [X]
        int     10h
 
        pop     dx
        pop     cx
        pop     bx
        pop     ax
 
        ret
PutPixels8      endp
 
;Рисование окружности по алгоритму "Midpoint circle algorithm"
;на входе:
; __Xc, __Yc - координаты центра окружности
; __R        - радиус окружности
; __Color    - цвет окружности
Circle          proc    __Xc:WORD, __Yc:WORD, __R:WORD, __Color:WORD
 
        push    ax
        push    bx              ;error
        push    cx              ;x
        push    dx              ;y
        push    si              ;deltaX
        push    di              ;deltaY
 
        mov     cx,     [__R]   ;       x=R-1
        dec     cx
        mov     dx,     0       ;       y=0
        mov     si,     1       ;       deltaX=1
        mov     di,     1       ;       deltaY=1
        mov     bx,     [__R]
        shl     bx,     1       ;       error=deltaX-2*R
        mov     [__R],  bx
        sub     bx,     si
        neg     bx
        ;Вычисление координат точек и вывод рисунка
@@do:
        ;вывод 8-ми точек окружности (в разных четвертях)
        mov     ax,     [__Xc]
        push    ax
        mov     ax,     [__Yc]
        push    ax
        push    cx
        push    dx
        mov     ax,     [__Color]
        push    ax
        call    PutPixels8      ;       putpixel(...)
 
@@if1:
        cmp     bx,     0       ;       if (error<=0)
        jg      @@if2           ;       {
        inc     dx              ;               y++;
        add     bx,     di      ;               error += deltaY
        add     di,     2       ;               deltaY += 2
                                ;       }
@@if2:
        cmp     bx,     0       ;       if (error>0)
        jle     @@while         ;       {
        dec     cx              ;               x--;
        add     si,     2       ;               dx += 2;
        add     bx,     si      ;               error += deltaX - 2*R
        sub     bx,     [__R]   ;       }
@@while:
        cmp     cx,     dx      ;       while (x>=y)
        jae     @@do
 
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
Circle          endp

Этот алгоритм рисует окружность заметно быстрее, чем способ с привлечением FPU.
Но алгоритм имеет недостаток - при рисовании окружностей малых диаметров, на дисплее получается скруглённый квадрат.

Алгоритм Брезенхэма


http://ru.wikibooks.nym.su/wik... Брезенхэма
http://atarasevich.blogspot.co... ithms.html
https://ru.wikibooks.org/wiki/... ружности_3
http://ermak.cs.nstu.ru/kg_riv... h_sEc0.3.1
http://algolist.manual.ru/grap... circle.php
Как видно из описания алгоритма, отдельно рассматриваются три ситуации — движение по вертикали, по горизонтали, по диагонали.
Реализация алгоритма на языке C
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*--------------------------------------------------- V_BRcirc
 * Генерирует 1/8 окружности по алгоритму Брезенхема
 *
 * Процедура может строить 1/4 окружности.
 * Для этого надо цикл while заменить на for (;;)
 * и после Pixel_circle проверять достижение конца по условию
 * if (y <= end) break;
 * Где end устанавливается равным 0
 * В этом случае не нужен и последний оператор
 * if (x == y) Pixel_circle (xc, yc, x, y, pixel);
 * Генерацию 1/8 можно обеспечить задав end = r / sqrt (2)
 */
 
void V_BRcirc (xc, yc, r, pixel)
int  xc, yc, r, pixel;
{  int  x, y, z, Dd;
   x= 0;  y= r;  Dd= 2*(1-r);
   while (x < y) {
      Pixel_circle (xc, yc, x, y, pixel);
      if (!Dd) goto Pd;
      z= 2*Dd - 1;
      if (Dd > 0) {
         if (z + 2*x <= 0) goto Pd; else goto Pv;
      }
      if (z + 2*y > 0) goto Pd;
Pg:   ++x;      Dd= Dd + 2*x + 1;   continue; /* Горизонт */
Pd:   ++x; --y; Dd= Dd + 2*(x-y+1); continue; /* Диагонал */
Pv:   --y;      Dd= Dd - 2*y + 1;             /* Вертикал */
   }
   if (x == y) Pixel_circle (xc, yc, x, y, pixel);
}  /* V_BRcirc */

Реализовывать его нет смысла, т.к. множество переходов внутри цикла построения несколько замедлят выполнение кода из-за особенностей процессора (разрушение очереди предвыбранных команд).


Алгоритм Мичнера


Часто именно этот алгоритм приводят под названием «алгоритм Брезенхэма».
Его описание и реализации на разных языках программирования:

https://www.cyberforum.ru/post251171.html
https://www.cyberforum.ru/post13023232.html
http://members.chello.at/~easy... enham.html
http://atarasevich.blogspot.co... ithms.html
http://algolist.manual.ru/grap... circle.php

Реализация алгоритма на языке C
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*--------------------------------------------------- V_MIcirc
 * Генерирует 1/8 окружности по алгоритму Мичнера
 */
 
void V_MIcirc (xc, yc, r, pixel)
int  xc, yc, r, pixel;
{  int  x, y, d;
   x= 0;  y= r;  d= 3 - 2*r;
   while (x < y) {
      Pixel_circle (xc, yc, x, y, pixel);
      if (d < 0) d= d + 4*x + 6; else {
         d= d + 4*(x-y) + 10;  --y;
      }
      ++x;
   }
   if (x == y) Pixel_circle (xc, yc, x, y, pixel);
}  /* V_MIcirc */


Отформатирую этот исходник и чуть переименую переменные
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Circle(int Xc, int Yc, int R, int Color)
{
    int x,y,error;
    x=0;
    y=R;
    error=3-2*R;
    while(x<y)
    {
        PutPixels8(Xc,Yc,R,x,y,Color);
        if(error<0)
            error += 4*x+6;
        else
        {
            error += 4*(x-y)+10;
            y--;
        }
        x++;
    }
    if(x==y)
        PutPixels8(Xc,Yc,R,x,y,Color);
}
На ассемблере можно реализовать построчным переводом. Но нетрудно заметить, что если поменять местами строки в ветке else и пересчитать выражение для переменной error, то получится
C
12
13
14
15
16
        else
        {
            y--;
            error += 4*(x-y)+6;
        }
Таким образом выражения для error из обоих ветвей стали похожими. Так как производим работу в ассемблере, то доступны многие приемы, невообразимые из языков высокого уровня. В данном случае, введём переменную AX, которая равна 0 при (error<0) и равна (-1) в противном случае. Т.к. в процессорах Intel x86 используется дополнительный код для представления отрицательных чисел, то число (-1) представлено как единицы во всех двоичных разрядах новой переменной AX. И теперь получаем новую реализацию условия:
C
10
11
12
        AX=(error<0? 0: -1);
        y += AX;
        error += 4*(x- (AX & y))+6;
Это небольшая оптимизация позволит отказаться от условных переходов и немного увеличит скорость.

Кроме этого, можно воспользоваться тем, что цикл while-do (с предусловием) благодаря значениям x и y при инициализации всегда выполнится хотя бы один раз. Значит можно заменить его на цикл с постусловием. Это избавит от лишнего перехода.

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
;Вспомогательная процедура алгоритма рисования окружности.
;Рисует 8 точек окружности.
PutPixels8      proc    Xc:WORD, Yc:WORD, X:WORD, Y:WORD, PixelColor:WORD
        push    ax
        push    bx
        push    cx
        push    dx
 
        mov     ax,     [PixelColor]
        mov     ah,     0Ch
        mov     bh,     0
        ;putpixel(x0 + x, y0 + y);
        mov     cx,     [Xc]
        add     cx,     [X]
        mov     dx,     [Yc]
        add     dx,     [Y]
        int     10h
        ;putpixel(x0 - x, y0 + y);
        mov     cx,     [Xc]
        sub     cx,     [X]
        ;mov    dx,     [Yc]
        ;add    dx,     [Y]
        int     10h
        ;putpixel(x0 - x, y0 - y);
        ;mov    cx,     [Xc]
        ;sub    cx,     [X]
        mov     dx,     [Yc]
        sub     dx,     [Y]
        int     10h
        ;putpixel(x0 + x, y0 - y);
        mov     cx,     [Xc]
        add     cx,     [X]
        ;mov    dx,     [Yc]
        ;sub    dx,     [Y]
        int     10h
 
        ;putpixel(x0 + y, y0 + x);
        mov     cx,     [Xc]
        add     cx,     [Y]
        mov     dx,     [Yc]
        add     dx,     [X]
        int     10h
        ;putpixel(x0 - y, y0 + x);
        mov     cx,     [Xc]
        sub     cx,     [Y]
        ;mov    dx,     [Yc]
        ;add    dx,     [X]
        int     10h
        ;putpixel(x0 - y, y0 - x);
        ;mov    cx,     [Xc]
        ;sub    cx,     [Y]
        mov     dx,     [Yc]
        sub     dx,     [X]
        int     10h
        ;putpixel(x0 + y, y0 - x);
        mov     cx,     [Xc]
        add     cx,     [Y]
        mov     dx,     [Yc]
        sub     dx,     [X]
        int     10h
 
        pop     dx
        pop     cx
        pop     bx
        pop     ax
 
        ret
PutPixels8      endp
 
;Рисование окружности методом Мичнера для 1/8 окружности
;на входе:
; __Xc, __Yc - координаты центра окружности
; __R        - радиус окружности
; __Color    - цвет окружности
Circle          proc    __Xc:WORD, __Yc:WORD, __R:WORD, __Color:WORD
 
 
        push    ax
        push    bx              ;error
        push    cx              ;x
        push    dx              ;y
        push    si
        push    di
 
        mov     cx,     0       ;       x=0
        mov     dx,     [__R]   ;       y=R
        mov     bx,     3       ;       error=3-2*y
        sub     bx,     dx
        sub     bx,     dx
        ;Вычисление координат точек и вывод рисунка
@@do:
        ;вывод 8-ми точек окружности (в разных четвертях)
        push    [__Xc]
        push    [__Yc]
        push    cx
        push    dx
        push    [__Color]
        call    PutPixels8      ;       putpixel(...)
 
@@if:
        mov     ax,     bx      ;       if (error<0)
        shl     ax,     1       ;       {
        sbb     ax,     ax      ;
        not     ax              ;
        add     dx,     ax      ;               error += x*4+6
        and     ax,     dx      ;       }
        sub     ax,     cx      ;       else
        shl     ax,     1       ;       {
        shl     ax,     1       ;               error += 4(x-y)+10
        sub     bx,     ax      ;               y--;
        add     bx,     6       ;       }
 
        inc     cx              ;       x++;
@@while:
        cmp     cx,     dx      ;       while (x<=y)
        jle     @@do
 
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
Circle          endp
Пример вызова
Assembler
1
2
3
4
5
6
                ;рисование окружности
                push    word ptr [Xc]       ;Xcenter
                push    word ptr [Yc]       ;Ycenter
                push    word ptr [Radius]
                push    word ptr [Color]
                call    Circle
Модификации алгоритмов растеризации окружности

Помимо приведённых выше алгоритмов, среди примеров реализации встречается другие варианты. Причины модификации и источники как правило не указываются. Подозреваю, что подобную информацию следует искать среди более серьёзной литературы, вроде "Искусство программирования" Дональда Кнута.

Вариант алгоритма Мичнера. Присутствует более сложное условие и построение для четырёх точек.
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
8
9
10
11
12
13
void plotCircle(int xm, int ym, int r)
{
   int x = -r, y = 0, err = 2-2*r; /* II. Quadrant */ 
   do {
      setPixel(xm-x, ym+y); /*   I. Quadrant */
      setPixel(xm-y, ym-x); /*  II. Quadrant */
      setPixel(xm+x, ym-y); /* III. Quadrant */
      setPixel(xm+y, ym+x); /*  IV. Quadrant */
      r = err;
      if (r <= y) err += ++y*2+1;           /* e_xy+e_y < 0 */
      if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
   } while (x < 0);
}
2
Модератор
Эксперт по электронике
 Аватар для ФедосеевПавел
8673 / 4510 / 1670
Регистрация: 01.02.2015
Сообщений: 13,939
Записей в блоге: 13
20.04.2020, 17:19
Вывод диагностического сообщения об ошибке
в программе для Windows x86 (32-разрядной)

Введение
В случае возникновения ошибки при выполнении функции WinAPI в описаниях MSDN предлагается уточнять детали при помощи функции GetLastError.
Дополнительно предлагается по коду ошибки получить соответствующее текстовое диагностическое сообщение при помощи FormatMessage.

Специалисты Microsoft приводят пример использования этих удобных процедур:
пример из MSDN
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <windows.h>
#include <strsafe.h>
 
void ErrorExit(LPTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code
 
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 
 
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
 
    // Display the error message and exit the process
 
    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 
 
    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw); 
}
 
void main()
{
    // Generate an error
 
    if(!GetProcessId(NULL))
        ErrorExit(TEXT("GetProcessId"));
}
Этот код для вызова всплывающего окна с диагностическим сообщением неоднократно используется в различных статьях MSDN, например, в примере к FindFirstFile:
другой пример из MSDN
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <windows.h>
#include <tchar.h> 
#include <stdio.h>
#include <strsafe.h>
#pragma comment(lib, "User32.lib")
 
void DisplayErrorBox(LPTSTR lpszFunction);
 
int _tmain(int argc, TCHAR *argv[])
{
   WIN32_FIND_DATA ffd;
   LARGE_INTEGER filesize;
   TCHAR szDir[MAX_PATH];
   size_t length_of_arg;
   HANDLE hFind = INVALID_HANDLE_VALUE;
   DWORD dwError=0;
   
   // If the directory is not specified as a command-line argument,
   // print usage.
 
   if(argc != 2)
   {
      _tprintf(TEXT("\nUsage: %s <directory name>\n"), argv[0]);
      return (-1);
   }
 
   // Check that the input path plus 3 is not longer than MAX_PATH.
   // Three characters are for the "\*" plus NULL appended below.
 
   StringCchLength(argv[1], MAX_PATH, &length_of_arg);
 
   if (length_of_arg > (MAX_PATH - 3))
   {
      _tprintf(TEXT("\nDirectory path is too long.\n"));
      return (-1);
   }
 
   _tprintf(TEXT("\nTarget directory is %s\n\n"), argv[1]);
 
   // Prepare string for use with FindFile functions.  First, copy the
   // string to a buffer, then append '\*' to the directory name.
 
   StringCchCopy(szDir, MAX_PATH, argv[1]);
   StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
 
   // Find the first file in the directory.
 
   hFind = FindFirstFile(szDir, &ffd);
 
   if (INVALID_HANDLE_VALUE == hFind) 
   {
      DisplayErrorBox(TEXT("FindFirstFile"));
      return dwError;
   } 
   
   // List all the files in the directory with some info about them.
 
   do
   {
      if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
      {
         _tprintf(TEXT("  %s   <DIR>\n"), ffd.cFileName);
      }
      else
      {
         filesize.LowPart = ffd.nFileSizeLow;
         filesize.HighPart = ffd.nFileSizeHigh;
         _tprintf(TEXT("  %s   %ld bytes\n"), ffd.cFileName, filesize.QuadPart);
      }
   }
   while (FindNextFile(hFind, &ffd) != 0);
 
   dwError = GetLastError();
   if (dwError != ERROR_NO_MORE_FILES) 
   {
      DisplayErrorBox(TEXT("FindFirstFile"));
   }
 
   FindClose(hFind);
   return dwError;
}
 
 
void DisplayErrorBox(LPTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code
 
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 
 
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
 
    // Display the error message and clean up
 
    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 
 
    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
}

Его удобно применять при поиске ошибок в программе.

Реализация на masm32
Код оформлен в виде процедуры.
В качестве параметра процедуре передаётся адрес строки, при помощи которой можно идентифицировать место в исходном коде. Например, в том же исходнике для демонстрации FindFirstFile, FindNextFile, FindClose - процедура DisplayErrorBox вызывается в двух местах с соответствующими строками.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
;-------------------------------------------------------------------------
; Всплывающее окно с сообщением об ошибке.
; В окно выводится:
; 1. сообщение, передаваемое в функцию,
; 2. текст "failed with error",
; 3. код ошибки,
; 4. расшифровка кода ошибки в текстовом виде
; Код процедуры взят с небольшими изменениями из примера
; https://docs.microsoft.com/ru-ru/windows/win32/fileio/listing-the-files-in-a-directory
; Изменения:
; 1. функция StringCchPrintf заменена на wsprintf, т.к. StringCchPrintf отсутствует
;    в библиотеках masm32, а кроме того, она только описывается в strsafe.h и, вероятно,
;    является частью библиотек Visual Studio.
; 2. в DisplayErrorBox переменная dw переименована в LastError.
;-------------------------------------------------------------------------
.data
        aszErrorMessage db      "%s failed with error %d: %s", 0
        aszMBError      db      "Error", 0
.code
DisplayErrorBox proc    lpszFunction:DWORD
        local   lpMsgBuf:DWORD
        local   lpDisplayBuf:DWORD
        local   LastError:DWORD
 
        ;Retrieve the system error message for the last-error code
        invoke  GetLastError
        mov     [LastError],    eax
 
        invoke  FormatMessage,\
                FORMAT_MESSAGE_ALLOCATE_BUFFER OR FORMAT_MESSAGE_FROM_SYSTEM OR FORMAT_MESSAGE_IGNORE_INSERTS,\
                NULL,\
                [LastError],\
                LANG_NEUTRAL OR (SUBLANG_DEFAULT SHL 10),\
                ADDR lpMsgBuf,\
                0,\
                NULL
        ;Display the error message and clean up
 
        ;lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
        ;  (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
        mov     ebx,    eax     ;ebx=lstrlen((LPCTSTR)lpMsgBuf)
        invoke  lstrlen,        [lpszFunction]
        add     ebx,    eax
        add     ebx,    40
        invoke  LocalAlloc,     LMEM_ZEROINIT, ebx
        mov     [lpDisplayBuf], eax
        ;
        invoke  wsprintf,       [lpDisplayBuf], \
                ADDR aszErrorMessage, [lpszFunction],\
                [LastError], [lpMsgBuf]
        ;
        invoke  MessageBox,     NULL, [lpDisplayBuf], ADDR aszMBError, MB_OK
 
        invoke  LocalFree,      [lpMsgBuf]
        invoke  LocalFree,      [lpDisplayBuf]
        ret
DisplayErrorBox endp
Использование
1. В начале программы определяются строки идентификации сообщений об ошибке.
2. Описывается прототип процедуры DisplayErrorBox.
3. В местах возможного возникновения ошибок добавляются проверки и вызовы DisplayErrorBox.
4. В конец сегмента кода копируется исходник процедуры DisplayErrorBox.

Например,
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.data
        ;строки идентификации сообщений об ошибке
        aszFail                 db      'CreateProcess', 0
.code
;прототип процедуры, для возможности использования invoke при вызове
DisplayErrorBox PROTO   :DWORD
.............................................
        ;вызов функции WinAPI, которая может завершиться ошибкой
        invoke  CreateProcess,  NULL,\
                                ADDR aszProgramName,\
                                NULL,\
                                NULL,\
                                FALSE,\
                                0,\
                                NULL,\
                                NULL,\
                                ADDR siStartupInfo,     \
                                ADDR piProcessInformation
        test    eax,    eax
        jnz     @f
                ;сообщение об ошибке создания процесса
                invoke  DisplayErrorBox,        ADDR aszFail
        @@:
При проверке безошибочности завершения вызываемой функции WinAPI нужно внимательно читать документацию по этой функции, т.к. не обязательно нулевой возвращаемый в регистре eax результат соответствует ошибке. Так функция DialogBoxParam при ошибке возвращает или 0 или (-1) и вызов диагностического сообщения будет отличаться
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.data
        ;строки идентификации сообщений об ошибке
        aszFail                 db      'DialogBoxParam', 0
.code
;прототип процедуры, для возможности использования invoke при вызове
DisplayErrorBox PROTO   :DWORD
.............................................
        ;вызов функции WinAPI, которая может завершиться ошибкой
        invoke DialogBoxParam,hInstance,ADDR dlgname,0,ADDR WndProc,0
        cmp     eax,    0
        jg     @f
                ;сообщение об ошибке
                invoke  DisplayErrorBox,        ADDR aszFail
        @@:
В результате вызова процедуры появится подобное всплывающее окно


Примеры, когда применение DisplayErrorBox или его аналогов в ассемблерных программах сократило время на поиск ошибок
Гиперссылка (подклассирование статического элемента управления)
Используя CreateProcess запустить внешнюю программу

Штатные средства, входящие в библиотеку masm32
Также, подобное всплывающее окно можно получить штатными для пакета masm32 средствами.

процедура GetErrDescription
В состав библиотеки masm32.lib входит процедура GetErrDescription, описанная в "\masm32\m32lib\geterror.asm".
Assembler
GetErrDescription proc uses ebx edi ErrNum:DWORD
Если при вызове процедуры, параметр ErrNum равен нулю, то для определения кода ошибки LastErrorNumber вызывается системная функция GetLastError. Если код ошибки известен сразу (например, WNetAddConnection2 возвращает в случае успеха значение 0, а при неудаче - код ошибки), то его можно передать в вызываемую процедуру GetErrDescription, которая без вызова GetLastError сформирует сообщение.

Для вызова GetErrDescription требуется
1. В начале программы добавить пути к заголовочным и библиотечным файлам
2. Вызвать процедуру с нулевым параметром или со значением кода ошибки.

Пример,
Assembler
1
2
3
4
5
6
7
8
9
10
11
        include \masm32\include\masm32.inc
 
        includelib \masm32\lib\masm32.lib
.............................................
        ;вызов функции WinAPI, которая может завершиться ошибкой
        invoke DialogBoxParam,hInstance,ADDR dlgname,0,ADDR WndProc,0
        cmp     eax,    0
        jg      @f
                ;сообщение об ошибке
                invoke  GetErrDescription,      0
        @@:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
        include \masm32\include\masm32.inc
 
        includelib \masm32\lib\masm32.lib
.............................................
        ;вызов функции WinAPI, которая может завершиться ошибкой
        ; и возвращающей код ошибки
        invoke  WNetAddConnection2,offset nr,offset Pass,0,CONNECT_UPDATE_PROFILE
        or      eax,    eax
        jne     GetErr
        ; ..... ;code if OK
GetErr:
        invoke GetErrDescription,eax
В результате вызова процедуры появится подобное всплывающее окно

макрос LastError$
В состав библиотеки входит макрос-функция LastError$, который описан в "\masm32\macroc.asm".
Макрос объединяет вызовы GetLastError() и FormatMessage() для получения диагностического сообщения об ошибке, на основе формируемого операционной системой сообщения об ошибке.
При использовании неявно формирует буфер в 1024 байт в сегменте неинициализированных данных .data?.
Для вызова LastError$ требуется
1. В начале программы добавить пути к заголовочным и библиотечным файлам
2. Обратиться к макросу.

Среди примеров к пакету присутствует
\masm32\examples\exampl07\lasterr\laster r.asm
в котором показано применение макроса
Assembler
1
2
3
4
5
6
7
8
9
10
    include \masm32\include\masm32rt.inc
.............
    .code
.............
    fclose NULL                         ; close an invalid empty file handle
 
    fn MessageBox,0,LastError$(), \
                 "System Error String", \
                 MB_OK
.............
0
Модератор
Эксперт по электронике
 Аватар для ФедосеевПавел
8673 / 4510 / 1670
Регистрация: 01.02.2015
Сообщений: 13,939
Записей в блоге: 13
09.01.2022, 17:00
Вывод диагностического сообщения об ошибке
в программе для Windows x86-64 (64-разрядной)

Введение
В случае возникновения ошибки при выполнении функции WinAPI в описаниях MSDN предлагается уточнять детали при помощи функции GetLastError.
Дополнительно предлагается по коду ошибки получить соответствующее текстовое диагностическое сообщение при помощи FormatMessage.

Специалисты Microsoft приводят пример использования этих удобных процедур:
пример из MSDN
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <windows.h>
#include <strsafe.h>
 
void ErrorExit(LPTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code
 
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 
 
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
 
    // Display the error message and exit the process
 
    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 
 
    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw); 
}
 
void main()
{
    // Generate an error
 
    if(!GetProcessId(NULL))
        ErrorExit(TEXT("GetProcessId"));
}
Этот код для вызова всплывающего окна с диагностическим сообщением неоднократно используется в различных статьях MSDN, например, в примере к FindFirstFile:
другой пример из MSDN
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <windows.h>
#include <tchar.h> 
#include <stdio.h>
#include <strsafe.h>
#pragma comment(lib, "User32.lib")
 
void DisplayErrorBox(LPTSTR lpszFunction);
 
int _tmain(int argc, TCHAR *argv[])
{
   WIN32_FIND_DATA ffd;
   LARGE_INTEGER filesize;
   TCHAR szDir[MAX_PATH];
   size_t length_of_arg;
   HANDLE hFind = INVALID_HANDLE_VALUE;
   DWORD dwError=0;
   
   // If the directory is not specified as a command-line argument,
   // print usage.
 
   if(argc != 2)
   {
      _tprintf(TEXT("\nUsage: %s <directory name>\n"), argv[0]);
      return (-1);
   }
 
   // Check that the input path plus 3 is not longer than MAX_PATH.
   // Three characters are for the "\*" plus NULL appended below.
 
   StringCchLength(argv[1], MAX_PATH, &length_of_arg);
 
   if (length_of_arg > (MAX_PATH - 3))
   {
      _tprintf(TEXT("\nDirectory path is too long.\n"));
      return (-1);
   }
 
   _tprintf(TEXT("\nTarget directory is %s\n\n"), argv[1]);
 
   // Prepare string for use with FindFile functions.  First, copy the
   // string to a buffer, then append '\*' to the directory name.
 
   StringCchCopy(szDir, MAX_PATH, argv[1]);
   StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
 
   // Find the first file in the directory.
 
   hFind = FindFirstFile(szDir, &ffd);
 
   if (INVALID_HANDLE_VALUE == hFind) 
   {
      DisplayErrorBox(TEXT("FindFirstFile"));
      return dwError;
   } 
   
   // List all the files in the directory with some info about them.
 
   do
   {
      if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
      {
         _tprintf(TEXT("  %s   <DIR>\n"), ffd.cFileName);
      }
      else
      {
         filesize.LowPart = ffd.nFileSizeLow;
         filesize.HighPart = ffd.nFileSizeHigh;
         _tprintf(TEXT("  %s   %ld bytes\n"), ffd.cFileName, filesize.QuadPart);
      }
   }
   while (FindNextFile(hFind, &ffd) != 0);
 
   dwError = GetLastError();
   if (dwError != ERROR_NO_MORE_FILES) 
   {
      DisplayErrorBox(TEXT("FindFirstFile"));
   }
 
   FindClose(hFind);
   return dwError;
}
 
 
void DisplayErrorBox(LPTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code
 
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 
 
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
 
    // Display the error message and clean up
 
    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 
 
    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
}

Его удобно применять при поиске ошибок в программе.

Реализация на masm64
Код оформлен в виде процедуры.
В качестве параметра процедуре передаётся адрес строки, при помощи которой можно идентифицировать место в исходном коде. Например, в том же исходнике для демонстрации FindFirstFile, FindNextFile, FindClose - процедура DisplayErrorBox вызывается в двух местах с соответствующими строками.

При реализации в 64-разрядной системе алгоритмически всё остаётся так же, как и для 32-разрядной версии. Единственная сложность возникает при реализации из-за способа передачи аргументов в функцию - при большом количестве параметров нужно внимательно следить за стековым фреймом, часть параметров сохранять в месте, зарезервированном вызывающим кодом. Также обращу внимание на опции компилятора, запрещающие автоматически формировать пролог и эпилог процедур, поэтому они формируются явно.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
;-------------------------------------------------------------------------
; Всплывающее окно с сообщением об ошибке.
; В окно выводится:
; 1. сообщение, передаваемое в функцию,
; 2. текст "failed with error",
; 3. код ошибки,
; 4. расшифровка кода ошибки в текстовом виде
; Код процедуры взят с небольшими изменениями из примера
; https://docs.microsoft.com/ru-ru/windows/win32/fileio/listing-the-files-in-a-directory
; Изменения:
; 1. функция StringCchPrintf заменена на wsprintf, т.к. StringCchPrintf отсутствует
;    в библиотеках masm32, а кроме того, она только описывается в strsafe.h и, вероятно,
;    является частью библиотек Visual Studio.
; 2. в DisplayErrorBox переменная dw переименована в LastError.
;-------------------------------------------------------------------------
.data
        szErrorMessage  db      "%s failed with error %d: %s", 0
        szMBError       db      "Error", 0
.code
 
option prologue: none
option epilogue: none
 
DisplayErrorBox proc    lpszFunction:LPVOID
        local   lpMsgBuf        :LPVOID
        local   lpDisplayBuf    :LPVOID
        local   LastError       :QWORD
 
        ;т.к. в опциях задано отсутствие эпилога и пролога,
        ;то вычисления выполняем в коде
        ;вычисляем размер фрейма стека - он состоит из
        ; - локальных переменных (округляем до 8 байт)
        LocalsSize      =       ((2*(SIZEOF LPVOID)+1*(SIZEOF QWORD)+7)/8)*8
        ; - фрейма для вызываемых функций (максимальное число параметров у функции FormatMessage - 7 шт.)
        ;размер округляем до 16, т.к. перед вызовом стек был выровнен на границу 16, потом в стек
        ;поместили 8 байт адреса возврата, а при формировании фрейма будет помещён rbp (8 байт)
        FrameSize       =       (((LocalsSize+7*8)+15)/16)*16
        ;и самостоятельно формируем фрейм
        push    rbp
        mov     rbp,    rsp
        sub     rsp,    FrameSize
        ;сохраняем контекст в месте, предоставленном вызывающим кодом
        mov     [rbp+10h],      rcx
        mov     [rbp+18h],      rdx
        mov     [rbp+20h],      r8
        mov     [rbp+28h],      r9
 
        ;Retrieve the system error message for the last-error code
        call    GetLastError
        mov     LastError,      rax
        comment*
        -------------------------------------------------------------------------------------------------------
        invoke  FormatMessage,\
                FORMAT_MESSAGE_ALLOCATE_BUFFER OR FORMAT_MESSAGE_FROM_SYSTEM OR FORMAT_MESSAGE_IGNORE_INSERTS,\
                NULL,\
                LastError,\
                LANG_NEUTRAL OR (SUBLANG_DEFAULT SHL 10),\
                ADDR lpMsgBuf,\
                0,\
                NULL
        -------------------------------------------------------------------------------------------------------
        *
        mov     rcx,    FORMAT_MESSAGE_ALLOCATE_BUFFER OR FORMAT_MESSAGE_FROM_SYSTEM OR FORMAT_MESSAGE_IGNORE_INSERTS
        xor     edx,    edx
        mov     r8,     rax
        mov     r9d,    LANG_NEUTRAL OR (SUBLANG_DEFAULT SHL 10)
        lea     rax,    lpMsgBuf
        mov     [rsp+20h],      rax
        xor     eax,    eax
        mov     [rsp+28h],      rax
        mov     [rsp+30h],      rax
        call    FormatMessage
        ;Display the error message and clean up
        ;lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
        ;  (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
        mov     rbx,    rax     ;rbx=lstrlen((LPCTSTR)lpMsgBuf)
        lea     ecx,    lpszFunction
        call    lstrlen
        add     rbx,    rax
        add     rbx,    40
        mov     ecx,    LMEM_ZEROINIT
        mov     edx,    ebx
        call    LocalAlloc
        mov     lpDisplayBuf,   rax
        ;
        mov     rcx,    lpDisplayBuf
        lea     rdx,    [szErrorMessage]
        mov     r8,     lpszFunction
        mov     r9,     LastError
        mov     rax,    lpMsgBuf
        mov     [rsp+20h],      rax
        call    wsprintf
        ;invoke  MessageBox,     NULL, lpDisplayBuf, ADDR szMBError, MB_OK
        xor     ecx,    ecx
        mov     rdx,    lpDisplayBuf
        xor     r8,     r8      ;lea     r8,     szMBError
        mov     r9d,    MB_OK
        call    MessageBox
 
        mov     rcx,    lpMsgBuf
        call    LocalFree
        mov     rcx,    lpDisplayBuf
        call    LocalFree
 
        add     rsp,    FrameSize
        pop     rbp
        ret
DisplayErrorBox endp
Использование
1. В начале программы определяются строки идентификации сообщений об ошибке.
2. В местах возможного возникновения ошибок добавляются проверки и вызовы DisplayErrorBox.
3. В конец сегмента кода копируется исходник процедуры DisplayErrorBox.

Например,
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
.data
        ;строки идентификации сообщений об ошибке
        szFail          db      "CreateFile", 0
.code
...............................
        ;вызов функции WinAPI, которая может завершиться ошибкой
        ;открытие файла на чтение
        ;invoke  CreateFile,     ADDR szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,\
        ;                        OPEN_EXISTING, 0, NULL
        lea     rcx,    szFileName
        mov     edx,    GENERIC_READ
        mov     r8d,    FILE_SHARE_READ
        xor     r9d,    r9d
        mov     eax,    OPEN_EXISTING
        mov     [rsp+20h],      rax
        xor     eax,    eax
        mov     [rsp+28h],      rax
        mov     [rsp+30h],      rax
        call    CreateFile
        mov     [hFile],        rax
        ;проверка отсутствия ошибок при открытии файла
        cmp     rax,    INVALID_HANDLE_VALUE
        jne     @f
                ;вывод диагностического сообщения об ошибке и завершение программы
                ;invoke  DisplayErrorBox,        ADDR szFail
                lea     rcx,    szFail
                call    DisplayErrorBox
                jmp     @@Exit
        @@:
В результате вызова процедуры появится подобное всплывающее окно
Пример использования
Кликните здесь для просмотра всего текста

Программа должна вывести время последней записи в файл, имя которого указано в переменной szFileName, определённой в сегменте данных. В случае ошибки (отсутствия указанного файла) должно появиться всплывающее окно с диагностическим сообщением.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
.nolist
option dotname
option casemap: none
        include \masm64\Include64\win64.inc
        include \masm64\Include64\kernel32.inc
        include \masm64\Include64\user32.inc
        include \masm64\Include64\msvcrt.inc
 
        includelib \masm64\Lib64\kernel32.lib
        includelib \masm64\Lib64\user32.lib
        includelib \masm64\Lib64\msvcrt.lib
 
option prologue: none
option epilogue: none
 
.list
 
.data
FILETIME        struc
        dwLowDateTime   DWORD   ?
        dwHighDateTime  DWORD   ?
FILETIME        ends
SYSTEMTIME      struc
        wYear           WORD    ?
        wMonth          WORD    ?
        wDayOfWeek      WORD    ?
        wDay            WORD    ?
        wHour           WORD    ?
        wMinute         WORD    ?
        wSecond         WORD    ?
        wMilliseconds   WORD    ?
SYSTEMTIME      ends
        ;строки идентификации сообщений об ошибке
        szFail          db      "CreateFile", 0
        ;строки форматирования результатов работы программы - времени создания файла
        szFmtDateTime   db      "%02d/%02d/%d  %02d:%02d", 0
        szFmtLastWrite  db      "Last write time is: %s", 0Dh, 0Ah, 0
        ;имя обрабатываемого файла
        szFileName      db      "hello.asm", 0
.data?
        szBuffer        db      MAX_PATH dup(?)
        hFile           HANDLE  ?
.code
 
;-------------------------------------------------------------------------
; Всплывающее окно с сообщением об ошибке.
; В окно выводится:
; 1. сообщение, передаваемое в функцию,
; 2. текст "failed with error",
; 3. код ошибки,
; 4. расшифровка кода ошибки в текстовом виде
; Код процедуры взят с небольшими изменениями из примера
; https://docs.microsoft.com/ru-ru/windows/win32/fileio/listing-the-files-in-a-directory
; Изменения:
; 1. функция StringCchPrintf заменена на wsprintf, т.к. StringCchPrintf отсутствует
;    в библиотеках masm32, а кроме того, она только описывается в strsafe.h и, вероятно,
;    является частью библиотек Visual Studio.
; 2. в DisplayErrorBox переменная dw переименована в LastError.
;-------------------------------------------------------------------------
.data
        szErrorMessage  db      "%s failed with error %d: %s", 0
        szMBError       db      "Error", 0
.code
DisplayErrorBox proc    lpszFunction:LPVOID
        local   lpMsgBuf        :LPVOID
        local   lpDisplayBuf    :LPVOID
        local   LastError       :QWORD
 
        ;т.к. в опциях задано отсутствие эпилога и пролога,
        ;то вычисления выполняем в коде
        ;вычисляем размер фрейма стека - он состоит из
        ; - локальных переменных (округляем до 8 байт)
        LocalsSize      =       ((2*(SIZEOF LPVOID)+1*(SIZEOF QWORD)+7)/8)*8
        ; - фрейма для вызываемых функций (максимальное число параметров у функции FormatMessage - 7 шт.)
        ;размер округляем до 16, т.к. перед вызовом стек был выровнен на границу 16, потом в стек
        ;поместили 8 байт адреса возврата, а при формировании фрейма будет помещён rbp (8 байт)
        FrameSize       =       (((LocalsSize+7*8)+15)/16)*16
        ;и самостоятельно формируем фрейм
        push    rbp
        mov     rbp,    rsp
        sub     rsp,    FrameSize
        ;сохраняем контекст в месте, предоставленном вызывающим кодом
        mov     [rbp+10h],      rcx
        mov     [rbp+18h],      rdx
        mov     [rbp+20h],      r8
        mov     [rbp+28h],      r9
 
        ;Retrieve the system error message for the last-error code
        call    GetLastError
        mov     LastError,      rax
        comment*
        -------------------------------------------------------------------------------------------------------
        invoke  FormatMessage,\
                FORMAT_MESSAGE_ALLOCATE_BUFFER OR FORMAT_MESSAGE_FROM_SYSTEM OR FORMAT_MESSAGE_IGNORE_INSERTS,\
                NULL,\
                LastError,\
                LANG_NEUTRAL OR (SUBLANG_DEFAULT SHL 10),\
                ADDR lpMsgBuf,\
                0,\
                NULL
        -------------------------------------------------------------------------------------------------------
        *
        mov     rcx,    FORMAT_MESSAGE_ALLOCATE_BUFFER OR FORMAT_MESSAGE_FROM_SYSTEM OR FORMAT_MESSAGE_IGNORE_INSERTS
        xor     edx,    edx
        mov     r8,     rax
        mov     r9d,    LANG_NEUTRAL OR (SUBLANG_DEFAULT SHL 10)
        lea     rax,    lpMsgBuf
        mov     [rsp+20h],      rax
        xor     eax,    eax
        mov     [rsp+28h],      rax
        mov     [rsp+30h],      rax
        call    FormatMessage
        ;Display the error message and clean up
        ;lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
        ;  (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
        mov     rbx,    rax     ;rbx=lstrlen((LPCTSTR)lpMsgBuf)
        lea     ecx,    lpszFunction
        call    lstrlen
        add     rbx,    rax
        add     rbx,    40
        mov     ecx,    LMEM_ZEROINIT
        mov     edx,    ebx
        call    LocalAlloc
        mov     lpDisplayBuf,   rax
        ;
        mov     rcx,    lpDisplayBuf
        lea     rdx,    [szErrorMessage]
        mov     r8,     lpszFunction
        mov     r9,     LastError
        mov     rax,    lpMsgBuf
        mov     [rsp+20h],      rax
        call    wsprintf
        ;invoke  MessageBox,     NULL, lpDisplayBuf, ADDR szMBError, MB_OK
        xor     ecx,    ecx
        mov     rdx,    lpDisplayBuf
        xor     r8,     r8      ;lea     r8,     szMBError
        mov     r9d,    MB_OK
        call    MessageBox
 
        mov     rcx,    lpMsgBuf
        call    LocalFree
        mov     rcx,    lpDisplayBuf
        call    LocalFree
 
        add     rsp,    FrameSize
        pop     rbp
        ret
DisplayErrorBox endp
 
;// GetLastWriteTime - Retrieves the last-write time and converts
;//                    the time to a string
;//
;// Return value - TRUE if successful, FALSE otherwise
;// @hFile     - Valid file handle
;// lpszString - Pointer to buffer to receive string
GetLastWriteTime        proc    @hFile:HANDLE, lpszString:LPTSTR, dwSize:DWORD
        LOCAL   ftCreate:       FILETIME
        LOCAL   ftAccess:       FILETIME
        LOCAL   ftWrite:        FILETIME
        LOCAL   stUTC:          SYSTEMTIME
        LOCAL   stLocal:        SYSTEMTIME
        LOCAL   dwRet:          QWORD
 
        ;т.к. в опциях задано отсутствие эпилога и пролога,
        ;то вычисления выполняем в коде
        ;вычисляем размер фрейма стека - он состоит из
        ; - локальных переменных (округляем до 8 байт)
        LocalsSize      =       ((3*(SIZEOF FILETIME)+2*(SIZEOF SYSTEMTIME)+1*(SIZEOF QWORD)+7)/8)*8
        ; - фрейма для вызываемых функций (максимальное число параметров у функции FormatMessage - 7 шт.)
        ;размер округляем до 16, т.к. перед вызовом стек был выровнен на границу 16, потом в стек
        ;поместили 8 байт адреса возврата, а при формировании фрейма будет помещён rbp (8 байт)
        FrameSize       =       (((LocalsSize+7*8)+15)/16)*16
        ;и самостоятельно формируем фрейм
        push    rbp
        mov     rbp,    rsp
        sub     rsp,    FrameSize
        ;сохраняем контекст в месте, предоставленном вызывающим кодом
        mov     [rbp+10h],      rcx
        mov     [rbp+18h],      rdx
        mov     [rbp+20h],      r8
        mov     [rbp+28h],      r9
 
        ;// Retrieve the file times for the file.
        ;invoke  GetFileTime,    @hFile, ADDR ftCreate, ADDR ftAccess, ADDR ftWrite
        mov     rcx,    @hFile
        lea     rdx,    ftCreate
        lea     r8,     ftAccess
        lea     r9,     ftWrite
        call    GetFileTime
        test    rax,    rax
        jz      @@Exit
 
        ;// Convert the last-write time to local time.
        ;invoke  FileTimeToSystemTime, ADDR ftWrite, ADDR stUTC
        lea     rcx,    ftWrite
        lea     rdx,    stUTC
        call    FileTimeToSystemTime
        ;invoke  SystemTimeToTzSpecificLocalTime, NULL, ADDR stUTC, ADDR stLocal
        xor     ecx,    ecx
        lea     rdx,    stUTC
        lea     r8,     stLocal
        call    SystemTimeToTzSpecificLocalTime
 
        ;// Build a string showing the date and time.
        ;invoke  wsprintf,       lpszString, ADDR szFmtDateTime,\
        ;                        stLocal.wMonth, stLocal.wDay, stLocal.wYear,\
        ;                        stLocal.wHour, stLocal.wMinute
        mov     rcx,    lpszString
        lea     rdx,    szFmtDateTime
        movzx   r8,     stLocal.wDay
        movzx   r9,     stLocal.wMonth
        movzx   rax,    stLocal.wYear
        mov     [rsp+20h],      rax
        movzx   rax,    stLocal.wHour
        mov     [rsp+28h],      rax
        movzx   rax,    stLocal.wMinute
        mov     [rsp+30h],      rax
        xor     eax,    eax
        call    wsprintf
@@Exit:
        add     rsp,    FrameSize
        pop     rbp
        ret
GetLastWriteTime        endp
 
WinMain proc
        ;формирование фрейма стека на весь код основной программы
        ;с учётом максимального количества параметров среди вызываемых функций
        sub     rsp,    48h
        mov     rbp,    rsp
        ;открытие файла на чтение
        ;invoke  CreateFile,     ADDR szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,\
        ;                        OPEN_EXISTING, 0, NULL
        lea     rcx,    szFileName
        mov     edx,    GENERIC_READ
        mov     r8d,    FILE_SHARE_READ
        xor     r9d,    r9d
        mov     eax,    OPEN_EXISTING
        mov     [rsp+20h],      rax
        xor     eax,    eax
        mov     [rsp+28h],      rax
        mov     [rsp+30h],      rax
        call    CreateFile
        mov     [hFile],        rax
        ;проверка отсутствия ошибок при открытии файла
        cmp     rax,    INVALID_HANDLE_VALUE
        jne     @f
                ;вывод диагностического сообщения об ошибке и завершение программы
                ;invoke  DisplayErrorBox,        ADDR szFail
                lea     rcx,    szFail
                call    DisplayErrorBox
                jmp     @@Exit
        @@:
        ;вывод времени последней записи в файл
        ;invoke  GetLastWriteTime,       hFile, ADDR szBuffer, MAX_PATH
        mov     rcx,    hFile
        lea     rdx,    szBuffer
        mov     r8d,    MAX_PATH
        call    GetLastWriteTime
        ;invoke  __imp_printf,   ADDR szFmtLastWrite, ADDR szBuffer
        lea     rcx,    szFmtLastWrite
        lea     rdx,    szBuffer
        xor     eax,    eax
        call    __imp_printf
        ;завершение программы
        call    __imp__getch
@@Exit:
        xor     ecx,    ecx
        call    ExitProcess
WinMain endp
 
end
Компиляция
Windows Batch file
1
ml64 /Cp /Fl main.asm /link /subsystem:console /entry:WinMain /OUT:main.exe

Штатные средства, входящие в библиотеку masm64
На данный момент пакет masm64, распространяемый Steve Hutchesson, не содержит функции, подобной GetErrDescription или макроса LastError$ (из пакета masm32). Но пакет masm64 динамично развивается, так что, возможно, в будущем пополнится подобным функционалом.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
09.01.2022, 17:00

Видеоуроки по Ассемблеру MASM/TASM (для DOS) на русском языке
Всем доброго времени суток. Вобщем, ищу видеоуроки на русском языке по Ассемблеру. Нужно для подготовки к экзамену (нужно будет...

MASM, TASM, FASM: что выбрать для программирования в ядре
Какой асемлер выбрать для проганья в едре? вынь

Есть ли компиляторы Tasm или Masm для 64-разрядных систем
Есть ли компиляторы Tasm или Masm для 64-разрядной с-мы??? если есть просьба скинуть...

Подскажите ссылки на FAQ для разделов Assembler, MASM, TASM
Скиньте FAQ для разделов Assembler,MASM,TASM где много вариантов &quot;Hello,world!$&quot; и где написана статья &quot;Где скачать компилятор...

Assembler Переделать из TASM в masm
model small .stack 256h .data dbEnter db 10,13,'Enter string:$' dbCode db 10,13,'Code string:' dbStrM db 128 dbStrL db ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
66
Закрытая тема Создать тему
Новые блоги и статьи
Сукцессия 20. Война автоботов. Делаем ставки. Лёд против "пш-пш!".
anaschu 11.07.2026
Битва ИИ в ризосфере: Как мы скрестили жесткие ОДУ, закон Фика и «подземный шантаж» Уважаемые коллеги! Хочу поделиться уникальным опытом ведения междисциплинарной компьютерной разработки. Прямо. . .
Сукцессия 19. Какие выводы я сделал из модели? Часть 2.Внутримикоризный стехиометрический буфер и хроно-дискретные зоны экспансии
anaschu 11.07.2026
Био-Инженерный Ликбез: Архитектура Модели Ризосферы Математический аппарат моделирования
Сукцессия 18. Какие выводы я сделал из войны грибов с деревьями? Часть 1- рассказ и немного про термины
anaschu 11.07.2026
Био-Инженерный Ликбез: Архитектура Модели Ризосферы Математический аппарат моделирования Система ОДУ описывает динамику изменения массы различных компонентов системы во времени. Ключевой. . .
Просветление в 5 секунд
kumehtar 10.07.2026
Андрей Веретенников выдал фразу недавно: "Вам не обязательно кем-то себя считать"
Эксперимент с картинкой-обложкой на CodinGame
Adler 10.07.2026
Решил на днях провести эксперимент. У меня есть аккаунт на CodinGame, ТОП-3 по РФ среди 2200 участников (в разделе соревнования по программированию ИИ-ботов). Там есть обложка, куда можно влепить. . .
GitDOS: Когда я решил скрестить MS-DOS, GitHub и Claude
MaGz GoLd 08.07.2026
Привет, Киберфорум! Хочу поделиться своим проектом, который, надеюсь, вас заинтересует. GitDOS — это веб-приложение, которое позволяет запускать DOS в браузере с тремя крутыми фичами: диски живут в. . .
Учебный мини "цифровой двойник" = (Сервер = Java + Flask_питон )+(3D = Гугловскаая 4T nvideo + google colab) +( WMS = Odoo на Docker)
anaschu 07.07.2026
Увидел в требованиях к вакансии создателя имитационных моделей связку (REst + Api + WMS + Anylogic + питон + 3D Nvideo), и решил посмотреть, что это такое. Из этого всего я работал более менее. . .
Море в объективе
kumehtar 06.07.2026
Хотел написать много проникновенных слов, но передумал. Оно само - говорит. Кто слышит тот слышит.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru